d4bf750c9e
Initial commit
819 lines
28 KiB
Python
819 lines
28 KiB
Python
"""
|
|
The MIT License (MIT)
|
|
|
|
Copyright (c) 2015-2021 Rapptz
|
|
Copyright (c) 2021-present Pycord Development
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a
|
|
copy of this software and associated documentation files (the "Software"),
|
|
to deal in the Software without restriction, including without limitation
|
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
and/or sell copies of the Software, and to permit persons to whom the
|
|
Software is furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
DEALINGS IN THE SOFTWARE.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import datetime
|
|
import io
|
|
import os
|
|
from typing import TYPE_CHECKING, TypeVar, Union
|
|
|
|
from typing_extensions import override
|
|
|
|
from .appinfo import PartialAppInfo
|
|
from .asset import Asset
|
|
from .enums import (
|
|
ChannelType,
|
|
InviteTarget,
|
|
InviteTargetUsersJobStatusCode,
|
|
VerificationLevel,
|
|
try_enum,
|
|
)
|
|
from .file import File
|
|
from .mixins import Hashable
|
|
from .object import Object
|
|
from .role import Role
|
|
from .utils import _get_as_snowflake, parse_time, snowflake_time
|
|
|
|
__all__ = (
|
|
"PartialInviteChannel",
|
|
"PartialInviteGuild",
|
|
"Invite",
|
|
"InviteTargetUsers",
|
|
"InviteTargetUsersJobStatus",
|
|
)
|
|
|
|
if TYPE_CHECKING:
|
|
from .abc import GuildChannel
|
|
from .guild import Guild
|
|
from .scheduled_events import ScheduledEvent
|
|
from .state import ConnectionState
|
|
from .types.channel import PartialChannel as InviteChannelPayload
|
|
from .types.invite import GatewayInvite as GatewayInvitePayload
|
|
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
|
|
from .user import User
|
|
|
|
InviteGuildType = Union[Guild, "PartialInviteGuild", Object]
|
|
InviteChannelType = Union[GuildChannel, "PartialInviteChannel", Object]
|
|
|
|
|
|
class PartialInviteChannel:
|
|
"""Represents a "partial" invite channel.
|
|
|
|
This model will be given when the user is not part of the
|
|
guild the :class:`Invite` resolves to.
|
|
|
|
.. container:: operations
|
|
|
|
.. describe:: x == y
|
|
|
|
Checks if two partial channels are the same.
|
|
|
|
.. describe:: x != y
|
|
|
|
Checks if two partial channels are not the same.
|
|
|
|
.. describe:: hash(x)
|
|
|
|
Return the partial channel's hash.
|
|
|
|
.. describe:: str(x)
|
|
|
|
Returns the partial channel's name.
|
|
|
|
Attributes
|
|
----------
|
|
name: :class:`str`
|
|
The partial channel's name.
|
|
id: :class:`int`
|
|
The partial channel's ID.
|
|
type: :class:`ChannelType`
|
|
The partial channel's type.
|
|
"""
|
|
|
|
__slots__ = ("id", "name", "type")
|
|
|
|
def __init__(self, data: InviteChannelPayload):
|
|
self.id: int = int(data["id"])
|
|
self.name: str = data["name"]
|
|
self.type: ChannelType = try_enum(ChannelType, data["type"])
|
|
|
|
def __str__(self) -> str:
|
|
return self.name
|
|
|
|
def __repr__(self) -> str:
|
|
return (
|
|
f"<PartialInviteChannel id={self.id} name={self.name} type={self.type!r}>"
|
|
)
|
|
|
|
@property
|
|
def mention(self) -> str:
|
|
"""The string that allows you to mention the channel."""
|
|
return f"<#{self.id}>"
|
|
|
|
@property
|
|
def created_at(self) -> datetime.datetime:
|
|
"""Returns the channel's creation time in UTC."""
|
|
return snowflake_time(self.id)
|
|
|
|
|
|
class PartialInviteGuild:
|
|
"""Represents a "partial" invite guild.
|
|
|
|
This model will be given when the user is not part of the
|
|
guild the :class:`Invite` resolves to.
|
|
|
|
.. container:: operations
|
|
|
|
.. describe:: x == y
|
|
|
|
Checks if two partial guilds are the same.
|
|
|
|
.. describe:: x != y
|
|
|
|
Checks if two partial guilds are not the same.
|
|
|
|
.. describe:: hash(x)
|
|
|
|
Return the partial guild's hash.
|
|
|
|
.. describe:: str(x)
|
|
|
|
Returns the partial guild's name.
|
|
|
|
Attributes
|
|
----------
|
|
name: :class:`str`
|
|
The partial guild's name.
|
|
id: :class:`int`
|
|
The partial guild's ID.
|
|
verification_level: :class:`VerificationLevel`
|
|
The partial guild's verification level.
|
|
features: List[:class:`str`]
|
|
A list of features the guild has. See :attr:`Guild.features` for more information.
|
|
description: Optional[:class:`str`]
|
|
The partial guild's description.
|
|
"""
|
|
|
|
__slots__ = (
|
|
"_state",
|
|
"features",
|
|
"_icon",
|
|
"_banner",
|
|
"id",
|
|
"name",
|
|
"_splash",
|
|
"verification_level",
|
|
"description",
|
|
)
|
|
|
|
def __init__(self, state: ConnectionState, data: InviteGuildPayload, id: int):
|
|
self._state: ConnectionState = state
|
|
self.id: int = id
|
|
self.name: str = data["name"]
|
|
self.features: list[str] = data.get("features", [])
|
|
self._icon: str | None = data.get("icon")
|
|
self._banner: str | None = data.get("banner")
|
|
self._splash: str | None = data.get("splash")
|
|
self.verification_level: VerificationLevel = try_enum(
|
|
VerificationLevel, data.get("verification_level")
|
|
)
|
|
self.description: str | None = data.get("description")
|
|
|
|
def __str__(self) -> str:
|
|
return self.name
|
|
|
|
def __repr__(self) -> str:
|
|
return (
|
|
f"<{self.__class__.__name__} id={self.id} name={self.name!r} features={self.features} "
|
|
f"description={self.description!r}>"
|
|
)
|
|
|
|
@property
|
|
def created_at(self) -> datetime.datetime:
|
|
"""Returns the guild's creation time in UTC."""
|
|
return snowflake_time(self.id)
|
|
|
|
@property
|
|
def icon(self) -> Asset | None:
|
|
"""Returns the guild's icon asset, if available."""
|
|
if self._icon is None:
|
|
return None
|
|
return Asset._from_guild_icon(self._state, self.id, self._icon)
|
|
|
|
@property
|
|
def banner(self) -> Asset | None:
|
|
"""Returns the guild's banner asset, if available."""
|
|
if self._banner is None:
|
|
return None
|
|
return Asset._from_guild_image(
|
|
self._state, self.id, self._banner, path="banners"
|
|
)
|
|
|
|
@property
|
|
def splash(self) -> Asset | None:
|
|
"""Returns the guild's invite splash asset, if available."""
|
|
if self._splash is None:
|
|
return None
|
|
return Asset._from_guild_image(
|
|
self._state, self.id, self._splash, path="splashes"
|
|
)
|
|
|
|
|
|
class InviteTargetUsersJobStatus:
|
|
"""Represents the status of a target users processing job for an invite.
|
|
|
|
.. versionadded:: 2.8
|
|
|
|
Attributes
|
|
----------
|
|
total_users: :class:`int`
|
|
The total number of users to process.
|
|
processed_users: :class:`int`
|
|
The number of users that have been processed so far.
|
|
created_at: Optional[:class:`datetime.datetime`]
|
|
When the job was created. ``None`` if the creation time is not available.
|
|
completed_at: Optional[:class:`datetime.datetime`]
|
|
When the job was completed. ``None`` if the job is still processing.
|
|
error_message: Optional[:class:`str`]
|
|
The error message if the job failed. ``None`` if no error occurred.
|
|
status: :class:`InviteTargetUsersJobStatusCode`
|
|
The current status of the job.
|
|
"""
|
|
|
|
def __init__(self, *, data: InviteTargetUsersJobStatusPayload):
|
|
self.total_users: int = data["total_users"]
|
|
self.processed_users: int = data["processed_users"]
|
|
self.created_at: datetime.datetime | None = parse_time(data.get("created_at"))
|
|
self.completed_at: datetime.datetime | None = parse_time(
|
|
data.get("completed_at")
|
|
)
|
|
self.error_message: str | None = data.get("error_message")
|
|
self.status: InviteTargetUsersJobStatusCode = try_enum(
|
|
InviteTargetUsersJobStatusCode, data["status"]
|
|
)
|
|
|
|
@override
|
|
def __repr__(self) -> str:
|
|
return (
|
|
f"<InviteTargetUsersJobStatus total_users={self.total_users} processed_users={self.processed_users} "
|
|
f"created_at={self.created_at!r} completed_at={self.completed_at!r} error_message={self.error_message} "
|
|
f"status={self.status}>"
|
|
)
|
|
|
|
|
|
class InviteTargetUsers:
|
|
"""
|
|
Represents the target users CSV file for an invite.
|
|
|
|
.. versionadded:: 2.8
|
|
|
|
Attributes
|
|
----------
|
|
invite_code: str
|
|
The invite code for which the target users are associated.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
*,
|
|
state: ConnectionState,
|
|
invite_code: str,
|
|
):
|
|
self._state: ConnectionState = state
|
|
self.invite_code: str = invite_code
|
|
|
|
async def read(self) -> bytes:
|
|
"""|coro|
|
|
|
|
Retrieves this invite's target users CSV file as a :class:`bytes` object.
|
|
|
|
You must have created this invite or the :attr:`~Permissions.manage_guild` or :attr:`~Permissions.view_audit_log`
|
|
permission to do this.
|
|
|
|
Returns
|
|
-------
|
|
:class:`bytes`
|
|
The content of the CSV file.
|
|
|
|
Raises
|
|
------
|
|
DiscordException
|
|
There was no internal connection state.
|
|
HTTPException
|
|
Downloading the file failed.
|
|
NotFound
|
|
This invite does not have any target users set.
|
|
Forbidden
|
|
You do not have permission to view the target users.
|
|
"""
|
|
return await self._state.http.get_invite_target_users(self.invite_code)
|
|
|
|
async def save(
|
|
self,
|
|
fp: str | bytes | os.PathLike[str] | io.BufferedIOBase,
|
|
*,
|
|
seek_begin: bool = True,
|
|
) -> int:
|
|
"""|coro|
|
|
|
|
Saves this invite's target users CSV file into a file-like object.
|
|
|
|
You must have created this invite or the :attr:`~Permissions.manage_guild` or :attr:`~Permissions.view_audit_log`
|
|
permission to do this.
|
|
|
|
Parameters
|
|
----------
|
|
fp: Union[:class:`io.BufferedIOBase`, :class:`os.PathLike`]
|
|
The file-like object to save this file to or the filename
|
|
to use. If a filename is passed then a file is created with that
|
|
filename and used instead.
|
|
seek_begin: :class:`bool`
|
|
Whether to seek to the beginning of the file after saving is
|
|
successfully done.
|
|
|
|
Returns
|
|
-------
|
|
:class:`int`
|
|
The number of bytes written.
|
|
|
|
Raises
|
|
------
|
|
DiscordException
|
|
There was no internal connection state.
|
|
HTTPException
|
|
Downloading the file failed.
|
|
NotFound
|
|
This invite does not have any target users set.
|
|
Forbidden
|
|
You do not have permission to view the target users.
|
|
"""
|
|
data = await self.read()
|
|
if isinstance(fp, io.BufferedIOBase):
|
|
written = fp.write(data)
|
|
if seek_begin:
|
|
fp.seek(0)
|
|
return written
|
|
else:
|
|
with open(fp, "wb") as f:
|
|
return f.write(data)
|
|
|
|
async def as_user_ids(self) -> list[int]:
|
|
"""|coro|
|
|
|
|
Retrieves a list of user IDs that can accept this invite. This internally
|
|
reads the invite's target users CSV file and parses the user IDs from it.
|
|
|
|
You must have created this invite or the :attr:`~Permissions.manage_guild` or :attr:`~Permissions.view_audit_log`
|
|
permission to do this.
|
|
|
|
Returns
|
|
-------
|
|
list[int]
|
|
A list of user IDs that can accept this invite.
|
|
"""
|
|
return [
|
|
int(line.split(",")[0])
|
|
for line in (await self.read())
|
|
.decode()
|
|
.splitlines()[1:] # first line is standardized "user_ids" header
|
|
]
|
|
|
|
|
|
I = TypeVar("I", bound="Invite")
|
|
|
|
|
|
class Invite(Hashable):
|
|
r"""Represents a Discord :class:`Guild` or :class:`abc.GuildChannel` invite.
|
|
|
|
Depending on the way this object was created, some of the attributes can
|
|
have a value of ``None``.
|
|
|
|
.. container:: operations
|
|
|
|
.. describe:: x == y
|
|
|
|
Checks if two invites are equal.
|
|
|
|
.. describe:: x != y
|
|
|
|
Checks if two invites are not equal.
|
|
|
|
.. describe:: hash(x)
|
|
|
|
Returns the invite hash.
|
|
|
|
.. describe:: str(x)
|
|
|
|
Returns the invite URL.
|
|
|
|
The following table illustrates what methods will obtain the attributes:
|
|
|
|
+------------------------------------+------------------------------------------------------------+
|
|
| Attribute | Method |
|
|
+====================================+============================================================+
|
|
| :attr:`max_age` | :meth:`abc.GuildChannel.invites`\, :meth:`Guild.invites` |
|
|
+------------------------------------+------------------------------------------------------------+
|
|
| :attr:`max_uses` | :meth:`abc.GuildChannel.invites`\, :meth:`Guild.invites` |
|
|
+------------------------------------+------------------------------------------------------------+
|
|
| :attr:`created_at` | :meth:`abc.GuildChannel.invites`\, :meth:`Guild.invites` |
|
|
+------------------------------------+------------------------------------------------------------+
|
|
| :attr:`temporary` | :meth:`abc.GuildChannel.invites`\, :meth:`Guild.invites` |
|
|
+------------------------------------+------------------------------------------------------------+
|
|
| :attr:`uses` | :meth:`abc.GuildChannel.invites`\, :meth:`Guild.invites` |
|
|
+------------------------------------+------------------------------------------------------------+
|
|
| :attr:`approximate_member_count` | :meth:`Client.fetch_invite` with `with_counts` enabled |
|
|
+------------------------------------+------------------------------------------------------------+
|
|
| :attr:`approximate_presence_count` | :meth:`Client.fetch_invite` with `with_counts` enabled |
|
|
+------------------------------------+------------------------------------------------------------+
|
|
| :attr:`expires_at` | :meth:`Client.fetch_invite` with `with_expiration` enabled |
|
|
+------------------------------------+------------------------------------------------------------+
|
|
|
|
If it's not in the table above then it is available by all methods.
|
|
|
|
Attributes
|
|
-----------
|
|
max_age: :class:`int`
|
|
How long before the invite expires in seconds.
|
|
A value of ``0`` indicates that it doesn't expire.
|
|
code: :class:`str`
|
|
The URL fragment used for the invite.
|
|
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.
|
|
revoked: :class:`bool`
|
|
Indicates if the invite has been revoked.
|
|
created_at: :class:`datetime.datetime`
|
|
An aware UTC datetime object denoting the time the invite was created.
|
|
temporary: :class:`bool`
|
|
Indicates that the invite grants temporary membership.
|
|
If ``True``, members who joined via this invite will be kicked upon disconnect.
|
|
uses: :class:`int`
|
|
How many times the invite has been used.
|
|
max_uses: :class:`int`
|
|
How many times the invite can be used.
|
|
A value of ``0`` indicates that it has unlimited uses.
|
|
inviter: Optional[:class:`User`]
|
|
The user who created the invite.
|
|
approximate_member_count: Optional[:class:`int`]
|
|
The approximate number of members in the guild.
|
|
approximate_presence_count: Optional[:class:`int`]
|
|
The approximate number of members currently active in the guild.
|
|
This includes idle, dnd, online, and invisible members. Offline members are excluded.
|
|
expires_at: Optional[:class:`datetime.datetime`]
|
|
The expiration date of the invite. If the value is ``None`` when received through
|
|
`Client.fetch_invite` with `with_expiration` enabled, the invite will never expire.
|
|
|
|
.. versionadded:: 2.0
|
|
|
|
channel: Union[:class:`abc.GuildChannel`, :class:`Object`, :class:`PartialInviteChannel`]
|
|
The channel the invite is for.
|
|
target_type: :class:`InviteTarget`
|
|
The type of target for the voice channel invite.
|
|
|
|
.. versionadded:: 2.0
|
|
|
|
target_user: Optional[:class:`User`]
|
|
The user whose stream to display for this invite, if any.
|
|
|
|
.. versionadded:: 2.0
|
|
|
|
target_application: Optional[:class:`PartialAppInfo`]
|
|
The embedded application the invite targets, if any.
|
|
|
|
.. versionadded:: 2.0
|
|
scheduled_event: Optional[:class:`ScheduledEvent`]
|
|
The scheduled event linked with the invite.
|
|
roles: List[Union[:class:`Role`, :class:`Object`]]
|
|
The roles that will be assigned to a user that joins via this invite.
|
|
|
|
When using `Client.fetch_invite`, these may be partial role objects and have nullish attributes.
|
|
|
|
.. versionadded:: 2.8
|
|
"""
|
|
|
|
__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",
|
|
"expires_at",
|
|
"roles",
|
|
)
|
|
|
|
BASE = "https://discord.gg"
|
|
|
|
def __init__(
|
|
self,
|
|
*,
|
|
state: ConnectionState,
|
|
data: InvitePayload | VanityInvitePayload,
|
|
guild: PartialInviteGuild | Guild | None = None,
|
|
channel: PartialInviteChannel | GuildChannel | None = None,
|
|
):
|
|
self._state: ConnectionState = state
|
|
self.max_age: int | None = data.get("max_age")
|
|
self.code: str = data["code"]
|
|
self.guild: InviteGuildType | None = self._resolve_guild(
|
|
data.get("guild"), guild
|
|
)
|
|
self.revoked: bool | None = data.get("revoked")
|
|
self.created_at: datetime.datetime | None = parse_time(data.get("created_at"))
|
|
self.temporary: bool | None = data.get("temporary")
|
|
self.uses: int | None = data.get("uses")
|
|
self.max_uses: int | None = data.get("max_uses")
|
|
self.approximate_presence_count: int | None = data.get(
|
|
"approximate_presence_count"
|
|
)
|
|
self.approximate_member_count: int | None = data.get("approximate_member_count")
|
|
|
|
expires_at = data.get("expires_at", None)
|
|
self.expires_at: datetime.datetime | None = (
|
|
parse_time(expires_at) if expires_at else None
|
|
)
|
|
|
|
inviter_data = data.get("inviter")
|
|
self.inviter: User | None = (
|
|
None if inviter_data is None else self._state.create_user(inviter_data)
|
|
)
|
|
|
|
self.channel: InviteChannelType | None = self._resolve_channel(
|
|
data.get("channel"), channel
|
|
)
|
|
|
|
target_user_data = data.get("target_user")
|
|
self.target_user: User | None = (
|
|
None
|
|
if target_user_data is None
|
|
else self._state.create_user(target_user_data)
|
|
)
|
|
|
|
self.target_type: InviteTarget = try_enum(
|
|
InviteTarget, data.get("target_type", 0)
|
|
)
|
|
|
|
from .scheduled_events import ScheduledEvent
|
|
|
|
scheduled_event: ScheduledEventPayload = data.get("guild_scheduled_event")
|
|
self.scheduled_event: ScheduledEvent | None = (
|
|
ScheduledEvent(state=state, data=scheduled_event)
|
|
if scheduled_event
|
|
else None
|
|
)
|
|
|
|
application = data.get("target_application")
|
|
self.target_application: PartialAppInfo | None = (
|
|
PartialAppInfo(data=application, state=state) if application else None
|
|
)
|
|
|
|
self.roles: list[Object | Role] = self._resolve_roles(
|
|
role_ids=data.get("role_ids") or [], roles=data.get("roles") or []
|
|
)
|
|
|
|
@classmethod
|
|
def from_incomplete(
|
|
cls: type[I], *, state: ConnectionState, data: InvitePayload
|
|
) -> I:
|
|
guild: Guild | PartialInviteGuild | None
|
|
try:
|
|
guild_data = data["guild"]
|
|
except KeyError:
|
|
# If we're here, then this is a group DM
|
|
guild = None
|
|
else:
|
|
guild_id = int(guild_data["id"])
|
|
guild = state._get_guild(guild_id)
|
|
if guild is None:
|
|
# If it's not cached, then it has to be a partial guild
|
|
guild = PartialInviteGuild(state, guild_data, guild_id)
|
|
|
|
# As far as I know, invites always need a channel
|
|
# So this should never raise.
|
|
channel: PartialInviteChannel | GuildChannel = PartialInviteChannel(
|
|
data["channel"]
|
|
)
|
|
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)
|
|
|
|
@classmethod
|
|
def from_gateway(
|
|
cls: type[I], *, state: ConnectionState, data: GatewayInvitePayload
|
|
) -> I:
|
|
guild_id: int | None = _get_as_snowflake(data, "guild_id")
|
|
guild: Guild | Object | None = state._get_guild(guild_id)
|
|
channel_id = int(data["channel_id"])
|
|
if guild is not None:
|
|
channel = guild.get_channel(channel_id) or Object(id=channel_id) # type: ignore
|
|
else:
|
|
guild = Object(id=guild_id) if guild_id is not None else None
|
|
channel = Object(id=channel_id)
|
|
|
|
return cls(state=state, data=data, guild=guild, channel=channel) # type: ignore
|
|
|
|
def _resolve_guild(
|
|
self,
|
|
data: InviteGuildPayload | None,
|
|
guild: Guild | PartialInviteGuild | None = None,
|
|
) -> InviteGuildType | None:
|
|
if guild is not None:
|
|
return guild
|
|
|
|
if data is None:
|
|
return None
|
|
|
|
guild_id = int(data["id"])
|
|
return PartialInviteGuild(self._state, data, guild_id)
|
|
|
|
def _resolve_channel(
|
|
self,
|
|
data: InviteChannelPayload | None,
|
|
channel: PartialInviteChannel | GuildChannel | None = None,
|
|
) -> InviteChannelType | None:
|
|
if channel is not None:
|
|
return channel
|
|
|
|
if data is None:
|
|
return None
|
|
|
|
return PartialInviteChannel(data)
|
|
|
|
def _resolve_roles(
|
|
self, role_ids: list[Snowflake], roles: list[RolePayload]
|
|
) -> list[Object | Role]:
|
|
if roles and self.guild is not None:
|
|
result: list[Object | Role] = [
|
|
Role(guild=self.guild, data=role, state=self._state) for role in roles
|
|
]
|
|
return result
|
|
if self.guild is not None and not isinstance(self.guild, PartialInviteGuild):
|
|
return [
|
|
self.guild.get_role(int(role_id)) or Object(role_id)
|
|
for role_id in role_ids
|
|
]
|
|
else:
|
|
return [Object(role_id) for role_id in role_ids]
|
|
|
|
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} "
|
|
f"scheduled_event={self.scheduled_event!r} "
|
|
f"roles={self.roles!r}"
|
|
f">"
|
|
)
|
|
|
|
def __hash__(self) -> int:
|
|
return hash(self.code)
|
|
|
|
@property
|
|
def id(self) -> str:
|
|
"""Returns the proper code portion of the invite."""
|
|
return self.code
|
|
|
|
@property
|
|
def url(self) -> str:
|
|
"""A property that retrieves the invite URL."""
|
|
return f"{self.BASE}/{self.code}{f'?event={self.scheduled_event.id}' if self.scheduled_event else ''}"
|
|
|
|
@property
|
|
def target_users(self) -> InviteTargetUsers:
|
|
"""An :class:`InviteTargetUsers` object for managing the target users list for this invite.
|
|
|
|
.. versionadded:: 2.8
|
|
"""
|
|
return InviteTargetUsers(invite_code=self.code, state=self._state)
|
|
|
|
async def edit_target_users(self, target_users_file: File) -> None:
|
|
"""|coro|
|
|
|
|
Updates the target users list for this invite.
|
|
|
|
You must have created this invite or have the :attr:`~Permissions.manage_guild` permission to do this.
|
|
|
|
You can use :func:`utils.users_to_csv` to generate a virtual CSV file from a sequence of user IDs.
|
|
|
|
Parameters
|
|
----------
|
|
target_users_file: :class:`File`
|
|
A CSV file with a single column of user IDs for all the users able to accept this invite.
|
|
|
|
Raises
|
|
------
|
|
HTTPException
|
|
Updating the target users failed.
|
|
Forbidden
|
|
You do not have permissions to edit this invite.
|
|
NotFound
|
|
The invite is invalid or expired.
|
|
"""
|
|
await self._state.http.update_invite_target_users(
|
|
self.invite_code, target_users_file=target_users_file
|
|
)
|
|
|
|
async def fetch_target_users_job_status(self) -> InviteTargetUsersJobStatus:
|
|
"""|coro|
|
|
|
|
Retrieves the status of the target users processing job for this invite.
|
|
|
|
You must have created this invite or have the :attr:`~Permissions.manage_guild` or :attr:`~Permissions.view_audit_log` permissions.
|
|
permission to do this.
|
|
|
|
Returns
|
|
-------
|
|
:class:`InviteTargetUsersJobStatus`
|
|
The job status information.
|
|
|
|
Raises
|
|
------
|
|
HTTPException
|
|
Fetching the job status failed.
|
|
NotFound
|
|
The invite is invalid or expired.
|
|
Forbidden
|
|
You do not have permission to view the target users.
|
|
"""
|
|
r = await self._state.http.get_invite_target_users_job_status(self.code)
|
|
return InviteTargetUsersJobStatus(data=r)
|
|
|
|
async def delete(self, *, reason: str | None = None):
|
|
"""|coro|
|
|
|
|
Revokes the instant invite.
|
|
|
|
You must have the :attr:`~Permissions.manage_channels` permission to do this.
|
|
|
|
Parameters
|
|
----------
|
|
reason: Optional[:class:`str`]
|
|
The reason for deleting this invite. Shows up on the audit log.
|
|
|
|
Raises
|
|
------
|
|
Forbidden
|
|
You do not have permissions to revoke invites.
|
|
NotFound
|
|
The invite is invalid or expired.
|
|
HTTPException
|
|
Revoking the invite failed.
|
|
"""
|
|
|
|
await self._state.http.delete_invite(self.code, reason=reason)
|
|
|
|
def set_scheduled_event(self, event: ScheduledEvent) -> None:
|
|
"""Links the given scheduled event to this invite.
|
|
|
|
.. note::
|
|
|
|
Scheduled events aren't actually associated with invites on the API.
|
|
Any guild channel invite can have an event attached to it. Using
|
|
:meth:`abc.GuildChannel.create_invite`, :meth:`Client.fetch_invite`,
|
|
or this method, you can link scheduled events.
|
|
|
|
.. versionadded:: 2.0
|
|
|
|
Parameters
|
|
----------
|
|
event: :class:`ScheduledEvent`
|
|
The scheduled event object to link.
|
|
"""
|
|
self.scheduled_event = event
|