From 6c6c0fd62ba59d9f75d69705b792aeaa6174488f Mon Sep 17 00:00:00 2001 From: alemi Date: Wed, 3 Jan 2024 04:25:31 +0100 Subject: [PATCH] feat: added crude email notifier also default to console notifier enabled --- Cargo.toml | 5 +++- src/config.rs | 30 ++++++++++++++++---- src/main.rs | 23 ++++++++++++---- src/notifications/email.rs | 56 ++++++++++++++++++++++++++++++++++++++ src/notifications/mod.rs | 3 ++ 5 files changed, 106 insertions(+), 11 deletions(-) create mode 100644 src/notifications/email.rs diff --git a/Cargo.toml b/Cargo.toml index ef65ab8..e49d523 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,11 +25,13 @@ axum = "0.6.20" sqlx = { version = "0.7.3", features = ["runtime-tokio", "tls-rustls", "any"] } # notification providers teloxide = { version = "0.12.2", features = ["macros"], optional = true } +mail-send = { version = "0.4.6", optional = true } +tokio-rustls = { version = "0.25.0", optional = true } [features] default = [ "mysql", "sqlite", "postgres", - "telegram", + "telegram", "email" ] # db drivers mysql = ["sqlx/mysql"] @@ -37,3 +39,4 @@ sqlite = ["sqlx/sqlite"] postgres = ["sqlx/postgres"] # notifier providers telegram = ["dep:teloxide"] +email = ["dep:mail-send", "dep:tokio-rustls"] diff --git a/src/config.rs b/src/config.rs index ce138a8..86e94c7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -17,20 +17,40 @@ pub struct ConfigOverrides { pub date: bool, } -#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct ConfigNotifiers { - pub providers: Vec, + pub providers: Vec, +} + +// by default enable console notifier +impl Default for ConfigNotifiers { + fn default() -> Self { + ConfigNotifiers { + providers: vec![NotifierProvider::Console], + } + } } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub enum ConfigNotifierProvider { - ConsoleNotifier, +pub enum NotifierProvider { + Console, #[cfg(feature = "telegram")] - TelegramNotifier { + Telegram { token: String, chat_id: i64, }, + + #[cfg(feature = "email")] + Email { + server: String, + port: u16, + username: String, + password: String, + from: String, + to: String, + subject: String, + }, } fn _true() -> bool { true } diff --git a/src/main.rs b/src/main.rs index 344057e..ab18d88 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use std::{net::SocketAddr, io::Write}; use clap::{Parser, Subcommand}; use config::ConfigOverrides; -use crate::{storage::StorageProvider, routes::Context, notifications::console::ConsoleTracingNotifier, config::{Config, ConfigNotifierProvider}}; +use crate::{storage::StorageProvider, routes::Context, notifications::console::ConsoleTracingNotifier, config::{Config, NotifierProvider}}; mod notifications; @@ -64,9 +64,9 @@ async fn main() { match args.action { CliAction::Default => { let mut cfg = Config::default(); - cfg.notifiers.providers.push(ConfigNotifierProvider::ConsoleNotifier); + cfg.notifiers.providers.push(NotifierProvider::Console); #[cfg(feature = "telegram")] - cfg.notifiers.providers.push(ConfigNotifierProvider::TelegramNotifier { token: "asd".into(), chat_id: -1 }); + cfg.notifiers.providers.push(NotifierProvider::Telegram { token: "asd".into(), chat_id: -1 }); println!("{}", toml::to_string(&cfg).unwrap()); }, CliAction::Review { batch } => { @@ -112,18 +112,31 @@ async fn main() { for notifier in config.notifiers.providers { match notifier { - ConfigNotifierProvider::ConsoleNotifier => { + NotifierProvider::Console => { tracing::info!("registering console notifier"); state.register(Box::new(ConsoleTracingNotifier {})); }, #[cfg(feature = "telegram")] - ConfigNotifierProvider::TelegramNotifier { token, chat_id } => { + NotifierProvider::Telegram { token, chat_id } => { tracing::info!("registering telegram notifier for chat {}", chat_id); state.register(Box::new( notifications::telegram::TGNotifier::new(&token, chat_id) )); }, + + #[cfg(feature = "email")] + NotifierProvider::Email { + server, port, username, password, from, to, subject + } => { + tracing::info!("registering email notifier to {} on server {}:{} ('{}')", to, server, port, subject); + state.register(Box::new( + notifications::email::EmailNotifier::new( + &server, port, &username, &password, &from, &to, &subject + ).await + )); + + } } } diff --git a/src/notifications/email.rs b/src/notifications/email.rs new file mode 100644 index 0000000..cd3f61f --- /dev/null +++ b/src/notifications/email.rs @@ -0,0 +1,56 @@ +use mail_send::{SmtpClientBuilder, SmtpClient, mail_builder::MessageBuilder}; +use tokio::{sync::Mutex, net::TcpStream}; +use tokio_rustls::client::TlsStream; + +use crate::model::Page; + +use super::NotificationProcessor; + +pub struct EmailNotifier { + client: Mutex>>, + from: String, + to: String, + subject: String, +} + +impl EmailNotifier { + pub async fn new( + server: &str, + port: u16, + username: &str, + password: &str, + from: &str, + to: &str, + subject: &str, + ) -> Self { + let client = SmtpClientBuilder::new(server, port) + .implicit_tls(false) + .credentials((username, password)) + .connect() + .await + .unwrap(); + + EmailNotifier { + client: Mutex::new(client), + from: from.to_string(), + to: to.to_string(), + subject: subject.to_string() + } + } +} + +#[async_trait::async_trait] +impl NotificationProcessor for EmailNotifier { + async fn process(&self, notification: &Page) { + let message = MessageBuilder::new() + .from((notification.author.as_str(), notification.contact.as_ref().unwrap_or(&self.from).as_str())) + .to(vec![("Guestbook", self.to.as_str())]) + .subject(&self.subject) + .text_body(¬ification.body); + + if let Err(e) = self.client.lock().await.send(message).await { + tracing::error!("could not send message via email: {}", e); + } + + } +} diff --git a/src/notifications/mod.rs b/src/notifications/mod.rs index a1a02ea..ef7c05e 100644 --- a/src/notifications/mod.rs +++ b/src/notifications/mod.rs @@ -2,6 +2,9 @@ #[cfg(feature = "telegram")] pub mod telegram; +#[cfg(feature = "email")] +pub mod email; + pub mod console;