Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/1510.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add the new :meth:`Invite.fetch_target_users`, :meth:`Invite.update_target_users`, :meth:`Invite.fetch_target_users_job_status` methods as well as :attr:`Invite.roles`, :attr:`Invite.flags`, :class:`GuildInviteFlags`. Update :meth:`abc.GuildChannel.create_invite` to be able to specify ``roles`` and the ``target_users_file``.
24 changes: 23 additions & 1 deletion disnake/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1300,10 +1300,12 @@ async def create_invite(
unique: bool = True,
target_type: InviteTarget | None = None,
target_user: User | None = None,
target_users_file: File | None = None,
target_application: Snowflake | PartyType | None = None,
guild_scheduled_event: GuildScheduledEvent | None = None,
roles: list[Role] | None = None,
Comment thread
Snipy7374 marked this conversation as resolved.
Outdated
) -> Invite:
"""|coro|
r"""|coro|

Creates an instant invite from a text or voice channel.

Expand Down Expand Up @@ -1336,6 +1338,17 @@ async def create_invite(

.. versionadded:: 2.0

target_users_file: :class:`~disnake.File` | :data:`None`
A csv file with a list of users able to accept the invite.
This file must only have valid user ids separated by ``/n``.
Comment thread
Snipy7374 marked this conversation as resolved.
Outdated
A valid file content would look like this: ::
Comment thread
Snipy7374 marked this conversation as resolved.
Outdated

710570210159099984
1081815963990761542
... other user ids

.. versionadded:: 2.13
Comment thread
Snipy7374 marked this conversation as resolved.
Outdated

target_application: :class:`.Snowflake` | :data:`None`
The ID of the embedded application for the invite, required if ``target_type`` is :attr:`.InviteTarget.embedded_application`.

Expand All @@ -1349,6 +1362,13 @@ async def create_invite(

.. versionadded:: 2.3

roles: :class:`list`\[:class:`.Role`] | :data:`None`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
roles: :class:`list`\[:class:`.Role`] | :data:`None`
roles: :class:`~collections.abc.Collection`\[:class:`.Role`] | :data:`None`

A list of roles added to the user upon accepting the invite.
You must have the :attr:`.Permissions.manage_roles` permission and cannot assign roles with
higher permissions than you to do this.
Comment thread
Snipy7374 marked this conversation as resolved.
Outdated

.. versionadded:: 2.13
Comment thread
Snipy7374 marked this conversation as resolved.
Outdated

reason: :class:`str` | :data:`None`
The reason for creating this invite. Shows up on the audit log.

Expand Down Expand Up @@ -1379,7 +1399,9 @@ async def create_invite(
unique=unique,
target_type=try_enum_to_int(target_type),
target_user_id=target_user.id if target_user else None,
target_users_file=target_users_file,
target_application_id=target_application.id if target_application else None,
role_ids=[r.id for r in roles] if roles else None,
)
invite = Invite.from_incomplete(data=data, state=self._state)
invite.guild_scheduled_event = guild_scheduled_event
Expand Down
75 changes: 75 additions & 0 deletions disnake/flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"SKUFlags",
"ApplicationInstallTypes",
"InteractionContextTypes",
"GuildInviteFlags",
)

BF = TypeVar("BF", bound="BaseFlags")
Expand Down Expand Up @@ -2922,3 +2923,77 @@ def bot_dm(self) -> int:
def private_channel(self) -> int:
""":class:`bool`: Returns ``True`` if the command is usable in DMs and group DMs with other users."""
return 1 << 2


class GuildInviteFlags(BaseFlags):
"""Wraps up Discord Invite flags.

.. collapse:: operations

.. describe:: x == y

Checks if two GuildInviteFlags instances are equal.
.. describe:: x != y

Checks if two GuildInviteFlags instances are not equal.
.. describe:: x <= y

Checks if a GuildInviteFlags instance is a subset of another GuildInviteFlags instance.
.. describe:: x >= y

Checks if a GuildInviteFlags instance is a superset of another GuildInviteFlags instance.
.. describe:: x < y

Checks if a GuildInviteFlags instance is a strict subset of another GuildInviteFlags instance.
.. describe:: x > y

Checks if a GuildInviteFlags instance is a strict superset of another GuildInviteFlags instance.
.. describe:: x | y, x |= y

Returns a new GuildInviteFlags instance with all enabled flags from both x and y.
(Using ``|=`` will update in place).
.. describe:: x & y, x &= y

Returns a new GuildInviteFlags instance with only flags enabled on both x and y.
(Using ``&=`` will update in place).
.. describe:: x ^ y, x ^= y

Returns a new GuildInviteFlags instance with only flags enabled on one of x or y, but not both.
(Using ``^=`` will update in place).
.. describe:: ~x

Returns a new GuildInviteFlags instance with all flags from x inverted.
.. 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.

Additionally supported are a few operations on class attributes.

.. describe:: GuildInviteFlags.y | GuildInviteFlags.z, GuildInviteFlags(y=True) | GuildInviteFlags.z

Returns a GuildInviteFlags instance with all provided flags enabled.

.. describe:: ~GuildInviteFlags.y

Returns a GuildInviteFlags instance with all flags except ``y`` inverted from their default value.

.. versionadded:: 2.13
Comment thread
Snipy7374 marked this conversation as resolved.
Outdated

Attributes
----------
value: :class:`int`
The raw value. You should query flags via the properties
rather than using this raw value.
"""

__slots__ = ()

@flag_value
def is_guest_invite(self) -> int:
""":class:`bool`: Returns ``True`` if this invite is a guest invite for a voice channel."""
return 1 << 0
53 changes: 44 additions & 9 deletions disnake/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,20 +132,27 @@ def set_attachments(payload: dict[str, Any], files: Sequence[File]) -> None:
payload["attachments"] = attachments


def to_multipart(payload: dict[str, Any], files: Sequence[File]) -> list[dict[str, Any]]:
def to_multipart(
payload: dict[str, Any], files: Sequence[File], *, is_csv: bool = False
) -> list[dict[str, Any]]:
"""Converts the payload and list of files to a multipart payload,
as specified by https://docs.discord.com/developers/reference#uploading-files
"""
multipart: list[dict[str, Any]] = []
for index, file in enumerate(files):
multipart.append(
{
"name": f"files[{index}]",
"value": file.fp,
"filename": file.filename,
"content_type": "application/octet-stream",
}
)
if is_csv:
multipart.append(
{"name": "target_users_file", "value": file.fp, "content_type": "text/csv"}
)
else:
multipart.append(
{
"name": f"files[{index}]",
"value": file.fp,
"filename": file.filename,
"content_type": "application/octet-stream",
}
)
Comment thread
Snipy7374 marked this conversation as resolved.
Outdated

multipart.append({"name": "payload_json", "value": utils._to_json(payload)})
return multipart
Expand Down Expand Up @@ -1950,7 +1957,9 @@ def create_invite(
unique: bool = True,
target_type: invite.InviteTargetType | None = None,
target_user_id: Snowflake | None = None,
target_users_file: File | None = None,
target_application_id: Snowflake | None = None,
role_ids: list[Snowflake] | None = None,
) -> Response[invite.Invite]:
r = Route("POST", "/channels/{channel_id}/invites", channel_id=channel_id)
payload: dict[str, Any] = {
Expand All @@ -1969,6 +1978,16 @@ def create_invite(
if target_application_id:
payload["target_application_id"] = str(target_application_id)

if role_ids:
payload["role_ids"] = role_ids

if target_users_file:
return self.request(
r,
reason=reason,
form=to_multipart(payload=payload, files=[target_users_file], is_csv=True),
)

return self.request(r, reason=reason, json=payload)

def get_invite(
Expand All @@ -1988,6 +2007,22 @@ def get_invite(
Route("GET", "/invites/{invite_id}", invite_id=invite_id), params=params
)

def get_invite_target_users(self, invite_id: str) -> Response[str]:
return self.request(Route("GET", "/invites/{invite_id}/target-users", invite_id=invite_id))

def update_invite_target_users(self, invite_id: str, *, file: File) -> Response[None]:
return self.request(
Route("PUT", "/invites/{invite_id}/target-users", invite_id=invite_id),
form=to_multipart(payload={}, files=[file], is_csv=True),
)

def get_invite_target_users_job_status(
self, invite_id: str
) -> Response[invite.TargetUsersJobPayload]:
return self.request(
Route("GET", "/invites/{invite_id}/target-users/job-status", invite_id=invite_id)
)

def invites_from(self, guild_id: Snowflake) -> Response[list[invite.Invite]]:
return self.request(Route("GET", "/guilds/{guild_id}/invites", guild_id=guild_id))

Expand Down
Loading
Loading