Browse Source

Implement new connection capabilities

pull/10109/head
dolfies 3 years ago
parent
commit
c4b6007979
  1. 99
      discord/client.py
  2. 2
      discord/commands.py
  3. 168
      discord/connections.py
  4. 18
      discord/enums.py
  5. 32
      discord/http.py
  6. 22
      discord/state.py
  7. 4
      discord/types/gateway.py
  8. 22
      discord/types/user.py
  9. 62
      docs/api.rst

99
discord/client.py

@ -54,7 +54,7 @@ from .widget import Widget
from .guild import Guild from .guild import Guild
from .emoji import Emoji from .emoji import Emoji
from .channel import _private_channel_factory, _threaded_channel_factory, GroupChannel, PartialMessageable from .channel import _private_channel_factory, _threaded_channel_factory, GroupChannel, PartialMessageable
from .enums import ActivityType, ChannelType, Status, InviteType, try_enum from .enums import ActivityType, ChannelType, ConnectionLinkType, ConnectionType, Status, InviteType, try_enum
from .mentions import AllowedMentions from .mentions import AllowedMentions
from .errors import * from .errors import *
from .enums import Status from .enums import Status
@ -337,6 +337,20 @@ class Client:
""" """
return utils.SequenceProxy(self._connection._messages or []) return utils.SequenceProxy(self._connection._messages or [])
@property
def connections(self) -> List[Connection]:
"""List[:class:`.Connection`]: The connections that the connected client has.
These connections don't have the :attr:`.Connection.metadata` attribute populated.
.. versionadded:: 2.0
.. note::
Due to a Discord limitation, removed connections may not be removed from this cache.
"""
return list(self._connection.connections.values())
@property @property
def private_channels(self) -> List[PrivateChannel]: def private_channels(self) -> List[PrivateChannel]:
"""List[:class:`.abc.PrivateChannel`]: The private channels that the connected client is participating on.""" """List[:class:`.abc.PrivateChannel`]: The private channels that the connected client is participating on."""
@ -2197,7 +2211,7 @@ class Client:
await note.fetch() await note.fetch()
return note return note
async def connections(self) -> List[Connection]: async def fetch_connections(self) -> List[Connection]:
"""|coro| """|coro|
Retrieves all of your connections. Retrieves all of your connections.
@ -2218,6 +2232,87 @@ class Client:
data = await state.http.get_connections() data = await state.http.get_connections()
return [Connection(data=d, state=state) for d in data] return [Connection(data=d, state=state) for d in data]
async def authorize_connection(
self, type: ConnectionType, two_way_link_type: Optional[ConnectionLinkType] = None, continuation: bool = False
) -> str:
"""|coro|
Retrieves a URL to authorize a connection with a third-party service.
.. versionadded:: 2.0
Parameters
-----------
type: :class:`.ConnectionType`
The type of connection to authorize.
two_way_link_type: Optional[:class:`.ConnectionLinkType`]
The type of two-way link to use, if any.
continuation: :class:`bool`
Whether this is a continuation of a previous authorization.
Raises
-------
HTTPException
Authorizing the connection failed.
Returns
--------
:class:`str`
The URL to redirect the user to.
"""
data = await self.http.authorize_connection(
str(type), str(two_way_link_type) if two_way_link_type else None, continuation=continuation
)
return data['url']
async def create_connection(
self,
type: ConnectionType,
code: str,
state: str,
*,
insecure: bool = True,
friend_sync: bool = MISSING,
) -> None:
"""|coro|
Creates a new connection.
This is a low-level method that requires data obtained from other APIs.
.. versionadded:: 2.0
Parameters
-----------
type: :class:`.ConnectionType`
The type of connection to add.
code: :class:`str`
The authorization code for the connection.
state: :class:`str`
The state used to authorize the connection.
insecure: :class:`bool`
Whether the authorization is insecure. Defaults to ``True``.
friend_sync: :class:`bool`
Whether friends are synced over the connection.
Defaults to ``True`` for :attr:`.ConnectionType.facebook` and :attr:`.ConnectionType.contacts`, else ``False``.
Raises
-------
HTTPException
Creating the connection failed.
"""
friend_sync = (
friend_sync if friend_sync is not MISSING else type in (ConnectionType.facebook, ConnectionType.contacts)
)
await self.http.add_connection(
str(type),
code=code,
state=state,
insecure=insecure,
friend_sync=friend_sync,
)
async def fetch_private_channels(self) -> List[PrivateChannel]: async def fetch_private_channels(self) -> List[PrivateChannel]:
"""|coro| """|coro|

