Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement missing stuff for scheduled events #1507

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 10 additions & 0 deletions interactions/api/events/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
GuildJoin,
GuildLeft,
GuildMembersChunk,
GuildScheduledEventCreate,
GuildScheduledEventUpdate,
GuildScheduledEventDelete,
GuildScheduledEventUserAdd,
GuildScheduledEventUserRemove,
GuildStickersUpdate,
GuildUnavailable,
GuildUpdate,
Expand Down Expand Up @@ -126,6 +131,11 @@
"GuildJoin",
"GuildLeft",
"GuildMembersChunk",
"GuildScheduledEventCreate",
"GuildScheduledEventUpdate",
"GuildScheduledEventDelete",
"GuildScheduledEventUserAdd",
"GuildScheduledEventUserRemove",
"GuildStickersUpdate",
"GuildUnavailable",
"GuildUpdate",
Expand Down
59 changes: 59 additions & 0 deletions interactions/api/events/discord.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ async def an_event_handler(event: ChannelCreate):
"GuildJoin",
"GuildLeft",
"GuildMembersChunk",
"GuildScheduledEventCreate",
"GuildScheduledEventUpdate",
"GuildScheduledEventDelete",
"GuildScheduledEventUserAdd",
"GuildScheduledEventUserRemove",
"GuildStickersUpdate",
"GuildAvailable",
"GuildUnavailable",
Expand Down Expand Up @@ -109,6 +114,7 @@ async def an_event_handler(event: ChannelCreate):
from interactions.models.discord.auto_mod import AutoModerationAction, AutoModRule
from interactions.models.discord.reaction import Reaction
from interactions.models.discord.app_perms import ApplicationCommandPermission
from interactions.models.discord.scheduled_event import ScheduledEvent


@attrs.define(eq=False, order=False, hash=False, kw_only=False)
Expand Down Expand Up @@ -751,3 +757,56 @@ class GuildAuditLogEntryCreate(GuildEvent):

audit_log_entry: interactions.models.AuditLogEntry = attrs.field(repr=False)
"""The audit log entry object"""


@attrs.define(eq=False, order=False, hash=False, kw_only=False)
class GuildScheduledEventCreate(BaseEvent):
"""Dispatched when scheduled event is created"""

scheduled_event: "ScheduledEvent" = attrs.field(repr=True)
"""The scheduled event object"""


@attrs.define(eq=False, order=False, hash=False, kw_only=False)
class GuildScheduledEventUpdate(BaseEvent):
"""Dispatched when scheduled event is updated"""

before: Absent["ScheduledEvent"] = attrs.field(repr=True)
"""The scheduled event before this event was created"""
after: "ScheduledEvent" = attrs.field(repr=True)
"""The scheduled event after this event was created"""


@attrs.define(eq=False, order=False, hash=False, kw_only=False)
class GuildScheduledEventDelete(GuildScheduledEventCreate):
"""Dispatched when scheduled event is deleted"""


@attrs.define(eq=False, order=False, hash=False, kw_only=False)
class GuildScheduledEventUserAdd(GuildEvent):
"""Dispatched when scheduled event is created"""

scheduled_event_id: "Snowflake_Type" = attrs.field(repr=True)
"""The ID of the scheduled event"""
user_id: "Snowflake_Type" = attrs.field(repr=True)
"""The ID of the user that has been added/removed from scheduled event"""

@property
def scheduled_event(self) -> Optional["ScheduledEvent"]:
"""The scheduled event object if cached"""
return self.client.get_scheduled_event(self.scheduled_event_id)

@property
def user(self) -> Optional["User"]:
"""The user that has been added/removed from scheduled event if cached"""
return self.client.get_user(self.user_id)

@property
def member(self) -> Optional["Member"]:
"""The guild member that has been added/removed from scheduled event if cached"""
return self.client.get_member(self.guild_id, self.user.id)


@attrs.define(eq=False, order=False, hash=False, kw_only=False)
class GuildScheduledEventUserRemove(GuildScheduledEventUserAdd):
"""Dispatched when scheduled event is removed"""
2 changes: 2 additions & 0 deletions interactions/api/events/processors/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from .message_events import MessageEvents
from .reaction_events import ReactionEvents
from .role_events import RoleEvents
from .scheduled_events import ScheduledEvents
from .stage_events import StageEvents
from .thread_events import ThreadEvents
from .user_events import UserEvents
Expand All @@ -20,6 +21,7 @@
"MessageEvents",
"ReactionEvents",
"RoleEvents",
"ScheduledEvents",
"StageEvents",
"ThreadEvents",
"UserEvents",
Expand Down
50 changes: 50 additions & 0 deletions interactions/api/events/processors/scheduled_events.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import copy
from typing import TYPE_CHECKING

