diff --git a/Cargo.lock b/Cargo.lock index fc4464c..3f82443 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3359,6 +3359,7 @@ dependencies = [ "intl-memoizer", "log", "matrix-sdk", + "percent-encoding", "petname", "rand 0.8.5", "rand_core 0.6.4", diff --git a/Cargo.toml b/Cargo.toml index 8bc43e0..0156190 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ fluent-langneg = "0.13.0" intl-memoizer = "0.5.1" log = "0.4.17" matrix-sdk = { version = "0.6.2", default-features = false, features = ["rustls-tls"] } +percent-encoding = "2.2.0" petname = { version = "1.1.3", optional = true, default-features = false, features = ["std_rng", "default_dictionary"] } rand = "0.8.5" rand_core = { version = "0.6.4", features = ["std"] } diff --git a/src/config.rs b/src/config.rs index ee21dd8..061579e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -55,10 +55,17 @@ pub struct Config { pub matrix_server: String, #[serde(default = "Config::default_matrix_user")] pub matrix_user: String, + /// Our address, with protocol and hostname but without path or trailing `/` + /// It is used to make hyperlinks that should be clickable from anywhere. + #[serde(default = "Config::default_public_address")] + pub public_address: String, /// Are we behind a reverse proxy? /// Determines whether we assume client address is in a Forwarded header or socket address. #[serde(default = "Config::default_reverse_proxy")] pub reverse_proxy: bool, + /// Our root url path (without protocol and hostname) + /// Useful when used behind a reverse proxy in a pseudo-subdirectory. + /// Should start and end with `/`. #[serde(default = "Config::default_root_url")] pub root_url: String, /// Templates directory. May be absolute or relative to config/data directory. @@ -127,6 +134,9 @@ impl Config { fn default_matrix_user() -> String { "@tuxmain:matrix.txmn.tk".into() } + fn default_public_address() -> String { + "http://127.0.0.1:31720".into() + } fn default_reverse_proxy() -> bool { false } @@ -161,6 +171,7 @@ impl Default for Config { matrix_room: Self::default_matrix_room(), matrix_server: Self::default_matrix_server(), matrix_user: Self::default_matrix_user(), + public_address: Self::default_public_address(), reverse_proxy: Self::default_reverse_proxy(), root_url: Self::default_root_url(), templates_dir: Self::default_templates_dir(), diff --git a/src/notify.rs b/src/notify.rs index 50f5791..b70b075 100644 --- a/src/notify.rs +++ b/src/notify.rs @@ -5,6 +5,10 @@ use log::error; use matrix_sdk::ruma; use std::time::{Duration, SystemTime}; +pub struct Notification { + pub topic: String, +} + enum OptionSince { Some(T), NoneSince(SystemTime), @@ -48,14 +52,30 @@ impl Notifier { } } - async fn notify(&mut self, config: &Config) { + async fn notify(&mut self, config: &Config, notification: Notification) { match &self.matrix { None => {} Some(OptionSince::Some((_client, room))) => { + let decoded_topic = tera::escape_html( + &percent_encoding::percent_decode_str(¬ification.topic).decode_utf8_lossy(), + ); if let Err(e) = room .send( - ruma::events::room::message::RoomMessageEventContent::text_plain( - "New comment.", + ruma::events::room::message::RoomMessageEventContent::text_html( + format!( + "New comment on topic \"{}\": {}{}t/{}", + decoded_topic, + config.public_address, + config.root_url, + notification.topic, + ), + format!( + "New comment on topic \"{}\".", + config.public_address, + config.root_url, + notification.topic, + decoded_topic, + ), ), None, ) @@ -77,10 +97,10 @@ impl Notifier { } } -pub async fn run_notifier(config: &Config, recv: Receiver<()>) { +pub async fn run_notifier(config: &Config, recv: Receiver) { let mut notifier = Notifier::new(config).await; - for () in recv { - notifier.notify(config).await; + for notification in recv { + notifier.notify(config, notification).await; } } diff --git a/src/server.rs b/src/server.rs index 8e19fa6..f887394 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,6 +1,8 @@ #![allow(clippy::too_many_arguments)] -use crate::{config::*, db::*, helpers, locales::*, queries::*, templates::*}; +use crate::{ + config::*, db::*, helpers, locales::*, notify::Notification, queries::*, templates::*, +}; use argon2::{Argon2, PasswordHash, PasswordVerifier}; use crossbeam_channel::Sender; @@ -247,7 +249,7 @@ async fn handle_post_comments( templates: &Templates, dbs: Dbs, locales: &Locales, - notify_send: Sender<()>, + notify_send: Sender, ) -> tide::Result { let admin = req.cookie("admin").map_or(false, |psw| { check_admin_password_hash(config, &String::from(psw.value())) @@ -339,7 +341,11 @@ async fn handle_post_comments( helpers::new_pending_comment(&comment, &dbs) .map_err(|e| error!("Adding pending comment: {:?}", e)) .ok(); - notify_send.send(()).ok(); + notify_send + .send(Notification { + topic: topic.to_string(), + }) + .ok(); } else { context.insert("new_comment_author", &query.comment.author); context.insert("new_comment_email", &query.comment.email);