diff --git a/changelog/1295.feature.rst b/changelog/1295.feature.rst new file mode 100644 index 0000000000..0007255865 --- /dev/null +++ b/changelog/1295.feature.rst @@ -0,0 +1 @@ +Added support for recurrence rules to ``GuildScheduledEvent`` via the ``recurrence_rule`` parameter. diff --git a/disnake/enums.py b/disnake/enums.py index fb6dd6cbe4..d417ee44db 100644 --- a/disnake/enums.py +++ b/disnake/enums.py @@ -60,6 +60,9 @@ "GuildScheduledEventEntityType", "GuildScheduledEventStatus", "GuildScheduledEventPrivacyLevel", + "GuildScheduledEventFrequency", + "GuildScheduledEventWeekday", + "GuildScheduledEventMonth", "ThreadArchiveDuration", "WidgetStyle", "Locale", @@ -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. diff --git a/disnake/guild.py b/disnake/guild.py index fcc01e3d01..58a23c17d1 100644 --- a/disnake/guild.py +++ b/disnake/guild.py @@ -48,6 +48,7 @@ ChannelType, ContentFilter, GuildScheduledEventEntityType, + GuildScheduledEventFrequency, GuildScheduledEventPrivacyLevel, Locale, NotificationLevel, @@ -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 @@ -2659,6 +2664,7 @@ async def create_scheduled_event( privacy_level: GuildScheduledEventPrivacyLevel = ..., description: str = ..., image: AssetBytes = ..., + recurrence_rule: Optional[GuildScheduledEventRecurrenceRule] = ..., reason: Optional[str] = ..., ) -> GuildScheduledEvent: ... @@ -2677,6 +2683,7 @@ async def create_scheduled_event( privacy_level: GuildScheduledEventPrivacyLevel = ..., description: str = ..., image: AssetBytes = ..., + recurrence_rule: Optional[GuildScheduledEventRecurrenceRule] = ..., reason: Optional[str] = ..., ) -> GuildScheduledEvent: ... @@ -2693,6 +2700,7 @@ async def create_scheduled_event( privacy_level: GuildScheduledEventPrivacyLevel = ..., description: str = ..., image: AssetBytes = ..., + recurrence_rule: Optional[GuildScheduledEventRecurrenceRule] = ..., reason: Optional[str] = ..., ) -> GuildScheduledEvent: ... @@ -2708,6 +2716,7 @@ async def create_scheduled_event( entity_metadata: GuildScheduledEventMetadata = MISSING, description: str = MISSING, image: AssetBytes = MISSING, + recurrence_rule: Optional[GuildScheduledEventRecurrenceRule] = MISSING, reason: Optional[str] = None, ) -> GuildScheduledEvent: """|coro| @@ -2729,6 +2738,32 @@ async def create_scheduled_event( ``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 @@ -2745,6 +2780,10 @@ async def create_scheduled_event( .. 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` @@ -2775,8 +2814,11 @@ async def create_scheduled_event( 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. @@ -2836,6 +2878,85 @@ async def create_scheduled_event( 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.") + + # 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.") + + # 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.") + + # 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." + ) + + # 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." + ) + + # 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.") + if len(recurrence_rule.by_n_weekday) != 1: + raise ValueError("'by_n_weekday' must contain exactly one entry.") + + # 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." + ) + 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." + ) + 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." + ) + + # 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 diff --git a/disnake/guild_scheduled_event.py b/disnake/guild_scheduled_event.py index 0d93bf1e12..6b13dcf4d2 100644 --- a/disnake/guild_scheduled_event.py +++ b/disnake/guild_scheduled_event.py @@ -3,14 +3,19 @@ from __future__ import annotations from datetime import datetime -from typing import TYPE_CHECKING, Any, Dict, Literal, Optional, overload +from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, overload + +from disnake import utils from .asset import Asset from .enums import ( ChannelType, GuildScheduledEventEntityType, + GuildScheduledEventFrequency, + GuildScheduledEventMonth, GuildScheduledEventPrivacyLevel, GuildScheduledEventStatus, + GuildScheduledEventWeekday, try_enum, ) from .mixins import Hashable @@ -33,11 +38,48 @@ from .types.guild_scheduled_event import ( GuildScheduledEvent as GuildScheduledEventPayload, GuildScheduledEventEntityMetadata as GuildScheduledEventEntityMetadataPayload, + GuildScheduledEventNWeekday as GuildScheduledEventNWeekdayPayload, + GuildScheduledEventRecurrenceRule as GuildScheduledEventRecurrenceRulePayload, ) from .user import User +__all__ = ( + "GuildScheduledEventMetadata", + "GuildScheduledEvent", + "GuildScheduledEventRecurrenceRule", + "GuildScheduledEventNWeekday", +) + + +class GuildScheduledEventNWeekday: + """Represents a specific weekday occurrence within a month for recurrence rules. + + .. versionadded:: 2.11 + + Attributes + ---------- + n: :class:`int` + The week number (1-5) that the event should occur on. + + day: :class:`GuildScheduledEventWeekday` + The day of the week (e.g. :attr:`GuildScheduledEventWeekday.TUESDAY`) the event should occur on. + """ + + __slots__ = ("n", "day") + + def __init__(self, *, n: int, day: GuildScheduledEventWeekday) -> None: + self.n = n + self.day = day + + def __repr__(self) -> str: + return f"" + + def to_dict(self) -> GuildScheduledEventNWeekdayPayload: + return {"n": self.n, "day": self.day.value} -__all__ = ("GuildScheduledEventMetadata", "GuildScheduledEvent") + @classmethod + def from_dict(cls, data: GuildScheduledEventNWeekdayPayload) -> GuildScheduledEventNWeekday: + return cls(n=data["n"], day=GuildScheduledEventWeekday(data["day"])) class GuildScheduledEventMetadata: @@ -72,6 +114,157 @@ def from_dict( return GuildScheduledEventMetadata(location=data.get("location")) +class GuildScheduledEventRecurrenceRule: + """Represents the recurrence rule payload used when creating a scheduled event. + + This structure defines how and when a scheduled event repeats. + + Certain combinations and values are restricted by the Discord client. + See the `official Discord documentation on recurrence rules `__ + for detailed constraints and valid combinations. + + .. versionadded:: 2.11 + + Attributes + ---------- + start: :class:`str` + An ISO8601 timestamp representing the start time of the recurrence interval. + + frequency: :class:`GuildScheduledEventFrequency` + How often the event repeats. + + interval: :class:`int` + The spacing between each occurrence. For example, a value of ``2`` with a + weekly frequency would mean "every other week". + + Valid only when ``frequency`` is :attr:`GuildScheduledEventFrequency.WEEKLY` if greater than ``1``. + + by_weekday: Optional[List[:class:`GuildScheduledEventWeekday`]] + A list of :class:`GuildScheduledEventWeekday` values indicating the weekdays the event repeats on. + + Valid only when ``frequency`` is :attr:`GuildScheduledEventFrequency.DAILY` or + :attr:`GuildScheduledEventFrequency.WEEKLY`. + + - For ``DAILY`` frequency: Must match one of the allowed weekday sets. + - For ``WEEKLY`` frequency: May only contain one entry. + + by_n_weekday: Optional[List[:class:`GuildScheduledEventNWeekday`]] + A list of weekday-within-week combinations, such as "2nd Tuesday". + + Each item represents a specific week (1-5) and a weekday (0 = Monday, ..., 6 = Sunday), + indicating a recurring event like "third Friday of every month". + + - Valid only when ``frequency`` is :attr:`GuildScheduledEventFrequency.MONTHLY`. + - Must contain exactly one item. + + by_month: Optional[List[:class:`GuildScheduledEventMonth`]] + A list of month values, represented by :class:`GuildScheduledEventMonth`. + + - Valid only when ``frequency`` is :attr:`GuildScheduledEventFrequency.YEARLY`. + - Must be used together with ``by_month_day``. + - Must contain exactly one item. + + by_month_day: Optional[List[:class:`int`]] + A list of days in the month (1-31). + + - Valid only when ``frequency`` is :attr:`GuildScheduledEventFrequency.YEARLY`. + - Must be used together with ``by_month``. + - Must contain exactly one item. + """ + + __slots__ = ( + "start", + "frequency", + "interval", + "by_weekday", + "by_n_weekday", + "by_month", + "by_month_day", + ) + + def __repr__(self) -> str: + return ( + f"" + ) + + def __init__( + self, + *, + start: datetime, + frequency: GuildScheduledEventFrequency, + interval: int = 1, + by_weekday: Optional[List[GuildScheduledEventWeekday]] = None, + by_n_weekday: Optional[List[GuildScheduledEventNWeekday]] = None, + by_month: Optional[List[GuildScheduledEventMonth]] = None, + by_month_day: Optional[List[int]] = None, + ) -> None: + self.start = start + self.frequency = frequency + self.interval = interval + self.by_weekday = by_weekday + self.by_n_weekday = by_n_weekday + self.by_month = by_month + self.by_month_day = by_month_day + + def to_dict(self) -> GuildScheduledEventRecurrenceRulePayload: + data: GuildScheduledEventRecurrenceRulePayload = { + "start": utils.isoformat_utc(self.start), + "frequency": self.frequency.value, + "interval": self.interval, + } + + if self.by_weekday: + data["by_weekday"] = [d.value for d in self.by_weekday] + + if self.by_n_weekday: + data["by_n_weekday"] = [{"n": n.n, "day": n.day.value} for n in self.by_n_weekday] + + if self.by_month: + data["by_month"] = [m.value for m in self.by_month] + + if self.by_month_day: + data["by_month_day"] = self.by_month_day + + return data + + @classmethod + def from_dict( + cls, data: GuildScheduledEventRecurrenceRulePayload + ) -> GuildScheduledEventRecurrenceRule: + return cls( + start=datetime.fromisoformat(data.get("start")), + frequency=GuildScheduledEventFrequency(data.get("frequency")), + interval=data.get("interval", 1), + by_weekday=( + [GuildScheduledEventWeekday(d) for d in data.get("by_weekday", [])] + if data.get("by_weekday") is not None + else None + ), + by_n_weekday=( + [ + GuildScheduledEventNWeekday( + n=nd.get("n"), + day=GuildScheduledEventWeekday(nd.get("day")), + ) + for nd in data.get("by_n_weekday", []) + ] + if data.get("by_n_weekday") is not None + else None + ), + by_month=( + [GuildScheduledEventMonth(m) for m in data.get("by_month", [])] + if data.get("by_month") is not None + else None + ), + by_month_day=data.get("by_month_day"), + ) + + class GuildScheduledEvent(Hashable): """Represents a guild scheduled event. @@ -124,6 +317,9 @@ class GuildScheduledEvent(Hashable): The ID of an entity associated with the guild scheduled event. entity_metadata: :class:`GuildScheduledEventMetadata` Additional metadata for 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. user_count: Optional[:class:`int`] The number of users subscribed to the guild scheduled event. If the guild scheduled event was fetched with ``with_user_count`` set to ``False``, this field is ``None``. @@ -135,6 +331,7 @@ class GuildScheduledEvent(Hashable): "guild_id", "channel_id", "creator_id", + "creator", "name", "description", "scheduled_start_time", @@ -144,7 +341,7 @@ class GuildScheduledEvent(Hashable): "entity_type", "entity_id", "entity_metadata", - "creator", + "recurrence_rule", "user_count", "_image", "_cs_guild", @@ -178,6 +375,13 @@ def _update(self, data: GuildScheduledEventPayload) -> None: None if metadata is None else GuildScheduledEventMetadata.from_dict(metadata) ) + recurrence_rule = data.get("recurrence_rule") + self.recurrence_rule: Optional[GuildScheduledEventRecurrenceRule] = ( + None + if recurrence_rule is None + else GuildScheduledEventRecurrenceRule.from_dict(recurrence_rule) + ) + creator_data = data.get("creator") self.creator: Optional[User] if creator_data is not None: @@ -201,6 +405,7 @@ def __repr__(self) -> str: ("status", self.status), ("entity_type", self.entity_type), ("entity_metadata", self.entity_metadata), + ("recurrence_rule", self.recurrence_rule), ("creator", self.creator), ) inner = " ".join(f"{k!s}={v!r}" for k, v in attrs) diff --git a/disnake/http.py b/disnake/http.py index a454b01f11..434ca9d602 100644 --- a/disnake/http.py +++ b/disnake/http.py @@ -2212,6 +2212,7 @@ def create_guild_scheduled_event( entity_type: int, channel_id: Optional[Snowflake] = None, entity_metadata: Optional[Dict[str, Any]] = None, + recurrence_rule: Optional[Dict[str, Any]] = None, scheduled_end_time: Optional[str] = None, description: Optional[str] = None, image: Optional[str] = None, @@ -2240,6 +2241,9 @@ def create_guild_scheduled_event( if image is not None: payload["image"] = image + if recurrence_rule is not None: + payload["recurrence_rule"] = recurrence_rule + return self.request(r, json=payload, reason=reason) def get_guild_scheduled_event( diff --git a/disnake/types/guild_scheduled_event.py b/disnake/types/guild_scheduled_event.py index b770fe9f7e..2f19267873 100644 --- a/disnake/types/guild_scheduled_event.py +++ b/disnake/types/guild_scheduled_event.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: MIT -from typing import Literal, Optional, TypedDict +from typing import List, Literal, Optional, TypedDict from typing_extensions import NotRequired @@ -11,6 +11,7 @@ GuildScheduledEventPrivacyLevel = Literal[2] GuildScheduledEventStatus = Literal[1, 2, 3, 4] GuildScheduledEventEntityType = Literal[1, 2, 3] +GuildScheduledEventFrequency = Literal[0, 1, 2, 3] class GuildScheduledEventUser(TypedDict): @@ -23,6 +24,22 @@ class GuildScheduledEventEntityMetadata(TypedDict, total=False): location: str +class GuildScheduledEventNWeekday(TypedDict): + n: int + day: int # Matches Weekday enum int value (0-6) + + +class GuildScheduledEventRecurrenceRule(TypedDict): + start: str # ISO8601 string + frequency: GuildScheduledEventFrequency # YEARLY, MONTHLY, WEEKLY, DAILY + interval: int + + by_weekday: NotRequired[List[int]] # List of Weekday int values (0-6) + by_n_weekday: NotRequired[List[GuildScheduledEventNWeekday]] + by_month: NotRequired[List[int]] # 1-12 + by_month_day: NotRequired[List[int]] # 1-31 + + class GuildScheduledEvent(TypedDict): id: Snowflake guild_id: Snowflake @@ -40,3 +57,4 @@ class GuildScheduledEvent(TypedDict): creator: NotRequired[User] user_count: NotRequired[int] image: NotRequired[Optional[str]] + recurrence_rule: Optional[GuildScheduledEventRecurrenceRule] diff --git a/docs/api/guild_scheduled_events.rst b/docs/api/guild_scheduled_events.rst index f58625ec19..26475fd8dd 100644 --- a/docs/api/guild_scheduled_events.rst +++ b/docs/api/guild_scheduled_events.rst @@ -10,6 +10,14 @@ This section documents everything related to Guild Scheduled Events. Discord Models --------------- +GuildScheduledEventRecurrenceRule +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. attributetable:: GuildScheduledEventRecurrenceRule + +.. autoclass:: GuildScheduledEventRecurrenceRule() + :members: + GuildScheduledEvent ~~~~~~~~~~~~~~~~~~~ @@ -37,6 +45,14 @@ GuildScheduledEventMetadata .. autoclass:: GuildScheduledEventMetadata :members: +GuildScheduledEventNWeekday +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. attributetable:: GuildScheduledEventNWeekday + +.. autoclass:: GuildScheduledEventNWeekday + :members: + Enumerations ------------ @@ -58,6 +74,131 @@ GuildScheduledEventPrivacyLevel .. autoclass:: GuildScheduledEventPrivacyLevel() :members: +GuildScheduledEventFrequency +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. class:: GuildScheduledEventFrequency + + 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 + + .. attribute:: YEARLY + + The event occurs once a year. + + .. attribute:: MONTHLY + + The event occurs once a month. + + .. attribute:: WEEKLY + + The event occurs once a week. + + .. attribute:: DAILY + + The event occurs every day. + +GuildScheduledEventWeekday +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. class:: GuildScheduledEventWeekday + + Represents the day of the week used in recurrence rules. + + Used for specifying which days an event should recur on. + + .. versionadded:: 2.11 + + .. attribute:: MONDAY + + Monday. + + .. attribute:: TUESDAY + + Tuesday. + + .. attribute:: WEDNESDAY + + Wednesday. + + .. attribute:: THURSDAY + + Thursday. + + .. attribute:: FRIDAY + + Friday. + + .. attribute:: SATURDAY + + Saturday. + + .. attribute:: SUNDAY + + Sunday. + +GuildScheduledEventMonth +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. class:: GuildScheduledEventMonth + + Represents the month of the year used in recurrence rules. + + Used for specifying which months an event should recur on. + + .. versionadded:: 2.11 + + .. attribute:: JANUARY + + January. + + .. attribute:: FEBRUARY + + February. + + .. attribute:: MARCH + + March. + + .. attribute:: APRIL + + April. + + .. attribute:: MAY + + May. + + .. attribute:: JUNE + + June. + + .. attribute:: JULY + + July. + + .. attribute:: AUGUST + + August. + + .. attribute:: SEPTEMBER + + September. + + .. attribute:: OCTOBER + + October. + + .. attribute:: NOVEMBER + + November. + + .. attribute:: DECEMBER + + December. + Events ------