Skip to content

Commit

Permalink
feat: guild scheduled events caching
Browse files Browse the repository at this point in the history
  • Loading branch information
Daniel-Worrall committed Jan 14, 2022
1 parent 41e75fa commit 7999d68
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 0 deletions.
104 changes: 104 additions & 0 deletions src/discordcr/cache.cr
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ module Discord
# affected guilds would be missing here too.
getter guilds

# A map of cached scheduled events, i.e. all scheduled events on all servers
# the bot is on.
getter scheduled_events

# A map of cached stage instances, i. e. all stage instances on all servers
# the bot is on.
getter stage_instances
Expand All @@ -73,6 +77,14 @@ module Discord
# [channel IDs]}.
getter guild_channels

# Mapping of guilds to the scheduled events on them, represented as {guild ID =>
# [scheduled event IDs]}.
getter guild_scheduled_events

# Mapping of guild scheduled event to the users subscribed to them, represented as
# {guild scheduled event ID => [user IDs]}.
getter guild_scheduled_event_users

# Mapping of guilds to the Stage instances on them, represented as {guild ID =>
# [stage instance IDs]}.
getter guild_stage_instances
Expand All @@ -89,12 +101,15 @@ module Discord
@guilds = Hash(UInt64, Guild).new
@members = Hash(UInt64, Hash(UInt64, GuildMember)).new
@roles = Hash(UInt64, Role).new
@scheduled_events = Hash(UInt64, GuildScheduledEvent).new
@stage_instances = Hash(UInt64, StageInstance).new

@dm_channels = Hash(UInt64, UInt64).new

@guild_roles = Hash(UInt64, Array(UInt64)).new
@guild_channels = Hash(UInt64, Array(UInt64)).new
@guild_scheduled_events = Hash(UInt64, Array(UInt64)).new
@guild_scheduled_event_users = Hash(UInt64, Array(UInt64)).new
@guild_stage_instances = Hash(UInt64, Array(UInt64)).new

@voice_states = Hash(UInt64, Hash(UInt64, VoiceState)).new
Expand Down Expand Up @@ -138,6 +153,43 @@ module Discord
@roles[id.to_u64] # There is no endpoint for getting an individual role, so we will have to ignore that case for now.
end

# Resolves a guild scheduled event by the *guild_id* of the guild the scheduled
# event is on, and the *event_id* of the event itself. An API request will be performed
# if the object is not cached.
def resolve_guild_scheduled_event(guild_id : UInt64 | Snowflake, event_id : UInt64 | Snowflake) : GuildScheduledEvent
guild_id = guild_id.to_u64
event_id = event_id.to_u64
@scheduled_events.fetch(event_id) do
event = @client.get_guild_scheduled_event(guild_id, event_id)
cache(event)
add_guild_scheduled_event(event.guild_id, event.id)
event
end
end

# Resolves an array of users by the *guild_id* of the guild the guild scheduled event
# is on, and the *event_id* of the event itself. API requests will be performed
# if the object is not cached. If a limit is provided, the subscribed users will
# only be cached if the number of users is below the limit, to ensure it remains synced.
# User and member data is cached regardless. Member data is included if *with_member* is true.
def resolve_guild_scheduled_event_users(guild_id : UInt64 | Snowflake, event_id : UInt64 | Snowflake,
with_member : Bool? = nil, limit : Int32? = nil) : Array(UInt64)
guild_id = guild_id.to_u64
event_id = event_id.to_u64
@guild_scheduled_event_users.fetch(event_id) do
users = @client.page_guild_scheduled_event_users(guild_id, event_id, with_member: with_member, limit: limit).to_a

users.each do |user|
cache user.user
if member = user.member
cache member, guild_id
end
end
return users.map &.user.id.to_u64 if users.size == limit
@guild_scheduled_event_users[event_id] = users.map &.user.id.to_u64
end
end

# Resolves a Stage instance by its *ID*.
# An API request will be performed if the object is not cached.
def resolve_stage_instance(id : UInt64 | Snowflake) : StageInstance
Expand Down Expand Up @@ -185,6 +237,10 @@ module Discord
@guilds.delete(id.to_u64)
end

def delete_scheduled_event(id : UInt64 | Snowflake)
@scheduled_events.delete(id.to_u64)
end

# Deletes a stage instance from the cache given its *ID*.
def delete_stage_instance(id : UInt64 | Snowflake)
@stage_instances.delete(id.to_u64)
Expand Down Expand Up @@ -247,6 +303,11 @@ module Discord
@roles[role.id.to_u64] = role
end

# Adds a specific *guild scheduled event* to the cache.
def cache(guild_scheduled_event : GuildScheduledEvent)
@scheduled_events[guild_scheduled_event.id.to_u64] = guild_scheduled_event
end

# Adds a specific *Stage instance* to the cache.
def cache(stage_instance : StageInstance)
@stage_instances[stage_instance.id.to_u64] = stage_instance
Expand Down Expand Up @@ -324,6 +385,49 @@ module Discord
@guild_channels[guild_id]?.try { |local_channels| local_channels.delete(channel_id) }
end