import interactions.api.events as events
from interactions.client.const import MISSING
from interactions.models import ScheduledEvent
from ._template import EventMixinTemplate, Processor

if TYPE_CHECKING:
from interactions.api.events import RawGatewayEvent

__all__ = ("ScheduledEvents",)


class ScheduledEvents(EventMixinTemplate):
@Processor.define()
async def _on_raw_guild_scheduled_event_create(self, event: "RawGatewayEvent") -> None:
scheduled_event = self.cache.place_scheduled_event_data(event.data)

self.dispatch(events.GuildScheduledEventCreate(scheduled_event))

@Processor.define()
async def _on_raw_guild_scheduled_event_update(self, event: "RawGatewayEvent") -> None:
before = copy.copy(self.cache.get_scheduled_event(event.data.get("id")))
after = self.cache.place_scheduled_event_data(event.data)

self.dispatch(events.GuildScheduledEventUpdate(before or MISSING, after))

@Processor.define()
async def _on_raw_guild_scheduled_event_delete(self, event: "RawGatewayEvent") -> None:
# for some reason this event returns the deleted scheduled event data?
# so we create an object from it
scheduled_event = ScheduledEvent.from_dict(event.data, self)
self.cache.delete_scheduled_event(event.data.get("id"))

self.dispatch(events.GuildScheduledEventDelete(scheduled_event))

@Processor.define()
async def _on_raw_guild_scheduled_event_user_add(self, event: "RawGatewayEvent") -> None:
scheduled_event = self.cache.get_scheduled_event(event.data.get("guild_scheduled_event_id"))
user = self.cache.get_user(event.data.get("user_id"))

self.dispatch(events.GuildScheduledEventUserAdd(event.data.get("guild_id"), scheduled_event, user))

@Processor.define()
async def _on_raw_guild_scheduled_event_user_remove(self, event: "RawGatewayEvent") -> None:
scheduled_event = self.cache.get_scheduled_event(event.data.get("guild_scheduled_event_id"))
user = self.cache.get_user(event.data.get("user_id"))

