Added docker building.

This commit is contained in:
2023-02-21 19:56:36 +00:00
parent f5b43573c8
commit b6ab9c8888
31 changed files with 1476 additions and 75 deletions

View File

@ -0,0 +1,176 @@
use std::{collections::HashMap, time::Duration};
use grammers_client::{Client, InputMessage, Update};
use regex::Regex;
use crate::{
bot::{filters::base::Filter, handlers::Handler},
utils::messages::get_message,
};
lazy_static::lazy_static! {
static ref SUPPORTED_CURS: Vec<&'static str> = vec![
"GBP",
"HUF",
"USD",
"EUR",
"CNY",
"NOK",
"UAH",
"SEK",
"CHF",
"KRW",
"JPY",
"KZT",
"PLN",
"TRY",
"AMD",
"RSD",
];
static ref CONVERTION_ALIASES: HashMap<&'static str, &'static str> = HashMap::from(
[
// GBP
("фунт", "GBP"),
// USD
("бакс", "USD"),
("доллар", "USD"),
// EUR
("евро", "EUR"),
// JPY
("иен", "JPY"),
("йен", "JPY"),
// KRW
("вон", "KRW"),
// CHF
("франк", "CHF"),
// SEK
("крон", "CHF"),
// CNY
("юан", "CNY"),
// UAH
("гривна", "UAH"),
("гривны", "UAH"),
("гривен", "UAH"),
("грен", "UAH"),
// KZT
("тенге", "KZT"),
("тэнге", "KZT"),
// PLN
("злот", "PLN"),
// TRY
("лир", "TRY"),
// AMD
("драм", "AMD"),
// RSD
("динар", "RSD"),
]
);
static ref CUR_REGEX: Regex = {
#[allow(clippy::clone_double_ref)]
let a = CONVERTION_ALIASES.keys()
.copied()
.chain(SUPPORTED_CURS.iter().copied())
.collect::<Vec<_>>()
.join("|");
Regex::new(format!(r"\s*(?P<cur_value>\d+([\.,]\d+)?)\s+(?P<cur_name>{a})").as_str()).unwrap()
};
}
#[derive(Clone)]
pub struct CurrencyTextFilter;
#[derive(Clone)]
pub struct CurrencyConverter {
client: reqwest::Client,
}
impl CurrencyConverter {
pub fn new() -> anyhow::Result<Self> {
let client = reqwest::ClientBuilder::new()
.timeout(Duration::from_secs(2))
.gzip(true)
.build()?;
Ok(Self { client })
}
}
/// This filter check if the message matches regex for currencies.
impl Filter for CurrencyTextFilter {
fn filter(&self, update: &Update) -> anyhow::Result<bool> {
let Some(message) = get_message(update) else {
return Ok(false);
};
Ok(CUR_REGEX.is_match(message.text()))
}
}
#[async_trait::async_trait]
impl Handler for CurrencyConverter {
async fn react(&self, _: &Client, update: &Update) -> anyhow::Result<()> {
let Some(message) = get_message(update) else{ return Ok(())};
let response = self
.client
.get("https://www.cbr-xml-daily.ru/daily_json.js")
.send()
.await?
.error_for_status()?
.json::<serde_json::Value>()
.await?;
let Some(valutes) = response
.get("Valute")
.and_then(serde_json::Value::as_object) else{
log::warn!("Can't get valutes fom response.");
return Ok(());
};
let mut calucates = Vec::new();
for capture in CUR_REGEX.captures_iter(message.text()) {
// We parse supplied value from message
let Some(num_value) = capture
.name("cur_value")
// Convert match to string.
.map(|mtch| mtch.as_str())
// Parse it.
.and_then(|val| val.parse::<f64>().ok()) else{
continue;
};
let cur_name = capture.name("cur_name").map(|mtch| mtch.as_str());
let Some(cur_name) = cur_name
// We check if the value is an alias.
.and_then(|val| CONVERTION_ALIASES.get(val).copied())
// get previous value if not.
.or(cur_name) else{
continue;
};
let calculated = valutes
.get(cur_name)
.and_then(|info| info.get("Value"))
.map(ToString::to_string)
.and_then(|value| value.as_str().parse::<f64>().ok())
.map(|multiplier| multiplier * num_value);
if let Some(value) = calculated {
calucates.push(format!(
"<pre>{num_value} {cur_name} = {value:.2} RUB</pre><br>"
));
}
}
if !calucates.is_empty() {
let mut bot_response =
String::from("<b>Полагаясь на текущий курс валют могу сказать следующее:</b>\n\n");
for calc in calucates {
bot_response.push_str(calc.as_str());
}
message
// We send it as silent, so we can filter this message later.
.reply(InputMessage::html(bot_response).silent(true))
.await?;
}
Ok(())
}
}