mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-04-18 23:15:48 +00:00
Implement Guild Scheduled Events
This commit is contained in:
parent
90cabd1673
commit
3c6279b947
@ -56,6 +56,7 @@ from .raw_models import *
|
||||
from .team import *
|
||||
from .sticker import *
|
||||
from .stage_instance import *
|
||||
from .scheduled_event import *
|
||||
from .interactions import *
|
||||
from .components import *
|
||||
from .threads import *
|
||||
|
@ -208,6 +208,15 @@ class Asset(AssetMixin):
|
||||
animated=False,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _from_scheduled_event_cover_image(cls, state, scheduled_event_id: int, cover_image_hash: str) -> Asset:
|
||||
return cls(
|
||||
state,
|
||||
url=f'{cls.BASE}/guild-events/{scheduled_event_id}/{cover_image_hash}.png?size=1024',
|
||||
key=cover_image_hash,
|
||||
animated=False,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _from_guild_image(cls, state, guild_id: int, image: str, path: str) -> Asset:
|
||||
animated = image.startswith('a_')
|
||||
|
@ -229,12 +229,14 @@ class AuditLogChanges:
|
||||
'tags': ('emoji', None),
|
||||
'default_message_notifications': ('default_notifications', _enum_transformer(enums.NotificationLevel)),
|
||||
'video_quality_mode': (None, _enum_transformer(enums.VideoQualityMode)),
|
||||
'privacy_level': (None, _enum_transformer(enums.StagePrivacyLevel)),
|
||||
'privacy_level': (None, _enum_transformer(enums.PrivacyLevel)),
|
||||
'format_type': (None, _enum_transformer(enums.StickerFormatType)),
|
||||
'type': (None, _transform_type),
|
||||
'communication_disabled_until': ('timed_out_until', _transform_timestamp),
|
||||
'expire_behavior': (None, _enum_transformer(enums.ExpireBehaviour)),
|
||||
'mfa_level': (None, _enum_transformer(enums.MFALevel)),
|
||||
'status': (None, _enum_transformer(enums.EventStatus)),
|
||||
'entity_type': (None, _enum_transformer(enums.EntityType)),
|
||||
}
|
||||
# fmt: on
|
||||
|
||||
|
@ -45,8 +45,9 @@ from typing import (
|
||||
import datetime
|
||||
|
||||
import discord.abc
|
||||
from .scheduled_event import ScheduledEvent
|
||||
from .permissions import PermissionOverwrite, Permissions
|
||||
from .enums import ChannelType, StagePrivacyLevel, try_enum, VideoQualityMode
|
||||
from .enums import ChannelType, PrivacyLevel, try_enum, VideoQualityMode
|
||||
from .mixins import Hashable
|
||||
from .object import Object
|
||||
from . import utils
|
||||
@ -950,6 +951,14 @@ class VocalGuildChannel(discord.abc.Connectable, discord.abc.GuildChannel, Hasha
|
||||
}
|
||||
# fmt: on
|
||||
|
||||
@property
|
||||
def scheduled_events(self) -> List[ScheduledEvent]:
|
||||
"""List[:class:`ScheduledEvent`]: Returns all scheduled events for this channel.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
return [event for event in self.guild.scheduled_events if event.channel_id == self.id]
|
||||
|
||||
@utils.copy_doc(discord.abc.GuildChannel.permissions_for)
|
||||
def permissions_for(self, obj: Union[Member, Role], /) -> Permissions:
|
||||
base = super().permissions_for(obj)
|
||||
@ -1259,7 +1268,7 @@ class StageChannel(VocalGuildChannel):
|
||||
return utils.get(self.guild.stage_instances, channel_id=self.id)
|
||||
|
||||
async def create_instance(
|
||||
self, *, topic: str, privacy_level: StagePrivacyLevel = MISSING, reason: Optional[str] = None
|
||||
self, *, topic: str, privacy_level: PrivacyLevel = MISSING, reason: Optional[str] = None
|
||||
) -> StageInstance:
|
||||
"""|coro|
|
||||
|
||||
@ -1274,8 +1283,8 @@ class StageChannel(VocalGuildChannel):
|
||||
-----------
|
||||
topic: :class:`str`
|
||||
The stage instance's topic.
|
||||
privacy_level: :class:`StagePrivacyLevel`
|
||||
The stage instance's privacy level. Defaults to :attr:`StagePrivacyLevel.guild_only`.
|
||||
privacy_level: :class:`PrivacyLevel`
|
||||
The stage instance's privacy level. Defaults to :attr:`PrivacyLevel.guild_only`.
|
||||
reason: :class:`str`
|
||||
The reason the stage instance was created. Shows up on the audit log.
|
||||
|
||||
@ -1297,7 +1306,7 @@ class StageChannel(VocalGuildChannel):
|
||||
payload: Dict[str, Any] = {'channel_id': self.id, 'topic': topic}
|
||||
|
||||
if privacy_level is not MISSING:
|
||||
if not isinstance(privacy_level, StagePrivacyLevel):
|
||||
if not isinstance(privacy_level, PrivacyLevel):
|
||||
raise TypeError('privacy_level field must be of type PrivacyLevel')
|
||||
|
||||
payload['privacy_level'] = privacy_level.value
|
||||
|
@ -1437,7 +1437,12 @@ class Client:
|
||||
# Invite management
|
||||
|
||||
async def fetch_invite(
|
||||
self, url: Union[Invite, str], *, with_counts: bool = True, with_expiration: bool = True
|
||||
self,
|
||||
url: Union[Invite, str],
|
||||
*,
|
||||
with_counts: bool = True,
|
||||
with_expiration: bool = True,
|
||||
scheduled_event_id: Optional[int] = None,
|
||||
) -> Invite:
|
||||
"""|coro|
|
||||
|
||||
@ -1462,9 +1467,20 @@ class Client:
|
||||
:attr:`.Invite.expires_at` field.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
scheduled_event_id: Optional[:class:`int`]
|
||||
The ID of the scheduled event this invite is for.
|
||||
|
||||
.. note::
|
||||
|
||||
It is not possible to provide a url that contains an ``event_id`` parameter
|
||||
when using this parameter.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
Raises
|
||||
-------
|
||||
ValueError
|
||||
The url contains an ``event_id``, but ``scheduled_event_id`` has also been provided.
|
||||
NotFound
|
||||
The invite has expired or is invalid.
|
||||
HTTPException
|
||||
@ -1476,8 +1492,19 @@ class Client:
|
||||
The invite from the URL/ID.
|
||||
"""
|
||||
|
||||
invite_id = utils.resolve_invite(url)
|
||||
data = await self.http.get_invite(invite_id, with_counts=with_counts, with_expiration=with_expiration)
|
||||
resolved = utils.resolve_invite(url)
|
||||
|
||||
if scheduled_event_id and resolved.event:
|
||||
raise ValueError('Cannot specify scheduled_event_id and contain an event_id in the url.')
|
||||
|
||||
scheduled_event_id = scheduled_event_id or resolved.event
|
||||
|
||||
data = await self.http.get_invite(
|
||||
resolved.code,
|
||||
with_counts=with_counts,
|
||||
with_expiration=with_expiration,
|
||||
guild_scheduled_event_id=scheduled_event_id,
|
||||
)
|
||||
return Invite.from_incomplete(state=self._connection, data=data)
|
||||
|
||||
async def delete_invite(self, invite: Union[Invite, str], /) -> None:
|
||||
@ -1507,8 +1534,8 @@ class Client:
|
||||
Revoking the invite failed.
|
||||
"""
|
||||
|
||||
invite_id = utils.resolve_invite(invite)
|
||||
await self.http.delete_invite(invite_id)
|
||||
resolved = utils.resolve_invite(invite)
|
||||
await self.http.delete_invite(resolved.code)
|
||||
|
||||
# Miscellaneous stuff
|
||||
|
||||
|
120
discord/enums.py
120
discord/enums.py
@ -51,12 +51,14 @@ __all__ = (
|
||||
'ComponentType',
|
||||
'ButtonStyle',
|
||||
'TextStyle',
|
||||
'StagePrivacyLevel',
|
||||
'PrivacyLevel',
|
||||
'InteractionType',
|
||||
'InteractionResponseType',
|
||||
'NSFWLevel',
|
||||
'MFALevel',
|
||||
'Locale',
|
||||
'EntityType',
|
||||
'EventStatus',
|
||||
)
|
||||
|
||||
|
||||
@ -330,6 +332,9 @@ class AuditLogAction(Enum):
|
||||
sticker_create = 90
|
||||
sticker_update = 91
|
||||
sticker_delete = 92
|
||||
scheduled_event_create = 100
|
||||
scheduled_event_update = 101
|
||||
scheduled_event_delete = 102
|
||||
thread_create = 110
|
||||
thread_update = 111
|
||||
thread_delete = 112
|
||||
@ -339,50 +344,53 @@ class AuditLogAction(Enum):
|
||||
def category(self) -> Optional[AuditLogActionCategory]:
|
||||
# fmt: off
|
||||
lookup: Dict[AuditLogAction, Optional[AuditLogActionCategory]] = {
|
||||
AuditLogAction.guild_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.channel_create: AuditLogActionCategory.create,
|
||||
AuditLogAction.channel_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.channel_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.overwrite_create: AuditLogActionCategory.create,
|
||||
AuditLogAction.overwrite_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.overwrite_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.kick: None,
|
||||
AuditLogAction.member_prune: None,
|
||||
AuditLogAction.ban: None,
|
||||
AuditLogAction.unban: None,
|
||||
AuditLogAction.member_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.member_role_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.member_move: None,
|
||||
AuditLogAction.member_disconnect: None,
|
||||
AuditLogAction.bot_add: None,
|
||||
AuditLogAction.role_create: AuditLogActionCategory.create,
|
||||
AuditLogAction.role_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.role_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.invite_create: AuditLogActionCategory.create,
|
||||
AuditLogAction.invite_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.invite_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.webhook_create: AuditLogActionCategory.create,
|
||||
AuditLogAction.webhook_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.webhook_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.emoji_create: AuditLogActionCategory.create,
|
||||
AuditLogAction.emoji_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.emoji_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.message_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.message_bulk_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.message_pin: None,
|
||||
AuditLogAction.message_unpin: None,
|
||||
AuditLogAction.integration_create: AuditLogActionCategory.create,
|
||||
AuditLogAction.integration_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.integration_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.stage_instance_create: AuditLogActionCategory.create,
|
||||
AuditLogAction.stage_instance_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.stage_instance_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.sticker_create: AuditLogActionCategory.create,
|
||||
AuditLogAction.sticker_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.sticker_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.thread_create: AuditLogActionCategory.create,
|
||||
AuditLogAction.thread_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.thread_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.guild_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.channel_create: AuditLogActionCategory.create,
|
||||
AuditLogAction.channel_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.channel_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.overwrite_create: AuditLogActionCategory.create,
|
||||
AuditLogAction.overwrite_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.overwrite_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.kick: None,
|
||||
AuditLogAction.member_prune: None,
|
||||
AuditLogAction.ban: None,
|
||||
AuditLogAction.unban: None,
|
||||
AuditLogAction.member_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.member_role_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.member_move: None,
|
||||
AuditLogAction.member_disconnect: None,
|
||||
AuditLogAction.bot_add: None,
|
||||
AuditLogAction.role_create: AuditLogActionCategory.create,
|
||||
AuditLogAction.role_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.role_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.invite_create: AuditLogActionCategory.create,
|
||||
AuditLogAction.invite_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.invite_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.webhook_create: AuditLogActionCategory.create,
|
||||
AuditLogAction.webhook_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.webhook_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.emoji_create: AuditLogActionCategory.create,
|
||||
AuditLogAction.emoji_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.emoji_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.message_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.message_bulk_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.message_pin: None,
|
||||
AuditLogAction.message_unpin: None,
|
||||
AuditLogAction.integration_create: AuditLogActionCategory.create,
|
||||
AuditLogAction.integration_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.integration_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.stage_instance_create: AuditLogActionCategory.create,
|
||||
AuditLogAction.stage_instance_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.stage_instance_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.sticker_create: AuditLogActionCategory.create,
|
||||
AuditLogAction.sticker_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.sticker_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.scheduled_event_create: AuditLogActionCategory.create,
|
||||
AuditLogAction.scheduled_event_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.scheduled_event_delete: AuditLogActionCategory.delete,
|
||||
AuditLogAction.thread_create: AuditLogActionCategory.create,
|
||||
AuditLogAction.thread_update: AuditLogActionCategory.update,
|
||||
AuditLogAction.thread_delete: AuditLogActionCategory.delete,
|
||||
}
|
||||
# fmt: on
|
||||
return lookup[self]
|
||||
@ -416,6 +424,8 @@ class AuditLogAction(Enum):
|
||||
return 'stage_instance'
|
||||
elif v < 93:
|
||||
return 'sticker'
|
||||
elif v < 103:
|
||||
return 'guild_scheduled_event'
|
||||
elif v < 113:
|
||||
return 'thread'
|
||||
|
||||
@ -568,9 +578,7 @@ class TextStyle(Enum):
|
||||
return self.value
|
||||
|
||||
|
||||
class StagePrivacyLevel(Enum):
|
||||
public = 1
|
||||
closed = 2
|
||||
class PrivacyLevel(Enum):
|
||||
guild_only = 2
|
||||
|
||||
|
||||
@ -625,6 +633,22 @@ class Locale(Enum):
|
||||
E = TypeVar('E', bound='Enum')
|
||||
|
||||
|
||||
class EntityType(Enum):
|
||||
stage_instance = 1
|
||||
voice = 2
|
||||
external = 3
|
||||
|
||||
|
||||
class EventStatus(Enum):
|
||||
scheduled = 1
|
||||
active = 2
|
||||
completed = 3
|
||||
canceled = 4
|
||||
|
||||
ended = 3
|
||||
cancelled = 4
|
||||
|
||||
|
||||
def create_unknown_value(cls: Type[E], val: Any) -> E:
|
||||
value_cls = cls._enum_value_cls_ # type: ignore - This is narrowed below
|
||||
name = f'unknown_{val}'
|
||||
|
@ -938,6 +938,22 @@ class Intents(BaseFlags):
|
||||
"""
|
||||
return 1 << 15
|
||||
|
||||
@flag_value
|
||||
def guild_scheduled_events(self):
|
||||
""":class:`bool`: Whether guild scheduled event related events are enabled.
|
||||
|
||||
This corresponds to the following events:
|
||||
|
||||
- :func:`on_scheduled_event_create`
|
||||
- :func:`on_scheduled_event_update`
|
||||
- :func:`on_scheduled_event_delete`
|
||||
- :func:`on_scheduled_event_user_add`
|
||||
- :func:`on_scheduled_event_user_remove`
|
||||
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
return 1 << 16
|
||||
|
||||
|
||||
@fill_with_flags()
|
||||
class MemberCacheFlags(BaseFlags):
|
||||
|
218
discord/guild.py
218
discord/guild.py
@ -60,6 +60,8 @@ from .enums import (
|
||||
AuditLogAction,
|
||||
VideoQualityMode,
|
||||
ChannelType,
|
||||
EntityType,
|
||||
PrivacyLevel,
|
||||
try_enum,
|
||||
VerificationLevel,
|
||||
ContentFilter,
|
||||
@ -74,6 +76,7 @@ from .widget import Widget
|
||||
from .asset import Asset
|
||||
from .flags import SystemChannelFlags
|
||||
from .integrations import Integration, _integration_factory
|
||||
from .scheduled_event import ScheduledEvent
|
||||
from .stage_instance import StageInstance
|
||||
from .threads import Thread, ThreadMember
|
||||
from .sticker import GuildSticker
|
||||
@ -293,6 +296,7 @@ class Guild(Hashable):
|
||||
'_rules_channel_id',
|
||||
'_public_updates_channel_id',
|
||||
'_stage_instances',
|
||||
'_scheduled_events',
|
||||
'_threads',
|
||||
)
|
||||
|
||||
@ -466,6 +470,11 @@ class Guild(Hashable):
|
||||
stage_instance = StageInstance(guild=self, data=s, state=state)
|
||||
self._stage_instances[stage_instance.id] = stage_instance
|
||||
|
||||
self._scheduled_events: Dict[int, ScheduledEvent] = {}
|
||||
for s in guild.get('guild_scheduled_events', []):
|
||||
scheduled_event = ScheduledEvent(data=s, state=state)
|
||||
self._scheduled_events[scheduled_event.id] = scheduled_event
|
||||
|
||||
cache_joined = self._state.member_cache_flags.joined
|
||||
self_id = self._state.self_id
|
||||
for mdata in guild.get('members', []):
|
||||
@ -867,6 +876,31 @@ class Guild(Hashable):
|
||||
"""
|
||||
return self._stage_instances.get(stage_instance_id)
|
||||
|
||||
@property
|
||||
def scheduled_events(self) -> List[ScheduledEvent]:
|
||||
"""List[:class:`ScheduledEvent`]: Returns a :class:`list` of the guild's scheduled events.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
return list(self._scheduled_events.values())
|
||||
|
||||
def get_scheduled_event(self, scheduled_event_id: int, /) -> Optional[ScheduledEvent]:
|
||||
"""Returns a scheduled event with the given ID.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
scheduled_event_id: :class:`int`
|
||||
The ID to search for.
|
||||
|
||||
Returns
|
||||
--------
|
||||
Optional[:class:`ScheduledEvent`]
|
||||
The scheduled event or ``None`` if not found.
|
||||
"""
|
||||
return self._scheduled_events.get(scheduled_event_id)
|
||||
|
||||
@property
|
||||
def owner(self) -> Optional[Member]:
|
||||
"""Optional[:class:`Member`]: The member that owns the guild."""
|
||||
@ -2413,6 +2447,190 @@ class Guild(Hashable):
|
||||
"""
|
||||
await self._state.http.delete_guild_sticker(self.id, sticker.id, reason)
|
||||
|
||||
async def fetch_scheduled_events(self, *, with_counts: bool = True) -> List[ScheduledEvent]:
|
||||
"""|coro|
|
||||
|
||||
Retrieves a list of all scheduled events for the guild.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
Parameters
|
||||
------------
|
||||
with_counts: :class:`bool`
|
||||
Whether to include the number of users that are subscribed to the event.
|
||||
Defaults to ``True``.
|
||||
|
||||
Raises
|
||||
-------
|
||||
HTTPException
|
||||
Retrieving the scheduled events failed.
|
||||
|
||||
Returns
|
||||
--------
|
||||
List[:class:`ScheduledEvent`]
|
||||
The scheduled events.
|
||||
"""
|
||||
data = await self._state.http.get_scheduled_events(self.id, with_counts)
|
||||
return [ScheduledEvent(state=self._state, data=d) for d in data]
|
||||
|
||||
async def fetch_scheduled_event(self, scheduled_event_id: int, /, *, with_counts: bool = True) -> ScheduledEvent:
|
||||
"""|coro|
|
||||
|
||||
Retrieves a scheduled event from the guild.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
Parameters
|
||||
------------
|
||||
id: :class:`int`
|
||||
The scheduled event ID.
|
||||
with_counts: :class:`bool`
|
||||
Whether to include the number of users that are subscribed to the event.
|
||||
Defaults to ``True``.
|
||||
|
||||
Raises
|
||||
-------
|
||||
NotFound
|
||||
The scheduled event was not found.
|
||||
HTTPException
|
||||
Retrieving the scheduled event failed.
|
||||
|
||||
Returns
|
||||
--------
|
||||
:class:`ScheduledEvent`
|
||||
The scheduled event.
|
||||
"""
|
||||
data = await self._state.http.get_scheduled_event(self.id, scheduled_event_id, with_counts)
|
||||
return ScheduledEvent(state=self._state, data=data)
|
||||
|
||||
async def create_scheduled_event(
|
||||
self,
|
||||
*,
|
||||
name: str,
|
||||
start_time: datetime.datetime,
|
||||
entity_type: EntityType,
|
||||
privacy_level: PrivacyLevel = MISSING,
|
||||
channel: Optional[Snowflake] = MISSING,
|
||||
location: str = MISSING,
|
||||
end_time: datetime.datetime = MISSING,
|
||||
description: str = MISSING,
|
||||
image: bytes = MISSING,
|
||||
reason: Optional[str] = None,
|
||||
) -> ScheduledEvent:
|
||||
r"""|coro|
|
||||
|
||||
Creates a scheduled event for the guild.
|
||||
|
||||
Requires :attr:`~Permissions.manage_events` permissions.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
Parameters
|
||||
------------
|
||||
name: :class:`str`
|
||||
The name of the scheduled event.
|
||||
description: :class:`str`
|
||||
The description of the scheduled event.
|
||||
channel: Optional[:class:`abc.Snowflake`]
|
||||
The channel to send the scheduled event to.
|
||||
|
||||
Required if ``entity_type`` is either :attr:`EntityType.voice` or
|
||||
:attr:`EntityType.stage_instance`.
|
||||
start_time: :class:`datetime.datetime`
|
||||
The scheduled start time of the scheduled event. This must be a timezone-aware
|
||||
datetime object. Consider using :func:`utils.utcnow`.
|
||||
end_time: :class:`datetime.datetime`
|
||||
The scheduled end time of the scheduled event. This must be a timezone-aware
|
||||
datetime object. Consider using :func:`utils.utcnow`.
|
||||
|
||||
Required if the entity type is :attr:`EntityType.external`.
|
||||
entity_type: :class:`EntityType`
|
||||
The entity type of the scheduled event.
|
||||
image: :class:`bytes`
|
||||
The image of the scheduled event.
|
||||
location: :class:`str`
|
||||
The location of the scheduled event.
|
||||
|
||||
Required if the ``entity_type`` is :attr:`EntityType.external`.
|
||||
reason: Optional[:class:`str`]
|
||||
The reason for creating this scheduled event. Shows up on the audit log.
|
||||
|
||||
Raises
|
||||
-------
|
||||
TypeError
|
||||
`image` was not a :term:`py:bytes-like object`, or ``privacy_level``
|
||||
was not a :class:`PrivacyLevel`, or ``entity_type`` was not an
|
||||
:class:`EntityType`, ``status`` was not an :class:`EventStatus`,
|
||||
or an argument was provided that was incompatible with the provided
|
||||
``entity_type``.
|
||||
ValueError
|
||||
``start_time`` or ``end_time`` was not a timezone-aware datetime object.
|
||||
Forbidden
|
||||
You are not allowed to create scheduled events.
|
||||
HTTPException
|
||||
Creating the scheduled event failed.
|
||||
|
||||
Returns
|
||||
--------
|
||||
:class:`ScheduledEvent`
|
||||
The created scheduled event.
|
||||
"""
|
||||
payload = {}
|
||||
metadata = {}
|
||||
|
||||
payload['name'] = name
|
||||
|
||||
if start_time is not MISSING:
|
||||
if start_time.tzinfo is None:
|
||||
raise ValueError(
|
||||
'start_time must be an aware datetime. Consider using discord.utils.utcnow() or datetime.datetime.now().astimezone() for local time.'
|
||||
)
|
||||
payload['scheduled_start_time'] = start_time.isoformat()
|
||||
|
||||
if not isinstance(entity_type, EntityType):
|
||||
raise TypeError('entity_type must be of type EntityType')
|
||||
|
||||
payload['entity_type'] = entity_type.value
|
||||
|
||||
payload['privacy_level'] = PrivacyLevel.guild_only.value
|
||||
|
||||
if description is not MISSING:
|
||||
payload['description'] = description
|
||||
|
||||
if image is not MISSING:
|
||||
image_as_str: str = utils._bytes_to_base64_data(image)
|
||||
payload['image'] = image_as_str
|
||||
|
||||
if entity_type in (EntityType.stage_instance, EntityType.voice):
|
||||
if channel is MISSING or channel is None:
|
||||
raise TypeError('channel must be set when entity_type is voice or stage_instance')
|
||||
|
||||
payload['channel_id'] = channel.id
|
||||
|
||||
if location is not MISSING:
|
||||
raise TypeError('location cannot be set when entity_type is voice or stage_instance')
|
||||
else:
|
||||
if channel is not MISSING:
|
||||
raise TypeError('channel cannot be set when entity_type is external')
|
||||
|
||||
if location is MISSING or location is None:
|
||||
raise TypeError('location must be set when entity_type is external')
|
||||
|
||||
metadata['location'] = location
|
||||
|
||||
if end_time is not MISSING:
|
||||
if end_time.tzinfo is None:
|
||||
raise ValueError(
|
||||
'end_time must be an aware datetime. Consider using discord.utils.utcnow() or datetime.datetime.now().astimezone() for local time.'
|
||||
)
|
||||
payload['scheduled_end_time'] = end_time.isoformat()
|
||||
|
||||
if metadata:
|
||||
payload['entity_metadata'] = metadata
|
||||
|
||||
data = await self._state.http.create_guild_scheduled_event(self.id, **payload, reason=reason)
|
||||
return ScheduledEvent(state=self._state, data=data)
|
||||
|
||||
async def fetch_emojis(self) -> List[Emoji]:
|
||||
r"""|coro|
|
||||
|
||||
|
@ -1426,12 +1426,21 @@ class HTTPClient:
|
||||
return self.request(r, reason=reason, json=payload)
|
||||
|
||||
def get_invite(
|
||||
self, invite_id: str, *, with_counts: bool = True, with_expiration: bool = True
|
||||
self,
|
||||
invite_id: str,
|
||||
*,
|
||||
with_counts: bool = True,
|
||||
with_expiration: bool = True,
|
||||
guild_scheduled_event_id: Optional[Snowflake] = None,
|
||||
) -> Response[invite.Invite]:
|
||||
params = {
|
||||
params: Dict[str, Any] = {
|
||||
'with_counts': int(with_counts),
|
||||
'with_expiration': int(with_expiration),
|
||||
}
|
||||
|
||||
if guild_scheduled_event_id:
|
||||
params['guild_scheduled_event_id'] = guild_scheduled_event_id
|
||||
|
||||
return self.request(Route('GET', '/invites/{invite_id}', invite_id=invite_id), params=params)
|
||||
|
||||
def invites_from(self, guild_id: Snowflake) -> Response[List[invite.Invite]]:
|
||||
@ -1583,8 +1592,16 @@ class HTTPClient:
|
||||
) -> Response[List[scheduled_event.GuildScheduledEvent]]:
|
||||
...
|
||||
|
||||
@overload
|
||||
def get_scheduled_events(
|
||||
self, guild_id: Snowflake, with_user_count: bool
|
||||
) -> Union[
|
||||
Response[List[scheduled_event.GuildScheduledEventWithUserCount]], Response[List[scheduled_event.GuildScheduledEvent]]
|
||||
]:
|
||||
...
|
||||
|
||||
def get_scheduled_events(self, guild_id: Snowflake, with_user_count: bool) -> Response[Any]:
|
||||
params = {'with_user_count': with_user_count}
|
||||
params = {'with_user_count': int(with_user_count)}
|
||||
return self.request(Route('GET', '/guilds/{guild_id}/scheduled-events', guild_id=guild_id), params=params)
|
||||
|
||||
def create_guild_scheduled_event(
|
||||
@ -1619,10 +1636,16 @@ class HTTPClient:
|
||||
) -> Response[scheduled_event.GuildScheduledEvent]:
|
||||
...
|
||||
|
||||
@overload
|
||||
def get_scheduled_event(
|
||||
self, guild_id: Snowflake, guild_scheduled_event_id: Snowflake, with_user_count: bool
|
||||
) -> Union[Response[scheduled_event.GuildScheduledEventWithUserCount], Response[scheduled_event.GuildScheduledEvent]]:
|
||||
...
|
||||
|
||||
def get_scheduled_event(
|
||||
self, guild_id: Snowflake, guild_scheduled_event_id: Snowflake, with_user_count: bool
|
||||
) -> Response[Any]:
|
||||
params = {'with_user_count': with_user_count}
|
||||
params = {'with_user_count': int(with_user_count)}
|
||||
return self.request(
|
||||
Route(
|
||||
'GET',
|
||||
@ -1643,6 +1666,7 @@ class HTTPClient:
|
||||
'privacy_level',
|
||||
'scheduled_start_time',
|
||||
'scheduled_end_time',
|
||||
'status',
|
||||
'description',
|
||||
'entity_type',
|
||||
'image',
|
||||
@ -1664,6 +1688,8 @@ class HTTPClient:
|
||||
self,
|
||||
guild_id: Snowflake,
|
||||
guild_scheduled_event_id: Snowflake,
|
||||
*,
|
||||
reason: Optional[str] = None,
|
||||
) -> Response[None]:
|
||||
return self.request(
|
||||
Route(
|
||||
@ -1672,10 +1698,11 @@ class HTTPClient:
|
||||
guild_id=guild_id,
|
||||
guild_scheduled_event_id=guild_scheduled_event_id,
|
||||
),
|
||||
reason=reason,
|
||||
)
|
||||
|
||||
@overload
|
||||
def get_scheduled_users(
|
||||
def get_scheduled_event_users(
|
||||
self,
|
||||
guild_id: Snowflake,
|
||||
guild_scheduled_event_id: Snowflake,
|
||||
@ -1683,11 +1710,11 @@ class HTTPClient:
|
||||
with_member: Literal[True],
|
||||
before: Optional[Snowflake] = ...,
|
||||
after: Optional[Snowflake] = ...,
|
||||
) -> Response[scheduled_event.ScheduledEventUserWithMember]:
|
||||
) -> Response[scheduled_event.ScheduledEventUsersWithMember]:
|
||||
...
|
||||
|
||||
@overload
|
||||
def get_scheduled_users(
|
||||
def get_scheduled_event_users(
|
||||
self,
|
||||
guild_id: Snowflake,
|
||||
guild_scheduled_event_id: Snowflake,
|
||||
@ -1695,10 +1722,22 @@ class HTTPClient:
|
||||
with_member: Literal[False],
|
||||
before: Optional[Snowflake] = ...,
|
||||
after: Optional[Snowflake] = ...,
|
||||
) -> Response[scheduled_event.ScheduledEventUser]:
|
||||
) -> Response[scheduled_event.ScheduledEventUsers]:
|
||||
...
|
||||
|
||||
def get_scheduled_users(
|
||||
@overload
|
||||
def get_scheduled_event_users(
|
||||
self,
|
||||
guild_id: Snowflake,
|
||||
guild_scheduled_event_id: Snowflake,
|
||||
limit: int,
|
||||
with_member: bool,
|
||||
before: Optional[Snowflake] = ...,
|
||||
after: Optional[Snowflake] = ...,
|
||||
) -> Union[Response[scheduled_event.ScheduledEventUsersWithMember], Response[scheduled_event.ScheduledEventUsers]]:
|
||||
...
|
||||
|
||||
def get_scheduled_event_users(
|
||||
self,
|
||||
guild_id: Snowflake,
|
||||
guild_scheduled_event_id: Snowflake,
|
||||
@ -1709,7 +1748,7 @@ class HTTPClient:
|
||||
) -> Response[Any]:
|
||||
params: Dict[str, Any] = {
|
||||
'limit': limit,
|
||||
'with_member': with_member,
|
||||
'with_member': int(with_member),
|
||||
}
|
||||
|
||||
if before is not None:
|
||||
|
@ -31,6 +31,7 @@ from .object import Object
|
||||
from .mixins import Hashable
|
||||
from .enums import ChannelType, VerificationLevel, InviteTarget, try_enum
|
||||
from .appinfo import PartialAppInfo
|
||||
from .scheduled_event import ScheduledEvent
|
||||
|
||||
__all__ = (
|
||||
'PartialInviteChannel',
|
||||
@ -51,6 +52,7 @@ if TYPE_CHECKING:
|
||||
from .guild import Guild
|
||||
from .abc import GuildChannel
|
||||
from .user import User
|
||||
from .abc import Snowflake
|
||||
|
||||
InviteGuildType = Union[Guild, 'PartialInviteGuild', Object]
|
||||
InviteChannelType = Union[GuildChannel, 'PartialInviteChannel', Object]
|
||||
@ -303,6 +305,14 @@ class Invite(Hashable):
|
||||
target_application: Optional[:class:`PartialAppInfo`]
|
||||
The embedded application the invite targets, if any.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
scheduled_event: Optional[:class:`ScheduledEvent`]
|
||||
The scheduled event associated with this invite, if any.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
scheduled_event_id: Optional[:class:`int`]
|
||||
The ID of the scheduled event associated with this invite, if any.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
|
||||
@ -324,6 +334,8 @@ class Invite(Hashable):
|
||||
'approximate_presence_count',
|
||||
'target_application',
|
||||
'expires_at',
|
||||
'scheduled_event',
|
||||
'scheduled_event_id',
|
||||
)
|
||||
|
||||
BASE = 'https://discord.gg'
|
||||
@ -366,6 +378,17 @@ class Invite(Hashable):
|
||||
PartialAppInfo(data=application, state=state) if application else None
|
||||
)
|
||||
|
||||
scheduled_event = data.get('guild_scheduled_event')
|
||||
self.scheduled_event: Optional[ScheduledEvent] = (
|
||||
ScheduledEvent(
|
||||
state=self._state,
|
||||
data=scheduled_event,
|
||||
)
|
||||
if scheduled_event
|
||||
else None
|
||||
)
|
||||
self.scheduled_event_id: Optional[int] = self.scheduled_event.id if self.scheduled_event else None
|
||||
|
||||
@classmethod
|
||||
def from_incomplete(cls: Type[I], *, state: ConnectionState, data: InvitePayload) -> I:
|
||||
guild: Optional[Union[Guild, PartialInviteGuild]]
|
||||
@ -451,7 +474,33 @@ class Invite(Hashable):
|
||||
@property
|
||||
def url(self) -> str:
|
||||
""":class:`str`: A property that retrieves the invite URL."""
|
||||
return self.BASE + '/' + self.code
|
||||
url = self.BASE + '/' + self.code
|
||||
if self.scheduled_event_id is not None:
|
||||
url += '?event=' + str(self.scheduled_event_id)
|
||||
return url
|
||||
|
||||
def set_scheduled_event(self: I, scheduled_event: Snowflake, /) -> I:
|
||||
"""Sets the scheduled event for this invite.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
Parameters
|
||||
----------
|
||||
scheduled_event: :class:`~discord.abc.Snowflake`
|
||||
The ID of the scheduled event.
|
||||
|
||||
Returns
|
||||
--------
|
||||
:class:`Invite`
|
||||
The invite with the new scheduled event.
|
||||
"""
|
||||
self.scheduled_event_id = scheduled_event.id
|
||||
try:
|
||||
self.scheduled_event = self.guild.get_scheduled_event(scheduled_event.id) # type: ignore - handled below
|
||||
except AttributeError:
|
||||
self.scheduled_event = None
|
||||
|
||||
return self
|
||||
|
||||
async def delete(self, *, reason: Optional[str] = None):
|
||||
"""|coro|
|
||||
|
549
discord/scheduled_event.py
Normal file
549
discord/scheduled_event.py
Normal file
@ -0,0 +1,549 @@
|
||||
"""
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-present Rapptz
|
||||
|
||||
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
|
||||
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, AsyncIterator, Dict, Optional, Union
|
||||
|
||||
from .asset import Asset
|
||||
from .enums import EventStatus, EntityType, PrivacyLevel, try_enum
|
||||
from .mixins import Hashable
|
||||
from .object import Object, OLDEST_OBJECT
|
||||
from .utils import parse_time, _get_as_snowflake, _bytes_to_base64_data, MISSING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .types.scheduled_event import (
|
||||
GuildScheduledEvent as GuildScheduledEventPayload,
|
||||
GuildScheduledEventWithUserCount as GuildScheduledEventWithUserCountPayload,
|
||||
EntityMetadata,
|
||||
)
|
||||
|
||||
from .abc import Snowflake
|
||||
from .channel import VoiceChannel, StageChannel
|
||||
from .state import ConnectionState
|
||||
from .user import User
|
||||
|
||||
GuildScheduledEventPayload = Union[GuildScheduledEventPayload, GuildScheduledEventWithUserCountPayload]
|
||||
|
||||
# fmt: off
|
||||
__all__ = (
|
||||
"ScheduledEvent",
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
|
||||
class ScheduledEvent(Hashable):
|
||||
"""Represents a scheduled event in a guild.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
.. container:: operations
|
||||
|
||||
.. describe:: x == y
|
||||
|
||||
Checks if two scheduled events are equal.
|
||||
|
||||
.. describe:: x != y
|
||||
|
||||
Checks if two scheduled events are not equal.
|
||||
|
||||
.. describe:: hash(x)
|
||||
|
||||
Returns the scheduled event's hash.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
id: :class:`int`
|
||||
The scheduled event's ID.
|
||||
name: :class:`str`
|
||||
The name of the scheduled event.
|
||||
description: :class:`str`
|
||||
The description of the scheduled event.
|
||||
entity_type: :class:`EntityType`
|
||||
The type of entity this event is for.
|
||||
entity_id: :class:`int`
|
||||
The ID of the entity this event is for.
|
||||
start_time: :class:`datetime.datetime`
|
||||
The time that the scheduled event will start in UTC.
|
||||
end_time: :class:`datetime.datetime`
|
||||
The time that the scheduled event will end in UTC.
|
||||
privacy_level: :class:`PrivacyLevel`
|
||||
The privacy level of the scheduled event.
|
||||
status: :class:`EventStatus`
|
||||
The status of the scheduled event.
|
||||
user_count: :class:`int`
|
||||
The number of users subscribed to the scheduled event.
|
||||
creator: Optional[:class:`User`]
|
||||
The user that created the scheduled event.
|
||||
location: Optional[:class:`str`]
|
||||
The location of the scheduled event.
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
'_state',
|
||||
'_users',
|
||||
'id',
|
||||
'guild_id',
|
||||
'name',
|
||||
'description',
|
||||
'entity_type',
|
||||
'entity_id',
|
||||
'start_time',
|
||||
'end_time',
|
||||
'privacy_level',
|
||||
'status',
|
||||
'_cover_image',
|
||||
'user_count',
|
||||
'creator',
|
||||
'channel_id',
|
||||
'location',
|
||||
)
|
||||
|
||||
def __init__(self, *, state: ConnectionState, data: GuildScheduledEventPayload) -> None:
|
||||
self._state = state
|
||||
self._users: Dict[int, User] = {}
|
||||
self._update(data)
|
||||
|
||||
def _update(self, data: GuildScheduledEventPayload) -> None:
|
||||
self.id: int = int(data['id'])
|
||||
self.guild_id: int = int(data['guild_id'])
|
||||
self.name: str = data['name']
|
||||
self.description: str = data.get('description', '')
|
||||
self.entity_type = try_enum(EntityType, data['entity_type'])
|
||||
self.entity_id: int = int(data['id'])
|
||||
self.start_time: datetime = parse_time(data['scheduled_start_time'])
|
||||
self.privacy_level: PrivacyLevel = try_enum(PrivacyLevel, data['status'])
|
||||
self.status: EventStatus = try_enum(EventStatus, data['status'])
|
||||
self._cover_image: Optional[str] = data['image']
|
||||
self.user_count: int = data.get('user_count', 0)
|
||||
|
||||
creator = data.get('creator')
|
||||
self.creator: Optional[User] = self._state.store_user(creator) if creator else None
|
||||
|
||||
self.end_time: Optional[datetime] = parse_time(data.get('scheduled_end_time'))
|
||||
self.channel_id: Optional[int] = _get_as_snowflake(data, 'channel_id')
|
||||
|
||||
metadata = data.get('metadata')
|
||||
if metadata:
|
||||
self._unroll_metadata(metadata)
|
||||
|
||||
def _unroll_metadata(self, data: EntityMetadata):
|
||||
self.location: Optional[str] = data.get('location')
|
||||
|
||||
@classmethod
|
||||
def from_creation(cls, *, state: ConnectionState, data: GuildScheduledEventPayload):
|
||||
creator_id = data.get('creator_id')
|
||||
self = cls(state=state, data=data)
|
||||
if creator_id:
|
||||
self.creator = self._state.get_user(int(creator_id))
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<GuildScheduledEvent id={self.id} name={self.name!r} guild_id={self.guild_id!r} creator={self.creator!r}>'
|
||||
|
||||
@property
|
||||
def cover_image(self) -> Optional[Asset]:
|
||||
"""Optional[:class:`Asset`]: The scheduled event's cover image."""
|
||||
if self._cover_image is None:
|
||||
return None
|
||||
return Asset._from_scheduled_event_cover_image(self._state, self.id, self._cover_image)
|
||||
|
||||
@property
|
||||
def channel(self) -> Optional[Union[VoiceChannel, StageChannel]]:
|
||||
"""Optional[Union[:class:`VoiceChannel`, :class:`StageChannel`]]: The channel this scheduled event is in."""
|
||||
return self.guild.get_channel(self.channel_id) # type: ignore
|
||||
|
||||
async def start(self, *, reason: Optional[str] = None) -> ScheduledEvent:
|
||||
"""|coro|
|
||||
|
||||
Starts the scheduled event.
|
||||
|
||||
Shorthand for:
|
||||
|
||||
.. code-block:: python3
|
||||
|
||||
await event.edit(status=EventStatus.active)
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
reason: Optional[:class:`str`]
|
||||
The reason for starting the scheduled event.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
The scheduled event has already started or has ended.
|
||||
Forbidden
|
||||
You do not have the proper permissions to start the scheduled event.
|
||||
HTTPException
|
||||
The scheduled event could not be started.
|
||||
|
||||
Returns
|
||||
-------
|
||||
:class:`ScheduledEvent`
|
||||
The scheduled event that was started.
|
||||
"""
|
||||
if self.status is not EventStatus.scheduled:
|
||||
raise ValueError('This scheduled event is already running.')
|
||||
|
||||
return await self.edit(status=EventStatus.active, reason=reason)
|
||||
|
||||
async def end(self, *, reason: Optional[str] = None) -> ScheduledEvent:
|
||||
"""|coro|
|
||||
|
||||
Ends the scheduled event.
|
||||
|
||||
Shorthand for:
|
||||
|
||||
.. code-block:: python3
|
||||
|
||||
await event.edit(status=EventStatus.completed)
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
reason: Optional[:class:`str`]
|
||||
The reason for ending the scheduled event.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
The scheduled event is not active or has already ended.
|
||||
Forbidden
|
||||
You do not have the proper permissions to end the scheduled event.
|
||||
HTTPException
|
||||
The scheduled event could not be ended.
|
||||
|
||||
Returns
|
||||
-------
|
||||
:class:`ScheduledEvent`
|
||||
The scheduled event that was ended.
|
||||
"""
|
||||
if self.status is not EventStatus.active:
|
||||
raise ValueError('This scheduled event is not active.')
|
||||
|
||||
return await self.edit(status=EventStatus.ended, reason=reason)
|
||||
|
||||
async def cancel(self, *, reason: Optional[str] = None) -> ScheduledEvent:
|
||||
"""|coro|
|
||||
|
||||
Cancels the scheduled event.
|
||||
|
||||
Shorthand for:
|
||||
|
||||
.. code-block:: python3
|
||||
|
||||
await event.edit(status=EventStatus.cancelled)
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
reason: Optional[:class:`str`]
|
||||
The reason for cancelling the scheduled event.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
The scheduled event is already running.
|
||||
Forbidden
|
||||
You do not have the proper permissions to cancel the scheduled event.
|
||||
HTTPException
|
||||
The scheduled event could not be cancelled.
|
||||
|
||||
Returns
|
||||
-------
|
||||
:class:`ScheduledEvent`
|
||||
The scheduled event that was cancelled.
|
||||
"""
|
||||
if self.status is not EventStatus.scheduled:
|
||||
raise ValueError('This scheduled event is already running.')
|
||||
|
||||
return await self.edit(status=EventStatus.cancelled, reason=reason)
|
||||
|
||||
async def edit(
|
||||
self,
|
||||
*,
|
||||
name: str = MISSING,
|
||||
description: str = MISSING,
|
||||
channel: Optional[Snowflake] = MISSING,
|
||||
start_time: datetime = MISSING,
|
||||
end_time: datetime = MISSING,
|
||||
privacy_level: PrivacyLevel = MISSING,
|
||||
entity_type: EntityType = MISSING,
|
||||
status: EventStatus = MISSING,
|
||||
image: bytes = MISSING,
|
||||
location: str = MISSING,
|
||||
reason: Optional[str] = None,
|
||||
) -> ScheduledEvent:
|
||||
r"""|coro|
|
||||
|
||||
Edits the scheduled event.
|
||||
|
||||
Requires :attr:`~Permissions.manage_events` permissions.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
name: :class:`str`
|
||||
The name of the scheduled event.
|
||||
description: :class:`str`
|
||||
The description of the scheduled event.
|
||||
channel: Optional[:class:`~discord.abc.Snowflake`]
|
||||
The channel to put the scheduled event in.
|
||||
|
||||
Required if the entity type is either :attr:`EntityType.voice` or
|
||||
:attr:`EntityType.stage_instance`.
|
||||
start_time: :class:`datetime.datetime`
|
||||
The time that the scheduled event will start. This must be a timezone-aware
|
||||
datetime object. Consider using :func:`utils.utcnow`.
|
||||
end_time: :class:`datetime.datetime`
|
||||
The time that the scheduled event will end. This must be a timezone-aware
|
||||
datetime object. Consider using :func:`utils.utcnow`.
|
||||
|
||||
Required if the entity type is :attr:`EntityType.external`.
|
||||
privacy_level: :class:`PrivacyLevel`
|
||||
The privacy level of the scheduled event.
|
||||
entity_type: :class:`EntityType`
|
||||
The new entity type.
|
||||
status: :class:`EventStatus`
|
||||
The new status of the scheduled event.
|
||||
image: :class:`bytes`
|
||||
The new image of the scheduled event.
|
||||
location: :class:`str`
|
||||
The new location of the scheduled event.
|
||||
|
||||
Required if the entity type is :attr:`EntityType.external`.
|
||||
reason: Optional[:class:`str`]
|
||||
The reason for editing the scheduled event. Shows up on the audit log.
|
||||
|
||||
Raises
|
||||
-------
|
||||
TypeError
|
||||
`image` was not a :term:`py:bytes-like object`, or ``privacy_level``
|
||||
was not a :class:`PrivacyLevel`, or ``entity_type`` was not an
|
||||
:class:`EntityType`, ``status`` was not an :class:`EventStatus`, or
|
||||
an argument was provided that was incompatible with the scheduled event's
|
||||
entity type.
|
||||
ValueError
|
||||
``start_time`` or ``end_time`` was not a timezone-aware datetime object.
|
||||
Forbidden
|
||||
You do not have permissions to edit the scheduled event.
|
||||
HTTPException
|
||||
Editing the scheduled event failed.
|
||||
|
||||
Returns
|
||||
--------
|
||||
:class:`ScheduledEvent`
|
||||
The edited scheduled event.
|
||||
"""
|
||||
payload = {}
|
||||
metadata = {}
|
||||
|
||||
if name is not MISSING:
|
||||
payload['name'] = name
|
||||
|
||||
if start_time is not MISSING:
|
||||
if start_time.tzinfo is None:
|
||||
raise ValueError(
|
||||
'start_time must be an aware datetime. Consider using discord.utils.utcnow() or datetime.datetime.now().astimezone() for local time.'
|
||||
)
|
||||
payload['scheduled_start_time'] = start_time.isoformat()
|
||||
|
||||
if description is not MISSING:
|
||||
payload['description'] = description
|
||||
|
||||
if privacy_level is not MISSING:
|
||||
if not isinstance(privacy_level, PrivacyLevel):
|
||||
raise TypeError('privacy_level must be of type PrivacyLevel.')
|
||||
|
||||
payload['privacy_level'] = privacy_level.value
|
||||
|
||||
if status is not MISSING:
|
||||
if not isinstance(status, EventStatus):
|
||||
raise TypeError('status must be of type EventStatus')
|
||||
|
||||
payload['status'] = status.value
|
||||
|
||||
if image is not MISSING:
|
||||
image_as_str: str = _bytes_to_base64_data(image)
|
||||
payload['image'] = image_as_str
|
||||
|
||||
if entity_type is not MISSING:
|
||||
if not isinstance(entity_type, EntityType):
|
||||
raise TypeError('entity_type must be of type EntityType')
|
||||
|
||||
payload['entity_type'] = entity_type.value
|
||||
|
||||
_entity_type = entity_type or self.entity_type
|
||||
|
||||
if _entity_type in (EntityType.stage_instance, EntityType.voice):
|
||||
if channel is MISSING or channel is None:
|
||||
raise TypeError('channel must be set when entity_type is voice or stage_instance')
|
||||
|
||||
payload['channel_id'] = channel.id
|
||||
|
||||
if location is not MISSING:
|
||||
raise TypeError('location cannot be set when entity_type is voice or stage_instance')
|
||||
else:
|
||||
if channel is not MISSING:
|
||||
raise TypeError('channel cannot be set when entity_type is external')
|
||||
|
||||
if location is MISSING or location is None:
|
||||
raise TypeError('location must be set when entity_type is external')
|
||||
|
||||
metadata['location'] = location
|
||||
|
||||
if end_time is MISSING:
|
||||
raise TypeError('end_time must be set when entity_type is external')
|
||||
|
||||
if end_time.tzinfo is None:
|
||||
raise ValueError(
|
||||
'end_time must be an aware datetime. Consider using discord.utils.utcnow() or datetime.datetime.now().astimezone() for local time.'
|
||||
)
|
||||
payload['scheduled_end_time'] = end_time.isoformat()
|
||||
|
||||
if metadata:
|
||||
payload['entity_metadata'] = metadata
|
||||
|
||||
data = await self._state.http.edit_scheduled_event(self.guild_id, self.id, **payload, reason=reason)
|
||||
s = ScheduledEvent(state=self._state, data=data)
|
||||
s._users = self._users
|
||||
return s
|
||||
|
||||
async def delete(self, *, reason: Optional[str] = None) -> None:
|
||||
"""|coro|
|
||||
|
||||
Deletes the scheduled event.
|
||||
|
||||
Requires :attr:`~Permissions.manage_events` permissions.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
reason: Optional[:class:`str`]
|
||||
The reason for deleting the scheduled event. Shows up on the audit log.
|
||||
|
||||
Raises
|
||||
------
|
||||
Forbidden
|
||||
You do not have permissions to delete the scheduled event.
|
||||
HTTPException
|
||||
Deleting the scheduled event failed.
|
||||
"""
|
||||
await self._state.http.delete_scheduled_event(self.guild_id, self.id, reason=reason)
|
||||
|
||||
async def users(
|
||||
self,
|
||||
*,
|
||||
limit: Optional[int] = None,
|
||||
before: Optional[Snowflake] = None,
|
||||
after: Optional[Snowflake] = None,
|
||||
oldest_first: bool = MISSING,
|
||||
) -> AsyncIterator[User]:
|
||||
"""|coro|
|
||||
|
||||
Retrieves all :class:`User` that are in this thread.
|
||||
|
||||
This requires :attr:`Intents.members` to get information about members
|
||||
other than yourself.
|
||||
|
||||
Raises
|
||||
-------
|
||||
HTTPException
|
||||
Retrieving the members failed.
|
||||
|
||||
Returns
|
||||
--------
|
||||
List[:class:`User`]
|
||||
All thread members in the thread.
|
||||
"""
|
||||
|
||||
async def _before_strategy(retrieve, before, limit):
|
||||
before_id = before.id if before else None
|
||||
users = await self._state.http.get_scheduled_event_users(
|
||||
self.guild_id, self.id, limit=retrieve, with_member=False, before=before_id
|
||||
)
|
||||
|
||||
if users:
|
||||
if limit is not None:
|
||||
limit -= len(users)
|
||||
|
||||
before = Object(id=users[-1]['user']['id'])
|
||||
|
||||
return users, before, limit
|
||||
|
||||
async def _after_strategy(retrieve, after, limit):
|
||||
after_id = after.id if after else None
|
||||
users = await self._state.http.get_scheduled_event_users(
|
||||
self.guild_id, self.id, limit=retrieve, with_member=False, after=after_id
|
||||
)
|
||||
|
||||
if users:
|
||||
if limit is not None:
|
||||
limit -= len(users)
|
||||
|
||||
after = Object(id=users[0]['user']['id'])
|
||||
|
||||
return users, after, limit
|
||||
|
||||
if limit is None:
|
||||
limit = self.user_count or None
|
||||
|
||||
if oldest_first is MISSING:
|
||||
reverse = after is not None
|
||||
else:
|
||||
reverse = oldest_first
|
||||
|
||||
predicate = None
|
||||
|
||||
if reverse:
|
||||
strategy, state = _after_strategy, after
|
||||
if before:
|
||||
predicate = lambda u: u['user']['id'] < before.id
|
||||
else:
|
||||
strategy, state = _before_strategy, before
|
||||
if after and after != OLDEST_OBJECT:
|
||||
predicate = lambda u: u['user']['id'] > after.id
|
||||
|
||||
while True:
|
||||
retrieve = min(100 if limit is None else limit, 100)
|
||||
if retrieve < 1:
|
||||
return
|
||||
|
||||
data, state, limit = await strategy(retrieve, state, limit)
|
||||
|
||||
if len(data) < 100:
|
||||
limit = 0
|
||||
|
||||
if reverse:
|
||||
data = reversed(data)
|
||||
if predicate:
|
||||
data = filter(predicate, data)
|
||||
|
||||
users = (self._state.store_user(raw_user['user']) for raw_user in data)
|
||||
|
||||
for user in users:
|
||||
yield user
|
||||
|
||||
def _add_user(self, user: User) -> None:
|
||||
self._users[user.id] = user
|
||||
|
||||
def _pop_user(self, user_id: int) -> None:
|
||||
self._users.pop(user_id)
|
@ -28,7 +28,7 @@ from typing import Optional, TYPE_CHECKING
|
||||
|
||||
from .utils import MISSING, cached_slot_property
|
||||
from .mixins import Hashable
|
||||
from .enums import StagePrivacyLevel, try_enum
|
||||
from .enums import PrivacyLevel, try_enum
|
||||
|
||||
# fmt: off
|
||||
__all__ = (
|
||||
@ -72,7 +72,7 @@ class StageInstance(Hashable):
|
||||
The ID of the channel that the stage instance is running in.
|
||||
topic: :class:`str`
|
||||
The topic of the stage instance.
|
||||
privacy_level: :class:`StagePrivacyLevel`
|
||||
privacy_level: :class:`PrivacyLevel`
|
||||
The privacy level of the stage instance.
|
||||
discoverable_disabled: :class:`bool`
|
||||
Whether discoverability for the stage instance is disabled.
|
||||
@ -98,7 +98,7 @@ class StageInstance(Hashable):
|
||||
self.id: int = int(data['id'])
|
||||
self.channel_id: int = int(data['channel_id'])
|
||||
self.topic: str = data['topic']
|
||||
self.privacy_level: StagePrivacyLevel = try_enum(StagePrivacyLevel, data['privacy_level'])
|
||||
self.privacy_level: PrivacyLevel = try_enum(PrivacyLevel, data['privacy_level'])
|
||||
self.discoverable_disabled: bool = data.get('discoverable_disabled', False)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
@ -110,14 +110,11 @@ class StageInstance(Hashable):
|
||||
# the returned channel will always be a StageChannel or None
|
||||
return self._state.get_channel(self.channel_id) # type: ignore
|
||||
|
||||
def is_public(self) -> bool:
|
||||
return self.privacy_level is StagePrivacyLevel.public
|
||||
|
||||
async def edit(
|
||||
self,
|
||||
*,
|
||||
topic: str = MISSING,
|
||||
privacy_level: StagePrivacyLevel = MISSING,
|
||||
privacy_level: PrivacyLevel = MISSING,
|
||||
reason: Optional[str] = None,
|
||||
) -> None:
|
||||
"""|coro|
|
||||
@ -131,7 +128,7 @@ class StageInstance(Hashable):
|
||||
-----------
|
||||
topic: :class:`str`
|
||||
The stage instance's new topic.
|
||||
privacy_level: :class:`StagePrivacyLevel`
|
||||
privacy_level: :class:`PrivacyLevel`
|
||||
The stage instance's new privacy level.
|
||||
reason: :class:`str`
|
||||
The reason the stage instance was edited. Shows up on the audit log.
|
||||
@ -152,7 +149,7 @@ class StageInstance(Hashable):
|
||||
payload['topic'] = topic
|
||||
|
||||
if privacy_level is not MISSING:
|
||||
if not isinstance(privacy_level, StagePrivacyLevel):
|
||||
if not isinstance(privacy_level, PrivacyLevel):
|
||||
raise TypeError('privacy_level field must be of type PrivacyLevel')
|
||||
|
||||
payload['privacy_level'] = privacy_level.value
|
||||
|
@ -56,6 +56,7 @@ from .invite import Invite
|
||||
from .integrations import _integration_factory
|
||||
from .interactions import Interaction
|
||||
from .ui.view import ViewStore, View
|
||||
from .scheduled_event import ScheduledEvent
|
||||
from .stage_instance import StageInstance
|
||||
from .threads import Thread, ThreadMember
|
||||
from .sticker import GuildSticker
|
||||
@ -744,6 +745,12 @@ class ConnectionState:
|
||||
guild._remove_channel(channel)
|
||||
self.dispatch('guild_channel_delete', channel)
|
||||
|
||||
if channel.type in (ChannelType.voice, ChannelType.stage_voice):
|
||||
for s in guild.scheduled_events:
|
||||
if s.channel_id == channel.id:
|
||||
guild._scheduled_events.pop(s.id)
|
||||
self.dispatch('scheduled_event_delete', guild, s)
|
||||
|
||||
def parse_channel_update(self, data: gw.ChannelUpdateEvent) -> None:
|
||||
channel_type = try_enum(ChannelType, data.get('type'))
|
||||
channel_id = int(data['id'])
|
||||
@ -1286,6 +1293,80 @@ class ConnectionState:
|
||||
else:
|
||||
_log.debug('STAGE_INSTANCE_DELETE referencing unknown guild ID: %s. Discarding.', data['guild_id'])
|
||||
|
||||
def parse_guild_scheduled_event_create(self, data: gw.GuildScheduledEventCreateEvent) -> None:
|
||||
guild = self._get_guild(int(data['guild_id']))
|
||||
if guild is not None:
|
||||
scheduled_event = ScheduledEvent(state=self, data=data)
|
||||
guild._scheduled_events[scheduled_event.id] = scheduled_event
|
||||
self.dispatch('scheduled_event_create', guild, scheduled_event)
|
||||
else:
|
||||
_log.debug('SCHEDULED_EVENT_CREATE referencing unknown guild ID: %s. Discarding.', data['guild_id'])
|
||||
|
||||
def parse_guild_scheduled_event_update(self, data: gw.GuildScheduledEventUpdateEvent) -> None:
|
||||
guild = self._get_guild(int(data['guild_id']))
|
||||
if guild is not None:
|
||||
scheduled_event = guild._scheduled_events.get(int(data['id']))
|
||||
if scheduled_event is not None:
|
||||
old_scheduled_event = copy.copy(scheduled_event)
|
||||
scheduled_event._update(data)
|
||||
self.dispatch('scheduled_event_update', guild, old_scheduled_event, scheduled_event)
|
||||
else:
|
||||
_log.debug('SCHEDULED_EVENT_UPDATE referencing unknown scheduled event ID: %s. Discarding.', data['id'])
|
||||
else:
|
||||
_log.debug('SCHEDULED_EVENT_UPDATE referencing unknown guild ID: %s. Discarding.', data['guild_id'])
|
||||
|
||||
def parse_guild_scheduled_event_delete(self, data: gw.GuildScheduledEventDeleteEvent) -> None:
|
||||
guild = self._get_guild(int(data['guild_id']))
|
||||
if guild is not None:
|
||||
try:
|
||||
scheduled_event = guild._scheduled_events.pop(int(data['id']))
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
self.dispatch('scheduled_event_delete', guild, scheduled_event)
|
||||
else:
|
||||
_log.debug('SCHEDULED_EVENT_DELETE referencing unknown guild ID: %s. Discarding.', data['guild_id'])
|
||||
|
||||
def parse_guild_scheduled_event_user_add(self, data: gw.GuildScheduledEventUserAdd) -> None:
|
||||
guild = self._get_guild(int(data['guild_id']))
|
||||
if guild is not None:
|
||||
scheduled_event = guild._scheduled_events.get(int(data['guild_scheduled_event_id']))
|
||||
if scheduled_event is not None:
|
||||
user = self.get_user(int(data['user_id']))
|
||||
if user is not None:
|
||||
scheduled_event._add_user(user)
|
||||
self.dispatch('scheduled_event_user_add', guild, scheduled_event, user)
|
||||
else:
|
||||
_log.debug('SCHEDULED_EVENT_USER_ADD referencing unknown user ID: %s. Discarding.', data['user_id'])
|
||||
self.dispatch('scheduled_event_user_add', guild, scheduled_event, user)
|
||||
else:
|
||||
_log.debug(
|
||||
'SCHEDULED_EVENT_USER_ADD referencing unknown scheduled event ID: %s. Discarding.',
|
||||
data['guild_scheduled_event_id'],
|
||||
)
|
||||
else:
|
||||
_log.debug('SCHEDULED_EVENT_USER_ADD referencing unknown guild ID: %s. Discarding.', data['guild_id'])
|
||||
|
||||
def parse_guild_scheduled_event_user_remove(self, data: gw.GuildScheduledEventUserRemove) -> None:
|
||||
guild = self._get_guild(int(data['guild_id']))
|
||||
if guild is not None:
|
||||
scheduled_event = guild._scheduled_events.get(int(data['guild_scheduled_event_id']))
|
||||
if scheduled_event is not None:
|
||||
user = self.get_user(int(data['user_id']))
|
||||
if user is not None:
|
||||
scheduled_event._pop_user(user.id)
|
||||
self.dispatch('scheduled_event_user_remove', scheduled_event, user)
|
||||
else:
|
||||
_log.debug('SCHEDULED_EVENT_USER_REMOVE referencing unknown user ID: %s. Discarding.', data['user_id'])
|
||||
self.dispatch('scheduled_event_user_remove', scheduled_event, user)
|
||||
else:
|
||||
_log.debug(
|
||||
'SCHEDULED_EVENT_USER_REMOVE referencing unknown scheduled event ID: %s. Discarding.',
|
||||
data['guild_scheduled_event_id'],
|
||||
)
|
||||
else:
|
||||
_log.debug('SCHEDULED_EVENT_USER_REMOVE referencing unknown guild ID: %s. Discarding.', data['guild_id'])
|
||||
|
||||
def parse_voice_state_update(self, data: gw.VoiceStateUpdateEvent) -> None:
|
||||
guild = self._get_guild(utils._get_as_snowflake(data, 'guild_id'))
|
||||
channel_id = utils._get_as_snowflake(data, 'channel_id')
|
||||
|
@ -25,13 +25,15 @@ DEALINGS IN THE SOFTWARE.
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Literal, Optional, TypedDict, Union
|
||||
|
||||
from .webhook import Webhook
|
||||
from .guild import MFALevel, VerificationLevel, ExplicitContentFilterLevel, DefaultMessageNotificationLevel
|
||||
from .integration import IntegrationExpireBehavior, PartialIntegration
|
||||
from .user import User
|
||||
from .scheduled_event import EntityType, EventStatus, GuildScheduledEvent
|
||||
from .snowflake import Snowflake
|
||||
from .role import Role
|
||||
from .channel import ChannelType, VideoQualityMode, PermissionOverwrite
|
||||
from .channel import ChannelType, PrivacyLevel, VideoQualityMode, PermissionOverwrite
|
||||
from .threads import Thread
|
||||
|
||||
AuditLogEvent = Literal[
|
||||
@ -76,6 +78,9 @@ AuditLogEvent = Literal[
|
||||
90,
|
||||
91,
|
||||
92,
|
||||
100,
|
||||
101,
|
||||
102,
|
||||
110,
|
||||
111,
|
||||
112,
|
||||
@ -158,6 +163,7 @@ class _AuditLogChange_Int(TypedDict):
|
||||
'user_limit',
|
||||
'auto_archive_duration',
|
||||
'default_auto_archive_duration',
|
||||
'communication_disabled_until',
|
||||
]
|
||||
new_value: int
|
||||
old_value: int
|
||||
@ -217,6 +223,24 @@ class _AuditLogChange_Overwrites(TypedDict):
|
||||
old_value: List[PermissionOverwrite]
|
||||
|
||||
|
||||
class _AuditLogChange_PrivacyLevel(TypedDict):
|
||||
key: Literal['privacy_level']
|
||||
new_value: PrivacyLevel
|
||||
old_value: PrivacyLevel
|
||||
|
||||
|
||||
class _AuditLogChange_Status(TypedDict):
|
||||
key: Literal['status']
|
||||
new_value: EventStatus
|
||||
old_value: EventStatus
|
||||
|
||||
|
||||
class _AuditLogChange_EntityType(TypedDict):
|
||||
key: Literal['entity_type']
|
||||
new_value: EntityType
|
||||
old_value: EntityType
|
||||
|
||||
|
||||
AuditLogChange = Union[
|
||||
_AuditLogChange_Str,
|
||||
_AuditLogChange_AssetHash,
|
||||
@ -232,6 +256,9 @@ AuditLogChange = Union[
|
||||
_AuditLogChange_IntegrationExpireBehaviour,
|
||||
_AuditLogChange_VideoQualityMode,
|
||||
_AuditLogChange_Overwrites,
|
||||
_AuditLogChange_PrivacyLevel,
|
||||
_AuditLogChange_Status,
|
||||
_AuditLogChange_EntityType,
|
||||
]
|
||||
|
||||
|
||||
@ -265,3 +292,4 @@ class AuditLog(TypedDict):
|
||||
audit_log_entries: List[AuditLogEntry]
|
||||
integrations: List[PartialIntegration]
|
||||
threads: List[Thread]
|
||||
guild_scheduled_events: List[GuildScheduledEvent]
|
||||
|
@ -146,7 +146,7 @@ class GroupDMChannel(_BaseChannel):
|
||||
|
||||
Channel = Union[GuildChannel, DMChannel, GroupDMChannel]
|
||||
|
||||
PrivacyLevel = Literal[1, 2]
|
||||
PrivacyLevel = Literal[2]
|
||||
|
||||
|
||||
class StageInstance(TypedDict):
|
||||
|
@ -29,7 +29,7 @@ from .activity import PartialPresenceUpdate
|
||||
from .voice import GuildVoiceState
|
||||
from .integration import BaseIntegration, IntegrationApplication
|
||||
from .role import Role
|
||||
from .channel import Channel, ChannelType, StageInstance
|
||||
from .channel import ChannelType, StageInstance
|
||||
from .interactions import Interaction
|
||||
from .invite import InviteTargetType
|
||||
from .emoji import Emoji, PartialEmoji
|
||||
@ -41,6 +41,7 @@ from .appinfo import GatewayAppInfo, PartialAppInfo
|
||||
from .guild import Guild, UnavailableGuild
|
||||
from .user import User
|
||||
from .threads import Thread, ThreadMember
|
||||
from .scheduled_event import GuildScheduledEvent
|
||||
|
||||
|
||||
class SessionStartLimit(TypedDict):
|
||||
@ -348,6 +349,17 @@ class WebhooksUpdateEvent(TypedDict):
|
||||
|
||||
StageInstanceCreateEvent = StageInstanceUpdateEvent = StageInstanceDeleteEvent = StageInstance
|
||||
|
||||
GuildScheduledEventCreateEvent = GuildScheduledEventUpdateEvent = GuildScheduledEventDeleteEvent = GuildScheduledEvent
|
||||
|
||||
|
||||
class _GuildScheduledEventUsersEvent(TypedDict):
|
||||
guild_scheduled_event_id: Snowflake
|
||||
user_id: Snowflake
|
||||
guild_id: Snowflake
|
||||
|
||||
|
||||
GuildScheduledEventUserAdd = GuildScheduledEventUserRemove = _GuildScheduledEventUsersEvent
|
||||
|
||||
VoiceStateUpdateEvent = GuildVoiceState
|
||||
|
||||
|
||||
|
@ -23,8 +23,11 @@ DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
from typing import List, Literal, Optional, TypedDict
|
||||
|
||||
from .scheduled_event import GuildScheduledEvent
|
||||
from .sticker import GuildSticker
|
||||
from .snowflake import Snowflake
|
||||
from .channel import GuildChannel
|
||||
from .channel import GuildChannel, StageInstance
|
||||
from .voice import GuildVoiceState
|
||||
from .welcome_screen import WelcomeScreen
|
||||
from .activity import PartialPresenceUpdate
|
||||
@ -142,6 +145,9 @@ class Guild(_BaseGuildPreview, _GuildOptional):
|
||||
premium_tier: PremiumTier
|
||||
preferred_locale: str
|
||||
public_updates_channel_id: Optional[Snowflake]
|
||||
stickers: List[GuildSticker]
|
||||
stage_instances: List[StageInstance]
|
||||
guild_scheduled_events: List[GuildScheduledEvent]
|
||||
|
||||
|
||||
class InviteGuild(Guild, total=False):
|
||||
|
@ -26,6 +26,8 @@ from __future__ import annotations
|
||||
|
||||
from typing import Literal, Optional, TypedDict, Union
|
||||
|
||||
|
||||
from .scheduled_event import GuildScheduledEvent
|
||||
from .snowflake import Snowflake
|
||||
from .guild import InviteGuild, _GuildPreviewUnique
|
||||
from .channel import PartialChannel
|
||||
@ -41,6 +43,7 @@ class _InviteOptional(TypedDict, total=False):
|
||||
target_user: PartialUser
|
||||
target_type: InviteTargetType
|
||||
target_application: PartialAppInfo
|
||||
guild_scheduled_event: GuildScheduledEvent
|
||||
|
||||
|
||||
class _InviteMetadata(TypedDict, total=False):
|
||||
|
@ -22,28 +22,31 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
from typing import Literal, Optional, TypedDict, Union
|
||||
from typing import List, Literal, Optional, TypedDict, Union
|
||||
|
||||
from .snowflake import Snowflake
|
||||
from .user import User
|
||||
from .member import Member
|
||||
from .channel import PrivacyLevel as PrivacyLevel
|
||||
|
||||
GuildScheduledEventPrivacyLevel = Literal[1]
|
||||
EventStatus = Literal[1, 2, 3, 4]
|
||||
EntityType = Literal[1, 2, 3]
|
||||
|
||||
|
||||
class _BaseGuildScheduledEventOptional(TypedDict, total=False):
|
||||
creator_id: Optional[Snowflake]
|
||||
description: str
|
||||
creator: User
|
||||
user_count: int
|
||||
|
||||
|
||||
class _BaseGuildScheduledEvent(_BaseGuildScheduledEventOptional):
|
||||
id: Snowflake
|
||||
guild_id: Snowflake
|
||||
entity_id: Optional[Snowflake]
|
||||
name: str
|
||||
scheduled_start_time: str
|
||||
privacy_level: GuildScheduledEventPrivacyLevel
|
||||
privacy_level: PrivacyLevel
|
||||
status: EventStatus
|
||||
image: Optional[str]
|
||||
|
||||
@ -80,7 +83,7 @@ GuildScheduledEvent = Union[StageInstanceScheduledEvent, VoiceScheduledEvent, Ex
|
||||
|
||||
|
||||
class _WithUserCount(TypedDict):
|
||||
user_count: str
|
||||
user_count: int
|
||||
|
||||
|
||||
class _StageInstanceScheduledEventWithUserCount(StageInstanceScheduledEvent, _WithUserCount):
|
||||
@ -107,3 +110,7 @@ class ScheduledEventUser(TypedDict):
|
||||
|
||||
class ScheduledEventUserWithMember(ScheduledEventUser):
|
||||
member: Member
|
||||
|
||||
|
||||
ScheduledEventUsers = List[ScheduledEventUser]
|
||||
ScheduledEventUsersWithMember = List[ScheduledEventUserWithMember]
|
||||
|
@ -40,6 +40,7 @@ from typing import (
|
||||
List,
|
||||
Literal,
|
||||
Mapping,
|
||||
NamedTuple,
|
||||
Optional,
|
||||
Protocol,
|
||||
Sequence,
|
||||
@ -63,6 +64,8 @@ import sys
|
||||
import types
|
||||
import warnings
|
||||
|
||||
import yarl
|
||||
|
||||
try:
|
||||
import orjson
|
||||
except ModuleNotFoundError:
|
||||
@ -729,10 +732,19 @@ def _string_width(string: str, *, _IS_ASCII=_IS_ASCII) -> int:
|
||||
return sum(2 if func(char) in UNICODE_WIDE_CHAR_TYPE else 1 for char in string)
|
||||
|
||||
|
||||
def resolve_invite(invite: Union[Invite, str]) -> str:
|
||||
class ResolvedInvite(NamedTuple):
|
||||
code: str
|
||||
event: Optional[int]
|
||||
|
||||
|
||||
def resolve_invite(invite: Union[Invite, str]) -> ResolvedInvite:
|
||||
"""
|
||||
Resolves an invite from a :class:`~discord.Invite`, URL or code.
|
||||
|
||||
.. versionchanged:: 2.0
|
||||
Now returns a :class:`.ResolvedInvite` instead of a
|
||||
:class:`str`.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
invite: Union[:class:`~discord.Invite`, :class:`str`]
|
||||
@ -740,19 +752,24 @@ def resolve_invite(invite: Union[Invite, str]) -> str:
|
||||
|
||||
Returns
|
||||
--------
|
||||
:class:`str`
|
||||
The invite code.
|
||||
:class:`.ResolvedInvite`
|
||||
A data class containing the invite code and the event ID.
|
||||
"""
|
||||
from .invite import Invite # circular import
|
||||
|
||||
if isinstance(invite, Invite):
|
||||
return invite.code
|
||||
return ResolvedInvite(invite.code, invite.scheduled_event_id)
|
||||
else:
|
||||
rx = r'(?:https?\:\/\/)?discord(?:\.gg|(?:app)?\.com\/invite)\/(.+)'
|
||||
rx = r'(?:https?\:\/\/)?discord(?:\.gg|(?:app)?\.com\/invite)\/[^/]+'
|
||||
m = re.match(rx, invite)
|
||||
|
||||
if m:
|
||||
return m.group(1)
|
||||
return invite
|
||||
url = yarl.URL(invite)
|
||||
code = url.parts[-1]
|
||||
event_id = url.query.get('event')
|
||||
|
||||
return ResolvedInvite(code, int(event_id) if event_id else None)
|
||||
return ResolvedInvite(invite, None)
|
||||
|
||||
|
||||
def resolve_template(code: Union[Template, str]) -> str:
|
||||
|
@ -314,6 +314,6 @@ class Widget:
|
||||
:class:`Invite`
|
||||
The invite from the widget's invite URL.
|
||||
"""
|
||||
invite_id = resolve_invite(self._invite)
|
||||
data = await self._state.http.get_invite(invite_id, with_counts=with_counts)
|
||||
resolved = resolve_invite(self._invite)
|
||||
data = await self._state.http.get_invite(resolved.code, with_counts=with_counts)
|
||||
return Invite.from_incomplete(state=self._state, data=data)
|
||||
|
137
docs/api.rst
137
docs/api.rst
@ -1020,6 +1020,53 @@ to handle it, which defaults to print a traceback and ignoring the exception.
|
||||
:param after: The stage instance after the update.
|
||||
:type after: :class:`StageInstance`
|
||||
|
||||
.. function:: on_scheduled_event_create(event)
|
||||
on_scheduled_event_delete(event)
|
||||
|
||||
Called when a :class:`ScheduledEvent` is created or deleted.
|
||||
|
||||
This requires :attr:`Intents.guild_scheduled_events` to be enabled.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
:param event: The scheduled event that was created or deleted.
|
||||
:type event: :class:`ScheduledEvent`
|
||||
|
||||
.. function:: on_scheduled_event_update(before, after)
|
||||
|
||||
Called when a :class:`ScheduledEvent` is updated.
|
||||
|
||||
This requires :attr:`Intents.guild_scheduled_events` to be enabled.
|
||||
|
||||
The following, but not limited to, examples illustrate when this event is called:
|
||||
|
||||
- The scheduled start/end times are changed.
|
||||
- The channel is changed.
|
||||
- The description is changed.
|
||||
- The status is changed.
|
||||
- The image is changed.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
:param before: The scheduled event before the update.
|
||||
:type before: :class:`ScheduledEvent`
|
||||
:param after: The scheduled event after the update.
|
||||
:type after: :class:`ScheduledEvent`
|
||||
|
||||
.. function:: on_scheduled_event_user_add(event, user)
|
||||
on_scheduled_event_user_remove(event, user)
|
||||
|
||||
Called when a user is added or removed from a :class:`ScheduledEvent`.
|
||||
|
||||
This requires :attr:`Intents.guild_scheduled_events` to be enabled.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
:param event: The scheduled event that the user was added or removed from.
|
||||
:type event: :class:`ScheduledEvent`
|
||||
:param user: The user that was added or removed.
|
||||
:type user: :class:`User`
|
||||
|
||||
.. function:: on_member_ban(guild, user)
|
||||
|
||||
Called when user gets banned from a :class:`Guild`.
|
||||
@ -1110,6 +1157,22 @@ Utility Functions
|
||||
|
||||
.. autofunction:: discord.utils.escape_mentions
|
||||
|
||||
.. class:: ResolvedInvite
|
||||
|
||||
A data class which represents a resolved invite returned from :func:`discord.utils.resolve_invite`.
|
||||
|
||||
.. attribute:: code
|
||||
|
||||
The invite code.
|
||||
|
||||
:type: :class:`str`
|
||||
|
||||
.. attribute:: event
|
||||
|
||||
The id of the scheduled event that the invite refers to.
|
||||
|
||||
:type: Optional[:class:`int`]
|
||||
|
||||
.. autofunction:: discord.utils.resolve_invite
|
||||
|
||||
.. autofunction:: discord.utils.resolve_template
|
||||
@ -2503,20 +2566,12 @@ of :class:`enum.Enum`.
|
||||
|
||||
Represents full camera video quality.
|
||||
|
||||
.. class:: StagePrivacyLevel
|
||||
.. class:: PrivacyLevel
|
||||
|
||||
Represents a stage instance's privacy level.
|
||||
Represents the privacy level of a stage instance or scheduled event.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
.. attribute:: public
|
||||
|
||||
The stage instance can be joined by external users.
|
||||
|
||||
.. attribute:: closed
|
||||
|
||||
The stage instance can only be joined by members of the guild.
|
||||
|
||||
.. attribute:: guild_only
|
||||
|
||||
Alias for :attr:`.closed`
|
||||
@ -2726,6 +2781,50 @@ of :class:`enum.Enum`.
|
||||
|
||||
The guild requires 2 factor authentication.
|
||||
|
||||
.. class:: EntityType
|
||||
|
||||
Represents the type of entity that a scheduled event is for.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
.. attribute:: stage_instance
|
||||
|
||||
The scheduled event will occur in a stage instance.
|
||||
|
||||
.. attribute:: voice
|
||||
|
||||
The scheduled event will occur in a voice channel.
|
||||
|
||||
.. attribute:: external
|
||||
|
||||
The scheduled event will occur externally.
|
||||
|
||||
.. class:: EventStatus
|
||||
|
||||
Represents the status of an event.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
.. attribute:: scheduled
|
||||
|
||||
The event is scheduled.
|
||||
|
||||
.. attribute:: active
|
||||
|
||||
The event is active.
|
||||
|
||||
.. attribute:: completed
|
||||
|
||||
The event has ended.
|
||||
|
||||
.. attribute:: cancelled
|
||||
|
||||
The event has been cancelled.
|
||||
|
||||
.. attribute:: canceled
|
||||
|
||||
An alias for :attr:`cancelled`.
|
||||
|
||||
.. _discord-api-audit-logs:
|
||||
|
||||
Audit Log Data
|
||||
@ -3007,9 +3106,9 @@ AuditLogDiff
|
||||
|
||||
.. attribute:: privacy_level
|
||||
|
||||
The privacy level of the stage instance.
|
||||
The privacy level of the stage instance or scheduled event
|
||||
|
||||
:type: :class:`StagePrivacyLevel`
|
||||
:type: :class:`PrivacyLevel`
|
||||
|
||||
.. attribute:: roles
|
||||
|
||||
@ -3207,9 +3306,10 @@ AuditLogDiff
|
||||
|
||||
.. attribute:: description
|
||||
|
||||
The description of a guild, or a sticker.
|
||||
The description of a guild, a sticker, or a scheduled event.
|
||||
|
||||
See also :attr:`Guild.description`, or :attr:`GuildSticker.description`.
|
||||
See also :attr:`Guild.description`, :attr:`GuildSticker.description`, or
|
||||
:attr:`ScheduledEvent.description`.
|
||||
|
||||
:type: :class:`str`
|
||||
|
||||
@ -3563,6 +3663,15 @@ Guild
|
||||
:type: :class:`User`
|
||||
|
||||
|
||||
ScheduledEvent
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
.. attributetable:: ScheduledEvent
|
||||
|
||||
.. autoclass:: ScheduledEvent()
|
||||
:members:
|
||||
|
||||
|
||||
Integration
|
||||
~~~~~~~~~~~~
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user