feat: notify: link to new comment
This commit is contained in:
parent
aec876233d
commit
17e61b8a67
5 changed files with 48 additions and 9 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3359,6 +3359,7 @@ dependencies = [
|
||||||
"intl-memoizer",
|
"intl-memoizer",
|
||||||
"log",
|
"log",
|
||||||
"matrix-sdk",
|
"matrix-sdk",
|
||||||
|
"percent-encoding",
|
||||||
"petname",
|
"petname",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
|
|
|
@ -18,6 +18,7 @@ fluent-langneg = "0.13.0"
|
||||||
intl-memoizer = "0.5.1"
|
intl-memoizer = "0.5.1"
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
matrix-sdk = { version = "0.6.2", default-features = false, features = ["rustls-tls"] }
|
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"] }
|
petname = { version = "1.1.3", optional = true, default-features = false, features = ["std_rng", "default_dictionary"] }
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
rand_core = { version = "0.6.4", features = ["std"] }
|
rand_core = { version = "0.6.4", features = ["std"] }
|
||||||
|
|
|
@ -55,10 +55,17 @@ pub struct Config {
|
||||||
pub matrix_server: String,
|
pub matrix_server: String,
|
||||||
#[serde(default = "Config::default_matrix_user")]
|
#[serde(default = "Config::default_matrix_user")]
|
||||||
pub matrix_user: String,
|
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?
|
/// Are we behind a reverse proxy?
|
||||||
/// Determines whether we assume client address is in a Forwarded header or socket address.
|
/// Determines whether we assume client address is in a Forwarded header or socket address.
|
||||||
#[serde(default = "Config::default_reverse_proxy")]
|
#[serde(default = "Config::default_reverse_proxy")]
|
||||||
pub reverse_proxy: bool,
|
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")]
|
#[serde(default = "Config::default_root_url")]
|
||||||
pub root_url: String,
|
pub root_url: String,
|
||||||
/// Templates directory. May be absolute or relative to config/data directory.
|
/// Templates directory. May be absolute or relative to config/data directory.
|
||||||
|
@ -127,6 +134,9 @@ impl Config {
|
||||||
fn default_matrix_user() -> String {
|
fn default_matrix_user() -> String {
|
||||||
"@tuxmain:matrix.txmn.tk".into()
|
"@tuxmain:matrix.txmn.tk".into()
|
||||||
}
|
}
|
||||||
|
fn default_public_address() -> String {
|
||||||
|
"http://127.0.0.1:31720".into()
|
||||||
|
}
|
||||||
fn default_reverse_proxy() -> bool {
|
fn default_reverse_proxy() -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -161,6 +171,7 @@ impl Default for Config {
|
||||||
matrix_room: Self::default_matrix_room(),
|
matrix_room: Self::default_matrix_room(),
|
||||||
matrix_server: Self::default_matrix_server(),
|
matrix_server: Self::default_matrix_server(),
|
||||||
matrix_user: Self::default_matrix_user(),
|
matrix_user: Self::default_matrix_user(),
|
||||||
|
public_address: Self::default_public_address(),
|
||||||
reverse_proxy: Self::default_reverse_proxy(),
|
reverse_proxy: Self::default_reverse_proxy(),
|
||||||
root_url: Self::default_root_url(),
|
root_url: Self::default_root_url(),
|
||||||
templates_dir: Self::default_templates_dir(),
|
templates_dir: Self::default_templates_dir(),
|
||||||
|
|
|
@ -5,6 +5,10 @@ use log::error;
|
||||||
use matrix_sdk::ruma;
|
use matrix_sdk::ruma;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
|
pub struct Notification {
|
||||||
|
pub topic: String,
|
||||||
|
}
|
||||||
|
|
||||||
enum OptionSince<T> {
|
enum OptionSince<T> {
|
||||||
Some(T),
|
Some(T),
|
||||||
NoneSince(SystemTime),
|
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 {
|
match &self.matrix {
|
||||||
None => {}
|
None => {}
|
||||||
Some(OptionSince::Some((_client, room))) => {
|
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
|
if let Err(e) = room
|
||||||
.send(
|
.send(
|
||||||
ruma::events::room::message::RoomMessageEventContent::text_plain(
|
ruma::events::room::message::RoomMessageEventContent::text_html(
|
||||||
"New comment.",
|
format!(
|
||||||
|
"New comment on topic \"{}\": {}{}t/{}",
|
||||||
|
decoded_topic,
|
||||||
|
config.public_address,
|
||||||
|
config.root_url,
|
||||||
|
notification.topic,
|
||||||
|
),
|
||||||
|
format!(
|
||||||
|
"<a href=\"{}{}t/{}\">New comment on topic \"<em>{}</em>\".</a>",
|
||||||
|
config.public_address,
|
||||||
|
config.root_url,
|
||||||
|
notification.topic,
|
||||||
|
decoded_topic,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
None,
|
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<Notification>) {
|
||||||
let mut notifier = Notifier::new(config).await;
|
let mut notifier = Notifier::new(config).await;
|
||||||
for () in recv {
|
for notification in recv {
|
||||||
notifier.notify(config).await;
|
notifier.notify(config, notification).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#![allow(clippy::too_many_arguments)]
|
#![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 argon2::{Argon2, PasswordHash, PasswordVerifier};
|
||||||
use crossbeam_channel::Sender;
|
use crossbeam_channel::Sender;
|
||||||
|
@ -247,7 +249,7 @@ async fn handle_post_comments(
|
||||||
templates: &Templates,
|
templates: &Templates,
|
||||||
dbs: Dbs,
|
dbs: Dbs,
|
||||||
locales: &Locales,
|
locales: &Locales,
|
||||||
notify_send: Sender<()>,
|
notify_send: Sender<Notification>,
|
||||||
) -> tide::Result<tide::Response> {
|
) -> tide::Result<tide::Response> {
|
||||||
let admin = req.cookie("admin").map_or(false, |psw| {
|
let admin = req.cookie("admin").map_or(false, |psw| {
|
||||||
check_admin_password_hash(config, &String::from(psw.value()))
|
check_admin_password_hash(config, &String::from(psw.value()))
|
||||||
|
@ -339,7 +341,11 @@ async fn handle_post_comments(
|
||||||
helpers::new_pending_comment(&comment, &dbs)
|
helpers::new_pending_comment(&comment, &dbs)
|
||||||
.map_err(|e| error!("Adding pending comment: {:?}", e))
|
.map_err(|e| error!("Adding pending comment: {:?}", e))
|
||||||
.ok();
|
.ok();
|
||||||
notify_send.send(()).ok();
|
notify_send
|
||||||
|
.send(Notification {
|
||||||
|
topic: topic.to_string(),
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
} else {
|
} else {
|
||||||
context.insert("new_comment_author", &query.comment.author);
|
context.insert("new_comment_author", &query.comment.author);
|
||||||
context.insert("new_comment_email", &query.comment.email);
|
context.insert("new_comment_email", &query.comment.email);
|
||||||
|
|
Loading…
Reference in a new issue