From 03868c4879d0a41160244fb59008d8e542c8bccf Mon Sep 17 00:00:00 2001 From: dolfies Date: Sat, 25 Mar 2023 21:07:47 -0400 Subject: [PATCH] Implement recent messages --- discord/client.py | 111 +++++++++++++++++++++++++++++++++++++++ discord/http.py | 23 ++++++++ discord/state.py | 7 +++ discord/types/gateway.py | 4 ++ docs/api.rst | 21 ++++++++ 5 files changed, 166 insertions(+) diff --git a/discord/client.py b/discord/client.py index 969019451..e806081ed 100644 --- a/discord/client.py +++ b/discord/client.py @@ -4366,6 +4366,117 @@ class Client: data = await self._connection.http.get_premium_usage() return PremiumUsage(data=data) + async def recent_mentions( + self, + *, + limit: Optional[int] = 25, + before: SnowflakeTime = MISSING, + guild: Snowflake = MISSING, + roles: bool = True, + everyone: bool = True, + ) -> AsyncIterator[Message]: + """Returns an :term:`asynchronous iterator` that enables receiving your recent mentions. + + .. versionadded:: 2.0 + + Examples + --------- + + Usage :: + + counter = 0 + async for message in client.recent_mentions(limit=200): + if message.author == client.user: + counter += 1 + + Flattening into a list: :: + + messages = [message async for message in client.recent_mentions(limit=123)] + # messages is now a list of Message... + + All parameters are optional. + + Parameters + ----------- + limit: Optional[:class:`int`] + The number of messages to retrieve. + If ``None``, retrieves every recent mention received in the past week. Note, however, + that this would make it a slow operation. + before: Optional[Union[:class:`~discord.abc.Snowflake`, :class:`datetime.datetime`]] + Retrieve messages before this date or message. + If a datetime is provided, it is recommended to use a UTC aware datetime. + If the datetime is naive, it is assumed to be local time. + guild: :class:`.Guild` + The guild to retrieve recent mentions from. + If not provided, then the mentions are retrieved from all guilds. + roles: :class:`bool` + Whether to include role mentions. + everyone: :class:`bool` + Whether to include @everyone or @here mentions. + + Raises + ------ + HTTPException + The request to get recent message history failed. + + Yields + ------- + :class:`.Message` + The message with the message data parsed. + """ + _state = self._connection + + async def strategy(retrieve, before, limit): + before_id = before.id if before else None + data = await _state.http.get_recent_mentions( + retrieve, before=before_id, guild_id=guild.id if guild else None, roles=roles, everyone=everyone + ) + + if data: + if limit is not None: + limit -= len(data) + + before = Object(id=int(data[-1]['id'])) + + return data, before, limit + + if isinstance(before, datetime): + before = Object(id=utils.time_snowflake(before, high=False)) + + while True: + retrieve = min(100 if limit is None else limit, 100) + if retrieve < 1: + return + + data, before, limit = await strategy(retrieve, before, limit) + + # Terminate loop on next iteration; there's no data left after this + if len(data) < 100: + limit = 0 + + for raw_message in data: + channel, _ = _state._get_guild_channel(raw_message) + yield _state.create_message(channel=channel, data=raw_message) # type: ignore + + async def delete_recent_mention(self, message: Snowflake) -> None: + """|coro| + + Acknowledges a message the current user has been mentioned in. + + .. versionadded:: 2.0 + + Parameters + ----------- + message: :class:`.abc.Snowflake` + The message to delete. + + Raises + ------ + HTTPException + Deleting the recent mention failed. + """ + await self._connection.http.delete_recent_mention(message.id) + async def user_affinities(self) -> List[UserAffinity]: """|coro| diff --git a/discord/http.py b/discord/http.py index 4b0dd700a..05ada7e6c 100644 --- a/discord/http.py +++ b/discord/http.py @@ -3865,6 +3865,29 @@ class HTTPClient: ) ) + def get_recent_mentions( + self, + limit: int = 25, + before: Optional[Snowflake] = None, + guild_id: Optional[Snowflake] = None, + roles: bool = True, + everyone: bool = True, + ) -> Response[List[Message]]: + params = { + 'limit': limit, + 'roles': str(roles).lower(), + 'everyone': str(everyone).lower(), + } + if before is not None: + params['before'] = before + if guild_id is not None: + params['guild_id'] = guild_id + + return self.request(Route('GET', '/users/@me/mentions'), params=params) + + def delete_recent_mention(self, message_id: Snowflake) -> Response[None]: + return self.request(Route('DELETE', '/users/@me/mentions/{message_id}', message_id=message_id)) + async def get_preferred_voice_regions(self) -> List[dict]: async with self.__session.get('https://latency.discord.media/rtc') as resp: if resp.status == 200: diff --git a/discord/state.py b/discord/state.py index 09cf0913b..8856fd562 100644 --- a/discord/state.py +++ b/discord/state.py @@ -1178,6 +1178,13 @@ class ConnectionState: if reaction: self.dispatch('reaction_clear_emoji', reaction) + def parse_recent_mention_delete(self, data: gw.RecentMentionDeleteEvent) -> None: + message_id = int(data['message_id']) + message = self._get_message(message_id) + if message is not None: + self.dispatch('recent_mention_delete', message) + self.dispatch('raw_recent_mention_delete', message_id) + def parse_presences_replace(self, data: List[gw.PartialPresenceUpdate]) -> None: for presence in data: self.parse_presence_update(presence) diff --git a/discord/types/gateway.py b/discord/types/gateway.py index b0750f2dc..885e08e11 100644 --- a/discord/types/gateway.py +++ b/discord/types/gateway.py @@ -459,3 +459,7 @@ class ProtoSettings(TypedDict): class ProtoSettingsEvent(TypedDict): settings: ProtoSettings partial: bool + + +class RecentMentionDeleteEvent(TypedDict): + message_id: Snowflake diff --git a/docs/api.rst b/docs/api.rst index 96834d32d..5a54bdcd6 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1034,6 +1034,27 @@ Messages :param payload: The raw event payload data. :type payload: :class:`RawBulkMessageDeleteEvent` +.. function:: on_recent_mention_delete(message) + + Called when a message you were mentioned in in the last week is acknowledged and deleted. + If the message is not found in the internal message cache, then this event will not be called. + + .. versionadded:: 2.0 + + :param message: The message that was deleted. + :type message: :class:`Message` + +.. function:: on_raw_recent_mention_delete(message_id) + + Called when a message you were mentioned in in the last week is acknowledged and deleted. + Unlike :func:`on_recent_mention_delete`, this is called regardless of the message being in the + internal message cache or not. + + .. versionadded:: 2.0 + + :param message_id: The ID of the message that was deleted. + :type message_id: :class:`int` + Reactions ~~~~~~~~~~