diff --git a/changelog/1062.feature.rst b/changelog/1062.feature.rst new file mode 100644 index 0000000000..819b77a4a9 --- /dev/null +++ b/changelog/1062.feature.rst @@ -0,0 +1 @@ +Implement Guest invites. See also :attr:`disnake.Invite.flags`. diff --git a/disnake/flags.py b/disnake/flags.py index 0ca9d0e343..8eb3ea9428 100644 --- a/disnake/flags.py +++ b/disnake/flags.py @@ -40,6 +40,7 @@ "ChannelFlags", "AutoModKeywordPresets", "MemberFlags", + "InviteFlags", ) BF = TypeVar("BF", bound="BaseFlags") @@ -2289,3 +2290,94 @@ def bypasses_verification(self): def started_onboarding(self): """:class:`bool`: Returns ``True`` if the member has started onboarding.""" return 1 << 3 + + +class InviteFlags(BaseFlags): + """Wraps up a Discord Invite flag value. + + See :class:`SystemChannelFlags`. + + .. container:: operations + + .. describe:: x == y + + Checks if two InviteFlags instances are equal. + .. describe:: x != y + + Checks if two InviteFlags instances are not equal. + .. describe:: x <= y + + Checks if a InviteFlags instance is a subset of another InviteFlags instance. + + .. describe:: x >= y + + Checks if a InviteFlags instance is a superset of another InviteFlags instance. + + .. describe:: x < y + + Checks if a InviteFlags instance is a strict subset of another InviteFlags instance. + + .. describe:: x > y + + Checks if a InviteFlags instance is a strict superset of another InviteFlags instance. + + .. describe:: x | y, x |= y + + Returns a new InviteFlags instance with all enabled flags from both x and y. + (Using ``|=`` will update in place). + + .. describe:: x & y, x &= y + + Returns a new InviteFlags instance with only flags enabled on both x and y. + (Using ``&=`` will update in place). + + .. describe:: x ^ y, x ^= y + + Returns a new InviteFlags instance with only flags enabled on one of x or y, but not both. + (Using ``^=`` will update in place). + + .. describe:: ~x + + Returns a new InviteFlags instance with all flags from x inverted. + + .. describe:: hash(x) + + Return the flag's hash. + .. describe:: iter(x) + + Returns an iterator of ``(name, value)`` pairs. This allows it + to be, for example, constructed as a dict or a list of pairs. + + + Additionally supported are a few operations on class attributes. + + .. describe:: InviteFlags.y | InviteFlags.z, InviteFlags(y=True) | InviteFlags.z + + Returns a InviteFlags instance with all provided flags enabled. + + .. describe:: ~InviteFlags.y + + Returns a InviteFlags instance with all flags except ``y`` inverted from their default value. + + .. versionadded:: 2.10 + + Attributes + ---------- + value: :class:`int` + The raw value. This value is a bit array field of a 53-bit integer + representing the currently available flags. You should query + flags via the properties rather than using this raw value. + """ + + __slots__ = () + + if TYPE_CHECKING: + + @_generated + def __init__(self, *, guest: bool = ...) -> None: + ... + + @flag_value + def guest(self): + """:class:`bool`: Returns ``True`` if the invite is a guest invite.""" + return 1 << 0 diff --git a/disnake/guild.py b/disnake/guild.py index d68ff87e0e..bacd7ebe14 100644 --- a/disnake/guild.py +++ b/disnake/guild.py @@ -219,6 +219,7 @@ class Guild(Hashable): - ``DISCOVERABLE``: Guild shows up in Server Discovery. - ``ENABLED_DISCOVERABLE_BEFORE``: Guild had Server Discovery enabled at least once. - ``FEATURABLE``: Guild is able to be featured in Server Discovery. + - ``GUESTS_ENABLED``: Guild has access to guest invites. - ``HAS_DIRECTORY_ENTRY``: Guild is listed in a student hub. - ``HUB``: Guild is a student hub. - ``INVITE_SPLASH``: Guild's invite page can have a special splash. diff --git a/disnake/invite.py b/disnake/invite.py index 2d95ea6d8a..6b5a5ae95c 100644 --- a/disnake/invite.py +++ b/disnake/invite.py @@ -7,6 +7,7 @@ from .appinfo import PartialAppInfo from .asset import Asset from .enums import ChannelType, InviteTarget, NSFWLevel, VerificationLevel, try_enum +from .flags import InviteFlags from .guild_scheduled_event import GuildScheduledEvent from .mixins import Hashable from .object import Object @@ -398,6 +399,7 @@ class Invite(Hashable): "guild_scheduled_event", "guild_welcome_screen", "_state", + "_flags", ) BASE = "https://discord.gg" @@ -466,6 +468,8 @@ def __init__( else: self.guild_scheduled_event: Optional[GuildScheduledEvent] = None + self._flags: int = data.get("flags", 0) + @classmethod def from_incomplete(cls, *, state: ConnectionState, data: InvitePayload) -> Self: guild: Optional[Union[Guild, PartialInviteGuild]] @@ -565,6 +569,14 @@ def url(self) -> str: url += f"?event={self.guild_scheduled_event.id}" return url + @property + def flags(self) -> InviteFlags: + """:class:`InviteFlags`: Invite's flags. + + .. versionadded:: 2.10 + """ + return InviteFlags._from_value(self._flags) + async def delete(self, *, reason: Optional[str] = None) -> None: """|coro| diff --git a/disnake/member.py b/disnake/member.py index f3ff9008f7..eae3f882b2 100644 --- a/disnake/member.py +++ b/disnake/member.py @@ -716,6 +716,18 @@ def flags(self) -> MemberFlags: """ return MemberFlags._from_value(self._flags) + def is_guest(self) -> bool: + """Whether this member joined the guild as a guest (i.e. via a guest invite). + + .. versionadded:: 2.10 + + Returns + ------- + :class:`bool` + Whether the member is a guest. + """ + return self.joined_at is None + @overload async def ban( self, diff --git a/disnake/types/gateway.py b/disnake/types/gateway.py index 39d46d3062..96e9366b3d 100644 --- a/disnake/types/gateway.py +++ b/disnake/types/gateway.py @@ -338,6 +338,7 @@ class InviteCreateEvent(TypedDict): channel_id: Snowflake code: str created_at: str + flags: NotRequired[int] guild_id: NotRequired[Snowflake] inviter: NotRequired[User] max_age: int diff --git a/disnake/types/guild.py b/disnake/types/guild.py index 413199681f..ff2b1a1781 100644 --- a/disnake/types/guild.py +++ b/disnake/types/guild.py @@ -47,6 +47,7 @@ class UnavailableGuild(TypedDict): "DISCOVERABLE", "ENABLED_DISCOVERABLE_BEFORE", "FEATURABLE", + "GUESTS_ENABLED", "GUILD_HOME_TEST", # not yet documented/finalised "HAS_DIRECTORY_ENTRY", "HUB", diff --git a/disnake/types/invite.py b/disnake/types/invite.py index 93e573cfa5..42b54fe9f3 100644 --- a/disnake/types/invite.py +++ b/disnake/types/invite.py @@ -30,6 +30,7 @@ class _InviteMetadata(TypedDict, total=False): class Invite(_InviteMetadata): code: str + flags: NotRequired[int] guild: NotRequired[InviteGuild] channel: InviteChannel inviter: NotRequired[PartialUser] diff --git a/docs/api/invites.rst b/docs/api/invites.rst index 729f91272a..43a9cd6fa1 100644 --- a/docs/api/invites.rst +++ b/docs/api/invites.rst @@ -34,6 +34,17 @@ PartialInviteChannel .. autoclass:: PartialInviteChannel() :members: +Data Classes +------------ + +InviteFlags +~~~~~~~~~~~ + +.. attributetable:: InviteFlags + +.. autoclass:: InviteFlags() + :members: + Enumerations ------------