mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-24 20:14:19 +00:00
Differentiate between unknown command and parse error (#14)
Co-authored-by: Stefano Pigozzi <me@steffo.eu>
This commit is contained in:
parent
c1f76f8946
commit
a72f0ee43d
4 changed files with 143 additions and 35 deletions
|
@ -4,14 +4,15 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{Context, Error};
|
||||
use teloxide::Bot;
|
||||
use teloxide::payloads::SendMessageSetters;
|
||||
use teloxide::requests::Requester;
|
||||
use teloxide::types::{Message, ReplyParameters};
|
||||
use teloxide::utils::command::BotCommands;
|
||||
use teloxide::Bot;
|
||||
|
||||
use crate::services::telegram::dependencies::interface_database::DatabaseInterface;
|
||||
use crate::utils::anyhow_result::AnyResult;
|
||||
use crate::utils::italian::countable_noun_suffix;
|
||||
|
||||
pub mod start;
|
||||
pub mod fortune;
|
||||
|
@ -111,7 +112,7 @@ impl Command {
|
|||
log::trace!("Delegating error handling to error handler...");
|
||||
let result2 = match result1.as_ref() {
|
||||
Ok(_) => return,
|
||||
Err(e1) => self.handle_error(&bot, &message, e1).await
|
||||
Err(e1) => self.handle_error_command(&bot, &message, e1).await
|
||||
};
|
||||
|
||||
let e1 = result1.unwrap_err();
|
||||
|
@ -129,12 +130,49 @@ impl Command {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn handle_unknown(bot: Bot, message: Message) -> CommandResult {
|
||||
log::debug!("Received an unknown command or an invalid syntax: {:?}", message.text());
|
||||
pub async fn handle_malformed_simple(bot: Bot, message: Message, expected: usize, found: usize) -> CommandResult {
|
||||
log::debug!("Received a malformed command: {:?}", message.text());
|
||||
|
||||
log::trace!("Sending error message...");
|
||||
let text = format!(
|
||||
"⚠️ Il comando si aspetta {} argoment{}, ma ne ha ricevut{} solo {}.",
|
||||
expected,
|
||||
countable_noun_suffix(expected, "o", "i"),
|
||||
countable_noun_suffix(found, "o", "i"),
|
||||
found,
|
||||
);
|
||||
let _reply = bot
|
||||
.send_message(message.chat.id, "⚠️ Comando sconosciuto o sintassi non valida.")
|
||||
.send_message(message.chat.id, text)
|
||||
.reply_parameters(ReplyParameters::new(message.id))
|
||||
.await
|
||||
.context("Non è stato possibile inviare il messaggio di errore.")?;
|
||||
|
||||
log::trace!("Successfully handled malformed command!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn handle_malformed_complex(bot: Bot, message: Message) -> CommandResult {
|
||||
log::debug!("Received a malformed command: {:?}", message.text());
|
||||
|
||||
log::trace!("Sending error message...");
|
||||
let text = "⚠️ Il comando si aspetta una sintassi diversa da quella che ha ricevuto.";
|
||||
let _reply = bot
|
||||
.send_message(message.chat.id, text)
|
||||
.reply_parameters(ReplyParameters::new(message.id))
|
||||
.await
|
||||
.context("Non è stato possibile inviare il messaggio di errore.")?;
|
||||
|
||||
log::trace!("Successfully handled malformed command!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn handle_unknown(bot: Bot, message: Message) -> CommandResult {
|
||||
log::debug!("Received an unknown command: {:?}", message.text());
|
||||
|
||||
log::trace!("Sending error message...");
|
||||
let text = "⚠️ Il comando specificato non esiste.";
|
||||
let _reply = bot
|
||||
.send_message(message.chat.id, text)
|
||||
.reply_parameters(ReplyParameters::new(message.id))
|
||||
.await
|
||||
.context("Non è stato possibile inviare il messaggio di errore.")?;
|
||||
|
@ -143,7 +181,22 @@ impl Command {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_error(&self, bot: &Bot, message: &Message, error: &Error) -> CommandResult {
|
||||
pub async fn handle_error_parse(bot: &Bot, message: &Message, error: &Error) -> CommandResult {
|
||||
log::debug!("Encountered a parsing error while parsing: {:?}", message.text());
|
||||
|
||||
log::trace!("Sending error message...");
|
||||
let text = format!("⚠️ {error}");
|
||||
let _reply = bot
|
||||
.send_message(message.chat.id, text)
|
||||
.reply_parameters(ReplyParameters::new(message.id))
|
||||
.await
|
||||
.context("Non è stato possibile inviare il messaggio di errore.")?;
|
||||
|
||||
log::trace!("Successfully handled malparsed command!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn handle_error_command(&self, bot: &Bot, message: &Message, error: &Error) -> CommandResult {
|
||||
log::debug!(
|
||||
"Command message in {:?} with id {:?} and contents {:?} errored out with `{:?}`",
|
||||
&message.chat.id,
|
||||
|
@ -153,8 +206,9 @@ impl Command {
|
|||
);
|
||||
|
||||
log::trace!("Sending error message...");
|
||||
let text = format!("⚠️ {error}");
|
||||
let _reply = bot
|
||||
.send_message(message.chat.id, format!("⚠️ {error}"))
|
||||
.send_message(message.chat.id, text)
|
||||
.reply_parameters(ReplyParameters::new(message.id))
|
||||
.await
|
||||
.context("Non è stato possibile inviare il messaggio di errore.")?;
|
||||
|
@ -163,7 +217,7 @@ impl Command {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_fatal(&self, _bot: &Bot, message: &Message, error1: &Error, error2: &Error) -> CommandResult {
|
||||
pub async fn handle_fatal(&self, _bot: &Bot, message: &Message, error1: &Error, error2: &Error) -> CommandResult {
|
||||
log::error!(
|
||||
"Command message in {:?} with id {:?} and contents {:?} errored out with `{:?}`, and it was impossible to handle the error because of `{:?}`",
|
||||
&message.chat.id,
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Context;
|
||||
use regex::Regex;
|
||||
use commands::Command;
|
||||
use dependencies::interface_database::DatabaseInterface;
|
||||
use keyboard_callbacks::KeyboardCallback;
|
||||
use teloxide::dispatching::DefaultKey;
|
||||
use teloxide::dptree::entry;
|
||||
use teloxide::prelude::*;
|
||||
use teloxide::types::{Me, ParseMode};
|
||||
|
||||
use commands::Command;
|
||||
use dependencies::interface_database::DatabaseInterface;
|
||||
use keyboard_callbacks::KeyboardCallback;
|
||||
use teloxide::utils::command::{BotCommands, ParseError};
|
||||
|
||||
use crate::utils::anyhow_result::AnyResult;
|
||||
use crate::utils::telegram_string::TelegramEscape;
|
||||
|
@ -102,16 +101,74 @@ impl TelegramService {
|
|||
.context("Aggiornamento dei comandi del bot non riuscito.")
|
||||
}
|
||||
|
||||
async fn handle_message(bot: Bot, me: Me, message: Message, database: Arc<DatabaseInterface>) -> AnyResult<()> {
|
||||
log::debug!("Handling message: {message:#?}");
|
||||
|
||||
log::trace!("Accessing message text...");
|
||||
let text = match message.text() {
|
||||
None => {
|
||||
log::trace!("Message has no text; skipping it.");
|
||||
return Ok(())
|
||||
}
|
||||
Some(text) => {
|
||||
log::trace!("Message has text: {text:?}");
|
||||
text
|
||||
}
|
||||
};
|
||||
|
||||
log::trace!("Retrieving bot's username...");
|
||||
let username = me.username();
|
||||
|
||||
log::trace!("Parsing message text {text:?} as {username:?}...");
|
||||
let command = match Command::parse(text, username) {
|
||||
Ok(command) => {
|
||||
log::trace!("Message text parsed successfully as: {command:?}");
|
||||
command
|
||||
}
|
||||
Err(ParseError::WrongBotName(receiver)) => {
|
||||
log::debug!("Message is meant to be sent to {receiver:?}, while I'm running as {username:?}; skipping it.");
|
||||
return Ok(());
|
||||
}
|
||||
Err(ParseError::TooFewArguments { expected, found, .. }) |
|
||||
Err(ParseError::TooManyArguments { expected, found, .. }) => {
|
||||
log::debug!("Message text is a command with {found} arguments, but the command expected {expected}; handling as a malformed command.");
|
||||
Command::handle_malformed_simple(bot, message, expected, found).await
|
||||
.context("Impossibile gestire comando malformato semplice.")?;
|
||||
return Ok(());
|
||||
}
|
||||
Err(ParseError::IncorrectFormat(e)) => {
|
||||
log::debug!("Message text is a command with a custom format, but the parser returned the error {e:?}; handling as a malformed command.");
|
||||
Command::handle_malformed_complex(bot, message).await
|
||||
.context("Impossibile gestire comando malformato complesso.")?;
|
||||
return Ok(());
|
||||
}
|
||||
Err(ParseError::UnknownCommand(command)) => {
|
||||
log::debug!("Message text is command not present in the commands list {command:?}; handling it as an unknown command.");
|
||||
Command::handle_unknown(bot, message).await
|
||||
.context("Impossibile gestire comando sconosciuto.")?;
|
||||
return Ok(());
|
||||
}
|
||||
Err(ParseError::Custom(e)) => {
|
||||
log::debug!("Message text is a command, but the parser raised custom error {e:?}; handling it as a custom error.");
|
||||
let error = anyhow::format_err!(e);
|
||||
Command::handle_error_parse(&bot, &message, &error).await
|
||||
.context("Impossibile gestire comando con errore di parsing.")?;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
command.handle_self(bot, message, database).await
|
||||
.context("Impossibile gestire errore restituito dal comando.")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn dispatcher(&mut self) -> Dispatcher<Bot, anyhow::Error, DefaultKey> {
|
||||
log::debug!("Building dispatcher...");
|
||||
|
||||
let bot_name = self.me.user.username.as_ref().unwrap();
|
||||
log::trace!("Bot username is: @{bot_name:?}");
|
||||
|
||||
log::trace!("Determining pseudo-command regex...");
|
||||
let regex = Regex::new(&format!(r"^/[a-z0-9_]+(?:@{bot_name})?(?:\s+.*)?$")).unwrap();
|
||||
log::trace!("Pseudo-command regex is: {regex:?}");
|
||||
|
||||
let database = Arc::new(DatabaseInterface::new(self.database_url.clone()));
|
||||
|
||||
log::trace!("Building dispatcher...");
|
||||
|
@ -121,24 +178,8 @@ impl TelegramService {
|
|||
entry()
|
||||
// Messages
|
||||
.branch(Update::filter_message()
|
||||
// Pseudo-commands
|
||||
.branch(entry()
|
||||
// Only process commands matching the pseudo-command regex
|
||||
.filter(move |message: Message| -> bool {
|
||||
message
|
||||
.text()
|
||||
.is_some_and(|text| regex.is_match(text))
|
||||
})
|
||||
// Commands
|
||||
.branch(entry()
|
||||
// Only process commands matching a valid command, and parse their arguments
|
||||
.filter_command::<Command>()
|
||||
// Delegate handling
|
||||
.endpoint(Command::handle_self)
|
||||
)
|
||||
// No valid command was found
|
||||
.endpoint(Command::handle_unknown)
|
||||
)
|
||||
// Handle incoming messages
|
||||
.endpoint(Self::handle_message)
|
||||
)
|
||||
// Inline keyboard
|
||||
.branch(Update::filter_callback_query()
|
||||
|
|
12
src/utils/italian.rs
Normal file
12
src/utils/italian.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
use std::ops::Add;
|
||||
|
||||
pub fn countable_noun_suffix<T, U>(count: T, singular: &'static str, plural: &'static str)
|
||||
-> &'static str
|
||||
where
|
||||
T: Default + Add<usize, Output=U> + PartialEq<U>,
|
||||
{
|
||||
match count == (T::default() + 1) {
|
||||
true => singular,
|
||||
false => plural,
|
||||
}
|
||||
}
|
|
@ -4,3 +4,4 @@ pub mod time;
|
|||
pub mod version;
|
||||
pub mod anyhow_result;
|
||||
pub mod telegram_string;
|
||||
pub mod italian;
|
||||
|
|
Loading…
Reference in a new issue