Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add Invite.type #1142

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions changelog/1141.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Support fetching invites with ``null`` channel (e.g. friend invites).
1 change: 1 addition & 0 deletions changelog/1141.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add :attr:`Invite.type`.
9 changes: 7 additions & 2 deletions disnake/audit_logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
DefaultReaction as DefaultReactionPayload,
PermissionOverwrite as PermissionOverwritePayload,
)
from .types.invite import Invite as InvitePayload
from .types.role import Role as RolePayload
from .types.snowflake import Snowflake
from .types.threads import ForumTag as ForumTagPayload
Expand Down Expand Up @@ -799,15 +800,19 @@ def _convert_target_invite(self, target_id: int) -> Invite:
# so figure out which change has the full invite data
changeset = self.before if self.action is enums.AuditLogAction.invite_delete else self.after

fake_payload = {
fake_payload: InvitePayload = {
"max_age": changeset.max_age,
"max_uses": changeset.max_uses,
"code": changeset.code,
"temporary": changeset.temporary,
"uses": changeset.uses,
"type": 0,
"channel": None,
}

obj = Invite(state=self._state, data=fake_payload, guild=self.guild, channel=changeset.channel) # type: ignore
obj = Invite(
state=self._state, data=fake_payload, guild=self.guild, channel=changeset.channel
)
try:
obj.inviter = changeset.inviter
except AttributeError:
Expand Down
7 changes: 7 additions & 0 deletions disnake/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"ExpireBehavior",
"StickerType",
"StickerFormatType",
"InviteType",
"InviteTarget",
"VideoQualityMode",
"ComponentType",
Expand Down Expand Up @@ -602,6 +603,12 @@ def file_extension(self) -> str:
}


class InviteType(Enum):
guild = 0
group_dm = 1
friend = 2


class InviteTarget(Enum):
unknown = 0
stream = 1
Expand Down
11 changes: 9 additions & 2 deletions disnake/guild.py
Original file line number Diff line number Diff line change
Expand Up @@ -3185,7 +3185,10 @@ async def invites(self) -> List[Invite]:
data = await self._state.http.invites_from(self.id)
result = []
for invite in data:
channel = self.get_channel(int(invite["channel"]["id"]))
if channel_data := invite.get("channel"):
channel = self.get_channel(int(channel_data["id"]))
else:
channel = None
result.append(Invite(state=self._state, data=invite, guild=self, channel=channel))

return result
Expand Down Expand Up @@ -4130,11 +4133,15 @@ async def vanity_invite(self, *, use_cached: bool = False) -> Optional[Invite]:
# reliable or a thing anymore
data = await self._state.http.get_invite(payload["code"])

channel = self.get_channel(int(data["channel"]["id"]))
if channel_data := data.get("channel"):
channel = self.get_channel(int(channel_data["id"]))
else:
channel = None
payload["temporary"] = False
payload["max_uses"] = 0
payload["max_age"] = 0
payload["uses"] = payload.get("uses", 0)
payload["type"] = 0
return Invite(state=self._state, data=payload, guild=self, channel=channel)

# TODO: use MISSING when async iterators get refactored
Expand Down
38 changes: 22 additions & 16 deletions disnake/invite.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from .appinfo import PartialAppInfo
from .asset import Asset
from .enums import ChannelType, InviteTarget, NSFWLevel, VerificationLevel, try_enum
from .enums import ChannelType, InviteTarget, InviteType, NSFWLevel, VerificationLevel, try_enum
from .guild_scheduled_event import GuildScheduledEvent
from .mixins import Hashable
from .object import Object
Expand Down Expand Up @@ -307,8 +307,13 @@ class Invite(Hashable):
----------
code: :class:`str`
The URL fragment used for the invite.
type: :class:`InviteType`
The type of the invite.

.. versionadded:: 2.10

guild: Optional[Union[:class:`Guild`, :class:`Object`, :class:`PartialInviteGuild`]]
The guild the invite is for. Can be ``None`` if it's from a group direct message.
The guild the invite is for. Can be ``None`` if it's not a guild invite (see :attr:`type`).
max_age: Optional[:class:`int`]
How long before the invite expires in seconds.
A value of ``0`` indicates that it doesn't expire.
Expand Down Expand Up @@ -382,6 +387,7 @@ class Invite(Hashable):
__slots__ = (
"max_age",
"code",
"type",
"guild",
"created_at",
"uses",
Expand Down Expand Up @@ -412,6 +418,7 @@ def __init__(
) -> None:
self._state: ConnectionState = state
self.code: str = data["code"]
self.type: InviteType = try_enum(InviteType, data.get("type", 0))
self.guild: Optional[InviteGuildType] = self._resolve_guild(data.get("guild"), guild)

self.max_age: Optional[int] = data.get("max_age")
Expand Down Expand Up @@ -481,15 +488,12 @@ def from_incomplete(cls, *, state: ConnectionState, data: InvitePayload) -> Self
# If it's not cached, then it has to be a partial guild
guild = PartialInviteGuild(state, guild_data, guild_id)

# todo: this is no longer true
# As far as I know, invites always need a channel
# So this should never raise.
channel: Union[PartialInviteChannel, GuildChannel] = PartialInviteChannel(
data=data["channel"], state=state
)
if guild is not None and not isinstance(guild, PartialInviteGuild):
# Upgrade the partial data if applicable
channel = guild.get_channel(channel.id) or channel
channel: Optional[Union[PartialInviteChannel, GuildChannel]] = None
if channel_data := data.get("channel"):
channel = PartialInviteChannel(data=channel_data, state=state)
if guild is not None and not isinstance(guild, PartialInviteGuild):
# Upgrade the partial data if applicable
channel = guild.get_channel(channel.id) or channel

return cls(state=state, data=data, guild=guild, channel=channel)

Expand Down Expand Up @@ -543,11 +547,13 @@ def __str__(self) -> str:
return self.url

def __repr__(self) -> str:
return (
f"<Invite code={self.code!r} guild={self.guild!r} "
f"online={self.approximate_presence_count} "
f"members={self.approximate_member_count}>"
)
s = f"<Invite code={self.code!r} type={self.type!r}"
if self.type is InviteType.guild:
s += f" guild={self.guild!r} online={self.approximate_presence_count}"
if self.type is not InviteType.friend:
shiftinv marked this conversation as resolved.
Show resolved Hide resolved
s += f" members={self.approximate_member_count}"
s += ">"
return s

def __hash__(self) -> int:
return hash(self.code)
Expand Down
3 changes: 2 additions & 1 deletion disnake/types/gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from .guild_scheduled_event import GuildScheduledEvent
from .integration import BaseIntegration
from .interactions import BaseInteraction, GuildApplicationCommandPermissions
from .invite import InviteTargetType
from .invite import InviteTargetType, InviteType
from .member import MemberWithUser
from .message import Message
from .role import Role
Expand Down Expand Up @@ -348,6 +348,7 @@ class InviteCreateEvent(TypedDict):
target_user: NotRequired[User]
target_application: NotRequired[PartialAppInfo]
temporary: bool
type: InviteType
uses: int # always 0


Expand Down
4 changes: 3 additions & 1 deletion disnake/types/invite.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from .guild_scheduled_event import GuildScheduledEvent
from .user import PartialUser

InviteType = Literal[0, 1, 2]
InviteTargetType = Literal[1, 2]


Expand All @@ -30,8 +31,9 @@ class _InviteMetadata(TypedDict, total=False):

class Invite(_InviteMetadata):
code: str
type: InviteType
guild: NotRequired[InviteGuild]
channel: InviteChannel
channel: Optional[InviteChannel]
inviter: NotRequired[PartialUser]
target_type: NotRequired[InviteTargetType]
target_user: NotRequired[PartialUser]
Expand Down
21 changes: 21 additions & 0 deletions docs/api/invites.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,27 @@ PartialInviteChannel
Enumerations
------------

InviteType
~~~~~~~~~~

.. class:: InviteType

Represents the type of an invite.

.. versionadded:: 2.10

.. attribute:: guild

Represents an invite to a guild.

.. attribute:: group_dm

Represents an invite to a group channel.

.. attribute:: friend

Represents a friend invite.

InviteTarget
~~~~~~~~~~~~

Expand Down
Loading