Skip to content

Commit

Permalink
Support team members data in application info
Browse files Browse the repository at this point in the history
  • Loading branch information
fourjr authored and Rapptz committed Jun 29, 2019
1 parent 5698cf6 commit 3961e7e
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 5 deletions.
48 changes: 47 additions & 1 deletion discord/appinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@
DEALINGS IN THE SOFTWARE.
"""

from . import utils
from .user import User
from .asset import Asset
from .team import Team


class AppInfo:
Expand All @@ -40,6 +42,8 @@ class AppInfo:
The application name.
owner: :class:`User`
The application owner.
team: Optional[:class:`Team`]
The application's team.
icon: Optional[:class:`str`]
The icon hash, if it exists.
description: Optional[:class:`str`]
Expand All @@ -52,9 +56,28 @@ class AppInfo:
grant flow to join.
rpc_origins: Optional[List[:class:`str`]]
A list of RPC origin URLs, if RPC is enabled.
summary: :class:`str`
If this application is a game sold on Discord,
this field will be the summary field for the store page of its primary SKU
verify_key: :class:`str`
The base64 encoded key for the GameSDK's GetTicket
guild_id: Optional[:class:`int`]
If this application is a game sold on Discord,
this field will be the guild to which it has been linked
primary_sku_id: Optional[:class:`int`]
If this application is a game sold on Discord,
this field will be the id of the "Game SKU" that is created, if exists
slug: Optional[:class:`str`]
If this application is a game sold on Discord,
this field will be the URL slug that links to the store page
cover_image: Optional[:class:`str`]
If this application is a game sold on Discord,
this field will be the hash of the image on store embeds
"""
__slots__ = ('_state', 'description', 'id', 'name', 'rpc_origins',
'bot_public', 'bot_require_code_grant', 'owner', 'icon')
'bot_public', 'bot_require_code_grant', 'owner', 'icon',
'summary', 'verify_key', 'team', 'guild_id', 'primary_sku_id',
'slug', 'cover_image')

def __init__(self, state, data):
self._state = state
Expand All @@ -68,6 +91,18 @@ def __init__(self, state, data):
self.bot_require_code_grant = data['bot_require_code_grant']
self.owner = User(state=self._state, data=data['owner'])

team = data.get('team')
self.team = Team(state, team) if team else None

self.summary = data['summary']
self.verify_key = data['verify_key']

self.guild_id = utils._get_as_snowflake(data, 'guild_id')

self.primary_sku_id = utils._get_as_snowflake(data, 'primary_sku_id')
self.slug = data.get('slug')
self.cover_image = data.get('cover_image')

def __repr__(self):
return '<{0.__class__.__name__} id={0.id} name={0.name!r} description={0.description!r} public={0.bot_public} ' \
'owner={0.owner!r}>'.format(self)
Expand All @@ -76,3 +111,14 @@ def __repr__(self):
def icon_url(self):
""":class:`.Asset`: Retrieves the application's icon asset."""
return Asset._from_icon(self._state, self, 'app')

@property
def cover_image_url(self):
""":class:`.Asset`: Retrieves the cover image on a store embed."""
return Asset._from_cover_image(self._state, self)

@property
def guild(self):
"""Optional[:class:`Guild`]: If this application is a game sold on Discord,
this field will be the guild to which it has been linked"""
return self._state._get_guild(int(self.guild_id))
8 changes: 8 additions & 0 deletions discord/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ def _from_icon(cls, state, object, path):
url = 'https://cdn.discordapp.com/{0}-icons/{1.id}/{1.icon}.jpg'.format(path, object)
return cls(state, url)

@classmethod
def _from_cover_image(cls, state, obj):
if obj.cover_image is None:
return cls(state)

url = 'https://cdn.discordapp.com/app-assets/{0.id}/store/{0.cover_image}.jpg'.format(obj)
return cls(state, url)

@classmethod
def _from_guild_image(cls, state, id, hash, key, *, format='webp', size=1024):
if not utils.valid_icon_size(size):
Expand Down
5 changes: 5 additions & 0 deletions discord/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
'PremiumType',
'UserContentFilter',
'FriendFlags',
'TeamMembershipState',
'Theme',
)

Expand Down Expand Up @@ -392,6 +393,10 @@ class PremiumType(Enum):
nitro_classic = 1
nitro = 2

class TeamMembershipState(Enum):
invited = 1
accepted = 2

