TL;DR and Number of TOPs added to the mail.

There is also much better regex and the top instructions are
automatically being removed when being sent per email or put
in the wiki-function.
This commit is contained in:
murmeldin 2024-07-24 23:32:47 +02:00 committed by murmeldin
parent abbfef233f
commit 72bd5ba9cf
4 changed files with 107 additions and 47 deletions

2
.gitignore vendored
View file

@ -1,3 +1,5 @@
/target /target
/.idea /.idea
plenum_config.sqlite plenum_config.sqlite
.direnv
shell.nix

View file

@ -4,12 +4,10 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
#pandoc = "0.8" pandoc = "0.8"
#http = "1.0"
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }
chrono = "0.4.38" chrono = "0.4.38"
regex = "1.10.5" regex = "1.10.5"
#mail = "0.7.0"
futures = "0.3.30" futures = "0.3.30"
headers = "0.4.0" headers = "0.4.0"
reqwest = "0.12.5" reqwest = "0.12.5"
@ -18,6 +16,3 @@ rand = "0.9.0-alpha.1"
rusqlite = "0.31.0" rusqlite = "0.31.0"
uuid = { version = "1.10.0", features = ["v4"] } uuid = { version = "1.10.0", features = ["v4"] }
log = "0.4.22" log = "0.4.22"
#mail-core = "0.6.2"
#mail-headers = "0.6.6"
#mail-internals = "0.2.3"

View file