2
discord/commands.py

@ -173,7 +173,7 @@ class ApplicationCommand(Protocol):
def default_member_permissions(self) -> Optional[Permissions]: def default_member_permissions(self) -> Optional[Permissions]:
"""Optional[:class:`~discord.Permissions`]: The default permissions required to use this command. """Optional[:class:`~discord.Permissions`]: The default permissions required to use this command.
..note:: .. note::
This may be overrided on a guild-by-guild basis. This may be overrided on a guild-by-guild basis.
""" """
perms = self._default_member_permissions perms = self._default_member_permissions

168
discord/connections.py

@ -23,7 +23,7 @@ DEALINGS IN THE SOFTWARE.
""" """
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional from typing import TYPE_CHECKING, Any, Iterator, List, Optional, Tuple
from .enums import ConnectionType, try_enum from .enums import ConnectionType, try_enum
from .integrations import Integration from .integrations import Integration
@ -38,6 +38,7 @@ if TYPE_CHECKING:
__all__ = ( __all__ = (
'PartialConnection', 'PartialConnection',
'Connection', 'Connection',
'ConnectionMetadata',
) )
@ -46,6 +47,8 @@ class PartialConnection:
This is the info you get for other users' connections. This is the info you get for other users' connections.
.. versionadded:: 2.0
.. container:: operations .. container:: operations
.. describe:: x == y .. describe:: x == y
@ -64,8 +67,6 @@ class PartialConnection:
Returns the connection's name. Returns the connection's name.
.. versionadded:: 2.0
Attributes Attributes
---------- ----------
id: :class:`str` id: :class:`str`
@ -73,16 +74,14 @@ class PartialConnection:
name: :class:`str` name: :class:`str`
The connection's account name. The connection's account name.
type: :class:`ConnectionType` type: :class:`ConnectionType`
The connection service (e.g. youtube, twitch, etc.). The connection service type (e.g. youtube, twitch, etc.).
verified: :class:`bool` verified: :class:`bool`
Whether the connection is verified. Whether the connection is verified.
revoked: :class:`bool`
Whether the connection is revoked.
visible: :class:`bool` visible: :class:`bool`
Whether the connection is visible on the user's profile. Whether the connection is visible on the user's profile.
""" """
__slots__ = ('id', 'name', 'type', 'verified', 'revoked', 'visible') __slots__ = ('id', 'name', 'type', 'verified', 'visible')
def __init__(self, data: PartialConnectionPayload): def __init__(self, data: PartialConnectionPayload):
self._update(data) self._update(data)
@ -94,7 +93,7 @@ class PartialConnection:
return f'<{self.__class__.__name__} id={self.id!r} name={self.name!r} type={self.type!r} visible={self.visible}>' return f'<{self.__class__.__name__} id={self.id!r} name={self.name!r} type={self.type!r} visible={self.visible}>'
def __hash__(self) -> int: def __hash__(self) -> int:
return hash((self.name, self.id)) return hash((self.type.value, self.id))
def __eq__(self, other: object) -> bool: def __eq__(self, other: object) -> bool:
if isinstance(other, PartialConnection): if isinstance(other, PartialConnection):
@ -110,15 +109,15 @@ class PartialConnection:
self.id: str = data['id'] self.id: str = data['id']
self.name: str = data['name'] self.name: str = data['name']
self.type: ConnectionType = try_enum(ConnectionType, data['type']) self.type: ConnectionType = try_enum(ConnectionType, data['type'])
self.verified: bool = data['verified'] self.verified: bool = data['verified']
self.revoked: bool = data.get('revoked', False) self.visible: bool = True # If we have a partial connection, it's visible
self.visible: bool = True
class Connection(PartialConnection): class Connection(PartialConnection):
"""Represents a Discord profile connection. """Represents a Discord profile connection.
.. versionadded:: 2.0
.. container:: operations .. container:: operations
.. describe:: x == y .. describe:: x == y
@ -137,32 +136,54 @@ class Connection(PartialConnection):
Returns the connection's name. Returns the connection's name.
.. versionadded:: 2.0
Attributes Attributes
---------- ----------
revoked: :class:`bool`
Whether the connection is revoked.
friend_sync: :class:`bool` friend_sync: :class:`bool`
Whether friends are synced over the connection. Whether friends are synced over the connection.
show_activity: :class:`bool` show_activity: :class:`bool`
Whether activities from this connection will be shown in presences. Whether activities from this connection will be shown in presences.
access_token: :class:`str` two_way_link: :class:`bool`
Whether the connection is authorized both ways (i.e. it's both a connection and an authorization).
metadata_visible: :class:`bool`
Whether the connection's metadata is visible.
metadata: Optional[:class:`ConnectionMetadata`]
Various metadata about the connection.
The contents of this are always subject to change.
access_token: Optional[:class:`str`]
The OAuth2 access token for the account, if applicable. The OAuth2 access token for the account, if applicable.
integrations: List[:class:`Integration`] integrations: List[:class:`Integration`]
The integrations attached to the connection. The integrations attached to the connection.
""" """
__slots__ = ('_state', 'visible', 'friend_sync', 'show_activity', 'access_token', 'integrations') __slots__ = (
'_state',
'revoked',
'friend_sync',
'show_activity',
'two_way_link',
'metadata_visible',
'metadata',
'access_token',
'integrations',
)
def __init__(self, *, data: ConnectionPayload, state: ConnectionState): def __init__(self, *, data: ConnectionPayload, state: ConnectionState):
super().__init__(data) self._update(data)
self._state = state self._state = state
self.access_token: Optional[str] = None self.access_token: Optional[str] = None
def _update(self, data: ConnectionPayload): def _update(self, data: ConnectionPayload):
super()._update(data) super()._update(data)
self.visible: bool = bool(data.get('visibility', True)) self.revoked: bool = data.get('revoked', False)
self.visible: bool = bool(data.get('visibility', False))
self.friend_sync: bool = data.get('friend_sync', False) self.friend_sync: bool = data.get('friend_sync', False)
self.show_activity: bool = data.get('show_activity', True) self.show_activity: bool = data.get('show_activity', True)
self.two_way_link: bool = data.get('two_way_link', False)
self.metadata_visible: bool = bool(data.get('metadata_visibility', False))
self.metadata: Optional[ConnectionMetadata] = ConnectionMetadata(data['metadata']) if 'metadata' in data else None
# Only sometimes in the payload # Only sometimes in the payload
try: try:
@ -171,14 +192,16 @@ class Connection(PartialConnection):
pass pass
self.integrations: List[Integration] = [ self.integrations: List[Integration] = [
Integration(data=i, guild=self._resolve_guild(i)) for i in data.get('integrations', []) Integration(data=i, guild=self._resolve_guild(i)) for i in data.get('integrations') or []
] ]
def _resolve_guild(self, data: IntegrationPayload) -> Guild: def _resolve_guild(self, data: IntegrationPayload) -> Guild:
from .guild import Guild from .guild import Guild
state = self._state state = self._state
guild_data = data['guild'] guild_data = data.get('guild')
if not guild_data:
return None # type: ignore
guild_id = int(guild_data['id']) guild_id = int(guild_data['id'])
guild = state._get_guild(guild_id) guild = state._get_guild(guild_id)
@ -187,8 +210,14 @@ class Connection(PartialConnection):
return guild return guild
async def edit( async def edit(
self, *, name: str = MISSING, visible: bool = MISSING, show_activity: bool = MISSING, friend_sync: bool = MISSING self,
) -> None: *,
name: str = MISSING,
visible: bool = MISSING,
friend_sync: bool = MISSING,
show_activity: bool = MISSING,
metadata_visible: bool = MISSING,
) -> Connection:
"""|coro| """|coro|
Edit the connection. Edit the connection.
@ -201,27 +230,48 @@ class Connection(PartialConnection):
The new name of the connection. Only editable for certain connection types. The new name of the connection. Only editable for certain connection types.
visible: :class:`bool` visible: :class:`bool`
Whether the connection is visible on your profile. Whether the connection is visible on your profile.
friend_sync: :class:`bool`
Whether friends are synced over the connection.
show_activity: :class:`bool` show_activity: :class:`bool`
Whether activities from this connection will be shown in presences. Whether activities from this connection will be shown in presences.
friend_sync: :class:`bool`
Whether friends are synced over the connection.
metadata_visible: :class:`bool`
Whether the connection's metadata is visible.
Raises Raises
------ ------
HTTPException HTTPException
Editing the connection failed. Editing the connection failed.
Returns
-------
:class:`Connection`
The edited connection.
""" """
payload = {} payload = {}
if name is not MISSING: if name is not MISSING:
payload['name'] = name payload['name'] = name
if visible is not MISSING: if visible is not MISSING:
payload['visibility'] = visible payload['visibility'] = visible
if friend_sync is not MISSING:
payload['friend_sync'] = friend_sync
if show_activity is not MISSING: if show_activity is not MISSING:
payload['show_activity'] = show_activity payload['show_activity'] = show_activity
if friend_sync is not MISSING:
payload['friend_sync'] = friend_sync
if metadata_visible is not MISSING:
payload['metadata_visibility'] = metadata_visible
data = await self._state.http.edit_connection(self.type.value, self.id, **payload) data = await self._state.http.edit_connection(self.type.value, self.id, **payload)
self._update(data) return Connection(data=data, state=self._state)
async def refresh(self) -> None:
"""|coro|
Refreshes the connection. This updates the connection's :attr:`metadata`.
Raises
------
HTTPException
Refreshing the connection failed.
"""
await self._state.http.refresh_connection(self.type.value, self.id)
async def delete(self) -> None: async def delete(self) -> None:
"""|coro| """|coro|
@ -251,5 +301,67 @@ class Connection(PartialConnection):
The new access token. The new access token.
""" """
data = await self._state.http.get_connection_token(self.type.value, self.id) data = await self._state.http.get_connection_token(self.type.value, self.id)
self.access_token = token = data['access_token'] return data['access_token']
return token
class ConnectionMetadata:
"""Represents a connection's metadata.
Because of how unstable and wildly varying this metadata can be, this is a simple class that just
provides access ro the raw data using dot notation. This means if an attribute is not present,
``None`` will be returned instead of raising an AttributeError.
.. versionadded:: 2.0
.. container:: operations
.. describe:: x == y
Checks if two metadata objects are equal.
.. describe:: x != y
Checks if two metadata objects are not equal.
.. describe:: x[key]
Returns a metadata value if it is found, otherwise raises a :exc:`KeyError`.
.. describe:: key in x
Checks if a metadata value is present.
.. describe:: iter(x)
Returns an iterator of ``(field, value)`` pairs. This allows this class
to be used as an iterable in list/dict/etc constructions.
"""
__slots__ = ()
def __init__(self, data: Optional[dict]) -> None:
self.__dict__.update(data or {})
def __repr__(self) -> str:
return f'<ConnectionMetadata {" ".join(f"{k}={v!r}" for k, v in self.__dict__.items())}>'
def __eq__(self, other: object) -> bool:
if not isinstance(other, ConnectionMetadata):
return False
return self.__dict__ == other.__dict__
def __ne__(self, other: object) -> bool:
if not isinstance(other, ConnectionMetadata):
return True
return self.__dict__ != other.__dict__
def __iter__(self) -> Iterator[Tuple[str, Any]]:
yield from self.__dict__.items()
def __getitem__(self, key: str) -> Any:
return self.__dict__[key]
def __getattr__(self, attr: str) -> Any:
return None
def __contains__(self, key: str) -> bool:
return key in self.__dict__

18
discord/enums.py

@ -80,6 +80,7 @@ __all__ = (
'ScheduledEventEntityType', 'ScheduledEventEntityType',
'ApplicationType', 'ApplicationType',
'ConnectionType', 'ConnectionType',
'ConnectionLinkType',
) )
if TYPE_CHECKING: if TYPE_CHECKING:
@ -877,13 +878,16 @@ class AppCommandType(Enum):
class ConnectionType(Enum): class ConnectionType(Enum):
battle_net = 'battlenet' battle_net = 'battlenet'
contacts = 'contacts' contacts = 'contacts'
ebay = 'ebay'
epic_games = 'epicgames' epic_games = 'epicgames'
facebook = 'facebook' facebook = 'facebook'
github = 'github' github = 'github'
league_of_legends = 'leagueoflegends' league_of_legends = 'leagueoflegends'
paypal = 'paypal'
playstation = 'playstation' playstation = 'playstation'
reddit = 'reddit' reddit = 'reddit'
samsung = 'samsunggalaxy' riot_games = 'riotgames'
samsung = 'samsung'
spotify = 'spotify' spotify = 'spotify'
skype = 'skype' skype = 'skype'
steam = 'steam' steam = 'steam'
@ -892,6 +896,18 @@ class ConnectionType(Enum):
youtube = 'youtube' youtube = 'youtube'
xbox = 'xbox' xbox = 'xbox'
def __str__(self) -> str:
return self.value
class ConnectionLinkType(Enum):
web = 'web'
mobile = 'mobile'
desktop = 'desktop'
def __str__(self) -> str:
return self.value
def create_unknown_value(cls: Type[E], val: Any) -> E: def create_unknown_value(cls: Type[E], val: Any) -> E:
value_cls = cls._enum_value_cls_ # type: ignore # This is narrowed below value_cls = cls._enum_value_cls_ # type: ignore # This is narrowed below

