diff --git a/CHANGELOG.md b/CHANGELOG.md index d8ecf9a5c0..e362e9c00e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,11 @@ These changes are available on the `master` branch, but have not yet been releas ### Changed +- Inline `_InviteMetadata` fields into `IncompleteInvite`. + ([#3313](https://github.com/Pycord-Development/pycord/pull/3313)) +- Accept `IncompleteInvite` instead of `VanityInvite` in `Invite.__init__`. + ([#3313](https://github.com/Pycord-Development/pycord/pull/3313)) + ### Fixed - Fix `TypeError` when accessing `ApplicationCommand.guild_only` or @@ -22,6 +27,9 @@ These changes are available on the `master` branch, but have not yet been releas ### Deprecated +- Deprecated `Invite.id` in favor of `Invite.code`. + ([#3313](https://github.com/Pycord-Development/pycord/pull/3313)) + ### Removed ## [2.8.1] - 2026-07-25 diff --git a/discord/guild.py b/discord/guild.py index 0053cbfd61..a4661d0131 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -39,6 +39,7 @@ Tuple, TypeVar, Union, + cast, overload, ) @@ -121,6 +122,7 @@ from .types.guild import Guild as GuildPayload from .types.guild import GuildFeature, MFALevel from .types.guild import ModifyIncidents as ModifyIncidentsPayload + from .types.invite import IncompleteInvite from .types.member import Member as MemberPayload from .types.threads import Thread as ThreadPayload from .types.voice import VoiceState as GuildVoiceState @@ -3786,7 +3788,13 @@ async def vanity_invite(self) -> Invite | None: payload["max_uses"] = 0 payload["max_age"] = 0 payload["uses"] = payload.get("uses", 0) - return Invite(state=self._state, data=payload, guild=self, channel=channel) + + return Invite( + state=self._state, + data=cast("IncompleteInvite", payload), + guild=self, + channel=channel, + ) # TODO: use MISSING when async iterators get refactored def audit_logs( diff --git a/discord/invite.py b/discord/invite.py old mode 100644 new mode 100755 index 198f0987bf..89aebae948 --- a/discord/invite.py +++ b/discord/invite.py @@ -30,7 +30,7 @@ import os from typing import TYPE_CHECKING, TypeVar, Union -from typing_extensions import override +from typing_extensions import deprecated, override from .appinfo import PartialAppInfo from .asset import Asset @@ -62,12 +62,12 @@ from .state import ConnectionState from .types.channel import PartialChannel as InviteChannelPayload from .types.invite import GatewayInvite as GatewayInvitePayload + from .types.invite import IncompleteInvite as IncompleteInvitePayload from .types.invite import Invite as InvitePayload from .types.invite import InviteGuild as InviteGuildPayload from .types.invite import ( InviteTargetUsersJobStatus as InviteTargetUsersJobStatusPayload, ) - from .types.invite import VanityInvite as VanityInvitePayload from .types.role import Role as RolePayload from .types.scheduled_events import ScheduledEvent as ScheduledEventPayload from .types.snowflake import Snowflake @@ -512,25 +512,25 @@ class Invite(Hashable): """ __slots__ = ( - "max_age", - "code", - "guild", - "revoked", - "created_at", - "uses", - "temporary", - "max_uses", - "inviter", - "channel", - "target_user", - "target_type", "_state", "approximate_member_count", "approximate_presence_count", - "scheduled_event", - "target_application", + "channel", + "code", + "created_at", "expires_at", + "guild", + "inviter", + "max_age", + "max_uses", + "revoked", "roles", + "scheduled_event", + "target_application", + "target_type", + "target_user", + "temporary", + "uses", ) BASE = "https://discord.gg" @@ -539,7 +539,7 @@ def __init__( self, *, state: ConnectionState, - data: InvitePayload | VanityInvitePayload, + data: InvitePayload | IncompleteInvitePayload, guild: PartialInviteGuild | Guild | None = None, channel: PartialInviteChannel | GuildChannel | None = None, ): @@ -689,7 +689,7 @@ def _resolve_roles( return [Object(role_id) for role_id in role_ids] def __str__(self) -> str: - return self.url + return self.url if self.code is not None else "" def __repr__(self) -> str: return ( @@ -705,13 +705,18 @@ def __hash__(self) -> int: return hash(self.code) @property - def id(self) -> str: + @deprecated( + "Invite.id is deprecated since version 2.9, consider using Invite.code instead." + ) + def id(self) -> str: # type: ignore[override] """Returns the proper code portion of the invite.""" - return self.code + return self.code or "" @property def url(self) -> str: """A property that retrieves the invite URL.""" + if self.code is None: + raise ValueError("Cannot build an invite URL when code is None") return f"{self.BASE}/{self.code}{f'?event={self.scheduled_event.id}' if self.scheduled_event else ''}" @property @@ -795,7 +800,6 @@ async def delete(self, *, reason: str | None = None): HTTPException Revoking the invite failed. """ - await self._state.http.delete_invite(self.code, reason=reason) def set_scheduled_event(self, event: ScheduledEvent) -> None: diff --git a/discord/types/invite.py b/discord/types/invite.py index 584b573094..6d2b511b69 100644 --- a/discord/types/invite.py +++ b/discord/types/invite.py @@ -40,22 +40,20 @@ InviteTargetType = Literal[1, 2] -class _InviteMetadata(TypedDict, total=False): - uses: int - max_uses: int - max_age: int - temporary: bool - created_at: str - expires_at: str | None - - -class VanityInvite(_InviteMetadata): +class VanityInvite(TypedDict): code: str | None + uses: int -class IncompleteInvite(_InviteMetadata): +class IncompleteInvite(TypedDict): code: str channel: PartialChannel + uses: NotRequired[int] + max_uses: NotRequired[int] + max_age: NotRequired[int] + temporary: NotRequired[bool] + created_at: NotRequired[str] + expires_at: NotRequired[str | None] class Invite(IncompleteInvite):