@ -21,6 +21,8 @@ Pad-ins-Wiki-und-versenden-Skript
// Import other .rs files as modules // Import other .rs files as modules
mod key_value; mod key_value;
use std::borrow::Cow;
use key_value::KeyValueStore as KV; use key_value::KeyValueStore as KV;
mod create_new_pads; mod create_new_pads;
pub mod variables_and_settings; pub mod variables_and_settings;
@ -30,7 +32,8 @@ use regex::{Regex};
use uuid::Uuid; use uuid::Uuid;
use reqwest::Client; use reqwest::Client;
use std::error::Error; use std::error::Error;
use std::future::Future; // use std::future::Future;
use pandoc;
// use std::process::Command; // use std::process::Command;
// use headers::ContentType; // use headers::ContentType;
@ -45,7 +48,7 @@ use lettre::transport::smtp::authentication::Credentials;
const HEDGEDOC_SERVER_URL: &str = variables_and_settings::HEDGEDOC_SERVER_URL; const HEDGEDOC_SERVER_URL: &str = variables_and_settings::HEDGEDOC_SERVER_URL;
const PLENUM_TEMPLATE_URL: &str = variables_and_settings::PLENUM_TEMPLATE_URL; const PLENUM_TEMPLATE_URL: &str = variables_and_settings::PLENUM_TEMPLATE_URL;
const FALLBACK_TEMPLATE: &str = variables_and_settings::FALLBACK_TEMPLATE; const FALLBACK_TEMPLATE: &str = variables_and_settings::FALLBACK_TEMPLATE;
const TESTING_MODE: bool = true; const TESTING_MODE: bool = false;
fn kv_defaults (kv: &KV) { fn kv_defaults (kv: &KV) {
@ -59,6 +62,7 @@ async fn main() {
// config // config
let config = KV::new("plenum_config.sqlite").unwrap(); let config = KV::new("plenum_config.sqlite").unwrap();
kv_defaults(&config); kv_defaults(&config);
println!("[BEGINNING] Aktuelle Situation der Datenbank: \"current_pad_link\": https://md.berlin.ccc.de/{}, \"zukünftiges-plenumspad\" https://md.berlin.ccc.de/{}", config.get("aktuelles-plenumspad").unwrap().unwrap(), config.get("zukünftiges-plenumspad").unwrap_or_else(|error|Option::from(String::from("error"))).unwrap_or(String::from("error")));
// Dienstage diesen Monat // Dienstage diesen Monat
let all_tuesdays: Vec<Option<NaiveDate>> = get_tuesdays(0); let all_tuesdays: Vec<Option<NaiveDate>> = get_tuesdays(0);
@ -105,24 +109,31 @@ async fn main() {
let yesterday_was_plenum: bool = check_if_plenum(nächster_plenumtermin.clone(), yesterday); let yesterday_was_plenum: bool = check_if_plenum(nächster_plenumtermin.clone(), yesterday);
// Pad-Links aus der Datenbank laden: // Pad-Links aus der Datenbank laden:
let pad_id: String = config.clone().get("aktuelles-plenumspad").unwrap().unwrap(); let pad_id: String = config.get("aktuelles-plenumspad").unwrap().unwrap();
let current_pad_link: String = format!("https://md.berlin.ccc.de/{}/download", pad_id); let current_pad_link: String = format!("https://md.berlin.ccc.de/{}", pad_id);
let future_pad_id:String = config.clone().get("zukünftiges-plenumspad").unwrap().unwrap(); let future_pad_id:String = config.get("zukünftiges-plenumspad").unwrap_or_else(|error|Option::from(String::from("error"))).unwrap_or(String::from("error"));
let future_pad_link: String = format!("https://md.berlin.ccc.de/{}/download", future_pad_id); let future_pad_link: String = format!("https://md.berlin.ccc.de/{}", future_pad_id);
let mut message_id: String = String::from("FEHLER"); // message id initialisieren let mut message_id: String = String::from("FEHLER"); // message id initialisieren
let in_3_days_is_plenum = true; // let in_3_days_is_plenum = true;
let top_anzahl: i32 = 10; // Muss noch gecodet werden
let top_anzahl: i32 = 0; // Muss noch gecodet werden
let in_1_day_is_plenum = true;
if in_1_day_is_plenum { if in_1_day_is_plenum {
println!("In 1 Tag ist Plenum, deshalb wird eine Erinnerung raus geschickt!"); println!("In 1 Tag ist Plenum, deshalb wird eine Erinnerung raus geschickt!");
let pad_content = download_and_return_pad(current_pad_link.clone()).await.expect("Fehler beim Download des Pads!"); let pad_content = download_and_return_pad(format!("{}/download", current_pad_link.clone())).await.expect("Fehler beim Download des Pads!");
let pad_content_without_top_instructions = try_to_remove_top_instructions(pad_content);
let tldr_vec = create_tldr(&pad_content_without_top_instructions);
let mut tldr = String::new();
for element in tldr_vec {
tldr.push_str("\n");
tldr.push_str(&element)
}
println!("Pad-content geladen!"); println!("Pad-content geladen!");
if did_the_pad_change(&pad_content) { if number_of_tops(&pad_content_without_top_instructions) != 0 {
// Mail an alle senden, findet statt // Mail an alle senden, findet statt
let message: String = format!("Hallo liebe Mitreisenden,\nEs gibt Themen, deshalb wird das morgige Plenum statt finden.\nAnbei das Plenumspad:\n{current_pad_link}\n Bis morgen, 20 Uhr!"); let message: String = format!("Hallo liebe Mitreisenden,\nEs gibt Themen, deshalb wird das morgige Plenum statt finden.\nAnbei das Plenumspad:\n{current_pad_link}\nUnd hier ein TL;DR von den aktuellen Themen:{tldr}\n\nBis morgen, 20 Uhr!");
println!("---E-Mail:---\n{}\n-----------", message); println!("---E-Mail:---\n{}\n-----------", message);
let betreff = format!("Plenum vom {} findet statt", nächster_plenumtermin); let betreff = format!("Plenum vom {} findet statt", nächster_plenumtermin);
message_id = mail_versenden(message, betreff).expect("Plenum findet statt. Mail wurde versucht zu senden, konnte aber nicht gesendet werden!") message_id = mail_versenden(message, betreff).expect("Plenum findet statt. Mail wurde versucht zu senden, konnte aber nicht gesendet werden!")
@ -136,9 +147,10 @@ async fn main() {
} else if in_3_days_is_plenum { } else if in_3_days_is_plenum {
println!("In 3 Tagen ist Plenum, deshalb wird eine Erinnerung raus geschickt!"); println!("In 3 Tagen ist Plenum, deshalb wird eine Erinnerung raus geschickt!");
let pad_content = download_and_return_pad(current_pad_link.clone()).await.expect("Fehler beim Download des Pads!"); let pad_content = download_and_return_pad(current_pad_link.clone()).await.expect("Fehler beim Download des Pads!");
let pad_content_without_top_instructions = try_to_remove_top_instructions(pad_content);
println!("Pad-content geladen!"); println!("Pad-content geladen!");
if !did_the_pad_change(&pad_content) { if number_of_tops(&pad_content_without_top_instructions) == 0 {
// Mail an alle senden und absagen // Mail an alle senden und sagen, dass es noch keine Themen gibt
let message: String = format!("Hallo liebe Mitreisenden,\nEs sind leider bisher keine Themen zusammengekommen. Wenn bis Sonntag Abend keine Themen zusammenkommen, wird das Plenum voraussichtlich nicht statt finden.\nHier ist der Link zum Pad, wo ihr noch Themen einragen könnt: {}\n\n[Diese Nachricht wurde automatisiert vom Plenumsbot erstellt und ist daher ohne Unterschrift gültig.]", current_pad_link.clone()); let message: String = format!("Hallo liebe Mitreisenden,\nEs sind leider bisher keine Themen zusammengekommen. Wenn bis Sonntag Abend keine Themen zusammenkommen, wird das Plenum voraussichtlich nicht statt finden.\nHier ist der Link zum Pad, wo ihr noch Themen einragen könnt: {}\n\n[Diese Nachricht wurde automatisiert vom Plenumsbot erstellt und ist daher ohne Unterschrift gültig.]", current_pad_link.clone());
let betreff = format!("Plenum vom {}:Bisher noch keine Plenumsthemen", nächster_plenumtermin); let betreff = format!("Plenum vom {}:Bisher noch keine Plenumsthemen", nächster_plenumtermin);
println!("---E-Mail:---\n{}\n-----------", message); println!("---E-Mail:---\n{}\n-----------", message);
@ -147,17 +159,27 @@ async fn main() {
println!("message id: {}", message_id) println!("message id: {}", message_id)
} else if yesterday_was_plenum { } else if yesterday_was_plenum {
// This logic breaks on 02/2034, but on every other month it works // This logic breaks on 02/2034, but on every other month it works
let old_pad_content = download_and_return_pad(current_pad_link.clone()).await.expect("Fehler beim Download des Pads!"); let old_pad_content = download_and_return_pad(format!("{}/download", current_pad_link.clone())).await.expect("Fehler beim Download des Pads!");
generate_new_pad_for_following_date(übernächster_plenumtermin, überübernächster_plenumtermin, &config).await.expect("Fehler! Plenumspad konnte nicht generiert werden!"); generate_new_pad_for_following_date(übernächster_plenumtermin, überübernächster_plenumtermin, &config).await.expect("Fehler! Plenumspad konnte nicht generiert werden!");
println!("DATENBANK: aktuelles-plenumspad: {:?} und zukünftiges plenumspad: {:?}", &config.get("aktuelles-plenumspad"), &config.get("zukünftiges-plenumspad")); println!("DATENBANK: aktuelles-plenumspad: {:?} und zukünftiges plenumspad: {:?}", &config.get("aktuelles-plenumspad"), &config.get("zukünftiges-plenumspad"));
let message: String = format!("Hallo liebe Mitreisenden,\nAnbei das gestrige Plenumspad.\nHier sind die Links zum nächsten: {}\nund zum übernächsten Plenum: {}\nUnd hier ist das Protokoll des letzten Plenums:\n{}\n[Diese Nachricht wurde automatisiert vom Plenumsbot erstellt und ist daher ohne Unterschrift gültig.]", current_pad_link, future_pad_link, old_pad_content.clone()); let old_pad_content_without_top_instructions = try_to_remove_top_instructions(old_pad_content);
let tldr_vec = create_tldr(&old_pad_content_without_top_instructions);
let mut tldr = String::new();
for element in tldr_vec {
tldr.push_str("\n");
tldr.push_str(&element)
}
let message: String = format!("Hallo liebe Mitreisenden,\nAnbei das gestrige Plenumspad.\nHier sind die Links zum nächsten: {}\nund zum übernächsten Plenum:\nTL;DR:{} {}\nUnd hier ist das Protokoll des letzten Plenums:\n{}\n[Diese Nachricht wurde automatisiert vom Plenumsbot erstellt und ist daher ohne Unterschrift gültig.]", current_pad_link, future_pad_link, tldr, old_pad_content_without_top_instructions.clone());
let betreff: String = format!("Plenumsprotokoll vom {}: Es gab {} TOPs", nächster_plenumtermin, top_anzahl); let betreff: String = format!("Plenumsprotokoll vom {}: Es gab {} TOPs", nächster_plenumtermin, top_anzahl);
println!("---E-Mail:---\n{}\n-----------", message); println!("---E-Mail:---\n{}\n-----------", message);
message_id = mail_versenden(message, betreff).expect("Mail mit Plenumsprotokoll wurde versucht zu senden, konnte aber nicht gesendet werden!"); message_id = mail_versenden(message, betreff).expect("Mail mit Plenumsprotokoll wurde versucht zu senden, konnte aber nicht gesendet werden!");
pad_ins_wiki(old_pad_content); pad_ins_wiki(old_pad_content_without_top_instructions);
} }
// END ANKÜNDIGUNGSSCRIPT println!("[END] Aktuelle Situation der Datenbank: \"current_pad_link\": https://md.berlin.ccc.de/{}, \"zukünftiges-plenumspad\" https://md.berlin.ccc.de/{}", config.get("aktuelles-plenumspad").unwrap().unwrap(), config.get("zukünftiges-plenumspad").unwrap_or_else(|error|Option::from(String::from("error"))).unwrap_or(String::from("error")));
} }
@ -187,11 +209,9 @@ fn get_tuesdays(month_offset: u32) -> Vec<Option<NaiveDate>>{
current_day = current_day.unwrap().succ_opt() current_day = current_day.unwrap().succ_opt()
} }
// - Alle bis auf Donnerstage raus // - Alle bis auf Donnerstage raus
println!("Dienstage: ");
for element in days_in_month_vec { for element in days_in_month_vec {
match element.unwrap().weekday() { match element.unwrap().weekday() {
Weekday::Tue => {print!("{} ", element.unwrap()); Weekday::Tue => all_tuesdays.push(element),
all_tuesdays.push(element)},
_ => {} // Kein Dienstag, _ => {} // Kein Dienstag,
} }
} }
@ -201,19 +221,27 @@ fn get_tuesdays(month_offset: u32) -> Vec<Option<NaiveDate>>{
fn check_if_plenum(infrage_kommendes_plenum: String, date_to_check: NaiveDate) -> bool { fn check_if_plenum(infrage_kommendes_plenum: String, date_to_check: NaiveDate) -> bool {
// Überprüfen, ob an dem Datum Plenum ist // Überprüfen, ob an dem Datum Plenum ist
let date_to_check = date_to_check.to_string(); let date_to_check = date_to_check.to_string();
if infrage_kommendes_plenum == date_to_check { return if infrage_kommendes_plenum == date_to_check {
println!("Heute ist Plenum!"); true
return true
} else { } else {
return false false
} }
} }
fn did_the_pad_change (pad_content: &String) -> bool { fn number_of_tops (pad_content: &String) -> i32 {
// Logik: Wenn irgendwo TOP 2, top2, Top2 oder TOP2 steht, dann gibt es Themen // Logik: Wenn irgendwo TOP 2, top2, Top2 oder TOP2 steht, dann gibt es Themen
let re = Regex::new(r"(TOP 2|Top2|top2|TOP2|top 2)").unwrap(); let re = Regex::new(r"^##+ *([Tt][Oo][Pp] +\d[.\d]*.*)$").unwrap();
let m = re.find(&pad_content); let m = re.find(&pad_content);
m.is_some() m.iter().len() as i32
}
fn create_tldr (pad_content: &String) -> Vec<&str> {
// Logik: Wenn irgendwo TOP 2, top2, Top2 oder TOP2 steht, dann gibt es Themen
let re_top = Regex::new(r"^##+ *([Tt][Oo][Pp] +\d[.\d]*.*)$").unwrap();
let tldr: Vec<&str> = re_top.find_iter(&pad_content).map(|m|m.as_str()).collect();
//println!("{:?}", m);
tldr
//tldr_vec.append() = m.iter()
} }
fn mail_versenden(inhalt: String, betreff: String) -> std::result::Result<String, Box<dyn std::error::Error>> { fn mail_versenden(inhalt: String, betreff: String) -> std::result::Result<String, Box<dyn std::error::Error>> {
@ -278,11 +306,9 @@ async fn generate_new_pad_for_following_date(übernächster_plenumtermin: String
fn replace_placeholders(template: &str, übernächster_plenumtermin: String, überübernächster_plenumtermin: String) -> Result<String, Box<dyn Error>> { fn replace_placeholders(template: &str, übernächster_plenumtermin: String, überübernächster_plenumtermin: String) -> Result<String, Box<dyn Error>> {
let re_datum = Regex::new(r"\{\{Datum\}\}")?; let re_datum = Regex::new(r"\{\{Datum\}\}")?;
let re_naechstes_plenum = Regex::new(r"\{\{naechstes-plenum\}\}")?;
let result = re_datum.replace_all(template, übernächster_plenumtermin); let result = re_datum.replace_all(template, übernächster_plenumtermin);
let re_naechstes_plenum = Regex::new(r"\{\{naechstes-plenum\}\}")?;
let result = re_naechstes_plenum.replace_all(&result, überübernächster_plenumtermin); let result = re_naechstes_plenum.replace_all(&result, überübernächster_plenumtermin);
Ok(result.to_string()) Ok(result.to_string())
} }
@ -296,6 +322,17 @@ fn rotate (future_pad_id: &str, kv: &KV) {
} }
} }
fn try_to_remove_top_instructions (pad_content: String) -> String {
let re_top_instructions: Regex = Regex::new(r"(<!--(?:.||\n)*-->)").unwrap();
let result: Cow<str> = re_top_instructions.replace_all(&pad_content, "---");
result.to_string() // Wenn es nicht geklappt hat, wird einfach das Pad mit dem Kommentar zurückgegeben
}
fn pad_ins_wiki(old_pad_content: String) { fn pad_ins_wiki(old_pad_content: String) {
//Convert Markdown into Mediawiki
let pandoc_parsed = old_pad_content; // MUSS GEÄNDERT WERDEN
} }

View file

@ -1,17 +1,43 @@
pub const FALLBACK_TEMPLATE: &str = r#" pub const FALLBACK_TEMPLATE: &str = r#"
# Welcome to HedgeDoc # Plenum vom 2024-07-23
## Das Plenum hat leider wegen zu weniger Leute nicht statt gefunden
**Beginn 20:XX**
This is a template pad. ## Themen
[toc]
## Section 1
- Item 1 ## Anwesend
- Item 2 X
## Section 2
Add more content here... <!--
"#; Anleitung (Damit Themen richtig erkannt und in die Mail eingefügt werden):
Bitte zwei Hashtags nutzen und dann den Tagesordnungspunkt, so wie folgt:
## TOP 1: Der Vorstand berichtet
-->
## TOP 1: Vorschlag zur Nutzung eines Plenumsbots (murmeldin + nobody)
## Tpo 2:
## tOp 50L00: 5csgadffweorjapoiesd
## tcop 4:
## Top 5:
Wir (murmeldin + nobody) haben in der letzten Woche an einem Plenumsbot gewerkelt, der in Zukunft die Plena ankündigen, neue Pads erstellen und die Pads automatisch ins Wiki importieren soll. Wir wollen das Projekt vorstellen und etwas Feedback von euch bekommen.
**Ende 2X:XX**
Nächstes Plenum: Am 2024-08-13 um 20 Uhr
---"#;
pub const PLENUM_TEMPLATE_URL: &str = "https://md.berlin.ccc.de/plenum-template/download"; pub const PLENUM_TEMPLATE_URL: &str = "https://md.berlin.ccc.de/plenum-template/download";
pub const HEDGEDOC_SERVER_URL: &str = "https://md.berlin.ccc.de"; pub const HEDGEDOC_SERVER_URL: &str = "https://md.berlin.ccc.de";