Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 36 additions & 2 deletions discord/raw_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

from __future__ import annotations

import datetime
from typing import TYPE_CHECKING, Optional, Set, List

from .enums import ChannelType, try_enum
Expand All @@ -39,6 +40,7 @@
ReactionClearEmojiEvent,
IntegrationDeleteEvent,
ThreadDeleteEvent,
TypingEvent
)
from .message import Message
from .partial_emoji import PartialEmoji
Expand All @@ -56,6 +58,7 @@
'RawReactionClearEmojiEvent',
'RawIntegrationDeleteEvent',
'RawThreadDeleteEvent',
'RawTypingEvent',
)


Expand Down Expand Up @@ -283,7 +286,7 @@ def __init__(self, data: IntegrationDeleteEvent) -> None:
self.application_id: Optional[int] = int(data['application_id'])
except KeyError:
self.application_id: Optional[int] = None

class RawThreadDeleteEvent(_RawReprMixin):
"""Represents the payload for :func:`on_raw_thread_delete` event.

Expand Down Expand Up @@ -311,5 +314,36 @@ def __init__(self, data: ThreadDeleteEvent) -> None:
self.guild_id: int = int(data['guild_id'])
self.parent_id: int = int(data['parent_id'])
self.thread: Optional[Thread] = 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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should also use data['timestamp'] here.

self.member: Optional[Member] = None

try:
self.guild_id: Optional[int] = int(data['guild_id'])
except KeyError:
self.guild_id: Optional[int] = None

43 changes: 26 additions & 17 deletions discord/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -1337,28 +1337,37 @@ def parse_voice_server_update(self, data) -> None:
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:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be shortened to

        member_data = data.get('member')
        raw.member = None
        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)

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 or self.get_user(user_id)

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):
Expand Down Expand Up @@ -1534,4 +1543,4 @@ def parse_ready(self, data) -> None:

def parse_resumed(self, data) -> None:
self.dispatch('resumed')
self.dispatch('shard_resumed', data['__shard_id__'])
self.dispatch('shard_resumed', data['__shard_id__'])
13 changes: 13 additions & 0 deletions discord/types/raw_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,22 @@ class IntegrationDeleteEvent(_IntegrationDeleteEventOptional):
id: Snowflake
guild_id: Snowflake


class ThreadDeleteEvent(TypedDict, total=False):
thread_id: Snowflake
thread_type: int
guild_id: Snowflake
parent_id: Snowflake


class _TypingEventOptional(TypedDict, total=False):
guild_id: Snowflake
member: Member


class TypingEvent(_TypingEventOptional):
channel_id: Snowflake
user_id: Snowflake
timestamp: int


21 changes: 21 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,19 @@ 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 in the bot's cache or not.

If the typing event is occuring in a guild,
the member that started typing can be accessed via :attr:`RawTypingEvent.member`

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.
Expand Down Expand Up @@ -3913,6 +3926,14 @@ GuildSticker
.. autoclass:: GuildSticker()
:members:

RawTypingEvent
~~~~~~~~~~~~~~~~~~~~~~~

.. attributetable:: RawTypingEvent

.. autoclass:: RawTypingEvent()
:members:

RawMessageDeleteEvent
~~~~~~~~~~~~~~~~~~~~~~~

Expand Down