diff --git a/discord/abc.py b/discord/abc.py index 692472f8f..62688731b 100644 --- a/discord/abc.py +++ b/discord/abc.py @@ -60,6 +60,7 @@ from .http import handle_message_parameters from .voice_client import VoiceClient, VoiceProtocol from .sticker import GuildSticker, StickerItem from . import utils +from .flags import GuildInviteFlags __all__ = ( 'Snowflake', @@ -1257,6 +1258,7 @@ class GuildChannel: target_type: Optional[InviteTarget] = None, target_user: Optional[User] = None, target_application_id: Optional[int] = None, + guest_invite: bool = False, ) -> Invite: """|coro| @@ -1295,6 +1297,12 @@ class GuildChannel: The id of the embedded application for the invite, required if ``target_type`` is :attr:`.InviteTarget.embedded_application`. .. versionadded:: 2.0 + guest_invite: :class:`bool` + Whether the invite is a guest invite. + + This is only available to guilds that contain ``GUESTS_ENABLED`` in :attr:`.Guild.features`. + + .. versionadded:: 2.6 Raises ------- @@ -1312,6 +1320,10 @@ class GuildChannel: if target_type is InviteTarget.unknown: raise ValueError('Cannot create invite with an unknown target type') + flags = GuildInviteFlags._from_value(0) + if guest_invite: + flags.is_guest_invite = True + data = await self._state.http.create_invite( self.id, reason=reason, @@ -1322,6 +1334,7 @@ class GuildChannel: target_type=target_type.value if target_type else None, target_user_id=target_user.id if target_user else None, target_application_id=target_application_id, + flags=flags.value, ) return Invite.from_incomplete(data=data, state=self._state) diff --git a/discord/flags.py b/discord/flags.py index 20f8c5470..3327f2c75 100644 --- a/discord/flags.py +++ b/discord/flags.py @@ -64,6 +64,7 @@ __all__ = ( 'AppInstallationType', 'SKUFlags', 'EmbedFlags', + 'GuildInviteFlags', ) BF = TypeVar('BF', bound='BaseFlags') @@ -2397,3 +2398,59 @@ class EmbedFlags(BaseFlags): longer displayed. """ return 1 << 5 + + +class GuildInviteFlags(BaseFlags): + r"""Wraps up the Discord Guild Invite flags + + .. versionadded:: 2.6 + + .. container:: operations + + .. describe:: x == y + + Checks if two GuildInviteFlags are equal. + + .. describe:: x != y + + Checks if two GuildInviteFlags are not equal. + + .. describe:: x | y, x |= y + + Returns a GuildInviteFlags instance with all enabled flags from + both x and y. + + .. describe:: x ^ y, x ^= y + + Returns a GuildInviteFlags instance with only flags enabled on + only one of x or y, not on both. + + .. describe:: ~x + + Returns a GuildInviteFlags instance with all flags inverted from x. + + .. describe:: hash(x) + + Returns 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. + Note that aliases are not shown. + + .. describe:: bool(b) + + Returns whether any flag is set to ``True``. + + Attributes + ---------- + value: :class:`int` + The raw value. You should query flags via the properties + rather than using this raw value. + """ + + @flag_value + def is_guest_invite(self): + """:class:`bool`: Returns ``True`` if this is a guest invite for a voice channel.""" + return 1 << 0 diff --git a/discord/http.py b/discord/http.py index f1f4ec58b..28371031e 100644 --- a/discord/http.py +++ b/discord/http.py @@ -1834,6 +1834,7 @@ class HTTPClient: target_type: Optional[invite.InviteTargetType] = None, target_user_id: Optional[Snowflake] = None, target_application_id: Optional[Snowflake] = None, + flags: Optional[int] = None, ) -> Response[invite.Invite]: r = Route('POST', '/channels/{channel_id}/invites', channel_id=channel_id) payload = { @@ -1852,6 +1853,9 @@ class HTTPClient: if target_application_id: payload['target_application_id'] = str(target_application_id) + if flags is not None: + payload['flags'] = flags + return self.request(r, reason=reason, json=payload) def get_invite( diff --git a/discord/invite.py b/discord/invite.py index 8c37bd232..cde6217e7 100644 --- a/discord/invite.py +++ b/discord/invite.py @@ -32,6 +32,7 @@ from .mixins import Hashable from .enums import ChannelType, NSFWLevel, VerificationLevel, InviteTarget, InviteType, try_enum from .appinfo import PartialAppInfo from .scheduled_event import ScheduledEvent +from .flags import GuildInviteFlags __all__ = ( 'PartialInviteChannel', @@ -379,6 +380,7 @@ class Invite(Hashable): 'scheduled_event', 'scheduled_event_id', 'type', + '_flags', ) BASE = 'https://discord.gg' @@ -432,6 +434,7 @@ class Invite(Hashable): else None ) self.scheduled_event_id: Optional[int] = self.scheduled_event.id if self.scheduled_event else None + self._flags: int = data.get('flags', 0) @classmethod def from_incomplete(cls, *, state: ConnectionState, data: InvitePayload) -> Self: @@ -523,6 +526,14 @@ class Invite(Hashable): url += '?event=' + str(self.scheduled_event_id) return url + @property + def flags(self) -> GuildInviteFlags: + """:class:`GuildInviteFlags`: Returns the flags for this guild invite. + + .. versionadded:: 2.6 + """ + return GuildInviteFlags._from_value(self._flags) + def set_scheduled_event(self, scheduled_event: Snowflake, /) -> Self: """Sets the scheduled event for this invite. diff --git a/discord/member.py b/discord/member.py index 6af1571f4..ed52600dd 100644 --- a/discord/member.py +++ b/discord/member.py @@ -238,7 +238,8 @@ class Member(discord.abc.Messageable, _UserTag): ---------- joined_at: Optional[:class:`datetime.datetime`] An aware datetime object that specifies the date and time in UTC that the member joined the guild. - If the member left and rejoined the guild, this will be the latest date. In certain cases, this can be ``None``. + If the member left and rejoined the guild, this will be the latest date. + This can be ``None``, such as when the member is a guest. activities: Tuple[Union[:class:`BaseActivity`, :class:`Spotify`]] The activities that the user is currently doing. diff --git a/discord/types/invite.py b/discord/types/invite.py index f5f00078e..47c972994 100644 --- a/discord/types/invite.py +++ b/discord/types/invite.py @@ -65,6 +65,7 @@ class Invite(IncompleteInvite, total=False): target_application: PartialAppInfo guild_scheduled_event: GuildScheduledEvent type: InviteType + flags: NotRequired[int] class InviteWithCounts(Invite, _GuildPreviewUnique): @@ -84,6 +85,7 @@ class GatewayInviteCreate(TypedDict): target_type: NotRequired[InviteTargetType] target_user: NotRequired[PartialUser] target_application: NotRequired[PartialAppInfo] + flags: NotRequired[int] class GatewayInviteDelete(TypedDict): diff --git a/discord/types/member.py b/discord/types/member.py index 88fb619fd..576ef421d 100644 --- a/discord/types/member.py +++ b/discord/types/member.py @@ -34,7 +34,7 @@ class Nickname(TypedDict): class PartialMember(TypedDict): roles: SnowflakeList - joined_at: str + joined_at: Optional[str] # null if guest deaf: bool mute: bool flags: int diff --git a/docs/api.rst b/docs/api.rst index dda5553b7..89cefefed 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -5734,6 +5734,14 @@ EmbedFlags .. autoclass:: EmbedFlags() :members: +GuildInviteFlags +~~~~~~~~~~~~~~~~ + +.. attributetable:: GuildInviteFlags + +.. autoclass:: GuildInviteFlags( + :members: + ForumTag ~~~~~~~~~