Browse Source

First commit on the new recurrence rule objects

pull/9685/head
Developer Anonymous 10 months ago
parent
commit
9afda1cd7e
  1. 272
      discord/scheduled_event.py
  2. 2
      discord/types/scheduled_event.py

272
discord/scheduled_event.py

@ -24,7 +24,8 @@ DEALINGS IN THE SOFTWARE.
from __future__ import annotations from __future__ import annotations
from datetime import datetime import calendar
from datetime import datetime, date
from typing import ( from typing import (
TYPE_CHECKING, TYPE_CHECKING,
AsyncIterator, AsyncIterator,
@ -36,19 +37,25 @@ from typing import (
List, List,
NamedTuple, NamedTuple,
) )
from dateutil import rrule
from .asset import Asset from .asset import Asset
from .enums import EventStatus, EntityType, PrivacyLevel, try_enum from .enums import (
EventStatus,
EntityType,
PrivacyLevel,
try_enum
)
from .mixins import Hashable from .mixins import Hashable
from .object import Object, OLDEST_OBJECT from .object import Object, OLDEST_OBJECT
from .utils import parse_time, _get_as_snowflake, _bytes_to_base64_data, MISSING from .utils import parse_time, _get_as_snowflake, _bytes_to_base64_data, MISSING
if TYPE_CHECKING: if TYPE_CHECKING:
from typing_extensions import Self
from .types.scheduled_event import ( from .types.scheduled_event import (
GuildScheduledEvent as BaseGuildScheduledEventPayload, GuildScheduledEvent as BaseGuildScheduledEventPayload,
GuildScheduledEventWithUserCount as GuildScheduledEventWithUserCountPayload, GuildScheduledEventWithUserCount as GuildScheduledEventWithUserCountPayload,
GuildScheduledEventRecurrence as GuildScheduledEventRecurrencePayload,
GuildScheduledEventExceptionCounts as GuildScheduledEventExceptionCountsPayload,
EntityMetadata, EntityMetadata,
) )
@ -66,139 +73,202 @@ __all__ = (
) )
# fmt: on # fmt: on
class NWeekdays(NamedTuple):
n: int class _NWeekday(NamedTuple):
day: int week: Literal[1, 2, 3, 4, 5] # "n" for the API
day: rrule.weekday
class ScheduledEventRecurrenceRule: class ScheduledEventRecurrenceRule:
"""Represents a scheduled event recurrence rule. """The recurrence rule for a scheduled event.
.. versionadded:: 2.4 This follows :class:`dateutil.rrule.rrule` structure.
Parameters Parameters
---------- ----------
start: :class:`datetime.datetime` start: :class:`datetime.datetime`
An aware datetime object representing the recurrence start time. The datetime when the recurrence interval starts.
months: List[:class:`int`] frequency: :class:`int`
The months this event will be repeated on. How often the event occurs.
month_days: List[:class:`int`]
The month days this event will be repeated on. This can be one of :attr:`dateutil.rrule.YEARLY`, :attr:`dateutil.rrule.MONTHLY`,
weekdays: List[:class:`int`] :attr:`dateutil.rrule.WEEKLY`, or :attr:`dateutil.rrule.DAILY`.
The weekdays this event will be reapeated on.
year_days: List[:class:`int`]
The year days this event will be repeated on.
n_weekdays: List[Tuple[:class:`int`, :class:`int`]]
A ``(day, month)`` tuple that represents the N day of a month that this event
will be repeated on.
""" """
@overload # As noted in the docs, recurrence rule is implemented by dateutil.rrule so we use
def __init__( # that to have a better control on this.
self,
*,
start: datetime,
months: List[int],
month_days: List[int],
) -> None:
...
@overload
def __init__( def __init__(
self, self,
*, /,
start: datetime, start: datetime,
weekdays: List[int], frequency: Literal[0, 1, 2, 3,],
) -> None: interval: int,
...
@overload
def __init__(
self,
*, *,
start: datetime, weekdays: Optional[List[rrule.weekday]] = MISSING,
year_days: List[int], n_weekdays: Optional[List[_NWeekday]] = MISSING,
month_days: Optional[List[date]] = MISSING,
) -> None: ) -> None:
... self._rule: rrule.rrule = rrule.rrule(
frequency,
start,
interval,
)
self._weekdays = weekdays
self._n_weekdays = n_weekdays
self._month_days = month_days
@overload kwargs = {}
def __init__(
self,
*,
start: datetime,
n_weekdays: List[NWeekdays],
) -> None:
...
def __init__( if weekdays not in (MISSING, None):
self, kwargs.update(byweekday=[wkday.weekday for wkday in weekdays])
*, if n_weekdays not in (MISSING, None):
start: datetime, n_wkno = []
months: List[int] = MISSING, n_wkdays = []
month_days: List[int] = MISSING,
weekdays: List[int] = MISSING, for n_wkday in n_weekdays:
year_days: List[int] = MISSING, n_wkno.append(n_wkday[0])
n_weekdays: List[NWeekdays] = MISSING, n_wkdays.append(n_wkday[1])
) -> None:
if not start.tzinfo: kwargs.update(
raise ValueError( byweekno=n_wkno,
( byweekday=n_wkdays,
"'start' must be an aware datetime. Consider using discord.utils.utcnow()"
" or datetime.datetime.now().astimezone() for local time."
) )
del n_wkno, n_wkdays
if month_days not in (MISSING, None):
month_days_months = []
month_days_days = []
for month_day in month_days:
month_days_months.append(month_day.month)
month_days_days.append(month_day.day)
kwargs.update(
bymonth=month_days_months,
bymonthday=month_days_days,
) )
self.start: datetime = start del month_days_months, month_days_days
self._months: List[int] = months
self._month_days: List[int] = month_days
self._weekdays: List[int] = weekdays
self._year_days: List[int] = year_days
self._n_weekdays: List[NWeekdays] = n_weekdays
@property if kwargs:
def months(self) -> Optional[List[int]]: self._rule = self._rule.replace(**kwargs)
"""Optional[List[:class:`int`]]: The list of months this event will be repeated
on, or ``None``.
"""
return self._months if self._months is not MISSING else None
@property @property
def month_days(self) -> Optional[List[int]]: def weekdays(self) -> Optional[List[rrule.weekday]]:
"""Optional[List[:class:`int`]]: The list of month days this event will be repeated """Optional[List[:class:`dateutil.rrule.weekday`]]: Returns a read-only list of the weekdays
on, or ``None``. this event recurs on, or ``None``.
""" """
return self._month_days if self._month_days is not MISSING else None if self._weekdays in (MISSING, None):
return None
return self._weekdays.copy()
@weekdays.setter
def weekdays(self, new: Optional[List[rrule.weekday]]) -> None:
self.replace(weekdays=new)
@property @property
def weekdays(self) -> Optional[List[int]]: def n_weekdays(self) -> Optional[List[_NWeekday]]:
"""Optional[List[:class:`int`]]: The list of weekdays this event will be repeated """Optional[List[Tuple[:class:`int`, :class:`dateutil.rrule.weekday`]]]: Returns a read-only
on, or ``None``. list of the N weekdays this event recurs on, or ``None``.
""" """
return self._weekdays if self._weekdays is not MISSING else None if self._n_weekdays in (MISSING, None):
return None
return self._n_weekdays.copy()
@n_weekdays.setter
def n_weekdays(self, new: Optional[List[_NWeekday]]) -> None:
self.replace(n_weekdays=new)
@property @property
def year_days(self) -> Optional[List[int]]: def month_days(self) -> Optional[List[date]]:
"""Optional[List[:class:`int`]]: The list of year days this event will be repeated """Optional[List[:class:`datetime.date`]]: Returns a read-only list of the month days this
on, or ``None``. event recurs on, or ``None``.
""" """
return self._year_days if self._year_days is not MISSING else None if self._month_days in (MISSING, None):
return None
return self._month_days.copy()
@property @month_days.setter
def n_weekdays(self) -> Optional[List[NWeekdays]]: def month_days(self, new: Optional[List[date]]) -> None:
"""Optional[List[Tuple[:class:`int`, :class:`int`]]]: The ``(day, month)`` pairs self.replace(month_days=new)
this event will be repeated on, or ``None``.
def replace(
self,
*,
weekdays: Optional[List[rrule.weekday]] = MISSING,
n_weekdays: Optional[List[_NWeekday]] = MISSING,
month_days: Optional[List[date]] = MISSING,
) -> Self:
"""Replaces and returns the recurrence rule with the same values except for the
ones that are changed.
This is similar to :meth:`dateutil.rrule.rrule.replace`.
Parameters
----------
weekdays: Optional[List[:class:`dateutil.rrule.weekday`]]
The new weekdays for the event to recur on.
n_weekdays: Optional[List[Tuple[:class:`int`, :class:`dateutil.rrule.weekday`]]]
The new set of specific days within a week for the event to recur on.
month_days: Optional[List[:class:`datetime.date`]]
The new set of month and month days for the event to recur on.
.. note::
:attr:`datetime.date.year` attribute is ignored when updating the recurrence
rule.
Returns
-------
:class:`ScheduledEventRecurrenceRule`
The recurrence rule with the replaced values.
""" """
return self._n_weekdays if self._n_weekdays is not MISSING else None
def edit(self): kwargs = {}
... # TODO: finish this thingy
if weekdays is not MISSING:
if weekdays is None:
kwargs.update(byweekday=None)
else:
kwargs.update(byweekday=[wkday.weekday for wkday in weekdays])
if n_weekdays is not MISSING:
if n_weekdays is None:
kwargs.update(byweekno=None, byweekday=None)
else:
n_wkno = []
n_wkdays = []
for n_wkday in n_weekdays:
n_wkno.append(n_wkday[0])
n_wkdays.append(n_wkdays[1])
kwargs.update(byweekno=n_wkno, byweekday=n_wkdays)
del n_wkno, n_wkdays
if month_days is not MISSING:
if month_days is None:
kwargs.update(bymonth=None, bymonthday=None)
else:
month_days_months = []
month_days_days = []
@classmethod for month_day in month_days:
def from_dict(cls, data: GuildScheduledEventRecurrencePayload, /) -> ScheduledEventRecurrenceRule: month_days_months.append(month_day.month)
... # TODO: finish this ALSO month_days_days.append(month_day.day)
kwargs.update(bymonth=month_days_months, bymonthday=month_days_days)
del month_days_months, month_days_days
if not kwargs:
raise ValueError(
'You must provide at least one value to replace on the recurrence rule'
)
def to_dict(self) -> GuildScheduledEventRecurrencePayload: self._rule = self._rule.replace(**kwargs)
... # TODO: guessed it, finish this also return self
class ScheduledEvent(Hashable): class ScheduledEvent(Hashable):

2
discord/types/scheduled_event.py

@ -38,7 +38,7 @@ class _NWeekday(TypedDict):
day: Literal[0, 1, 2, 3, 4, 5, 6] day: Literal[0, 1, 2, 3, 4, 5, 6]
class ScheduledEventRecurrenceRule(TypedDict): class GuildScheduledEventRecurrenceRule(TypedDict):
start: str start: str
interval: int interval: int
frequency: int frequency: int

Loading…
Cancel
Save