From 8f6d0a1511bddce534c9ad624d164852b5529f29 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Mon, 8 Jul 2024 05:44:56 +0200 Subject: [PATCH] Allow `/help` to be used to get info about a specific command Also tweaks other commands to be read and handle exceptions correctly --- src/telegram/commands/echo.rs | 4 +-- src/telegram/commands/fortune.rs | 10 +++--- src/telegram/commands/help.rs | 54 +++++++++++++++++++++++++++-- src/telegram/commands/mod.rs | 59 ++++++++++++++++++++++++++------ src/telegram/commands/start.rs | 10 +++--- 5 files changed, 111 insertions(+), 26 deletions(-) diff --git a/src/telegram/commands/echo.rs b/src/telegram/commands/echo.rs index 53b9f6c1..d57145b2 100644 --- a/src/telegram/commands/echo.rs +++ b/src/telegram/commands/echo.rs @@ -5,7 +5,7 @@ use teloxide::requests::Requester; use teloxide::types::{Message}; use super::{CommandResult}; -pub async fn handler(bot: Bot, message: Message, text: String) -> CommandResult { +pub async fn handler(bot: &Bot, message: &Message, text: &str) -> CommandResult { let text = format!( "๐Ÿ’ฌ {text}" ); @@ -14,7 +14,7 @@ pub async fn handler(bot: Bot, message: Message, text: String) -> CommandResult .send_message(message.chat.id, text) .reply_to_message_id(message.id) .await - .context("Failed to send message")?; + .context("Non รจ stato possibile inviare la risposta.")?; Ok(()) } diff --git a/src/telegram/commands/fortune.rs b/src/telegram/commands/fortune.rs index e53f969d..97a5c06e 100644 --- a/src/telegram/commands/fortune.rs +++ b/src/telegram/commands/fortune.rs @@ -187,11 +187,11 @@ impl Hash for FortuneKey { } } -pub async fn handler(bot: Bot, message: Message) -> CommandResult { +pub async fn handler(bot: &Bot, message: &Message) -> CommandResult { let today = chrono::Local::now().date_naive(); let author = message.from() - .context("Failed to get the user who sent the original message")?; + .context("Non รจ stato possibile determinare chi ha inviato questo comando.")?; let author_id = author.id; let key = FortuneKey {today, author_id}; @@ -206,20 +206,20 @@ pub async fn handler(bot: Bot, message: Message) -> CommandResult { .collect::>() .try_into(); if hash.is_err() { - anyhow::bail!("Failed to select random seed"); + anyhow::bail!("Non รจ stato possibile determinare il tuo oroscopo."); } let hash = hash.unwrap(); let mut rng = rand::rngs::SmallRng::from_seed(hash); let fortune = FORTUNES.choose(&mut rng) - .context("Failed to choose a fortune")?; + .context("Non รจ stato possibile selezionare il tuo oroscopo.")?; let _reply = bot .send_message(message.chat.id, fortune.to_string()) .reply_to_message_id(message.id) .await - .context("Failed to send message")?; + .context("Non รจ stato possibile inviare la risposta.")?; Ok(()) } \ No newline at end of file diff --git a/src/telegram/commands/help.rs b/src/telegram/commands/help.rs index 95b6268d..82294185 100644 --- a/src/telegram/commands/help.rs +++ b/src/telegram/commands/help.rs @@ -2,11 +2,11 @@ use anyhow::Context; use teloxide::Bot; use teloxide::payloads::SendMessageSetters; use teloxide::requests::Requester; -use teloxide::types::{Message}; +use teloxide::types::{BotCommand, Message}; use teloxide::utils::command::BotCommands; use super::{CommandResult}; -pub async fn handler(bot: Bot, message: Message) -> CommandResult { +pub async fn handler_all(bot: &Bot, message: &Message) -> CommandResult { let descriptions = super::Command::descriptions().to_string(); let text = format!("โ“ Sono disponibili i seguenti comandi:\n\n{descriptions}"); @@ -15,7 +15,55 @@ pub async fn handler(bot: Bot, message: Message) -> CommandResult { .send_message(message.chat.id, text) .reply_to_message_id(message.id) .await - .context("Failed to send message")?; + .context("Non รจ stato possibile inviare la risposta.")?; Ok(()) } + +pub async fn handler_specific(bot: &Bot, message: &Message, target: &str) -> CommandResult { + let me = bot + .get_me() + .await + .context("Non รจ stato possibile recuperare informazioni sul bot.")?; + + let me_username = me.username.as_ref() + .context("Non รจ stato possibile determinare l'username del bot.")?; + + let suffix = format!("@{me_username}"); + + let target = target.strip_prefix("/") + .unwrap_or_else(|| target); + + let target = target.strip_suffix(&suffix) + .unwrap_or_else(|| target); + + log::trace!("Stripped target command: {target:?}"); + + let all_commands: Vec = super::Command::bot_commands(); + + log::trace!("All commands are: {all_commands:?}"); + + let identify_command = |cmd: &&BotCommand| -> bool { + let command = &cmd.command; + + let command = command.strip_prefix("/") + .unwrap_or_else(|| command); + + target == command + }; + + let target = match all_commands.iter().find(identify_command) { + Some(bot_command) => bot_command, + None => anyhow::bail!("Non รจ stato possibile trovare il comando specificato."), + }; + + let text = format!("โ“ Il comando {}{}:\n\n{}", target.command, suffix, target.description); + + let _reply = bot + .send_message(message.chat.id, text) + .reply_to_message_id(message.id) + .await + .context("Non รจ stato possibile inviare la risposta.")?; + + Ok(()) +} \ No newline at end of file diff --git a/src/telegram/commands/mod.rs b/src/telegram/commands/mod.rs index d9350a5d..bd5e28a8 100644 --- a/src/telegram/commands/mod.rs +++ b/src/telegram/commands/mod.rs @@ -7,7 +7,7 @@ use teloxide::dispatching::{DefaultKey, Dispatcher, HandlerExt, UpdateFilterExt} use teloxide::dptree::entry; use teloxide::payloads::SendMessageSetters; use teloxide::requests::Requester; -use teloxide::types::{Message, Update}; +use teloxide::types::{ChatId, Message, MessageId, Update}; use teloxide::utils::command::BotCommands; mod start; @@ -15,13 +15,13 @@ mod fortune; mod echo; mod help; -#[derive(Debug, Clone, BotCommands)] +#[derive(Debug, Clone, PartialEq, Eq, BotCommands)] #[command(rename_rule = "lowercase")] pub(self) enum Command { #[command(description = "Invia messaggio di introduzione.")] Start, - #[command(description = "Visualizza l'elenco dei comandi disponibili.")] - Help, + #[command(description = "Visualizza l'elenco dei comandi disponibili, o mostra informazioni su uno specifico comando.")] + Help(String), #[command(description = "Mostra il tuo oroscopo di oggi.")] Fortune, #[command(description = "Ripeti il testo inviato.")] @@ -31,21 +31,58 @@ pub(self) enum Command { async fn handle_command(bot: Bot, command: Command, message: Message) -> CommandResult { log::trace!("Received command: {command:?}"); - match command { - Command::Start => start::handler(bot, message).await, - Command::Help => help::handler(bot, message).await, - Command::Fortune => fortune::handler(bot, message).await, - Command::Echo(text) => echo::handler(bot, message, text).await, + let result = match command { + Command::Start => start::handler(&bot, &message).await, + Command::Help(target) => match target.as_str() { + "" => help::handler_all(&bot, &message).await, + _ => help::handler_specific(&bot, &message, &target).await, + }, + Command::Fortune => fortune::handler(&bot, &message).await, + Command::Echo(text) => echo::handler(&bot, &message, &text).await, + }; + + if result.is_ok() { + return Ok(()) } + + let chat_id = message.chat.id; + let message_id = message.id; + let error = result.unwrap_err(); + + let result2 = error_command(&bot, chat_id, message_id, &error).await; + + if result2.is_ok() { + return Ok(()) + } + + let error2 = result2.unwrap_err(); + + log::error!("Command message {message_id:?} in {chat_id:?} errored out with `{error}`, and it was impossible to handle the error because of `{error2}`\n\n{error2:?}"); + + Ok(()) +} + +async fn error_command(bot: &Bot, chat_id: ChatId, message_id: MessageId, error: &Error) -> CommandResult { + log::debug!("Command message {message_id:?} in {chat_id:?} errored out with `{error}`"); + + let text = format!("โš ๏ธ {}", error.to_string()); + + let _reply = bot + .send_message(chat_id, text) + .reply_to_message_id(message_id) + .await + .context("Non รจ stato possibile inviare la risposta.")?; + + Ok(()) } async fn unknown_command(bot: Bot, message: Message) -> CommandResult { - log::trace!("Received an unknown command."); + log::debug!("Received an unknown command."); bot.send_message(message.chat.id, "โš ๏ธ Comando sconosciuto.") .reply_to_message_id(message.id) .await - .context("Failed to send message")?; + .context("Non รจ stato possibile inviare la risposta.")?; Ok(()) } diff --git a/src/telegram/commands/start.rs b/src/telegram/commands/start.rs index e28de61a..86e9a22f 100644 --- a/src/telegram/commands/start.rs +++ b/src/telegram/commands/start.rs @@ -5,9 +5,9 @@ use teloxide::requests::Requester; use teloxide::types::{Message}; use super::{CommandResult}; -pub async fn handler(bot: Bot, message: Message) -> CommandResult { +pub async fn handler(bot: &Bot, message: &Message) -> CommandResult { let author = message.from() - .context("Failed to get the user who sent the original message")?; + .context("Non รจ stato possibile determinare chi ha inviato questo comando.")?; let author_username = match author.username.as_ref() { None => { @@ -21,10 +21,10 @@ pub async fn handler(bot: Bot, message: Message) -> CommandResult { let me = bot .get_me() .await - .context("Failed to get information about self")?; + .context("Non รจ stato possibile recuperare informazioni sul bot.")?; let me_username = me.username.as_ref() - .context("Failed to get bot's username")?; + .context("Non รจ stato possibile determinare l'username del bot.")?; let text = format!( "๐Ÿ‘‹ Ciao {author_username}! Sono @{me_username}, il robot tuttofare della RYG!\n\n\ @@ -36,7 +36,7 @@ pub async fn handler(bot: Bot, message: Message) -> CommandResult { .send_message(message.chat.id, text) .reply_to_message_id(message.id) .await - .context("Failed to send message")?; + .context("Non รจ stato possibile inviare la risposta.")?; Ok(()) }