diff --git a/README.md b/README.md index dcd91883cc4..d72e94cc420 100644 --- a/README.md +++ b/README.md @@ -43,21 +43,28 @@ docs. A basic ping-pong bot looks like: ```rust -use std::env; +use std::sync::Arc; use serenity::async_trait; -use serenity::model::channel::Message; +use serenity::gateway::client::FullEvent; use serenity::prelude::*; struct Handler; #[async_trait] impl EventHandler for Handler { - async fn message(&self, ctx: &Context, msg: &Message) { - if msg.content == "!ping" { - if let Err(why) = msg.channel_id.say(&ctx.http, "Pong!").await { - println!("Error sending message: {why:?}"); - } + async fn dispatch(&self, ctx: &Context, event: &FullEvent) { + match event { + FullEvent::Message { + new_message, .. + } => { + if new_message.content == "!ping" { + if let Err(why) = new_message.channel_id.say(&ctx.http, "Pong!").await { + println!("Error sending message: {why:?}"); + } + } + }, + _ => {}, } } } @@ -65,15 +72,16 @@ impl EventHandler for Handler { #[tokio::main] async fn main() { // Login with a bot token from the environment - let token = env::var("DISCORD_TOKEN").expect("Expected a token in the environment"); + let token = + Token::from_env("DISCORD_TOKEN").expect("Expected a valid token in the environment"); // Set gateway intents, which decides what events the bot will be notified about let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::DIRECT_MESSAGES | GatewayIntents::MESSAGE_CONTENT; // Create a new instance of the Client, logging in as a bot. - let mut client = Client::builder(&token, intents) - .event_handler(Handler) + let mut client = Client::builder(token, intents) + .event_handler(Arc::new(Handler)) .await .expect("Error creating client"); diff --git a/examples/e01_basic_ping_bot/src/main.rs b/examples/e01_basic_ping_bot/src/main.rs index 4f5594375e0..cb28c96fcd6 100644 --- a/examples/e01_basic_ping_bot/src/main.rs +++ b/examples/e01_basic_ping_bot/src/main.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use serenity::async_trait; use serenity::gateway::client::FullEvent; use serenity::prelude::*; @@ -54,8 +56,10 @@ async fn main() { // Create a new instance of the Client, logging in as a bot. This will automatically prepend // your bot token with "Bot ", which is a requirement by Discord for bot users. - let mut client = - Client::builder(token, intents).event_handler(Handler).await.expect("Err creating client"); + let mut client = Client::builder(token, intents) + .event_handler(Arc::new(Handler)) + .await + .expect("Err creating client"); // Finally, start a single shard, and start listening to events. // diff --git a/examples/e02_transparent_guild_sharding/src/main.rs b/examples/e02_transparent_guild_sharding/src/main.rs index cec2a922e2d..78011d020ae 100644 --- a/examples/e02_transparent_guild_sharding/src/main.rs +++ b/examples/e02_transparent_guild_sharding/src/main.rs @@ -1,4 +1,6 @@ #![expect(clippy::collapsible_if)] +use std::sync::Arc; + use serenity::async_trait; use serenity::gateway::client::FullEvent; use serenity::prelude::*; @@ -51,8 +53,10 @@ async fn main() { let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::DIRECT_MESSAGES | GatewayIntents::MESSAGE_CONTENT; - let mut client = - Client::builder(token, intents).event_handler(Handler).await.expect("Err creating client"); + let mut client = Client::builder(token, intents) + .event_handler(Arc::new(Handler)) + .await + .expect("Err creating client"); // The total number of shards to use. The "current shard number" of a shard - that is, the // shard it is assigned to - is indexed at 0, while the total shard count is indexed at 1. diff --git a/examples/e03_struct_utilities/src/main.rs b/examples/e03_struct_utilities/src/main.rs index 5b6ec331099..4b525d5ab73 100644 --- a/examples/e03_struct_utilities/src/main.rs +++ b/examples/e03_struct_utilities/src/main.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use serenity::async_trait; use serenity::builder::CreateMessage; use serenity::gateway::client::FullEvent; @@ -47,8 +49,10 @@ async fn main() { let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::DIRECT_MESSAGES | GatewayIntents::MESSAGE_CONTENT; - let mut client = - Client::builder(token, intents).event_handler(Handler).await.expect("Err creating client"); + let mut client = Client::builder(token, intents) + .event_handler(Arc::new(Handler)) + .await + .expect("Err creating client"); if let Err(why) = client.start().await { println!("Client error: {why:?}"); diff --git a/examples/e04_message_builder/src/main.rs b/examples/e04_message_builder/src/main.rs index c600d93a442..f2208f2b62b 100644 --- a/examples/e04_message_builder/src/main.rs +++ b/examples/e04_message_builder/src/main.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use serenity::async_trait; use serenity::gateway::client::FullEvent; use serenity::prelude::*; @@ -56,8 +58,10 @@ async fn main() { let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::DIRECT_MESSAGES | GatewayIntents::MESSAGE_CONTENT; - let mut client = - Client::builder(token, intents).event_handler(Handler).await.expect("Err creating client"); + let mut client = Client::builder(token, intents) + .event_handler(Arc::new(Handler)) + .await + .expect("Err creating client"); if let Err(why) = client.start().await { println!("Client error: {why:?}"); diff --git a/examples/e05_sample_bot_structure/src/main.rs b/examples/e05_sample_bot_structure/src/main.rs index f2eab860827..7292d1a4188 100644 --- a/examples/e05_sample_bot_structure/src/main.rs +++ b/examples/e05_sample_bot_structure/src/main.rs @@ -1,6 +1,7 @@ mod commands; use std::env; +use std::sync::Arc; use serenity::async_trait; use serenity::builder::{CreateInteractionResponse, CreateInteractionResponseMessage}; @@ -94,7 +95,7 @@ async fn main() { // Build our client. let mut client = Client::builder(token, GatewayIntents::empty()) - .event_handler(Handler) + .event_handler(Arc::new(Handler)) .await .expect("Error creating client"); diff --git a/examples/e06_env_logging/src/main.rs b/examples/e06_env_logging/src/main.rs index 53aa3850681..7c3a5671a44 100644 --- a/examples/e06_env_logging/src/main.rs +++ b/examples/e06_env_logging/src/main.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use serenity::async_trait; use serenity::prelude::*; use tracing::{debug, error, info, instrument}; @@ -50,8 +52,10 @@ async fn main() { | GatewayIntents::DIRECT_MESSAGES | GatewayIntents::MESSAGE_CONTENT; - let mut client = - Client::builder(token, intents).event_handler(Handler).await.expect("Err creating client"); + let mut client = Client::builder(token, intents) + .event_handler(Arc::new(Handler)) + .await + .expect("Err creating client"); if let Err(why) = client.start().await { error!("Client error: {:?}", why); diff --git a/examples/e07_shard_manager/src/main.rs b/examples/e07_shard_manager/src/main.rs index ce06ab4d482..48da33e7c06 100644 --- a/examples/e07_shard_manager/src/main.rs +++ b/examples/e07_shard_manager/src/main.rs @@ -19,6 +19,7 @@ //! Note that it may take a minute or more for a latency to be recorded or to update, depending on //! how often Discord tells the client to send a heartbeat. #![expect(clippy::collapsible_if)] +use std::sync::Arc; use std::time::Duration; use serenity::async_trait; @@ -54,8 +55,10 @@ async fn main() { let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::DIRECT_MESSAGES | GatewayIntents::MESSAGE_CONTENT; - let mut client = - Client::builder(token, intents).event_handler(Handler).await.expect("Err creating client"); + let mut client = Client::builder(token, intents) + .event_handler(Arc::new(Handler)) + .await + .expect("Err creating client"); // Here we get a DashMap of of the shards' status that we move into a new thread. let runners = client.shard_manager.runners.clone(); diff --git a/examples/e08_create_message_builder/src/main.rs b/examples/e08_create_message_builder/src/main.rs index 1bfb09fef86..66021d22274 100644 --- a/examples/e08_create_message_builder/src/main.rs +++ b/examples/e08_create_message_builder/src/main.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use serenity::async_trait; use serenity::builder::{CreateAttachment, CreateEmbed, CreateEmbedFooter, CreateMessage}; use serenity::gateway::client::FullEvent; @@ -62,8 +64,10 @@ async fn main() { let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::DIRECT_MESSAGES | GatewayIntents::MESSAGE_CONTENT; - let mut client = - Client::builder(token, intents).event_handler(Handler).await.expect("Err creating client"); + let mut client = Client::builder(token, intents) + .event_handler(Arc::new(Handler)) + .await + .expect("Err creating client"); if let Err(why) = client.start().await { println!("Client error: {why:?}"); diff --git a/examples/e09_collectors/src/main.rs b/examples/e09_collectors/src/main.rs index f8e383849d0..e4c05be4e09 100644 --- a/examples/e09_collectors/src/main.rs +++ b/examples/e09_collectors/src/main.rs @@ -1,6 +1,7 @@ //! This example will showcase the beauty of collectors. They allow to await messages or reactions //! from a user in the middle of a control flow, one being a command. use std::collections::HashSet; +use std::sync::Arc; use std::time::Duration; use serenity::async_trait; @@ -163,8 +164,10 @@ async fn main() { | GatewayIntents::MESSAGE_CONTENT | GatewayIntents::GUILD_MESSAGE_REACTIONS; - let mut client = - Client::builder(token, intents).event_handler(Handler).await.expect("Err creating client"); + let mut client = Client::builder(token, intents) + .event_handler(Arc::new(Handler)) + .await + .expect("Err creating client"); if let Err(why) = client.start().await { println!("Client error: {why:?}"); diff --git a/examples/e10_gateway_intents/src/main.rs b/examples/e10_gateway_intents/src/main.rs index 62b3d4fa2c3..8213cc159bc 100644 --- a/examples/e10_gateway_intents/src/main.rs +++ b/examples/e10_gateway_intents/src/main.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use serenity::async_trait; use serenity::prelude::*; @@ -41,7 +43,7 @@ async fn main() { GatewayIntents::GUILDS | GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT; // Build our client. let mut client = Client::builder(token, intents) - .event_handler(Handler) + .event_handler(Arc::new(Handler)) .await .expect("Error creating client"); diff --git a/examples/e11_global_data/src/main.rs b/examples/e11_global_data/src/main.rs index 89875a17626..8ba790d7453 100644 --- a/examples/e11_global_data/src/main.rs +++ b/examples/e11_global_data/src/main.rs @@ -88,7 +88,7 @@ async fn main() { // guarantee that Context::data will not panic if the same type is given, as providing the // incorrect type will lead to a compiler error, rather than a runtime panic. .data::(Arc::new(data)) - .event_handler(Handler) + .event_handler(Arc::new( Handler)) .await .expect("Err creating client"); diff --git a/examples/e12_parallel_loops/src/main.rs b/examples/e12_parallel_loops/src/main.rs index 3f6917075d8..2f5299a9ddf 100644 --- a/examples/e12_parallel_loops/src/main.rs +++ b/examples/e12_parallel_loops/src/main.rs @@ -1,4 +1,5 @@ #![expect(clippy::collapsible_if)] +use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use std::time::Duration; @@ -117,9 +118,9 @@ async fn main() { | GatewayIntents::GUILDS | GatewayIntents::MESSAGE_CONTENT; let mut client = Client::builder(token, intents) - .event_handler(Handler { + .event_handler(Arc::new(Handler { is_loop_running: AtomicBool::new(false), - }) + })) .await .expect("Error creating client"); diff --git a/examples/e13_sqlite_database/src/main.rs b/examples/e13_sqlite_database/src/main.rs index 673af342f86..17f132a41bd 100644 --- a/examples/e13_sqlite_database/src/main.rs +++ b/examples/e13_sqlite_database/src/main.rs @@ -1,6 +1,7 @@ // It is recommended that you read the README file, it is very important to this example. // This example will help us to use a sqlite database with our bot. use std::fmt::Write as _; +use std::sync::Arc; use serenity::async_trait; use serenity::model::prelude::*; @@ -106,9 +107,9 @@ async fn main() { // Run migrations, which updates the database's schema to the latest version. sqlx::migrate!("./migrations").run(&database).await.expect("Couldn't run database migrations"); - let bot = Bot { + let bot = Arc::new(Bot { database, - }; + }); let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::DIRECT_MESSAGES diff --git a/examples/e14_message_components/src/main.rs b/examples/e14_message_components/src/main.rs index 45b051e4da9..2e3c9a8b11a 100644 --- a/examples/e14_message_components/src/main.rs +++ b/examples/e14_message_components/src/main.rs @@ -1,4 +1,5 @@ use std::borrow::Cow; +use std::sync::Arc; use std::time::Duration; use dotenv::dotenv; @@ -170,7 +171,7 @@ async fn main() { | GatewayIntents::DIRECT_MESSAGES | GatewayIntents::MESSAGE_CONTENT; let mut client = Client::builder(token, intents) - .event_handler(Handler) + .event_handler(Arc::new(Handler)) .await .expect("Error creating client"); diff --git a/examples/testing/src/main.rs b/examples/testing/src/main.rs index a54ab26e32d..28ce568de33 100644 --- a/examples/testing/src/main.rs +++ b/examples/testing/src/main.rs @@ -1,4 +1,5 @@ use std::borrow::Cow; +use std::sync::Arc; use serenity::async_trait; use serenity::builder::*; @@ -430,6 +431,7 @@ async fn main() { Token::from_env("DISCORD_TOKEN").expect("Expected a valid token in the environment"); let intents = GatewayIntents::non_privileged() | GatewayIntents::MESSAGE_CONTENT; - let mut client = Client::builder(token, intents).event_handler(Handler).await.unwrap(); + let mut client = + Client::builder(token, intents).event_handler(Arc::new(Handler)).await.unwrap(); client.start().await.unwrap(); } diff --git a/src/gateway/client/mod.rs b/src/gateway/client/mod.rs index 27f84384ef3..951118dbc54 100644 --- a/src/gateway/client/mod.rs +++ b/src/gateway/client/mod.rs @@ -150,11 +150,10 @@ impl ClientBuilder { /// *Info*: If a reference to the framework is required for manual dispatch, you can implement /// [`Framework`] on [`Arc`] instead of `YourFrameworkType`. #[cfg(feature = "framework")] - pub fn framework(mut self, framework: F) -> Self - where - F: Framework + 'static, - { - self.framework = Some(Box::new(framework)); + pub fn framework(mut self, framework: Box) -> Self { + // This `Box` is converted to an `Arc` after `init` is called. The init step requires a + // mutable reference, and therefore accepting an `Arc` here would be misleading at best. + self.framework = Some(framework); self } @@ -188,11 +187,8 @@ impl ClientBuilder { /// Sets the voice gateway handler to be used. It will receive voice events sent over the /// gateway and then consider - based on its settings - whether to dispatch a command. #[cfg(feature = "voice")] - pub fn voice_manager(mut self, voice_manager: impl Into>) -> Self - where - V: VoiceGatewayManager + 'static, - { - self.voice_manager = Some(voice_manager.into()); + pub fn voice_manager(mut self, voice_manager: Arc) -> Self { + self.voice_manager = Some(voice_manager); self } @@ -235,35 +231,28 @@ impl ClientBuilder { self.intents } - /// Adds an event handler with multiple methods for each possible event. - pub fn event_handler(mut self, event_handler: impl Into>) -> Self - where - H: EventHandler + 'static, - { - self.event_handler = Some(event_handler.into()); + /// Sets the event handler where all received gateway events will be dispatched. + pub fn event_handler(mut self, event_handler: Arc) -> Self { + self.event_handler = Some(event_handler); self } - /// Gets the added event handlers. See [`Self::event_handler`] for more info. + /// Gets the added event handler. See [`Self::event_handler`] for more info. #[must_use] - pub fn get_event_handler(&self) -> Option<&Arc> { - self.event_handler.as_ref() + pub fn get_event_handler(&self) -> Option> { + self.event_handler.clone() } - /// 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: impl Into>) -> Self - where - H: RawEventHandler + 'static, - { - self.raw_event_handler = Some(raw_event_handler.into()); + /// Sets the raw event handler where all received gateway events will be dispatched. + pub fn raw_event_handler(mut self, raw_event_handler: Arc) -> Self { + self.raw_event_handler = Some(raw_event_handler); self } - /// Gets the added raw event handlers. See [`Self::raw_event_handler`] for more info. + /// Gets the added raw event handler. See [`Self::raw_event_handler`] for more info. #[must_use] - pub fn get_raw_event_handler(&self) -> Option<&Arc> { - self.raw_event_handler.as_ref() + pub fn get_raw_event_handler(&self) -> Option> { + self.raw_event_handler.clone() } /// Sets the initial activity.