From 447912650035cb4f59476af8e02f3b83d4386883 Mon Sep 17 00:00:00 2001 From: Joscha Date: Mon, 27 Feb 2023 12:41:49 +0100 Subject: [PATCH] Return whether command handled message --- CHANGELOG.md | 7 ++++++ examples/testbot_commands.rs | 8 +++---- src/bot/botrulez/full_help.rs | 16 +++++++++---- src/bot/botrulez/ping.rs | 10 ++++---- src/bot/botrulez/short_help.rs | 10 ++++---- src/bot/botrulez/uptime.rs | 16 +++++++++---- src/bot/command.rs | 8 ++++++- src/bot/command/bang.rs | 42 ++++++++++++++++++++++++---------- src/bot/command/clap.rs | 14 ++++++++---- src/bot/command/hidden.rs | 8 ++++++- src/bot/command/prefixed.rs | 10 ++++++-- src/bot/commands.rs | 40 ++++++++++++++++++++++++++------ 12 files changed, 142 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d26a07d..83adcc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,13 @@ Procedure when bumping the version number: ### Added - `bot::botrulez::Uptime` now implements `bot::command::Command` +- `bot::commands::Commands::fallthrough` +- `bot::commands::Commands::set_fallthrough` + +### Changed +- `bot::command::ClapCommand::execute` now returns a `Result` instead of a `Result<(), E>` +- `bot::command::Command::execute` now returns a `Result` instead of a `Result<(), E>` +- `bot::commands::Commands::handle_packet` now returns a `Result` instead of a `Result<(), E>` ## v0.3.1 - 2023-02-26 diff --git a/examples/testbot_commands.rs b/examples/testbot_commands.rs index 34ed170..3a07c19 100644 --- a/examples/testbot_commands.rs +++ b/examples/testbot_commands.rs @@ -34,10 +34,10 @@ impl ClapCommand for Kill { msg: &Message, ctx: &Context, bot: &mut Bot, - ) -> Result<(), conn::Error> { + ) -> Result { bot.stop = true; ctx.reply(msg.id, "/me dies").await?; - Ok(()) + Ok(true) } } @@ -61,14 +61,14 @@ impl ClapCommand for Test { msg: &Message, ctx: &Context, _bot: &mut Bot, - ) -> Result<(), conn::Error> { + ) -> Result { let content = if args.amount == 1 { format!("/me did {} test", args.amount) } else { format!("/me did {} tests", args.amount) }; ctx.reply(msg.id, content).await?; - Ok(()) + Ok(true) } } diff --git a/src/bot/botrulez/full_help.rs b/src/bot/botrulez/full_help.rs index f1508ab..20ffcc4 100644 --- a/src/bot/botrulez/full_help.rs +++ b/src/bot/botrulez/full_help.rs @@ -50,12 +50,20 @@ where B: HasDescriptions + Send, E: From, { - async fn execute(&self, arg: &str, msg: &Message, ctx: &Context, bot: &mut B) -> Result<(), E> { + async fn execute( + &self, + arg: &str, + msg: &Message, + ctx: &Context, + bot: &mut B, + ) -> Result { if arg.trim().is_empty() { let reply = self.formulate_reply(ctx, bot); ctx.reply(msg.id, reply).await?; + Ok(true) + } else { + Ok(false) } - Ok(()) } } @@ -77,9 +85,9 @@ where msg: &Message, ctx: &Context, bot: &mut B, - ) -> Result<(), E> { + ) -> Result { let reply = self.formulate_reply(ctx, bot); ctx.reply(msg.id, reply).await?; - Ok(()) + Ok(true) } } diff --git a/src/bot/botrulez/ping.rs b/src/bot/botrulez/ping.rs index cb48195..c7cea39 100644 --- a/src/bot/botrulez/ping.rs +++ b/src/bot/botrulez/ping.rs @@ -30,11 +30,13 @@ where msg: &Message, ctx: &Context, _bot: &mut B, - ) -> Result<(), E> { + ) -> Result { if arg.trim().is_empty() { ctx.reply(msg.id, &self.0).await?; + Ok(true) + } else { + Ok(false) } - Ok(()) } } @@ -55,8 +57,8 @@ where msg: &Message, ctx: &Context, _bot: &mut B, - ) -> Result<(), E> { + ) -> Result { ctx.reply(msg.id, &self.0).await?; - Ok(()) + Ok(true) } } diff --git a/src/bot/botrulez/short_help.rs b/src/bot/botrulez/short_help.rs index 028c1d1..1a359be 100644 --- a/src/bot/botrulez/short_help.rs +++ b/src/bot/botrulez/short_help.rs @@ -24,11 +24,13 @@ where msg: &Message, ctx: &Context, _bot: &mut B, - ) -> Result<(), E> { + ) -> Result { if arg.trim().is_empty() { ctx.reply(msg.id, &self.0).await?; + Ok(true) + } else { + Ok(false) } - Ok(()) } } @@ -49,8 +51,8 @@ where msg: &Message, ctx: &Context, _bot: &mut B, - ) -> Result<(), E> { + ) -> Result { ctx.reply(msg.id, &self.0).await?; - Ok(()) + Ok(true) } } diff --git a/src/bot/botrulez/uptime.rs b/src/bot/botrulez/uptime.rs index 8b1856f..b4465e5 100644 --- a/src/bot/botrulez/uptime.rs +++ b/src/bot/botrulez/uptime.rs @@ -81,12 +81,20 @@ where B: HasStartTime + Send, E: From, { - async fn execute(&self, arg: &str, msg: &Message, ctx: &Context, bot: &mut B) -> Result<(), E> { + async fn execute( + &self, + arg: &str, + msg: &Message, + ctx: &Context, + bot: &mut B, + ) -> Result { if arg.trim().is_empty() { let reply = self.formulate_reply(ctx, bot, false); ctx.reply(msg.id, reply).await?; + Ok(true) + } else { + Ok(false) } - Ok(()) } } @@ -112,9 +120,9 @@ where msg: &Message, ctx: &Context, bot: &mut B, - ) -> Result<(), E> { + ) -> Result { let reply = self.formulate_reply(ctx, bot, args.connected); ctx.reply(msg.id, reply).await?; - Ok(()) + Ok(true) } } diff --git a/src/bot/command.rs b/src/bot/command.rs index 5c400ca..226adb0 100644 --- a/src/bot/command.rs +++ b/src/bot/command.rs @@ -54,5 +54,11 @@ pub trait Command { None } - async fn execute(&self, arg: &str, msg: &Message, ctx: &Context, bot: &mut B) -> Result<(), E>; + async fn execute( + &self, + arg: &str, + msg: &Message, + ctx: &Context, + bot: &mut B, + ) -> Result; } diff --git a/src/bot/command/bang.rs b/src/bot/command/bang.rs index 16c9b12..79f74a2 100644 --- a/src/bot/command/bang.rs +++ b/src/bot/command/bang.rs @@ -65,15 +65,21 @@ where Some(format!("{}{} - {inner}", self.prefix, self.name)) } - async fn execute(&self, arg: &str, msg: &Message, ctx: &Context, bot: &mut B) -> Result<(), E> { + async fn execute( + &self, + arg: &str, + msg: &Message, + ctx: &Context, + bot: &mut B, + ) -> Result { // TODO Replace with let-else let (name, rest) = match parse_command(arg, &self.prefix) { Some(parsed) => parsed, - None => return Ok(()), + None => return Ok(false), }; if name != self.name { - return Ok(()); + return Ok(false); } self.inner.execute(rest, msg, ctx, bot).await @@ -112,22 +118,28 @@ where Some(format!("{}{} - {inner}", self.prefix, self.name)) } - async fn execute(&self, arg: &str, msg: &Message, ctx: &Context, bot: &mut B) -> Result<(), E> { + async fn execute( + &self, + arg: &str, + msg: &Message, + ctx: &Context, + bot: &mut B, + ) -> Result { // TODO Replace with let-else let (name, rest) = match parse_command(arg, &self.prefix) { Some(parsed) => parsed, - None => return Ok(()), + None => return Ok(false), }; if name != self.name { - return Ok(()); + return Ok(false); } if parse_specific(rest).is_some() { // The command looks like a specific command. If we treated it like // a general command match, we would interpret other bots' specific // commands as general commands. - return Ok(()); + return Ok(false); } self.inner.execute(rest, msg, ctx, bot).await @@ -167,25 +179,31 @@ where Some(format!("{}{} @{nick} - {inner}", self.prefix, self.name)) } - async fn execute(&self, arg: &str, msg: &Message, ctx: &Context, bot: &mut B) -> Result<(), E> { + async fn execute( + &self, + arg: &str, + msg: &Message, + ctx: &Context, + bot: &mut B, + ) -> Result { // TODO Replace with let-else let (name, rest) = match parse_command(arg, &self.prefix) { Some(parsed) => parsed, - None => return Ok(()), + None => return Ok(false), }; if name != self.name { - return Ok(()); + return Ok(false); } // TODO Replace with let-else let (nick, rest) = match parse_specific(rest) { Some(parsed) => parsed, - None => return Ok(()), + None => return Ok(false), }; if nick::normalize(nick) != nick::normalize(&ctx.joined.session.name) { - return Ok(()); + return Ok(false); } self.inner.execute(rest, msg, ctx, bot).await diff --git a/src/bot/command/clap.rs b/src/bot/command/clap.rs index efb7fce..a22b49a 100644 --- a/src/bot/command/clap.rs +++ b/src/bot/command/clap.rs @@ -16,7 +16,7 @@ pub trait ClapCommand { msg: &Message, ctx: &Context, bot: &mut B, - ) -> Result<(), E>; + ) -> Result; } /// Parse bash-like quoted arguments separated by whitespace. @@ -110,12 +110,18 @@ where C::Args::command().get_about().map(|s| format!("{s}")) } - async fn execute(&self, arg: &str, msg: &Message, ctx: &Context, bot: &mut B) -> Result<(), E> { + async fn execute( + &self, + arg: &str, + msg: &Message, + ctx: &Context, + bot: &mut B, + ) -> Result { let mut args = match parse_quoted_args(arg) { Ok(args) => args, Err(err) => { ctx.reply(msg.id, err).await?; - return Ok(()); + return Ok(true); } }; @@ -127,7 +133,7 @@ where Ok(args) => args, Err(err) => { ctx.reply(msg.id, format!("{}", err.render())).await?; - return Ok(()); + return Ok(true); } }; diff --git a/src/bot/command/hidden.rs b/src/bot/command/hidden.rs index c3f6bf2..0aad60d 100644 --- a/src/bot/command/hidden.rs +++ b/src/bot/command/hidden.rs @@ -17,7 +17,13 @@ where None } - async fn execute(&self, arg: &str, msg: &Message, ctx: &Context, bot: &mut B) -> Result<(), E> { + async fn execute( + &self, + arg: &str, + msg: &Message, + ctx: &Context, + bot: &mut B, + ) -> Result { self.0.execute(arg, msg, ctx, bot).await } } diff --git a/src/bot/command/prefixed.rs b/src/bot/command/prefixed.rs index 486ab55..f572732 100644 --- a/src/bot/command/prefixed.rs +++ b/src/bot/command/prefixed.rs @@ -29,11 +29,17 @@ where Some(format!("{} - {inner}", self.prefix)) } - async fn execute(&self, arg: &str, msg: &Message, ctx: &Context, bot: &mut B) -> Result<(), E> { + async fn execute( + &self, + arg: &str, + msg: &Message, + ctx: &Context, + bot: &mut B, + ) -> Result { if let Some(rest) = arg.trim_start().strip_prefix(&self.prefix) { self.inner.execute(rest, msg, ctx, bot).await } else { - Ok(()) + Ok(false) } } } diff --git a/src/bot/commands.rs b/src/bot/commands.rs index b9e6993..e276757 100644 --- a/src/bot/commands.rs +++ b/src/bot/commands.rs @@ -7,11 +7,32 @@ use super::instance::{InstanceConfig, Snapshot}; pub struct Commands { commands: Vec + Send + Sync>>, + fallthrough: bool, } impl Commands { pub fn new() -> Self { - Self { commands: vec![] } + Self { + commands: vec![], + fallthrough: false, + } + } + + /// Whether further commands should be executed after a command returns + /// `true`. + /// + /// If disabled, commands are run until the first command that returns + /// `true`. If enabled, all commands are run irrespective of their return + /// values. + pub fn fallthrough(&self) -> bool { + self.fallthrough + } + + /// Set whether fallthrough is active. + /// + /// See [`Self::fallthrough`] for more details. + pub fn set_fallthrough(&mut self, active: bool) { + self.fallthrough = active; } pub fn add(&mut self, command: C) @@ -28,21 +49,22 @@ impl Commands { .collect::>() } - /// Returns `true` if a command was found and executed, `false` otherwise. + /// Returns `true` if one or more commands returned `true`, `false` + /// otherwise. pub async fn handle_packet( &self, config: &InstanceConfig, packet: &ParsedPacket, snapshot: &Snapshot, bot: &mut B, - ) -> Result<(), E> { + ) -> Result { let msg = match &packet.content { Ok(Data::SendEvent(SendEvent(msg))) => msg, - _ => return Ok(()), + _ => return Ok(false), }; let joined = match &snapshot.state { - conn::State::Joining(_) => return Ok(()), + conn::State::Joining(_) => return Ok(false), conn::State::Joined(joined) => joined.clone(), }; @@ -52,11 +74,15 @@ impl Commands { joined, }; + let mut handled = false; for command in &self.commands { - command.execute(&msg.content, msg, &ctx, bot).await?; + handled = handled || command.execute(&msg.content, msg, &ctx, bot).await?; + if !self.fallthrough && handled { + break; + } } - Ok(()) + Ok(handled) } }