Browse Source

Base commit

pull/9685/head
Developer Anonymous 1 year ago
parent
commit
41706ddb27
  1. 13
      discord/guild.py
  2. 23
      discord/http.py
  3. 269
      discord/scheduled_event.py
  4. 29
      discord/types/scheduled_event.py

13
discord/guild.py

@ -83,7 +83,7 @@ from .widget import Widget
from .asset import Asset from .asset import Asset
from .flags import SystemChannelFlags from .flags import SystemChannelFlags
from .integrations import Integration, PartialIntegration, _integration_factory from .integrations import Integration, PartialIntegration, _integration_factory
from .scheduled_event import ScheduledEvent from .scheduled_event import ScheduledEvent, ScheduledEventRecurrence
from .stage_instance import StageInstance from .stage_instance import StageInstance
from .threads import Thread, ThreadMember from .threads import Thread, ThreadMember
from .sticker import GuildSticker from .sticker import GuildSticker
@ -3003,6 +3003,7 @@ class Guild(Hashable):
description: str = ..., description: str = ...,
image: bytes = ..., image: bytes = ...,
reason: Optional[str] = ..., reason: Optional[str] = ...,
recurrence: Optional[ScheduledEventRecurrence] = ...,
) -> ScheduledEvent: ) -> ScheduledEvent:
... ...
@ -3019,6 +3020,7 @@ class Guild(Hashable):
description: str = ..., description: str = ...,
image: bytes = ..., image: bytes = ...,
reason: Optional[str] = ..., reason: Optional[str] = ...,
recurrence: Optional[ScheduledEventRecurrence] = ...,
) -> ScheduledEvent: ) -> ScheduledEvent:
... ...
@ -3034,6 +3036,7 @@ class Guild(Hashable):
description: str = ..., description: str = ...,
image: bytes = ..., image: bytes = ...,
reason: Optional[str] = ..., reason: Optional[str] = ...,
recurrence: Optional[ScheduledEventRecurrence] = ...,
) -> ScheduledEvent: ) -> ScheduledEvent:
... ...
@ -3049,6 +3052,7 @@ class Guild(Hashable):
description: str = ..., description: str = ...,
image: bytes = ..., image: bytes = ...,
reason: Optional[str] = ..., reason: Optional[str] = ...,
recurrence: Optional[ScheduledEventRecurrence] = ...,
) -> ScheduledEvent: ) -> ScheduledEvent:
... ...
@ -3065,6 +3069,7 @@ class Guild(Hashable):
description: str = MISSING, description: str = MISSING,
image: bytes = MISSING, image: bytes = MISSING,
reason: Optional[str] = None, reason: Optional[str] = None,
recurrence: Optional[ScheduledEventRecurrence] = MISSING,
) -> ScheduledEvent: ) -> ScheduledEvent:
r"""|coro| r"""|coro|
@ -3111,6 +3116,9 @@ class Guild(Hashable):
Required if the ``entity_type`` is :attr:`EntityType.external`. Required if the ``entity_type`` is :attr:`EntityType.external`.
reason: Optional[:class:`str`] reason: Optional[:class:`str`]
The reason for creating this scheduled event. Shows up on the audit log. The reason for creating this scheduled event. Shows up on the audit log.
recurrence: Optional[:class:`ScheduledEventRecurrence`]
The recurrence rule this event will follow. If this is `None` then this is
a one-time event.
Raises Raises
------- -------
@ -3205,6 +3213,9 @@ class Guild(Hashable):
) )
payload['scheduled_end_time'] = end_time.isoformat() payload['scheduled_end_time'] = end_time.isoformat()
if recurrence not in (MISSING, None):
payload['recurrence_rule'] = recurrence.to_dict()
if metadata: if metadata:
payload['entity_metadata'] = metadata payload['entity_metadata'] = metadata

23
discord/http.py

