From e808867a347aba9aa79ffbb90e0c91f2ae3e9b4e Mon Sep 17 00:00:00 2001 From: shiftinv Date: Fri, 9 Aug 2024 21:04:00 +0200 Subject: [PATCH] feat: add `soundboard_sounds` caching from guild_create --- disnake/client.py | 28 ++++++++++++++++++++++++++-- disnake/guild.py | 10 ++++++++++ disnake/state.py | 23 +++++++++++++++++++++++ disnake/types/guild.py | 2 ++ disnake/types/soundboard.py | 2 +- 5 files changed, 62 insertions(+), 3 deletions(-) diff --git a/disnake/client.py b/disnake/client.py index 185f405b8c..6d44cbccca 100644 --- a/disnake/client.py +++ b/disnake/client.py @@ -68,7 +68,7 @@ from .mentions import AllowedMentions from .object import Object from .sku import SKU -from .soundboard import SoundboardSound +from .soundboard import GuildSoundboardSound, SoundboardSound from .stage_instance import StageInstance from .state import ConnectionState from .sticker import GuildSticker, StandardSticker, StickerPack, _sticker_factory @@ -572,6 +572,14 @@ def stickers(self) -> List[GuildSticker]: """ return self._connection.stickers + @property + def soundboard_sounds(self) -> List[GuildSoundboardSound]: + """List[:class:`.GuildSoundboardSound`]: The soundboard sounds that the connected client has. + + .. versionadded:: 2.10 + """ + return self._connection.soundboard_sounds + @property def cached_messages(self) -> Sequence[Message]: """Sequence[:class:`.Message`]: Read-only list of messages the connected client has cached. @@ -1497,7 +1505,7 @@ def get_sticker(self, id: int, /) -> Optional[GuildSticker]: .. note:: - To retrieve standard stickers, use :meth:`.fetch_sticker`. + To retrieve standard stickers, use :meth:`.fetch_sticker` or :meth:`.fetch_sticker_packs`. Returns @@ -1507,6 +1515,22 @@ def get_sticker(self, id: int, /) -> Optional[GuildSticker]: """ return self._connection.get_sticker(id) + def get_soundboard_sounds(self, id: int, /) -> Optional[GuildSoundboardSound]: + """Returns a guild soundboard sound with the given ID. + + .. versionadded:: 2.10 + + .. note:: + + To retrieve standard soundboard sounds, use :meth:`.fetch_default_soundboard_sounds`. + + Returns + ------- + Optional[:class:`.GuildSoundboardSound`] + The soundboard sound or ``None`` if not found. + """ + return self._connection.get_soundboard_sound(id) + def get_all_channels(self) -> Generator[GuildChannel, None, None]: """A generator that retrieves every :class:`.abc.GuildChannel` the client can 'access'. diff --git a/disnake/guild.py b/disnake/guild.py index 46c480f5ba..a0cf0ae656 100644 --- a/disnake/guild.py +++ b/disnake/guild.py @@ -166,6 +166,11 @@ class Guild(Hashable): .. versionadded:: 2.0 + soundboard_sounds: Tuple[:class:`GuildSoundboardSound`, ...] + All soundboard sounds that the guild owns. + + .. versionadded:: 2.10 + afk_timeout: :class:`int` The timeout to get sent to the AFK channel. afk_channel: Optional[:class:`VoiceChannel`] @@ -325,6 +330,7 @@ class Guild(Hashable): "mfa_level", "emojis", "stickers", + "soundboard_sounds", "features", "verification_level", "explicit_content_filter", @@ -558,6 +564,10 @@ def _from_data(self, guild: GuildPayload) -> None: self.stickers: Tuple[GuildSticker, ...] = tuple( state.store_sticker(self, d) for d in guild.get("stickers", []) ) + # TODO: rest methods + self.soundboard_sounds: Tuple[GuildSoundboardSound, ...] = tuple( + state.store_soundboard_sound(self, d) for d in guild.get("soundboard_sounds", []) + ) self.features: List[GuildFeature] = guild.get("features", []) self._splash: Optional[str] = guild.get("splash") self._system_channel_id: Optional[int] = utils._get_as_snowflake(guild, "system_channel_id") diff --git a/disnake/state.py b/disnake/state.py index 299ec309ec..f0f6726b1a 100644 --- a/disnake/state.py +++ b/disnake/state.py @@ -105,6 +105,7 @@ from .types.emoji import Emoji as EmojiPayload, PartialEmoji as PartialEmojiPayload from .types.guild import Guild as GuildPayload, UnavailableGuild as UnavailableGuildPayload from .types.message import Message as MessagePayload + from .types.soundboard import GuildSoundboardSound as GuildSoundboardSoundPayload from .types.sticker import GuildSticker as GuildStickerPayload from .types.user import User as UserPayload from .types.webhook import Webhook as WebhookPayload @@ -308,6 +309,7 @@ def clear( self._users: weakref.WeakValueDictionary[int, User] = weakref.WeakValueDictionary() self._emojis: Dict[int, Emoji] = {} self._stickers: Dict[int, GuildSticker] = {} + self._soundboard_sounds: Dict[int, GuildSoundboardSound] = {} self._guilds: Dict[int, Guild] = {} if application_commands: @@ -418,6 +420,15 @@ def store_sticker(self, guild: Guild, data: GuildStickerPayload) -> GuildSticker self._stickers[sticker_id] = sticker = GuildSticker(state=self, data=data) return sticker + def store_soundboard_sound( + self, guild: Guild, data: GuildSoundboardSoundPayload + ) -> GuildSoundboardSound: + sticker_id = int(data["sound_id"]) + self._soundboard_sounds[sticker_id] = sound = GuildSoundboardSound( + state=self, data=data, guild_id=guild.id + ) + return sound + def store_view(self, view: View, message_id: Optional[int] = None) -> None: self._view_store.add_view(view, message_id) @@ -453,6 +464,9 @@ def _remove_guild(self, guild: Guild) -> None: for sticker in guild.stickers: self._stickers.pop(sticker.id, None) + for sound in guild.soundboard_sounds: + self._soundboard_sounds.pop(sound.id, None) + del guild def _get_global_application_command( @@ -528,6 +542,10 @@ def emojis(self) -> List[Emoji]: def stickers(self) -> List[GuildSticker]: return list(self._stickers.values()) + @property + def soundboard_sounds(self) -> List[GuildSoundboardSound]: + return list(self._soundboard_sounds.values()) + def get_emoji(self, emoji_id: Optional[int]) -> Optional[Emoji]: # the keys of self._emojis are ints return self._emojis.get(emoji_id) # type: ignore @@ -536,6 +554,10 @@ def get_sticker(self, sticker_id: Optional[int]) -> Optional[GuildSticker]: # the keys of self._stickers are ints return self._stickers.get(sticker_id) # type: ignore + def get_soundboard_sound(self, sound_id: Optional[int]) -> Optional[GuildSoundboardSound]: + # the keys of self._soundboard_sounds are ints + return self._soundboard_sounds.get(sound_id) # type: ignore + @property def private_channels(self) -> List[PrivateChannel]: return list(self._private_channels.values()) @@ -1960,6 +1982,7 @@ def parse_entitlement_delete(self, data: gateway.EntitlementDelete) -> None: entitlement = Entitlement(data=data, state=self) self.dispatch("entitlement_delete", entitlement) + # TODO: update cache, add non-raw events def parse_guild_soundboard_sound_create(self, data: gateway.GuildSoundboardSoundCreate) -> None: guild_id = utils._get_as_snowflake(data, "guild_id") guild = self._get_guild(guild_id) diff --git a/disnake/types/guild.py b/disnake/types/guild.py index 5041a89240..6b53b8e1f3 100644 --- a/disnake/types/guild.py +++ b/disnake/types/guild.py @@ -11,6 +11,7 @@ from .member import Member from .role import CreateRole, Role from .snowflake import Snowflake +from .soundboard import GuildSoundboardSound from .sticker import GuildSticker from .threads import Thread from .user import User @@ -149,6 +150,7 @@ class Guild(_BaseGuildPreview): presences: NotRequired[List[PartialPresenceUpdate]] stage_instances: NotRequired[List[StageInstance]] guild_scheduled_events: NotRequired[List[GuildScheduledEvent]] + soundboard_sounds: NotRequired[List[GuildSoundboardSound]] class InviteGuild(Guild, total=False): diff --git a/disnake/types/soundboard.py b/disnake/types/soundboard.py index f7c018a4c5..b976df281e 100644 --- a/disnake/types/soundboard.py +++ b/disnake/types/soundboard.py @@ -24,4 +24,4 @@ class SoundboardSound(PartialSoundboardSound): class GuildSoundboardSound(SoundboardSound): guild_id: NotRequired[Snowflake] - user: NotRequired[User] + user: NotRequired[User] # only available via REST, given appropriate permissions