From 7c29184fa4882f521b3d3c6c62dace0bffe531cb Mon Sep 17 00:00:00 2001 From: dolfies Date: Fri, 17 Jun 2022 21:53:32 -0400 Subject: [PATCH] Type connection payloads, improve docstrings, move type to an enum, add fields to edit() --- discord/connections.py | 87 +++++++++++++++++++++++++++++------- discord/enums.py | 20 +++++++++ discord/types/integration.py | 7 ++- discord/types/user.py | 40 ++++++++++++++++- 4 files changed, 136 insertions(+), 18 deletions(-) diff --git a/discord/connections.py b/discord/connections.py index 100bc42c6..ced71bafb 100644 --- a/discord/connections.py +++ b/discord/connections.py @@ -23,12 +23,17 @@ DEALINGS IN THE SOFTWARE. """ from __future__ import annotations -from typing import Optional, TYPE_CHECKING +from typing import TYPE_CHECKING, List, Optional +from .enums import ConnectionType, try_enum +from .integrations import Integration from .utils import MISSING if TYPE_CHECKING: + from .guild import Guild from .state import ConnectionState + from .types.integration import ConnectionIntegration as IntegrationPayload + from .types.user import Connection as ConnectionPayload, PartialConnection as PartialConnectionPayload __all__ = ( 'PartialConnection', @@ -37,9 +42,9 @@ __all__ = ( class PartialConnection: - """Represents a partial Discord profile connection + """Represents a partial Discord profile connection. - This is the info you get for other people's connections. + This is the info you get for other users' connections. .. container:: operations @@ -59,14 +64,16 @@ class PartialConnection: Returns the connection's name. + .. versionadded:: 2.0 + Attributes ---------- id: :class:`str` The connection's account ID. name: :class:`str` The connection's account name. - type: :class:`str` - The connection service (e.g. 'youtube') + type: :class:`ConnectionType` + The connection service (e.g. youtube, twitch, etc.). verified: :class:`bool` Whether the connection is verified. revoked: :class:`bool` @@ -77,7 +84,7 @@ class PartialConnection: __slots__ = ('id', 'name', 'type', 'verified', 'revoked', 'visible') - def __init__(self, data: dict): + def __init__(self, data: PartialConnectionPayload): self._update(data) def __str__(self) -> str: @@ -99,10 +106,10 @@ class PartialConnection: return self.id != other.id or self.name != other.name return True - def _update(self, data: dict): + def _update(self, data: PartialConnectionPayload): self.id: str = data['id'] self.name: str = data['name'] - self.type: str = data['type'] + self.type: ConnectionType = try_enum(ConnectionType, data['type']) self.verified: bool = data['verified'] self.revoked: bool = data.get('revoked', False) @@ -110,7 +117,27 @@ class PartialConnection: class Connection(PartialConnection): - """Represents a Discord profile connection + """Represents a Discord profile connection. + + .. container:: operations + + .. describe:: x == y + + Checks if two connections are equal. + + .. describe:: x != y + + Checks if two connections are not equal. + + .. describe:: hash(x) + + Return the connection's hash. + + .. describe:: str(x) + + Returns the connection's name. + + .. versionadded:: 2.0 Attributes ---------- @@ -120,16 +147,18 @@ class Connection(PartialConnection): Whether activities from this connection will be shown in presences. access_token: :class:`str` The OAuth2 access token for the account, if applicable. + integrations: List[:class:`Integration`] + The integrations attached to the connection. """ - __slots__ = ('_state', 'visible', 'friend_sync', 'show_activity', 'access_token') + __slots__ = ('_state', 'visible', 'friend_sync', 'show_activity', 'access_token', 'integrations') - def __init__(self, *, data: dict, state: ConnectionState): + def __init__(self, *, data: ConnectionPayload, state: ConnectionState): super().__init__(data) self._state = state self.access_token: Optional[str] = None - def _update(self, data: dict): + def _update(self, data: ConnectionPayload): super()._update(data) self.visible: bool = bool(data.get('visibility', True)) self.friend_sync: bool = data.get('friend_sync', False) @@ -141,7 +170,25 @@ class Connection(PartialConnection): except KeyError: pass - async def edit(self, *, visible: bool = MISSING, show_activity: bool = MISSING): + self.integrations: List[Integration] = [ + Integration(data=i, guild=self._resolve_guild(i)) for i in data.get('integrations', []) + ] + + def _resolve_guild(self, data: IntegrationPayload) -> Guild: + from .guild import Guild + + state = self._state + guild_data = data['guild'] + + guild_id = int(guild_data['id']) + guild = state._get_guild(guild_id) + if guild is None: + guild = Guild(data=guild_data, state=state) + return guild + + async def edit( + self, *, name: str = MISSING, visible: bool = MISSING, show_activity: bool = MISSING, friend_sync: bool = MISSING + ) -> None: """|coro| Edit the connection. @@ -150,8 +197,12 @@ class Connection(PartialConnection): Parameters ---------- + name: :class:`str` + The new name of the connection. Only editable for certain connection types. visible: :class:`bool` Whether the connection is visible on your profile. + friend_sync: :class:`bool` + Whether friends are synced over the connection. show_activity: :class:`bool` Whether activities from this connection will be shown in presences. @@ -161,11 +212,15 @@ class Connection(PartialConnection): Editing the connection failed. """ payload = {} + if name is not MISSING: + payload['name'] = name if visible is not MISSING: payload['visibility'] = visible + if friend_sync is not MISSING: + payload['friend_sync'] = friend_sync if show_activity is not MISSING: payload['show_activity'] = show_activity - data = await self._state.http.edit_connection(self.type, self.id, **payload) + data = await self._state.http.edit_connection(self.type.value, self.id, **payload) self._update(data) async def delete(self) -> None: @@ -178,7 +233,7 @@ class Connection(PartialConnection): HTTPException Deleting the connection failed. """ - await self._state.http.delete_connection(self.type, self.id) + await self._state.http.delete_connection(self.type.value, self.id) async def fetch_access_token(self) -> str: """|coro| @@ -195,6 +250,6 @@ class Connection(PartialConnection): :class:`str` The new access token. """ - data = await self._state.http.get_connection_token(self.type, self.id) + data = await self._state.http.get_connection_token(self.type.value, self.id) self.access_token = token = data['access_token'] return token diff --git a/discord/enums.py b/discord/enums.py index 8ed358b74..19bb79ee3 100644 --- a/discord/enums.py +++ b/discord/enums.py @@ -79,6 +79,7 @@ __all__ = ( 'ScheduledEventStatus', 'ScheduledEventEntityType', 'ApplicationType', + 'ConnectionType', ) if TYPE_CHECKING: @@ -873,6 +874,25 @@ class AppCommandType(Enum): return self.value +class ConnectionType(Enum): + battle_net = 'battlenet' + contacts = 'contacts' + epic_games = 'epicgames' + facebook = 'facebook' + github = 'github' + league_of_legends = 'leagueoflegends' + playstation = 'playstation' + reddit = 'reddit' + samsung = 'samsunggalaxy' + spotify = 'spotify' + skype = 'skype' + steam = 'steam' + twitch = 'twitch' + twitter = 'twitter' + youtube = 'youtube' + xbox = 'xbox' + + def create_unknown_value(cls: Type[E], val: Any) -> E: value_cls = cls._enum_value_cls_ # type: ignore # This is narrowed below name = f'unknown_{val}' diff --git a/discord/types/integration.py b/discord/types/integration.py index cf73b7a90..cefde2f18 100644 --- a/discord/types/integration.py +++ b/discord/types/integration.py @@ -27,6 +27,7 @@ from __future__ import annotations from typing import Literal, Optional, TypedDict, Union from typing_extensions import NotRequired +from .guild import Guild from .snowflake import Snowflake from .user import User @@ -78,4 +79,8 @@ class BotIntegration(BaseIntegration): application: IntegrationApplication -Integration = Union[BaseIntegration, StreamIntegration, BotIntegration] +class ConnectionIntegration(BaseIntegration): + guild: Guild + + +Integration = Union[BaseIntegration, StreamIntegration, BotIntegration, ConnectionIntegration] diff --git a/discord/types/user.py b/discord/types/user.py index 1d44dcfbb..c2d477cf7 100644 --- a/discord/types/user.py +++ b/discord/types/user.py @@ -22,8 +22,11 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ +from typing import List, Literal, Optional, TypedDict +from typing_extensions import NotRequired + +from .integration import ConnectionIntegration from .snowflake import Snowflake -from typing import Literal, Optional, TypedDict class PartialUser(TypedDict): @@ -33,6 +36,25 @@ class PartialUser(TypedDict): avatar: Optional[str] +ConnectionType = Literal[ + 'battlenet', + 'contacts', + 'epicgames', + 'facebook', + 'github', + 'leagueoflegends', + 'playstation', + 'reddit', + 'samsunggalaxy', + 'spotify', + 'skype', + 'steam', + 'twitch', + 'twitter', + 'youtube', + 'xbox', +] +ConnectionVisibilty = Literal[0, 1] PremiumType = Literal[0, 1, 2] @@ -52,3 +74,19 @@ class User(PartialUser, total=False): analytics_token: str phone: Optional[str] token: str + + +class PartialConnection(TypedDict): + id: str + type: ConnectionType + name: str + verified: bool + revoked: NotRequired[bool] + + +class Connection(PartialConnection): + visibility: int + show_activity: bool + friend_sync: bool + integrations: NotRequired[List[ConnectionIntegration]] + access_token: NotRequired[str]