Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/current' into reworked-permiss…
Browse files Browse the repository at this point in the history
…ion-checks
  • Loading branch information
jamesbt365 committed Nov 13, 2024
2 parents b49f06f + d416114 commit 4e1af94
Show file tree
Hide file tree
Showing 15 changed files with 647 additions and 292 deletions.
812 changes: 535 additions & 277 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ indexmap = "2.2.6"
[dependencies.serenity]
default-features = false
features = ["builder", "client", "gateway", "model", "utils", "collector", "framework"]
version = "0.12.2"
version = "0.12.3"

[dev-dependencies]
# For the examples
Expand Down
2 changes: 1 addition & 1 deletion examples/basic_structure/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ async fn main() {
Duration::from_secs(3600),
))),
additional_prefixes: vec![
poise::Prefix::Literal("hey bot"),
poise::Prefix::Literal("hey bot,"),
poise::Prefix::Literal("hey bot"),
],
..Default::default()
},
Expand Down
2 changes: 1 addition & 1 deletion macros/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions macros/src/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ pub struct CommandArgs {
category: Option<String>,
custom_data: Option<syn::Expr>,

manual_cooldowns: Option<bool>,

// In seconds
global_cooldown: Option<u64>,
user_cooldown: Option<u64>,
Expand Down Expand Up @@ -280,6 +282,7 @@ fn generate_command(mut inv: Invocation) -> Result<proc_macro2::TokenStream, dar
let description = wrap_option_to_string(inv.description.as_ref());
let category = wrap_option_to_string(inv.args.category.as_ref());

let manual_cooldowns = wrap_option(inv.args.manual_cooldowns);
let cooldown_config = generate_cooldown_config(&inv.args);

let default_member_permissions = &inv.default_member_permissions;
Expand Down Expand Up @@ -354,6 +357,7 @@ fn generate_command(mut inv: Invocation) -> Result<proc_macro2::TokenStream, dar
description_localizations: #description_localizations,
help_text: #help_text,
hide_in_help: #hide_in_help,
manual_cooldowns: #manual_cooldowns,
cooldowns: std::sync::Mutex::new(::poise::Cooldowns::new()),
cooldown_config: #cooldown_config,
reuse_response: #reuse_response,
Expand Down
5 changes: 4 additions & 1 deletion macros/src/command/prefix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ pub fn generate_prefix_action(inv: &Invocation) -> Result<proc_macro2::TokenStre
error,
))?;

if !ctx.framework.options.manual_cooldowns {
let is_framework_cooldown = !ctx.command.manual_cooldowns
.unwrap_or_else(|| ctx.framework.options.manual_cooldowns);

if is_framework_cooldown {
ctx.command.cooldowns.lock().unwrap().start_cooldown(ctx.cooldown_context());
}

Expand Down
10 changes: 8 additions & 2 deletions macros/src/command/slash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,10 @@ pub fn generate_slash_action(inv: &Invocation) -> Result<proc_macro2::TokenStrea
#( (#param_names: #param_types), )*
).await.map_err(|error| error.to_framework_error(ctx))?;

