Skip to content

Commit

Permalink
chore: split modules
Browse files Browse the repository at this point in the history
  • Loading branch information
notdodo committed Oct 26, 2024
1 parent 1488321 commit 3b573cd
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 148 deletions.
107 changes: 107 additions & 0 deletions app/bot/src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use aws_sdk_dynamodb::Client as DynamoDbClient;
use teloxide::{
payloads::SendMessageSetters,
prelude::{Bot, Requester},
types::{LinkPreviewOptions, Message, ParseMode},
utils::command::BotCommands,
};

use crate::station;
pub(crate) mod utils;

#[derive(BotCommands, Clone)]
#[command(rename_rule = "lowercase")]
pub(crate) enum BaseCommand {
/// Visualizza la lista dei comandi
Help,
/// Ottieni informazioni riguardanti il bot
Info,
/// Inizia ad interagire con il bot
Start,
/// Visualizza la lista delle stazioni disponibili
Stazioni,
}

pub(crate) async fn base_commands_handler(
bot: Bot,
msg: Message,
cmd: BaseCommand,
) -> Result<(), teloxide::RequestError> {
let text = match cmd {
BaseCommand::Help => BaseCommand::descriptions().to_string(),
BaseCommand::Start => {
if msg.chat.is_group() || msg.chat.is_supergroup() {
format!("Ciao {}! Scrivete il nome di una stazione da monitorare (e.g. /Cesena o `/S. Carlo`)
o cercatene una con /stazioni",
msg.chat.title().unwrap_or(""))
} else {
format!("Ciao @{}! Scrivi il nome di una stazione da monitorare (e.g. `Cesena` o `/S. Carlo`) \
o cercane una con /stazioni",
msg.chat.username().unwrap_or(msg.chat.first_name().unwrap_or("")))
}
}
BaseCommand::Stazioni => station::stations().join("\n"),
BaseCommand::Info => {
let info = "Bot Telegram che permette di leggere i livello idrometrici dei fiumi dell'Emilia Romagna \
I dati idrometrici sono ottenuti dalle API messe a disposizione da allertameteo.regione.emilia-romagna.it\n\n\
Il progetto è completamente open-source (https://github.com/notdodo/erfiume_bot).\n\
Per donazioni per mantenere il servizio attivo: buymeacoffee.com/d0d0\n\n\
Inizia con /start o /stazioni";
info.to_string()
}
};

bot.send_message(msg.chat.id, utils::escape_markdown_v2(&text))
.link_preview_options(LinkPreviewOptions {
is_disabled: true,
url: None,
prefer_small_media: false,
prefer_large_media: false,
show_above_text: false,
})
.parse_mode(ParseMode::MarkdownV2)
.await?;

Ok(())
}

pub(crate) async fn message_handler(
bot: &Bot,
msg: &Message,
dynamodb_client: DynamoDbClient,
) -> Result<teloxide::prelude::Message, teloxide::RequestError> {
let text = msg.text().unwrap();
let text = match station::search::get_station(
&dynamodb_client,
text.to_string(),
"Stazioni",
)
.await
{
Ok(Some(item)) => {
if item.nomestaz != text {
format!("{}\nSe non è la stazione corretta prova ad affinare la ricerca.", item.create_station_message())
}else {
item.create_station_message().to_string()
}
}
Err(_) | Ok(None) => "Nessuna stazione trovata con la parola di ricerca.\nInserisci esattamente il nome che vedi dalla pagina https://allertameteo.regione.emilia-romagna.it/livello-idrometrico\nAd esempio 'Cesena', 'Lavino di Sopra' o 'S. Carlo'.\nSe non sai quale cercare prova con /stazioni".to_string()
};
let mut message = text.clone();
if fastrand::choose_multiple(0..10, 1)[0] == 8 {
message = format!("{}\n\nContribuisci al progetto per mantenerlo attivo e sviluppare nuove funzionalità tramite una donazione: https://buymeacoffee.com/d0d0", text);
}
if fastrand::choose_multiple(0..50, 1)[0] == 8 {
message = format!("{}\n\nEsplora o contribuisci al progetto open-source per sviluppare nuove funzionalità: https://github.com/notdodo/erfiume_bot", text);
}
bot.send_message(msg.chat.id, utils::escape_markdown_v2(&message))
.link_preview_options(LinkPreviewOptions {
is_disabled: false,
url: None,
prefer_small_media: true,
prefer_large_media: false,
show_above_text: false,
})
.parse_mode(ParseMode::MarkdownV2)
.await
}
20 changes: 20 additions & 0 deletions app/bot/src/commands/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
pub(crate) fn escape_markdown_v2(text: &str) -> String {
text.replace("\\", "\\\\")
.replace("_", "\\_")
.replace("*", "\\*")
.replace("[", "\\[")
.replace("]", "\\]")
.replace("(", "\\(")
.replace(")", "\\)")
.replace("~", "\\~")
.replace(">", "\\>")
.replace("#", "\\#")
.replace("+", "\\+")
.replace("-", "\\-")
.replace("=", "\\=")
.replace("|", "\\|")
.replace("{", "\\{")
.replace("}", "\\}")
.replace(".", "\\.")
.replace("!", "\\!")
}
133 changes: 8 additions & 125 deletions app/bot/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,18 @@ use aws_config::BehaviorVersion;
use aws_sdk_dynamodb::Client as DynamoDbClient;
use lambda_runtime::{service_fn, Error as LambdaError, LambdaEvent};
use serde_json::{json, Value};
use station::search::get_station;
use teloxide::{
dispatching::{HandlerExt, UpdateFilterExt},
dptree::deps,
payloads::SendMessageSetters,
prelude::{dptree, Bot, Requester, Update},
respond,
types::{LinkPreviewOptions, Me, Message, ParseMode},
utils::command::BotCommands,
types::{Me, Message},
};
use tracing::{info, instrument};
use tracing_subscriber::EnvFilter;
mod commands;
mod station;

