diff --git a/Cargo.lock b/Cargo.lock index df255ae..a35e4f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,7 +62,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.8", "once_cell", "version_check", ] @@ -87,9 +87,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.65" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" +checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" [[package]] name = "anymap2" @@ -331,7 +331,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" dependencies = [ "futures-core", - "getrandom 0.2.7", + "getrandom 0.2.8", "instant", "pin-project-lite 0.2.9", "rand 0.8.5", @@ -346,9 +346,9 @@ checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" [[package]] name = "base64" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64ct" @@ -503,9 +503,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.0.17" +version = "4.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06badb543e734a2d6568e19a40af66ed5364360b9226184926f89d229b4b4267" +checksum = "335867764ed2de42325fafe6d18b8af74ba97ee0c590fa016f157535b42ab04b" dependencies = [ "bitflags", "clap_derive", @@ -515,9 +515,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.0.13" +version = "4.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f169caba89a7d512b5418b09864543eeb4d497416c917d7137863bd2076ad" +checksum = "16a1b0f6422af32d5da0c58e2703320f379216ee70198241c84173a8c5ac28f3" dependencies = [ "heck", "proc-macro-error", @@ -690,9 +690,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.79" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f83d0ebf42c6eafb8d7c52f7e5f2d3003b89c7aa4fd2b79229209459a849af8" +checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a" dependencies = [ "cc", "cxxbridge-flags", @@ -702,9 +702,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.79" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07d050484b55975889284352b0ffc2ecbda25c0c55978017c132b29ba0818a86" +checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827" dependencies = [ "cc", "codespan-reporting", @@ -717,15 +717,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.79" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d2199b00553eda8012dfec8d3b1c75fce747cf27c169a270b3b99e3448ab78" +checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a" [[package]] name = "cxxbridge-macro" -version = "1.0.79" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb67a6de1f602736dd7eaead0080cf3435df806c61b24b13328db128c58868f" +checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7" dependencies = [ "proc-macro2", "quote", @@ -1092,9 +1092,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if", "libc", @@ -1149,9 +1149,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" +checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" dependencies = [ "bytes", "fnv", @@ -1470,9 +1470,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.135" +version = "0.2.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" +checksum = "55edcf6c0bb319052dea84732cf99db461780fd5e8d3eb46ab6ff312ab31f197" [[package]] name = "link-cplusplus" @@ -1521,9 +1521,9 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" [[package]] name = "matrix-sdk" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a075b2690bc305ca02755a868cfa9f9779cfc2353f7184f3cf1a0a405ecdb2" +checksum = "cbeafb4809f33f377165f2fbcf10e0613053ad206762194c3050a727fd3abcb2" dependencies = [ "anymap2", "async-once-cell", @@ -1555,9 +1555,9 @@ dependencies = [ [[package]] name = "matrix-sdk-base" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de51f1662779739e7a74677f37cdf744a2c82dfb7618e89d5e18967a4540181b" +checksum = "b944f6d1fc8779ba790dd0b942ceff45c626c1f5da847f01122d355ad06511bd" dependencies = [ "async-stream", "async-trait", @@ -1616,14 +1616,14 @@ checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" [[package]] name = "mio" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.36.1", + "windows-sys", ] [[package]] @@ -1714,7 +1714,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-sys 0.42.0", + "windows-sys", ] [[package]] @@ -1866,9 +1866,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "polling" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899b00b9c8ab553c743b3e11e87c5c7d423b2a2de229ba95b24a756344748011" +checksum = "ab4609a838d88b73d8238967b60dd115cc08d38e2bbaf51ee1e4b695f89122e2" dependencies = [ "autocfg", "cfg-if", @@ -2019,7 +2019,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.8", ] [[package]] @@ -2046,7 +2046,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.8", "redox_syscall", "thiserror", ] @@ -2319,18 +2319,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.145" +version = "1.0.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.145" +version = "1.0.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" dependencies = [ "proc-macro2", "quote", @@ -2601,9 +2601,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.102" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" +checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" dependencies = [ "proc-macro2", "quote", @@ -2821,15 +2821,25 @@ dependencies = [ ] [[package]] -name = "toml_edit" -version = "0.14.4" +name = "toml_datetime" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5376256e44f2443f8896ac012507c19a012df0fe8758b55246ae51a2279db51f" +checksum = "808b51e57d0ef8f71115d8f3a01e7d3750d01c79cac4b3eda910f4389fdf92fd" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1541ba70885967e662f69d31ab3aeca7b1aaecfcd58679590b893e9239c3646" dependencies = [ "combine", "indexmap", "itertools", "serde", + "toml_datetime", ] [[package]] @@ -3027,7 +3037,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.8", ] [[package]] @@ -3271,19 +3281,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", -] - [[package]] name = "windows-sys" version = "0.42.0" @@ -3291,12 +3288,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.0", - "windows_i686_gnu 0.42.0", - "windows_i686_msvc 0.42.0", - "windows_x86_64_gnu 0.42.0", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.0", + "windows_x86_64_msvc", ] [[package]] @@ -3305,48 +3302,24 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" -[[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" - [[package]] name = "windows_aarch64_msvc" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" -[[package]] -name = "windows_i686_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" - [[package]] name = "windows_i686_gnu" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" -[[package]] -name = "windows_i686_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" - [[package]] name = "windows_i686_msvc" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" -[[package]] -name = "windows_x86_64_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" - [[package]] name = "windows_x86_64_gnu" version = "0.42.0" @@ -3359,12 +3332,6 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" -[[package]] -name = "windows_x86_64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" - [[package]] name = "windows_x86_64_msvc" version = "0.42.0" diff --git a/Cargo.toml b/Cargo.toml index b5fc49d..75f5ca6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,20 +9,20 @@ edition = "2021" [dependencies] argon2 = "0.4.1" -base64 = "0.13.0" -clap = { version = "4.0.17", default-features = false, features = ["derive", "std"] } +base64 = "0.13.1" +clap = { version = "4.0.18", default-features = false, features = ["derive", "std"] } crossbeam-channel = "0.5.6" directories = "4.0.1" log = "0.4.17" -matrix-sdk = { version = "0.6.0", default-features = false, features = ["rustls-tls"] } +matrix-sdk = { version = "0.6.2", default-features = false, features = ["rustls-tls"] } rand = "0.8.5" rand_core = { version = "0.6.4", features = ["std"] } rpassword = "7.1.0" -serde = { version = "1.0.145", features = ["derive", "rc"] } +serde = { version = "1.0.147", features = ["derive", "rc"] } sha2 = "0.10.6" sled = "0.34.7" tera = { version = "1.17.1", features = ["builtins", "date-locale"] } tide = { version = "0.16.0", default-features = false, features = ["h1-server", "cookies", "logger"] } tokio = { version = "1.21.2", features = ["macros", "rt-multi-thread"] } -toml_edit = { version = "0.14.4", features = ["easy"] } +toml_edit = { version = "0.15.0", features = ["easy"] } typed-sled = "0.2.0" diff --git a/README.md b/README.md index 0bd973f..e00c5cc 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,12 @@ Each topic is accessible at `/t/`. Admin login is accessible at `/admin`. Once authenticated, you can see the pending comments on the topic pages. +### Matrix + +If enabled, a message can be sent to a Matrix room (private or public) on every new comment. + +The account must have joined the room for Webcomment to be able to send messages to it. + ## License CopyLeft 2022 Pascal Engélibert [(why copyleft?)](//txmn.tk/blog/why-copyleft/) diff --git a/src/config.rs b/src/config.rs index 746eacb..9b4fa6f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -45,6 +45,9 @@ pub struct Config { pub matrix_notify: bool, #[serde(default = "Config::default_matrix_password")] pub matrix_password: String, + /// If connection fails, retry connecting after minimum this duration (seconds) + #[serde(default = "Config::default_matrix_retry_timeout")] + pub matrix_retry_timeout: u64, #[serde(default = "Config::default_matrix_room")] pub matrix_room: String, #[serde(default = "Config::default_matrix_server")] @@ -108,6 +111,9 @@ impl Config { fn default_matrix_password() -> String { "".into() } + fn default_matrix_retry_timeout() -> u64 { + 3600 + } fn default_matrix_room() -> String { "#maintenance:matrix.txmn.tk".into() } @@ -144,6 +150,7 @@ impl Default for Config { listen: Self::default_listen(), matrix_notify: Self::default_matrix_notify(), matrix_password: Self::default_matrix_password(), + matrix_retry_timeout: Self::default_matrix_retry_timeout(), matrix_room: Self::default_matrix_room(), matrix_server: Self::default_matrix_server(), matrix_user: Self::default_matrix_user(), diff --git a/src/notify.rs b/src/notify.rs index e61cbc3..974c385 100644 --- a/src/notify.rs +++ b/src/notify.rs @@ -1,64 +1,129 @@ use crate::config::Config; use crossbeam_channel::Receiver; +use log::error; use matrix_sdk::ruma; -use std::sync::Arc; +use std::{ + sync::Arc, + time::{Duration, SystemTime}, +}; + +enum OptionSince { + Some(T), + NoneSince(SystemTime), +} + +impl OptionSince { + fn from_result(result: Result, f: F) -> Self { + match result { + Ok(val) => Self::Some(val), + Err(e) => { + f(e); + Self::NoneSince(SystemTime::now()) + } + } + } +} + +impl From> for OptionSince { + fn from(opt: Option) -> Self { + match opt { + Some(val) => Self::Some(val), + None => Self::NoneSince(SystemTime::now()), + } + } +} struct Notifier { - matrix: Option<(matrix_sdk::Client, matrix_sdk::room::Joined)>, + matrix: Option>, } impl Notifier { async fn new(config: &Config) -> Self { Self { matrix: if config.matrix_notify { - let user = ruma::UserId::parse(&config.matrix_user).unwrap(); - let client = matrix_sdk::Client::builder() - .homeserver_url(&config.matrix_server) - .user_agent("Webcomment") - .build() - .await - .unwrap(); - - client - .login_username(&user, &config.matrix_password) - .send() - .await - .unwrap(); - client - .sync_once(matrix_sdk::config::SyncSettings::default()) - .await - .unwrap(); - - let room_id = <&ruma::RoomId>::try_from(config.matrix_room.as_str()).unwrap(); - let room = client.get_room(room_id).unwrap(); - - if let matrix_sdk::room::Room::Joined(room) = room { - Some((client, room)) - } else { - None - } + Some(OptionSince::from_result(init_matrix(config).await, |e| { + error!("Cannot init Matrix: {:?}", e) + })) } else { None }, } } - async fn notify(&self) { - if let Some((_client, room)) = &self.matrix { - room.send( - ruma::events::room::message::RoomMessageEventContent::text_plain("New comment."), - None, - ) - .await - .unwrap(); + async fn notify(&mut self, config: &Config) { + match &self.matrix { + None => {} + Some(OptionSince::Some((_client, room))) => { + room.send( + ruma::events::room::message::RoomMessageEventContent::text_plain( + "New comment.", + ), + None, + ) + .await + .unwrap(); + } + Some(OptionSince::NoneSince(earlier)) => { + if SystemTime::now().duration_since(*earlier).unwrap() + > Duration::from_secs(config.matrix_retry_timeout) + { + self.matrix = Some(OptionSince::from_result(init_matrix(config).await, |e| { + error!("Cannot init Matrix: {:?}", e) + })) + } + } } } } pub async fn run_notifier(config: Arc, recv: Receiver<()>) { - let notifier = Notifier::new(&config).await; + let mut notifier = Notifier::new(&config).await; for () in recv { - notifier.notify().await; + notifier.notify(&config).await; + } +} + +#[derive(Debug)] +enum MatrixError { + CannotConnect(matrix_sdk::ClientBuildError), + CannotLogin(matrix_sdk::Error), + CannotSync(matrix_sdk::Error), + RoomNotJoined, + UnknownRoom, +} + +async fn init_matrix( + config: &Config, +) -> Result<(matrix_sdk::Client, matrix_sdk::room::Joined), MatrixError> { + let user = ruma::UserId::parse(&config.matrix_user) + .expect("Matrix username should be in format `@user:homeserver`"); + let room_id = <&ruma::RoomId>::try_from(config.matrix_room.as_str()) + .expect("Matrix room should be in format `#roomname:homeserver` or `!roomid:homeserver`"); + + let client = matrix_sdk::Client::builder() + .homeserver_url(&config.matrix_server) + .user_agent("Webcomment") + .handle_refresh_tokens() + .build() + .await + .map_err(MatrixError::CannotConnect)?; + + client + .login_username(&user, &config.matrix_password) + .send() + .await + .map_err(MatrixError::CannotLogin)?; + client + .sync_once(matrix_sdk::config::SyncSettings::default()) + .await + .map_err(MatrixError::CannotSync)?; + + let room = client.get_room(room_id).ok_or(MatrixError::UnknownRoom)?; + + if let matrix_sdk::room::Room::Joined(room) = room { + Ok((client, room)) + } else { + Err(MatrixError::RoomNotJoined) } }