use async_trait::async_trait;
pub use error::Error;
use twilight_model::{
channel::{Channel, ChannelType, StageInstance},
gateway::event::Event,
guild::Permissions,
id::{
marker::{
ChannelMarker, EmojiMarker, GuildMarker, MessageMarker, RoleMarker, StageMarker,
StickerMarker, UserMarker,
},
Id,
},
user::CurrentUser,
};
use twilight_util::permission_calculator::PermissionCalculator;
use crate::{
model::{
CachedActivity, CachedAttachment, CachedChannel, CachedEmbed, CachedEmbedField,
CachedEmoji, CachedGuild, CachedMember, CachedMessage, CachedMessageSticker,
CachedPresence, CachedReaction, CachedRole, CachedSticker,
},
Backend,
};
#[allow(clippy::std_instead_of_core)]
mod error {
use thiserror::Error;
use twilight_model::{
channel::Channel,
id::{
marker::{ChannelMarker, GuildMarker, RoleMarker, UserMarker},
Id,
},
};
use crate::{
backend,
model::{CachedChannel, CachedMember},
};
#[derive(Error, Debug)]
pub enum Error<E: backend::Error> {
#[error("An error was returned by the backend:\n{0}")]
Backend(E),
#[error("The DM channel doesn't have any recipients other than the bot itself:\n{0:?}")]
PrivateChannelMissingRecipient(Box<Channel>),
#[error("The thread doesn't have a parent ID:\n{0:?}")]
ThreadMissingParent(Box<CachedChannel>),
#[error("The current user isn't in the cache")]
CurrentUserMissing,
#[error(
"One of the roles of the member to cache isn't in the cache:\nUser ID: {user_id}, \
Role ID: {role_id}"
)]
MemberRoleMissing {
user_id: Id<UserMarker>,
role_id: Id<RoleMarker>,
},
#[error(
"The communication disabled until timestamp of the given member to calculate \
permissions for isn't valid:\n{0:?}"
)]
MemberBadTimeoutTimestamp(Box<CachedMember>),
#[error("The channel to calculate permissions for isn't in the cache:\n{0}")]
PermissionsChannelMissing(Id<ChannelMarker>),
#[error("The guild to calculate permissions for isn't in the cache:\n{0}")]
PermissionsGuildMissing(Id<GuildMarker>),
#[error(
"The member to calculate permissions for isn't in the cache:\nUser ID: {user_id}, \
Guild ID: {guild_id}"
)]
PermissionsMemberMissing {
user_id: Id<UserMarker>,
guild_id: Id<GuildMarker>,
},
#[error(
"The everyone role in the guild to calculate permissions for isn't in the cache:\n{0}"
)]
PermissionsGuildEveryoneRoleMissing(Id<GuildMarker>),
#[error("The given channel to calculate permissions for doesn't have a guild ID:\n{0:?}")]
PermissionsChannelNotInGuild(Box<CachedChannel>),
}
}
#[async_trait]
pub trait Cache: Backend {
#[allow(clippy::too_many_lines)]
async fn update(&self, event: &Event) -> Result<(), Error<Self::Error>> {
match event {
Event::ChannelCreate(channel) => {
self.add_channel(channel).await?;
}
Event::ChannelUpdate(channel) => {
self.add_channel(channel).await?;
}
Event::ChannelDelete(channel) => {
self.delete_channel(channel.id).await?;
}
Event::ThreadCreate(thread) => {
self.add_channel(thread).await?;
}
Event::ThreadUpdate(thread) => {
self.add_channel(thread).await?;
}
Event::ThreadDelete(thread) => {
self.delete_channel(thread.id).await?;
}
Event::GuildCreate(guild) => {
for channel in guild.channels.iter().chain(&guild.threads) {
self.add_channel(channel).await?;
}
for emoji in &guild.emojis {
self.upsert_emoji(CachedEmoji::from_emoji(emoji, guild.id))
.await?;
}
for sticker in &guild.stickers {
self.upsert_sticker(sticker.into()).await?;
}
for member in &guild.members {
self.add_member_roles(member.user.id, member.roles.clone())
.await?;
self.upsert_member(member.into()).await?;
}
for presence in &guild.presences {
self.upsert_presence(presence.into()).await?;
}
for role in &guild.roles {
self.upsert_role(CachedRole::from_role(role.clone(), guild.id))
.await?;
}
for stage in &guild.stage_instances {
self.upsert_stage_instance(stage.clone()).await?;
}
self.upsert_guild(CachedGuild::from(&guild.0)).await?;
}
Event::GuildUpdate(guild) => {
if let Some(mut cached_guild) = self.guild(guild.id).await? {
cached_guild.update(guild);
self.upsert_guild(cached_guild).await?;
}
}
Event::GuildDelete(guild) => {
if !guild.unavailable {
self.delete_guild_channels(guild.id).await?;
self.delete_guild_emojis(guild.id).await?;
self.delete_guild_stickers(guild.id).await?;
self.delete_guild_members(guild.id).await?;
self.delete_guild_presences(guild.id).await?;
self.delete_guild_roles(guild.id).await?;
self.delete_guild_stage_instances(guild.id).await?;
self.delete_guild(guild.id).await?;
}
}
Event::GuildEmojisUpdate(emojis) => {
self.delete_guild_emojis(emojis.guild_id).await?;
for emoji in &emojis.emojis {
self.upsert_emoji(CachedEmoji::from_emoji(emoji, emojis.guild_id))
.await?;
}
}
Event::GuildStickersUpdate(stickers) => {
self.delete_guild_stickers(stickers.guild_id).await?;
for sticker in &stickers.stickers {
self.upsert_sticker(sticker.into()).await?;
}
}
Event::MemberAdd(member) => {
self.add_member_roles(member.user.id, member.roles.clone())
.await?;
self.upsert_member(CachedMember::from(&member.0)).await?;
}
Event::MemberChunk(members) => {
for member in &members.members {
self.add_member_roles(member.user.id, member.roles.clone())
.await?;
self.upsert_member(member.into()).await?;
}
}
Event::MemberUpdate(member) => {
if let Some(mut cached_member) =
self.member(member.guild_id, member.user.id).await?
{
cached_member.update(member);
self.upsert_member(cached_member).await?;
self.delete_member_roles(member.guild_id, member.user.id)
.await?;
self.add_member_roles(member.user.id, member.roles.clone())
.await?;
}
}
Event::MemberRemove(member) => {
self.delete_member(member.user.id, member.guild_id).await?;
self.delete_member_roles(member.guild_id, member.user.id)
.await?;
}
Event::MessageCreate(message) => {
for attachment in message.attachments.clone() {
self.upsert_attachment(CachedAttachment::from_attachment(
attachment, message.id,
))
.await?;
}
for sticker in message.sticker_items.clone() {
self.upsert_message_sticker(CachedMessageSticker::from_sticker(
sticker, message.id,
))
.await?;
}
for embed in message.embeds.clone() {
let fields = embed.fields.clone();
let cached_embed = CachedEmbed::from_embed(embed, message.id);
for field in fields {
self.upsert_embed_field(CachedEmbedField::from_embed_field(
field,
cached_embed.id,
))
.await?;
}
self.upsert_embed(cached_embed).await?;
}
self.upsert_message(CachedMessage::from(&message.0)).await?;
}
Event::MessageUpdate(message) => {
if let Some(mut cached_message) = self.message(message.id).await? {
cached_message.update(message);
if let Some(attachments) = &message.attachments {
self.delete_message_attachments(message.id).await?;
for attachment in attachments.clone() {
self.upsert_attachment(CachedAttachment::from_attachment(
attachment, message.id,
))
.await?;
}
}
if let Some(embeds) = &message.embeds {
let cached_embeds = self.embeds(message.id).await?;
for (embed, _) in cached_embeds {
self.delete_embed_fields(embed.id).await?;
self.delete_embed(embed.id).await?;
}
for embed in embeds.clone() {
let fields = embed.fields.clone();
let cached_embed = CachedEmbed::from_embed(embed, message.id);
for field in fields {
self.upsert_embed_field(CachedEmbedField::from_embed_field(
field,
cached_embed.id,
))
.await?;
}
self.upsert_embed(cached_embed).await?;
}
}
self.upsert_message(cached_message).await?;
}
}
Event::MessageDelete(message) => {
self.remove_message(message.id).await?;
}
Event::MessageDeleteBulk(messages) => {
for message_id in &messages.ids {
self.remove_message(*message_id).await?;
}
}
Event::PresenceUpdate(presence) => {
self.delete_user_activities(presence.user.id()).await?;
for activity in &presence.activities {
self.upsert_activity(CachedActivity::from_activity(
activity,
presence.user.id(),
))
.await?;
}
self.upsert_presence(CachedPresence::from(&presence.0))
.await?;
}
Event::ReactionAdd(reaction) => {
self.upsert_reaction(CachedReaction::from(&reaction.0))
.await?;
}
Event::ReactionRemove(reaction) => {
self.delete_reaction(
reaction.message_id,
reaction.user_id,
reaction.emoji.clone(),
)
.await?;
}
Event::ReactionRemoveEmoji(reaction) => {
self.delete_message_reactions_by_emoji(reaction.message_id, reaction.emoji.clone())
.await?;
}
Event::ReactionRemoveAll(reaction) => {
self.delete_message_reactions(reaction.message_id).await?;
}
Event::Ready(ready) => {
self.set_current_user(ready.user.clone()).await?;
}
Event::UserUpdate(user) => {
self.set_current_user(user.0.clone()).await?;
}
Event::RoleCreate(role) => {
self.upsert_role(CachedRole::from_role(role.role.clone(), role.guild_id))
.await?;
}
Event::RoleUpdate(role) => {
self.upsert_role(CachedRole::from_role(role.role.clone(), role.guild_id))
.await?;
}
Event::RoleDelete(role) => {
self.delete_role(role.role_id).await?;
}
Event::StageInstanceCreate(stage) => {
self.upsert_stage_instance(stage.clone().0).await?;
}
Event::StageInstanceUpdate(stage) => {
self.upsert_stage_instance(stage.clone().0).await?;
}
Event::StageInstanceDelete(stage) => {
self.delete_stage_instance(stage.id).await?;
}
_ => {}
}
Ok(())
}
async fn self_channel_permissions(
&self,
channel_id: Id<ChannelMarker>,
) -> Result<Permissions, Error<Self::Error>> {
let current_user_id = self.current_user().await?.id;
self.channel_permissions(current_user_id, channel_id).await
}
async fn self_guild_permissions(
&self,
guild_id: Id<GuildMarker>,
) -> Result<Permissions, Error<Self::Error>> {
let current_user_id = self.current_user().await?.id;
self.guild_permissions(guild_id, current_user_id).await
}
async fn channel_permissions(
&self,
user_id: Id<UserMarker>,
channel_id: Id<ChannelMarker>,
) -> Result<Permissions, Error<Self::Error>> {
let channel = self
.channel(channel_id)
.await?
.ok_or(Error::PermissionsChannelMissing(channel_id))?;
let guild_id = channel
.guild_id
.ok_or_else(|| Error::PermissionsChannelNotInGuild(Box::new(channel.clone())))?;
self.permissions(guild_id, user_id, Some(channel)).await
}
async fn guild_permissions(
&self,
guild_id: Id<GuildMarker>,
user_id: Id<UserMarker>,
) -> Result<Permissions, Error<Self::Error>> {
self.permissions(guild_id, user_id, None).await
}
#[doc(hidden)]
async fn permissions(
&self,
guild_id: Id<GuildMarker>,
user_id: Id<UserMarker>,
cached_channel: Option<CachedChannel>,
) -> Result<Permissions, Error<Self::Error>> {
let guild = self
.guild(guild_id)
.await?
.ok_or(Error::PermissionsGuildMissing(guild_id))?;
let everyone_role = self
.role(guild_id.cast())
.await?
.ok_or(Error::PermissionsGuildEveryoneRoleMissing(guild_id))?;
let roles: Vec<_> = self
.member_roles(user_id, guild_id)
.await?
.iter()
.map(|role| (role.id, role.permissions))
.collect();
let calculator =
PermissionCalculator::new(guild_id, user_id, everyone_role.permissions, &roles)
.owner_id(guild.owner_id);
let permissions = if let Some(channel) = cached_channel {
calculator.in_channel(
channel.kind,
&channel.permission_overwrites.unwrap_or_default(),
)
} else {
calculator.root()
};
let member = self
.member(guild_id, user_id)
.await?
.ok_or(Error::PermissionsMemberMissing { user_id, guild_id })?;
if !permissions.contains(Permissions::ADMINISTRATOR)
&& member
.communication_disabled()
.map_err(|_err| Error::MemberBadTimeoutTimestamp(Box::new(member)))?
{
Ok(permissions
.intersection(Permissions::VIEW_CHANNEL | Permissions::READ_MESSAGE_HISTORY))
} else {
Ok(permissions)
}
}
async fn current_user(&self) -> Result<CurrentUser, Error<Self::Error>>;
async fn channel(
&self,
channel_id: Id<ChannelMarker>,
) -> Result<Option<CachedChannel>, Error<Self::Error>>;
async fn private_channel(
&self,
recipient_id: Id<UserMarker>,
) -> Result<Option<Id<ChannelMarker>>, Error<Self::Error>>;
async fn guild(
&self,
guild_id: Id<GuildMarker>,
) -> Result<Option<CachedGuild>, Error<Self::Error>>;
async fn emoji(
&self,
emoji_id: Id<EmojiMarker>,
) -> Result<Option<CachedEmoji>, Error<Self::Error>>;
async fn sticker(
&self,
sticker_id: Id<StickerMarker>,
) -> Result<Option<CachedSticker>, Error<Self::Error>>;
async fn member(
&self,
guild_id: Id<GuildMarker>,
user_id: Id<UserMarker>,
) -> Result<Option<CachedMember>, Error<Self::Error>>;
async fn message(
&self,
message_id: Id<MessageMarker>,
) -> Result<Option<CachedMessage>, Error<Self::Error>>;
async fn embeds(
&self,
message_id: Id<MessageMarker>,
) -> Result<Vec<(CachedEmbed, Vec<CachedEmbedField>)>, Error<Self::Error>> {
let mut embeds = vec![];
let cached_embeds = self.cached_embeds(message_id).await?;
for embed in cached_embeds {
let fields = self.embed_fields(embed.id).await?;
embeds.push((embed, fields));
}
Ok(embeds)
}
async fn attachments(
&self,
message_id: Id<MessageMarker>,
) -> Result<Vec<CachedAttachment>, Error<Self::Error>>;
async fn stickers(
&self,
message_id: Id<MessageMarker>,
) -> Result<Vec<CachedMessageSticker>, Error<Self::Error>>;
async fn reactions(
&self,
message_id: Id<MessageMarker>,
) -> Result<Vec<CachedReaction>, Error<Self::Error>>;
async fn role(&self, role_id: Id<RoleMarker>)
-> Result<Option<CachedRole>, Error<Self::Error>>;
async fn member_roles(
&self,
user_id: Id<UserMarker>,
guild_id: Id<GuildMarker>,
) -> Result<Vec<CachedRole>, Error<Self::Error>>;
async fn stage_instance(
&self,
stage_id: Id<StageMarker>,
) -> Result<Option<StageInstance>, Error<Self::Error>>;
#[doc(hidden)]
async fn add_channel(&self, channel: &Channel) -> Result<(), Error<Self::Error>> {
if channel.kind == ChannelType::Private {
let recipient_user_id = self.private_channel_recipient(channel).await?;
self.upsert_private_channel(channel.id, recipient_user_id)
.await?;
} else {
self.upsert_channel(CachedChannel::from(channel)).await?;
}
Ok(())
}
#[doc(hidden)]
async fn private_channel_recipient(
&self,
channel: &Channel,
) -> Result<Id<UserMarker>, Error<Self::Error>> {
let current_user_id = self.current_user().await?.id;
let recipient_user_id = channel
.recipients
.as_ref()
.and_then(|recipients| recipients.iter().find(|user| user.id == current_user_id))
.ok_or_else(|| Error::PrivateChannelMissingRecipient(Box::new(channel.clone())))?
.id;
Ok(recipient_user_id)
}
#[doc(hidden)]
async fn add_member_roles(
&self,
user_id: Id<UserMarker>,
role_ids: Vec<Id<RoleMarker>>,
) -> Result<(), Error<Self::Error>> {
for role_id in role_ids {
let mut role = self
.role(role_id)
.await?
.ok_or(Error::MemberRoleMissing { user_id, role_id })?;
role.user_id = Some(user_id);
self.upsert_role(role).await?;
}
Ok(())
}
#[doc(hidden)]
async fn remove_message(
&self,
message_id: Id<MessageMarker>,
) -> Result<(), Error<Self::Error>> {
let embeds = self.embeds(message_id).await?;
for (embed, _) in embeds {
self.delete_embed_fields(embed.id).await?;
self.delete_embed(embed.id).await?;
}
self.delete_message_attachments(message_id).await?;
self.delete_message_reactions(message_id).await?;
self.delete_message_stickers(message_id).await?;
self.delete_message(message_id).await?;
Ok(())
}
}