Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

create_application_commands can now take any IntoIterator: not just &[] #298

Open
wants to merge 8 commits into
base: next
Choose a base branch
from
2 changes: 1 addition & 1 deletion examples/basic_structure/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ async fn main() {
// Enforce command checks even for owners (enforced by default)
// Set to true to bypass checks, which is useful for testing
skip_checks_for_owners: false,
event_handler: |_ctx, event, _framework, _data| {
event_handler: |_framework, event| {
Box::pin(async move {
println!(
"Got an event in event handler: {:?}",
Expand Down
11 changes: 5 additions & 6 deletions examples/event_handler/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,7 @@ async fn main() {
})
})
.options(poise::FrameworkOptions {
event_handler: |ctx, event, framework, data| {
Box::pin(event_handler(ctx, event, framework, data))
},
event_handler: |framework, event| Box::pin(event_handler(framework, event)),
..Default::default()
})
.build();
Expand All @@ -46,11 +44,12 @@ async fn main() {
}

async fn event_handler(
ctx: &serenity::Context,
framework: poise::FrameworkContext<'_, Data, Error>,
event: &serenity::FullEvent,
_framework: poise::FrameworkContext<'_, Data, Error>,
data: &Data,
) -> Result<(), Error> {
let data = framework.user_data;
let ctx = framework.serenity_context;

match event {
serenity::FullEvent::Ready { data_about_bot, .. } => {
println!("Logged in as {}", data_about_bot.user.name);
Expand Down
2 changes: 1 addition & 1 deletion examples/feature_showcase/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ async fn main() {
],
prefix_options: poise::PrefixFrameworkOptions {
prefix: Some("~".into()),
non_command_message: Some(|_, _, msg| {
non_command_message: Some(|_, msg| {
Box::pin(async move {
println!("non command message!: {}", msg.content);
Ok(())
Expand Down
6 changes: 4 additions & 2 deletions examples/feature_showcase/modal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,15 @@ pub async fn component_modal(ctx: crate::Context<'_>) -> Result<(), Error> {

ctx.send(reply).await?;

while let Some(mci) = serenity::ComponentInteractionCollector::new(ctx.serenity_context())
let serenity_ctx = ctx.serenity_context();
while let Some(mci) = serenity::ComponentInteractionCollector::new(serenity_ctx)
.timeout(std::time::Duration::from_secs(120))
.filter(move |mci| mci.data.custom_id == "open_modal")
.await
{
let data =
poise::execute_modal_on_component_interaction::<MyModal>(ctx, mci, None, None).await?;
poise::execute_modal_on_component_interaction::<MyModal>(serenity_ctx, mci, None, None)
.await?;
println!("Got data: {:?}", data);
}
Ok(())
Expand Down
4 changes: 2 additions & 2 deletions examples/manual_dispatch/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ impl serenity::EventHandler for Handler {
// FrameworkContext contains all data that poise::Framework usually manages
let shard_manager = (*self.shard_manager.lock().unwrap()).clone().unwrap();
let framework_data = poise::FrameworkContext {
bot_id: serenity::UserId::new(846453852164587620),
serenity_context: &ctx,
options: &self.options,
user_data: &(),
shard_manager: &shard_manager,
};

let event = serenity::FullEvent::Message { new_message };
poise::dispatch_event(framework_data, &ctx, event).await;
poise::dispatch_event(framework_data, event).await;
}

// For slash commands or edit tracking to work, forward interaction_create and message_update
Expand Down
2 changes: 1 addition & 1 deletion macros/src/command/prefix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ pub fn generate_prefix_action(inv: &Invocation) -> Result<proc_macro2::TokenStre
Ok(quote::quote! {
|ctx| Box::pin(async move {
let ( #( #param_idents, )* .. ) = ::poise::parse_prefix_args!(
ctx.serenity_context, ctx.msg, ctx.args, 0 =>
ctx.serenity_context(), ctx.msg, ctx.args, 0 =>
#( #param_specs, )*
#wildcard_arg
).await.map_err(|(error, input)| poise::FrameworkError::new_argument_parse(
Expand Down
2 changes: 1 addition & 1 deletion macros/src/command/slash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ pub fn generate_slash_action(inv: &Invocation) -> Result<proc_macro2::TokenStrea
#[allow(clippy::needless_question_mark)]

let ( #( #param_identifiers, )* ) = ::poise::parse_slash_args!(
ctx.serenity_context, ctx.interaction, ctx.args =>
ctx.serenity_context(), ctx.interaction, ctx.args =>
#( (#param_names: #param_types), )*
).await.map_err(|error| error.to_framework_error(ctx))?;

Expand Down
8 changes: 5 additions & 3 deletions src/builtins/help.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Contains the built-in help command and surrounding infrastructure

use crate::{serenity_prelude as serenity, CreateReply};
use std::fmt::Write as _;
use std::{borrow::Cow, fmt::Write as _};

/// Optional configuration for how the help message from [`help()`] looks
pub struct HelpConfiguration<'a> {
Expand Down Expand Up @@ -85,7 +85,9 @@ impl TwoColumnList {
}

/// Get the prefix from options
pub(super) async fn get_prefix_from_options<U, E>(ctx: crate::Context<'_, U, E>) -> Option<String> {
pub(super) async fn get_prefix_from_options<U, E>(
ctx: crate::Context<'_, U, E>,
) -> Option<Cow<'static, str>> {
let options = &ctx.framework().options().prefix_options;
match &options.prefix {
Some(fixed_prefix) => Some(fixed_prefix.clone()),
Expand Down Expand Up @@ -155,7 +157,7 @@ async fn help_single_command<U, E>(
// None can happen if the prefix is dynamic, and the callback
// fails due to help being invoked with slash or context menu
// commands. Not sure there's a better way to handle this.
None => String::from("<prefix>"),
None => Cow::Borrowed("<prefix>"),
};
invocations.push(format!("`{}{}`", prefix, command.name));
if subprefix.is_none() {
Expand Down
14 changes: 7 additions & 7 deletions src/builtins/pretty_help.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Contains a built-in help command and surrounding infrastructure that uses embeds.

use crate::{serenity_prelude as serenity, CreateReply};
use std::fmt::Write as _;
use std::{borrow::Cow, fmt::Write as _};

/// Optional configuration for how the help message from [`pretty_help()`] looks
pub struct PrettyHelpConfiguration<'a> {
Expand Down Expand Up @@ -82,7 +82,7 @@ async fn pretty_help_all_commands<U, E>(

for cmd in cmds {
let name = cmd.context_menu_name.as_deref().unwrap_or(&cmd.name);
let prefix = format_cmd_prefix(cmd, &options_prefix);
let prefix = format_cmd_prefix(cmd, options_prefix.as_deref());

if let Some(description) = cmd.description.as_deref() {
writeln!(buffer, "{}{}`: *{}*", prefix, name, description).ok();
Expand All @@ -93,7 +93,7 @@ async fn pretty_help_all_commands<U, E>(
if config.show_subcommands {
for sbcmd in &cmd.subcommands {
let name = sbcmd.context_menu_name.as_deref().unwrap_or(&sbcmd.name);
let prefix = format_cmd_prefix(sbcmd, &options_prefix);
let prefix = format_cmd_prefix(sbcmd, options_prefix.as_deref());

if let Some(description) = sbcmd.description.as_deref() {
writeln!(buffer, "> {}{}`: *{}*", prefix, name, description).ok();
Expand Down Expand Up @@ -128,11 +128,11 @@ async fn pretty_help_all_commands<U, E>(
}

/// Figures out which prefix a command should have
fn format_cmd_prefix<U, E>(cmd: &crate::Command<U, E>, options_prefix: &Option<String>) -> String {
fn format_cmd_prefix<U, E>(cmd: &crate::Command<U, E>, options_prefix: Option<&str>) -> String {
if cmd.slash_action.is_some() {
"`/".into()
} else if cmd.prefix_action.is_some() {
format!("`{}", options_prefix.as_deref().unwrap_or_default())
format!("`{}", options_prefix.unwrap_or_default())
} else if cmd.context_menu_action.is_some() {
match cmd.context_menu_action {
Some(crate::ContextMenuCommandAction::Message(_)) => "Message menu: `".into(),
Expand Down Expand Up @@ -188,7 +188,7 @@ async fn pretty_help_single_command<U, E>(
.await
// This can happen if the prefix is dynamic, and the callback fails
// due to help being invoked with slash or context menu commands.
.unwrap_or_else(|| String::from("<prefix>"));
.unwrap_or(Cow::Borrowed("<prefix>"));
invocations.push(format!("`{}{}`", prefix, command.name));
subprefix = subprefix.or(Some(format!("> `{}{}`", prefix, command.name)));
}
Expand Down Expand Up @@ -248,7 +248,7 @@ async fn pretty_help_single_command<U, E>(
.subcommands
.iter()
.map(|sbcmd| {
let prefix = format_cmd_prefix(sbcmd, &subprefix); // i have no idea about this really
let prefix = format_cmd_prefix(sbcmd, subprefix.as_deref()); // i have no idea about this really
let name = sbcmd.context_menu_name.as_deref().unwrap_or(&sbcmd.name);
if let Some(description) = sbcmd.description.as_deref() {
format!("> {}{}`: *{} *", prefix, name, description)
Expand Down
17 changes: 9 additions & 8 deletions src/builtins/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ use crate::serenity_prelude as serenity;
/// serenity::Command::set_global_commands(ctx, create_commands).await?;
/// # Ok(()) }
/// ```
pub fn create_application_commands<U, E>(
commands: &[crate::Command<U, E>],
pub fn create_application_commands<'a, U: 'a, E: 'a>(
commands: impl IntoIterator<Item = &'a crate::Command<U, E>>,
) -> Vec<serenity::CreateCommand> {
/// We decided to extract context menu commands recursively, despite the subcommand hierarchy
/// not being preserved. Because it's more confusing to just silently discard context menu
Expand All @@ -35,8 +35,9 @@ pub fn create_application_commands<U, E>(
}
}

let mut commands_builder = Vec::with_capacity(commands.len());
for command in commands {
let command_iter = commands.into_iter();
let mut commands_builder = Vec::with_capacity(command_iter.size_hint().0);
for command in command_iter {
if let Some(slash_command) = command.create_as_slash_command() {
commands_builder.push(slash_command);
}
Expand All @@ -49,9 +50,9 @@ pub fn create_application_commands<U, E>(
///
/// Thin wrapper around [`create_application_commands`] that funnels the returned builder into
/// [`serenity::Command::set_global_commands`].
pub async fn register_globally<U, E>(
pub async fn register_globally<'a, U: 'a, E: 'a>(
http: impl AsRef<serenity::Http>,
commands: &[crate::Command<U, E>],
commands: impl IntoIterator<Item = &'a crate::Command<U, E>>,
) -> Result<(), serenity::Error> {
let builder = create_application_commands(commands);
serenity::Command::set_global_commands(http, builder).await?;
Expand All @@ -62,9 +63,9 @@ pub async fn register_globally<U, E>(
///
/// Thin wrapper around [`create_application_commands`] that funnels the returned builder into
/// [`serenity::GuildId::set_commands`].
pub async fn register_in_guild<U, E>(
pub async fn register_in_guild<'a, U: 'a, E: 'a>(
http: impl AsRef<serenity::Http>,
commands: &[crate::Command<U, E>],
commands: impl IntoIterator<Item = &'a crate::Command<U, E>>,
guild_id: serenity::GuildId,
) -> Result<(), serenity::Error> {
let builder = create_application_commands(commands);
Expand Down
2 changes: 1 addition & 1 deletion src/dispatch/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ async fn check_permissions_and_cooldown_single<'a, U, E>(
}

// Before running any pre-command checks, make sure the bot has the permissions it needs
match missing_permissions(ctx, ctx.framework().bot_id, cmd.required_bot_permissions).await {
match missing_permissions(ctx, ctx.framework().bot_id(), cmd.required_bot_permissions).await {
Some(missing_permissions) if missing_permissions.is_empty() => {}
Some(missing_permissions) => {
return Err(crate::FrameworkError::MissingBotPermissions {
Expand Down
29 changes: 16 additions & 13 deletions src/dispatch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ pub use slash::*;

use crate::serenity_prelude as serenity;

// TODO: integrate serenity::Context in here? Every place where FrameworkContext is passed is also
// passed serenity::Context
/// A view into data stored by [`crate::Framework`]
pub struct FrameworkContext<'a, U, E> {
/// User ID of this bot
/// Serenity's context
pub serenity_context: &'a serenity::Context,
/// User ID of this bot, available through serenity_context if cache is enabled.
#[cfg(not(feature = "cache"))]
pub bot_id: serenity::UserId,
/// Framework configuration
pub options: &'a crate::FrameworkOptions<U, E>,
Expand All @@ -32,6 +33,16 @@ impl<U, E> Clone for FrameworkContext<'_, U, E> {
}
}
impl<'a, U, E> FrameworkContext<'a, U, E> {
/// Returns the user ID of the bot.
pub fn bot_id(&self) -> serenity::UserId {
#[cfg(feature = "cache")]
let bot_id = self.serenity_context.cache.current_user().id;
#[cfg(not(feature = "cache"))]
let bot_id = self.bot_id;

bot_id
}

/// Returns the stored framework options, including commands.
///
/// This function exists for API compatiblity with [`crate::Framework`]. On this type, you can
Expand Down Expand Up @@ -61,7 +72,6 @@ impl<'a, U, E> FrameworkContext<'a, U, E> {
/// Central event handling function of this library
pub async fn dispatch_event<U: Send + Sync, E>(
framework: crate::FrameworkContext<'_, U, E>,
ctx: &serenity::Context,
event: serenity::FullEvent,
) {
match &event {
Expand All @@ -71,7 +81,6 @@ pub async fn dispatch_event<U: Send + Sync, E>(
let trigger = crate::MessageDispatchTrigger::MessageCreate;
if let Err(error) = prefix::dispatch_message(
framework,
ctx,
new_message,
trigger,
&invocation_data,
Expand Down Expand Up @@ -101,7 +110,6 @@ pub async fn dispatch_event<U: Send + Sync, E>(
};
if let Err(error) = prefix::dispatch_message(
framework,
ctx,
&msg,
trigger,
&invocation_data,
Expand All @@ -123,7 +131,7 @@ pub async fn dispatch_event<U: Send + Sync, E>(
.unwrap()
.process_message_delete(*deleted_message_id);
if let Some(bot_response) = bot_response {
if let Err(e) = bot_response.delete(ctx).await {
if let Err(e) = bot_response.delete(framework.serenity_context).await {
tracing::warn!("failed to delete bot response: {}", e);
}
}
Expand All @@ -136,7 +144,6 @@ pub async fn dispatch_event<U: Send + Sync, E>(
let mut parent_commands = Vec::new();
if let Err(error) = slash::dispatch_interaction(
framework,
ctx,
interaction,
&std::sync::atomic::AtomicBool::new(false),
&invocation_data,
Expand All @@ -155,7 +162,6 @@ pub async fn dispatch_event<U: Send + Sync, E>(
let mut parent_commands = Vec::new();
if let Err(error) = slash::dispatch_autocomplete(
framework,
ctx,
interaction,
&std::sync::atomic::AtomicBool::new(false),
&invocation_data,
Expand All @@ -172,12 +178,9 @@ pub async fn dispatch_event<U: Send + Sync, E>(

// Do this after the framework's Ready handling, so that get_user_data() doesnt
// potentially block infinitely
if let Err(error) =
(framework.options.event_handler)(ctx, &event, framework, framework.user_data).await
{
if let Err(error) = (framework.options.event_handler)(framework, &event).await {
let error = crate::FrameworkError::EventHandler {
error,
ctx,
event: &event,
framework,
};
Expand Down
Loading
Loading