Skip to content

Commit

Permalink
hopefully be presentable
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesbt365 committed Oct 22, 2024
1 parent 1cb6f93 commit 52dd607
Showing 1 changed file with 91 additions and 59 deletions.
150 changes: 91 additions & 59 deletions src/dispatch/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,41 @@
use crate::serenity_prelude as serenity;

/// Used in `user_permissions`, if a user is passed to that function, their permissions will be
/// returned, in DMs the return value with be `Permissions::all()`.
struct PermissionsInfo {
/// The Permissions of the user, if requested.
user_permissions: Option<serenity::Permissions>,
/// The Permissions of the bot, if requested.
bot_permissions: Option<serenity::Permissions>,
}

/// Retrieves user permissions in the given channel. If unknown, returns None. If in DMs, returns
/// `Permissions::all()`.
async fn user_permissions<U, E>(
async fn users_permissions<U, E>(
ctx: crate::Context<'_, U, E>,
user_id: Option<serenity::UserId>,
bot_id: Option<serenity::UserId>,
) -> Option<(Option<serenity::Permissions>, Option<serenity::Permissions>)> {
) -> Option<PermissionsInfo> {
let guild_id = ctx.guild_id();
let channel_id = ctx.channel_id();
let guild_id = match guild_id {
Some(x) => x,
// no permission checks in DMs
None => {
return Some((
Some(serenity::Permissions::all()),
Some(serenity::Permissions::all()),
))
}
// No permission checks in DMs.
let Some(guild_id) = guild_id else {
return Some(PermissionsInfo {
user_permissions: Some(serenity::Permissions::all()),
bot_permissions: Some(serenity::Permissions::all()),
});
};

match ctx {
crate::Context::Application(ctx) => {
// This should be present on all interactions within a guild. But discord can be a bit
// funny sometimes, so lets be safe.
if let Some(member) = &ctx.interaction.member {
return Some((member.permissions, ctx.interaction.app_permissions));
return Some(PermissionsInfo {
user_permissions: member.permissions,
bot_permissions: ctx.interaction.app_permissions,
});
}
}
crate::Context::Prefix(_) => {}
Expand Down Expand Up @@ -63,82 +72,103 @@ async fn user_permissions<U, E>(
None
};

get_user_permissions(ctx, &channel, user_member.as_ref(), bot_member.as_ref()).await
}

/// Fetches the users permissions, If the cache feature is enabled it will be checked first.
async fn get_user_permissions<U, E>(
ctx: crate::Context<'_, U, E>,
channel: &serenity::GuildChannel,
user: Option<&serenity::Member>,
bot: Option<&serenity::Member>,
) -> Option<PermissionsInfo> {
#[cfg(feature = "cache")]
let cached_perms = {
ctx.cache().guild(guild_id).map(|guild| {
(
user_member
.as_ref()
.map(|m| guild.user_permissions_in(&channel, m)),
bot_member
.as_ref()
.map(|m| guild.user_permissions_in(&channel, m)),
)
})
let Some(permissions) = cached_guild(ctx, channel, user, bot) else {
return fetch_guild(ctx, channel, user, bot).await;
};

#[cfg(feature = "cache")]
let users_permissions = if let Some(users_permissions) = cached_perms {
users_permissions
} else {
let partial_guild = guild_id
.to_partial_guild(ctx.serenity_context())
.await
.ok()?;
(
user_member.map(|m| partial_guild.user_permissions_in(&channel, &m)),
bot_member.map(|m| partial_guild.user_permissions_in(&channel, &m)),
)
};
return Some(permissions);

#[cfg(not(feature = "cache"))]
let partial_guild = guild_id
.to_partial_guild(ctx.serenity_context())
.await
.ok()?;
#[cfg(not(feature = "cache"))]
let users_permissions = (
user_member.map(|m| partial_guild.user_permissions_in(&channel, &m)),
bot_member.map(|m| partial_guild.user_permissions_in(&channel, &m)),
);
fetch_guild(ctx, channel, user, bot).await
}

#[cfg(feature = "cache")]
/// Checks the cache for the guild, returning the permissions if present.
fn cached_guild<U, E>(
ctx: crate::Context<'_, U, E>,
channel: &serenity::GuildChannel,
user: Option<&serenity::Member>,
bot: Option<&serenity::Member>,
) -> Option<PermissionsInfo> {
ctx.guild().map(|guild| {
let user_permissions = user.map(|m| guild.user_permissions_in(channel, m));
let bot_permissions = bot.map(|m| guild.user_permissions_in(channel, m));

PermissionsInfo {
user_permissions,
bot_permissions,
}
})
}

/// Fetches the partial guild from http, returning the permissions if available.
async fn fetch_guild<U, E>(
ctx: crate::Context<'_, U, E>,
channel: &serenity::GuildChannel,
user: Option<&serenity::Member>,
bot: Option<&serenity::Member>,
) -> Option<PermissionsInfo> {
let partial_guild = channel.guild_id.to_partial_guild(ctx.http()).await.ok()?;

let user_permissions = user.map(|m| partial_guild.user_permissions_in(channel, m));
let bot_permissions = bot.map(|m| partial_guild.user_permissions_in(channel, m));

Some(users_permissions)
Some(PermissionsInfo {
user_permissions,
bot_permissions,
})
}

/// Retrieves the set of permissions that are lacking, relative to the given required permission set
///
/// Returns None if permissions couldn't be retrieved.
async fn missing_permissions<U, E>(
ctx: crate::Context<'_, U, E>,
user: (serenity::UserId, serenity::Permissions),
bot: (serenity::UserId, serenity::Permissions),
user_id: serenity::UserId,
user_permissions: serenity::Permissions,
bot_id: serenity::UserId,
bot_permissions: serenity::Permissions,
) -> Option<(serenity::Permissions, serenity::Permissions)> {
// If both user and bot are None, return empty permissions
if user.1.is_empty() && bot.1.is_empty() {
if user_permissions.is_empty() && bot_permissions.is_empty() {
return Some((
serenity::Permissions::empty(),
serenity::Permissions::empty(),
));
}

let user_id = match user.1.is_empty() {
let user_id = match user_permissions.is_empty() {
true => None,
false => Some(user.0),
false => Some(user_id),
};

let bot_id = match bot.1.is_empty() {
let bot_id = match bot_permissions.is_empty() {
true => None,
false => Some(bot.0),
false => Some(bot_id),
};

// Fetch permissions, returning None if an error occurred
let (user_perms, bot_perms) = user_permissions(ctx, user_id, bot_id).await?;
let permissions = users_permissions(ctx, user_id, bot_id).await?;

let user_missing_perms = user_perms
.map(|permissions| user.1 - permissions)
let user_missing_perms = permissions
.user_permissions
.map(|permissions| user_permissions - permissions)
.unwrap_or_default();
let bot_missing_perms = bot_perms
.map(|permissions| user.1 - permissions)
let bot_missing_perms = permissions
.bot_permissions
.map(|permissions| bot_permissions - permissions)
.unwrap_or_default();

Some((user_missing_perms, bot_missing_perms))
Expand Down Expand Up @@ -201,8 +231,10 @@ async fn check_permissions_and_cooldown_single<'a, U, E>(
// Make sure that user has required permissions
if let Some((user_missing_permissions, bot_missing_permissions)) = missing_permissions(
ctx,
(ctx.author().id, cmd.required_permissions),
(ctx.framework().bot_id, cmd.required_bot_permissions),
ctx.author().id,
cmd.required_permissions,
ctx.framework().bot_id,
cmd.required_bot_permissions,
)
.await
{
Expand Down

0 comments on commit 52dd607

Please sign in to comment.