Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
43227c4
at first i only wanted to add cached members...
Lumabots Dec 9, 2025
fe47678
correcting some bs, like some unknow type added for i dont know what …
Lumabots Dec 9, 2025
fff9b3b
use discord variable name, try an implementation for audit logs
Lumabots Dec 9, 2025
9e6a986
feat: add recurrence support for scheduled events with new enums and …
Lumabots Dec 9, 2025
7b2c31f
refactor: remove ScheduledEventLocation class and related attributes …
Lumabots Dec 9, 2025
5a2581e
feat: enhance scheduled event recurrence with validation and serializ…
Lumabots Dec 9, 2025
973a3d2
Merge branch 'master' into on_scheduled_event_delete
Lumabots Dec 9, 2025
9d1d5d7
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 9, 2025
8c85a05
revert breaking change
Lumabots Dec 10, 2025
40d79b6
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 10, 2025
b5dc3e9
Update discord/enums.py
Lumabots Dec 12, 2025
405f2c6
Merge branch 'master' into on_scheduled_event_delete
Lumabots Dec 12, 2025
13420ce
paillat comment
Lumabots Dec 12, 2025
469a7b5
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 12, 2025
a2f09fe
feat: add overloads for ScheduledEventRecurrenceRule constructor
Lumabots Dec 12, 2025
7e42577
Merge branch 'master' into on_scheduled_event_delete
Lumabots Dec 29, 2025
1c50c9e
Merge branch 'master' into on_scheduled_event_delete
Lumabots Jan 16, 2026
2ff8daf
Merge branch 'master' into on_scheduled_event_delete
Lumabots Jan 16, 2026
6dfd511
refactor: 🗑️ Remove scheduled event recurrence classes and related at…
Lumabots Jan 16, 2026
7fdf52e
Update scheduled_events.py
Lumabots Jan 16, 2026
bafd549
refactor: 🗑️ Change _cached_subscribers from dict to set for improved…
Lumabots Jan 16, 2026
8f887d3
paillat comment
Lumabots Jan 16, 2026
6772e09
Update discord/enums.py
Lumabots Jan 16, 2026
79194b5
refactor: 🗑️ Update location type references and deprecate 'cover' pa…
Lumabots Jan 16, 2026
70c37cb
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 16, 2026
2800a08
refactor: 🗑️ Remove unnecessary calls to _add_scheduled_event in Conn…
Lumabots Jan 16, 2026
b9ddb02
refactor: 🗑️ Enhance create_scheduled_event method with overloads and…
Lumabots Jan 16, 2026
7b4f7f5
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 18 additions & 35 deletions discord/audit_logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,8 @@ class AuditLogChanges:
"type": (None, _transform_type),
"status": (None, _enum_transformer(enums.ScheduledEventStatus)),
"entity_type": (
"location_type",
_enum_transformer(enums.ScheduledEventLocationType),
"entity_type",
_enum_transformer(enums.ScheduledEventEntityType),
),
"command_id": ("command_id", _transform_snowflake),
"image_hash": ("image", _transform_scheduled_event_image),
Expand Down Expand Up @@ -318,7 +318,11 @@ def __init__(
"$add_allow_list",
]:
self._handle_trigger_metadata(
self.before, self.after, entry, elem["new_value"], attr # type: ignore
self.before,
self.after,
entry,
elem["new_value"],
attr, # type: ignore
)
continue
elif attr in [
Expand All @@ -327,7 +331,11 @@ def __init__(
"$remove_allow_list",
]:
self._handle_trigger_metadata(
self.after, self.before, entry, elem["new_value"], attr # type: ignore
self.after,
self.before,
entry,
elem["new_value"],
attr, # type: ignore
)
continue

Expand All @@ -349,21 +357,6 @@ def __init__(
if transformer:
before = transformer(entry, before)

if attr == "location" and hasattr(self.before, "location_type"):
from .scheduled_events import ScheduledEventLocation

if (
self.before.location_type
is enums.ScheduledEventLocationType.external
):
before = ScheduledEventLocation(state=state, value=before)
elif hasattr(self.before, "channel"):
before = ScheduledEventLocation(
state=state, value=self.before.channel
)

setattr(self.before, attr, before)

try:
after = elem["new_value"]
except KeyError:
Expand All @@ -372,21 +365,6 @@ def __init__(
if transformer:
after = transformer(entry, after)

if attr == "location" and hasattr(self.after, "location_type"):
from .scheduled_events import ScheduledEventLocation

if (
self.after.location_type
is enums.ScheduledEventLocationType.external
):
after = ScheduledEventLocation(state=state, value=after)
elif hasattr(self.after, "channel"):
after = ScheduledEventLocation(
state=state, value=self.after.channel
)

setattr(self.after, attr, after)

# add an alias
if hasattr(self.after, "colour"):
self.after.color = self.after.colour
Expand Down Expand Up @@ -691,7 +669,12 @@ def _convert_target_invite(self, target_id: int) -> Invite:
"uses": changeset.uses,
}

obj = Invite(state=self._state, data=fake_payload, guild=self.guild, channel=changeset.channel) # type: ignore
obj = Invite(
state=self._state,
data=fake_payload,
guild=self.guild,
channel=changeset.channel,
) # type: ignore
try:
obj.inviter = changeset.inviter
except AttributeError:
Expand Down
13 changes: 11 additions & 2 deletions discord/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"EmbeddedActivity",
"ScheduledEventStatus",
"ScheduledEventPrivacyLevel",
"ScheduledEventEntityType",
"ScheduledEventLocationType",
"InputTextStyle",
"SlashCommandOptionType",
Expand Down Expand Up @@ -955,13 +956,21 @@ def __int__(self):
return self.value


class ScheduledEventLocationType(Enum):
"""Scheduled event location type"""
class ScheduledEventEntityType(Enum):
"""Scheduled event entity type"""

stage_instance = 1
voice = 2
external = 3

def __int__(self):
return self.value


# TODO(Paillat-dev): Add @deprecated notice using warnings.deprecated in relevant PR
class ScheduledEventLocationType(ScheduledEventEntityType):
"""Scheduled event location type (deprecated alias for :class:`ScheduledEventEntityType`)"""


class AutoModTriggerType(Enum):
"""Automod trigger type"""
Expand Down
147 changes: 121 additions & 26 deletions discord/guild.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,21 @@
NotificationLevel,
NSFWLevel,
OnboardingMode,
ScheduledEventLocationType,
ScheduledEventEntityType,
ScheduledEventPrivacyLevel,
SortOrder,
VerificationLevel,
VideoQualityMode,
VoiceRegion,
try_enum,
)
from .errors import ClientException, HTTPException, InvalidArgument, InvalidData
from .errors import (
ClientException,
HTTPException,
InvalidArgument,
InvalidData,
ValidationError,
)
from .file import File
from .flags import SystemChannelFlags
from .incidents import IncidentsData
Expand All @@ -87,13 +93,17 @@
from .onboarding import Onboarding
from .permissions import PermissionOverwrite
from .role import Role, RoleColours
from .scheduled_events import ScheduledEvent, ScheduledEventLocation
from .scheduled_events import (
ScheduledEvent,
ScheduledEventEntityMetadata,
ScheduledEventLocation,
)
from .soundboard import SoundboardSound
from .stage_instance import StageInstance
from .sticker import GuildSticker
from .threads import Thread, ThreadMember
from .user import User
from .utils import _D, _FETCHABLE
from .utils import _D, _FETCHABLE, warn_deprecated
from .welcome_screen import WelcomeScreen, WelcomeScreenChannel
from .widget import Widget

Expand Down Expand Up @@ -4276,37 +4286,82 @@ def get_scheduled_event(self, event_id: int, /) -> ScheduledEvent | None:
"""
return self._scheduled_events.get(event_id)

@overload
async def create_scheduled_event(
self,
*,
name: str,
description: str = MISSING,
start_time: datetime.datetime,
end_time: datetime.datetime = MISSING,
location: str | int | VoiceChannel | StageChannel | ScheduledEventLocation,
location: (
str | int | VoiceChannel | StageChannel | ScheduledEventLocation
) = MISSING,
reason: str | None = None,
image: bytes = MISSING,
) -> ScheduledEvent | None: ...

@overload
async def create_scheduled_event(
self,
*,
name: str,
description: str = MISSING,
scheduled_start_time: datetime.datetime,
scheduled_end_time: datetime.datetime = MISSING,
entity_type: ScheduledEventEntityType = MISSING,
entity_metadata: ScheduledEventEntityMetadata | None = MISSING,
channel_id: int = MISSING,
privacy_level: ScheduledEventPrivacyLevel = ScheduledEventPrivacyLevel.guild_only,
reason: str | None = None,
image: bytes = MISSING,
) -> ScheduledEvent | None: ...
async def create_scheduled_event(
self,
*,
name: str,
description: str = MISSING,
scheduled_start_time: datetime.datetime,
scheduled_end_time: datetime.datetime = MISSING,
location: (
str | int | VoiceChannel | StageChannel | ScheduledEventLocation
) = MISSING,
entity_type: ScheduledEventEntityType = MISSING,
entity_metadata: ScheduledEventEntityMetadata | None = MISSING,
channel_id: int = MISSING,
privacy_level: ScheduledEventPrivacyLevel = ScheduledEventPrivacyLevel.guild_only,
reason: str | None = None,
image: bytes = MISSING,
start_time: datetime.datetime = MISSING,
end_time: datetime.datetime = MISSING,
) -> ScheduledEvent | None:
"""|coro|
Creates a scheduled event.

For EXTERNAL events, ``entity_metadata`` with a location and ``end_time`` are required.
For STAGE_INSTANCE or VOICE events, ``channel_id`` is required.

Parameters
----------
name: :class:`str`
The name of the scheduled event.
description: Optional[:class:`str`]
The description of the scheduled event.
start_time: :class:`datetime.datetime`
scheduled_start_time: :class:`datetime.datetime`
A datetime object of when the scheduled event is supposed to start.
end_time: Optional[:class:`datetime.datetime`]
scheduled_end_time: Optional[:class:`datetime.datetime`]
A datetime object of when the scheduled event is supposed to end.
location: :class:`ScheduledEventLocation`
The location of where the event is happening.
Required for EXTERNAL events.
entity_type: :class:`ScheduledEventEntityType`
The type of scheduled event (STAGE_INSTANCE, VOICE, or EXTERNAL).
entity_metadata: Optional[:class:`ScheduledEventEntityMetadata`]
The entity metadata (required for EXTERNAL events with a location).
channel_id: Optional[Union[:class:`int`, :class:`VoiceChannel`, :class:`StageChannel`]]
The channel ID for STAGE_INSTANCE or VOICE events.
Can be a channel object or a snowflake ID.
privacy_level: :class:`ScheduledEventPrivacyLevel`
The privacy level of the event. Currently, the only possible value
is :attr:`ScheduledEventPrivacyLevel.guild_only`, which is default,
so there is no need to change this parameter.
is :attr:`ScheduledEventPrivacyLevel.guild_only`, which is default.
reason: Optional[:class:`str`]
The reason to show in the audit log.
image: Optional[:class:`bytes`]
Expand All @@ -4323,34 +4378,74 @@ async def create_scheduled_event(
You do not have the Manage Events permission.
HTTPException
The operation failed.
ValidationError
Invalid parameters for the event type.
"""
payload: dict[str, str | int] = {
"name": name,
"scheduled_start_time": start_time.isoformat(),
"scheduled_start_time": scheduled_start_time.isoformat(),
"privacy_level": int(privacy_level),
"entity_type": int(entity_type),
}
if location is MISSING and entity_type is MISSING:
raise TypeError("Either location or entity_type must be provided.")
if start_time is MISSING and scheduled_start_time is MISSING:
raise TypeError(
"Either start_time or scheduled_start_time must be provided."
)
if start_time is not MISSING:
warn_deprecated("start_time", "scheduled_start_time", "2.7")
if scheduled_start_time is MISSING:
scheduled_start_time = start_time

if not isinstance(location, ScheduledEventLocation):
location = ScheduledEventLocation(state=self._state, value=location)

payload["entity_type"] = location.type.value

if location.type == ScheduledEventLocationType.external:
payload["channel_id"] = None
payload["entity_metadata"] = {"location": location.value}
else:
payload["channel_id"] = location.value.id
payload["entity_metadata"] = None
if end_time is not MISSING:
warn_deprecated("end_time", "scheduled_end_time", "2.7")
if scheduled_end_time is MISSING:
scheduled_end_time = end_time

if location is not MISSING:
warn_deprecated("location", "entity_metadata", "2.7", "3.0")
if entity_metadata is MISSING:
if not isinstance(location, (ScheduledEventLocation)):
location = ScheduledEventLocation(state=self._state, value=location)
if entity_type is MISSING:
entity_type = location.type
if location.type == ScheduledEventEntityType.external:
entity_metadata = ScheduledEventEntityMetadata(str(location))

if scheduled_end_time is not MISSING:
payload["scheduled_end_time"] = scheduled_end_time.isoformat()

if description is not MISSING:
payload["description"] = description

if end_time is not MISSING:
payload["scheduled_end_time"] = end_time.isoformat()

if image is not MISSING:
payload["image"] = utils._bytes_to_base64_data(image)

if entity_type == ScheduledEventEntityType.external:
if entity_metadata is MISSING or entity_metadata is None:
raise ValidationError(
"entity_metadata with a location is required for EXTERNAL events."
)
if not entity_metadata.location:
raise ValidationError(
"entity_metadata.location cannot be empty for EXTERNAL events."
)
if scheduled_end_time is MISSING:
raise ValidationError(
"scheduled_end_time is required for EXTERNAL events."
)

payload["channel_id"] = None
payload["entity_metadata"] = entity_metadata.to_payload()
else:
if channel_id is MISSING:
raise ValidationError(
"channel_id is required for STAGE_INSTANCE and VOICE events."
)

payload["channel_id"] = channel_id

data = await self._state.http.create_scheduled_event(
guild_id=self.id, reason=reason, **payload
)
Expand Down
Loading