Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/1295.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added support for recurrence rules to ``GuildScheduledEvent`` via the ``recurrence_rule`` parameter.
56 changes: 56 additions & 0 deletions disnake/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@
"GuildScheduledEventEntityType",
"GuildScheduledEventStatus",
"GuildScheduledEventPrivacyLevel",
"GuildScheduledEventFrequency",
"GuildScheduledEventWeekday",
"GuildScheduledEventMonth",
"ThreadArchiveDuration",
"WidgetStyle",
"Locale",
Expand Down Expand Up @@ -1525,6 +1528,59 @@ class GuildScheduledEventStatus(Enum):
"""


class GuildScheduledEventFrequency(Enum):
"""Represents the frequency of recurrence for a scheduled event.

This determines how often the event should repeat, such as daily, weekly, monthly, or yearly.

.. versionadded:: 2.11
"""

YEARLY = 0
MONTHLY = 1
WEEKLY = 2
DAILY = 3


class GuildScheduledEventWeekday(Enum):
"""Represents the day of the week used in recurrence rules.

Used for specifying which days an event should recur on.

.. versionadded:: 2.11
"""

MONDAY = 0
TUESDAY = 1
WEDNESDAY = 2
THURSDAY = 3
FRIDAY = 4
SATURDAY = 5
SUNDAY = 6


class GuildScheduledEventMonth(Enum):
"""Represents the month of the year used in recurrence rules.

Used for specifying which months an event should recur on.

.. versionadded:: 2.11
"""

JANUARY = 1
FEBRUARY = 2
MARCH = 3
APRIL = 4
MAY = 5
JUNE = 6
JULY = 7
AUGUST = 8
SEPTEMBER = 9
OCTOBER = 10
NOVEMBER = 11
DECEMBER = 12


class GuildScheduledEventPrivacyLevel(Enum):
"""Represents the privacy level of a guild scheduled event.

Expand Down
125 changes: 123 additions & 2 deletions disnake/guild.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
ChannelType,
ContentFilter,
GuildScheduledEventEntityType,
GuildScheduledEventFrequency,
GuildScheduledEventPrivacyLevel,
Locale,
NotificationLevel,
Expand All @@ -63,7 +64,11 @@
from .errors import ClientException, HTTPException, InvalidData
from .file import File
from .flags import SystemChannelFlags
from .guild_scheduled_event import GuildScheduledEvent, GuildScheduledEventMetadata
from .guild_scheduled_event import (
GuildScheduledEvent,
GuildScheduledEventMetadata,
GuildScheduledEventRecurrenceRule,
)
from .integrations import Integration, _integration_factory
from .invite import Invite
from .iterators import AuditLogIterator, BanIterator, MemberIterator
Expand Down Expand Up @@ -2659,6 +2664,7 @@
privacy_level: GuildScheduledEventPrivacyLevel = ...,
description: str = ...,
image: AssetBytes = ...,
recurrence_rule: Optional[GuildScheduledEventRecurrenceRule] = ...,
reason: Optional[str] = ...,
) -> GuildScheduledEvent: ...

Expand All @@ -2677,6 +2683,7 @@
privacy_level: GuildScheduledEventPrivacyLevel = ...,
description: str = ...,
image: AssetBytes = ...,
recurrence_rule: Optional[GuildScheduledEventRecurrenceRule] = ...,
reason: Optional[str] = ...,
) -> GuildScheduledEvent: ...

Expand All @@ -2693,6 +2700,7 @@
privacy_level: GuildScheduledEventPrivacyLevel = ...,
description: str = ...,
image: AssetBytes = ...,
recurrence_rule: Optional[GuildScheduledEventRecurrenceRule] = ...,
reason: Optional[str] = ...,
) -> GuildScheduledEvent: ...