self.dispatch(events.GuildScheduledEventUserRemove(event.data.get("guild_id"), scheduled_event, user))
28 changes: 27 additions & 1 deletion interactions/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,12 @@
events.AutoModCreated: [Intents.AUTO_MODERATION_CONFIGURATION, Intents.AUTO_MOD],
events.AutoModUpdated: [Intents.AUTO_MODERATION_CONFIGURATION, Intents.AUTO_MOD],
events.AutoModDeleted: [Intents.AUTO_MODERATION_CONFIGURATION, Intents.AUTO_MOD],
# Intents.GUILD_SCHEDULED_EVENTS
events.GuildScheduledEventCreate: [Intents.GUILD_SCHEDULED_EVENTS],
events.GuildScheduledEventUpdate: [Intents.GUILD_SCHEDULED_EVENTS],
events.GuildScheduledEventDelete: [Intents.GUILD_SCHEDULED_EVENTS],
events.GuildScheduledEventUserAdd: [Intents.GUILD_SCHEDULED_EVENTS],
events.GuildScheduledEventUserRemove: [Intents.GUILD_SCHEDULED_EVENTS],
# multiple intents
events.ThreadMembersUpdate: [Intents.GUILDS, Intents.GUILD_MEMBERS],
events.TypingStart: [
Expand Down Expand Up @@ -211,6 +217,7 @@ class Client(
processors.MessageEvents,
processors.ReactionEvents,
processors.RoleEvents,
processors.ScheduledEvents,
processors.StageEvents,
processors.ThreadEvents,
processors.UserEvents,
Expand Down Expand Up @@ -2282,10 +2289,29 @@ async def fetch_scheduled_event(
"""
try:
scheduled_event_data = await self.http.get_scheduled_event(guild_id, scheduled_event_id, with_user_count)
return ScheduledEvent.from_dict(scheduled_event_data, self)
return self.cache.place_scheduled_event_data(scheduled_event_data)
except NotFound:
return None

def get_scheduled_event(
self,
scheduled_event_id: "Snowflake_Type",
) -> Optional["ScheduledEvent"]:
Damego marked this conversation as resolved.
Show resolved Hide resolved
"""
Get a scheduled event by id.

!!! note
This method is an alias for the cache which will return a cached object.

Args:
scheduled_event_id: The ID of the scheduled event to get

Returns:
The scheduled event if found, otherwise None

"""
return self.cache.get_scheduled_event(scheduled_event_id)

async def fetch_custom_emoji(
self, emoji_id: "Snowflake_Type", guild_id: "Snowflake_Type", *, force: bool = False
) -> Optional[CustomEmoji]:
Expand Down
42 changes: 42 additions & 0 deletions interactions/client/smart_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from interactions.models.discord.role import Role
from interactions.models.discord.snowflake import to_snowflake, to_optional_snowflake
from interactions.models.discord.user import Member, User
from interactions.models.discord.scheduled_event import ScheduledEvent
from interactions.models.internal.active_voice_state import ActiveVoiceState

__all__ = ("GlobalCache", "create_cache")
Expand Down Expand Up @@ -70,6 +71,7 @@ class GlobalCache:
member_cache: dict = attrs.field(repr=False, factory=dict) # key: (guild_id, user_id)
channel_cache: dict = attrs.field(repr=False, factory=dict) # key: channel_id
guild_cache: dict = attrs.field(repr=False, factory=dict) # key: guild_id
scheduled_events_cache: dict = attrs.field(repr=False, factory=dict) # key: guild_scheduled_event_id

# Expiring discord objects cache
message_cache: TTLCache = attrs.field(repr=False, factory=TTLCache) # key: (channel_id, message_id)
Expand Down Expand Up @@ -903,3 +905,43 @@ def delete_emoji(self, emoji_id: "Snowflake_Type") -> None:
self.emoji_cache.pop(to_snowflake(emoji_id), None)

# endregion Emoji cache

# region ScheduledEvents cache

def get_scheduled_event(self, scheduled_event_id: "Snowflake_Type") -> Optional["ScheduledEvent"]:
"""
Get a scheduled event based on the scheduled event ID.

Args:
scheduled_event_id: The ID of the scheduled event

Returns:
The ScheduledEvent if found
"""
return self.scheduled_events_cache.get(to_snowflake(scheduled_event_id))

def place_scheduled_event_data(self, data: discord_typings.GuildScheduledEventData) -> "ScheduledEvent":
"""
Take json data representing a scheduled event, process it, and cache it.

Args:
data: json representation of the scheduled event

Returns:
The processed scheduled event
"""
scheduled_event = ScheduledEvent.from_dict(data, self._client)
self.scheduled_events_cache[scheduled_event.id] = scheduled_event

return scheduled_event

def delete_scheduled_event(self, scheduled_event_id: "Snowflake_Type") -> None:
"""
Delete a scheduled event from the cache.

Args:
scheduled_event_id: The ID of the scheduled event
"""
self.scheduled_events_cache.pop(to_snowflake(scheduled_event_id), None)

# endregion ScheduledEvents cache
6 changes: 3 additions & 3 deletions interactions/models/discord/guild.py
Original file line number Diff line number Diff line change
Expand Up @@ -1253,7 +1253,7 @@ async def list_scheduled_events(self, with_user_count: bool = False) -> List["mo

"""
scheduled_events_data = await self._client.http.list_schedules_events(self.id, with_user_count)
return models.ScheduledEvent.from_list(scheduled_events_data, self._client)
return [self._client.cache.place_scheduled_event_data(data) for data in scheduled_events_data]

async def fetch_scheduled_event(
self, scheduled_event_id: Snowflake_Type, with_user_count: bool = False
Expand All @@ -1275,7 +1275,7 @@ async def fetch_scheduled_event(
)
except NotFound:
return None
return models.ScheduledEvent.from_dict(scheduled_event_data, self._client)
return self._client.cache.place_scheduled_event_data(scheduled_event_data)

async def create_scheduled_event(
self,
Expand Down Expand Up @@ -1339,7 +1339,7 @@ async def create_scheduled_event(
}

scheduled_event_data = await self._client.http.create_scheduled_event(self.id, payload, reason)
return models.ScheduledEvent.from_dict(scheduled_event_data, self._client)
return self._client.cache.place_scheduled_event_data(scheduled_event_data)

async def create_custom_sticker(
self,
Expand Down