@ -1985,6 +1985,7 @@ class HTTPClient:
'description', 'description',
'entity_type', 'entity_type',
'image', 'image',
'recurrence_rule'
) )
payload = {k: v for k, v in payload.items() if k in valid_keys} payload = {k: v for k, v in payload.items() if k in valid_keys}
@ -2038,6 +2039,7 @@ class HTTPClient:
'description', 'description',
'entity_type', 'entity_type',
'image', 'image',
'recurrence_rule'
) )
payload = {k: v for k, v in payload.items() if k in valid_keys} payload = {k: v for k, v in payload.items() if k in valid_keys}
@ -2133,6 +2135,27 @@ class HTTPClient:
), ),
params=params, params=params,
) )
def get_scheduled_event_counts(
self,
guild_id: Snowflake,
guild_scheduled_event_id: Snowflake,
scheduled_event_exception_ids: Tuple[Snowflake, ...]
) -> Response[scheduled_event.GuildScheduledEventExceptionCounts]:
route: str = '/guilds/{guild_id}/scheduled-events/{guild_scheduled_event_id}/users/counts?'
if len(scheduled_event_exception_ids) > 0:
for exception_id in scheduled_event_exception_ids:
route += f"guild_scheduled_event_exception_ids={exception_id}&"
return self.request(
Route(
'GET',
route,
guild_id=guild_id,
guild_scheduled_event_id=guild_scheduled_event_id
)
)
# Application commands (global) # Application commands (global)

269
discord/scheduled_event.py

