diff --git a/changelog/1083.bugfix.rst b/changelog/1083.bugfix.rst new file mode 100644 index 0000000000..15f696e783 --- /dev/null +++ b/changelog/1083.bugfix.rst @@ -0,0 +1 @@ +Don't cache :attr:`Guild.emojis`, :attr:`~Guild.stickers`, or :attr:`~Guild.scheduled_events` if the corresponding intent is disabled. diff --git a/disnake/guild.py b/disnake/guild.py index d68ff87e0e..43d3934a88 100644 --- a/disnake/guild.py +++ b/disnake/guild.py @@ -364,7 +364,9 @@ class Guild(Hashable): 3: _GuildLimit(emoji=250, stickers=60, bitrate=384e3, filesize=104857600), } - def __init__(self, *, data: GuildPayload, state: ConnectionState) -> None: + def __init__( + self, *, data: GuildPayload, state: ConnectionState, from_gateway: bool = False + ) -> None: self._channels: Dict[int, GuildChannel] = {} self._members: Dict[int, Member] = {} self._voice_states: Dict[int, VoiceState] = {} @@ -372,7 +374,7 @@ def __init__(self, *, data: GuildPayload, state: ConnectionState) -> None: self._stage_instances: Dict[int, StageInstance] = {} self._scheduled_events: Dict[int, GuildScheduledEvent] = {} self._state: ConnectionState = state - self._from_data(data) + self._from_data(data, from_gateway=from_gateway) def _add_channel(self, channel: GuildChannel, /) -> None: self._channels[channel.id] = channel @@ -512,7 +514,7 @@ def get_command_named(self, name: str, /) -> Optional[APIApplicationCommand]: """ return self._state._get_guild_command_named(self.id, name) - def _from_data(self, guild: GuildPayload) -> None: + def _from_data(self, guild: GuildPayload, *, from_gateway: bool = False) -> None: # according to Stan, this is always available even if the guild is unavailable # I don't have this guarantee when someone updates the guild. member_count = guild.get("member_count", None) @@ -535,6 +537,7 @@ def _from_data(self, guild: GuildPayload) -> None: self._banner: Optional[str] = guild.get("banner") self.unavailable: bool = guild.get("unavailable", False) self.id: int = int(guild["id"]) + self._roles: Dict[int, Role] = {} state = self._state # speed up attribute access for r in guild.get("roles", []): @@ -542,12 +545,15 @@ def _from_data(self, guild: GuildPayload) -> None: self._roles[role.id] = role self.mfa_level: MFALevel = guild.get("mfa_level", 0) - self.emojis: Tuple[Emoji, ...] = tuple( - state.store_emoji(self, d) for d in guild.get("emojis", []) - ) - self.stickers: Tuple[GuildSticker, ...] = tuple( - state.store_sticker(self, d) for d in guild.get("stickers", []) - ) + + self.emojis: Tuple[Emoji, ...] = () + self.stickers: Tuple[GuildSticker, ...] = () + # don't cache emojis/stickers if this is part of a gw event and the intent is disabled + # (we still want to store them on this guild object even with the intent disabled if obtained from `fetch_guild` etc.) + if not from_gateway or state._intents.emojis_and_stickers: + self.emojis = tuple(state.store_emoji(self, d) for d in guild.get("emojis", [])) + self.stickers = tuple(state.store_sticker(self, d) for d in guild.get("stickers", [])) + 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") @@ -585,12 +591,13 @@ def _from_data(self, guild: GuildPayload) -> None: stage_instance = StageInstance(guild=self, data=s, state=state) self._stage_instances[stage_instance.id] = stage_instance - scheduled_events = guild.get("guild_scheduled_events") - if scheduled_events is not None: - self._scheduled_events = {} - for e in scheduled_events: - scheduled_event = GuildScheduledEvent(state=state, data=e) - self._scheduled_events[scheduled_event.id] = scheduled_event + if not from_gateway or state._intents.guild_scheduled_events: + scheduled_events = guild.get("guild_scheduled_events") + if scheduled_events is not None: + self._scheduled_events = {} + for e in scheduled_events: + scheduled_event = GuildScheduledEvent(state=state, data=e) + self._scheduled_events[scheduled_event.id] = scheduled_event cache_joined = self._state.member_cache_flags.joined self_id = self._state.self_id diff --git a/disnake/state.py b/disnake/state.py index ca915aa33f..901e459e3b 100644 --- a/disnake/state.py +++ b/disnake/state.py @@ -275,6 +275,10 @@ def __init__( if not self._intents.members or member_cache_flags._empty: self.store_user = self.create_user + if not self._intents.emojis_and_stickers: + self.store_emoji = self.create_emoji + self.store_sticker = self.create_sticker + self.parsers = parsers = {} for attr, func in inspect.getmembers(self): if attr.startswith("parse_"): @@ -398,11 +402,17 @@ def store_emoji(self, guild: Guild, data: EmojiPayload) -> Emoji: self._emojis[emoji_id] = emoji = Emoji(guild=guild, state=self, data=data) return emoji + def create_emoji(self, guild: Guild, data: EmojiPayload) -> Emoji: + return Emoji(guild=guild, state=self, data=data) + def store_sticker(self, guild: Guild, data: GuildStickerPayload) -> GuildSticker: sticker_id = int(data["id"]) self._stickers[sticker_id] = sticker = GuildSticker(state=self, data=data) return sticker + def create_sticker(self, guild: Guild, data: GuildStickerPayload) -> GuildSticker: + return GuildSticker(state=self, data=data) + def store_view(self, view: View, message_id: Optional[int] = None) -> None: self._view_store.add_view(view, message_id) @@ -575,6 +585,7 @@ def _add_guild_from_data(self, data: Union[GuildPayload, UnavailableGuildPayload guild = Guild( data=data, # type: ignore # may be unavailable guild state=self, + from_gateway=True, ) self._add_guild(guild) return guild @@ -1337,6 +1348,8 @@ def parse_guild_emojis_update(self, data: gateway.GuildEmojisUpdateEvent) -> Non ) return + # n.b. this isn't gated by an intent check, since we shouldn't receive + # this event in the first place if the intent is disabled before_emojis = guild.emojis for emoji in before_emojis: self._emojis.pop(emoji.id, None) @@ -1353,8 +1366,8 @@ def parse_guild_stickers_update(self, data: gateway.GuildStickersUpdateEvent) -> return before_stickers = guild.stickers - for emoji in before_stickers: - self._stickers.pop(emoji.id, None) + for sticker in before_stickers: + self._stickers.pop(sticker.id, None) guild.stickers = tuple(self.store_sticker(guild, d) for d in data["stickers"]) self.dispatch("guild_stickers_update", guild, before_stickers, guild.stickers) @@ -1366,7 +1379,7 @@ def _get_create_guild(self, data: gateway.GuildCreateEvent) -> Guild: guild = self._get_guild(int(data["id"])) if guild is not None: guild.unavailable = False - guild._from_data(data) # type: ignore # data type not narrowed correctly to full guild + guild._from_data(data, from_gateway=True) # type: ignore # data type not narrowed correctly to full guild return guild return self._add_guild_from_data(data) @@ -1444,7 +1457,7 @@ def parse_guild_update(self, data: gateway.GuildUpdateEvent) -> None: guild = self._get_guild(int(data["id"])) if guild is not None: old_guild = copy.copy(guild) - guild._from_data(data) + guild._from_data(data, from_gateway=True) self.dispatch("guild_update", old_guild, guild) else: _log.debug("GUILD_UPDATE referencing an unknown guild ID: %s. Discarding.", data["id"]) diff --git a/disnake/template.py b/disnake/template.py index f0a0777abd..0dace7a869 100644 --- a/disnake/template.py +++ b/disnake/template.py @@ -46,7 +46,14 @@ def self_id(self): def member_cache_flags(self): return self.__state.member_cache_flags - def store_emoji(self, guild, packet): + @property + def _intents(self): + return self.__state._intents + + def store_emoji(self, guild, data): + return None + + def store_sticker(self, guild, data): return None def _get_voice_client(self, id):