From 06b97132a61cada244f63cdbc03999e9a2406c33 Mon Sep 17 00:00:00 2001 From: GnomedDev Date: Wed, 24 Apr 2024 13:29:15 +0100 Subject: [PATCH] Remove multiple event handler support --- examples/e01_basic_ping_bot/src/main.rs | 4 +- .../src/main.rs | 4 +- examples/e03_struct_utilities/src/main.rs | 4 +- examples/e04_message_builder/src/main.rs | 4 +- examples/e05_sample_bot_structure/src/main.rs | 6 +- examples/e06_env_logging/src/main.rs | 4 +- examples/e07_shard_manager/src/main.rs | 2 +- .../e08_create_message_builder/src/main.rs | 4 +- examples/e09_collectors/src/main.rs | 4 +- examples/e10_gateway_intents/src/main.rs | 6 +- examples/e11_global_data/src/main.rs | 4 +- examples/e12_parallel_loops/src/main.rs | 9 +-- examples/e13_sqlite_database/src/main.rs | 2 +- examples/e14_message_components/src/main.rs | 2 +- examples/testing/src/main.rs | 14 ++-- src/cache/mod.rs | 4 +- src/client/context.rs | 16 ++-- src/client/dispatch.rs | 80 ++++++------------- src/client/event_handler.rs | 11 ++- src/client/mod.rs | 61 +++++++------- src/gateway/bridge/shard_manager.rs | 14 ++-- src/gateway/bridge/shard_queuer.rs | 16 ++-- src/gateway/bridge/shard_runner.rs | 34 ++++---- src/model/channel/attachment.rs | 4 +- src/model/channel/guild_channel.rs | 2 +- src/model/error.rs | 2 +- src/model/guild/mod.rs | 2 +- src/model/guild/partial_guild.rs | 2 +- src/model/user.rs | 4 +- 29 files changed, 150 insertions(+), 175 deletions(-) diff --git a/examples/e01_basic_ping_bot/src/main.rs b/examples/e01_basic_ping_bot/src/main.rs index 24a9b22ced4..86a41bcff5f 100644 --- a/examples/e01_basic_ping_bot/src/main.rs +++ b/examples/e01_basic_ping_bot/src/main.rs @@ -13,7 +13,7 @@ impl EventHandler for Handler { // // Event handlers are dispatched through a threadpool, and so multiple events can be dispatched // simultaneously. - async fn message(&self, ctx: &Context, msg: &Message) { + async fn message(&self, ctx: Context, msg: Message) { if msg.content == "!ping" { // Sending a message can fail, due to a network error, an authentication error, or lack // of permissions to post in the channel, so log to stdout when some error happens, @@ -29,7 +29,7 @@ impl EventHandler for Handler { // Ids, current user data, private channels, and more. // // In this case, just print what the current user's username is. - async fn ready(&self, _: &Context, ready: &Ready) { + async fn ready(&self, _: Context, ready: Ready) { println!("{} is connected!", ready.user.name); } } diff --git a/examples/e02_transparent_guild_sharding/src/main.rs b/examples/e02_transparent_guild_sharding/src/main.rs index 85eca831573..288323e8b29 100644 --- a/examples/e02_transparent_guild_sharding/src/main.rs +++ b/examples/e02_transparent_guild_sharding/src/main.rs @@ -24,7 +24,7 @@ struct Handler; #[async_trait] impl EventHandler for Handler { - async fn message(&self, ctx: &Context, msg: &Message) { + async fn message(&self, ctx: Context, msg: Message) { if msg.content == "!ping" { println!("Shard {}", ctx.shard_id); @@ -34,7 +34,7 @@ impl EventHandler for Handler { } } - async fn ready(&self, _: &Context, ready: &Ready) { + async fn ready(&self, _: Context, ready: Ready) { println!("{} is connected!", ready.user.name); } } diff --git a/examples/e03_struct_utilities/src/main.rs b/examples/e03_struct_utilities/src/main.rs index 7b1e62ad254..e304d3dcd2e 100644 --- a/examples/e03_struct_utilities/src/main.rs +++ b/examples/e03_struct_utilities/src/main.rs @@ -10,7 +10,7 @@ struct Handler; #[async_trait] impl EventHandler for Handler { - async fn message(&self, context: &Context, msg: &Message) { + async fn message(&self, context: Context, msg: Message) { if msg.content == "!messageme" { // If the `utils`-feature is enabled, then model structs will have a lot of useful // methods implemented, to avoid using an often otherwise bulky Context, or even much @@ -27,7 +27,7 @@ impl EventHandler for Handler { } } - async fn ready(&self, _: &Context, ready: &Ready) { + async fn ready(&self, _: Context, ready: Ready) { println!("{} is connected!", ready.user.name); } } diff --git a/examples/e04_message_builder/src/main.rs b/examples/e04_message_builder/src/main.rs index 67e61e53826..1c3fd296d21 100644 --- a/examples/e04_message_builder/src/main.rs +++ b/examples/e04_message_builder/src/main.rs @@ -10,7 +10,7 @@ struct Handler; #[async_trait] impl EventHandler for Handler { - async fn message(&self, context: &Context, msg: &Message) { + async fn message(&self, context: Context, msg: Message) { if msg.content == "!ping" { let channel = match msg.channel_id.to_channel(&context).await { Ok(channel) => channel, @@ -38,7 +38,7 @@ impl EventHandler for Handler { } } - async fn ready(&self, _: &Context, ready: &Ready) { + async fn ready(&self, _: Context, ready: Ready) { println!("{} is connected!", ready.user.name); } } diff --git a/examples/e05_sample_bot_structure/src/main.rs b/examples/e05_sample_bot_structure/src/main.rs index dc9bdc2c52e..76026564090 100644 --- a/examples/e05_sample_bot_structure/src/main.rs +++ b/examples/e05_sample_bot_structure/src/main.rs @@ -13,7 +13,7 @@ struct Handler; #[async_trait] impl EventHandler for Handler { - async fn interaction_create(&self, ctx: &Context, interaction: &Interaction) { + async fn interaction_create(&self, ctx: Context, interaction: Interaction) { if let Interaction::Command(command) = interaction { println!("Received command interaction: {command:#?}"); @@ -22,7 +22,7 @@ impl EventHandler for Handler { "id" => Some(commands::id::run(&command.data.options())), "attachmentinput" => Some(commands::attachmentinput::run(&command.data.options())), "modal" => { - commands::modal::run(ctx, command).await.unwrap(); + commands::modal::run(&ctx, &command).await.unwrap(); None }, _ => Some("not implemented :(".to_string()), @@ -38,7 +38,7 @@ impl EventHandler for Handler { } } - async fn ready(&self, ctx: &Context, ready: &Ready) { + async fn ready(&self, ctx: Context, ready: Ready) { println!("{} is connected!", ready.user.name); let guild_id = GuildId::new( diff --git a/examples/e06_env_logging/src/main.rs b/examples/e06_env_logging/src/main.rs index 944b1f41dfb..0e1a674a29b 100644 --- a/examples/e06_env_logging/src/main.rs +++ b/examples/e06_env_logging/src/main.rs @@ -10,7 +10,7 @@ struct Handler; #[async_trait] impl EventHandler for Handler { - async fn ready(&self, _: &Context, ready: &Ready) { + async fn ready(&self, _: Context, ready: Ready) { // Log at the INFO level. This is a macro from the `tracing` crate. info!("{} is connected!", ready.user.name); } @@ -20,7 +20,7 @@ impl EventHandler for Handler { // Handler doesn't implement Debug here, so we specify to skip that argument. // Context doesn't implement Debug either, so it is also skipped. #[instrument(skip(self, _ctx))] - async fn resume(&self, _ctx: &Context, _resume: &ResumedEvent) { + async fn resume(&self, _ctx: Context, _resume: ResumedEvent) { // Log at the DEBUG level. // // In this example, this will not show up in the logs because DEBUG is diff --git a/examples/e07_shard_manager/src/main.rs b/examples/e07_shard_manager/src/main.rs index 2c004bccc83..53ddcedfd35 100644 --- a/examples/e07_shard_manager/src/main.rs +++ b/examples/e07_shard_manager/src/main.rs @@ -31,7 +31,7 @@ struct Handler; #[async_trait] impl EventHandler for Handler { - async fn ready(&self, _: &Context, ready: &Ready) { + async fn ready(&self, _: Context, ready: Ready) { if let Some(shard) = ready.shard { // Note that array index 0 is 0-indexed, while index 1 is 1-indexed. // diff --git a/examples/e08_create_message_builder/src/main.rs b/examples/e08_create_message_builder/src/main.rs index 7a78d3d0ae6..712713e4e50 100644 --- a/examples/e08_create_message_builder/src/main.rs +++ b/examples/e08_create_message_builder/src/main.rs @@ -11,7 +11,7 @@ struct Handler; #[async_trait] impl EventHandler for Handler { - async fn message(&self, ctx: &Context, msg: &Message) { + async fn message(&self, ctx: Context, msg: Message) { if msg.content == "!hello" { // The create message builder allows you to easily create embeds and messages using a // builder syntax. @@ -43,7 +43,7 @@ impl EventHandler for Handler { } } - async fn ready(&self, _: &Context, ready: &Ready) { + async fn ready(&self, _: Context, ready: Ready) { println!("{} is connected!", ready.user.name); } } diff --git a/examples/e09_collectors/src/main.rs b/examples/e09_collectors/src/main.rs index 1bb5793a51f..a7ecaa0819d 100644 --- a/examples/e09_collectors/src/main.rs +++ b/examples/e09_collectors/src/main.rs @@ -15,11 +15,11 @@ struct Handler; #[async_trait] impl EventHandler for Handler { - async fn ready(&self, _: &Context, ready: &Ready) { + async fn ready(&self, _: Context, ready: Ready) { println!("{} is connected!", ready.user.name); } - async fn message(&self, ctx: &Context, msg: &Message) { + async fn message(&self, ctx: Context, msg: Message) { let mut score = 0u32; let _ = msg.reply(&ctx, "How was that crusty crab called again? 10 seconds time!").await; diff --git a/examples/e10_gateway_intents/src/main.rs b/examples/e10_gateway_intents/src/main.rs index f847435100e..50b22a5e030 100644 --- a/examples/e10_gateway_intents/src/main.rs +++ b/examples/e10_gateway_intents/src/main.rs @@ -10,17 +10,17 @@ struct Handler; #[async_trait] impl EventHandler for Handler { // This event will be dispatched for guilds, but not for direct messages. - async fn message(&self, _ctx: &Context, msg: &Message) { + async fn message(&self, _ctx: Context, msg: Message) { println!("Received message: {}", msg.content); } // As the intents set in this example, this event shall never be dispatched. // Try it by changing your status. - async fn presence_update(&self, _ctx: &Context, _new_data: &Presence) { + async fn presence_update(&self, _ctx: Context, _new_data: Presence) { println!("Presence Update"); } - async fn ready(&self, _: &Context, ready: &Ready) { + async fn ready(&self, _: Context, ready: Ready) { println!("{} is connected!", ready.user.name); } } diff --git a/examples/e11_global_data/src/main.rs b/examples/e11_global_data/src/main.rs index 41f37a13fb4..af5dd4b84fc 100644 --- a/examples/e11_global_data/src/main.rs +++ b/examples/e11_global_data/src/main.rs @@ -21,7 +21,7 @@ struct Handler; #[async_trait] impl EventHandler for Handler { - async fn message(&self, ctx: &Context, msg: &Message) { + async fn message(&self, ctx: Context, msg: Message) { // Since data is located in Context, this means you are able to use it within events! let data = ctx.data::(); @@ -54,7 +54,7 @@ impl EventHandler for Handler { } } - async fn ready(&self, _: &Context, ready: &Ready) { + async fn ready(&self, _: Context, ready: Ready) { println!("{} is connected!", ready.user.name); } } diff --git a/examples/e12_parallel_loops/src/main.rs b/examples/e12_parallel_loops/src/main.rs index 2c00e5ec5ee..1fdf82a8004 100644 --- a/examples/e12_parallel_loops/src/main.rs +++ b/examples/e12_parallel_loops/src/main.rs @@ -17,7 +17,7 @@ struct Handler { #[async_trait] impl EventHandler for Handler { - async fn message(&self, ctx: &Context, msg: &Message) { + async fn message(&self, ctx: Context, msg: Message) { if msg.content.starts_with("!ping") { if let Err(why) = msg.channel_id.say(&ctx.http, "Pong!").await { eprintln!("Error sending message: {why:?}"); @@ -25,13 +25,13 @@ impl EventHandler for Handler { } } - async fn ready(&self, _ctx: &Context, ready: &Ready) { + async fn ready(&self, _ctx: Context, ready: Ready) { println!("{} is connected!", ready.user.name); } // We use the cache_ready event just in case some cache operation is required in whatever use // case you have for this. - async fn cache_ready(&self, ctx: &Context, _guilds: &Vec) { + async fn cache_ready(&self, ctx: Context, _guilds: Vec) { println!("Cache built successfully!"); // We need to check that the loop is not already running when this event triggers, as this @@ -53,10 +53,9 @@ impl EventHandler for Handler { }); // And of course, we can run more than one thread at different timings. - let ctx2 = ctx.clone(); tokio::spawn(async move { loop { - set_activity_to_current_time(&ctx2); + set_activity_to_current_time(&ctx); tokio::time::sleep(Duration::from_secs(60)).await; } }); diff --git a/examples/e13_sqlite_database/src/main.rs b/examples/e13_sqlite_database/src/main.rs index 085d3196478..11e4b500f87 100644 --- a/examples/e13_sqlite_database/src/main.rs +++ b/examples/e13_sqlite_database/src/main.rs @@ -12,7 +12,7 @@ struct Bot { #[async_trait] impl EventHandler for Bot { - async fn message(&self, ctx: &Context, msg: &Message) { + async fn message(&self, ctx: Context, msg: Message) { let user_id = msg.author.id.get() as i64; if let Some(task_description) = msg.content.strip_prefix("~todo add") { diff --git a/examples/e14_message_components/src/main.rs b/examples/e14_message_components/src/main.rs index 8e4c065145d..54bc6401d82 100644 --- a/examples/e14_message_components/src/main.rs +++ b/examples/e14_message_components/src/main.rs @@ -28,7 +28,7 @@ struct Handler; #[async_trait] impl EventHandler for Handler { - async fn message(&self, ctx: &Context, msg: &Message) { + async fn message(&self, ctx: Context, msg: Message) { if msg.content != "animal" { return; } diff --git a/examples/testing/src/main.rs b/examples/testing/src/main.rs index 5069babfcbd..12a7d79d5d9 100644 --- a/examples/testing/src/main.rs +++ b/examples/testing/src/main.rs @@ -9,7 +9,7 @@ mod model_type_sizes; const IMAGE_URL: &str = "https://raw.githubusercontent.com/serenity-rs/serenity/current/logo.png"; const IMAGE_URL_2: &str = "https://rustacean.net/assets/rustlogo.png"; -async fn message(ctx: &Context, msg: &Message) -> Result<(), serenity::Error> { +async fn message(ctx: &Context, msg: Message) -> Result<(), serenity::Error> { let channel_id = msg.channel_id; let guild_id = msg.guild_id.unwrap(); if let Some(_args) = msg.content.strip_prefix("testmessage ") { @@ -235,7 +235,7 @@ async fn message(ctx: &Context, msg: &Message) -> Result<(), serenity::Error> { async fn interaction( ctx: &Context, - interaction: &CommandInteraction, + interaction: CommandInteraction, ) -> Result<(), serenity::Error> { if interaction.data.name == "editattachments" { // Respond with an image @@ -376,13 +376,13 @@ async fn interaction( struct Handler; #[serenity::async_trait] impl EventHandler for Handler { - async fn message(&self, ctx: &Context, msg: &Message) { - message(ctx, msg).await.unwrap(); + async fn message(&self, ctx: Context, msg: Message) { + message(&ctx, msg).await.unwrap(); } - async fn interaction_create(&self, ctx: &Context, i: &Interaction) { + async fn interaction_create(&self, ctx: Context, i: Interaction) { match i { - Interaction::Command(i) => interaction(ctx, i).await.unwrap(), + Interaction::Command(i) => interaction(&ctx, i).await.unwrap(), Interaction::Component(i) => println!("{:#?}", i.data), Interaction::Autocomplete(i) => { i.create_response( @@ -399,7 +399,7 @@ impl EventHandler for Handler { } } - async fn reaction_remove_emoji(&self, _ctx: &Context, removed_reactions: &Reaction) { + async fn reaction_remove_emoji(&self, _ctx: Context, removed_reactions: Reaction) { println!("Got ReactionRemoveEmoji event: {removed_reactions:?}"); } } diff --git a/src/cache/mod.rs b/src/cache/mod.rs index f732357efc8..4a53ea43ece 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -266,7 +266,7 @@ impl Cache { /// #[serenity::async_trait] /// # #[cfg(feature = "client")] /// impl EventHandler for Handler { - /// async fn cache_ready(&self, ctx: &Context, _: &Vec) { + /// async fn cache_ready(&self, ctx: Context, _: Vec) { /// println!("{} unknown members", ctx.cache.unknown_members()); /// } /// } @@ -307,7 +307,7 @@ impl Cache { /// /// #[serenity::async_trait] /// impl EventHandler for Handler { - /// async fn ready(&self, context: &Context, _: &Ready) { + /// async fn ready(&self, context: Context, _: Ready) { /// let guilds = context.cache.guilds().len(); /// /// println!("Guilds in the Cache: {}", guilds); diff --git a/src/client/context.rs b/src/client/context.rs index d911100da32..1bf2f2a2c03 100644 --- a/src/client/context.rs +++ b/src/client/context.rs @@ -114,7 +114,7 @@ impl Context { /// /// #[serenity::async_trait] /// impl EventHandler for Handler { - /// async fn message(&self, ctx: &Context, msg: &Message) { + /// async fn message(&self, ctx: Context, msg: Message) { /// if msg.content == "!online" { /// ctx.online(); /// } @@ -142,7 +142,7 @@ impl Context { /// /// #[serenity::async_trait] /// impl EventHandler for Handler { - /// async fn message(&self, ctx: &Context, msg: &Message) { + /// async fn message(&self, ctx: Context, msg: Message) { /// if msg.content == "!idle" { /// ctx.idle(); /// } @@ -170,7 +170,7 @@ impl Context { /// /// #[serenity::async_trait] /// impl EventHandler for Handler { - /// async fn message(&self, ctx: &Context, msg: &Message) { + /// async fn message(&self, ctx: Context, msg: Message) { /// if msg.content == "!dnd" { /// ctx.dnd(); /// } @@ -198,7 +198,7 @@ impl Context { /// /// #[serenity::async_trait] /// impl EventHandler for Handler { - /// async fn message(&self, ctx: &Context, msg: &Message) { + /// async fn message(&self, ctx: Context, msg: Message) { /// if msg.content == "!invisible" { /// ctx.invisible(); /// } @@ -229,7 +229,7 @@ impl Context { /// /// #[serenity::async_trait] /// impl EventHandler for Handler { - /// async fn message(&self, ctx: &Context, msg: &Message) { + /// async fn message(&self, ctx: Context, msg: Message) { /// if msg.content == "!reset_presence" { /// ctx.reset_presence(); /// } @@ -259,7 +259,7 @@ impl Context { /// /// #[serenity::async_trait] /// impl EventHandler for Handler { - /// async fn message(&self, ctx: &Context, msg: &Message) { + /// async fn message(&self, ctx: Context, msg: Message) { /// let mut args = msg.content.splitn(2, ' '); /// /// if let (Some("~setgame"), Some(game)) = (args.next(), args.next()) { @@ -286,7 +286,7 @@ impl Context { /// /// #[serenity::async_trait] /// impl EventHandler for Handler { - /// async fn ready(&self, ctx: &Context, _: &Ready) { + /// async fn ready(&self, ctx: Context, _: Ready) { /// use serenity::model::user::OnlineStatus; /// /// ctx.set_presence(None, OnlineStatus::Idle); @@ -303,7 +303,7 @@ impl Context { /// /// #[serenity::async_trait] /// impl EventHandler for Handler { - /// async fn ready(&self, context: &Context, _: &Ready) { + /// async fn ready(&self, context: Context, _: Ready) { /// use serenity::gateway::ActivityData; /// use serenity::model::user::OnlineStatus; /// diff --git a/src/client/dispatch.rs b/src/client/dispatch.rs index 6bf6b998fa6..ed973ee8ca1 100644 --- a/src/client/dispatch.rs +++ b/src/client/dispatch.rs @@ -1,14 +1,13 @@ use std::sync::Arc; #[cfg(feature = "gateway")] -use super::event_handler::{EventHandler, RawEventHandler}; +use super::event_handler::InternalEventHandler; use super::{Context, FullEvent}; #[cfg(feature = "cache")] use crate::cache::{Cache, CacheUpdate}; #[cfg(feature = "framework")] use crate::framework::Framework; use crate::internal::prelude::*; -use crate::internal::tokio::spawn_named; use crate::model::channel::ChannelType; use crate::model::event::Event; use crate::model::guild::Member; @@ -41,24 +40,23 @@ macro_rules! update_cache { ($cache:ident, $event:ident) => {}; } -pub(crate) fn dispatch_model( +/// Calls the user's event handlers and the framework handler. +/// +/// This MUST be called from a different task to the recv_event loop, to allow for +/// intra-shard concurrency between the shard loop and event handler. +pub(crate) async fn dispatch_model( event: Event, context: Context, #[cfg(feature = "framework")] framework: Option>, - event_handlers: &[Arc], - raw_event_handlers: &[Arc], + event_handler: Option, ) { - struct DispatchContext { - context: Context, - full_event: FullEvent, - task_name: &'static str, - extra_event: Option, - } - - for raw_handler in raw_event_handlers { - let (handler, context, event) = (Arc::clone(raw_handler), context.clone(), event.clone()); - tokio::spawn(async move { handler.raw_event(context, event).await }); - } + let handler = match event_handler { + Some(InternalEventHandler::Normal(handler)) => Some(handler), + Some(InternalEventHandler::Raw(raw_handler)) => { + return raw_handler.raw_event(context, event).await; + }, + None => None, + }; let (full_event, extra_event) = update_cache_with_event( #[cfg(feature = "cache")] @@ -66,49 +64,21 @@ pub(crate) fn dispatch_model( event, ); - let dispatch_ctx = Arc::new(DispatchContext { - task_name: full_event.snake_case_name(), - context, - full_event, - extra_event, - }); - - for handler in event_handlers { - let handler = Arc::clone(handler); - let dispatch_ctx = Arc::clone(&dispatch_ctx); - - spawn_named(dispatch_ctx.task_name, async move { - let DispatchContext { - context, - full_event, - extra_event, - .. - } = &*dispatch_ctx; - - if let Some(extra_event) = extra_event { - extra_event.dispatch(context, &*handler).await; - } + #[cfg(feature = "framework")] + if let Some(framework) = framework { + if let Some(extra_event) = &extra_event { + framework.dispatch(&context, extra_event).await; + } - full_event.dispatch(context, &*handler).await; - }); + framework.dispatch(&context, &full_event).await; } - #[cfg(feature = "framework")] - if let Some(framework) = framework { - spawn_named("dispatch::framework::dispatch", async move { - let DispatchContext { - context, - full_event, - extra_event, - .. - } = &*dispatch_ctx; - - if let Some(extra_event) = extra_event { - framework.dispatch(context, extra_event).await; - } + if let Some(handler) = handler { + if let Some(extra_event) = extra_event { + extra_event.dispatch(context.clone(), &*handler).await; + } - framework.dispatch(context, full_event).await; - }); + full_event.dispatch(context, &*handler).await; } } diff --git a/src/client/event_handler.rs b/src/client/event_handler.rs index 43c8f850e3d..a980aaf9554 100644 --- a/src/client/event_handler.rs +++ b/src/client/event_handler.rs @@ -1,6 +1,7 @@ use std::collections::VecDeque; #[cfg(feature = "cache")] use std::num::NonZeroU16; +use std::sync::Arc; use async_trait::async_trait; use strum::{EnumCount, IntoStaticStr, VariantNames}; @@ -22,7 +23,7 @@ macro_rules! event_handler { $( $( #[doc = $doc] )* $( #[cfg(feature = $feature)] )? - async fn $method_name(&self, $($context: &Context,)? $( $arg_name: &$arg_type ),*) { + async fn $method_name(&self, $($context: Context,)? $( $arg_name: $arg_type ),*) { // Suppress unused argument warnings #[allow(dropping_references, dropping_copy_types)] drop(( $($context,)? $($arg_name),* )) @@ -66,7 +67,7 @@ macro_rules! event_handler { } /// Runs the given [`EventHandler`]'s code for this event. - pub async fn dispatch(&self, ctx: &Context, handler: &dyn EventHandler) { + pub async fn dispatch(self, ctx: Context, handler: &dyn EventHandler) { match self { $( $( #[cfg(feature = $feature)] )? @@ -484,3 +485,9 @@ pub trait RawEventHandler: Send + Sync { /// Dispatched when any event occurs async fn raw_event(&self, _ctx: Context, _ev: Event) {} } + +#[derive(Clone)] +pub enum InternalEventHandler { + Raw(Arc), + Normal(Arc), +} diff --git a/src/client/mod.rs b/src/client/mod.rs index 865cbfd7e12..edb808544e8 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -36,7 +36,7 @@ use tracing::debug; pub use self::context::Context; pub use self::error::Error as ClientError; #[cfg(feature = "gateway")] -pub use self::event_handler::{EventHandler, FullEvent, RawEventHandler}; +pub use self::event_handler::{EventHandler, FullEvent, InternalEventHandler, RawEventHandler}; #[cfg(feature = "gateway")] use super::gateway::GatewayError; #[cfg(feature = "cache")] @@ -52,6 +52,7 @@ use crate::gateway::{ActivityData, PresenceData}; use crate::gateway::{ShardManager, ShardManagerOptions}; use crate::http::Http; use crate::internal::prelude::*; +use crate::internal::tokio::spawn_named; #[cfg(feature = "gateway")] use crate::model::gateway::GatewayIntents; use crate::model::id::ApplicationId; @@ -73,8 +74,8 @@ pub struct ClientBuilder { framework: Option>, #[cfg(feature = "voice")] voice_manager: Option>, - event_handlers: Vec>, - raw_event_handlers: Vec>, + event_handler: Option>, + raw_event_handler: Option>, presence: PresenceData, } @@ -107,8 +108,8 @@ impl ClientBuilder { framework: None, #[cfg(feature = "voice")] voice_manager: None, - event_handlers: vec![], - raw_event_handlers: vec![], + event_handler: None, + raw_event_handler: None, presence: PresenceData::default(), } } @@ -232,29 +233,30 @@ impl ClientBuilder { where H: EventHandler + 'static, { - self.event_handlers.push(event_handler.into()); - + self.event_handler = Some(event_handler.into()); self } /// Gets the added event handlers. See [`Self::event_handler`] for more info. #[must_use] - pub fn get_event_handlers(&self) -> &[Arc] { - &self.event_handlers + pub fn get_event_handler(&self) -> Option<&Arc> { + self.event_handler.as_ref() } /// Adds an event handler with a single method where all received gateway events will be /// dispatched. - pub fn raw_event_handler(mut self, raw_event_handler: H) -> Self { - self.raw_event_handlers.push(Arc::new(raw_event_handler)); - + pub fn raw_event_handler(mut self, raw_event_handler: impl Into>) -> Self + where + H: RawEventHandler + 'static, + { + self.raw_event_handler = Some(raw_event_handler.into()); self } /// Gets the added raw event handlers. See [`Self::raw_event_handler`] for more info. #[must_use] - pub fn get_raw_event_handlers(&self) -> &[Arc] { - &self.raw_event_handlers + pub fn get_raw_event_handler(&self) -> Option<&Arc> { + self.raw_event_handler.as_ref() } /// Sets the initial activity. @@ -289,21 +291,27 @@ impl IntoFuture for ClientBuilder { let data = self.data.unwrap_or(Arc::new(())); #[cfg(feature = "framework")] let framework = self.framework; - let event_handlers = self.event_handlers; - let raw_event_handlers = self.raw_event_handlers; let intents = self.intents; let presence = self.presence; let http = self.http; + let event_handler = match (self.event_handler, self.raw_event_handler) { + (Some(_), Some(_)) => panic!("Cannot provide both a normal and raw event handlers"), + (Some(h), None) => Some(InternalEventHandler::Normal(h)), + (None, Some(h)) => Some(InternalEventHandler::Raw(h)), + (None, None) => None, + }; + if let Some(ratelimiter) = &http.ratelimiter { - let event_handlers_clone = event_handlers.clone(); - ratelimiter.set_ratelimit_callback(Box::new(move |info| { - for event_handler in &event_handlers_clone { - let info = info.clone(); - let event_handler = Arc::clone(event_handler); - tokio::spawn(async move { event_handler.ratelimit(&info).await }); - } - })); + if let Some(InternalEventHandler::Normal(event_handler)) = &event_handler { + let event_handler = Arc::clone(event_handler); + ratelimiter.set_ratelimit_callback(Box::new(move |info| { + let event_handler = Arc::clone(&event_handler); + spawn_named("ratelimit::dispatch", async move { + event_handler.ratelimit(info).await; + }); + })); + } } #[cfg(feature = "voice")] @@ -329,8 +337,7 @@ impl IntoFuture for ClientBuilder { let framework_cell = Arc::new(OnceLock::new()); let (shard_manager, shard_manager_ret_value) = ShardManager::new(ShardManagerOptions { data: Arc::clone(&data), - event_handlers, - raw_event_handlers, + event_handler, #[cfg(feature = "framework")] framework: Arc::clone(&framework_cell), #[cfg(feature = "voice")] @@ -394,7 +401,7 @@ impl IntoFuture for ClientBuilder { /// /// #[serenity::async_trait] /// impl EventHandler for Handler { -/// async fn message(&self, context: &Context, msg: &Message) { +/// async fn message(&self, context: Context, msg: Message) { /// if msg.content == "!ping" { /// let _ = msg.channel_id.say(&context, "Pong!"); /// } diff --git a/src/gateway/bridge/shard_manager.rs b/src/gateway/bridge/shard_manager.rs index 1d045b617a5..75c0803498e 100644 --- a/src/gateway/bridge/shard_manager.rs +++ b/src/gateway/bridge/shard_manager.rs @@ -16,7 +16,7 @@ use super::VoiceGatewayManager; use super::{ShardId, ShardQueue, ShardQueuer, ShardQueuerMessage, ShardRunnerInfo}; #[cfg(feature = "cache")] use crate::cache::Cache; -use crate::client::{EventHandler, RawEventHandler}; +use crate::client::InternalEventHandler; #[cfg(feature = "framework")] use crate::framework::Framework; use crate::gateway::{ConnectionStage, GatewayError, PresenceData}; @@ -49,7 +49,7 @@ use crate::model::gateway::GatewayIntents; /// use std::env; /// use std::sync::{Arc, OnceLock}; /// -/// use serenity::client::{EventHandler, RawEventHandler}; +/// use serenity::client::{EventHandler, InternalEventHandler, RawEventHandler}; /// use serenity::gateway::{ShardManager, ShardManagerOptions}; /// use serenity::http::Http; /// use serenity::model::gateway::GatewayIntents; @@ -59,7 +59,6 @@ use crate::model::gateway::GatewayIntents; /// struct Handler; /// /// impl EventHandler for Handler {} -/// impl RawEventHandler for Handler {} /// /// # let http: Arc = unimplemented!(); /// let gateway_info = http.get_bot_gateway().await?; @@ -72,8 +71,7 @@ use crate::model::gateway::GatewayIntents; /// /// ShardManager::new(ShardManagerOptions { /// data, -/// event_handlers: vec![event_handler], -/// raw_event_handlers: vec![], +/// event_handler: Some(InternalEventHandler::Normal(event_handler)), /// framework: Arc::new(OnceLock::new()), /// # #[cfg(feature = "voice")] /// # voice_manager: None, @@ -129,8 +127,7 @@ impl ShardManager { let mut shard_queuer = ShardQueuer { data: opt.data, - event_handlers: opt.event_handlers, - raw_event_handlers: opt.raw_event_handlers, + event_handler: opt.event_handler, #[cfg(feature = "framework")] framework: opt.framework, last_start: None, @@ -359,8 +356,7 @@ impl Drop for ShardManager { pub struct ShardManagerOptions { pub data: Arc, - pub event_handlers: Vec>, - pub raw_event_handlers: Vec>, + pub event_handler: Option, #[cfg(feature = "framework")] pub framework: Arc>>, #[cfg(feature = "voice")] diff --git a/src/gateway/bridge/shard_queuer.rs b/src/gateway/bridge/shard_queuer.rs index 278bbb273c8..428ebb952a3 100644 --- a/src/gateway/bridge/shard_queuer.rs +++ b/src/gateway/bridge/shard_queuer.rs @@ -23,7 +23,7 @@ use super::{ }; #[cfg(feature = "cache")] use crate::cache::Cache; -use crate::client::{EventHandler, RawEventHandler}; +use crate::client::InternalEventHandler; #[cfg(feature = "framework")] use crate::framework::Framework; use crate::gateway::{ConnectionStage, PresenceData, Shard, ShardRunnerMessage}; @@ -43,14 +43,11 @@ pub struct ShardQueuer { /// /// [`Client::data`]: crate::Client::data pub data: Arc, - /// A reference to an [`EventHandler`], such as the one given to the [`Client`]. + /// A reference to [`EventHandler`] or [`RawEventHandler`]. /// - /// [`Client`]: crate::Client - pub event_handlers: Vec>, - /// A reference to an [`RawEventHandler`], such as the one given to the [`Client`]. - /// - /// [`Client`]: crate::Client - pub raw_event_handlers: Vec>, + /// [`EventHandler`]: crate::client::EventHandler + /// [`RawEventHandler`]: crate::client::RawEventHandler + pub event_handler: Option, /// A copy of the framework #[cfg(feature = "framework")] pub framework: Arc>>, @@ -226,8 +223,7 @@ impl ShardQueuer { let mut runner = ShardRunner::new(ShardRunnerOptions { data: Arc::clone(&self.data), - event_handlers: self.event_handlers.clone(), - raw_event_handlers: self.raw_event_handlers.clone(), + event_handler: self.event_handler.clone(), #[cfg(feature = "framework")] framework: self.framework.get().cloned(), manager: Arc::clone(&self.manager), diff --git a/src/gateway/bridge/shard_runner.rs b/src/gateway/bridge/shard_runner.rs index e40cbcc48b8..2e64075eff4 100644 --- a/src/gateway/bridge/shard_runner.rs +++ b/src/gateway/bridge/shard_runner.rs @@ -16,7 +16,7 @@ use super::{ShardId, ShardManager, ShardRunnerMessage}; #[cfg(feature = "cache")] use crate::cache::Cache; use crate::client::dispatch::dispatch_model; -use crate::client::{Context, EventHandler, RawEventHandler}; +use crate::client::{Context, InternalEventHandler}; #[cfg(feature = "framework")] use crate::framework::Framework; use crate::gateway::{GatewayError, ReconnectType, Shard, ShardAction}; @@ -28,8 +28,7 @@ use crate::model::event::{Event, GatewayEvent}; /// A runner for managing a [`Shard`] and its respective WebSocket client. pub struct ShardRunner { data: Arc, - event_handlers: Vec>, - raw_event_handlers: Vec>, + event_handler: Option, #[cfg(feature = "framework")] framework: Option>, manager: Arc, @@ -56,8 +55,7 @@ impl ShardRunner { runner_rx: rx, runner_tx: tx, data: opt.data, - event_handlers: opt.event_handlers, - raw_event_handlers: opt.raw_event_handlers, + event_handler: opt.event_handler, #[cfg(feature = "framework")] framework: opt.framework, manager: opt.manager, @@ -120,15 +118,17 @@ impl ShardRunner { if post != pre { self.update_manager().await; - for event_handler in self.event_handlers.clone() { + if let Some(InternalEventHandler::Normal(event_handler)) = &self.event_handler { + let event_handler = Arc::clone(event_handler); let context = self.make_context(); let event = ShardStageUpdateEvent { new: post, old: pre, shard_id: self.shard.shard_info().id, }; + spawn_named("dispatch::event_handler::shard_stage_update", async move { - event_handler.shard_stage_update(&context, &event).await; + event_handler.shard_stage_update(context, event).await; }); } } @@ -172,14 +172,15 @@ impl ShardRunner { if let Some(event) = event { #[cfg(feature = "collector")] self.collectors.lock().expect("poison").retain_mut(|callback| (callback.0)(&event)); - - dispatch_model( - event, - self.make_context(), - #[cfg(feature = "framework")] - self.framework.clone(), - &self.event_handlers, - &self.raw_event_handlers, + spawn_named( + "shard_runner::dispatch", + dispatch_model( + event, + self.make_context(), + #[cfg(feature = "framework")] + self.framework.clone(), + self.event_handler.clone(), + ), ); } @@ -473,8 +474,7 @@ impl ShardRunner { /// Options to be passed to [`ShardRunner::new`]. pub struct ShardRunnerOptions { pub data: Arc, - pub event_handlers: Vec>, - pub raw_event_handlers: Vec>, + pub event_handler: Option, #[cfg(feature = "framework")] pub framework: Option>, pub manager: Arc, diff --git a/src/model/channel/attachment.rs b/src/model/channel/attachment.rs index f1d7b5ce398..3fc57449a5c 100644 --- a/src/model/channel/attachment.rs +++ b/src/model/channel/attachment.rs @@ -105,8 +105,8 @@ impl Attachment { /// #[serenity::async_trait] /// # #[cfg(feature = "client")] /// impl EventHandler for Handler { - /// async fn message(&self, context: &Context, message: &Message) { - /// for attachment in &message.attachments { + /// async fn message(&self, context: Context, message: Message) { + /// for attachment in message.attachments { /// let content = match attachment.download().await { /// Ok(content) => content, /// Err(why) => { diff --git a/src/model/channel/guild_channel.rs b/src/model/channel/guild_channel.rs index 2599c923361..9af665949ab 100644 --- a/src/model/channel/guild_channel.rs +++ b/src/model/channel/guild_channel.rs @@ -629,7 +629,7 @@ impl GuildChannel { /// /// #[serenity::async_trait] /// impl EventHandler for Handler { - /// async fn message(&self, context: &Context, msg: &Message) { + /// async fn message(&self, context: Context, msg: Message) { /// let Some(guild) = msg.guild(&context.cache) else { /// return; /// }; diff --git a/src/model/error.rs b/src/model/error.rs index f1e3c4b5bca..c30c61767e2 100644 --- a/src/model/error.rs +++ b/src/model/error.rs @@ -116,7 +116,7 @@ impl fmt::Display for Minimum { /// #[serenity::async_trait] /// #[cfg(feature = "client")] /// impl EventHandler for Handler { -/// async fn guild_ban_removal(&self, ctx: &Context, guild_id: &GuildId, user: &User) { +/// async fn guild_ban_removal(&self, ctx: Context, guild_id: GuildId, user: User) { /// match guild_id.ban(&ctx.http, user.id, 8, Some("No unbanning people!")).await { /// Ok(()) => { /// // Ban successful. diff --git a/src/model/guild/mod.rs b/src/model/guild/mod.rs index 4b0facdf2e3..68cb05f3c33 100644 --- a/src/model/guild/mod.rs +++ b/src/model/guild/mod.rs @@ -2186,7 +2186,7 @@ impl Guild { /// #[serenity::async_trait] /// #[cfg(all(feature = "cache", feature = "client"))] /// impl EventHandler for Handler { - /// async fn message(&self, ctx: &Context, msg: &Message) { + /// async fn message(&self, ctx: Context, msg: Message) { /// if let Some(guild_id) = msg.guild_id { /// if let Some(guild) = guild_id.to_guild_cached(&ctx.cache) { /// if let Some(role) = guild.role_by_name("role_name") { diff --git a/src/model/guild/partial_guild.rs b/src/model/guild/partial_guild.rs index e6c39d5ad31..a0e8281e35a 100644 --- a/src/model/guild/partial_guild.rs +++ b/src/model/guild/partial_guild.rs @@ -1294,7 +1294,7 @@ impl PartialGuild { /// #[serenity::async_trait] /// #[cfg(all(feature = "cache", feature = "client"))] /// impl EventHandler for Handler { - /// async fn message(&self, ctx: &Context, msg: &Message) { + /// async fn message(&self, ctx: Context, msg: Message) { /// if let Some(guild_id) = msg.guild_id { /// if let Some(guild) = guild_id.to_guild_cached(&ctx.cache) { /// if let Some(role) = guild.role_by_name("role_name") { diff --git a/src/model/user.rs b/src/model/user.rs index 96f093707d7..f1004906be1 100644 --- a/src/model/user.rs +++ b/src/model/user.rs @@ -511,7 +511,7 @@ impl User { /// #[serenity::async_trait] /// # #[cfg(feature = "client")] /// impl EventHandler for Handler { - /// async fn message(&self, context: &Context, msg: &Message) { + /// async fn message(&self, context: Context, msg: Message) { /// if msg.content == "!mytag" { /// let content = format!("Your tag is: {}", msg.author.tag()); /// let _ = msg.channel_id.say(&context.http, &content).await; @@ -628,7 +628,7 @@ impl UserId { /// #[serenity::async_trait] /// # #[cfg(feature = "client")] /// impl EventHandler for Handler { - /// async fn message(&self, ctx: &Context, msg: &Message) { + /// async fn message(&self, ctx: Context, msg: Message) { /// if msg.content == "~help" { /// let builder = CreateMessage::new().content("Helpful info here."); ///