use super::{config::EventType, InMemoryCache, InMemoryCacheError};
use async_trait::async_trait;
use dashmap::DashMap;
use std::{collections::HashSet, hash::Hash, ops::Deref, sync::Arc};
#[allow(unused_imports)]
use tracing::debug;
use twilight_cache_trait::UpdateCache;
use twilight_model::{
channel::{message::MessageReaction, Channel, GuildChannel},
gateway::{event::Event, payload::*, presence::Presence},
guild::GuildStatus,
id::GuildId,
};
fn guard(this: &InMemoryCache, event_type: EventType) -> bool {
this.0.config.event_types().contains(event_type)
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for Event {
#[allow(clippy::cognitive_complexity)]
async fn update(&self, c: &InMemoryCache) -> Result<(), InMemoryCacheError> {
use Event::*;
match self {
BanAdd(_) => Ok(()),
BanRemove(_) => Ok(()),
ChannelCreate(v) => c.update(v).await,
ChannelDelete(v) => c.update(v).await,
ChannelPinsUpdate(v) => c.update(v).await,
ChannelUpdate(v) => c.update(v).await,
GatewayHeartbeat(_) => Ok(()),
GatewayHeartbeatAck => Ok(()),
GatewayHello(_) => Ok(()),
GatewayInvalidateSession(_v) => Ok(()),
GatewayReconnect => Ok(()),
GiftCodeUpdate => Ok(()),
GuildCreate(v) => c.update(v.deref()).await,
GuildDelete(v) => c.update(v.deref()).await,
GuildEmojisUpdate(v) => c.update(v).await,
GuildIntegrationsUpdate(v) => c.update(v).await,
GuildUpdate(v) => c.update(v.deref()).await,
InviteCreate(_) => Ok(()),
InviteDelete(_) => Ok(()),
MemberAdd(v) => c.update(v.deref()).await,
MemberRemove(v) => c.update(v).await,
MemberUpdate(v) => c.update(v.deref()).await,
MemberChunk(v) => c.update(v).await,
MessageCreate(v) => c.update(v.deref()).await,
MessageDelete(v) => c.update(v).await,
MessageDeleteBulk(v) => c.update(v).await,
MessageUpdate(v) => c.update(v.deref()).await,
PresenceUpdate(v) => c.update(v.deref()).await,
PresencesReplace => Ok(()),
ReactionAdd(v) => c.update(v.deref()).await,
ReactionRemove(v) => c.update(v.deref()).await,
ReactionRemoveAll(v) => c.update(v).await,
ReactionRemoveEmoji(_) => Ok(()),
Ready(v) => c.update(v.deref()).await,
Resumed => Ok(()),
RoleCreate(v) => c.update(v).await,
RoleDelete(v) => c.update(v).await,
RoleUpdate(v) => c.update(v).await,
ShardConnected(_) => Ok(()),
ShardConnecting(_) => Ok(()),
ShardDisconnected(_) => Ok(()),
ShardIdentifying(_) => Ok(()),
ShardReconnecting(_) => Ok(()),
ShardPayload(_) => Ok(()),
ShardResuming(_) => Ok(()),
TypingStart(v) => c.update(v.deref()).await,
UnavailableGuild(v) => c.update(v).await,
UserUpdate(v) => c.update(v).await,
VoiceServerUpdate(v) => c.update(v).await,
VoiceStateUpdate(v) => c.update(v.deref()).await,
WebhooksUpdate(v) => c.update(v).await,
}
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for BanAdd {
async fn update(&self, _: &InMemoryCache) -> Result<(), InMemoryCacheError> {
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for BanRemove {
async fn update(&self, _: &InMemoryCache) -> Result<(), InMemoryCacheError> {
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for ChannelCreate {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::CHANNEL_CREATE) {
return Ok(());
}
match self.0.clone() {
Channel::Group(c) => {
super::upsert_item(&cache.0.groups, c.id, c).await;
}
Channel::Guild(c) => {
if let Some(gid) = c.guild_id() {
cache.cache_guild_channel(gid, c.clone()).await;
}
}
Channel::Private(c) => {
cache.cache_private_channel(c.clone()).await;
}
}
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for ChannelDelete {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::CHANNEL_DELETE) {
return Ok(());
}
match self.0 {
Channel::Group(ref c) => {
cache.delete_group(c.id).await;
}
Channel::Guild(ref c) => {
cache.delete_guild_channel(c.id()).await;
}
Channel::Private(ref c) => {
cache.0.channels_private.remove(&c.id);
}
}
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for ChannelPinsUpdate {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::CHANNEL_PINS_UPDATE) {
return Ok(());
}
{
if let Some(mut item) = cache.0.channels_guild.get_mut(&self.channel_id) {
let channel = Arc::make_mut(&mut item.data);
if let GuildChannel::Text(text) = channel {
text.last_pin_timestamp = self.last_pin_timestamp.clone();
}
return Ok(());
}
}
{
if let Some(mut channel) = cache.0.channels_private.get_mut(&self.channel_id) {
Arc::make_mut(&mut channel).last_pin_timestamp = self.last_pin_timestamp.clone();
return Ok(());
}
}
{
if let Some(mut group) = cache.0.groups.get_mut(&self.channel_id) {
Arc::make_mut(&mut group).last_pin_timestamp = self.last_pin_timestamp.clone();
}
}
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for ChannelUpdate {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::CHANNEL_UPDATE) {
return Ok(());
}
match self.0.clone() {
Channel::Group(c) => {
cache.cache_group(c.clone()).await;
}
Channel::Guild(c) => {
if let Some(gid) = c.guild_id() {
cache.cache_guild_channel(gid, c.clone()).await;
}
}
Channel::Private(c) => {
cache.cache_private_channel(c.clone()).await;
}
}
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for GuildCreate {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::GUILD_CREATE) {
return Ok(());
}
cache.cache_guild(self.0.clone()).await;
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for GuildDelete {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
async fn remove_ids<T: Eq + Hash, U>(
guild_map: &DashMap<GuildId, HashSet<T>>,
container: &DashMap<T, U>,
guild_id: GuildId,
) {
if let Some((_, ids)) = guild_map.remove(&guild_id) {
for id in ids {
container.remove(&id);
}
}
}
if !guard(cache, EventType::GUILD_DELETE) {
return Ok(());
}
let id = self.id;
cache.0.guilds.remove(&id);
remove_ids(&cache.0.guild_channels, &cache.0.channels_guild, id).await;
remove_ids(&cache.0.guild_emojis, &cache.0.emojis, id).await;
remove_ids(&cache.0.guild_roles, &cache.0.roles, id).await;
cache.0.guild_voice_states.remove(&id);
if let Some((_, ids)) = cache.0.guild_members.remove(&id) {
for user_id in ids {
cache.0.members.remove(&(id, user_id));
}
}
if let Some((_, ids)) = cache.0.guild_presences.remove(&id) {
for user_id in ids {
cache.0.presences.remove(&(Some(id), user_id));
}
}
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for GuildEmojisUpdate {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::GUILD_EMOJIS_UPDATE) {
return Ok(());
}
cache
.cache_emojis(self.guild_id, self.emojis.values().cloned())
.await;
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for GuildIntegrationsUpdate {
async fn update(&self, _: &InMemoryCache) -> Result<(), InMemoryCacheError> {
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for GuildUpdate {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::GUILD_UPDATE) {
return Ok(());
}
let mut guild = match cache
.0
.guilds
.get_mut(&self.0.id)
.map(|r| Arc::clone(r.value()))
{
Some(guild) => guild,
None => return Ok(()),
};
let g = &self.0;
let mut guild = Arc::make_mut(&mut guild);
guild.afk_channel_id = g.afk_channel_id;
guild.afk_timeout = g.afk_timeout;
guild.banner = g.banner.clone();
guild.default_message_notifications = g.default_message_notifications;
guild.description = g.description.clone();
guild.embed_channel_id = g.embed_channel_id;
guild.embed_enabled.replace(g.embed_enabled);
guild.features = g.features.clone();
guild.icon = g.icon.clone();
guild.max_members = g.max_members;
guild.max_presences = Some(g.max_presences.unwrap_or(25000));
guild.mfa_level = g.mfa_level;
guild.name = g.name.clone();
guild.owner = g.owner;
guild.owner_id = g.owner_id;
guild.permissions = g.permissions;
guild.preferred_locale = g.preferred_locale.clone();
guild.premium_tier = g.premium_tier;
guild
.premium_subscription_count
.replace(g.premium_subscription_count.unwrap_or(0));
guild.region = g.region.clone();
guild.splash = g.splash.clone();
guild.system_channel_id = g.system_channel_id;
guild.verification_level = g.verification_level;
guild.vanity_url_code = g.vanity_url_code.clone();
guild.widget_channel_id = g.widget_channel_id;
guild.widget_enabled = g.widget_enabled;
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for MemberAdd {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::MEMBER_ADD) {
return Ok(());
}
cache.cache_member(self.guild_id, self.0.clone()).await;
cache
.0
.guild_members
.entry(self.guild_id)
.or_default()
.insert(self.0.user.id);
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for MemberChunk {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::MEMBER_CHUNK) {
return Ok(());
}
if self.members.is_empty() {
return Ok(());
}
cache
.cache_members(self.guild_id, self.members.values().cloned())
.await;
let user_ids = self.members.keys();
let mut guild = cache.0.guild_members.entry(self.guild_id).or_default();
for id in user_ids {
guild.insert(*id);
}
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for MemberRemove {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::MEMBER_REMOVE) {
return Ok(());
}
cache.0.members.remove(&(self.guild_id, self.user.id));
if let Some(mut members) = cache.0.guild_members.get_mut(&self.guild_id) {
members.remove(&self.user.id);
}
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for MemberUpdate {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::MEMBER_UPDATE) {
return Ok(());
}
let mut member = match cache.0.members.get_mut(&(self.guild_id, self.user.id)) {
Some(member) => member,
None => return Ok(()),
};
let mut member = Arc::make_mut(&mut member);
member.nick = self.nick.clone();
member.roles = self.roles.clone();
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for MessageCreate {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::MESSAGE_CREATE) {
return Ok(());
}
let mut channel = cache.0.messages.entry(self.0.channel_id).or_default();
if channel.len() > cache.0.config.message_cache_size() {
if let Some(k) = channel.iter().next_back().map(|x| *x.0) {
channel.remove(&k);
}
}
channel.insert(self.0.id, Arc::new(From::from(self.0.clone())));
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for MessageDelete {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::MESSAGE_DELETE) {
return Ok(());
}
let mut channel = cache.0.messages.entry(self.channel_id).or_default();
channel.remove(&self.id);
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for MessageDeleteBulk {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::MESSAGE_DELETE_BULK) {
return Ok(());
}
let mut channel = cache.0.messages.entry(self.channel_id).or_default();
for id in &self.ids {
channel.remove(id);
}
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for MessageUpdate {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::MESSAGE_UPDATE) {
return Ok(());
}
let mut channel = cache.0.messages.entry(self.channel_id).or_default();
if let Some(mut message) = channel.get_mut(&self.id) {
let mut msg = Arc::make_mut(&mut message);
if let Some(attachments) = &self.attachments {
msg.attachments = attachments.clone();
}
if let Some(content) = &self.content {
msg.content = content.clone();
}
if let Some(edited_timestamp) = &self.edited_timestamp {
msg.edited_timestamp.replace(edited_timestamp.clone());
}
if let Some(embeds) = &self.embeds {
msg.embeds = embeds.clone();
}
if let Some(mention_everyone) = self.mention_everyone {
msg.mention_everyone = mention_everyone;
}
if let Some(mention_roles) = &self.mention_roles {
msg.mention_roles = mention_roles.clone();
}
if let Some(mentions) = &self.mentions {
msg.mentions = mentions.iter().map(|x| x.id).collect::<Vec<_>>();
}
if let Some(pinned) = self.pinned {
msg.pinned = pinned;
}
if let Some(timestamp) = &self.timestamp {
msg.timestamp = timestamp.clone();
}
if let Some(tts) = self.tts {
msg.tts = tts;
}
}
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for PresenceUpdate {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::PRESENCE_UPDATE) {
return Ok(());
}
let presence = Presence {
activities: self.activities.clone(),
client_status: self.client_status.clone(),
game: self.game.clone(),
guild_id: self.guild_id,
nick: self.nick.clone(),
status: self.status,
user: self.user.clone(),
};
cache.cache_presence(self.guild_id, presence).await;
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for ReactionAdd {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::REACTION_ADD) {
return Ok(());
}
let mut channel = cache.0.messages.entry(self.0.channel_id).or_default();
let mut message = match channel.get_mut(&self.0.message_id) {
Some(message) => message,
None => return Ok(()),
};
let msg = Arc::make_mut(&mut message);
if let Some(reaction) = msg.reactions.iter_mut().find(|r| r.emoji == self.0.emoji) {
if !reaction.me {
if let Some(current_user) = cache.current_user().await? {
if current_user.id == self.0.user_id {
reaction.me = true;
}
}
}
reaction.count += 1;
} else {
let mut me = false;
if let Some(current_user) = cache.current_user().await? {
if current_user.id == self.0.user_id {
me = true;
}
}
msg.reactions.push(MessageReaction {
count: 1,
emoji: self.0.emoji.clone(),
me,
});
}
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for ReactionRemove {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::REACTION_REMOVE) {
return Ok(());
}
let mut channel = cache.0.messages.entry(self.0.channel_id).or_default();
let mut message = match channel.get_mut(&self.0.message_id) {
Some(message) => message,
None => return Ok(()),
};
let msg = Arc::make_mut(&mut message);
if let Some(reaction) = msg.reactions.iter_mut().find(|r| r.emoji == self.0.emoji) {
if reaction.me {
if let Some(current_user) = cache.current_user().await? {
if current_user.id == self.0.user_id {
reaction.me = false;
}
}
}
if reaction.count > 1 {
reaction.count -= 1;
} else {
msg.reactions.retain(|e| !(e.emoji == self.0.emoji.clone()));
}
}
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for ReactionRemoveAll {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::REACTION_REMOVE_ALL) {
return Ok(());
}
let mut channel = cache.0.messages.entry(self.channel_id).or_default();
let mut message = match channel.get_mut(&self.message_id) {
Some(message) => message,
None => return Ok(()),
};
let msg = Arc::make_mut(&mut message);
msg.reactions.clear();
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for Ready {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::READY) {
return Ok(());
}
cache.cache_current_user(self.user.clone()).await;
for status in self.guilds.values() {
match status {
GuildStatus::Offline(u) => {
cache.unavailable_guild(u.id).await;
}
GuildStatus::Online(g) => {
cache.cache_guild(g.clone()).await;
}
}
}
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for RoleCreate {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::ROLE_CREATE) {
return Ok(());
}
super::upsert_guild_item(
&cache.0.roles,
self.guild_id,
self.role.id,
self.role.clone(),
)
.await;
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for RoleDelete {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::ROLE_DELETE) {
return Ok(());
}
cache.delete_role(self.role_id).await;
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for RoleUpdate {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::ROLE_UPDATE) {
return Ok(());
}
cache.cache_role(self.guild_id, self.role.clone()).await;
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for TypingStart {
async fn update(&self, _: &InMemoryCache) -> Result<(), InMemoryCacheError> {
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for UnavailableGuild {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::UNAVAILABLE_GUILD) {
return Ok(());
}
cache.0.guilds.remove(&self.id);
cache.0.unavailable_guilds.insert(self.id);
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for UserUpdate {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::USER_UPDATE) {
return Ok(());
}
cache.cache_current_user(self.0.clone()).await;
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for VoiceServerUpdate {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::VOICE_SERVER_UPDATE) {
return Ok(());
}
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for VoiceStateUpdate {
async fn update(&self, cache: &InMemoryCache) -> Result<(), InMemoryCacheError> {
if !guard(cache, EventType::VOICE_STATE_UPDATE) {
return Ok(());
}
cache.cache_voice_state(self.0.clone()).await;
Ok(())
}
}
#[async_trait]
impl UpdateCache<InMemoryCache, InMemoryCacheError> for WebhooksUpdate {
async fn update(&self, _: &InMemoryCache) -> Result<(), InMemoryCacheError> {
Ok(())
}
}