From bbbc158536dc6f365130fe46776d51ba5ac3e66e Mon Sep 17 00:00:00 2001 From: EAimTY Date: Fri, 29 Oct 2021 23:14:22 +0900 Subject: [PATCH] add `/toggle_mention` command --- Cargo.toml | 2 +- src/bot.rs | 239 ++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 183 insertions(+), 58 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 949894d..259c982 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bing-dict-telegram-bot" description = "A Telegram bot uses Bing Dictionary to translate words from Chinese to English or English to Chinese" -version = "0.2.1" +version = "0.2.2" authors = ["EAimTY "] edition = "2021" readme = "README.md" diff --git a/src/bot.rs b/src/bot.rs index c2183be..ab6873c 100644 --- a/src/bot.rs +++ b/src/bot.rs @@ -3,10 +3,10 @@ use futures_util::future::BoxFuture; use std::{collections::HashSet, sync::Arc}; use tgbot::{ longpoll::LongPoll, - methods::SendMessage, - types::{Command, Update, UpdateKind}, + methods::{GetMe, SendMessage}, + types::{Command, Me, MessageKind, Update, UpdateKind}, webhook::{self, HyperError}, - Api, ApiError, Config as ApiConfig, ParseProxyError, UpdateHandler, + Api, ApiError, Config as ApiConfig, ExecuteError, ParseProxyError, UpdateHandler, }; use thiserror::Error; use tokio::sync::RwLock; @@ -20,10 +20,19 @@ pub async fn run(config: &Config) -> Result<(), Error> { let api = Api::new(api_config)?; + let bot_info = api.execute(GetMe).await?; + if config.webhook == 0 { - LongPoll::new(api.clone(), Handler::new(api)).run().await; + LongPoll::new(api.clone(), Handler::new(api, bot_info)) + .run() + .await; } else { - webhook::run_server(([0, 0, 0, 0], config.webhook), "/", Handler::new(api)).await?; + webhook::run_server( + ([0, 0, 0, 0], config.webhook), + "/", + Handler::new(api, bot_info), + ) + .await?; } Ok(()) @@ -31,17 +40,21 @@ pub async fn run(config: &Config) -> Result<(), Error> { struct Context { api: Api, + bot_username: String, command_toggle: RwLock>, + mention_toggle: RwLock>, } #[derive(Clone)] struct Handler(Arc); impl Handler { - fn new(api: Api) -> Self { + fn new(api: Api, bot_info: Me) -> Self { Self(Arc::new(Context { api, + bot_username: format!("@{}", bot_info.username), command_toggle: RwLock::new(HashSet::new()), + mention_toggle: RwLock::new(HashSet::new()), })) } } @@ -58,36 +71,155 @@ impl UpdateHandler for Handler { let chat_id = message.get_chat_id(); let mut result = None; - if text.data.starts_with('/') { + if !text.data.starts_with('/') { + let command_toggle = context.command_toggle.read().await; + if command_toggle.contains(&chat_id) { + let text = text.data.trim(); + let mut input = None; + + let mention_toggle = context.mention_toggle.read().await; + if mention_toggle.contains(&chat_id) + || (!matches!(message.kind, MessageKind::Group { .. }) + && !matches!(message.kind, MessageKind::Supergroup { .. })) + { + input = Some(text); + } + + if text.starts_with(&context.bot_username) { + input = Some(text[context.bot_username.len()..].trim()); + } else if text.ends_with(&context.bot_username) { + input = + Some(text[..text.len() - context.bot_username.len()].trim()); + } + + if let Some(input) = input { + if !input.is_empty() { + result = match bing_dict::translate(input).await { + Ok(result) => Some(result.unwrap_or_else(|| { + String::from("No paraphrase found") + })), + Err(err) => { + eprintln!("{}", err); + return; + } + }; + } else { + result = Some(String::from("No input")); + } + } + } + } else { if let Ok(command) = Command::try_from(message) { - match command.get_name() { - "/dict" => { - let input = - command.get_message().get_text().unwrap().data[5..].trim(); - if !input.is_empty() { - result = match bing_dict::translate(input).await { - Ok(result) => Some(result.unwrap_or_else(|| { - String::from("No paraphrase found") - })), - Err(err) => { - eprintln!("{}", err); - return; + #[derive(PartialEq)] + enum ArgPos { + Left, + Right, + None, + } + + let mut pos = ArgPos::None; + + if command.get_args().first() == Some(&context.bot_username) { + pos = ArgPos::Left; + } else if command.get_args().last() == Some(&context.bot_username) { + pos = ArgPos::Right; + } + + if pos != ArgPos::None + || (!matches!( + command.get_message().kind, + MessageKind::Group { .. } + ) && !matches!( + command.get_message().kind, + MessageKind::Supergroup { .. } + )) + { + match command.get_name() { + "/dict" => { + let input; + + match pos { + ArgPos::Left => { + input = Some( + command.get_message().get_text().unwrap().data + [5..] + .trim() + .trim_start_matches(&context.bot_username) + .trim(), + ) + } + ArgPos::Right => { + input = Some( + command.get_message().get_text().unwrap().data + [5..] + .trim() + .trim_end_matches(&context.bot_username) + .trim(), + ) + } + ArgPos::None => { + input = Some( + command.get_message().get_text().unwrap().data + [5..] + .trim(), + ) + } + } + + if let Some(input) = input { + if !input.is_empty() { + result = match bing_dict::translate(input).await { + Ok(result) => { + Some(result.unwrap_or_else(|| { + String::from("No paraphrase found") + })) + } + Err(err) => { + eprintln!("{}", err); + return; + } + }; + } else { + result = Some(String::from("No input")); } - }; + } } - } - "/toggle" => { - let mut command_toggle = context.command_toggle.write().await; - if command_toggle.insert(chat_id) { - result = Some(String::from("OK. I will translate all non-command messages you send")); - } else { - command_toggle.remove(&chat_id); - result = Some(String::from("OK. I will only translate the words after the /dict command")); + + "/toggle_command" => { + let mut command_toggle = + context.command_toggle.write().await; + if command_toggle.insert(chat_id) { + result = Some(String::from("Okay. I will translate all non-command messages you send")); + } else { + command_toggle.remove(&chat_id); + result = Some(String::from("OK. I will only translate the words after the /dict command")); + } } - } - "/start" => { - result = Some(String::from( - r#" + + "/toggle_mention" => { + if matches!( + command.get_message().kind, + MessageKind::Group { .. } + ) || matches!( + command.get_message().kind, + MessageKind::Supergroup { .. } + ) { + let mut mention_toggle = + context.mention_toggle.write().await; + if mention_toggle.insert(chat_id) { + result = Some(String::from("Okay. Now you don't need to @ me to trigger a non-command message translation anymore")); + } else { + mention_toggle.remove(&chat_id); + result = Some(String::from("Fine. I will only react to non-command messages that mentioned me")); + } + } else { + result = Some(String::from("This is not a group chat")); + } + } + + "/start" => { + result = Some(String::from( + r#" This is a Telegram bot uses Bing Dictionary to translate words from Chinese to English or English to Chinese. /dict [word] - translate a word @@ -95,40 +227,31 @@ This is a Telegram bot uses Bing Dictionary to translate words from Chinese to E Use "/help" to get more information. "#, - )); - } - "/about" => { - result = Some(String::from( - r#" + )); + } + + "/about" => { + result = Some(String::from( + r#" A Telegram bot uses Bing Dictionary to translate words from Chinese to English or English to Chinese. https://github.com/EAimTY/bing-dict-telegram-bot "#, - )); - } - "/help" => { - result = Some(String::from( - r#" + )); + } + + "/help" => { + result = Some(String::from( + r#" /dict [word] - translate a word /toggle - toggle translate-all-messages mode for current chat /about - About this bot /help - Get this help message "#, - )); - } - _ => {} - } - } - } else { - let command_toggle = context.command_toggle.read().await; - if command_toggle.contains(&chat_id) { - result = match bing_dict::translate(&text.data).await { - Ok(result) => Some( - result.unwrap_or_else(|| String::from("No paraphrase found")), - ), - Err(err) => { - eprintln!("{}", err); - return; + )); + } + + _ => {} } } } @@ -151,6 +274,8 @@ pub enum Error { #[error(transparent)] Api(#[from] ApiError), #[error(transparent)] + Execute(#[from] ExecuteError), + #[error(transparent)] Hyper(#[from] HyperError), #[error(transparent)] ParseProxy(#[from] ParseProxyError),