|
@ -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): |
|
|