Expand All @@ -2708,6 +2716,7 @@
entity_metadata: GuildScheduledEventMetadata = MISSING,
description: str = MISSING,
image: AssetBytes = MISSING,
recurrence_rule: Optional[GuildScheduledEventRecurrenceRule] = MISSING,
reason: Optional[str] = None,
) -> GuildScheduledEvent:
"""|coro|
Expand All @@ -2729,6 +2738,32 @@
``None``, :attr:`~GuildScheduledEventEntityType.external` (set automatically), required, required
unset, :attr:`~GuildScheduledEventEntityType.external`, required, required

Based on the recurrence frequency, there are different restrictions regarding other parameter values, as shown below:

.. csv-table::
:header: "``Frequency``", "``A`llowed Fields``", "``Notes``"
:widths: 15, 35, 50

"Daily (3)", "by_weekday", "Must match one of the approved weekday sets (e.g. Mon-Fri, Sat-Sun)"
"Weekly (2)", "by_weekday", "Only one weekday allowed; interval=2 is allowed for biweekly"
"Monty (1)", "by_n_weekday", "Only one n-th weekday allowed (e.g. 2nd Tuesday)"
"Yearly (0)", "by_month + by_month_day", "Must both be provided; each must contain exactly one value"

Additional constraints:

- ``by_weekday`` and ``by_n_weekday`` cannot both be set.
- ``by_month`` and ``by_month_day`` cannot both be set.
- ``interval`` must be 1, except for WEEKLY frequency where it may be 2.

Valid weekday sets for Daily frequency:

- Monday - Friday: [0, 1, 2, 3, 4]
- Tuesday - Saturday: [1, 2, 3, 4, 5]
- Sunday - Thursday: [6, 0, 1, 2, 3]
- Friday & Saturday: [4, 5]
- Saturday & Sunday: [5, 6]
- Sunday & Monday: [6, 0]

.. versionadded:: 2.3

.. versionchanged:: 2.6
Expand All @@ -2745,6 +2780,10 @@
.. versionchanged:: 2.6
Infer ``entity_type`` from channel if provided.

.. versionchanged:: 2.11
Added the ``recurrence_rule`` parameter to support recurring events.


Parameters
----------
name: :class:`str`
Expand Down Expand Up @@ -2775,8 +2814,11 @@
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: :class:`GuildScheduledEventMetadata`
entity_metadata: Optional[:class:`GuildScheduledEventMetadata`]
The entity metadata of the guild scheduled event.
recurrence_rule: :class:`GuildScheduledEventRecurrenceRule`
An optional recurrence rule that specifies how the event should repeat over time.
This allows for recurring scheduled events such as weekly meetings or monthly check-ins.
reason: Optional[:class:`str`]
The reason for creating the guild scheduled event. Shows up on the audit log.

Expand Down Expand Up @@ -2836,6 +2878,85 @@

fields["entity_metadata"] = entity_metadata.to_dict()

if recurrence_rule is not MISSING:
if not isinstance(recurrence_rule, GuildScheduledEventRecurrenceRule):
raise TypeError(
"recurrence_rule must be an instance of GuildScheduledEventRecurrenceRule"
)

# Mutually exclusive: by_weekday vs by_n_weekday
if recurrence_rule.by_weekday and recurrence_rule.by_n_weekday:
raise ValueError("'by_weekday' and 'by_n_weekday' cannot both be set.")

Check failure on line 2889 in disnake/guild.py

View workflow job for this annotation

GitHub Actions / lint

Ruff (EM101)

disnake/guild.py:2889:34: EM101 Exception must not use a string literal, assign to variable first

# Mutually exclusive: by_month + by_month_day
if recurrence_rule.by_month and recurrence_rule.by_month_day:
raise ValueError("'by_month' and 'by_month_day' cannot both be set.")

Check failure on line 2893 in disnake/guild.py

View workflow job for this annotation

GitHub Actions / lint

Ruff (EM101)

disnake/guild.py:2893:34: EM101 Exception must not use a string literal, assign to variable first

# by_weekday restrictions
if recurrence_rule.by_weekday:
if recurrence_rule.frequency not in (
GuildScheduledEventFrequency.DAILY,
GuildScheduledEventFrequency.WEEKLY,
):
raise ValueError("'by_weekday' is only valid for DAILY or WEEKLY frequencies.")

Check failure on line 2901 in disnake/guild.py

View workflow job for this annotation

GitHub Actions / lint

Ruff (EM101)

disnake/guild.py:2901:38: EM101 Exception must not use a string literal, assign to variable first

# DAILY: must be one of the allowed sets
if recurrence_rule.frequency == GuildScheduledEventFrequency.DAILY:
allowed_sets = [
{0, 1, 2, 3, 4}, # Mon-Fri
{1, 2, 3, 4, 5}, # Tue-Sat
{6, 0, 1, 2, 3}, # Sun-Thu
{4, 5}, # Fri-Sat
{5, 6}, # Sat-Sun
{6, 0}, # Sun-Mon
]
current_set = {d.value for d in recurrence_rule.by_weekday}
if current_set not in allowed_sets:
raise ValueError(
"Invalid 'by_weekday' set for DAILY recurrence. "
"Must be one of the known allowed weekday sets."

Check failure on line 2917 in disnake/guild.py

View workflow job for this annotation

GitHub Actions / lint

Ruff (EM101)

disnake/guild.py:2916:29: EM101 Exception must not use a string literal, assign to variable first
)

# WEEKLY: can only have 1 weekday
elif recurrence_rule.frequency == GuildScheduledEventFrequency.WEEKLY:
if len(recurrence_rule.by_weekday) != 1:
raise ValueError(
"'by_weekday' must contain exactly one day for WEEKLY recurrence."

Check failure on line 2924 in disnake/guild.py

View workflow job for this annotation

GitHub Actions / lint

Ruff (EM101)

disnake/guild.py:2924:29: EM101 Exception must not use a string literal, assign to variable first
)

# by_n_weekday restriction: only for MONTHLY and only 1 entry
if recurrence_rule.by_n_weekday:
if recurrence_rule.frequency != GuildScheduledEventFrequency.MONTHLY:
raise ValueError("'by_n_weekday' is only valid for MONTHLY recurrence.")

Check failure on line 2930 in disnake/guild.py

View workflow job for this annotation

GitHub Actions / lint

Ruff (EM101)

disnake/guild.py:2930:38: EM101 Exception must not use a string literal, assign to variable first
if len(recurrence_rule.by_n_weekday) != 1:
raise ValueError("'by_n_weekday' must contain exactly one entry.")

Check failure on line 2932 in disnake/guild.py

View workflow job for this annotation

GitHub Actions / lint

Ruff (EM101)

disnake/guild.py:2932:38: EM101 Exception must not use a string literal, assign to variable first

# by_month + by_month_day restriction: only for YEARLY and length 1 each
if recurrence_rule.by_month or recurrence_rule.by_month_day:
if recurrence_rule.frequency != GuildScheduledEventFrequency.YEARLY:
raise ValueError(
"'by_month' and 'by_month_day' are only valid for YEARLY recurrence."

Check failure on line 2938 in disnake/guild.py

View workflow job for this annotation

GitHub Actions / lint

Ruff (EM101)

disnake/guild.py:2938:25: EM101 Exception must not use a string literal, assign to variable first
)
if not recurrence_rule.by_month or not recurrence_rule.by_month_day:
raise ValueError(
"Both 'by_month' and 'by_month_day' must be provided together."

Check failure on line 2942 in disnake/guild.py

View workflow job for this annotation

GitHub Actions / lint

Ruff (EM101)

disnake/guild.py:2942:25: EM101 Exception must not use a string literal, assign to variable first
)
if len(recurrence_rule.by_month) != 1 or len(recurrence_rule.by_month_day) != 1:
raise ValueError(
"'by_month' and 'by_month_day' must each contain exactly one value."

Check failure on line 2946 in disnake/guild.py

View workflow job for this annotation

GitHub Actions / lint

Ruff (EM101)

disnake/guild.py:2946:25: EM101 Exception must not use a string literal, assign to variable first
)

# interval restriction
if recurrence_rule.interval != 1:
if recurrence_rule.frequency != GuildScheduledEventFrequency.WEEKLY:
raise ValueError(
"Only WEEKLY recurrence can have interval values other than 1."
)
if recurrence_rule.interval != 2:
raise ValueError("For WEEKLY recurrence, interval can only be 1 or 2.")

fields["recurrence_rule"] = recurrence_rule.to_dict()

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

Expand Down
Loading
Loading