32
discord/http.py

@ -2184,16 +2184,40 @@ class HTTPClient:
# Connections # Connections
def get_connections(self): def get_connections(self) -> Response[List[user.Connection]]:
return self.request(Route('GET', '/users/@me/connections')) return self.request(Route('GET', '/users/@me/connections'))
def edit_connection(self, type: str, id: str, **payload): def edit_connection(self, type: str, id: str, **payload) -> Response[user.Connection]:
return self.request(Route('PATCH', '/users/@me/connections/{type}/{id}', type=type, id=id), json=payload) return self.request(Route('PATCH', '/users/@me/connections/{type}/{id}', type=type, id=id), json=payload)
def delete_connection(self, type: str, id: str): def refresh_connection(self, type: str, id: str, **payload) -> Response[None]:
return self.request(Route('POST', '/users/@me/connections/{type}/{id}/refresh', type=type, id=id), json=payload)
def delete_connection(self, type: str, id: str) -> Response[None]:
return self.request(Route('DELETE', '/users/@me/connections/{type}/{id}', type=type, id=id)) return self.request(Route('DELETE', '/users/@me/connections/{type}/{id}', type=type, id=id))
def get_connection_token(self, type: str, id: str): def authorize_connection(
self,
type: str,
two_way_link_type: Optional[str] = None,
continuation: bool = False,
) -> Response[user.ConnectionAuthorization]:
params = {}
if two_way_link_type is not None:
params['two_way_link'] = 'true'
params['two_way_link_type'] = two_way_link_type
if continuation:
params['continuation'] = 'true'
return self.request(Route('GET', '/connections/{type}/authorize', type=type), params=params)
def add_connection(
self,
type: str,
**payload,
) -> Response[None]:
return self.request(Route('POST', '/connections/{type}/callback', type=type), json=payload)
def get_connection_token(self, type: str, id: str) -> Response[user.ConnectionAccessToken]:
return self.request(Route('GET', '/users/@me/connections/{type}/{id}/access-token', type=type, id=id)) return self.request(Route('GET', '/users/@me/connections/{type}/{id}/access-token', type=type, id=id))
# Applications # Applications

