aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfourjr <[email protected]>2019-06-21 17:09:15 +0800
committerRapptz <[email protected]>2019-06-29 19:14:24 -0400
commit3961e7ef6dc05925927dbd2f899661a2058fd070 (patch)
treeb43468b9a29211e923342a3b27d834b465943732
parentImplement `Guild.fetch_roles` (diff)
downloaddiscord.py-3961e7ef6dc05925927dbd2f899661a2058fd070.tar.xz
discord.py-3961e7ef6dc05925927dbd2f899661a2058fd070.zip
Support team members data in application info
-rw-r--r--discord/appinfo.py48
-rw-r--r--discord/asset.py8
-rw-r--r--discord/enums.py5
-rw-r--r--discord/ext/commands/bot.py31
-rw-r--r--discord/team.py100
-rw-r--r--docs/api.rst21
6 files changed, 208 insertions, 5 deletions
diff --git a/discord/appinfo.py b/discord/appinfo.py
index f38f40b9..ddf5ae05 100644
--- a/discord/appinfo.py
+++ b/discord/appinfo.py
@@ -24,8 +24,10 @@ 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 .team import Team
class AppInfo:
@@ -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`]
@@ -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
@@ -68,6 +91,18 @@ class AppInfo:
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)
@@ -76,3 +111,14 @@ class AppInfo:
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))
diff --git a/discord/asset.py b/discord/asset.py
index 1d2ebbaa..1e1c8a58 100644
--- a/discord/asset.py
+++ b/discord/asset.py
@@ -95,6 +95,14 @@ class Asset:
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):
raise InvalidArgument("size must be a power of 2 between 16 and 4096")
diff --git a/discord/enums.py b/discord/enums.py
index 5fec3a02..a7868beb 100644
--- a/discord/enums.py
+++ b/discord/enums.py
@@ -47,6 +47,7 @@ __all__ = (
'PremiumType',
'UserContentFilter',
'FriendFlags',
+ 'TeamMembershipState',
'Theme',
)
@@ -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``.
diff --git a/discord/ext/commands/bot.py b/discord/ext/commands/bot.py
index 66e245dc..b45f828b 100644
--- a/discord/ext/commands/bot.py
+++ b/discord/ext/commands/bot.py
@@ -108,6 +108,16 @@ class BotBase(GroupMixin):
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
@@ -284,6 +294,9 @@ class BotBase(GroupMixin):
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`
@@ -295,11 +308,18 @@ class BotBase(GroupMixin):
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.
@@ -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
diff --git a/discord/team.py b/discord/team.py
new file mode 100644
index 00000000..5a787796
--- /dev/null
+++ b/discord/team.py
@@ -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)
diff --git a/docs/api.rst b/docs/api.rst
index c80bc304..e2a83100 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -42,6 +42,15 @@ Client
.. autoclass:: AppInfo
:members:
+.. autoclass:: GameInfo
+ :members:
+
+.. autoclass:: Team
+ :members:
+
+.. autoclass:: TeamMember
+ :members:
+
Voice
------
@@ -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
----------------