diff --git a/discord/guild.py b/discord/guild.py index 1d5baf505..c2f8f0de3 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -45,7 +45,7 @@ from .iterators import AuditLogIterator, MemberIterator from .widget import Widget from .asset import Asset from .flags import SystemChannelFlags -from .integrations import Integration +from .integrations import BotIntegration, StreamIntegration, _integration_factory __all__ = ( 'Guild', @@ -1803,7 +1803,14 @@ class Guild(Hashable): The list of integrations that are attached to the guild. """ data = await self._state.http.get_all_integrations(self.id) - return [Integration(guild=self, data=d) for d in data] + + def convert(d): + factory, itype = _integration_factory(d['type']) + if factory is None: + raise InvalidData('Unknown integration type {type!r} for integration ID {id}'.format_map(d)) + return factory(guild=self, data=d) + + return [convert(d) for d in data] async def fetch_emojis(self): r"""|coro| diff --git a/discord/integrations.py b/discord/integrations.py index 8a804bc3a..52f06c30e 100644 --- a/discord/integrations.py +++ b/discord/integrations.py @@ -27,19 +27,25 @@ from __future__ import annotations import datetime from typing import Optional, TYPE_CHECKING, overload from .utils import _get_as_snowflake, get, parse_time +from .role import Role from .user import User from .errors import InvalidArgument from .enums import try_enum, ExpireBehaviour __all__ = ( 'IntegrationAccount', + 'IntegrationApplication', 'Integration', + 'StreamIntegration', + 'BotIntegration', ) if TYPE_CHECKING: from .types.integration import ( IntegrationAccount as IntegrationAccountPayload, Integration as IntegrationPayload, + IntegrationType, + IntegrationApplication as IntegrationApplicationPayload, ) from .guild import Guild @@ -74,6 +80,75 @@ class Integration: Attributes ----------- + id: :class:`int` + The integration ID. + name: :class:`str` + The integration name. + guild: :class:`Guild` + The guild of the integration. + type: :class:`str` + The integration type (i.e. Twitch). + enabled: :class:`bool` + Whether the integration is currently enabled. + account: :class:`IntegrationAccount` + The account linked to this integration. + user: :class:`User` + The user that added this integration. + """ + + __slots__ = ( + 'guild', + 'id', + '_state', + 'type', + 'name', + 'account', + 'user', + 'enabled', + ) + + def __init__(self, *, data: IntegrationPayload, guild: Guild) -> None: + self.guild = guild + self._state = guild._state + self._from_data(data) + + def __repr__(self): + return f"<{self.__class__.__name__} id={self.id} name={self.name!r}>" + + def _from_data(self, data: IntegrationPayload) -> None: + self.id: int = int(data['id']) + self.type: IntegrationType = data['type'] + self.name: str = data['name'] + self.account: IntegrationAccount = IntegrationAccount(data['account']) + + user = data.get('user') + self.user = User(state=self._state, data=user) if user else None + self.enabled: bool = data['enabled'] + + async def delete(self) -> None: + """|coro| + + Deletes the integration. + + You must have the :attr:`~Permissions.manage_guild` permission to + do this. + + Raises + ------- + Forbidden + You do not have permission to delete the integration. + HTTPException + Deleting the integration failed. + """ + await self._state.http.delete_integration(self.guild.id, self.id) + +class StreamIntegration(Integration): + """Represents a stream integration for Twitch or YouTube. + + .. versionadded:: 2.0 + + Attributes + ---------- id: :class:`int` The integration ID. name: :class:`str` @@ -102,49 +177,30 @@ class Integration: An aware UTC datetime representing when the integration was last synced. """ - __slots__ = ( - 'id', - '_state', - 'guild', - 'name', - 'enabled', - 'type', - 'syncing', - 'role', + __slots__ = Integration.__slots__ + ( + 'revoked', 'expire_behaviour', 'expire_behavior', 'expire_grace_period', 'synced_at', - 'user', - 'account', - 'enable_emoticons', '_role_id', + 'role', + 'syncing', + 'enable_emoticons', + 'subscriber_count' ) - def __init__(self, *, data: IntegrationPayload, guild: Guild) -> None: - self.guild = guild - self._state = guild._state - self._from_data(data) - - def __repr__(self) -> str: - return f'' - - def _from_data(self, integ: IntegrationPayload): - self.id = _get_as_snowflake(integ, 'id') - self.name = integ['name'] - self.type = integ['type'] - self.enabled = integ['enabled'] - self.syncing = integ['syncing'] - self._role_id = _get_as_snowflake(integ, 'role_id') - self.role = get(self.guild.roles, id=self._role_id) - self.enable_emoticons = integ.get('enable_emoticons') - self.expire_behaviour = try_enum(ExpireBehaviour, integ['expire_behavior']) - self.expire_behavior = self.expire_behaviour - self.expire_grace_period = integ['expire_grace_period'] - self.synced_at = parse_time(integ['synced_at']) - - self.user = User(state=self._state, data=integ['user']) - self.account = IntegrationAccount(integ['account']) + def _from_data(self, data: IntegrationPayload) -> None: + super()._from_data(data) + self.revoked: bool = data['revoked'] + self.expire_behaviour: ExpireBehaviour = try_enum(ExpireBehaviour, data['expire_behavior']) + self.expire_grace_period: int = data['expire_grace_period'] + self.synced_at: datetime.datetime = parse_time(data['synced_at']) + self._role_id: int = int(data['role_id']) + self.role: Role = self.guild.get_role(self._role_id) + self.syncing: bool = data['syncing'] + self.enable_emoticons: bool = data['enable_emoticons'] + self.subscriber_count: int = data['subscriber_count'] @overload async def edit( @@ -231,19 +287,81 @@ class Integration: await self._state.http.sync_integration(self.guild.id, self.id) self.synced_at = datetime.datetime.now(datetime.timezone.utc) - async def delete(self) -> None: - """|coro| +class IntegrationApplication: + """Represents an application for a bot integration. - Deletes the integration. + .. versionadded:: 2.0 - You must have the :attr:`~Permissions.manage_guild` permission to - do this. + Attributes + ---------- + id: :class:`int` + The ID for this application. + name: :class:`str` + The application's name. + icon: Optional[:class:`str`] + The application's icon hash. + description: :class:`str` + The application's description. Can be an empty string. + summary: :class:`str` + The summary of the application. Can be an empty string. + user: Optional[:class:`User`] + The bot user on this application. + """ - Raises - ------- - Forbidden - You do not have permission to delete the integration. - HTTPException - Deleting the integration failed. - """ - await self._state.http.delete_integration(self.guild.id, self.id) + __slots__ = ( + 'id', + 'name', + 'icon', + 'description', + 'summary', + 'user', + ) + + def __init__(self, *, data: IntegrationApplicationPayload, state): + self.id: int = int(data['id']) + self.name: str = data['name'] + self.icon: Optional[str] = data['icon'] + self.description: str = data['description'] + self.summary: str = data['summary'] + user = data.get('bot') + self.user: Optional[User] = User(state=state, data=user) if user else None + +class BotIntegration(Integration): + """Represents a bot integration on discord. + + .. versionadded:: 2.0 + + Attributes + ---------- + id: :class:`int` + The integration ID. + name: :class:`str` + The integration name. + guild: :class:`Guild` + The guild of the integration. + type: :class:`str` + The integration type (i.e. Twitch). + enabled: :class:`bool` + Whether the integration is currently enabled. + user: :class:`User` + The user that added this integration. + account: :class:`IntegrationAccount` + The integration account information. + application: :class:`IntegrationApplication` + The application tied to this integration. + """ + + __slots__ = Integration.__slots__ + ('application',) + + def _from_data(self, data: IntegrationPayload) -> None: + super()._from_data(data) + self.application = IntegrationApplication(data=data['application'], state=self._state) + + +def _integration_factory(value): + if value == 'discord': + return BotIntegration, value + elif value in ('twitch', 'youtube'): + return StreamIntegration, value + else: + return Integration, value diff --git a/docs/api.rst b/docs/api.rst index 649e8d339..3971d4b7e 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -2986,6 +2986,15 @@ Integration .. autoclass:: IntegrationAccount() :members: +.. autoclass:: BotIntegration() + :members: + +.. autoclass:: IntegrationApplication() + :members: + +.. autoclass:: StreamIntegration() + :members: + Interaction ~~~~~~~~~~~~