From 1577d5de2e003462a7927da8cda6e83766dc0597 Mon Sep 17 00:00:00 2001
From: murmeldin
Date: Tue, 14 Jan 2025 17:15:07 +0100
Subject: [PATCH 1/2] fixed bug that triggered QueryNoRows on state-toc by
adding a default value and improved output when QueryNoRows occurs
---
src/email.rs | 5 +++++
src/key_value.rs | 1 +
src/main.rs | 5 +++--
3 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/src/email.rs b/src/email.rs
index 790fe1f..4698c56 100644
--- a/src/email.rs
+++ b/src/email.rs
@@ -46,6 +46,11 @@ pub const CONFIG: CfgGroup<'static> = CfgGroup {
description:
"Message-Id of last initial announcement to send In-Reply-To (if applicable).",
},
+ CfgField::Default {
+ key: "state-toc",
+ default: "error: no toc saved",
+ description: "Recipient of the emails sent.",
+ },
],
};
diff --git a/src/key_value.rs b/src/key_value.rs
index 9a612db..cdfea88 100644
--- a/src/key_value.rs
+++ b/src/key_value.rs
@@ -80,6 +80,7 @@ impl KeyValueStore {
let result = row.get(0)?;
Ok(result)
} else {
+ println!(": Keinen Wert für '{key}' gefunden, bitte Datenbank überprüfen!");
Err(rusqlite::Error::QueryReturnedNoRows)
}
}
diff --git a/src/main.rs b/src/main.rs
index 712222b..df6c4c0 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -417,7 +417,7 @@ fn do_reminder(
NYI!("trace/verbose annotations");
// fetch current pad contents & summarize
let (current_pad_id, _pad_content, toc, n_topics) = get_pad_info(config, hedgedoc);
- let old_toc = config.get("state-toc")?;
+ let old_toc = config.get("email-state-toc")?;
// construct email
let human_date = plenum_day.format("%d.%m.%Y");
let subject = if n_topics == 0 {
@@ -458,6 +458,7 @@ fn do_reminder(
"do we skip ahead to ProgramState::Logged here or do we later add a note to the wiki?"
);
}
+
config.set("state-name", &ProgramState::Reminded.to_string()).ok();
config.set("state-toc", &toc).ok();
Ok(())
@@ -576,7 +577,7 @@ fn do_cleanup(
_wiki: &Mediawiki,
) -> Result<(), Box> {
NYI!("trace/verbose annotations");
- config.delete("state-toc");
+ config.delete("email-state-toc");
config.delete("email-last-message-id");
NYI!("rotate pad links");
NYI!("double-check state for leftovers");
From c2d9dcdd744ae8bff63a6e8805df58eea383df8d Mon Sep 17 00:00:00 2001
From: murmeldin
Date: Tue, 14 Jan 2025 17:40:04 +0100
Subject: [PATCH 2/2] added matrix messages to do_reminder and do_announcement
and improved their html formatting
---
Cargo.lock | 1 +
Cargo.toml | 1 +
src/main.rs | 62 ++++++++++++++++++++++-------------------
src/matrix.rs | 76 ++++++++++++++++++++++-----------------------------
4 files changed, 68 insertions(+), 72 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index a8cbba1..4c1d2bc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1483,6 +1483,7 @@ dependencies = [
"colored",
"futures",
"headers",
+ "lazy_static",
"lettre",
"log",
"mediawiki",
diff --git a/Cargo.toml b/Cargo.toml
index 7283a37..7ed1452 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -24,6 +24,7 @@ nom = "7.1.3"
mediawiki = "0.3.1"
ollama-rs = "0.2.1"
tokio = "1.0.0"
+lazy_static = "1.4"
[[bin]]
name = "Plenum-Bot"
diff --git a/src/main.rs b/src/main.rs
index df6c4c0..ca5ea33 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -59,7 +59,7 @@ const CONFIG_SPEC: CfgSpec<'static> = CfgSpec {
description: "Various strings used.",
fields: &[
CfgField::Default { key: "email-greeting",
- default: "Hallo liebe Mitreisende,",
+ default: "Hallo liebe Mitreisenden,",
description: "\"Hello\"-greeting added at the start of every email.",
},
CfgField::Default { key: "email-signature",
@@ -400,13 +400,23 @@ fn do_announcement(
"Falls ihr noch Themen ergänzen wollt ist hier der Link zum Pad:\n {}",
hedgedoc.format_url(¤t_pad_id)
);
- let body = format!("{line1}\n\n{line2}");
+ let mut body = format!("{line1}\n\n{line2}");
// send it
let message_id = send_email(&subject, &body, email, config)?;
// on success, update state (ignore write errors, they'll be checked later)
config.set("email-message-id", &message_id).ok();
config.set("state-name", &ProgramState::Announced.to_string()).ok();
- config.set("state-toc", &toc).ok();
+ config.set("email-state-toc", &toc).ok();
+ let mut matrix = MatrixClient::new(
+ &config["matrix-homeserver-url"],
+ &config["matrix-user-id"],
+ &config["matrix-access-token"],
+ &config["matrix-room-id-1"],
+ &config["matrix-room-id-2"],
+ is_dry_run(),
+ );
+ let message = format!("{}\n\n{}{}",&config["text-email-greeting"], &body, &config["text-email-signature"]);
+ matrix.send_message_to_two_rooms(&message)?;
Ok(())
}
@@ -436,7 +446,7 @@ fn do_reminder(
} else {
format!("Es gab nochmal Änderungen, die aktualisierten Themen für das Plenum sind:\n\n{toc}\n\n")
};
- let body = if n_topics > 0 {
+ let mut body = if n_topics > 0 {
format!(
"{body_prefix}Die vollen Details findet ihr im Pad:\n {}\n\nBis {} um 20:00!",
hedgedoc.format_url(¤t_pad_id),
@@ -457,10 +467,21 @@ fn do_reminder(
NYI!(
"do we skip ahead to ProgramState::Logged here or do we later add a note to the wiki?"
);
+ // TODO: ADD SOMETHING TO WIKI
}
config.set("state-name", &ProgramState::Reminded.to_string()).ok();
- config.set("state-toc", &toc).ok();
+ config.set("email-state-toc", &toc).ok();
+ let mut matrix = MatrixClient::new(
+ &config["matrix-homeserver-url"],
+ &config["matrix-user-id"],
+ &config["matrix-access-token"],
+ &config["matrix-room-id-1"],
+ &config["matrix-room-id-2"],
+ is_dry_run(),
+ );
+ let message = format!("{}\n\n{}{}",&config["text-email-greeting"], &body, &config["text-email-signature"]);
+ matrix.send_message_to_two_rooms(&message)?;
Ok(())
}
@@ -501,7 +522,7 @@ fn do_protocol(
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 <{}/{}>.\n\nDie Protokolle der letzten Plena findet ihr im wiki unter <{}/index.php?title={}>.\n\n---Protokoll:---\n{}\n-----",
&config["hedgedoc-server-url"],
&config["hedgedoc-next-id"],
&config["wiki-server-url"],
@@ -518,7 +539,7 @@ fn do_protocol(
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 Pad für das nächste Plenum ist zu finden unter {}/{}.\n\nDie Protokolle der letzten Plena findet ihr im wiki unter {}/index.php?title={}.\n\n---Protokoll:---{}",
&config["hedgedoc-server-url"],
&config["hedgedoc-next-id"],
&config["wiki-server-url"],
@@ -533,39 +554,24 @@ fn do_protocol(
&config["matrix-homeserver-url"],
&config["matrix-user-id"],
&config["matrix-access-token"],
- &config["matrix-room-id-for-short-messages"],
- &config["matrix-room-id-for-long-messages"],
+ &config["matrix-room-id-1"],
+ &config["matrix-room-id-2"],
is_dry_run(),
);
// Send the matrix room message
let human_date = plenum_day.format("%d.%m.%Y");
let pad_content = pad_content.replace("[toc]", &toc);
- let long_message = format!(
+ let 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**Hier die Zusammenfassung:**\n{}",
+ Das Pad für das nächste Plenum ist zu finden unter {}/{}.\n\nDie Protokolle der letzten Plena findet ihr im wiki unter {}/index.php?title={}.\n\n**Hier die Zusammenfassung:**\n\n{}",
&config["hedgedoc-server-url"],
&config["hedgedoc-next-id"],
&config["wiki-server-url"],
&config["wiki-plenum-page"],
&summary_or_toc
);
- let full_long_message = format!(
- "{}\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={}.",
- &config["hedgedoc-server-url"],
- &config["hedgedoc-next-id"],
- &config["wiki-server-url"],
- &config["wiki-plenum-page"]
- );
- let full_short_message = format!(
- "{}\n{}{}",
- &config["text-email-greeting"], short_message, &config["text-email-signature"].strip_prefix("[").unwrap_or(&config["text-email-signature"]).strip_suffix("]").unwrap_or(&config["text-email-signature"])
- );
- matrix.send_short_and_long_messages_to_two_rooms(&full_short_message, &full_long_message)?;
+ let full_message = format!("{}\n\n{}{}",&config["text-email-greeting"], &message, &config["text-email-signature"]);
+ matrix.send_message_to_two_rooms(&full_message)?;
Ok(())
}
diff --git a/src/matrix.rs b/src/matrix.rs
index be122ab..05f10a8 100644
--- a/src/matrix.rs
+++ b/src/matrix.rs
@@ -1,5 +1,6 @@
use std::error::Error;
use std::io::Read;
+use lazy_static::lazy_static;
use colored::Colorize;
use regex::Regex;
@@ -32,12 +33,12 @@ pub const CONFIG: CfgGroup<'static> = CfgGroup {
description: "Access Token / \"password\" used for authenticating as the bot.",
},
CfgField::Default {
- key: "room-id-for-long-messages",
+ key: "room-id-1",
default: "!someLongRoomIdentifier:matrix.org",
description: "API Username associated with the bot account used for writing messages.",
},
CfgField::Default {
- key: "room-id-for-short-messages",
+ key: "room-id-2",
default: "!someLongRoomIdentifier:matrix.org",
description: "API Username associated with the bot account used for writing messages.",
},
@@ -51,8 +52,8 @@ pub struct MatrixClient {
is_dry_run: bool,
client: Client,
txn_id: u64,
- room_id_for_short_messages: String,
- room_id_for_long_messages: String,
+ room_id_1: String,
+ room_id_2: String,
}
#[derive(Serialize, Deserialize, Debug)]
@@ -82,8 +83,8 @@ impl std::fmt::Debug for MatrixClient {
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,
+ homeserver_url: &str, user_id: &str, access_token: &str, room_id_1: &str,
+ room_id_2: &str, is_dry_run: bool,
) -> Self {
Self {
homeserver_url: homeserver_url.to_string(),
@@ -92,8 +93,8 @@ impl MatrixClient {
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(),
+ room_id_2: room_id_2.to_string(),
+ room_id_1: room_id_1.to_string(),
}
}
fn request(
@@ -102,7 +103,7 @@ impl MatrixClient {
) -> Result> {
let client = reqwest::blocking::Client::new();
let url = format!("{}/_matrix/client{}", self.homeserver_url, endpoint);
- print!("url: {}", url.yellow());
+ verboseln!("url: {}\n", url.blue());
// Construct URL with query parameters
let mut request = client.request(method, &url);
@@ -176,41 +177,28 @@ impl MatrixClient {
current
}
- pub fn pandoc_convert_md_to_html(markdown: String) -> Result> {
- let (output, errors, status) = crate::pipe(
- "pandoc",
- &mut [
- "--from", "markdown-auto_identifiers",
- "--to", "html5",
- "--wrap=none", // Verhindert Zeilenumbrüche
- "--no-highlight", // Deaktiviert Syntax-Highlighting
- ],
- markdown,
- )?;
- if status.success() {
- println!("Resultat von Pandoc: {}", output);
- Ok(output)
- } else {
- Err(format!("Pandoc error, exit code {:?}\n{}", status, errors).into())
+ pub fn format_text_to_html(markdown: String) -> Result> {
+ lazy_static! {
+ static ref MATRIX_BOLD: Regex = Regex::new(r"\*\*(.+?)\*\*").unwrap();
+ static ref MATRIX_ITALIC: Regex = Regex::new(r"\*(.+?)\*").unwrap();
+ static ref MATRIX_URL: Regex = Regex::new(r"(https?://\S+?)(?:[.,])?(?:\s|$)").unwrap();
}
- }
-
- pub fn format_matrix_html(markdown: String) -> Result> {
- let mut html = Self::pandoc_convert_md_to_html(markdown)?;
- let url_regex = Regex::new(r"(https?://[^\s<]+)")?;
- html = url_regex.replace_all(&html, r#"$1"#).to_string();
+ let mut html = markdown;
- html = html.replace("\n\n", "
");
+ // Handle URLs first
+ html = MATRIX_URL.replace_all(&html, r"$1").to_string();
+
+ // Basic formatting
+ html = MATRIX_BOLD.replace_all(&html, r"$1").to_string();
+ html = MATRIX_ITALIC.replace_all(&html, r"$1").to_string();
+
+ // Convert newlines to
html = html.replace("\n", "
");
- html = html.replace("**", "");
- html = html.replace("__", "");
-
Ok(html)
}
-
- pub fn pandoc_convert_text_to_md(markdown: String) -> Result> {
+ pub fn pandoc_convert_text_to_md(markdown: String) -> Result> {
let (output, errors, status) = crate::pipe(
"pandoc",
&mut ["--from", "markdown-auto_identifiers", "--to", "html5"],
@@ -227,14 +215,14 @@ impl MatrixClient {
pub fn send_room_message(
&mut self, room_id: &str, text: &str,
) -> Result> {
- let formatted_text = Self::format_matrix_html(text.to_string())?;
+ let formatted_text = Self::format_text_to_html(text.to_string())?;
let content = HashMap::from([
("type", "m.room.message"),
("msgtype", "m.text"),
("body", text),
("format", "org.matrix.custom.html"),
("formatted_body", &formatted_text),
- ("m.mentions", "{}"),
+ // ("m.mentions", "{}"),
]);
self.send_room_event(&room_id, "m.room.message", &content)
}
@@ -243,14 +231,14 @@ impl MatrixClient {
&mut self, room: &str, event_type: &str, content: &impl Serialize,
) -> Result> {
let endpoint = format!("/r0/rooms/{}/send/{}/{}", room, event_type, self.txn_id());
- println!("room event:{}", &endpoint.red());
+ verboseln!("room event:{}", &endpoint.green());
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,
+ pub fn send_message_to_two_rooms(
+ &mut self, message: &str
) -> Result<(), Box> {
- self.send_room_message( &self.room_id_for_long_messages.clone(), &long_message,)?;
- self.send_room_message( &self.room_id_for_short_messages.clone(), &short_message,)?;
+ self.send_room_message( &self.room_id_2.clone(), &message,)?;
+ self.send_room_message( &self.room_id_1.clone(), &message,)?;
Ok(())
}