@ -25,7 +25,7 @@ DEALINGS IN THE SOFTWARE.
from __future__ import annotations from __future__ import annotations
from datetime import datetime from datetime import datetime
from typing import TYPE_CHECKING, AsyncIterator, Dict, Optional, Union, overload, Literal from typing import TYPE_CHECKING, AsyncIterator, Dict, Optional, Union, overload, Literal, List, Tuple
from .asset import Asset from .asset import Asset
from .enums import EventStatus, EntityType, PrivacyLevel, try_enum from .enums import EventStatus, EntityType, PrivacyLevel, try_enum
@ -37,6 +37,8 @@ if TYPE_CHECKING:
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,
) )
@ -51,10 +53,220 @@ if TYPE_CHECKING:
# fmt: off # fmt: off
__all__ = ( __all__ = (
"ScheduledEvent", "ScheduledEvent",
"ScheduledEventRecurrence",
"ScheduledEventExceptionCount"
) )
# fmt: on # fmt: on
class ScheduledEventExceptionCount:
"""Represents the exception counts in a Scheduled Event.
.. versionadded:: 2.4
.. container:: operations
.. describe:: x == y
Checks if two Exception Counts are equal.
"""
def __init__(self, data: GuildScheduledEventExceptionCountsPayload) -> None:
self.count: int = int(data.get('guild_scheduled_event_count'))
self._exception_snowflakes: Dict[Union[str, int], int] = data.get('guild_scheduled_event_exception_counts')
@property
def exception_ids(self) -> List[int]:
"""List[:class:`int`]: A list containing all the exception event IDs"""
return [int(id) for id in self._exception_snowflakes.keys()]
@property
def exceptions(self) -> Dict[int, int]:
"""Dict[:class:`int`, :class:`int`]: A dictionary containing all the
event IDs as keys and their respective exception counts as value.
"""
return {int(snowflake): count for snowflake, count in self._exception_snowflakes.items()}
class ScheduledEventRecurrence:
"""Represents a Scheduled Event Recurrence
.. versionadded:: 2.4
.. container:: operations
.. describe:: x == y
Checks if two Scheduled Event Recurrences are equal
Parameters
----------
start: :class:`datetime.datetime`
When the first event of this series is started.
end: Optional[:class:`datetime.datetime`]
When the events of this series will stop. If it is `None`, it will repeat forever.
weekday: :class:`int`
An integer representing the weekday this event will repeat in. Monday is 0
and Sunday is 6.
n_weekday: Tuple[:class:`int`, :class:`int`]
A tuple that contain the N weekday this event will repeat in.
For example, if you want for this event to repeat the 1st Monday of the month,
then this param should have a value of `(1, 0)`. Where ``1`` represents the
'first' and ``0`` the weekday, in this case, Monday.
month: :class:`int`
An integer representing the month this event will repeat in.
month_days: List[:class:`int`]
A list of integers representing the month days this event will repeat in.
This marks the days of the month this event will repeat in, for example, if it
is set to `1`, this event will repeat the first day of every month.
year_days: List[:class:`int`]
A list of integers representing the year days this event will repeat in.
This marks the days of the year this event will repeat in, for example, if it
is set to `1`, this event will repeat the first day of every year.
"""
@overload
def __init__(
self,
start: datetime,
*,
weekdays: List[Literal[0, 1, 2, 3, 4, 5, 6]],
end: Optional[datetime] = ...,
) -> None:
...
@overload
def __init__(
self,
start: datetime,
*,
n_weekday: Tuple[Literal[1, 2, 3, 4], int],
end: Optional[datetime] = ...,
) -> None:
...
@overload
def __init__(
self,
start: datetime,
*,
month: Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
month_days: List[int],
end: Optional[datetime] = ...,
) -> None:
...
@overload
def __init__(
self,
start: datetime,
*,
year_days: List[int],
end: Optional[datetime] = ...,
) -> None:
...
def __init__(
self,
start: datetime,
*,
weekdays: List[Literal[0, 1, 2, 3, 4, 5, 6]] = MISSING,
n_weekday: Tuple[Literal[1, 2, 3, 4], int] = MISSING,
month: Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] = MISSING,
month_days: List[int] = MISSING,
year_days: List[int] = MISSING,
end: Optional[datetime] = MISSING,
) -> None:
if not start.tzinfo:
raise ValueError(
'\'start\' must be an aware datetime. Consider using discord.utils.utcnow() or datetime.datetime.now().astimezone() for local time.'
)
if end not in (MISSING, None):
if not end.tzinfo:
raise ValueError(
'\'end\' must be an aware datetime. Consider using discord.utils.utcnow() or datetime.datetime.now().astimezone() for local time.'
)
self.start: datetime = start
self.end: Optional[datetime] = end if end is not MISSING else None
self.weekdays: Optional[List[int]] = weekdays if weekdays is not MISSING else None
self.n_weekday: Optional[Tuple[int, int]] = n_weekday if n_weekday is not MISSING else None
self.month: Optional[int] = month if month is not MISSING else None
self.month_days: Optional[List[int]] = month_days if month_days is not MISSING else None
self.year_days: Optional[List[int]] = year_days if year_days is not MISSING else None
self._interval: int = 1
def __eq__(self, other: object) -> bool:
if isinstance(other, self.__class__):
return (
self.start == other.start
)
return NotImplemented
def __set_interval(self, value: int) -> None:
# Inner function to set the interval to the one that we
# recieved from the API
self._interval: int = value
@property
def frequency(self) -> int:
""":class:`int`: Returns the frequency of this recurrent scheduled event"""
# This is now an internal parameter because if it is user-provided this could cause
# HTTPExceptions when creating or editing events.
if self.weekdays is not None:
return 2 if len(self.weekdays) == 1 else 3
elif self.n_weekday is not None:
return 1
elif self.month is not None and self.month_days is not None:
return 0
return 0 # In case None of the cases matches (i.e.: year_days) then we return 0
@property
def interval(self) -> int:
""":class:`int`: Returns the interval of this recurrent scheduled event"""
return self._interval
def to_dict(self) -> GuildScheduledEventRecurrencePayload:
return {
"start": self.start.isoformat(),
"end": self.end.isoformat() if self.end else None,
"by_weekday": self.weekdays or [],
"by_month": [self.month,] if self.month else [],
"by_month_day": self.month_days or [],
"by_n_weekday": [self.n_weekday,] if self.n_weekday else [],
"by_year_day": self.year_days or [],
"count": None, # There isn't counts, yet
"frequency": self.frequency,
"interval": self.interval,
} # type: ignore
@classmethod
def from_dict(cls, data: GuildScheduledEventRecurrencePayload) -> ScheduledEventRecurrence:
self: cls = cls(
start=datetime.fromisoformat(data.get('start')),
weekdays=data.get('by_weekday', MISSING),
n_weekdays=((d['n'], d['day']) for d in data.get('by_n_weekday')) if data.get('by_n_weekday', MISSING) is not MISSING else MISSING,
month=data.get('by_month')[0] if len(data.get('by_month', [])) > 0 and data.get('by_month', MISSING) is not MISSING else MISSING,
month_days=data.get('by_month_day', MISSING),
year_days=data.get('by_year_day', MISSING),
end=data.get('end', MISSING)
) # type: ignore
self.__set_interval(int(data.get('interval', 1)))
return self
class ScheduledEvent(Hashable): class ScheduledEvent(Hashable):
"""Represents a scheduled event in a guild. """Represents a scheduled event in a guild.
@ -104,6 +316,10 @@ class ScheduledEvent(Hashable):
.. versionadded:: 2.2 .. versionadded:: 2.2
location: Optional[:class:`str`] location: Optional[:class:`str`]
The location of the scheduled event. The location of the scheduled event.
recurrence: Optional[:class:`ScheduledEventRecurrence`]
The recurrence rule this event follows, if any.
.. versionadded:: 2.4
""" """
__slots__ = ( __slots__ = (
@ -125,6 +341,7 @@ class ScheduledEvent(Hashable):
'channel_id', 'channel_id',
'creator_id', 'creator_id',
'location', 'location',
'recurrence',
) )
def __init__(self, *, state: ConnectionState, data: GuildScheduledEventPayload) -> None: def __init__(self, *, state: ConnectionState, data: GuildScheduledEventPayload) -> None:
@ -145,6 +362,7 @@ class ScheduledEvent(Hashable):
self._cover_image: Optional[str] = data.get('image', None) self._cover_image: Optional[str] = data.get('image', None)
self.user_count: int = data.get('user_count', 0) self.user_count: int = data.get('user_count', 0)
self.creator_id: Optional[int] = _get_as_snowflake(data, 'creator_id') self.creator_id: Optional[int] = _get_as_snowflake(data, 'creator_id')
self.recurrence: Optional[ScheduledEventRecurrence] = ScheduledEventRecurrence.from_dict(data.get('recurrence_rule')) if data.get('recurrence_rule', None) is not None else None
creator = data.get('creator') creator = data.get('creator')
self.creator: Optional[User] = self._state.store_user(creator) if creator else None self.creator: Optional[User] = self._state.store_user(creator) if creator else None
@ -310,6 +528,7 @@ class ScheduledEvent(Hashable):
status: EventStatus = ..., status: EventStatus = ...,
image: bytes = ..., image: bytes = ...,
reason: Optional[str] = ..., reason: Optional[str] = ...,
recurrence: Optional[ScheduledEventRecurrence] = ...,
) -> ScheduledEvent: ) -> ScheduledEvent:
... ...
@ -327,6 +546,7 @@ class ScheduledEvent(Hashable):
status: EventStatus = ..., status: EventStatus = ...,
image: bytes = ..., image: bytes = ...,
reason: Optional[str] = ..., reason: Optional[str] = ...,
recurrence: Optional[ScheduledEventRecurrence] = ...,
) -> ScheduledEvent: ) -> ScheduledEvent:
... ...
@ -344,6 +564,7 @@ class ScheduledEvent(Hashable):
image: bytes = ..., image: bytes = ...,
location: str, location: str,
reason: Optional[str] = ..., reason: Optional[str] = ...,
recurrence: Optional[ScheduledEventRecurrence] = ...,
) -> ScheduledEvent: ) -> ScheduledEvent:
... ...
@ -360,6 +581,7 @@ class ScheduledEvent(Hashable):
status: EventStatus = ..., status: EventStatus = ...,
image: bytes = ..., image: bytes = ...,
reason: Optional[str] = ..., reason: Optional[str] = ...,
recurrence: Optional[ScheduledEventRecurrence] = ...,
) -> ScheduledEvent: ) -> ScheduledEvent:
... ...
@ -376,6 +598,7 @@ class ScheduledEvent(Hashable):
image: bytes = ..., image: bytes = ...,
location: str, location: str,
reason: Optional[str] = ..., reason: Optional[str] = ...,
recurrence: Optional[ScheduledEventRecurrence] = ...,
) -> ScheduledEvent: ) -> ScheduledEvent:
... ...
@ -393,6 +616,7 @@ class ScheduledEvent(Hashable):
image: bytes = MISSING, image: bytes = MISSING,
location: str = MISSING, location: str = MISSING,
reason: Optional[str] = None, reason: Optional[str] = None,
recurrence: Optional[ScheduledEventRecurrence] = MISSING,
) -> ScheduledEvent: ) -> ScheduledEvent:
r"""|coro| r"""|coro|
@ -441,6 +665,9 @@ class ScheduledEvent(Hashable):
Required if the entity type is :attr:`EntityType.external`. Required if the entity type is :attr:`EntityType.external`.
reason: Optional[:class:`str`] reason: Optional[:class:`str`]
The reason for editing the scheduled event. Shows up on the audit log. The reason for editing the scheduled event. Shows up on the audit log.
recurrence: Optional[:class:`ScheduledEventRecurrence`]
The recurrence rule this event will follow, or `None` to set it to a
one-time event.
Raises Raises
------- -------
@ -551,6 +778,12 @@ class ScheduledEvent(Hashable):
else: else:
payload['scheduled_end_time'] = end_time payload['scheduled_end_time'] = end_time
if recurrence is not MISSING:
if recurrence is not None:
payload['recurrence_rule'] = recurrence.to_dict()
else:
payload['recurrence_rule'] = None
if metadata: if metadata:
payload['entity_metadata'] = metadata payload['entity_metadata'] = metadata
@ -675,6 +908,40 @@ class ScheduledEvent(Hashable):
# There's no data left after this # There's no data left after this
break break
async def fetch_counts(self, *children: Snowflake) -> ScheduledEventExceptionCount:
"""|coro|
Retrieves all the counts for this Event children, if this event isn't
recurrent, then this will return `None`.
This also contains the exceptions of this Scheduled event.
.. versionadded:: 2.4
Parameters
----------
*children: :class:`Snowflake`
The snowflakes of the children to fetcht the counts of.
Raises
------
HTTPException
Fetching the counts failed.
Returns
-------
Optional[:class:`ScheduledEventExceptionCount`]
The counts of this event, or `None` if this event isn't recurrent or
there isn't any exception.
"""
if not self.recurrence:
return None
data = await self._state.http.get_scheduled_event_counts(self.guild_id, self.id, tuple([child.id for child in children]))
return ScheduledEventExceptionCount(data)
def _add_user(self, user: User) -> None: def _add_user(self, user: User) -> None:
self._users[user.id] = user self._users[user.id] = user