#[derive(BotCommands, Clone)]
#[command(rename_rule = "lowercase")]
enum BaseCommand {
/// Visualizza la lista dei comandi
Help,
/// Ottieni informazioni riguardanti il bot
Info,
/// Inizia ad interagire con il bot
Start,
/// Visualizza la lista delle stazioni disponibili
Stazioni,
}

#[tokio::main]
async fn main() -> Result<(), LambdaError> {
tracing_subscriber::fmt()
Expand All @@ -49,7 +34,7 @@ async fn main() -> Result<(), LambdaError> {
async fn lambda_handler(event: LambdaEvent<Value>) -> Result<Value, LambdaError> {
let bot = Bot::from_env();
let me: Me = bot.get_me().await?;
info!("{:?}", event);
info!("{:?}", event.payload);

let outer_json: Value = serde_json::from_value(
event
Expand All @@ -66,21 +51,13 @@ async fn lambda_handler(event: LambdaEvent<Value>) -> Result<Value, LambdaError>
let handler = Update::filter_message()
.branch(
dptree::entry()
.filter_command::<BaseCommand>()
.endpoint(base_commands_handler),
.filter_command::<commands::BaseCommand>()
.endpoint(commands::base_commands_handler),
)
.branch(dptree::endpoint(|msg: Message, bot: Bot| async move {
let text = message_handler(&msg);
bot.send_message(msg.chat.id, escape_markdown_v2(&text.await.unwrap()))
.link_preview_options(LinkPreviewOptions {
is_disabled: false,
url: None,
prefer_small_media: true,
prefer_large_media: false,
show_above_text: false,
})
.parse_mode(ParseMode::MarkdownV2)
.await?;
let shared_config = aws_config::load_defaults(BehaviorVersion::latest()).await;
let dynamodb_client = DynamoDbClient::new(&shared_config);
commands::message_handler(&bot, &msg, dynamodb_client).await?;
respond(())
}));

Expand All @@ -90,97 +67,3 @@ async fn lambda_handler(event: LambdaEvent<Value>) -> Result<Value, LambdaError>
"statusCode": 200,
}))
}

fn escape_markdown_v2(text: &str) -> String {
text.replace("\\", "\\\\")
.replace("_", "\\_")
.replace("*", "\\*")
.replace("[", "\\[")
.replace("]", "\\]")
.replace("(", "\\(")
.replace(")", "\\)")
.replace("~", "\\~")
.replace(">", "\\>")
.replace("#", "\\#")
.replace("+", "\\+")
.replace("-", "\\-")
.replace("=", "\\=")
.replace("|", "\\|")
.replace("{", "\\{")
.replace("}", "\\}")
.replace(".", "\\.")
.replace("!", "\\!")
}

async fn base_commands_handler(
bot: Bot,
msg: Message,
cmd: BaseCommand,
) -> Result<(), teloxide::RequestError> {
let text = match cmd {
BaseCommand::Help => BaseCommand::descriptions().to_string(),
BaseCommand::Start => {
if msg.chat.is_group() || msg.chat.is_supergroup() {
format!("Ciao {}! Scrivete il nome di una stazione da monitorare (e.g. /Cesena o `/S. Carlo`)
o cercatene una con /stazioni",
msg.chat.title().unwrap_or(""))
} else {
format!("Ciao @{}! Scrivi il nome di una stazione da monitorare (e.g. `Cesena` o `/S. Carlo`) \
o cercane una con /stazioni",
msg.chat.username().unwrap_or(msg.chat.first_name().unwrap_or("")))
}
}
BaseCommand::Stazioni => station::stations().join("\n"),
BaseCommand::Info => {
let info = "Bot Telegram che permette di leggere i livello idrometrici dei fiumi dell'Emilia Romagna \
I dati idrometrici sono ottenuti dalle API messe a disposizione da allertameteo.regione.emilia-romagna.it\n\n\
Il progetto è completamente open-source (https://github.com/notdodo/erfiume_bot).\n\
Per donazioni per mantenere il servizio attivo: buymeacoffee.com/d0d0\n\n\
Inizia con /start o /stazioni";
info.to_string()
}
};

bot.send_message(msg.chat.id, escape_markdown_v2(&text))
.link_preview_options(LinkPreviewOptions {
is_disabled: true,
url: None,
prefer_small_media: false,
prefer_large_media: false,
show_above_text: false,
})
.parse_mode(ParseMode::MarkdownV2)
.await?;

Ok(())
}

