stormbrigade_sheriff/sbsheriff/Lib/site-packages/disnake/guild_scheduled_event.py

709 lines
25 KiB
Python

# SPDX-License-Identifier: MIT
from __future__ import annotations
from datetime import datetime
from typing import TYPE_CHECKING, Any, Dict, Literal, Optional, overload
from .asset import Asset
from .enums import (
ChannelType,
GuildScheduledEventEntityType,
GuildScheduledEventPrivacyLevel,
GuildScheduledEventStatus,
try_enum,
)
from .mixins import Hashable
from .utils import (
MISSING,
_assetbytes_to_base64_data,
_get_as_snowflake,
cached_slot_property,
isoformat_utc,
parse_time,
snowflake_time,
)
if TYPE_CHECKING:
from .abc import GuildChannel, Snowflake
from .asset import AssetBytes
from .guild import Guild
from .iterators import GuildScheduledEventUserIterator
from .state import ConnectionState
from .types.guild_scheduled_event import (
GuildScheduledEvent as GuildScheduledEventPayload,
GuildScheduledEventEntityMetadata as GuildScheduledEventEntityMetadataPayload,
)
from .user import User
__all__ = ("GuildScheduledEventMetadata", "GuildScheduledEvent")
class GuildScheduledEventMetadata:
"""Represents a guild scheduled event entity metadata.
.. versionadded:: 2.3
Attributes
----------
location: Optional[:class:`str`]
The location of the guild scheduled event. If :attr:`GuildScheduledEvent.entity_type` is
:class:`GuildScheduledEventEntityType.external`, this value is not ``None``.
"""
__slots__ = ("location",)
def __init__(self, *, location: Optional[str] = None) -> None:
self.location: Optional[str] = location
def __repr__(self) -> str:
return f"<GuildScheduledEventMetadata location={self.location!r}>"
def to_dict(self) -> GuildScheduledEventEntityMetadataPayload:
if self.location is not None:
return {"location": self.location}
return {}
@classmethod
def from_dict(
cls, data: GuildScheduledEventEntityMetadataPayload
) -> GuildScheduledEventMetadata:
return GuildScheduledEventMetadata(location=data.get("location"))
class GuildScheduledEvent(Hashable):
"""Represents a guild scheduled event.
.. versionadded:: 2.3
.. container:: operations
.. describe:: x == y
Checks if two guild scheduled events are equal.
.. describe:: x != y
Checks if two guild scheduled events are not equal.
.. describe:: hash(x)
Returns the guild scheduled event's hash.
Attributes
----------
id: :class:`int`
The ID of the guild scheduled event.
guild_id: :class:`int`
The guild ID which the guild scheduled event belongs to.
channel_id: Optional[:class:`int`]
The channel ID in which the guild scheduled event will be hosted.
This field is ``None`` if :attr:`entity_type` is :class:`GuildScheduledEventEntityType.external`.
creator_id: Optional[:class:`int`]
The ID of the user that created the guild scheduled event.
This field is ``None`` for events created before October 25th, 2021.
creator: Optional[:class:`User`]
The user that created the guild scheduled event.
This field is ``None`` for events created before October 25th, 2021.
name: :class:`str`
The name of the guild scheduled event (1-100 characters).
description: :class:`str`
The description of the guild scheduled event (1-1000 characters).
scheduled_start_time: :class:`datetime.datetime`
The time when the guild scheduled event will start.
scheduled_end_time: Optional[:class:`datetime.datetime`]
The time when the guild scheduled event will end, or ``None`` if the event does not have a scheduled time to end.
privacy_level: :class:`GuildScheduledEventPrivacyLevel`
The privacy level of the guild scheduled event.
status: :class:`GuildScheduledEventStatus`
The status of the guild scheduled event.
entity_type: :class:`GuildScheduledEventEntityType`
The type of the guild scheduled event.
entity_id: Optional[:class:`int`]
The ID of an entity associated with the guild scheduled event.
entity_metadata: :class:`GuildScheduledEventMetadata`
Additional metadata for the guild scheduled event.
user_count: Optional[:class:`int`]
The number of users subscribed to the guild scheduled event.
If the guild scheduled event was fetched with ``with_user_count`` set to ``False``, this field is ``None``.
"""
__slots__ = (
"_state",
"id",
"guild_id",
"channel_id",
"creator_id",
"name",
"description",
"scheduled_start_time",
"scheduled_end_time",
"privacy_level",
"status",
"entity_type",
"entity_id",
"entity_metadata",
"creator",
"user_count",
"_image",
"_cs_guild",
"_cs_channel",
)
def __init__(self, *, state: ConnectionState, data: GuildScheduledEventPayload) -> None:
self._state: ConnectionState = state
self._update(data)
def _update(self, data: GuildScheduledEventPayload) -> None:
self.id: int = int(data["id"])
self.guild_id: int = int(data["guild_id"])
self.channel_id: Optional[int] = _get_as_snowflake(data, "channel_id")
self.creator_id: Optional[int] = _get_as_snowflake(data, "creator_id")
self.name: str = data["name"]
self.description: Optional[str] = data.get("description")
self.scheduled_start_time: datetime = parse_time(data["scheduled_start_time"])
self.scheduled_end_time: Optional[datetime] = parse_time(data["scheduled_end_time"])
self.privacy_level: GuildScheduledEventPrivacyLevel = try_enum(
GuildScheduledEventPrivacyLevel, data["privacy_level"]
)
self.status: GuildScheduledEventStatus = try_enum(GuildScheduledEventStatus, data["status"])
self.entity_type: GuildScheduledEventEntityType = try_enum(
GuildScheduledEventEntityType, data["entity_type"]
)
self.entity_id: Optional[int] = _get_as_snowflake(data, "entity_id")
metadata = data.get("entity_metadata")
self.entity_metadata: Optional[GuildScheduledEventMetadata] = (
None if metadata is None else GuildScheduledEventMetadata.from_dict(metadata)
)
creator_data = data.get("creator")
self.creator: Optional[User]
if creator_data is not None:
self.creator = self._state.create_user(creator_data)
else:
self.creator = self._state.get_user(self.creator_id)
self.user_count: Optional[int] = data.get("user_count")
self._image: Optional[str] = data.get("image")
def __repr__(self) -> str:
attrs = (
("id", self.id),
("guild_id", self.guild_id),
("channel_id", self.channel_id),
("name", self.name),
("description", self.description),
("scheduled_start_time", self.scheduled_start_time),
("scheduled_end_time", self.scheduled_end_time),
("privacy_level", self.privacy_level),
("status", self.status),
("entity_type", self.entity_type),
("entity_metadata", self.entity_metadata),
("creator", self.creator),
)
inner = " ".join(f"{k!s}={v!r}" for k, v in attrs)
return f"<{self.__class__.__name__} {inner}>"
def __str__(self) -> str:
return self.name
@property
def created_at(self) -> datetime:
""":class:`datetime.datetime`: Returns the guild scheduled event's creation time in UTC.
.. versionadded:: 2.6
"""
return snowflake_time(self.id)
@property
def url(self) -> str:
""":class:`str`: The guild scheduled event's URL.
.. versionadded:: 2.6
"""
return f"https://discord.com/events/{self.guild_id}/{self.id}"
@cached_slot_property("_cs_guild")
def guild(self) -> Optional[Guild]:
"""Optional[:class:`Guild`]: The guild which the guild scheduled event belongs to."""
return self._state._get_guild(self.guild_id)
@cached_slot_property("_cs_channel")
def channel(self) -> Optional[GuildChannel]:
"""Optional[:class:`abc.GuildChannel`]: The channel in which the guild scheduled event will be hosted.
This will be ``None`` if :attr:`entity_type` is :class:`GuildScheduledEventEntityType.external`.
"""
if self.channel_id is None:
return None
guild = self.guild
return None if guild is None else guild.get_channel(self.channel_id)
@property
def image(self) -> Optional[Asset]:
"""Optional[:class:`Asset`]: The cover image asset of the guild scheduled event, if available."""
if self._image is None:
return None
return Asset._from_guild_scheduled_event_image(self._state, self.id, self._image)
async def delete(self) -> None:
"""|coro|
Deletes the guild scheduled event.
You must have :attr:`.Permissions.manage_events` permission to do this.
Raises
------
Forbidden
You do not have proper permissions to delete the event.
NotFound
The event does not exist.
HTTPException
Deleting the event failed.
"""
await self._state.http.delete_guild_scheduled_event(self.guild_id, self.id)
# note: unlike `Guild.create_scheduled_event`, we don't remove `entity_type`-specific defaults
# from parameters in the overloads here, as the entity_type might be the same as before,
# in which case those parameters don't have to be passed
# no entity_type specified
@overload
async def edit(
self,
*,
name: str = ...,
description: Optional[str] = ...,
image: Optional[AssetBytes] = ...,
channel: Optional[Snowflake] = ...,
privacy_level: GuildScheduledEventPrivacyLevel = ...,
scheduled_start_time: datetime = ...,
scheduled_end_time: Optional[datetime] = ...,
entity_metadata: Optional[GuildScheduledEventMetadata] = ...,
status: GuildScheduledEventStatus = ...,
reason: Optional[str] = ...,
) -> GuildScheduledEvent:
...
# new entity_type is `external`, no channel
@overload
async def edit(
self,
*,
entity_type: Literal[GuildScheduledEventEntityType.external],
channel: None = ...,
name: str = ...,
description: Optional[str] = ...,
image: Optional[AssetBytes] = ...,
privacy_level: GuildScheduledEventPrivacyLevel = ...,
scheduled_start_time: datetime = ...,
scheduled_end_time: datetime = ...,
entity_metadata: GuildScheduledEventMetadata = ...,
status: GuildScheduledEventStatus = ...,
reason: Optional[str] = ...,
) -> GuildScheduledEvent:
...
# new entity_type is `voice` or `stage_instance`, no entity_metadata
@overload
async def edit(
self,
*,
entity_type: Literal[
GuildScheduledEventEntityType.voice,
GuildScheduledEventEntityType.stage_instance,
],
name: str = ...,
description: Optional[str] = ...,
image: Optional[AssetBytes] = ...,
channel: Snowflake = ...,
privacy_level: GuildScheduledEventPrivacyLevel = ...,
scheduled_start_time: datetime = ...,
scheduled_end_time: Optional[datetime] = ...,
status: GuildScheduledEventStatus = ...,
reason: Optional[str] = ...,
) -> GuildScheduledEvent:
...
# channel=None, no entity_type
@overload
async def edit(
self,
*,
channel: None,
name: str = ...,
description: Optional[str] = ...,
image: Optional[AssetBytes] = ...,
privacy_level: GuildScheduledEventPrivacyLevel = ...,
scheduled_start_time: datetime = ...,
scheduled_end_time: datetime = ...,
entity_metadata: GuildScheduledEventMetadata = ...,
status: GuildScheduledEventStatus = ...,
reason: Optional[str] = ...,
) -> GuildScheduledEvent:
...
# valid channel, no entity_type
@overload
async def edit(
self,
*,
channel: Snowflake,
name: str = ...,
description: Optional[str] = ...,
image: Optional[AssetBytes] = ...,
privacy_level: GuildScheduledEventPrivacyLevel = ...,
scheduled_start_time: datetime = ...,
scheduled_end_time: Optional[datetime] = ...,
status: GuildScheduledEventStatus = ...,
reason: Optional[str] = ...,
) -> GuildScheduledEvent:
...
async def edit(
self,
*,
name: str = MISSING,
description: Optional[str] = MISSING,
image: Optional[AssetBytes] = MISSING,
channel: Optional[Snowflake] = MISSING,
privacy_level: GuildScheduledEventPrivacyLevel = MISSING,
scheduled_start_time: datetime = MISSING,
scheduled_end_time: Optional[datetime] = MISSING,
entity_type: GuildScheduledEventEntityType = MISSING,
entity_metadata: Optional[GuildScheduledEventMetadata] = MISSING,
status: GuildScheduledEventStatus = MISSING,
reason: Optional[str] = None,
) -> GuildScheduledEvent:
"""|coro|
Edits the guild scheduled event.
You must have :attr:`.Permissions.manage_events` permission to do this.
.. versionchanged:: 2.6
Updates must follow requirements of :func:`Guild.create_scheduled_event`
.. versionchanged:: 2.6
Now raises :exc:`TypeError` instead of :exc:`ValueError` for
invalid parameter types.
.. versionchanged:: 2.6
Removed ``channel_id`` parameter in favor of ``channel``.
.. versionchanged:: 2.6
Naive datetime parameters are now assumed to be in the local
timezone instead of UTC.
Parameters
----------
name: :class:`str`
The name of the guild scheduled event.
description: Optional[:class:`str`]
The description of the guild scheduled event.
image: Optional[|resource_type|]
The cover image of the guild scheduled event. Set to ``None`` to remove the image.
.. versionadded:: 2.4
.. versionchanged:: 2.5
Now accepts various resource types in addition to :class:`bytes`.
channel: Optional[:class:`.abc.Snowflake`]
The channel in which the guild scheduled event will be hosted.
Set to ``None`` if changing ``entity_type`` to :class:`GuildScheduledEventEntityType.external`.
.. versionadded:: 2.6
privacy_level: :class:`GuildScheduledEventPrivacyLevel`
The privacy level of the guild scheduled event.
scheduled_start_time: :class:`datetime.datetime`
The time to schedule the guild scheduled event.
If the datetime is naive, it is assumed to be local time.
scheduled_end_time: Optional[:class:`datetime.datetime`]
The time when the guild scheduled event is scheduled to end.
If the datetime is naive, it is assumed to be local time.
entity_type: :class:`GuildScheduledEventEntityType`
The entity type of the guild scheduled event.
entity_metadata: Optional[:class:`GuildScheduledEventMetadata`]
The entity metadata of the guild scheduled event.
status: :class:`GuildScheduledEventStatus`
The status of the guild scheduled event.
See also :func:`start`, :func:`end`, and :func:`cancel`.
reason: Optional[:class:`str`]
The reason for editing the guild scheduled event. Shows up on the audit log.
Raises
------
Forbidden
You do not have proper permissions to edit the event.
NotFound
The event does not exist or the ``image`` asset couldn't be found.
HTTPException
Editing the event failed.
TypeError
The ``image`` asset is a lottie sticker (see :func:`Sticker.read`),
one of ``entity_type``, ``privacy_level``, ``entity_metadata`` or ``status``
is not of the correct type, or the provided channel's type is neither :class:`ChannelType.voice` nor
:class:`ChannelType.stage_voice`.
Returns
-------
:class:`GuildScheduledEvent`
The newly updated guild scheduled event instance.
"""
fields: Dict[str, Any] = {}
if entity_type is MISSING:
if channel is None:
entity_type = GuildScheduledEventEntityType.external
elif channel is not MISSING and isinstance(
channel_type := getattr(channel, "type", None), ChannelType
):
if channel_type is ChannelType.voice:
entity_type = GuildScheduledEventEntityType.voice
elif channel_type is ChannelType.stage_voice:
entity_type = GuildScheduledEventEntityType.stage_instance
else:
raise TypeError("channel type must be either 'voice' or 'stage_voice'")
if privacy_level is not MISSING:
if not isinstance(privacy_level, GuildScheduledEventPrivacyLevel):
raise TypeError(
"privacy_level must be an instance of GuildScheduledEventPrivacyLevel"
)
fields["privacy_level"] = privacy_level.value
if entity_type is not MISSING:
if not isinstance(entity_type, GuildScheduledEventEntityType):
raise TypeError("entity_type must be an instance of GuildScheduledEventEntityType")
fields["entity_type"] = entity_type.value
if entity_metadata is not MISSING:
if entity_metadata is None:
fields["entity_metadata"] = None
elif isinstance(entity_metadata, GuildScheduledEventMetadata):
fields["entity_metadata"] = entity_metadata.to_dict()
else:
raise TypeError(
"entity_metadata must be an instance of GuildScheduledEventMetadata"
)
if status is not MISSING:
if not isinstance(status, GuildScheduledEventStatus):
raise TypeError("status must be an instance of GuildScheduledEventStatus")
fields["status"] = status.value
if name is not MISSING:
fields["name"] = name
if description is not MISSING:
fields["description"] = description
if image is not MISSING:
fields["image"] = await _assetbytes_to_base64_data(image)
if channel is not MISSING:
fields["channel_id"] = channel.id if channel is not None else None
elif entity_type is GuildScheduledEventEntityType.external:
# special case, as the API wants `channel_id=null` if type is being changed to `external`
fields["channel_id"] = None
if scheduled_start_time is not MISSING:
fields["scheduled_start_time"] = isoformat_utc(scheduled_start_time)
if scheduled_end_time is not MISSING:
fields["scheduled_end_time"] = isoformat_utc(scheduled_end_time)
data = await self._state.http.edit_guild_scheduled_event(
guild_id=self.guild_id, event_id=self.id, reason=reason, **fields
)
return GuildScheduledEvent(state=self._state, data=data)
async def start(self, *, reason: Optional[str] = None) -> GuildScheduledEvent:
"""|coro|
Starts the guild scheduled event.
Changes the event status to :attr:`~GuildScheduledEventStatus.active`.
You must have :attr:`.Permissions.manage_events` permission to do this.
.. versionadded:: 2.7
Parameters
----------
reason: Optional[:class:`str`]
The reason for starting the guild scheduled event. Shows up on the audit log.
Raises
------
ValueError
The event has already started or ended, or was cancelled.
Forbidden
You do not have permissions to start the event.
HTTPException
Starting the event failed.
Returns
-------
:class:`GuildScheduledEvent`
The started guild scheduled event instance.
"""
if self.status is not GuildScheduledEventStatus.scheduled:
raise ValueError("This event is not scheduled")
return await self._edit_status(GuildScheduledEventStatus.active, reason=reason)
async def end(self, *, reason: Optional[str] = None) -> GuildScheduledEvent:
"""|coro|
Ends the guild scheduled event.
Changes the event status to :attr:`~GuildScheduledEventStatus.completed`.
You must have :attr:`.Permissions.manage_events` permission to do this.
.. versionadded:: 2.7
Parameters
----------
reason: Optional[:class:`str`]
The reason for ending the guild scheduled event. Shows up on the audit log.
Raises
------
ValueError
The event has not started yet, has already ended, or was cancelled.
Forbidden
You do not have permissions to end the event.
HTTPException
Ending the event failed.
Returns
-------
:class:`GuildScheduledEvent`
The ended guild scheduled event instance.
"""
if self.status is not GuildScheduledEventStatus.active:
raise ValueError("This event is not active")
return await self._edit_status(GuildScheduledEventStatus.completed, reason=reason)
async def cancel(self, *, reason: Optional[str] = None) -> GuildScheduledEvent:
"""|coro|
Cancels the guild scheduled event.
Changes the event status to :attr:`~GuildScheduledEventStatus.cancelled`.
You must have :attr:`.Permissions.manage_events` permission to do this.
.. versionadded:: 2.7
Parameters
----------
reason: Optional[:class:`str`]
The reason for cancelling the guild scheduled event. Shows up on the audit log.
Raises
------
ValueError
The event has already started or ended, or was already cancelled.
Forbidden
You do not have permissions to cancel the event.
HTTPException
Cancelling the event failed.
Returns
-------
:class:`GuildScheduledEvent`
The cancelled guild scheduled event instance.
"""
if self.status is not GuildScheduledEventStatus.scheduled:
raise ValueError("This event is not scheduled")
return await self._edit_status(GuildScheduledEventStatus.cancelled, reason=reason)
# shortcut for editing just the event status, bypasses other edit logic
async def _edit_status(
self, status: GuildScheduledEventStatus, *, reason: Optional[str]
) -> GuildScheduledEvent:
data = await self._state.http.edit_guild_scheduled_event(
guild_id=self.guild_id,
event_id=self.id,
reason=reason,
status=status.value,
)
return GuildScheduledEvent(state=self._state, data=data)
def fetch_users(
self,
*,
limit: Optional[int] = None,
with_members: bool = True,
before: Optional[Snowflake] = None,
after: Optional[Snowflake] = None,
) -> GuildScheduledEventUserIterator:
"""|coro|
Returns an :class:`AsyncIterator` of users subscribed to the guild scheduled event.
If ``before`` is specified, users are returned in reverse order,
i.e. starting with the highest ID.
.. versionchanged:: 2.5
Now returns an :class:`AsyncIterator` instead of a list of the first 100 users.
Parameters
----------
limit: Optional[:class:`int`]
The number of users to retrieve.
with_members: :class:`bool`
Whether to include some users as members. Defaults to ``True``.
before: Optional[:class:`abc.Snowflake`]
Retrieve users before this object.
after: Optional[:class:`abc.Snowflake`]
Retrieve users after this object.
Raises
------
Forbidden
You do not have proper permissions to fetch the users.
NotFound
The event does not exist.
HTTPException
Retrieving the users failed.
Yields
------
Union[:class:`User`, :class:`Member`]
The member (if retrievable) or user subscribed to the guild scheduled event.
Examples
--------
Usage ::
async for user in event.fetch_users(limit=500):
print(user.name)
Flattening into a list ::
users = await event.fetch_users(limit=250).flatten()
"""
from .iterators import GuildScheduledEventUserIterator # cyclic import
return GuildScheduledEventUserIterator(
self,
limit=limit,
with_members=with_members,
before=before,
after=after,
)