From e28f11d9bb3ccd05fa7b28ca23c76ff0861046ae Mon Sep 17 00:00:00 2001 From: Rafael Bradley Date: Thu, 21 Aug 2025 19:19:02 -0300 Subject: [PATCH] Make permissions checking not depend on the cache --- Cargo.toml | 2 +- src/groups/servers/permissions.rs | 66 ++++++++++++++++++++++--------- src/main.rs | 1 - 3 files changed, 48 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7313c8b..4222937 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ twilight-cache-inmemory = { version = "0.16.0", features = ["permission-calculat twilight-gateway = "0.16.0" twilight-http = { version = "0.16.0", features = ["simd-json"] } twilight-model = "0.16.0" -twilight-util = { version = "0.16.0", features = ["builder"] } +twilight-util = { version = "0.16.0", features = ["builder", "permission-calculator"] } twilight-validate = "0.16.0" unicode-segmentation = "1.12.0" unidecode = "0.3.0" diff --git a/src/groups/servers/permissions.rs b/src/groups/servers/permissions.rs index 535fc17..0499045 100644 --- a/src/groups/servers/permissions.rs +++ b/src/groups/servers/permissions.rs @@ -1,5 +1,9 @@ use async_trait::async_trait; -use twilight_model::guild::Permissions; +use twilight_model::{ + guild::Permissions, + id::{Id, marker::RoleMarker}, +}; +use twilight_util::permission_calculator::PermissionCalculator; use crate::{ framework::commands::{CommandError, context::CommandContext, permissions::Permission}, @@ -31,28 +35,52 @@ pub struct ServerModeratorPermission; impl Permission for ServerModeratorPermission { async fn check(&self, ctx: &CommandContext) -> Result { if let Some(guild_id) = ctx.event.guild_id { - let permissions = ctx + let Some(guild) = ctx.rt.cache.guild(guild_id) else { + log::warn!("Could not get guild for ID {guild_id}!"); + + return Ok(false); + }; + + let member = ctx .rt - .cache - .permissions() - .root(ctx.event.author.id, guild_id); + .http + .guild_member(guild_id, ctx.event.author.id) + .await + .map_err(CommandError::DiscordAPIFailed)? + .model() + .await + .map_err(CommandError::DiscordDeserializationFailed)?; - if let Ok(permissions) = permissions { - let guild = ctx - .rt - .cache - .guild(guild_id) - .ok_or(CommandError::CacheStateInconsistent)?; + let Some(everyone_role) = ctx.rt.cache.role(guild_id.cast()) else { + log::warn!("Could not get @everyone role for guild ID {guild_id}!"); - Ok(permissions - .contains(Permissions::MANAGE_CHANNELS | Permissions::MANAGE_MESSAGES) - || guild.owner_id() == ctx.event.author.id) - } else { - Err(CommandError::CacheStateInconsistent) - } - } else { - Ok(true) + return Ok(false); + }; + + let member_roles: Vec<(Id, Permissions)> = member + .roles + .into_iter() + .filter_map(|role_id| { + ctx.rt + .cache + .role(role_id) + .map(|role| (role.id, role.permissions)) + }) + .collect(); + + let calculator = PermissionCalculator::new( + guild_id, + ctx.event.author.id, + everyone_role.permissions, + &member_roles, + ) + .owner_id(guild.owner_id()); + + return Ok(calculator.root().contains(Permissions::MANAGE_CHANNELS | Permissions::MANAGE_MESSAGES)); } + + // DMs channel, the user has all permissions. + Ok(true) } async fn on_handle_denied(&self, ctx: &CommandContext) -> Result<(), CommandError> { diff --git a/src/main.rs b/src/main.rs index 6c62343..baafd51 100644 --- a/src/main.rs +++ b/src/main.rs @@ -244,7 +244,6 @@ async fn start( .shard(shard) .intent(Intents::GUILDS) .intent(Intents::GUILD_MESSAGES) - .intent(Intents::GUILD_MEMBERS) .intent(Intents::MESSAGE_CONTENT) .command(HelpCommand) .command(CallCommand::new())