# Returns all guild scheduled events, identified by the guild's *guild_id*.
def guild_scheduled_events(guild_id : UInt64 | Snowflake) : Array(UInt64)
@guild_scheduled_events[guild_id.to_u64]
end

# Marks a guild scheduled event, identified by the *event_id*, as belonging to a particular
# guild, identified by the *guild_id*.
def add_guild_scheduled_event(guild_id : UInt64 | Snowflake, event_id : UInt64 | Snowflake)
local_events = @guild_scheduled_events[guild_id.to_u64] ||= [] of UInt64
local_events << event_id.to_u64
end

# Marks a guild scheduled event, identified by the *event_id*, as belonging to a particular
# guild, identified by the *guild_id*. This should only be called when the event is created
# during a websocket connection, not a GUILD_CREATE, otherwise the event users cache will be out of sync.
def create_guild_scheduled_event(guild_id : UInt64 | Snowflake, event_id : UInt64 | Snowflake)
add_guild_scheduled_event(guild_id, event_id)
@guild_scheduled_event_users[event_id.to_u64] = [] of UInt64
end

# Marks a guild scheduled event, identified by the *event_id*, as not belonging to a particular guild,
# identified by its *guild_id*, anymore.
def remove_guild_scheduled_event(guild_id : UInt64 | Snowflake, event_id : UInt64 | Snowflake)
@guild_scheduled_events[guild_id.to_u64]?.try { |local_events| local_events.delete(event_id.to_u64) }
end

# Returns all guild scheduled event users, identified by its *event_id*.
def guild_scheduled_event_users(event_id : UInt64 | Snowflake) : Array(UInt64)
@guild_scheduled_event_users[event_id.to_u64]
end

# Marks a user, identified by the *user_id*, as subscribed to a particular guild scheduled event,
# identified by the *event_id*.
def add_guild_scheduled_event_user(event_id : UInt64 | Snowflake, user_id : UInt64 | Snowflake)
@guild_scheduled_event_users[event_id.to_u64]?.try { |local_event_users| local_event_users << user_id.to_u64 }
end

# Marks a user, identified by the *user_id*, as unsubscribed to a particular guild scheduled event,
# identified by the *event_id*.
def remove_guild_scheduled_event_user(event_id : UInt64 | Snowflake, user_id : UInt64 | Snowflake)
@guild_scheduled_event_users[event_id.to_u64]?.try { |local_event_users| local_event_users.delete(user_id.to_u64) }
end

# Returns all Stage instances of a guild, identified by its *guild_id*.
def guild_stage_instances(guild_id : UInt64 | Snowflake) : Array(UInt64)
@guild_stage_instances[guild_id.to_u64]
Expand Down
18 changes: 18 additions & 0 deletions src/discordcr/client.cr
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,13 @@ module Discord
cache voice_state
end

payload.guild_scheduled_events.try &.each do |guild_scheduled_event|
cache guild_scheduled_event
@cache.try &.add_guild_scheduled_event(guild_scheduled_event.guild_id, guild_scheduled_event.id)
# Reset user cache in the case of servers returning from an outage
@cache.try &.guild_scheduled_event_users.delete(guild_scheduled_event.guild_id)
end

payload.stage_instances.each do |stage_instance|
cache stage_instance
@cache.try &.add_guild_stage_instance(guild.id, stage_instance.id)
Expand Down Expand Up @@ -595,22 +602,33 @@ module Discord
when "GUILD_SCHEDULED_EVENT_CREATE"
payload = GuildScheduledEvent.from_json(data)

cache payload
@cache.try &.create_guild_scheduled_event(payload.guild_id, payload.id)

call_event guild_scheduled_event_create, payload
when "GUILD_SCHEDULED_EVENT_UPDATE"
payload = GuildScheduledEvent.from_json(data)

cache payload

call_event guild_scheduled_event_update, payload
when "GUILD_SCHEDULED_EVENT_DELETE"
payload = GuildScheduledEvent.from_json(data)

@cache.try &.remove_guild_scheduled_event(payload.guild_id, payload.id)

call_event guild_scheduled_event_delete, payload
when "GUILD_SCHEDULED_EVENT_USER_ADD"
payload = Gateway::GuildScheduledEventUserPayload.from_json(data)

@cache.try &.add_guild_scheduled_event_user(payload.guild_scheduled_event_id, payload.user_id)

call_event guild_scheduled_event_user_add, payload
when "GUILD_SCHEDULED_EVENT_USER_REMOVE"
payload = Gateway::GuildScheduledEventUserPayload.from_json(data)

@cache.try &.remove_guild_scheduled_event_user(payload.guild_scheduled_event_id, payload.user_id)

call_event guild_scheduled_event_user_remove, payload
when "INVITE_CREATE"
payload = Gateway::InviteCreatePayload.from_json(data)
Expand Down

0 comments on commit 7999d68

Please sign in to comment.