22
discord/state.py

@ -79,6 +79,7 @@ from .member import _ClientStatus
from .modal import Modal from .modal import Modal
from .member import VoiceState from .member import VoiceState
from .appinfo import InteractionApplication from .appinfo import InteractionApplication
from .connections import Connection
if TYPE_CHECKING: if TYPE_CHECKING:
from .abc import PrivateChannel, Snowflake as abcSnowflake from .abc import PrivateChannel, Snowflake as abcSnowflake
@ -453,6 +454,7 @@ class ConnectionState:
self._users: weakref.WeakValueDictionary[int, User] = weakref.WeakValueDictionary() self._users: weakref.WeakValueDictionary[int, User] = weakref.WeakValueDictionary()
self.settings: Optional[UserSettings] = None self.settings: Optional[UserSettings] = None
self.consents: Optional[Tracking] = None self.consents: Optional[Tracking] = None
self.connections: Dict[str, Connection] = {}
self.analytics_token: Optional[str] = None self.analytics_token: Optional[str] = None
self.preferred_regions: List[str] = [] self.preferred_regions: List[str] = []
self.country_code: Optional[str] = None self.country_code: Optional[str] = None
@ -512,7 +514,8 @@ class ConnectionState:
@property @property
def session_id(self) -> Optional[str]: def session_id(self) -> Optional[str]:
return self.ws.session_id if self.ws:
return self.ws.session_id
@property @property
def ws(self): def ws(self):
@ -886,6 +889,7 @@ class ConnectionState:
self.consents = Tracking(data=data.get('consents', {}), state=self) self.consents = Tracking(data=data.get('consents', {}), state=self)
self.country_code = data.get('country_code', 'US') self.country_code = data.get('country_code', 'US')
self.session_type = data.get('session_type', 'normal') self.session_type = data.get('session_type', 'normal')
self.connections = {c['id']: Connection(state=self, data=c) for c in data.get('connected_accounts', [])}
if 'required_action' in data: # Locked more than likely if 'required_action' in data: # Locked more than likely
self.parse_user_required_action_update(data) self.parse_user_required_action_update(data)
@ -1088,8 +1092,20 @@ class ConnectionState:
required_action = try_enum(RequiredActionType, data['required_action']) required_action = try_enum(RequiredActionType, data['required_action'])
self.dispatch('required_action_update', required_action) self.dispatch('required_action_update', required_action)
def parse_user_connections_update(self, data: gw.ResumedEvent) -> None: def parse_user_connections_update(self, data: gw.Connection) -> None:
self.dispatch('connections_update') id = data['id']
if id not in self.connections:
self.connections[id] = connection = Connection(state=self, data=data)
self.dispatch('connection_create', connection)
else:
# TODO: We can also get to this point if the connection has been deleted
# We can detect that by checking if the payload is identical to the previous payload
# However, certain events can also trigger updates with identical payloads, so we can't rely on that
# For now, we assume everything is an update; thanks Discord
connection = self.connections[id]
old_connection = copy.copy(connection)
connection._update(data)
self.dispatch('connection_update', old_connection, connection)
def parse_sessions_replace(self, data: List[Dict[str, Any]]) -> None: def parse_sessions_replace(self, data: List[Dict[str, Any]]) -> None:
overall = MISSING overall = MISSING

