aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNadir Chowdhury <[email protected]>2020-06-28 19:50:43 +0100
committerGitHub <[email protected]>2020-06-28 14:50:43 -0400
commita64006ee9bf32b5a8e353db7399c9ff868afcb3d (patch)
tree8056865d84b50674a00e4200ae21a8ea38274d5d
parentAdd support for Discord templates (diff)
downloaddiscord.py-a64006ee9bf32b5a8e353db7399c9ff868afcb3d.tar.xz
discord.py-a64006ee9bf32b5a8e353db7399c9ff868afcb3d.zip
Add support for integrations
-rw-r--r--discord/__init__.py1
-rw-r--r--discord/enums.py8
-rw-r--r--discord/guild.py96
-rw-r--r--discord/http.py32
-rw-r--r--discord/integrations.py202
-rw-r--r--docs/api.rst31
6 files changed, 369 insertions, 1 deletions
diff --git a/discord/__init__.py b/discord/__init__.py
index 589a1629..5e07acec 100644
--- a/discord/__init__.py
+++ b/discord/__init__.py
@@ -39,6 +39,7 @@ from .permissions import Permissions, PermissionOverwrite
from .role import Role
from .file import File
from .colour import Color, Colour
+from .integrations import Integration, IntegrationAccount
from .invite import Invite, PartialInviteChannel, PartialInviteGuild
from .template import Template
from .widget import Widget, WidgetMember, WidgetChannel
diff --git a/discord/enums.py b/discord/enums.py
index 6859a1c7..87fc7322 100644
--- a/discord/enums.py
+++ b/discord/enums.py
@@ -50,6 +50,8 @@ __all__ = (
'TeamMembershipState',
'Theme',
'WebhookType',
+ 'ExpireBehaviour',
+ 'ExpireBehavior'
)
def _create_value_cls(name):
@@ -432,6 +434,12 @@ class WebhookType(Enum):
incoming = 1
channel_follower = 2
+class ExpireBehaviour(Enum):
+ remove_role = 0
+ kick = 1
+
+ExpireBehavior = ExpireBehaviour
+
def try_enum(cls, val):
"""A function that tries to turn the value into enum ``cls``.
diff --git a/discord/guild.py b/discord/guild.py
index fdad7389..a31acbbe 100644
--- a/discord/guild.py
+++ b/discord/guild.py
@@ -46,6 +46,8 @@ from .webhook import Webhook
from .widget import Widget
from .asset import Asset
from .flags import SystemChannelFlags
+from .integrations import Integration
+
BanEntry = namedtuple('BanEntry', 'reason user')
_GuildLimit = namedtuple('_GuildLimit', 'emoji bitrate filesize')
@@ -1535,6 +1537,100 @@ class Guild(Hashable):
data = await self._state.http.get_custom_emoji(self.id, emoji_id)
return Emoji(guild=self, state=self._state, data=data)
+ async def create_integration(self, *, type, id):
+ """|coro|
+
+ Attaches an integration to the guild.
+
+ You must have the :attr:`~Permissions.manage_guild` permission to
+ do this.
+
+ .. versionadded:: 1.4
+
+ Parameters
+ -----------
+ type: :class:`str`
+ The integration type (e.g. Twitch).
+ id: :class:`int`
+ The integration ID.
+
+ Raises
+ -------
+ Forbidden
+ You do not have permission to create the integration.
+ HTTPException
+ The account could not be found.
+ """
+ await self._state.http.create_integration(self.id, type, id)
+
+ async def integrations(self):
+ """|coro|
+
+ Returns a list of all integrations attached to the guild.
+
+ You must have the :attr:`~Permissions.manage_guild` permission to
+ do this.
+
+ .. versionadded:: 1.4
+
+ Raises
+ -------
+ Forbidden
+ You do not have permission to create the integration.
+ HTTPException
+ Fetching the integrations failed.
+
+ Returns
+ --------
+ List[:class:`Integration`]
+ 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]
+
+ async def fetch_emojis(self):
+ """|coro|
+
+ Retrieves all custom :class:`Emoji`s from the guild.
+
+ Raises
+ ---------
+ HTTPException
+ An error occurred fetching the emojis.
+
+ Returns
+ --------
+ List[:class:`Emoji`]
+ The retrieved emojis.
+ """
+ data = await self._state.http.get_all_custom_emojis(self.id)
+ return [Emoji(guild=self, state=self._state, data=d) for d in data]
+
+ async def fetch_emoji(self, emoji_id):
+ """|coro|
+
+ Retrieves a custom :class:`Emoji` from the guild.
+
+ Parameters
+ -------------
+ emoji_id: :class:`int`
+ The emoji's ID.
+
+ Raises
+ ---------
+ NotFound
+ The emoji requested could not be found.
+ HTTPException
+ An error occurred fetching the emoji.
+
+ Returns
+ --------
+ :class:`Emoji`
+ The retrieved emoji.
+ """
+ data = await self._state.http.get_custom_emoji(self.id, emoji_id)
+ return Emoji(guild=self, state=self._state, data=data)
+
async def create_custom_emoji(self, *, name, image, roles=None, reason=None):
r"""|coro|
diff --git a/discord/http.py b/discord/http.py
index c5650c24..1ad5df26 100644
--- a/discord/http.py
+++ b/discord/http.py
@@ -712,6 +712,38 @@ class HTTPClient:
r = Route('PATCH', '/guilds/{guild_id}/emojis/{emoji_id}', guild_id=guild_id, emoji_id=emoji_id)
return self.request(r, json=payload, reason=reason)
+ def get_all_integrations(self, guild_id):
+ r = Route('GET', '/guilds/{guild_id}/integrations', guild_id=guild_id)
+
+ return self.request(r)
+
+ def create_integration(self, guild_id, type, id):
+ payload = {
+ 'type': type,
+ 'id': id
+ }
+
+ r = Route('POST', '/guilds/{guild_id}/integrations', guild_id=guild_id)
+ return self.request(r, json=payload)
+
+ def edit_integration(self, guild_id, integration_id, **payload):
+ r = Route('PATCH', '/guilds/{guild_id}/integrations/{integration_id}', guild_id=guild_id,
+ integration_id=integration_id)
+
+ return self.request(r, json=payload)
+
+ def sync_integration(self, guild_id, integration_id):
+ r = Route('POST', '/guilds/{guild_id}/integrations/{integration_id}/sync', guild_id=guild_id,
+ integration_id=integration_id)
+
+ return self.request(r)
+
+ def delete_integration(self, guild_id, integration_id):
+ r = Route('DELETE', '/guilds/{guild_id}/integrations/{integration_id}', guild_id=guild_id,
+ integration_id=integration_id)
+
+ return self.request(r)
+
def get_audit_logs(self, guild_id, limit=100, before=None, after=None, user_id=None, action_type=None):
params = {'limit': limit}
if before:
diff --git a/discord/integrations.py b/discord/integrations.py
new file mode 100644
index 00000000..2035b24d
--- /dev/null
+++ b/discord/integrations.py
@@ -0,0 +1,202 @@
+# -*- coding: utf-8 -*-
+
+"""
+The MIT License (MIT)
+
+Copyright (c) 2015-2020 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.
+"""
+
+import datetime
+from collections import namedtuple
+from .utils import _get_as_snowflake, get, parse_time
+from .user import User
+from .errors import InvalidArgument
+from .enums import try_enum, ExpireBehaviour
+
+class IntegrationAccount(namedtuple('IntegrationAccount', 'id name')):
+ """Represents an integration account.
+
+ .. versionadded:: 1.4
+
+ Attributes
+ -----------
+ id: :class:`int`
+ The account ID.
+ name: :class:`str`
+ The account name.
+ """
+
+ __slots__ = ()
+
+ def __repr__(self):
+ return '<IntegrationAccount id={0.id} name={0.name!r}>'.format(self)
+
+class Integration:
+ """Represents a guild integration.
+
+ .. versionadded:: 1.4
+
+ 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.
+ syncing: :class:`bool`
+ Where the integration is currently syncing.
+ role: :class:`Role`
+ The role which the integration uses for subscribers.
+ enable_emoticons: :class:`bool`
+ Whether emoticons should be synced for this integration (currently twitch only).
+ expire_behaviour: :class:`ExpireBehaviour`
+ The behaviour of expiring subscribers. Aliased to ``expire_behavior`` as well.
+ expire_grace_period: :class:`int`
+ The grace period (in days) for expiring subscribers.
+ user: :class:`User`
+ The user for the integration.
+ account: :class:`IntegrationAccount`
+ The integration account information.
+ synced_at: :class:`datetime.datetime`
+ When the integration was last synced.
+ """
+
+ __slots__ = ('id', '_state', 'guild', 'name', 'enabled', 'type',
+ 'syncing', 'role', 'expire_behaviour', 'expire_behavior',
+ 'expire_grace_period', 'synced_at', 'user', 'account')
+
+ def __init__(self, *, data, guild):
+ self.guild = guild
+ self._state = guild._state
+ self._from_data(data)
+
+ def __repr__(self):
+ return '<Integration id={0.id} name={0.name!r} type={0.type!r}>'.format(self)
+
+ def _from_data(self, integ):
+ 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'])
+
+ async def edit(self, **fields):
+ """|coro|
+
+ Edits the integration.
+
+ You must have the :attr:`~Permissions.manage_guild` permission to
+ do this.
+
+ Parameters
+ -----------
+ expire_behaviour: :class:`ExpireBehaviour`
+ The behaviour when an integration subscription lapses. Aliased to ``expire_behavior`` as well.
+ expire_grace_period: :class:`int`
+ The period (in days) where the integration will ignore lapsed subscriptions.
+ enable_emoticons: :class:`bool`
+ Where emoticons should be synced for this integration (currently twitch only).
+
+ Raises
+ -------
+ Forbidden
+ You do not have permission to edit the integration.
+ HTTPException
+ Editing the guild failed.
+ InvalidArgument
+ ``expire_behaviour`` did not receive a :class:`ExpireBehaviour`.
+ """
+ try:
+ expire_behaviour = fields['expire_behaviour']
+ except KeyError:
+ expire_behaviour = fields.get('expire_behavior', self.expire_behaviour)
+
+ if not isinstance(expire_behaviour, ExpireBehaviour):
+ raise InvalidArgument('expire_behaviour field must be of type ExpireBehaviour')
+
+ expire_grace_period = fields.get('expire_grace_period', self.expire_grace_period)
+
+ payload = {
+ 'expire_behavior': expire_behaviour.value,
+ 'expire_grace_period': expire_grace_period,
+ }
+
+ enable_emoticons = fields.get('enable_emoticons')
+
+ if enable_emoticons is not None:
+ payload['enable_emoticons'] = enable_emoticons
+
+ await self._state.http.edit_integration(self.guild.id, self.id, **payload)
+
+ self.expire_behaviour = expire_behavior
+ self.expire_behavior = self.expire_behaviour
+ self.expire_grace_period = expire_grace_period
+ self.enable_emoticons = enable_emoticons
+
+ async def sync(self):
+ """|coro|
+
+ Syncs the integration.
+
+ You must have the :attr:`~Permissions.manage_guild` permission to
+ do this.
+
+ Raises
+ -------
+ Forbidden
+ You do not have permission to sync the integration.
+ HTTPException
+ Syncing the integration failed.
+ """
+ await self._state.http.sync_integration(self.guild.id, self.id)
+ self.synced_at = datetime.datetime.utcnow()
+
+ async def delete(self):
+ """|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)
diff --git a/docs/api.rst b/docs/api.rst
index a9a3a468..8b4439d2 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -480,6 +480,8 @@ to handle it, which defaults to print a traceback and ignoring the exception.
.. function:: on_guild_integrations_update(guild)
+ .. versionadded:: 1.4
+
Called whenever an integration is created, modified, or removed from a guild.
:param guild: The guild that had its integrations updated.
@@ -1670,7 +1672,6 @@ of :class:`enum.Enum`.
The action is the update of something.
-
.. class:: RelationshipType
Specifies the type of :class:`Relationship`.
@@ -1810,6 +1811,24 @@ of :class:`enum.Enum`.
Represents a webhook that is internally managed by Discord, used for following channels.
+.. class:: ExpireBehaviour
+
+ Represents the behaviour the :class:`Integration` should perform
+ when a user's subscription has finished.
+
+ There is an alias for this called ``ExpireBehavior``.
+
+ .. versionadded:: 1.4
+
+ .. attribute:: remove_role
+
+ This will remove the :attr:`Integration.role` from the user
+ when their subscription is finished.
+
+ .. attribute:: kick
+
+ This will kick the user when their subscription is finished.
+
.. class:: DefaultAvatar
Represents the default avatar of a Discord :class:`User`
@@ -1838,6 +1857,7 @@ of :class:`enum.Enum`.
Represents the default avatar with the color red.
See also :attr:`Colour.red`
+
Async Iterator
----------------
@@ -2507,6 +2527,15 @@ Guild
.. automethod:: audit_logs
:async-for:
+Integration
+~~~~~~~~~~~~
+
+.. autoclass:: Integration()
+ :members:
+
+.. autoclass:: IntegrationAccount()
+ :members:
+
Member
~~~~~~