def try_enum(cls, val):
"""A function that tries to turn the value into enum ``cls``.
Expand Down
31 changes: 27 additions & 4 deletions discord/ext/commands/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,16 @@ def __init__(self, command_prefix, help_command=_default, description=None, **op
self._help_command = None
self.description = inspect.cleandoc(description) if description else ''
self.owner_id = options.get('owner_id')
self.owner_ids = options.get('owner_ids', {})

if self.owner_id and self.owner_ids:
raise ValueError('Both owner_id and owner_ids are set.')
elif not isinstance(self.owner_id, int):
raise ValueError('owner_id is not an int.')
elif not isinstance(self.owner_ids, (set, list, tuple)):
raise ValueError('owner_ids is not a set, list or tuple.')
elif not all(isinstance(i, int) for i in self.owner_ids):
raise ValueError('owner_ids has to be an iterable of int.')

if options.pop('self_bot', False):
self._skip_check = lambda x, y: x != y
Expand Down Expand Up @@ -284,6 +294,9 @@ async def is_owner(self, user):
If an :attr:`owner_id` is not set, it is fetched automatically
through the use of :meth:`~.Bot.application_info`.
The function also checks if the application is team-owned if
:attr:`owner_id` is not set.
Parameters
-----------
user: :class:`.abc.User`
Expand All @@ -295,11 +308,18 @@ async def is_owner(self, user):
Whether the user is the owner.
"""

if self.owner_id is None:
if self.owner_id:
return user.id == self.owner_id
elif self.owner_ids:
return user.id in self.owner_ids
else:
app = await self.application_info()
self.owner_id = owner_id = app.owner.id
return user.id == owner_id
return user.id == self.owner_id
if app.team:
self.owner_ids = {m.id for m in app.team.members}
return user.id in self.owner_ids
else:
self.owner_id = owner_id = app.owner.id
return user.id == owner_id

def before_invoke(self, coro):
"""A decorator that registers a coroutine as a pre-invoke hook.
Expand Down Expand Up @@ -959,6 +979,9 @@ class Bot(BotBase, discord.Client):
The ID that owns the bot. If this is not set and is then queried via
:meth:`.is_owner` then it is fetched automatically using
:meth:`~.Bot.application_info`.
owner_ids: Optional[:class:`set`]
The IDs that owns the bot. This is similar to `owner_id`.
If both `owner_id` and `owner_ids` are set, ValueError would be raised.
"""
pass

Expand Down
100 changes: 100 additions & 0 deletions discord/team.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# -*- coding: utf-8 -*-

"""
The MIT License (MIT)
Copyright (c) 2015-2019 Rapptz
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
"""

from . import utils
from .user import User
from .asset import Asset
from .enums import TeamMembershipState, try_enum


class Team:
"""Represents an application team for a bot provided by Discord.
Attributes
-------------
id: :class:`int`
The team ID.
name: :class:`str`
The team name
icon: Optional[:class:`str`]
The icon hash, if it exists.
owner_id: :class:`int`
The team's owner ID.
members: List[:class:`TeamMember`]
A list of the members in the team
"""
__slots__ = ('_state', 'id', 'name', 'icon', 'owner_id', 'members')

def __init__(self, state, data):
self._state = state

self.id = utils._get_as_snowflake(data, 'id')
self.name = data['name']
self.icon = data['icon']
self.owner_id = utils._get_as_snowflake(data, 'owner_user_id')
self.members = [TeamMember(self, self._state, member) for member in data['members']]

def __repr__(self):
return '<{0.__class__.__name__} id={0.id} name={0.name}>'.format(self)

@property
def icon_url(self):
""":class:`.Asset`: Retrieves the team's icon asset."""
return Asset._from_icon(self._state, self, 'team')

@property
def owner(self):
"""Optional[:class:`User`]: The team's owner, if available from the cache."""
return self._state.get_user(self.owner_id)


class TeamMember:
"""Represents a team member in a team.
Attributes
-------------
team: :class:`team`
The team that the member is from.
membership_state: :class:`TeamMembershipState`
The membership state of the member (e.g. invited or accepted)
user: :class:`User`
The team member
"""
__slots__ = ('_state', 'team', 'membership_state',
'permissions', 'user')

def __init__(self, team, state, data):
self._state = state
self.team = team

self.membership_state = try_enum(TeamMembershipState, data['membership_state'])
self.permissions = data['permissions']
self.user = User(state=self._state, data=data['user'])

def __repr__(self):
return '<{0.__class__.__name__} id={0.user.id} name={0.user.name!r}>'.format(self)
21 changes: 21 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ Client
.. autoclass:: AppInfo
:members:

.. autoclass:: GameInfo
:members:

.. autoclass:: Team
:members:

.. autoclass:: TeamMember
:members:

Voice
------

Expand Down Expand Up @@ -1537,6 +1546,18 @@ of :class:`enum.Enum`.
Represents the Dark theme on Discord.


.. class:: TeamMembershipState

Represents the membership state of a team member retrieved through :func:Bot.application_info.

.. attribue:: invited

Represents an invited member.

.. attribute:: accepted

Represents a member currently in the team.

Async Iterator
----------------

Expand Down

0 comments on commit 3961e7e

Please sign in to comment.