-
Notifications
You must be signed in to change notification settings - Fork 117
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: jamesbt365 <[email protected]>
- Loading branch information
1 parent
3f1dac6
commit ea082eb
Showing
9 changed files
with
255 additions
and
76 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
//! Module for calculating permission checks for commands | ||
|
||
use crate::serenity_prelude as serenity; | ||
|
||
mod application; | ||
mod prefix; | ||
|
||
/// Simple POD type to hold the results of permission lookups. | ||
struct PermissionsInfo { | ||
/// The Permissions of the author, if requested. | ||
author_permissions: Option<serenity::Permissions>, | ||
/// The Permissions of the bot, if requested. | ||
bot_permissions: Option<serenity::Permissions>, | ||
} | ||
|
||
impl PermissionsInfo { | ||
/// Returns the fake permissions info from a DM. | ||
fn dm_permissions() -> Self { | ||
Self { | ||
author_permissions: Some(serenity::Permissions::dm_permissions()), | ||
bot_permissions: Some(serenity::Permissions::dm_permissions()), | ||
} | ||
} | ||
} | ||
|
||
/// Retrieves the permissions for the context author and the bot. | ||
async fn get_author_and_bot_permissions<U, E>( | ||
ctx: crate::Context<'_, U, E>, | ||
skip_author: bool, | ||
skip_bot: bool, | ||
) -> Option<PermissionsInfo> { | ||
// No permission checks in DMs. | ||
let Some(guild_id) = ctx.guild_id() else { | ||
return Some(PermissionsInfo::dm_permissions()); | ||
}; | ||
|
||
match ctx { | ||
crate::Context::Application(ctx) => { | ||
Some(application::get_author_and_bot_permissions(ctx.interaction)) | ||
} | ||
crate::Context::Prefix(ctx) => { | ||
prefix::get_author_and_bot_permissions(ctx, guild_id, skip_author, skip_bot).await | ||
} | ||
} | ||
} | ||
|
||
/// Retrieves the set of permissions that are lacking, relative to the given required permission set | ||
/// | ||
/// Returns None if permissions couldn't be retrieved. | ||
pub(super) async fn calculate_missing<U, E>( | ||
ctx: crate::Context<'_, U, E>, | ||
author_required_permissions: serenity::Permissions, | ||
bot_required_permissions: serenity::Permissions, | ||
) -> Option<(serenity::Permissions, serenity::Permissions)> { | ||
// If both user and bot are None, return empty permissions | ||
if author_required_permissions.is_empty() && bot_required_permissions.is_empty() { | ||
return Some(( | ||
serenity::Permissions::empty(), | ||
serenity::Permissions::empty(), | ||
)); | ||
} | ||
|
||
// Fetch permissions, returning None if an error occurred | ||
let permissions = get_author_and_bot_permissions( | ||
ctx, | ||
author_required_permissions.is_empty(), | ||
bot_required_permissions.is_empty(), | ||
) | ||
.await?; | ||
|
||
let author_missing_perms = permissions | ||
.author_permissions | ||
.map(|permissions| author_required_permissions - permissions) | ||
.unwrap_or_default(); | ||
|
||
let bot_missing_perms = permissions | ||
.bot_permissions | ||
.map(|permissions| bot_required_permissions - permissions) | ||
.unwrap_or_default(); | ||
|
||
Some((author_missing_perms, bot_missing_perms)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
//! Application command permissions calculation | ||
use crate::serenity_prelude as serenity; | ||
|
||
use super::PermissionsInfo; | ||
|
||
/// Gets the permissions of the ctx author and the bot. | ||
pub(super) fn get_author_and_bot_permissions( | ||
interaction: &serenity::CommandInteraction, | ||
) -> PermissionsInfo { | ||
let err = "member is Some if interaction is in guild"; | ||
let author_member = interaction.member.as_ref().expect(err); | ||
|
||
let err = "should always be some as inside interaction"; | ||
let author_permissions = author_member.permissions.expect(err); | ||
|
||
let err = "should always be some according to discord docs"; | ||
let bot_permissions = interaction.app_permissions.expect(err); | ||
|
||
PermissionsInfo { | ||
author_permissions: Some(author_permissions), | ||
bot_permissions: Some(bot_permissions), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
//! The cache variant of prefix permissions calculation | ||
|
||
use crate::{serenity_prelude as serenity, PrefixContext}; | ||
|
||
use crate::dispatch::permissions::PermissionsInfo; | ||
|
||
/// Gets the permissions of the ctx author and the bot. | ||
pub(in crate::dispatch::permissions) async fn get_author_and_bot_permissions<U, E>( | ||
ctx: PrefixContext<'_, U, E>, | ||
guild_id: serenity::GuildId, | ||
skip_author: bool, | ||
skip_bot: bool, | ||
) -> Option<PermissionsInfo> { | ||
// Should only fail if the guild is not cached, which is fair to bail on. | ||
let guild = ctx.cache().guild(guild_id)?; | ||
|
||
let author_permissions = if skip_author { | ||
None | ||
} else { | ||
let err_msg = "should only fail if the guild is not cached"; | ||
Some(ctx.msg.author_permissions(ctx.cache()).expect(err_msg)) | ||
}; | ||
|
||
let bot_permissions = if skip_bot { | ||
None | ||
} else { | ||
let channel_id = ctx.channel_id(); | ||
let bot_user_id = ctx.framework.bot_id(); | ||
Some(get_bot_permissions(&guild, channel_id, bot_user_id)?) | ||
}; | ||
|
||
Some(PermissionsInfo { | ||
author_permissions, | ||
bot_permissions, | ||
}) | ||
} | ||
|
||
/// Gets the permissions for the bot. | ||
fn get_bot_permissions( | ||
guild: &serenity::Guild, | ||
channel_id: serenity::ChannelId, | ||
bot_id: serenity::UserId, | ||
) -> Option<serenity::Permissions> { | ||
// Should never fail, as the bot member is always cached | ||
let bot_member = guild.members.get(&bot_id)?; | ||
|
||
if let Some(channel) = guild.channels.get(&channel_id) { | ||
Some(guild.user_permissions_in(channel, bot_member)) | ||
} else if let Some(thread) = guild.threads.iter().find(|th| th.id == channel_id) { | ||
let err = "parent id should always be Some for thread"; | ||
let parent_channel_id = thread.parent_id.expect(err); | ||
|
||
let parent_channel = guild.channels.get(&parent_channel_id)?; | ||
Some(guild.user_permissions_in(parent_channel, bot_member)) | ||
} else { | ||
// The message was either: | ||
// - Sent in a guild with broken caching | ||
// - Not set in a channel or thread? | ||
tracing::warn!("Could not find channel/thread ({channel_id}) for permissions check in cache for guild: {}", guild.id); | ||
None | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
//! The cache variant of prefix permissions calculation | ||
|
||
use crate::{serenity_prelude as serenity, PrefixContext}; | ||
|
||
use crate::dispatch::permissions::PermissionsInfo; | ||
|
||
/// Gets the permissions of the ctx author and the bot. | ||
pub(in crate::dispatch::permissions) async fn get_author_and_bot_permissions<U, E>( | ||
ctx: PrefixContext<'_, U, E>, | ||
guild_id: serenity::GuildId, | ||
skip_author: bool, | ||
skip_bot: bool, | ||
) -> Option<PermissionsInfo> { | ||
let http = ctx.http(); | ||
let guild = guild_id.to_partial_guild(http).await.ok()?; | ||
let guild_channel = { | ||
let channel = ctx.http().get_channel(ctx.channel_id()).await.ok()?; | ||
channel.guild().expect("channel should be a guild channel") | ||
}; | ||
|
||
let bot_permissions = if skip_bot { | ||
None | ||
} else { | ||
let bot_member = guild.id.member(http, ctx.framework.bot_id).await.ok()?; | ||
Some(guild.user_permissions_in(&guild_channel, &bot_member)) | ||
}; | ||
|
||
let author_permissions = if skip_author { | ||
None | ||
} else { | ||
let err = "should always be Some in MessageCreateEvent"; | ||
let author_member = ctx.msg.member.as_ref().expect(err); | ||
Some(guild.partial_member_permissions_in(&guild_channel, ctx.author().id, author_member)) | ||
}; | ||
|
||
Some(PermissionsInfo { | ||
author_permissions, | ||
bot_permissions, | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
//! Prefix command permissions calculation | ||
|
||
#[cfg(feature = "cache")] | ||
mod cache; | ||
#[cfg(not(feature = "cache"))] | ||
mod http; | ||
|
||
#[cfg(feature = "cache")] | ||
pub(super) use cache::get_author_and_bot_permissions; | ||
|
||
#[cfg(not(feature = "cache"))] | ||
pub(super) use http::get_author_and_bot_permissions; |
Oops, something went wrong.