diff --git a/discord/__init__.py b/discord/__init__.py index 023d40ef0..6ed1ec6a3 100644 --- a/discord/__init__.py +++ b/discord/__init__.py @@ -55,6 +55,7 @@ from .audit_logs import AuditLogChanges, AuditLogEntry, AuditLogDiff from .raw_models import * from .team import * from .sticker import Sticker +from .interactions import * VersionInfo = namedtuple('VersionInfo', 'major minor micro releaselevel serial') diff --git a/discord/enums.py b/discord/enums.py index 262289a65..cc616104e 100644 --- a/discord/enums.py +++ b/discord/enums.py @@ -418,6 +418,10 @@ class StickerType(Enum): apng = 2 lottie = 3 +class InteractionType(Enum): + ping = 1 + application_command = 2 + def try_enum(cls, val): """A function that tries to turn the value into enum ``cls``. diff --git a/discord/interactions.py b/discord/interactions.py new file mode 100644 index 000000000..a235a0a6f --- /dev/null +++ b/discord/interactions.py @@ -0,0 +1,104 @@ +# -*- coding: utf-8 -*- + +""" +The MIT License (MIT) + +Copyright (c) 2015-present 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 __future__ import annotations + +from . import utils +from .enums import try_enum, InteractionType + +__all__ = ( + 'Interaction', +) + +class Interaction: + """Represents a Discord interaction. + + An interaction happens when a user does an action that needs to + be notified. Current examples are slash commands but future examples + include forms and buttons. + + .. versionadded:: 2.0 + + Attributes + ----------- + id: :class:`int` + The interaction's ID. + type: :class:`InteractionType` + The interaction type. + guild_id: Optional[:class:`int`] + The guild ID the interaction was sent from. + channel_id: Optional[:class:`int`] + The channel ID the interaction was sent from. + application_id: :class:`int` + The application ID that the interaction was for. + user: Optional[Union[:class:`User`, :class:`Member`]] + The user or member that sent the interaction. + token: :class:`str` + The token to continue the interaction. These are valid + for 15 minutes. + """ + __slots__ = ( + 'id', + 'type', + 'guild_id', + 'channel_id', + 'data', + 'application_id', + 'user', + 'token', + 'version', + '_state', + ) + + def __init__(self, *, data, state=None): + self._state = state + self._from_data(data) + + def _from_data(self, data): + self.id = int(data['id']) + self.type = try_enum(InteractionType, data['type']) + self.data = data.get('data') + self.token = data['token'] + self.version = data['version'] + self.channel_id = utils._get_as_snowflake(data, 'channel_id') + self.guild_id = utils._get_as_snowflake(data, 'guild_id') + self.application_id = utils._get_as_snowflake(data, 'application_id') + + @property + def guild(self): + """Optional[:class:`Guild`]: The guild the interaction was sent from.""" + return self._state and self._state.get_guild(self.guild_id) + + @property + def channel(self): + """Optional[:class:`abc.GuildChannel`]: The channel the interaction was sent from. + + Note that due to a Discord limitation, DM channels are not resolved since there is + no data to complete them. + """ + guild = self.guild + return guild and guild.get_channel(self.channel_id) + diff --git a/discord/state.py b/discord/state.py index 2aa12d68e..82f5371ca 100644 --- a/discord/state.py +++ b/discord/state.py @@ -51,6 +51,7 @@ from . import utils from .flags import Intents, MemberCacheFlags from .object import Object from .invite import Invite +from .interactions import Interaction class ChunkRequest: def __init__(self, guild_id, loop, resolver, *, cache=True): @@ -586,6 +587,10 @@ class ConnectionState: if reaction: self.dispatch('reaction_clear_emoji', reaction) + def parse_interaction_create(self, data): + interaction = Interaction(data=data, state=self) + self.dispatch('interaction', interaction) + def parse_presence_update(self, data): guild_id = utils._get_as_snowflake(data, 'guild_id') guild = self._get_guild(guild_id) diff --git a/docs/api.rst b/docs/api.rst index b328ab22a..859f1e259 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -585,6 +585,17 @@ to handle it, which defaults to print a traceback and ignoring the exception. :param payload: The raw event payload data. :type payload: :class:`RawReactionClearEmojiEvent` +.. function:: on_interaction(interaction) + + Called when an interaction happened. + + This currently happens due to slash command invocations. + + .. versionadded:: 2.0 + + :param interaction: The interaction data. + :type interaction: :class:`Interaction` + .. function:: on_private_channel_delete(channel) on_private_channel_create(channel) @@ -1089,6 +1100,20 @@ of :class:`enum.Enum`. .. versionadded:: 1.5 +.. class:: InteractionType + + Specifies the type of :class:`Interaction`. + + .. versionadded:: 2.0 + + .. attribute:: ping + + Represents Discord pinging to see if the interaction response server is alive. + + .. attribute:: application_command + + Represents a slash command interaction. + .. class:: HypeSquadHouse Specifies the HypeSquad house a user belongs to. @@ -2699,6 +2724,14 @@ Integration .. autoclass:: IntegrationAccount() :members: +Interaction +~~~~~~~~~~~~ + +.. attributetable:: Interaction + +.. autoclass:: Interaction() + :members: + Member ~~~~~~