diff --git a/discord/abc.py b/discord/abc.py index 7ae34aece1..2ebef5fbbd 100644 --- a/discord/abc.py +++ b/discord/abc.py @@ -346,6 +346,10 @@ def __init__( ): ... def __str__(self) -> str: + flags = getattr(self, "flags", None) + if flags is not None and getattr(flags, "obfuscated", False): + return f"Obfuscated Channel ({self.id})" + return self.name @property diff --git a/discord/client.py b/discord/client.py index c45731271c..04f03a88ca 100644 --- a/discord/client.py +++ b/discord/client.py @@ -168,6 +168,10 @@ class Client: If not given, defaults to a regularly constructed :class:`Intents` class. .. versionadded:: 1.5 + capabilities: :class:`GatewayCapabilities` + Gateway capabilities to include with the IDENTIFY payload. Defaults to + :meth:`GatewayCapabilities.none`. These are mostly undocumented and may not + be supported for bot accounts. member_cache_flags: :class:`MemberCacheFlags` Allows for finer control over how the library caches members. If not given, defaults to cache as much as possible with the diff --git a/discord/flags.py b/discord/flags.py index cb2059e866..f43b74c2f0 100644 --- a/discord/flags.py +++ b/discord/flags.py @@ -34,6 +34,7 @@ "MessageFlags", "AttachmentFlags", "PublicUserFlags", + "GatewayCapabilities", "Intents", "MemberCacheFlags", "ApplicationFlags", @@ -587,6 +588,36 @@ def all(self) -> list[UserFlags]: ] +@fill_with_flags() +class GatewayCapabilities(BaseFlags): + """Wraps Discord gateway capabilities. + + These bits are mostly undocumented and meant for specialized clients. They are + sent with the IDENTIFY payload as the ``capabilities`` field. + + Attributes + ---------- + value: :class:`int` + The raw capability value. Prefer toggling via the provided attributes. + """ + + __slots__ = () + + @classmethod + def none(cls) -> GatewayCapabilities: + """Creates a :class:`GatewayCapabilities` instance with no bits enabled.""" + + self = cls.__new__(cls) + self.value = cls.DEFAULT_VALUE + return self + + @flag_value + def obfuscated_channels(self): + """:class:`bool`: Obfuscates channel identifiers in gateway events.""" + + return 1 << 15 + + @fill_with_flags() class Intents(BaseFlags): r"""Wraps up a Discord gateway intent flag. @@ -1585,6 +1616,12 @@ def hide_media_download_options(self): """ return 1 << 15 + @flag_value + def obfuscated(self): + """:class:`bool`: Returns ``True`` if the channel is obfuscated by the gateway.""" + + return 1 << 17 + @fill_with_flags() class AttachmentFlags(BaseFlags): diff --git a/discord/gateway.py b/discord/gateway.py index 288cfa5a10..19fb87c057 100644 --- a/discord/gateway.py +++ b/discord/gateway.py @@ -361,6 +361,7 @@ async def from_client( ws.session_id = session ws.sequence = sequence ws._max_heartbeat_timeout = client._connection.heartbeat_timeout + ws.capabilities = client._connection.capabilities.value if client._enable_debug_events: ws.send = ws.debug_send @@ -420,6 +421,9 @@ async def identify(self): }, "compress": True, "large_threshold": 250, + "capabilities": ( + self.capabilities if hasattr(self, "capabilities") else 0 + ), }, } diff --git a/discord/state.py b/discord/state.py index 92d22760de..be416f6825 100644 --- a/discord/state.py +++ b/discord/state.py @@ -51,7 +51,7 @@ from .channel import _channel_factory from .emoji import AppEmoji, GuildEmoji from .enums import ChannelType, InteractionType, ScheduledEventStatus, Status, try_enum -from .flags import ApplicationFlags, Intents, MemberCacheFlags +from .flags import ApplicationFlags, GatewayCapabilities, Intents, MemberCacheFlags from .guild import Guild from .integrations import _integration_factory from .interactions import Interaction @@ -248,6 +248,18 @@ def __init__( self._status: str | None = status self._intents: Intents = intents + capabilities = options.get("capabilities") + if capabilities is None: + capabilities = GatewayCapabilities.none() + elif isinstance(capabilities, int): + capabilities = GatewayCapabilities._from_value(capabilities) + elif not isinstance(capabilities, GatewayCapabilities): + raise TypeError( + "capabilities parameter must be GatewayCapabilities or int." + ) + + self.capabilities: GatewayCapabilities = capabilities + if not intents.members or cache_flags._empty: self.store_user = self.create_user # type: ignore self.deref_user = self.deref_user_no_intents # type: ignore