From d93d12e48a1ec470394112ba0e8caf55bf956bb9 Mon Sep 17 00:00:00 2001 From: Pavel Kirilin <win10@list.ru> Date: Thu, 23 Feb 2023 22:14:59 +0000 Subject: [PATCH] Added weather. --- src/bot/filters/message_fitlers.rs | 13 +++ src/bot/handlers/basic/mod.rs | 1 + src/bot/handlers/basic/weather_forecaster.rs | 84 ++++++++++++++++++++ src/bot/handlers/fun/mod.rs | 1 + src/bot/handlers/fun/repeator.rs | 15 ++++ src/bot/main.rs | 21 ++++- 6 files changed, 131 insertions(+), 4 deletions(-) create mode 100644 src/bot/handlers/basic/weather_forecaster.rs create mode 100644 src/bot/handlers/fun/repeator.rs diff --git a/src/bot/filters/message_fitlers.rs b/src/bot/filters/message_fitlers.rs index f2d973d..6533d4a 100644 --- a/src/bot/filters/message_fitlers.rs +++ b/src/bot/filters/message_fitlers.rs @@ -1,4 +1,5 @@ use grammers_client::Update; +use regex::Regex; use crate::utils::messages::get_message; @@ -31,9 +32,14 @@ pub struct ExcludedChatsFilter(pub Vec<i64>); #[derive(Clone)] pub struct MessageDirectionFilter(pub MessageDirection); +/// Filters text by predicate. #[derive(Clone)] pub struct TextFilter<'a>(pub &'a [&'a str], pub TextMatchMethod); +/// Filters using provided regex. +#[derive(Clone)] +pub struct RegexFilter(pub Regex); + impl Filter for ExcludedChatsFilter { fn filter(&self, update: &Update) -> anyhow::Result<bool> { let a = match update { @@ -89,3 +95,10 @@ impl Filter for SilentFilter { Ok(!message.silent()) } } + +impl Filter for RegexFilter { + fn filter(&self, update: &Update) -> anyhow::Result<bool> { + let Some(message) = get_message(update) else {return Ok(false)}; + Ok(self.0.is_match(message.text())) + } +} diff --git a/src/bot/handlers/basic/mod.rs b/src/bot/handlers/basic/mod.rs index 3f81a21..acc4306 100644 --- a/src/bot/handlers/basic/mod.rs +++ b/src/bot/handlers/basic/mod.rs @@ -1,3 +1,4 @@ pub mod currency_converter; pub mod get_chat_id; pub mod help; +pub mod weather_forecaster; diff --git a/src/bot/handlers/basic/weather_forecaster.rs b/src/bot/handlers/basic/weather_forecaster.rs new file mode 100644 index 0000000..2edd4a7 --- /dev/null +++ b/src/bot/handlers/basic/weather_forecaster.rs @@ -0,0 +1,84 @@ +use std::time::Duration; + +use grammers_client::{Client, InputMessage, Update}; +use rand::seq::SliceRandom; + +use crate::{bot::handlers::Handler, utils::messages::get_message}; +use rand::rngs::OsRng; + +#[derive(Clone)] +pub struct WeatherForecaster { + client: reqwest::Client, +} + +impl WeatherForecaster { + pub fn new() -> anyhow::Result<Self> { + let client = reqwest::ClientBuilder::new() + .timeout(Duration::from_secs(2)) + .gzip(true) + .build()?; + Ok(Self { client }) + } +} + +#[async_trait::async_trait] +impl Handler for WeatherForecaster { + async fn react(&self, _: &Client, update: &Update) -> anyhow::Result<()> { + let Some(message) = get_message(update) else { + return Ok(()); + }; + let Some(city) = message.text().strip_prefix(".w").map(str::trim) else { + return Ok(()); + }; + + let response = self + .client + .get(format!("https://wttr.in/{city}?0QT")) + .header("user-agent", "curl/7.0") + .send() + .await?; + + if response.status() == reqwest::StatusCode::NOT_FOUND { + message + .reply( + [ + "Лол, ты Ñам-то понÑл что напиÑал?", + "Ðе, Ñ Ñ…Ð· где Ñто.", + "ЕÑли ты дейÑтвительно тут живешь, то ÑочувÑтвую.", + ] + .choose(&mut OsRng) + .copied() + .unwrap(), + ) + .await?; + return Ok(()); + } + + if response.status() != reqwest::StatusCode::OK { + message.reply("Ð¡ÐµÑ€Ð²Ð¸Ñ Ð½ÐµÐ´Ð¾Ñтупен. Попробуй потом.").await?; + return Ok(()); + } + + let forecast = response.text().await?; + + message + .reply( + InputMessage::html( + forecast + // We need to wrap every line in pre tag. + .split('\n') + .filter(|line| !line.trim().is_empty()) + .map(|line| format!("<pre>{line}</pre>\n")) + // Also we add a message at the end by chaining another + // iterable. + .chain([String::from("Ваш прогноз.")]) + // And collect interator to final HTML string. + .collect::<String>(), + ) + .silent(true), + ) + .await?; + + Ok(()) + } +} diff --git a/src/bot/handlers/fun/mod.rs b/src/bot/handlers/fun/mod.rs index 8d24276..46355f6 100644 --- a/src/bot/handlers/fun/mod.rs +++ b/src/bot/handlers/fun/mod.rs @@ -1,3 +1,4 @@ pub mod blyaficator; pub mod greeter; +pub mod repeator; pub mod rotator; diff --git a/src/bot/handlers/fun/repeator.rs b/src/bot/handlers/fun/repeator.rs new file mode 100644 index 0000000..20398e3 --- /dev/null +++ b/src/bot/handlers/fun/repeator.rs @@ -0,0 +1,15 @@ +use grammers_client::{Client, Update}; + +use crate::{bot::handlers::Handler, utils::messages::get_message}; + +#[derive(Clone)] +pub struct Repeator; + +#[async_trait::async_trait] +impl Handler for Repeator { + async fn react(&self, _: &Client, update: &Update) -> anyhow::Result<()> { + let Some(message) = get_message(update) else { return Ok(()) }; + message.respond(message.text()).await?; + Ok(()) + } +} diff --git a/src/bot/main.rs b/src/bot/main.rs index 1288014..5ddd382 100644 --- a/src/bot/main.rs +++ b/src/bot/main.rs @@ -4,14 +4,15 @@ use crate::args::BotConfig; use grammers_client::{Client, Config, SignInError, Update}; use grammers_session::Session; use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; +use regex::Regex; use tokio::sync::RwLock; use super::{ filters::{ filtered_handler::FilteredHandler, message_fitlers::{ - ExcludedChatsFilter, MessageDirection, MessageDirectionFilter, SilentFilter, - TextFilter, TextMatchMethod, + ExcludedChatsFilter, MessageDirection, MessageDirectionFilter, RegexFilter, + SilentFilter, TextFilter, TextMatchMethod, }, }, handlers::{ @@ -19,8 +20,9 @@ use super::{ currency_converter::{CurrencyConverter, CurrencyTextFilter}, get_chat_id::GetChatId, help::Help, + weather_forecaster::WeatherForecaster, }, - fun::{blyaficator::Blyaficator, greeter::Greeter, rotator::Rotator}, + fun::{blyaficator::Blyaficator, greeter::Greeter, repeator::Repeator, rotator::Rotator}, Handler, }, }; @@ -90,11 +92,13 @@ async fn handle_with_log(handler: Box<dyn Handler>, client: Client, update_data: /// /// Also, every available handler is defined here. async fn run(args: BotConfig, client: Client) -> anyhow::Result<()> { + let me = client.get_me().await?; let handlers: Vec<FilteredHandler> = vec![ // Printing help. FilteredHandler::new(Help).add_filter(TextFilter(&[".h"], TextMatchMethod::IMatches)), // Greeting my fellow humans. FilteredHandler::new(Greeter) + .add_filter(ExcludedChatsFilter(vec![me.id()])) .add_filter(SilentFilter) .add_filter(MessageDirectionFilter(MessageDirection::Incoming)) .add_filter(TextFilter(&["привет"], TextMatchMethod::IStartsWith)) @@ -115,6 +119,16 @@ async fn run(args: BotConfig, client: Client) -> anyhow::Result<()> { FilteredHandler::new(Rotator) .add_filter(SilentFilter) .add_filter(TextFilter(&[".rl"], TextMatchMethod::IStartsWith)), + // Weather forecast. + FilteredHandler::new(WeatherForecaster::new()?) + .add_filter(SilentFilter) + .add_filter(TextFilter(&[".w"], TextMatchMethod::IStartsWith)), + // Smiley repeator. + FilteredHandler::new(Repeator) + .add_filter(ExcludedChatsFilter(vec![me.id()])) + .add_filter(MessageDirectionFilter(MessageDirection::Incoming)) + .add_filter(SilentFilter) + .add_filter(RegexFilter(Regex::new("^[)0]+$")?)), ]; let mut errors_count = 0; @@ -154,7 +168,6 @@ async fn run(args: BotConfig, client: Client) -> anyhow::Result<()> { } Ok(()) } - pub async fn bot_life( args: BotConfig, web_code: Arc<RwLock<Option<String>>>, -- GitLab