async fn message_handler(message: &Message) -> Result<String, teloxide::RequestError> {
let shared_config = aws_config::load_defaults(BehaviorVersion::latest()).await;
let dynamodb_client = DynamoDbClient::new(&shared_config);
let msg = message.text().unwrap();
let text = match get_station(
&dynamodb_client,
msg.to_string(),
"Stazioni",
)
.await
{
Ok(Some(item)) => {
if item.nomestaz != msg {
format!("{}\nSe non è la stazione corretta prova ad affinare la ricerca.", item.create_station_message())
}else {
item.create_station_message().to_string()
}
}
Err(_) | Ok(None) => "Nessuna stazione trovata con la parola di ricerca.\nInserisci esattamente il nome che vedi dalla pagina https://allertameteo.regione.emilia-romagna.it/livello-idrometrico\nAd esempio 'Cesena', 'Lavino di Sopra' o 'S. Carlo'.\nSe non sai quale cercare prova con /stazioni".to_string()
};
let mut message = text.clone();
if fastrand::choose_multiple(0..10, 1)[0] == 8 {
message = format!("{}\n\nContribuisci al progetto per mantenerlo attivo e sviluppare nuove funzionalità tramite una donazione: https://buymeacoffee.com/d0d0", text);
}
if fastrand::choose_multiple(0..50, 1)[0] == 8 {
message = format!("{}\n\nEsplora o contribuisci al progetto open-source per sviluppare nuove funzionalità: https://github.com/notdodo/erfiume_bot", text);
}
Ok(message)
}
2 changes: 1 addition & 1 deletion app/bot/src/station/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pub mod search;
pub(crate) mod search;

use chrono::{DateTime, TimeZone};
use chrono_tz::Europe::Rome;
Expand Down
23 changes: 19 additions & 4 deletions app/bot/src/station/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use super::{stations, Stazione, UNKNOWN_VALUE};

fn fuzzy_search(search: &str) -> Option<String> {
let stations = stations();
let closest_match = stations
stations
.iter()
.map(|s: &String| {
(
Expand All @@ -19,9 +19,7 @@ fn fuzzy_search(search: &str) -> Option<String> {
})
.filter(|(_, score)| *score < 4)
.min_by_key(|(_, score)| *score)
.map(|(station, _)| station.clone()); // Map to String and clone the station name

closest_match
.map(|(station, _)| station.clone())
}

pub async fn get_station(
Expand Down Expand Up @@ -175,4 +173,21 @@ mod tests {

assert_eq!(fuzzy_search(&message), expected);
}

#[test]
fn parse_string_field_yields_correct_value() {
let expected = "this is a string".to_string();
let item = HashMap::from([("field".to_string(), AttributeValue::S(expected.clone()))]);
assert_eq!(parse_string_field(&item, "field").unwrap(), expected);
}

#[test]
fn parse_optional_number_field_yields_correct_value() {
let expected = 4;
let item = HashMap::from([("field".to_string(), AttributeValue::N(expected.to_string()))]);
assert_eq!(
parse_optional_number_field::<i16>(&item, "field").unwrap(),
Some(expected)
);
}
}
19 changes: 1 addition & 18 deletions pulumi/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
iam,
lambda_,
scheduler,
secretsmanager,
)

from telegram_provider import Webhook
Expand All @@ -19,7 +18,7 @@
SYNC_MINUTES_RATE_NORMAL = 24 * 60 # Once a day
SYNC_MINUTES_RATE_MEDIUM = 2 * 60 # Every two hours
SYNC_MINUTES_RATE_EMERGENCY = 20
EMERGENCY = True
EMERGENCY = False
CUSTOM_DOMAIN_NAME = "erfiume.thedodo.xyz"

stazioni_table = dynamodb.Table(
Expand Down Expand Up @@ -52,13 +51,6 @@
),
)

telegram_token_secret = secretsmanager.Secret(
f"{RESOURCES_PREFIX}-telegram-bot-token",
name="telegram-bot-token",
description="The Telegram Bot token for erfiume_bot",
recovery_window_in_days=7,
)

fetcher_role = iam.Role(
f"{RESOURCES_PREFIX}-fetcher",
name=f"{RESOURCES_PREFIX}-fetcher",
Expand Down Expand Up @@ -141,15 +133,6 @@
],
"Resources": [chats_table.arn],
},
{
"Effect": "Allow",
"Actions": [
"secretsmanager:GetSecretValue",
],
"Resources": [
telegram_token_secret.arn,
],
},
],
).json,
)
Expand Down

0 comments on commit 3b573cd

Please sign in to comment.