4
discord/types/gateway.py

@ -39,7 +39,7 @@ from .message import Message
from .sticker import GuildSticker from .sticker import GuildSticker
from .appinfo import PartialAppInfo from .appinfo import PartialAppInfo
from .guild import Guild, UnavailableGuild, SupplementalGuild from .guild import Guild, UnavailableGuild, SupplementalGuild
from .user import User from .user import Connection, User
from .threads import Thread, ThreadMember from .threads import Thread, ThreadMember
from .scheduled_event import GuildScheduledEvent from .scheduled_event import GuildScheduledEvent
from .channel import DMChannel, GroupDMChannel from .channel import DMChannel, GroupDMChannel
@ -60,7 +60,7 @@ class ShardInfo(TypedDict):
class ReadyEvent(TypedDict): class ReadyEvent(TypedDict):
analytics_token: str analytics_token: str
auth_token: NotRequired[str] auth_token: NotRequired[str]
connected_accounts: List[dict] connected_accounts: List[Connection]
country_code: str country_code: str
friend_suggestion_count: int friend_suggestion_count: int
geo_ordered_rtc_regions: List[str] geo_ordered_rtc_regions: List[str]

22
discord/types/user.py

@ -22,7 +22,7 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
""" """
from typing import List, Literal, Optional, TypedDict from typing import Any, Dict, List, Literal, Optional, TypedDict
from typing_extensions import NotRequired from typing_extensions import NotRequired
from .integration import ConnectionIntegration from .integration import ConnectionIntegration
@ -39,13 +39,16 @@ class PartialUser(TypedDict):
ConnectionType = Literal[ ConnectionType = Literal[
'battlenet', 'battlenet',
'contacts', 'contacts',
'ebay',
'epicgames', 'epicgames',
'facebook', 'facebook',
'github', 'github',
'leagueoflegends', 'leagueoflegends',
'paypal',
'playstation', 'playstation',
'reddit', 'reddit',
'samsunggalaxy', 'riotgames',
'samsung',
'spotify', 'spotify',
'skype', 'skype',
'steam', 'steam',
@ -81,12 +84,23 @@ class PartialConnection(TypedDict):
type: ConnectionType type: ConnectionType
name: str name: str
verified: bool verified: bool
revoked: NotRequired[bool]
class Connection(PartialConnection): class Connection(PartialConnection):
visibility: int revoked: bool
visibility: Literal[0, 1]
metadata_visibility: Literal[0, 1]
show_activity: bool show_activity: bool
friend_sync: bool friend_sync: bool
two_way_link: bool
integrations: NotRequired[List[ConnectionIntegration]] integrations: NotRequired[List[ConnectionIntegration]]
access_token: NotRequired[str] access_token: NotRequired[str]
metadata: NotRequired[Dict[str, Any]]
class ConnectionAccessToken(TypedDict):
access_token: str
class ConnectionAuthorization(TypedDict):
url: str

62
docs/api.rst

@ -387,7 +387,7 @@ Client
.. function:: on_guild_settings_update(before, after) .. function:: on_guild_settings_update(before, after)
Called when a :class:`.Guild`'s :class:`GuildSettings` updates, for example: Called when a :class:`.Guild` :class:`GuildSettings` updates, for example:
- Muted guild or channel - Muted guild or channel
- Changed notification settings - Changed notification settings
@ -411,13 +411,32 @@ Client
:param action: The action required. :param action: The action required.
:type action: :class:`RequiredActionType` :type action: :class:`RequiredActionType`
.. function:: on_connections_update() Connections
~~~~~~~~~~~~
.. function:: on_connection_create(connection)
Called when your account connections are updated. Called when a connection is added to your account.
The updated connections are not provided and must be fetched by :meth:`Client.connections`.
.. versionadded:: 2.0 .. versionadded:: 2.0
:param connection: The connection that was added.
:type connection: :class:`Connection`
.. function:: on_connection_update(before, after)
Called when a connection is updated on your account.
.. note::
Due to a Discord limitation, this is also called when a connection is removed.
.. versionadded:: 2.0
:param before: The connection prior to being updated.
:type before: :class:`Connection`
:param after: The connection after being updated.
:type after: :class:`Connection`
Relationships Relationships
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
@ -3167,6 +3186,10 @@ of :class:`enum.Enum`.
The user has a contact sync connection. The user has a contact sync connection.
.. attribute:: ebay
The user has an eBay connection.
.. attribute:: epic_games .. attribute:: epic_games
The user has an Epic Games connection. The user has an Epic Games connection.
@ -3183,6 +3206,10 @@ of :class:`enum.Enum`.
The user has a League of Legends connection. The user has a League of Legends connection.
.. attribute:: paypal
The user has a PayPal connection.
.. attribute:: playstation .. attribute:: playstation
The user has a PlayStation connection. The user has a PlayStation connection.
@ -3191,6 +3218,10 @@ of :class:`enum.Enum`.
The user has a Reddit connection. The user has a Reddit connection.
.. attribute:: riot_games
The user has a Riot Games connection.
.. attribute:: samsung .. attribute:: samsung
The user has a Samsung Account connection. The user has a Samsung Account connection.
@ -3223,6 +3254,24 @@ of :class:`enum.Enum`.
The user has an Xbox Live connection. The user has an Xbox Live connection.
.. class:: ConnectionLinkType
Represents the type of two-way link a Discord connection has.
.. versionadded:: 2.0
.. attribute:: web
The connection is linked via web.
.. attribute:: mobile
The connection is linked via mobile.
.. attribute:: desktop
The connection is linked via desktop.
.. class:: InteractionType .. class:: InteractionType
Specifies the type of :class:`Interaction`. Specifies the type of :class:`Interaction`.
@ -4174,6 +4223,11 @@ Connection
.. autoclass:: PartialConnection() .. autoclass:: PartialConnection()
:members: :members:
.. attributetable:: ConnectionMetadata
.. autoclass:: ConnectionMetadata()
:members:
Application Application
~~~~~~~~~~~ ~~~~~~~~~~~

Loading…
Cancel
Save