From bac74af565d0ec44911c635cfd7cb54e476ff483 Mon Sep 17 00:00:00 2001 From: Sonic4999 Date: Wed, 1 Sep 2021 16:55:58 -0400 Subject: [PATCH] Added `on_raw_typing` event --- discord/raw_models.py | 38 +++++++++++++++++++++++++++++++++- discord/state.py | 41 ++++++++++++++++++++++--------------- discord/types/raw_models.py | 10 +++++++++ docs/api.rst | 19 +++++++++++++++++ 4 files changed, 91 insertions(+), 17 deletions(-) diff --git a/discord/raw_models.py b/discord/raw_models.py index cda754d1..3c9360ba 100644 --- a/discord/raw_models.py +++ b/discord/raw_models.py @@ -24,6 +24,7 @@ DEALINGS IN THE SOFTWARE. from __future__ import annotations +import datetime from typing import TYPE_CHECKING, Optional, Set, List if TYPE_CHECKING: @@ -34,7 +35,8 @@ if TYPE_CHECKING: MessageUpdateEvent, ReactionClearEvent, ReactionClearEmojiEvent, - IntegrationDeleteEvent + IntegrationDeleteEvent, + TypingEvent ) from .message import Message from .partial_emoji import PartialEmoji @@ -49,6 +51,7 @@ __all__ = ( 'RawReactionClearEvent', 'RawReactionClearEmojiEvent', 'RawIntegrationDeleteEvent', + 'RawTypingEvent' ) @@ -276,3 +279,36 @@ class RawIntegrationDeleteEvent(_RawReprMixin): self.application_id: Optional[int] = int(data['application_id']) except KeyError: self.application_id: Optional[int] = None + + +class RawTypingEvent(_RawReprMixin): + """Represents the payload for a :func:`on_raw_typing` event. + + .. versionadded:: 2.0 + + Attributes + ----------- + channel_id: :class:`int` + The channel ID where the typing originated from. + user_id: :class:`int` + The ID of the user that started typing. + when: :class:`datetime.datetime` + When the typing started as an aware datetime in UTC. + guild_id: Optional[:class:`int`] + The guild ID where the typing originated from, if applicable. + member: Optional[:class:`Member`] + The member who started typing. Only available if the member started typing in a guild. + """ + + __slots__ = ("channel_id", "user_id", "when", "guild_id", "member") + + def __init__(self, data: TypingEvent) -> None: + self.channel_id: int = int(data['channel_id']) + self.user_id: int = int(data['user_id']) + self.when: datetime.datetime = datetime.datetime.fromtimestamp(data.get('timestamp'), tz=datetime.timezone.utc) + self.member: Optional[Member] = None + + try: + self.guild_id: Optional[int] = int(data['guild_id']) + except KeyError: + self.guild_id: Optional[int] = None \ No newline at end of file diff --git a/discord/state.py b/discord/state.py index 0a9feac1..09777008 100644 --- a/discord/state.py +++ b/discord/state.py @@ -1327,28 +1327,37 @@ class ConnectionState: asyncio.create_task(logging_coroutine(coro, info='Voice Protocol voice server update handler')) def parse_typing_start(self, data) -> None: + raw = RawTypingEvent(data) + + member_data = data.get('member') + if member_data: + guild = self._get_guild(raw.guild_id) + if guild is not None: + raw.member = Member(data=member_data, guild=guild, state=self) + else: + raw.member = None + else: + raw.member = None + self.dispatch('raw_typing', raw) + channel, guild = self._get_guild_channel(data) if channel is not None: - member = None - user_id = utils._get_as_snowflake(data, 'user_id') - if isinstance(channel, DMChannel): - member = channel.recipient + user = raw.member or self._get_typing_user(channel, raw.user_id) - elif isinstance(channel, (Thread, TextChannel)) and guild is not None: - # user_id won't be None - member = guild.get_member(user_id) # type: ignore + if user is not None: + self.dispatch('typing', channel, user, raw.when) - if member is None: - member_data = data.get('member') - if member_data: - member = Member(data=member_data, state=self, guild=guild) + def _get_typing_user(self, channel: Optional[MessageableChannel], user_id: int) -> Optional[Union[User, Member]]: + if isinstance(channel, DMChannel): + return channel.recipient - elif isinstance(channel, GroupChannel): - member = utils.find(lambda x: x.id == user_id, channel.recipients) + elif isinstance(channel, (Thread, TextChannel)) and channel.guild is not None: + return channel.guild.get_member(user_id) # type: ignore - if member is not None: - timestamp = datetime.datetime.fromtimestamp(data.get('timestamp'), tz=datetime.timezone.utc) - self.dispatch('typing', channel, member, timestamp) + elif isinstance(channel, GroupChannel): + return utils.find(lambda x: x.id == user_id, channel.recipients) + + return self.get_user(user_id) def _get_reaction_user(self, channel: MessageableChannel, user_id: int) -> Optional[Union[User, Member]]: if isinstance(channel, TextChannel): diff --git a/discord/types/raw_models.py b/discord/types/raw_models.py index 3c45b299..2d779e51 100644 --- a/discord/types/raw_models.py +++ b/discord/types/raw_models.py @@ -85,3 +85,13 @@ class _IntegrationDeleteEventOptional(TypedDict, total=False): class IntegrationDeleteEvent(_IntegrationDeleteEventOptional): id: Snowflake guild_id: Snowflake + + +class _TypingEventOptional(TypedDict, total=False): + guild_id: Snowflake + member: Member + +class TypingEvent(_TypingEventOptional): + channel_id: Snowflake + user_id: Snowflake + timestamp: int \ No newline at end of file diff --git a/docs/api.rst b/docs/api.rst index 0a9ba5cc..5fc56af1 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -369,6 +369,17 @@ to handle it, which defaults to print a traceback and ignoring the exception. :param when: When the typing started as an aware datetime in UTC. :type when: :class:`datetime.datetime` +.. function:: on_raw_typing(payload) + + Called when someone begins typing a message. Unlike :func:`on_typing`, this is + called regardless if the user can be found or not. This most often happens + when a user types in DMs. + + This requires :attr:`Intents.typing` to be enabled. + + :param payload: The raw typing payload. + :type payload: :class:`RawTypingEvent` + .. function:: on_message(message) Called when a :class:`Message` is created and sent. @@ -3846,6 +3857,14 @@ GuildSticker .. autoclass:: GuildSticker() :members: +RawTypingEvent +~~~~~~~~~~~~~~~~~~~~~~~ + +.. attributetable:: RawTypingEvent + +.. autoclass:: RawTypingEvent() + :members: + RawMessageDeleteEvent ~~~~~~~~~~~~~~~~~~~~~~~ -- 2.47.2