Return whether command handled message
This commit is contained in:
parent
a6331d50b8
commit
4479126500
12 changed files with 142 additions and 47 deletions
|
|
@ -15,6 +15,13 @@ Procedure when bumping the version number:
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- `bot::botrulez::Uptime` now implements `bot::command::Command`
|
- `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<bool, E>` instead of a `Result<(), E>`
|
||||||
|
- `bot::command::Command::execute` now returns a `Result<bool, E>` instead of a `Result<(), E>`
|
||||||
|
- `bot::commands::Commands::handle_packet` now returns a `Result<bool, E>` instead of a `Result<(), E>`
|
||||||
|
|
||||||
## v0.3.1 - 2023-02-26
|
## v0.3.1 - 2023-02-26
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,10 +34,10 @@ impl ClapCommand<Bot, conn::Error> for Kill {
|
||||||
msg: &Message,
|
msg: &Message,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
bot: &mut Bot,
|
bot: &mut Bot,
|
||||||
) -> Result<(), conn::Error> {
|
) -> Result<bool, conn::Error> {
|
||||||
bot.stop = true;
|
bot.stop = true;
|
||||||
ctx.reply(msg.id, "/me dies").await?;
|
ctx.reply(msg.id, "/me dies").await?;
|
||||||
Ok(())
|
Ok(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,14 +61,14 @@ impl ClapCommand<Bot, conn::Error> for Test {
|
||||||
msg: &Message,
|
msg: &Message,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
_bot: &mut Bot,
|
_bot: &mut Bot,
|
||||||
) -> Result<(), conn::Error> {
|
) -> Result<bool, conn::Error> {
|
||||||
let content = if args.amount == 1 {
|
let content = if args.amount == 1 {
|
||||||
format!("/me did {} test", args.amount)
|
format!("/me did {} test", args.amount)
|
||||||
} else {
|
} else {
|
||||||
format!("/me did {} tests", args.amount)
|
format!("/me did {} tests", args.amount)
|
||||||
};
|
};
|
||||||
ctx.reply(msg.id, content).await?;
|
ctx.reply(msg.id, content).await?;
|
||||||
Ok(())
|
Ok(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,12 +50,20 @@ where
|
||||||
B: HasDescriptions + Send,
|
B: HasDescriptions + Send,
|
||||||
E: From<conn::Error>,
|
E: From<conn::Error>,
|
||||||
{
|
{
|
||||||
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<bool, E> {
|
||||||
if arg.trim().is_empty() {
|
if arg.trim().is_empty() {
|
||||||
let reply = self.formulate_reply(ctx, bot);
|
let reply = self.formulate_reply(ctx, bot);
|
||||||
ctx.reply(msg.id, reply).await?;
|
ctx.reply(msg.id, reply).await?;
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,9 +85,9 @@ where
|
||||||
msg: &Message,
|
msg: &Message,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
bot: &mut B,
|
bot: &mut B,
|
||||||
) -> Result<(), E> {
|
) -> Result<bool, E> {
|
||||||
let reply = self.formulate_reply(ctx, bot);
|
let reply = self.formulate_reply(ctx, bot);
|
||||||
ctx.reply(msg.id, reply).await?;
|
ctx.reply(msg.id, reply).await?;
|
||||||
Ok(())
|
Ok(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,11 +30,13 @@ where
|
||||||
msg: &Message,
|
msg: &Message,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
_bot: &mut B,
|
_bot: &mut B,
|
||||||
) -> Result<(), E> {
|
) -> Result<bool, E> {
|
||||||
if arg.trim().is_empty() {
|
if arg.trim().is_empty() {
|
||||||
ctx.reply(msg.id, &self.0).await?;
|
ctx.reply(msg.id, &self.0).await?;
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,8 +57,8 @@ where
|
||||||
msg: &Message,
|
msg: &Message,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
_bot: &mut B,
|
_bot: &mut B,
|
||||||
) -> Result<(), E> {
|
) -> Result<bool, E> {
|
||||||
ctx.reply(msg.id, &self.0).await?;
|
ctx.reply(msg.id, &self.0).await?;
|
||||||
Ok(())
|
Ok(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,11 +24,13 @@ where
|
||||||
msg: &Message,
|
msg: &Message,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
_bot: &mut B,
|
_bot: &mut B,
|
||||||
) -> Result<(), E> {
|
) -> Result<bool, E> {
|
||||||
if arg.trim().is_empty() {
|
if arg.trim().is_empty() {
|
||||||
ctx.reply(msg.id, &self.0).await?;
|
ctx.reply(msg.id, &self.0).await?;
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,8 +51,8 @@ where
|
||||||
msg: &Message,
|
msg: &Message,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
_bot: &mut B,
|
_bot: &mut B,
|
||||||
) -> Result<(), E> {
|
) -> Result<bool, E> {
|
||||||
ctx.reply(msg.id, &self.0).await?;
|
ctx.reply(msg.id, &self.0).await?;
|
||||||
Ok(())
|
Ok(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -81,12 +81,20 @@ where
|
||||||
B: HasStartTime + Send,
|
B: HasStartTime + Send,
|
||||||
E: From<conn::Error>,
|
E: From<conn::Error>,
|
||||||
{
|
{
|
||||||
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<bool, E> {
|
||||||
if arg.trim().is_empty() {
|
if arg.trim().is_empty() {
|
||||||
let reply = self.formulate_reply(ctx, bot, false);
|
let reply = self.formulate_reply(ctx, bot, false);
|
||||||
ctx.reply(msg.id, reply).await?;
|
ctx.reply(msg.id, reply).await?;
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,9 +120,9 @@ where
|
||||||
msg: &Message,
|
msg: &Message,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
bot: &mut B,
|
bot: &mut B,
|
||||||
) -> Result<(), E> {
|
) -> Result<bool, E> {
|
||||||
let reply = self.formulate_reply(ctx, bot, args.connected);
|
let reply = self.formulate_reply(ctx, bot, args.connected);
|
||||||
ctx.reply(msg.id, reply).await?;
|
ctx.reply(msg.id, reply).await?;
|
||||||
Ok(())
|
Ok(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,5 +54,11 @@ pub trait Command<B, E> {
|
||||||
None
|
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<bool, E>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,15 +65,21 @@ where
|
||||||
Some(format!("{}{} - {inner}", self.prefix, self.name))
|
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<bool, E> {
|
||||||
// TODO Replace with let-else
|
// TODO Replace with let-else
|
||||||
let (name, rest) = match parse_command(arg, &self.prefix) {
|
let (name, rest) = match parse_command(arg, &self.prefix) {
|
||||||
Some(parsed) => parsed,
|
Some(parsed) => parsed,
|
||||||
None => return Ok(()),
|
None => return Ok(false),
|
||||||
};
|
};
|
||||||
|
|
||||||
if name != self.name {
|
if name != self.name {
|
||||||
return Ok(());
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.inner.execute(rest, msg, ctx, bot).await
|
self.inner.execute(rest, msg, ctx, bot).await
|
||||||
|
|
@ -112,22 +118,28 @@ where
|
||||||
Some(format!("{}{} - {inner}", self.prefix, self.name))
|
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<bool, E> {
|
||||||
// TODO Replace with let-else
|
// TODO Replace with let-else
|
||||||
let (name, rest) = match parse_command(arg, &self.prefix) {
|
let (name, rest) = match parse_command(arg, &self.prefix) {
|
||||||
Some(parsed) => parsed,
|
Some(parsed) => parsed,
|
||||||
None => return Ok(()),
|
None => return Ok(false),
|
||||||
};
|
};
|
||||||
|
|
||||||
if name != self.name {
|
if name != self.name {
|
||||||
return Ok(());
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if parse_specific(rest).is_some() {
|
if parse_specific(rest).is_some() {
|
||||||
// The command looks like a specific command. If we treated it like
|
// The command looks like a specific command. If we treated it like
|
||||||
// a general command match, we would interpret other bots' specific
|
// a general command match, we would interpret other bots' specific
|
||||||
// commands as general commands.
|
// commands as general commands.
|
||||||
return Ok(());
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.inner.execute(rest, msg, ctx, bot).await
|
self.inner.execute(rest, msg, ctx, bot).await
|
||||||
|
|
@ -167,25 +179,31 @@ where
|
||||||
Some(format!("{}{} @{nick} - {inner}", self.prefix, self.name))
|
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<bool, E> {
|
||||||
// TODO Replace with let-else
|
// TODO Replace with let-else
|
||||||
let (name, rest) = match parse_command(arg, &self.prefix) {
|
let (name, rest) = match parse_command(arg, &self.prefix) {
|
||||||
Some(parsed) => parsed,
|
Some(parsed) => parsed,
|
||||||
None => return Ok(()),
|
None => return Ok(false),
|
||||||
};
|
};
|
||||||
|
|
||||||
if name != self.name {
|
if name != self.name {
|
||||||
return Ok(());
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Replace with let-else
|
// TODO Replace with let-else
|
||||||
let (nick, rest) = match parse_specific(rest) {
|
let (nick, rest) = match parse_specific(rest) {
|
||||||
Some(parsed) => parsed,
|
Some(parsed) => parsed,
|
||||||
None => return Ok(()),
|
None => return Ok(false),
|
||||||
};
|
};
|
||||||
|
|
||||||
if nick::normalize(nick) != nick::normalize(&ctx.joined.session.name) {
|
if nick::normalize(nick) != nick::normalize(&ctx.joined.session.name) {
|
||||||
return Ok(());
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.inner.execute(rest, msg, ctx, bot).await
|
self.inner.execute(rest, msg, ctx, bot).await
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ pub trait ClapCommand<B, E> {
|
||||||
msg: &Message,
|
msg: &Message,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
bot: &mut B,
|
bot: &mut B,
|
||||||
) -> Result<(), E>;
|
) -> Result<bool, E>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse bash-like quoted arguments separated by whitespace.
|
/// Parse bash-like quoted arguments separated by whitespace.
|
||||||
|
|
@ -110,12 +110,18 @@ where
|
||||||
C::Args::command().get_about().map(|s| format!("{s}"))
|
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<bool, E> {
|
||||||
let mut args = match parse_quoted_args(arg) {
|
let mut args = match parse_quoted_args(arg) {
|
||||||
Ok(args) => args,
|
Ok(args) => args,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
ctx.reply(msg.id, err).await?;
|
ctx.reply(msg.id, err).await?;
|
||||||
return Ok(());
|
return Ok(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -127,7 +133,7 @@ where
|
||||||
Ok(args) => args,
|
Ok(args) => args,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
ctx.reply(msg.id, format!("{}", err.render())).await?;
|
ctx.reply(msg.id, format!("{}", err.render())).await?;
|
||||||
return Ok(());
|
return Ok(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,13 @@ where
|
||||||
None
|
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<bool, E> {
|
||||||
self.0.execute(arg, msg, ctx, bot).await
|
self.0.execute(arg, msg, ctx, bot).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,17 @@ where
|
||||||
Some(format!("{} - {inner}", self.prefix))
|
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<bool, E> {
|
||||||
if let Some(rest) = arg.trim_start().strip_prefix(&self.prefix) {
|
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, bot).await
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,32 @@ use super::instance::{InstanceConfig, Snapshot};
|
||||||
|
|
||||||
pub struct Commands<B, E> {
|
pub struct Commands<B, E> {
|
||||||
commands: Vec<Box<dyn Command<B, E> + Send + Sync>>,
|
commands: Vec<Box<dyn Command<B, E> + Send + Sync>>,
|
||||||
|
fallthrough: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B, E> Commands<B, E> {
|
impl<B, E> Commands<B, E> {
|
||||||
pub fn new() -> Self {
|
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<C>(&mut self, command: C)
|
pub fn add<C>(&mut self, command: C)
|
||||||
|
|
@ -28,21 +49,22 @@ impl<B, E> Commands<B, E> {
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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(
|
pub async fn handle_packet(
|
||||||
&self,
|
&self,
|
||||||
config: &InstanceConfig,
|
config: &InstanceConfig,
|
||||||
packet: &ParsedPacket,
|
packet: &ParsedPacket,
|
||||||
snapshot: &Snapshot,
|
snapshot: &Snapshot,
|
||||||
bot: &mut B,
|
bot: &mut B,
|
||||||
) -> Result<(), E> {
|
) -> Result<bool, E> {
|
||||||
let msg = match &packet.content {
|
let msg = match &packet.content {
|
||||||
Ok(Data::SendEvent(SendEvent(msg))) => msg,
|
Ok(Data::SendEvent(SendEvent(msg))) => msg,
|
||||||
_ => return Ok(()),
|
_ => return Ok(false),
|
||||||
};
|
};
|
||||||
|
|
||||||
let joined = match &snapshot.state {
|
let joined = match &snapshot.state {
|
||||||
conn::State::Joining(_) => return Ok(()),
|
conn::State::Joining(_) => return Ok(false),
|
||||||
conn::State::Joined(joined) => joined.clone(),
|
conn::State::Joined(joined) => joined.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -52,11 +74,15 @@ impl<B, E> Commands<B, E> {
|
||||||
joined,
|
joined,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut handled = false;
|
||||||
for command in &self.commands {
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue