Browse Source

Handle interaction events

pull/10109/head
dolfies 4 years ago
parent
commit
6b0b510818
  1. 51
      discord/components.py
  2. 2
      discord/message.py
  3. 23
      discord/state.py

51
discord/components.py

@ -24,10 +24,12 @@ DEALINGS IN THE SOFTWARE.
from __future__ import annotations
from asyncio import TimeoutError
from datetime import datetime
from typing import Any, ClassVar, Dict, List, Optional, TYPE_CHECKING, Tuple, Type, TypeVar, Union
from .enums import try_enum, ComponentType, ButtonStyle
from .errors import InvalidData
from .utils import get_slots, MISSING, time_snowflake
from .partial_emoji import PartialEmoji, _EmojiTag
@ -54,6 +56,32 @@ __all__ = (
C = TypeVar('C', bound='Component')
class Interaction:
"""Represents an interaction.
Attributes
------------
id: :class:`int`
The interaction ID.
nonce: Optional[Union[:class:`int`, :class:`str`]]
The interaction's nonce.
successful: Optional[:class:`bool`]
Whether the interaction succeeded.
This is not immediately available, and is filled when Discord notifies us about the outcome of the interaction.
"""
__slots__ = ('id', 'nonce', 'successful')
def __init__(self, *, id: int, nonce: Optional[Union[int, str]] = None) -> None:
self.id = id
self.nonce = nonce
self.successful: Optional[bool] = None
def __repr__(self) -> str:
s = self.successful
return f'<Interaction id={self.id}{f" successful={s}" if s is not None else ""}>'
class Component:
"""Represents a Discord Bot UI Kit Component.
@ -210,10 +238,21 @@ class Button(Component):
Raises
-------
InvalidData
Didn't receive a response from Discord.
Doesn't mean the interaction failed.
NotFound
The originating message was not found.
HTTPException
Clicking the button failed.
Returns
--------
:class:`Interaction`
The interaction that was created.
"""
message = self.message
state = message._state
payload = {
'application_id': str(message.application_id),
'channel_id': str(message.channel.id),
@ -227,7 +266,17 @@ class Button(Component):
'nonce': str(time_snowflake(datetime.utcnow())),
'type': 3, # Should be an enum but eh
}
await message._state.http.interact(payload) # type: ignore
await state.http.interact(payload)
try:
i = await state.client.wait_for(
'interaction',
check=lambda d: d.nonce == payload['nonce'],
timeout=5,
)
except TimeoutError as exc:
raise InvalidData('Did not receive a response from Discord.') from exc
return i
class SelectMenu(Component):

2
discord/message.py

@ -518,7 +518,7 @@ class Message(Hashable):
content: :class:`str`
The actual contents of the message.
nonce: Optional[Union[:class:`str`, :class:`int`]]
The value used by the discord guild and the client to verify that the message is successfully sent.
The value used by Discord clients to verify that the message is successfully sent.
This is not stored long term within Discord's servers and is only used ephemerally.
embeds: List[:class:`Embed`]
A list of embeds the message has.

23
discord/state.py

@ -59,6 +59,7 @@ from .threads import Thread, ThreadMember
from .sticker import GuildSticker
from .settings import UserSettings
from .tracking import Tracking
from .components import Interaction
if TYPE_CHECKING:
@ -262,6 +263,7 @@ class ConnectionState:
self._voice_clients: Dict[int, VoiceProtocol] = {}
self._voice_states: Dict[int, VoiceState] = {}
self._interactions: Dict[int, Interaction] = {}
self._relationships: Dict[int, Relationship] = {}
self._private_channels: Dict[int, PrivateChannel] = {}
self._private_channels_by_user: Dict[int, DMChannel] = {}
@ -1609,6 +1611,27 @@ class ConnectionState:
else:
self.dispatch('relationship_remove', old)
def parse_interaction_create(self, data) -> None:
i = Interaction(**data)
self._interactions[i.id] = i
self.dispatch('interaction', i)
def parse_interaction_success(self, data) -> None:
id = int(data['id'])
i = self._interactions.pop(id, None)
if i is None:
i = Interaction(**data)
i.successful = True
self.dispatch('interaction_finish', i)
def parse_interaction_failed(self, data) -> None:
id = int(data['id'])
i = self._interactions.pop(id, None)
if i is None:
i = Interaction(**data)
i.successful = False
self.dispatch('interaction_finish', i)
def _get_reaction_user(self, channel: MessageableChannel, user_id: int) -> Optional[Union[User, Member]]:
if isinstance(channel, TextChannel):
return channel.guild.get_member(user_id)

Loading…
Cancel
Save