From 2df36a387a93a72a0e8a79c7fc683eb1976a89cd Mon Sep 17 00:00:00 2001 From: murmeldin Date: Thu, 18 Jul 2024 19:37:34 +0200 Subject: [PATCH] initial commit --- .gitignore | 1 + Cargo.toml | 20 ++++ plenum-template.md | 37 ++++++++ readme.md | 1 + src/main.rs | 231 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 290 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 plenum-template.md create mode 100644 readme.md create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..6f9dc36 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "Plenum-Bot" +version = "0.1.0" +edition = "2021" + +[dependencies] +#pandoc = "0.8" +#http = "1.0" +tokio = { version = "1", features = ["full"] } +chrono = "0.4.38" +regex = "1.10.5" +#mail = "0.7.0" +futures = "0.3.30" +headers = "0.4.0" +reqwest = "0.12.5" +lettre = "0.11.7" +rand = "0.9.0-alpha.1" +#mail-core = "0.6.2" +#mail-headers = "0.6.6" +#mail-internals = "0.2.3" diff --git a/plenum-template.md b/plenum-template.md new file mode 100644 index 0000000..c6cde6d --- /dev/null +++ b/plenum-template.md @@ -0,0 +1,37 @@ +# Plenum 23.Juli'24 +--- +Cursorparkplatz (0€/h) +v cursor nach unten + +^ cursor nach oben + +--- +# Meeting Minutes 2024-07-23 +**Beginn 20:XX** + +## Table of Contents +[toc] + + +## Anwesend +X + +## TOP 1: Der Vorstand berichtet + + +**Ende 2X:XX** + +Nächstes Plenum: Am 13.08.2024 um 20 Uhr + +--- + +# Backlog + + +## TOP: Titel (TOP-Owner) +- Beschreibung des TOPs +- dies ist ein Template-TOP + + +## TOP 2: (aus dem Backlog) +- TOP aus dem Backlog pullen diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..4e768b5 --- /dev/null +++ b/readme.md @@ -0,0 +1 @@ +# \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..4ca9128 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,231 @@ +// Dies ist der Plenumsbot vom Chaos Computer Club Berlin. +/* +Plenumsbot + Ankündigungsskript + • Schauen, wann ein Plenum stattfindet + • Wenn eins in 3 Tagen stattfindet, nächstes Pad kopieren und per Mail schicken + → Pad als .md herunterladen + → Text dranhängen und per mail an intern@ verschicken + • Wenn 1 Tag vor dem Plenum immer noch kein TOP im Pad ist + → Mail mit Absage verschicken +Neues-Pad-Erstellen-Skript + • Schauen, wann Plenum stattfindet + • Wenn eins im nächsten Monat stattfindet, von dem noch kein Pad erstellt wurde, dann eins hinzufügen +Pad-ins-Wiki-und-versenden-Skript + • Skript wird manuell nach dem Plenum ausgelöst + • Plenumspad wird als .md heruntergeladen + • Text dranhängen und per Mail an intern@ verschicken + • Pad in MediaWiki-Format umwandeln + • Neue Wiki-Seite erstellen und dort das umgewandelte Pad hochladen +*/ +use chrono::{Datelike, Local, NaiveDate, Weekday}; +use regex::{Regex}; +use std::process::Command; +use headers::ContentType; + +// MAIL START +use lettre::{Message, SmtpTransport, Transport}; +use lettre::message::{header, SinglePart}; +use lettre::transport::smtp::authentication::Credentials; +// MAIL END + +use rand::{Rng, thread_rng}; +use rand::distributions::Alphanumeric; + + +#[tokio::main] +async fn main() { + // BEGIN ANKÜNDIGUNGSSCRIPT + + // Dienstage diesen Monat + + let all_tuesdays: Vec> = get2nd_and4th_tuesdays(); + let zweiter_dienstag: String = all_tuesdays[1].unwrap().to_string(); // z.B. 2024-07-09 + let vierter_dienstag: String = all_tuesdays[3].unwrap().to_string(); // z.B. 2024-07-23 + + //Dienstage nächsten Monat + let all_tuesdays_next_month: Vec> = get2nd_and4th_tuesdays_of_next_month(); + let zweiter_dienstag_nächster_monat: String = all_tuesdays_next_month[1].unwrap().to_string(); + let _vierter_dienstag_nächster_monat: String = all_tuesdays_next_month[3].unwrap().to_string(); + + let today= Local::now(); + let today_simple = NaiveDate::from_ymd_opt(today.year(), today.month(), today.day()).unwrap(); + let in_1_day: NaiveDate = NaiveDate::from_ymd_opt(today.year(), today.month(), today.day()).unwrap().succ_opt().unwrap(); + let in_2_days: NaiveDate = in_1_day.succ_opt().unwrap(); + let in_3_days: NaiveDate = in_2_days.succ_opt().unwrap(); + + + // Nächste Plena nachschauen: + + let nächster_plenumtermin = if all_tuesdays[1].unwrap() >= today_simple.pred_opt().unwrap() { // Für das Pad rumschicken am nächsten Tag wird das Datum einen Tag nach Hinten gesetzt, + &zweiter_dienstag + } else { + &vierter_dienstag + }; + let übernächster_plenumtermin = if all_tuesdays[1].unwrap() >= today_simple.pred_opt().unwrap() { // hier das Gleiche. + &vierter_dienstag + } else { + &zweiter_dienstag_nächster_monat + }; + // Der Code muss nur für vor dem 2. und vor dem 4. Dienstag gebaut werden, weil im nächsten Monat der Code frühestens 7 Tage vor dem Plenum wieder passt. + + + let in_1_day_is_plenum: bool = check_if_plenum(zweiter_dienstag.clone(), vierter_dienstag.clone(), in_1_day); + let in_3_days_is_plenum: bool = check_if_plenum(zweiter_dienstag.clone(), vierter_dienstag.clone(), in_3_days); + + let pad_id: String = String::from("OlizTqsSQEeqil0OZmo-Qw"); + let current_pad_link: String = format!("https://md.berlin.ccc.de/{}/download", pad_id); + let future_pad_id:String = String::from("NOCH NICHT IMPLIMENTIERT"); + let future_pad_link: String = format!("https://md.berlin.ccc.de/{}/download", future_pad_id); + + if in_1_day_is_plenum { + let pad_content = download_and_return_pad(current_pad_link.clone()).await.expect("Fehler beim Download des Pads!"); + println!("Pad-content geladen!"); + if did_the_pad_change(&pad_content) { + // 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!\n\n[Diese Nachricht wurde automatisiert vom Plenumsbot erstellt und ist daher ohne Unterschrift gültig.]"); + println!("{}", message); + let betreff = format!("Plenum vom {} findet statt", nächster_plenumtermin); + mail_versenden(message, betreff).expect("Plenum findet statt. Mail wurde versucht zu senden, konnte aber nicht gesendet werden!") + } else { + // Mail an alle senden und absagen + let message: String = format!("Hallo liebe Mitreisenden,\nEs sind leider keine Themen zusammengekommen, deshalb wird das morgige Plenum leider nicht statt finden.\nHier ist der Link zum Pad vom nächsten Plenum, das am {} statt finden wird: {future_pad_link} und hier der morgige Padlink, der nicht mehr gebraucht wird: {current_pad_link}\nBis zum nächsten Plenum.\n\n[Diese Nachricht wurde automatisiert vom Plenumsbot erstellt und ist daher ohne Unterschrift gültig.]", nächster_plenumtermin); + println!("{}", message); + let betreff = format!("Plenum vom {} muss leider ausfallen", nächster_plenumtermin); + mail_versenden(message, betreff).expect("Plenum wird abgesagt. Mail wurde versucht zu senden, konnte aber nicht gesendet werden!") + } + } else if in_3_days_is_plenum { + let pad_content = download_and_return_pad(current_pad_link.clone()).await.expect("Fehler beim Download des Pads!"); + println!("Pad-content geladen!"); + if !did_the_pad_change(&pad_content) { + // Mail an alle senden und absagen + 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); + println!("{}", message); + mail_versenden(message, betreff).expect("Noch nicht genug Themen. Mail wurde versucht zu senden, konnte aber nicht gesendet werden!") + } + } + // END ANKÜNDIGUNGSSCRIPT + let datum = String::from("15.10.24"); + generate_new_pad_for_following_date(datum); +} + + +async fn download_and_return_pad(pad_link: String) -> Result> { + //https://md.berlin.ccc.de/OlizTqsSQEeqil0OZmo-Qw/download + let pad_content: String = reqwest::get(pad_link) + .await? + .text() + .await?; + //println!("{}", pad_content); + Ok(pad_content) +} + +fn get2nd_and4th_tuesdays() -> Vec>{ + // Jeden Donnerstag im Monat finden: + let today = Local::now(); // jetzige Zeit abrufen (z.B. 2024-07-17 15:36:31.660997700 +02:00) + let current_month = today.month(); // diesen Monat als Zahl (z.B. 7) + let current_year = today.year(); // dieses Jahr als Zahl (z.B. 2024) + let first_day: Option = NaiveDate::from_ymd_opt(current_year, current_month, 1); // Erster Tag des Monats (z.B. 2024-07-01) + let last_day: Option = NaiveDate::from_ymd_opt(current_year, current_month+1, 1).unwrap().pred_opt(); //Letzter Tag des Monats(z.B. 2024-07-31) + // - Vektor mit allen Tagen im Monat erstellen + let mut days_in_month_vec: Vec> = Vec::new(); + let mut all_tuesdays: Vec> = Vec::new(); + let mut current_day: Option = first_day; + while current_day <= last_day { + days_in_month_vec.push(current_day); + current_day = current_day.unwrap().succ_opt() + } + // - Alle bis auf Donnerstage raus + println!("Dienstage: "); + for element in days_in_month_vec { + match element.unwrap().weekday() { + Weekday::Tue => {print!("{} ", element.unwrap()); + all_tuesdays.push(element)}, + _ => {} // Kein Dienstag, + } + } + all_tuesdays +} + +fn get2nd_and4th_tuesdays_of_next_month() -> Vec>{ + // Jeden Donnerstag im Monat finden: + let today = Local::now(); // jetzige Zeit abrufen (z.B. 2024-07-17 15:36:31.660997700 +02:00) + let current_month = today.month(); // diesen Monat als Zahl (z.B. 7) + let current_year = today.year(); // dieses Jahr als Zahl (z.B. 2024) + let first_day: Option = NaiveDate::from_ymd_opt(current_year, current_month+1, 1); // Erster Tag des Monats (z.B. 2024-07-01) + let last_day: Option = NaiveDate::from_ymd_opt(current_year, current_month+2, 1).unwrap().pred_opt(); //Letzter Tag des Monats(z.B. 2024-07-31) + // - Vektor mit allen Tagen im Monat erstellen + let mut days_in_month_vec: Vec> = Vec::new(); + let mut all_tuesdays: Vec> = Vec::new(); + let mut current_day: Option = first_day; + while current_day <= last_day { + days_in_month_vec.push(current_day); + current_day = current_day.unwrap().succ_opt() + } + // - Alle bis auf Donnerstage raus + for element in days_in_month_vec { + match element.unwrap().weekday() { + Weekday::Tue => {print!("{} ", element.unwrap()); + all_tuesdays.push(element)}, + _ => {} // Kein Dienstag, + } + } + all_tuesdays +} + +fn check_if_plenum(zweiter_dienstag: String, vierter_dienstag: String, date_to_check: NaiveDate) -> bool { + // Überprüfen, ob an dem Datum Plenum ist + let date_to_check = date_to_check.to_string(); + if zweiter_dienstag == date_to_check || vierter_dienstag == date_to_check { + println!("Heute ist Plenum!"); + return true + } else { + return false + } +} + +fn did_the_pad_change (pad_content: &String) -> bool { + // 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 m = re.find(&pad_content); + m.is_some() +} + +fn mail_versenden(inhalt: String, betreff: String) -> std::result::Result<(), Box> { + // Define the email + let email = Message::builder() + // Set the sender's name and email address + .from("Plenum Bot ".parse().unwrap()) + // Set the recipient's name and email address + .to("Marek Krug ".parse().unwrap()) + // Set the subject of the email + .subject(betreff) + .singlepart(SinglePart::builder() + .header(header::ContentType::TEXT_PLAIN) + .body(inhalt)) + + .unwrap(); + // Set up the SMTP client + let creds = Credentials::new("plenum-bot@berlin.ccc.de".to_string(), "m9yBn16pUtbC".to_string()); + // Open a remote connection to gmail + let mailer = SmtpTransport::starttls_relay("mail.berlin.ccc.de")? + .credentials(creds) + .build(); + // Send the email + match mailer.send(&email) { + Ok(_) => println!("Email sent successfully!"), + Err(e) => eprintln!("Could not send email: {:?}", e), } + Ok(()) +} + +fn generate_new_pad_for_following_date(datum: String) { + let rand_string: String = thread_rng() + .sample_iter(&Alphanumeric) + .take(22) + .map(char::from) + .collect(); + + println!("{}", rand_string) + +} \ No newline at end of file