From 7093280d80f28a1555d1e8059dc12960db2051bf Mon Sep 17 00:00:00 2001 From: nobody Date: Fri, 23 Aug 2024 10:16:20 +0200 Subject: [PATCH] announcement/reminder --- src/email.rs | 8 +++ src/main.rs | 144 +++++++++++++++++++++++++++++++++++---------------- 2 files changed, 107 insertions(+), 45 deletions(-) diff --git a/src/email.rs b/src/email.rs index c6e732b..9976129 100644 --- a/src/email.rs +++ b/src/email.rs @@ -80,6 +80,14 @@ impl SimpleEmail { self.in_reply_to.clone(), ) } + pub fn into_parts(self) -> (Email, String, Vec, Option) { + (self.base, self.from, self.to, self.in_reply_to) + } + pub fn from_parts( + base: Email, from: String, to: Vec, in_reply_to: Option, + ) -> Self { + Self { base, from, to, in_reply_to } + } } impl Email { diff --git a/src/main.rs b/src/main.rs index 2b42aea..60c8846 100644 --- a/src/main.rs +++ b/src/main.rs @@ -76,6 +76,7 @@ const CONFIG_SPEC: CfgSpec<'static> = CfgSpec { default: "NORMAL", description: "Named state of the program logic. (NORMAL, ANNOUNCED, REMINDED, LOGGED)", }, + CfgField::Optional { key: "toc", description: "Table of contents / pad summary." }, CfgField::Optional { key: "last-run", description: "Last run of the program (used to figure out state of previous plenum mails.)", }, @@ -267,7 +268,7 @@ fn main() -> Result<(), Box> { &config["email-password"], is_dry_run(), ); - let email = SimpleEmail::new( + let mut email = SimpleEmail::new( email_, &config["email-from"], &config["email-to"], @@ -299,8 +300,12 @@ fn main() -> Result<(), Box> { eprintln!("WARNING: last run was a long time ago, resetting state."); last_state = ProgramState::Normal; do_cleanup(999, &today, &config, &hedgedoc, &email, &wiki)?; + // reset will have cleared in-reply-to if it existed + let (base, from, to, _) = email.into_parts(); + email = SimpleEmail::from_parts(base, from, to, config.get("email-in-reply-to").ok()); } } + let email = email; let last_state = last_state; // figure out where we should be // deltas has either 2 or 3 days, if 3 then the middle one is == 0 (i.e. today) @@ -415,7 +420,7 @@ fn main() -> Result<(), Box> { .get("hedgedoc-next-id") .expect("ID des nächsten Pads undefiniert. Bitte in der DB eintragen oder generieren."); - let mut message_id: Option = None; + let mut message_id: String; // let in_3_days_is_plenum = true; //TEMPORÄR ANFANG: BEI PRODUCTION MUSS DAS HIER RAUS @@ -453,7 +458,7 @@ Und hier ein TL;DR von den aktuellen Themen: Bis morgen, 20 Uhr!"# ); // ADJ_TIMEYWIMEY - message_id = send_email(&betreff, &message, &email, &config); + message_id = send_email(&betreff, &message, &email, &config)?; // .expect("Plenum findet statt. Mail wurde versucht zu senden, konnte aber nicht gesendet werden!") } else { // Mail an alle senden und absagen @@ -467,23 +472,9 @@ Hier ist der Link zum Pad vom nächsten Plenum, das am {} statt finden wird: Bis zum nächsten Plenum."#, nächster_plenumtermin ); - message_id = send_email(&betreff, &message, &email, &config); + message_id = send_email(&betreff, &message, &email, &config)?; // .expect("Plenum wird abgesagt. Mail wurde versucht zu senden, konnte aber nicht gesendet werden!")) } - } else if in_3_days_is_plenum { - println!("In 3 Tagen ist Plenum, deshalb wird eine Erinnerung raus geschickt!"); - if number_of_tops(&pad_content_without_top_instructions) == 0 { - // Mail an alle senden und sagen, dass es noch keine Themen gibt - let betreff = format!("Plenum vom {}: Bisher noch keine Themen", nächster_plenumtermin); - let message: String = format!( - r#"Es sind bisher leider keine Themen zusammengekommen. Wenn es bis Sonntag Abend keine Themen gibt, wird das Plenum voraussichtlich nicht statt finden. - -Hier ist der Link zum Pad, wo ihr noch Themen eintragen könnt: - {current_pad_link}"# - ); - message_id = send_email(&betreff, &message, &email, &config); - // .expect("Noch nicht genug Themen. Mail wurde versucht zu senden, konnte aber nicht gesendet werden!")) - } } else if yesterday_was_plenum { // This logic breaks on 02/2034, but on every other month it works let old_pad_content = @@ -516,7 +507,7 @@ Und hier ist das Protokoll des letzten Plenums: let betreff: String = format!("Plenumsprotokoll vom {}: Es gab {} TOPs", nächster_plenumtermin, top_anzahl); // XXX option x expect - message_id = send_email(&betreff, &message, &email, &config); + message_id = send_email(&betreff, &message, &email, &config)?; // .expect("Mail mit Plenumsprotokoll wurde versucht zu senden, konnte aber nicht gesendet werden!")); mediawiki::pad_ins_wiki(old_pad_content_without_top_instructions); } @@ -674,42 +665,103 @@ fn upper_first(s: &str) -> String { } } -/* ***** transition actions ***** */ +fn topic_count(n: usize, dative: bool) -> String { + match n { + 0 => format!("noch keine{} Themen", if dative { "n" } else { "" }), + 1 => format!("ein{} Thema", if dative { "em" } else { "" }), + _ => format!("{} Themen", n), + } +} -// BBBBBBBBBB +/* ***** repeating action parts ***** */ -#[allow(unused_variables)] -fn do_announcement( - ttp: i64, plenum_day: &NaiveDate, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, - wiki: &Mediawiki, -) -> Result<(), Box> { +fn get_pad_info(config: &KV, hedgedoc: &HedgeDoc) -> (String, String, usize) { let current_pad_id = &config["hedgedoc-last-id"]; let pad_content = hedgedoc.download(current_pad_id).expect("Hedgedoc: Download-Fehler"); let toc = hedgedoc::summarize(pad_content); verboseln!("Zusammenfassung des aktuellen Plenum-Pads:\n{}", toc.cyan()); let n_topics = toc.lines().count(); - verboseln!("(Das sind {} Themen.)", n_topics.to_string().cyan()); - // TODO: if - // summary empty: write message variant - // summary not empty: write other message variant - // TODO: set/write state as Announced - // TODO: write summary - todo!() + verboseln!("(Also {}.)", topic_count(n_topics, false).cyan()); + (current_pad_id.to_string(), toc, n_topics) +} + +/* ***** transition actions ***** */ + +// BBBBBBBBBB + +fn do_announcement( + ttp: i64, plenum_day: &NaiveDate, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, + _wiki: &Mediawiki, +) -> Result<(), Box> { + // fetch current pad contents & summarize + let (current_pad_id, toc, n_topics) = get_pad_info(config, hedgedoc); + // construct email + let subject = format!( + "Plenum {} (am {}): bisher {}", + relative_date(ttp), + plenum_day.format("%d.%m.%Y"), + topic_count(n_topics, false) + ); + let line1 = if n_topics == 0 { + format!( "Es sind bisher leider noch keine Themen zusammengekommen. Wenn am Montag immer noch nix ist, dann fällt das Plenum aus." ) + } else { + format!("Die bisherigen Themen für das Plenum sind:\n\n{toc}") + }; + let line2 = format!( + "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}"); + // 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(); + Ok(()) } -#[allow(unused_variables)] fn do_reminder( ttp: i64, plenum_day: &NaiveDate, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, - wiki: &Mediawiki, + _wiki: &Mediawiki, ) -> Result<(), Box> { - // TODO: get pad - // TODO: make summary - // TODO: if - // summary empty: write message variant - // summary not empty: make diff, send diff etc. - // TODO: set/write state as Reminded - // TODO: write summary - todo!() + // fetch current pad contents & summarize + let (current_pad_id, toc, n_topics) = get_pad_info(config, hedgedoc); + let old_toc = config.get("state-toc").unwrap_or_default(); + // construct email + let subject_suffix = if n_topics == 0 { + " fällt aus (keine Themen)".to_string() + } else { + format!(" findet mit {} statt", topic_count(n_topics, true)) + }; + let subject = format!( + "Plenum {} (am {}) {}", + relative_date(ttp), + plenum_day.format("%d.%m.%Y"), + subject_suffix + ); + let body_prefix = if old_toc == toc { + format!("Die Themen sind gleich geblieben. ") + } 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 { + format!( + "{body_prefix}Die vollen Details findet ihr im Pad:\n {}", + hedgedoc.format_url(¤t_pad_id) + ) + } else { + "Da es immer noch keine Themen gibt fällt das Plenum aus.\n\n".to_string() + + "(Natürlich könnt ihr im Bedarfsfall immer noch kurzfristig ein Treffen einberufen, aber bitte " + + "kündigt das so früh wie möglich an, damit Leute sich darauf einstellen können.)" + // TODO generate + add link for next pad + }; + // 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("state-name", &ProgramState::Reminded.to_string()).ok(); + config.set("state-toc", &toc).ok(); + Ok(()) } #[allow(unused_variables)] @@ -880,11 +932,13 @@ impl std::fmt::Display for ProgramState { /* ***** wrappers ***** */ -fn send_email(subject: &str, body: &str, email: &SimpleEmail, config: &KV) -> Option { +fn send_email( + subject: &str, body: &str, email: &SimpleEmail, config: &KV, +) -> Result> { let full_subject = format!("[PleB] {}", subject); let full_body = format!( "{}\n\n{}\n\n{}", &config["text-email-greeting"], body, &config["text-email-signature"] ); - email.send_email(full_subject, full_body).ok() + email.send_email(full_subject, full_body) }