diff --git a/src/lib.rs b/src/lib.rs index f9f4565..5f10aed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,6 @@ pub mod date; pub mod email; pub mod hedgedoc; pub mod key_value; -pub mod matrix; pub mod mediawiki; pub mod template; diff --git a/src/main.rs b/src/main.rs index 25b8408..f46a1b5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,6 @@ use cccron_lib::email::{self, Email, SimpleEmail}; use cccron_lib::hedgedoc::{self, HedgeDoc}; use cccron_lib::is_dry_run; use cccron_lib::key_value::{KeyValueStore as KV, KeyValueStore}; -use cccron_lib::matrix::{self, MatrixClient}; use cccron_lib::mediawiki::{self, Mediawiki}; use cccron_lib::NYI; use cccron_lib::{trace_var, trace_var_, verboseln}; @@ -56,7 +55,7 @@ const CONFIG_SPEC: CfgSpec<'static> = CfgSpec { hedgedoc::CONFIG, mediawiki::CONFIG, email::CONFIG, - matrix::CONFIG, + // TODO: Matrix, …? CfgGroup { name: "text", description: "Various strings used.", fields: &[ @@ -471,79 +470,40 @@ fn do_protocol( wiki: &Mediawiki, ) -> Result<(), Box> { NYI!("trace/verbose annotations"); - let (current_pad_id, pad_content_without_cleanup, toc, n_topics) = get_pad_info(config, hedgedoc); + let (current_pad_id, pad_content, toc, n_topics) = get_pad_info(config, hedgedoc); if !toc.is_empty() { let human_date = plenum_day.format("%d.%m.%Y"); - let pad_content = hedgedoc::strip_metadata(pad_content_without_cleanup.clone()); + let pad_content = hedgedoc::strip_metadata(pad_content); let subject = format!("Protokoll vom Plenum am {human_date}"); let pad_content = pad_content.replace("[toc]", &toc); let body = format!( "Anbei das Protokoll vom {human_date}, ab sofort auch im Wiki zu finden.\n\n\ - Das Pad für das nächste Plenum ist zu finden unter <{}/{}>.\nDie Protokolle der letzten Plena findet ihr im wiki unter <{}/index.php?title={}>.\n\n---Protokoll:---\n{}\n-----", + Das Pad für das nächste Plenum ist zu finden unter <{}/{}>.\nDie Protokolle der letzten Plena findet ihr im wiki unter <{}/index.php?title={}>.\n\n---Protokoll:---\n{pad_content}\n-----", &config["hedgedoc-server-url"], &config["hedgedoc-next-id"], &config["wiki-server-url"], - &config["wiki-plenum-page"], - pad_content.clone(), + &config["wiki-plenum-page"] ); let _message_id = send_email(&subject, &body, email, config)?; mediawiki::pad_ins_wiki(pad_content, wiki, plenum_day)?; config.set("state-name", &ProgramState::Logged.to_string()).ok(); } else { let human_date = plenum_day.format("%d.%m.%Y"); - let pad_content = hedgedoc::strip_metadata(pad_content_without_cleanup.clone()); + let pad_content = hedgedoc::strip_metadata(pad_content); let subject = format!("Protokoll vom ausgefallenem Plenum am {human_date}"); let pad_content = pad_content.replace("[toc]", &toc); let body = format!( - "Anbei das Protokoll vom {human_date}, ab sofort auch im Wiki zu finden.\n\n\ - Das Pad für das nächste Plenum ist zu finden unter {}/{}.\nDie Protokolle der letzten Plena findet ihr im wiki unter {}/index.php?title={}.\n\n---Protokoll:---{}", + "Das letzte Plenum hatte Anbei das Protokoll vom {human_date}, ab sofort auch im Wiki zu finden.\n\n\ + Das Pad für das nächste Plenum ist zu finden unter {}/{}.\nDie Protokolle der letzten Plena findet ihr im wiki unter {}/index.php?title={}.\n\n---Protokoll:---{pad_content}", &config["hedgedoc-server-url"], &config["hedgedoc-next-id"], &config["wiki-server-url"], - &config["wiki-plenum-page"], - pad_content.clone(), + &config["wiki-plenum-page"] ); let _message_id = send_email(&subject, &body, email, config)?; mediawiki::pad_ins_wiki(pad_content, wiki, plenum_day)?; config.set("state-name", &ProgramState::Logged.to_string()).ok(); } - let mut matrix = MatrixClient::new( - &config["matrix-homeserver-url"], - &config["matrix-user-id"], - &config["matrix-access-token"], - "!YduwXBXwKifXYApwKF:catgirl.cloud", //&config["room-id-for-short-messages"], - "!YduwXBXwKifXYApwKF:catgirl.cloud", //&config["room-id-for-long-messages"], - is_dry_run(), - ); - // Send the matrix room message - let human_date = plenum_day.format("%d.%m.%Y"); - let pad_content = hedgedoc::strip_metadata(pad_content_without_cleanup); - let pad_content = pad_content.replace("[toc]", &toc); - let long_message = format!( - "Anbei das Protokoll vom {human_date}, ab sofort auch im Wiki zu finden.\n\n\ - Das Pad für das nächste Plenum ist zu finden unter {}/{}.\nDie Protokolle der letzten Plena findet ihr im wiki unter {}/index.php?title={}.\n\n", - &config["hedgedoc-server-url"], - &config["hedgedoc-next-id"], - &config["wiki-server-url"], - &config["wiki-plenum-page"], - ); - let full_long_message = format!( - "{}\n\n{}\n\n{}", - &config["text-email-greeting"], long_message, &config["text-email-signature"] - ); - let short_message = format!( - "Das letzte Plenum hatte Anbei das Protokoll vom {human_date}, ab sofort auch im Wiki zu finden.\n\n\ - Das Pad für das nächste Plenum ist zu finden unter {}/{}.\nDie Protokolle der letzten Plena findet ihr im wiki unter {}/index.php?title={}.\n\n", - &config["hedgedoc-server-url"], - &config["hedgedoc-next-id"], - &config["wiki-server-url"], - &config["wiki-plenum-page"] - ); - let full_short_message = format!( - "{}\n\n{}\n\n{}", - &config["text-email-greeting"], short_message, &config["text-email-signature"] - ); - matrix.send_short_and_long_messages_to_two_rooms(&full_short_message, &full_long_message)?; Ok(()) } diff --git a/src/matrix.rs b/src/matrix.rs deleted file mode 100644 index 688a783..0000000 --- a/src/matrix.rs +++ /dev/null @@ -1,194 +0,0 @@ -use std::cell::OnceCell; -use std::error::Error; - -use clap::builder::Str; -use colored::Colorize; - -use nom::Err; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::time::{SystemTime, UNIX_EPOCH}; - -use reqwest::blocking::Client; -use serde_json::{json, Value}; - -use crate::config_spec::{CfgField, CfgGroup}; -use crate::{trace_var, verboseln}; - -pub const CONFIG: CfgGroup<'static> = CfgGroup { - name: "matrix", - description: "API Settings for matrix", - fields: &[ - CfgField::Default { - key: "homeserver-url", - default: "https://matrix-client.matrix.org", - description: "Homeserver where the bot logs in.", - }, - CfgField::Default { - key: "user-id", - default: "@bot_username:matrix.org", - description: "API Username associated with the bot account used for writing messages.", - }, - CfgField::Password { - key: "access-token", - description: "Access Token / \"password\" used for authenticating as the bot.", - }, - CfgField::Default { - key: "room-id-for-long-messages", - default: "!someLongRoomIdentifier:matrix.org", - description: "API Username associated with the bot account used for writing messages.", - }, - CfgField::Default { - key: "room-id-for-short-messages", - default: "!someLongRoomIdentifier:matrix.org", - description: "API Username associated with the bot account used for writing messages.", - }, - ], -}; - -pub struct MatrixClient { - homeserver_url: String, - user_id: String, - access_token: String, - is_dry_run: bool, - client: Client, - txn_id: u64, - room_id_for_short_messages: String, - room_id_for_long_messages: String, -} - -#[derive(Serialize, Deserialize, Debug)] -struct LoginRequest { - user: String, - password: String, - #[serde(rename = "type")] - login_type: String, -} - -#[derive(Serialize, Deserialize, Debug)] -struct LoginResponse { - access_token: String, -} - -impl std::fmt::Debug for MatrixClient { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("matrix") - .field("homeserver_url", &self.homeserver_url) - .field("user_id", &self.user_id) - .field("access_token", &"*****") - .field("is_dry_run", &self.is_dry_run) - .field("client", &self.client) - .finish() - } -} - -impl MatrixClient { - pub fn new( - homeserver_url: &str, user_id: &str, access_token: &str, room_id_for_short_messages: &str, room_id_for_long_messages: &str, is_dry_run: bool, - ) -> Self { - Self { - homeserver_url: homeserver_url.to_string(), - user_id: user_id.to_string(), - access_token: access_token.to_string(), - is_dry_run, - client: Client::builder().cookie_store(true).build().unwrap(), - txn_id: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs(), - room_id_for_long_messages: room_id_for_long_messages.to_string(), - room_id_for_short_messages: room_id_for_short_messages.to_string(), - } - } - fn request( - &self, method: reqwest::Method, endpoint: &str, - query_data: Option<&HashMap>, json_data: Option<&T>, unauth: bool, - ) -> Result> { - let client = reqwest::blocking::Client::new(); - let url = format!("{}/_matrix/client{}", self.homeserver_url, endpoint); - - // Construct URL with query parameters - let mut request = client.request(method, &url); - if let Some(params) = query_data { - request = request.query(params); - } - - // Add JSON body if provided - if let Some(data) = json_data { - request = request.json(data); - } - - // Add authorization header if not unauthenticated request - if !unauth { - request = request.header("Authorization", format!("Bearer {}", self.access_token)); - } - - let response = request.send()?; - - if response.status().is_success() { - Ok(response.json()?) - } else { - Err(format!("Request failed: {}", response.status()).into()) - } - } - - fn put( - &self, endpoint: &str, query_data: Option<&HashMap>, json_data: Option<&T>, - unauth: bool, - ) -> Result> { - self.request(reqwest::Method::PUT, endpoint, query_data, json_data, unauth) - } - - fn post( - &self, endpoint: &str, query_data: Option<&HashMap>, json_data: Option<&T>, - unauth: bool, - ) -> Result> { - self.request(reqwest::Method::POST, endpoint, query_data, json_data, unauth) - } - - fn get( - &self, endpoint: &str, query_data: Option<&HashMap>, unauth: bool, - ) -> Result> { - self.request::>( - reqwest::Method::GET, - endpoint, - query_data, - None, - unauth, - ) - } - - pub fn login(&mut self, username: &str, password: &str) -> Result<(), Box> { - let login_request = LoginRequest { - user: username.to_string(), - password: password.to_string(), - login_type: "m.login.password".to_string(), - }; - - let response: LoginResponse = - serde_json::from_value(self.post("/r0/login", None, Some(&login_request), true)?)?; - - self.access_token = response.access_token; - Ok(()) - } - - fn txn_id(&mut self) -> u64 { - let current = self.txn_id; - self.txn_id += 1; - current - } - - pub fn send_room_message(&mut self, room_id: &str, text: &str) -> Result> { - let content = HashMap::from([("msgtype", "m.text"), ("body", text)]); - self.send_room_event(&room_id, "m.room.message", &content) - } - - fn send_room_event( - &mut self, room: &str, event_type: &str, content: &impl Serialize, - ) -> Result> { - let endpoint = format!("/r0/rooms/{}/send/{}/{}", room, event_type, self.txn_id()); - self.put(&endpoint, None, Some(content), false) - } - pub fn send_short_and_long_messages_to_two_rooms(&mut self, short_message: &str, long_message: &str) -> Result<(), Box> { - self.send_room_message(&long_message, &self.room_id_for_long_messages.clone())?; - self.send_room_message(&short_message, &self.room_id_for_short_messages.clone())?; - Ok(()) - } -} diff --git a/src/mediawiki.rs b/src/mediawiki.rs index 416333d..b7b761a 100644 --- a/src/mediawiki.rs +++ b/src/mediawiki.rs @@ -1,6 +1,5 @@ use std::cell::OnceCell; use std::error::Error; -use std::f64::consts::E; use chrono::{Datelike, NaiveDate, Utc}; use colored::Colorize; @@ -76,13 +75,13 @@ impl std::fmt::Debug for Mediawiki { pub enum ValidRequestTypes { Get, Post, - PostForEditing, + PostForEditing } pub enum ValidPageEdits { WithPotentiallyOverriding, WithoutOverriding, - ModifyPlenumPageAfterwards_WithoutOverriding, + ModifyPlenumPageAfterwards_WithoutOverriding } impl Mediawiki { @@ -224,6 +223,7 @@ impl Mediawiki { resp } + /// Creates a completely new wiki page with page_content and page_title as inputs pub fn new_wiki_page( &self, page_title: &str, page_content: &str, update_main_page: ValidPageEdits, @@ -243,7 +243,7 @@ impl Mediawiki { } let url = format!("{}/api.php", self.server_url); - + let params: Box<[(&str, &str)]> = match update_main_page { ValidPageEdits::WithPotentiallyOverriding => { // This means we *EDIT* the *Main Page* and do not prevent overwriting @@ -253,9 +253,9 @@ impl Mediawiki { ("title", page_title), ("text", page_content), ("token", self.csrf_token.get().unwrap()), - ("bot", "true"), - ]) - }, + ("bot", "true") + ]) + } ValidPageEdits::ModifyPlenumPageAfterwards_WithoutOverriding => { // This means we *CREATE* a *new Page* and always prevent overwriting Box::from([ @@ -267,7 +267,7 @@ impl Mediawiki { ("createonly", "true"), // Prevent overwriting existing pages ("bot", "true"), ]) - }, + } ValidPageEdits::WithoutOverriding => { // This means we *CREATE* a *new Page* and always prevent overwriting Box::from([ @@ -279,7 +279,7 @@ impl Mediawiki { ("createonly", "true"), // Prevent overwriting existing pages ("bot", "true"), ]) - }, + } }; verboseln!("Current page title: {page_title}"); @@ -294,19 +294,11 @@ impl Mediawiki { let response_result = serde_json::from_str::(&request_result); let response = response_result.unwrap_or_else(|e| { print!("Error while creating new wiki page:\n{}", e.to_string().cyan()); - return serde_json::from_str("\"(error)\"").unwrap(); + return serde_json::from_str("\"(error)\"").unwrap() }); verboseln!("pos2"); // Check if the page creation was successful - if let Some(error) = response.get("error") { - if let Some(info) = error.get("info") { - if info == "The page you tried to create has been created already." { - verboseln!("The page you tried to create has been created already. Continuing...") - } else { - println!("There was an error while editing the wiki: {}", info.to_string().yellow()) - } - } - } else if let Some(edit) = response.get("edit") { + if let Some(edit) = response.get("edit") { verboseln!("pos3"); if edit.get("result").and_then(|r| r.as_str()) == Some("Success") { verboseln!("Successfully created wiki page: {}", page_title); @@ -326,9 +318,9 @@ impl Mediawiki { verboseln!("updating main page..."); self.update_plenum_page(page_title)? }, - _ => (), + _ => () }; - return Ok(page_title.to_string()); + return Ok(page_title.to_string()) } /// This function is responsible for updating the main plenum page: @@ -347,6 +339,7 @@ impl Mediawiki { // check first if the script has been run before and if the link already exists if !page_content.contains(&new_page_title_to_link_to) { + // check if the current year heading pattern exists if !page_content.contains(&year_heading_pattern) { // If not, add a new year heading pattern @@ -382,11 +375,7 @@ impl Mediawiki { verboseln!("{}", "The bot appears to have been run before and a duplicate link to the new plenum pad was avoided.".yellow()) } // refresh page - self.new_wiki_page( - &self.plenum_main_page_name, - &page_content, - ValidPageEdits::WithPotentiallyOverriding, - )?; + self.new_wiki_page(&self.plenum_main_page_name, &page_content, ValidPageEdits::WithPotentiallyOverriding)?; Ok(()) } /// This function downloads and returns the contents of a wiki page when given the page's title (e.g. `page_title = Plenum/13._August_2024`) @@ -523,11 +512,7 @@ pub fn pad_ins_wiki( let page_title = create_page_title(date); let full_page_title = format!("{}/{}", wiki.plenum_main_page_name, page_title); - wiki.new_wiki_page( - &full_page_title, - &pad_converted, - ValidPageEdits::ModifyPlenumPageAfterwards_WithoutOverriding, - )?; + wiki.new_wiki_page(&full_page_title, &pad_converted, ValidPageEdits::ModifyPlenumPageAfterwards_WithoutOverriding)?; verboseln!("Finished successfully with wiki"); Ok(())