Added weather.
This commit is contained in:
@ -1,4 +1,5 @@
|
|||||||
use grammers_client::Update;
|
use grammers_client::Update;
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
use crate::utils::messages::get_message;
|
use crate::utils::messages::get_message;
|
||||||
|
|
||||||
@ -31,9 +32,14 @@ pub struct ExcludedChatsFilter(pub Vec<i64>);
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct MessageDirectionFilter(pub MessageDirection);
|
pub struct MessageDirectionFilter(pub MessageDirection);
|
||||||
|
|
||||||
|
/// Filters text by predicate.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct TextFilter<'a>(pub &'a [&'a str], pub TextMatchMethod);
|
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 {
|
impl Filter for ExcludedChatsFilter {
|
||||||
fn filter(&self, update: &Update) -> anyhow::Result<bool> {
|
fn filter(&self, update: &Update) -> anyhow::Result<bool> {
|
||||||
let a = match update {
|
let a = match update {
|
||||||
@ -89,3 +95,10 @@ impl Filter for SilentFilter {
|
|||||||
Ok(!message.silent())
|
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()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
pub mod currency_converter;
|
pub mod currency_converter;
|
||||||
pub mod get_chat_id;
|
pub mod get_chat_id;
|
||||||
pub mod help;
|
pub mod help;
|
||||||
|
pub mod weather_forecaster;
|
||||||
|
84
src/bot/handlers/basic/weather_forecaster.rs
Normal file
84
src/bot/handlers/basic/weather_forecaster.rs
Normal file
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
pub mod blyaficator;
|
pub mod blyaficator;
|
||||||
pub mod greeter;
|
pub mod greeter;
|
||||||
|
pub mod repeator;
|
||||||
pub mod rotator;
|
pub mod rotator;
|
||||||
|
15
src/bot/handlers/fun/repeator.rs
Normal file
15
src/bot/handlers/fun/repeator.rs
Normal file
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
@ -4,14 +4,15 @@ use crate::args::BotConfig;
|
|||||||
use grammers_client::{Client, Config, SignInError, Update};
|
use grammers_client::{Client, Config, SignInError, Update};
|
||||||
use grammers_session::Session;
|
use grammers_session::Session;
|
||||||
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
|
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
|
||||||
|
use regex::Regex;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
filters::{
|
filters::{
|
||||||
filtered_handler::FilteredHandler,
|
filtered_handler::FilteredHandler,
|
||||||
message_fitlers::{
|
message_fitlers::{
|
||||||
ExcludedChatsFilter, MessageDirection, MessageDirectionFilter, SilentFilter,
|
ExcludedChatsFilter, MessageDirection, MessageDirectionFilter, RegexFilter,
|
||||||
TextFilter, TextMatchMethod,
|
SilentFilter, TextFilter, TextMatchMethod,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
handlers::{
|
handlers::{
|
||||||
@ -19,8 +20,9 @@ use super::{
|
|||||||
currency_converter::{CurrencyConverter, CurrencyTextFilter},
|
currency_converter::{CurrencyConverter, CurrencyTextFilter},
|
||||||
get_chat_id::GetChatId,
|
get_chat_id::GetChatId,
|
||||||
help::Help,
|
help::Help,
|
||||||
|
weather_forecaster::WeatherForecaster,
|
||||||
},
|
},
|
||||||
fun::{blyaficator::Blyaficator, greeter::Greeter, rotator::Rotator},
|
fun::{blyaficator::Blyaficator, greeter::Greeter, repeator::Repeator, rotator::Rotator},
|
||||||
Handler,
|
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.
|
/// Also, every available handler is defined here.
|
||||||
async fn run(args: BotConfig, client: Client) -> anyhow::Result<()> {
|
async fn run(args: BotConfig, client: Client) -> anyhow::Result<()> {
|
||||||
|
let me = client.get_me().await?;
|
||||||
let handlers: Vec<FilteredHandler> = vec![
|
let handlers: Vec<FilteredHandler> = vec![
|
||||||
// Printing help.
|
// Printing help.
|
||||||
FilteredHandler::new(Help).add_filter(TextFilter(&[".h"], TextMatchMethod::IMatches)),
|
FilteredHandler::new(Help).add_filter(TextFilter(&[".h"], TextMatchMethod::IMatches)),
|
||||||
// Greeting my fellow humans.
|
// Greeting my fellow humans.
|
||||||
FilteredHandler::new(Greeter)
|
FilteredHandler::new(Greeter)
|
||||||
|
.add_filter(ExcludedChatsFilter(vec![me.id()]))
|
||||||
.add_filter(SilentFilter)
|
.add_filter(SilentFilter)
|
||||||
.add_filter(MessageDirectionFilter(MessageDirection::Incoming))
|
.add_filter(MessageDirectionFilter(MessageDirection::Incoming))
|
||||||
.add_filter(TextFilter(&["привет"], TextMatchMethod::IStartsWith))
|
.add_filter(TextFilter(&["привет"], TextMatchMethod::IStartsWith))
|
||||||
@ -115,6 +119,16 @@ async fn run(args: BotConfig, client: Client) -> anyhow::Result<()> {
|
|||||||
FilteredHandler::new(Rotator)
|
FilteredHandler::new(Rotator)
|
||||||
.add_filter(SilentFilter)
|
.add_filter(SilentFilter)
|
||||||
.add_filter(TextFilter(&[".rl"], TextMatchMethod::IStartsWith)),
|
.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;
|
let mut errors_count = 0;
|
||||||
@ -154,7 +168,6 @@ async fn run(args: BotConfig, client: Client) -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn bot_life(
|
pub async fn bot_life(
|
||||||
args: BotConfig,
|
args: BotConfig,
|
||||||
web_code: Arc<RwLock<Option<String>>>,
|
web_code: Arc<RwLock<Option<String>>>,
|
||||||
|
Reference in New Issue
Block a user