if !ctx.framework.options.manual_cooldowns {
let is_framework_cooldown = !ctx.command.manual_cooldowns
.unwrap_or_else(|| ctx.framework.options.manual_cooldowns);

if is_framework_cooldown {
ctx.command.cooldowns.lock().unwrap().start_cooldown(ctx.cooldown_context());
}

Expand Down Expand Up @@ -200,7 +203,10 @@ pub fn generate_context_menu_action(
Ok(quote::quote! {
<#param_type as ::poise::ContextMenuParameter<_, _>>::to_action(|ctx, value| {
Box::pin(async move {
if !ctx.framework.options.manual_cooldowns {
let is_framework_cooldown = !ctx.command.manual_cooldowns
.unwrap_or_else(|| ctx.framework.options.manual_cooldowns);

if is_framework_cooldown {
ctx.command.cooldowns.lock().unwrap().start_cooldown(ctx.cooldown_context());
}

Expand Down
2 changes: 1 addition & 1 deletion macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ for example for command-specific help (i.e. `~help command_name`). Escape newlin
- `reuse_response`: After the first response, post subsequent responses as edits to the initial message (prefix only)
## Cooldown
- `manual_cooldowns`: Allows overriding the framework's built-in cooldowns tracking without affecting other commands.
- `global_cooldown`: Minimum duration in seconds between invocations, globally
- `user_cooldown`: Minimum duration in seconds between invocations, per user
- `guild_cooldown`: Minimum duration in seconds between invocations, per guild
Expand Down
20 changes: 18 additions & 2 deletions src/framework/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,13 @@ impl<U: Send + Sync, E: Send + Sync> serenity::Framework for Framework<U, E> {
self.shard_manager = Some(client.shard_manager.clone());

if self.options.initialize_owners {
if let Err(e) = insert_owners_from_http(&client.http, &mut self.options.owners).await {
if let Err(e) = insert_owners_from_http(
&client.http,
&mut self.options.owners,
&self.options.initialized_team_roles,
)
.await
{
tracing::warn!("Failed to insert owners from HTTP: {e}");
}
}
Expand Down Expand Up @@ -242,6 +248,7 @@ fn message_content_intent_sanity_check<U, E>(
pub async fn insert_owners_from_http(
http: &serenity::Http,
owners: &mut std::collections::HashSet<serenity::UserId>,
initialized_teams: &Option<Vec<serenity::TeamMemberRole>>,
) -> Result<(), serenity::Error> {
let application_info = http.get_current_application_info().await?;

Expand All @@ -256,7 +263,16 @@ pub async fn insert_owners_from_http(
continue;
}

if let TeamMemberRole::Admin | TeamMemberRole::Developer = member.role {
// Default configuration.
let Some(initialized_teams) = initialized_teams else {
if let TeamMemberRole::Admin | TeamMemberRole::Developer = member.role {
owners.insert(member.user.id);
}
continue;
};

// s has specified the teams they want to be considered "Owner".
if initialized_teams.iter().any(|r| *r == member.role) {
owners.insert(member.user.id);
}
}
Expand Down
54 changes: 54 additions & 0 deletions src/prefix_argument/argument_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,57 @@ impl<'a> PopArgumentHack<'a, serenity::Attachment> for &PhantomData<serenity::At
Ok((args, attachment_index + 1, attachment))
}
}

/// Macro to allow for using mentions in snowflake types
macro_rules! snowflake_pop_argument {
($type:ty, $parse_fn:ident, $error_type:ident) => {
/// Error thrown when the user enters a string that cannot be parsed correctly.
#[derive(Default, Debug)]
pub struct $error_type {
#[doc(hidden)]
pub __non_exhaustive: (),
}

impl std::error::Error for $error_type {}
impl std::fmt::Display for $error_type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(concat!(
"Enter a valid ",
stringify!($error_type),
" ID or a mention."
))
}
}

#[async_trait::async_trait]
impl<'a> PopArgumentHack<'a, $type> for &PhantomData<$type> {
async fn pop_from(
self,
args: &'a str,
attachment_index: usize,
ctx: &serenity::Context,
msg: &serenity::Message,
) -> Result<
(&'a str, usize, $type),
(Box<dyn std::error::Error + Send + Sync>, Option<String>),
> {
let (args, string) =
pop_string(args).map_err(|_| (TooFewArguments::default().into(), None))?;

if let Some(parsed_id) = string
.parse()
.ok()
.or_else(|| serenity::utils::$parse_fn(&string))
{
Ok((args.trim_start(), attachment_index, parsed_id))
} else {
Err(($error_type::default().into(), Some(string)))
}
}
}
};
}

snowflake_pop_argument!(serenity::UserId, parse_user_mention, InvalidUserId);
snowflake_pop_argument!(serenity::ChannelId, parse_channel_mention, InvalidChannelId);
snowflake_pop_argument!(serenity::RoleId, parse_role_mention, InvalidRoleId);
7 changes: 4 additions & 3 deletions src/reply/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ impl CreateReply {
}

/// Add an attachment.
///
/// This will not have an effect in a slash command's initial response!
pub fn attachment(mut self, attachment: serenity::CreateAttachment) -> Self {
self.attachments.push(attachment);
self
Expand Down Expand Up @@ -152,7 +150,7 @@ impl CreateReply {
let crate::CreateReply {
content,
embeds,
attachments: _, // no support for attachment edits in serenity yet
attachments,
components,
ephemeral: _, // can't edit ephemerality in retrospect
allowed_mentions,
Expand All @@ -168,6 +166,9 @@ impl CreateReply {
if let Some(allowed_mentions) = allowed_mentions {
builder = builder.allowed_mentions(allowed_mentions);
}
for attachment in attachments {
builder = builder.new_attachment(attachment);
}

builder.embeds(embeds)
}
Expand Down
5 changes: 5 additions & 0 deletions src/structs/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ pub struct Command<U, E> {
/// Multiline description with detailed usage instructions. Displayed in the command specific
/// help: `~help command_name`
pub help_text: Option<String>,
/// if `true`, disables automatic cooldown handling before this commands invocation.
///
/// Will override [`crate::FrameworkOptions::manual_cooldowns`] allowing manual cooldowns
/// on select commands.
pub manual_cooldowns: Option<bool>,
/// Handles command cooldowns. Mainly for framework internal use
pub cooldowns: std::sync::Mutex<crate::CooldownTracker>,
/// Configuration for the [`crate::CooldownTracker`]
Expand Down
2 changes: 1 addition & 1 deletion src/structs/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ context_methods! {
/// Like [`Self::say`], but formats the message as a reply to the user's command
/// message.
///
/// Equivalent to `.send(|b| b.content("...").reply(true))`.
/// Equivalent to `.send(CreateReply::default().content("...").reply(true))`.
///
/// Only has an effect in prefix context, because slash command responses are always
/// formatted as a reply.
Expand Down
4 changes: 2 additions & 2 deletions src/structs/framework_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ pub enum FrameworkError<'a, U, E> {
ctx: crate::Context<'a, U, E>,
},
/// Command was invoked but the bot is lacking the permissions specified in
/// [`crate::Command::required_permissions`]
/// [`crate::Command::required_bot_permissions`]
#[non_exhaustive]
MissingBotPermissions {
/// Which permissions in particular the bot is lacking for this command
Expand All @@ -105,7 +105,7 @@ pub enum FrameworkError<'a, U, E> {
ctx: crate::Context<'a, U, E>,
},
/// Command was invoked but the user is lacking the permissions specified in
/// [`crate::Command::required_bot_permissions`]
/// [`crate::Command::required_permissions`]
#[non_exhaustive]
MissingUserPermissions {
/// List of permissions that the user is lacking. May be None if retrieving the user's
Expand Down
8 changes: 8 additions & 0 deletions src/structs/framework_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ pub struct FrameworkOptions<U, E> {
///
/// True by default.
pub initialize_owners: bool,
/// If set and [`Self::initialize_owners`] is `true`, the selected teams will be initialized
/// using the results of [`serenity::Http::get_current_application_info()`].
///
/// When set to `None`, only users with the Developer and Admin roles are initialized.
///
/// None by default.
pub initialized_team_roles: Option<Vec<serenity::TeamMemberRole>>,
// #[non_exhaustive] forbids struct update syntax for ?? reason
#[doc(hidden)]
pub __non_exhaustive: (),
Expand Down Expand Up @@ -117,6 +124,7 @@ where
prefix_options: Default::default(),
owners: Default::default(),
initialize_owners: true,
initialized_team_roles: None,
__non_exhaustive: (),
}
}
Expand Down

0 comments on commit 4e1af94

Please sign in to comment.