29
discord/types/scheduled_event.py

@ -22,17 +22,38 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
""" """
from typing import List, Literal, Optional, TypedDict, Union from typing import List, Literal, Optional, TypedDict, Union, Dict
from typing_extensions import NotRequired from typing_extensions import NotRequired
from .snowflake import Snowflake from .snowflake import Snowflake
from .user import User from .user import User
from .member import Member from .member import Member
from .channel import PrivacyLevel as PrivacyLevel from .channel import PrivacyLevel as PrivacyLevel
EventStatus = Literal[1, 2, 3, 4] EventStatus = Literal[1, 2, 3, 4]
EntityType = Literal[1, 2, 3] EntityType = Literal[1, 2, 3]
class _NWeekday(TypedDict):
n: int
day: Literal[0, 1, 2, 3, 4, 5, 6]
class GuildScheduledEventRecurrence(TypedDict):
start: str
end: Optional[str]
frequency: int
interval: int
by_weekday: Optional[List[Literal[0, 1, 2, 3, 4, 5, 6]]] # NOTE: 0 = monday; 6 = sunday
by_n_weekday: Optional[List[_NWeekday]]
by_month: Optional[List[Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]]]
by_month_day: Optional[List[int]] # NOTE: day range between 1 and 31
by_year_day: Optional[List[int]]
count: Optional[int] # maybe?
# NOTE: for this ^ enum, it is recommended to use "calendar" module constants: MONDAY; TUESDAY; WEDNESDAY; etc
# as they follow these patterns and is a built-in module.
class GuildScheduledEventExceptionCounts(TypedDict):
guild_scheduled_event_count: int
guild_scheduled_event_exception_counts: Dict[Snowflake, int]
# NOTE: This class doesn't represent any of the user counts or the 'count' param in recurrence
class _BaseGuildScheduledEvent(TypedDict): class _BaseGuildScheduledEvent(TypedDict):
id: Snowflake id: Snowflake
@ -42,6 +63,10 @@ class _BaseGuildScheduledEvent(TypedDict):
scheduled_start_time: str scheduled_start_time: str
privacy_level: PrivacyLevel privacy_level: PrivacyLevel
status: EventStatus status: EventStatus
auto_start: bool
guild_scheduled_events_exceptions: List # Didn't found items in the list yet
recurrence_rule: Optional[GuildScheduledEventRecurrence]
sku_ids: List[Snowflake]
creator_id: NotRequired[Optional[Snowflake]] creator_id: NotRequired[Optional[Snowflake]]
description: NotRequired[Optional[str]] description: NotRequired[Optional[str]]
creator: NotRequired[User] creator: NotRequired[User]

Loading…
Cancel
Save