From 2aba3d0c0c2d47307b3f647ee3af10a2bae5e2ae Mon Sep 17 00:00:00 2001 From: dolfies Date: Mon, 10 Apr 2023 13:45:49 -0400 Subject: [PATCH] Implement scheduled event subscriptions and add raw scheduled event user add/remove events --- discord/guild.py | 23 +++++++++++++++++++ discord/http.py | 39 ++++++++++++++++++++++++++++++-- discord/scheduled_event.py | 34 ++++++++++++++++++++++++---- discord/state.py | 12 +++++----- discord/types/scheduled_event.py | 15 ++++++------ docs/api.rst | 14 ++++++++++++ 6 files changed, 118 insertions(+), 19 deletions(-) diff --git a/discord/guild.py b/discord/guild.py index adc9b0e31..d23326242 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -3216,6 +3216,29 @@ class Guild(Hashable): """ await self._state.http.delete_guild_sticker(self.id, sticker.id, reason) + async def subscribed_scheduled_events(self) -> List[Union[ScheduledEvent, Object]]: + """|coro| + + Retrieves a list of all scheduled events that the current user is subscribed to. + + .. versionadded:: 2.1 + + Raises + ------- + HTTPException + Retrieving the subscribed scheduled events failed. + + Returns + -------- + List[Union[:class:`ScheduledEvent`, :class:`Object`]] + The subscribed scheduled events. Falls back to an object if the event is not found in cache. + """ + data = await self._state.http.get_subscribed_scheduled_events(self.id) + return [ + self.get_scheduled_event(int(d['guild_scheduled_event_id'])) or Object(id=int(d['guild_scheduled_event_id'])) + for d in data + ] + async def fetch_scheduled_events(self, *, with_counts: bool = True) -> List[ScheduledEvent]: """|coro| diff --git a/discord/http.py b/discord/http.py index e948539b3..5d3cd7610 100644 --- a/discord/http.py +++ b/discord/http.py @@ -2406,9 +2406,15 @@ class HTTPClient: ... def get_scheduled_events(self, guild_id: Snowflake, with_user_count: bool) -> Response[Any]: - params = {'with_user_count': int(with_user_count)} + params = {'with_user_count': str(with_user_count).lower()} return self.request(Route('GET', '/guilds/{guild_id}/scheduled-events', guild_id=guild_id), params=params) + def get_subscribed_scheduled_events( + self, guild_id: Snowflake + ) -> Response[List[scheduled_event.SubscribedGuildScheduledEvent]]: + params = {'guild_ids': guild_id} + return self.request(Route('GET', '/users/@me/scheduled-events'), params=params) + def create_guild_scheduled_event( self, guild_id: Snowflake, *, reason: Optional[str] = None, **payload: Any ) -> Response[scheduled_event.GuildScheduledEvent]: @@ -2555,7 +2561,6 @@ class HTTPClient: 'limit': limit, 'with_member': str(with_member).lower(), } - if before is not None: params['before'] = before if after is not None: @@ -2571,6 +2576,36 @@ class HTTPClient: params=params, ) + def create_scheduled_event_user( + self, + guild_id: Snowflake, + guild_scheduled_event_id: Snowflake, + ) -> Response[scheduled_event.SubscribedGuildScheduledEvent]: + return self.request( + Route( + 'PUT', + '/guilds/{guild_id}/scheduled-events/{guild_scheduled_event_id}/users/@me', + guild_id=guild_id, + guild_scheduled_event_id=guild_scheduled_event_id, + ), + ) + + def delete_scheduled_event_user( + self, + guild_id: Snowflake, + guild_scheduled_event_id: Snowflake, + ) -> Response[None]: + return self.request( + Route( + 'DELETE', + '/guilds/{guild_id}/scheduled-events/{guild_scheduled_event_id}/users/@me', + guild_id=guild_id, + guild_scheduled_event_id=guild_scheduled_event_id, + ), + ) + + # Guild automod management + def get_auto_moderation_rules(self, guild_id: Snowflake) -> Response[List[automod.AutoModerationRule]]: return self.request(Route('GET', '/guilds/{guild_id}/auto-moderation/rules', guild_id=guild_id)) diff --git a/discord/scheduled_event.py b/discord/scheduled_event.py index 8194789c3..a2a1a1d17 100644 --- a/discord/scheduled_event.py +++ b/discord/scheduled_event.py @@ -58,8 +58,6 @@ __all__ = ( class ScheduledEvent(Hashable): """Represents a scheduled event in a guild. - .. versionadded:: 2.0 - .. container:: operations .. describe:: x == y @@ -74,6 +72,8 @@ class ScheduledEvent(Hashable): Returns the scheduled event's hash. + .. versionadded:: 2.0 + Attributes ---------- id: :class:`int` @@ -100,8 +100,6 @@ class ScheduledEvent(Hashable): The user that created the scheduled event. creator_id: Optional[:class:`int`] The ID of the user that created the scheduled event. - - .. versionadded:: 2.0 location: Optional[:class:`str`] The location of the scheduled event. """ @@ -674,6 +672,34 @@ class ScheduledEvent(Hashable): # There's no data left after this break + async def subscribe(self) -> None: + """|coro| + + Subscribes the current user to this event. + + .. versionadded:: 2.1 + + Raises + ------- + HTTPException + Subscribing failed. + """ + await self._state.http.create_scheduled_event_user(self.guild_id, self.id) + + async def unsubscribe(self) -> None: + """|coro| + + Unsubscribes the current user from this event. + + .. versionadded:: 2.1 + + Raises + ------- + HTTPException + Unsubscribing failed. + """ + await self._state.http.delete_scheduled_event_user(self.guild_id, self.id) + def _add_user(self, user: User) -> None: self._users[user.id] = user diff --git a/discord/state.py b/discord/state.py index 3802b79aa..2ef3b418d 100644 --- a/discord/state.py +++ b/discord/state.py @@ -2477,12 +2477,12 @@ class ConnectionState: if guild is not None: scheduled_event = guild._scheduled_events.get(int(data['guild_scheduled_event_id'])) if scheduled_event is not None: - user = self.get_user(int(data['user_id'])) + user_id = int(data['user_id']) + user = self.get_user(user_id) if user is not None: scheduled_event._add_user(user) self.dispatch('scheduled_event_user_add', scheduled_event, user) - else: - _log.debug('SCHEDULED_EVENT_USER_ADD referencing unknown user ID: %s. Discarding.', data['user_id']) + self.dispatch('raw_scheduled_event_user_add', scheduled_event, user_id) else: _log.debug( 'SCHEDULED_EVENT_USER_ADD referencing unknown scheduled event ID: %s. Discarding.', @@ -2496,12 +2496,12 @@ class ConnectionState: if guild is not None: scheduled_event = guild._scheduled_events.get(int(data['guild_scheduled_event_id'])) if scheduled_event is not None: - user = self.get_user(int(data['user_id'])) + user_id = int(data['user_id']) + user = self.get_user(user_id) if user is not None: scheduled_event._pop_user(user.id) self.dispatch('scheduled_event_user_remove', scheduled_event, user) - else: - _log.debug('SCHEDULED_EVENT_USER_REMOVE referencing unknown user ID: %s. Discarding.', data['user_id']) + self.dispatch('raw_scheduled_event_user_remove', scheduled_event, user_id) else: _log.debug( 'SCHEDULED_EVENT_USER_REMOVE referencing unknown scheduled event ID: %s. Discarding.', diff --git a/discord/types/scheduled_event.py b/discord/types/scheduled_event.py index 91c15cb08..e1758698e 100644 --- a/discord/types/scheduled_event.py +++ b/discord/types/scheduled_event.py @@ -98,17 +98,18 @@ GuildScheduledEventWithUserCount = Union[ ] -class ScheduledEventUser(User): - ... - - -class ScheduledEventUserWithMember(ScheduledEventUser): +class UserWithMember(User): guild_member: Member class ScheduledEventUsers(TypedDict): - users: List[ScheduledEventUser] + users: List[User] class ScheduledEventUsersWithMember(TypedDict): - users: ScheduledEventUserWithMember + users: List[UserWithMember] + + +class SubscribedGuildScheduledEvent(TypedDict): + guild_scheduled_event_id: Snowflake + user_id: Snowflake diff --git a/docs/api.rst b/docs/api.rst index 5b470c1d8..034bf2e23 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1348,6 +1348,20 @@ Scheduled Events :param user: The user that was added or removed. :type user: :class:`User` +.. function:: on_raw_scheduled_event_user_add(event, user_id) + on_raw_scheduled_event_user_remove(event, user_id) + + Called when a user is added or removed from a :class:`ScheduledEvent`. + Unlike :func:`on_scheduled_event_user_add` and :func:`on_scheduled_event_user_remove` + these are called regardless of the user being in the internal user cache or not. + + .. versionadded:: 2.1 + + :param event: The scheduled event that the user was added or removed from. + :type event: :class:`ScheduledEvent` + :param user_id: The ID of the user that was added or removed. + :type user_id: :class:`int` + Stages ~~~~~~~