diff --git a/euphoxide-bot/examples/examplebot.rs b/euphoxide-bot/examples/examplebot.rs index 613321c..cf19a02 100644 --- a/euphoxide-bot/examples/examplebot.rs +++ b/euphoxide-bot/examples/examplebot.rs @@ -3,14 +3,13 @@ use std::time::Duration; use async_trait::async_trait; use euphoxide::api::Message; use euphoxide_bot::{ - bot::Bot, - command::{ - bang::{General, Specific}, - basic::Described, - botrulez::{FullHelp, Ping, ShortHelp}, - Command, Commands, Context, Info, Propagate, - }, + bang::{General, Specific}, + basic::Described, + botrulez::{FullHelp, Ping, ShortHelp}, + Command, Commands, Context, Info, Propagate, }; +use euphoxide_client::MultiClient; +use log::error; use tokio::sync::mpsc; struct Pyramid; @@ -26,7 +25,6 @@ impl Command for Pyramid { _arg: &str, msg: &Message, ctx: &Context, - _bot: &Bot, ) -> euphoxide::Result { let mut parent = msg.id; @@ -42,7 +40,8 @@ impl Command for Pyramid { } } -async fn run() -> anyhow::Result<()> { +#[tokio::main] +async fn main() { let (event_tx, mut event_rx) = mpsc::channel(10); let commands = Commands::new() @@ -56,28 +55,24 @@ async fn run() -> anyhow::Result<()> { "help", FullHelp::new().with_after("Created using euphoxide."), ))) - .then(General::new("pyramid", Pyramid)); + .then(General::new("pyramid", Pyramid)) + .build(); - let bot: Bot = Bot::new_simple(commands, event_tx); + let clients = MultiClient::new(event_tx); - bot.clients + clients .client_builder("test") .with_username("examplebot") .build_and_add() .await; while let Some(event) = event_rx.recv().await { - bot.handle_event(event); - } - - Ok(()) -} - -#[tokio::main] -async fn main() { - loop { - if let Err(err) = run().await { - println!("Error while running: {err}"); - } + let commands = commands.clone(); + let clients = clients.clone(); + tokio::task::spawn(async move { + if let Err(err) = commands.handle_event(clients, event).await { + error!("Oops: {err}") + } + }); } } diff --git a/euphoxide-bot/src/bot.rs b/euphoxide-bot/src/bot.rs deleted file mode 100644 index 4d36212..0000000 --- a/euphoxide-bot/src/bot.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::{fmt::Debug, sync::Arc}; - -use euphoxide_client::{MultiClient, MultiClientConfig, MultiClientEvent}; -use log::error; -use tokio::sync::mpsc; - -use crate::command::Commands; - -#[non_exhaustive] -pub struct Bot { - pub commands: Arc>, - pub clients: MultiClient, -} - -impl Bot { - pub fn new_simple(commands: Commands, event_tx: mpsc::Sender) -> Self { - Self::new(MultiClientConfig::default(), commands, event_tx) - } -} - -impl Bot { - pub fn new( - clients_config: MultiClientConfig, - commands: Commands, - event_tx: mpsc::Sender, - ) -> Self { - Self { - commands: Arc::new(commands), - clients: MultiClient::new_with_config(clients_config, event_tx), - } - } -} - -impl Bot -where - E: Debug + 'static, -{ - pub fn handle_event(&self, event: MultiClientEvent) { - let bot = self.clone(); - tokio::task::spawn(async move { - if let Err(err) = bot.commands.on_event(event, &bot).await { - error!("while handling event: {err:#?}"); - } - }); - } -} - -impl Clone for Bot { - fn clone(&self) -> Self { - Self { - commands: self.commands.clone(), - clients: self.clients.clone(), - } - } -} diff --git a/euphoxide-bot/src/command.rs b/euphoxide-bot/src/command.rs index 08863a3..626649f 100644 --- a/euphoxide-bot/src/command.rs +++ b/euphoxide-bot/src/command.rs @@ -4,7 +4,7 @@ pub mod botrulez; #[cfg(feature = "clap")] pub mod clap; -use std::future::Future; +use std::{future::Future, sync::Arc}; use async_trait::async_trait; use euphoxide::{ @@ -14,18 +14,18 @@ use euphoxide::{ state::{Joined, State}, }, }; -use euphoxide_client::{Client, MultiClientEvent}; - -use crate::bot::Bot; +use euphoxide_client::{Client, MultiClient, MultiClientEvent}; #[non_exhaustive] -pub struct Context { +pub struct Context { + pub commands: Arc>, + pub clients: MultiClient, pub client: Client, pub conn: ClientConnHandle, pub joined: Joined, } -impl Context { +impl Context { pub async fn send( &self, content: impl ToString, @@ -111,17 +111,11 @@ pub enum Propagate { #[allow(unused_variables)] #[async_trait] pub trait Command { - fn info(&self, ctx: &Context) -> Info { + fn info(&self, ctx: &Context) -> Info { Info::default() } - async fn execute( - &self, - arg: &str, - msg: &Message, - ctx: &Context, - bot: &Bot, - ) -> Result; + async fn execute(&self, arg: &str, msg: &Message, ctx: &Context) -> Result; } pub struct Commands { @@ -142,14 +136,44 @@ impl Commands { self } - pub fn infos(&self, ctx: &Context) -> Vec { + pub fn build(self) -> Arc { + Arc::new(self) + } + + pub fn infos(&self, ctx: &Context) -> Vec { self.commands.iter().map(|c| c.info(ctx)).collect() } - pub(crate) async fn on_event( - &self, + pub async fn handle_message( + self: Arc, + clients: MultiClient, + client: Client, + conn: ClientConnHandle, + joined: Joined, + msg: &Message, + ) -> Result { + let ctx = Context { + commands: self.clone(), + clients, + client, + conn, + joined, + }; + + for command in &self.commands { + let propagate = command.execute(&msg.content, msg, &ctx).await?; + if propagate == Propagate::No { + return Ok(Propagate::No); + } + } + + Ok(Propagate::Yes) + } + + pub async fn handle_event( + self: Arc, + clients: MultiClient, event: MultiClientEvent, - bot: &Bot, ) -> Result { let MultiClientEvent::Packet { client, @@ -169,20 +193,8 @@ impl Commands { return Ok(Propagate::Yes); }; - let ctx = Context { - client, - conn, - joined, - }; - - for command in &self.commands { - let propagate = command.execute(&msg.content, msg, &ctx, bot).await?; - if propagate == Propagate::No { - return Ok(Propagate::No); - } - } - - Ok(Propagate::Yes) + self.handle_message(clients, client, conn, joined, msg) + .await } } diff --git a/euphoxide-bot/src/command/bang.rs b/euphoxide-bot/src/command/bang.rs index 021c4fd..ad94c4a 100644 --- a/euphoxide-bot/src/command/bang.rs +++ b/euphoxide-bot/src/command/bang.rs @@ -3,8 +3,6 @@ use async_trait::async_trait; use euphoxide::{api::Message, nick}; -use crate::bot::Bot; - use super::{Command, Context, Info, Propagate}; // TODO Don't ignore leading whitespace? @@ -51,19 +49,13 @@ impl Command for Global where C: Command + Sync, { - fn info(&self, ctx: &Context) -> Info { + fn info(&self, ctx: &Context) -> Info { self.inner .info(ctx) .with_prepended_trigger(format!("{}{}", self.prefix, self.name)) } - async fn execute( - &self, - arg: &str, - msg: &Message, - ctx: &Context, - bot: &Bot, - ) -> Result { + async fn execute(&self, arg: &str, msg: &Message, ctx: &Context) -> Result { let Some((name, rest)) = parse_prefix_initiated(arg, &self.prefix) else { return Ok(Propagate::Yes); }; @@ -72,7 +64,7 @@ where return Ok(Propagate::Yes); } - self.inner.execute(rest, msg, ctx, bot).await + self.inner.execute(rest, msg, ctx).await } } @@ -102,19 +94,13 @@ impl Command for General where C: Command + Sync, { - fn info(&self, ctx: &Context) -> Info { + fn info(&self, ctx: &Context) -> Info { self.inner .info(ctx) .with_prepended_trigger(format!("{}{}", self.prefix, self.name)) } - async fn execute( - &self, - arg: &str, - msg: &Message, - ctx: &Context, - bot: &Bot, - ) -> Result { + async fn execute(&self, arg: &str, msg: &Message, ctx: &Context) -> Result { let Some((name, rest)) = parse_prefix_initiated(arg, &self.prefix) else { return Ok(Propagate::Yes); }; @@ -130,7 +116,7 @@ where return Ok(Propagate::Yes); } - self.inner.execute(rest, msg, ctx, bot).await + self.inner.execute(rest, msg, ctx).await } } @@ -160,20 +146,14 @@ impl Command for Specific where C: Command + Sync, { - fn info(&self, ctx: &Context) -> Info { + fn info(&self, ctx: &Context) -> Info { let nick = nick::mention(&ctx.joined.session.name); self.inner .info(ctx) .with_prepended_trigger(format!("{}{} @{nick}", self.prefix, self.name)) } - async fn execute( - &self, - arg: &str, - msg: &Message, - ctx: &Context, - bot: &Bot, - ) -> Result { + async fn execute(&self, arg: &str, msg: &Message, ctx: &Context) -> Result { let Some((name, rest)) = parse_prefix_initiated(arg, &self.prefix) else { return Ok(Propagate::Yes); }; @@ -190,7 +170,7 @@ where return Ok(Propagate::Yes); } - self.inner.execute(rest, msg, ctx, bot).await + self.inner.execute(rest, msg, ctx).await } } diff --git a/euphoxide-bot/src/command/basic.rs b/euphoxide-bot/src/command/basic.rs index b6dda4c..5f7cac0 100644 --- a/euphoxide-bot/src/command/basic.rs +++ b/euphoxide-bot/src/command/basic.rs @@ -3,8 +3,6 @@ use async_trait::async_trait; use euphoxide::api::Message; -use crate::bot::Bot; - use super::{Command, Context, Info, Propagate}; /// Rewrite or hide command info. @@ -55,7 +53,7 @@ impl Command for Described where C: Command + Sync, { - fn info(&self, ctx: &Context) -> Info { + fn info(&self, ctx: &Context) -> Info { let info = self.inner.info(ctx); Info { trigger: self.trigger.clone().unwrap_or(info.trigger), @@ -63,14 +61,8 @@ where } } - async fn execute( - &self, - arg: &str, - msg: &Message, - ctx: &Context, - bot: &Bot, - ) -> Result { - self.inner.execute(arg, msg, ctx, bot).await + async fn execute(&self, arg: &str, msg: &Message, ctx: &Context) -> Result { + self.inner.execute(arg, msg, ctx).await } } @@ -93,19 +85,13 @@ impl Command for Prefixed where C: Command + Sync, { - fn info(&self, ctx: &Context) -> Info { + fn info(&self, ctx: &Context) -> Info { self.inner.info(ctx).with_prepended_trigger(&self.prefix) } - async fn execute( - &self, - arg: &str, - msg: &Message, - ctx: &Context, - bot: &Bot, - ) -> Result { + async fn execute(&self, arg: &str, msg: &Message, ctx: &Context) -> Result { if let Some(rest) = arg.trim_start().strip_prefix(&self.prefix) { - self.inner.execute(rest, msg, ctx, bot).await + self.inner.execute(rest, msg, ctx).await } else { Ok(Propagate::Yes) } diff --git a/euphoxide-bot/src/command/botrulez/full_help.rs b/euphoxide-bot/src/command/botrulez/full_help.rs index 627cf5b..aee9ac4 100644 --- a/euphoxide-bot/src/command/botrulez/full_help.rs +++ b/euphoxide-bot/src/command/botrulez/full_help.rs @@ -5,10 +5,7 @@ use euphoxide::api::Message; #[cfg(feature = "clap")] use crate::command::clap::ClapCommand; -use crate::{ - bot::Bot, - command::{Command, Context, Propagate}, -}; +use crate::command::{Command, Context, Propagate}; #[derive(Default)] pub struct FullHelp { @@ -31,7 +28,7 @@ impl FullHelp { self } - fn formulate_reply(&self, ctx: &Context, bot: &Bot) -> String { + fn formulate_reply(&self, ctx: &Context) -> String { let mut result = String::new(); if !self.before.is_empty() { @@ -39,7 +36,7 @@ impl FullHelp { result.push('\n'); } - for info in bot.commands.infos(ctx) { + for info in ctx.commands.infos(ctx) { if let Some(trigger) = &info.trigger { result.push_str(trigger); if let Some(description) = &info.description { @@ -64,15 +61,9 @@ impl Command for FullHelp where E: From, { - async fn execute( - &self, - arg: &str, - msg: &Message, - ctx: &Context, - bot: &Bot, - ) -> Result { + async fn execute(&self, arg: &str, msg: &Message, ctx: &Context) -> Result { if arg.trim().is_empty() { - let reply = self.formulate_reply(ctx, bot); + let reply = self.formulate_reply(ctx); ctx.reply_only(msg.id, reply).await?; Ok(Propagate::No) } else { @@ -98,10 +89,9 @@ where &self, _args: Self::Args, msg: &Message, - ctx: &Context, - bot: &Bot, + ctx: &Context, ) -> Result { - let reply = self.formulate_reply(ctx, bot); + let reply = self.formulate_reply(ctx); ctx.reply_only(msg.id, reply).await?; Ok(Propagate::No) } diff --git a/euphoxide-bot/src/command/botrulez/ping.rs b/euphoxide-bot/src/command/botrulez/ping.rs index 779a7a6..6511565 100644 --- a/euphoxide-bot/src/command/botrulez/ping.rs +++ b/euphoxide-bot/src/command/botrulez/ping.rs @@ -5,10 +5,7 @@ use euphoxide::api::Message; #[cfg(feature = "clap")] use crate::command::clap::ClapCommand; -use crate::{ - bot::Bot, - command::{Command, Context, Propagate}, -}; +use crate::command::{Command, Context, Propagate}; pub struct Ping(pub String); @@ -29,13 +26,7 @@ impl Command for Ping where E: From, { - async fn execute( - &self, - arg: &str, - msg: &Message, - ctx: &Context, - _bot: &Bot, - ) -> Result { + async fn execute(&self, arg: &str, msg: &Message, ctx: &Context) -> Result { if arg.trim().is_empty() { ctx.reply_only(msg.id, &self.0).await?; Ok(Propagate::No) @@ -62,8 +53,7 @@ where &self, _args: Self::Args, msg: &Message, - ctx: &Context, - _bot: &Bot, + ctx: &Context, ) -> Result { ctx.reply_only(msg.id, &self.0).await?; Ok(Propagate::No) diff --git a/euphoxide-bot/src/command/botrulez/short_help.rs b/euphoxide-bot/src/command/botrulez/short_help.rs index 0eac90c..8b1c9b8 100644 --- a/euphoxide-bot/src/command/botrulez/short_help.rs +++ b/euphoxide-bot/src/command/botrulez/short_help.rs @@ -5,10 +5,7 @@ use euphoxide::api::Message; #[cfg(feature = "clap")] use crate::command::clap::ClapCommand; -use crate::{ - bot::Bot, - command::{Command, Context, Propagate}, -}; +use crate::command::{Command, Context, Propagate}; pub struct ShortHelp(pub String); @@ -23,13 +20,7 @@ impl Command for ShortHelp where E: From, { - async fn execute( - &self, - arg: &str, - msg: &Message, - ctx: &Context, - _bot: &Bot, - ) -> Result { + async fn execute(&self, arg: &str, msg: &Message, ctx: &Context) -> Result { if arg.trim().is_empty() { ctx.reply_only(msg.id, &self.0).await?; Ok(Propagate::No) @@ -56,8 +47,7 @@ where &self, _args: Self::Args, msg: &Message, - ctx: &Context, - _bot: &Bot, + ctx: &Context, ) -> Result { ctx.reply_only(msg.id, &self.0).await?; Ok(Propagate::No) diff --git a/euphoxide-bot/src/command/botrulez/uptime.rs b/euphoxide-bot/src/command/botrulez/uptime.rs index e8b1d8b..40f6cc6 100644 --- a/euphoxide-bot/src/command/botrulez/uptime.rs +++ b/euphoxide-bot/src/command/botrulez/uptime.rs @@ -6,10 +6,7 @@ use jiff::{Span, Timestamp, Unit}; #[cfg(feature = "clap")] use crate::command::clap::ClapCommand; -use crate::{ - bot::Bot, - command::{Command, Context, Propagate}, -}; +use crate::command::{Command, Context, Propagate}; pub fn format_time(t: Timestamp) -> String { t.strftime("%Y-%m-%d %H:%M:%S UTC").to_string() @@ -62,14 +59,8 @@ pub trait HasStartTime { } impl Uptime { - fn formulate_reply( - &self, - ctx: &Context, - bot: &Bot, - joined: bool, - connected: bool, - ) -> String { - let start = bot.clients.start_time(); + fn formulate_reply(&self, ctx: &Context, joined: bool, connected: bool) -> String { + let start = ctx.clients.start_time(); let now = Timestamp::now(); let mut reply = format!( @@ -105,15 +96,9 @@ impl Command for Uptime where E: From, { - async fn execute( - &self, - arg: &str, - msg: &Message, - ctx: &Context, - bot: &Bot, - ) -> Result { + async fn execute(&self, arg: &str, msg: &Message, ctx: &Context) -> Result { if arg.trim().is_empty() { - let reply = self.formulate_reply(ctx, bot, false, false); + let reply = self.formulate_reply(ctx, false, false); ctx.reply_only(msg.id, reply).await?; Ok(Propagate::No) } else { @@ -146,10 +131,9 @@ where &self, args: Self::Args, msg: &Message, - ctx: &Context, - bot: &Bot, + ctx: &Context, ) -> Result { - let reply = self.formulate_reply(ctx, bot, args.present, args.connected); + let reply = self.formulate_reply(ctx, args.present, args.connected); ctx.reply_only(msg.id, reply).await?; Ok(Propagate::No) } diff --git a/euphoxide-bot/src/command/clap.rs b/euphoxide-bot/src/command/clap.rs index 7d241ef..2e071b6 100644 --- a/euphoxide-bot/src/command/clap.rs +++ b/euphoxide-bot/src/command/clap.rs @@ -4,8 +4,6 @@ use async_trait::async_trait; use clap::{CommandFactory, Parser}; use euphoxide::api::Message; -use crate::bot::Bot; - use super::{Command, Context, Info, Propagate}; #[async_trait] @@ -16,8 +14,7 @@ pub trait ClapCommand { &self, args: Self::Args, msg: &Message, - ctx: &Context, - bot: &Bot, + ctx: &Context, ) -> Result; } @@ -107,20 +104,14 @@ where C: ClapCommand + Sync, C::Args: Parser + Send, { - fn info(&self, _ctx: &Context) -> Info { + fn info(&self, _ctx: &Context) -> Info { Info { description: C::Args::command().get_about().map(|s| s.to_string()), ..Info::default() } } - async fn execute( - &self, - arg: &str, - msg: &Message, - ctx: &Context, - bot: &Bot, - ) -> Result { + async fn execute(&self, arg: &str, msg: &Message, ctx: &Context) -> Result { let mut args = match parse_quoted_args(arg) { Ok(args) => args, Err(err) => { @@ -141,7 +132,7 @@ where } }; - self.0.execute(args, msg, ctx, bot).await + self.0.execute(args, msg, ctx).await } } diff --git a/euphoxide-bot/src/lib.rs b/euphoxide-bot/src/lib.rs index 975f881..b76141f 100644 --- a/euphoxide-bot/src/lib.rs +++ b/euphoxide-bot/src/lib.rs @@ -1,2 +1,3 @@ -pub mod bot; -pub mod command; +mod command; + +pub use self::command::*;