announcement/reminder

This commit is contained in:
nobody 2024-08-23 10:16:20 +02:00 committed by murmeldin
parent fecf6ebed5
commit 7093280d80
2 changed files with 107 additions and 45 deletions

View file

@ -80,6 +80,14 @@ impl SimpleEmail {
self.in_reply_to.clone(), self.in_reply_to.clone(),
) )
} }
pub fn into_parts(self) -> (Email, String, Vec<String>, Option<String>) {
(self.base, self.from, self.to, self.in_reply_to)
}
pub fn from_parts(
base: Email, from: String, to: Vec<String>, in_reply_to: Option<String>,
) -> Self {
Self { base, from, to, in_reply_to }
}
} }
impl Email { impl Email {

View file

@ -76,6 +76,7 @@ const CONFIG_SPEC: CfgSpec<'static> = CfgSpec {
default: "NORMAL", default: "NORMAL",
description: "Named state of the program logic. (NORMAL, ANNOUNCED, REMINDED, LOGGED)", 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", CfgField::Optional { key: "last-run",
description: "Last run of the program (used to figure out state of previous plenum mails.)", description: "Last run of the program (used to figure out state of previous plenum mails.)",
}, },
@ -267,7 +268,7 @@ fn main() -> Result<(), Box<dyn Error>> {
&config["email-password"], &config["email-password"],
is_dry_run(), is_dry_run(),
); );
let email = SimpleEmail::new( let mut email = SimpleEmail::new(
email_, email_,
&config["email-from"], &config["email-from"],
&config["email-to"], &config["email-to"],
@ -299,8 +300,12 @@ fn main() -> Result<(), Box<dyn Error>> {
eprintln!("WARNING: last run was a long time ago, resetting state."); eprintln!("WARNING: last run was a long time ago, resetting state.");
last_state = ProgramState::Normal; last_state = ProgramState::Normal;
do_cleanup(999, &today, &config, &hedgedoc, &email, &wiki)?; 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; let last_state = last_state;
// figure out where we should be // figure out where we should be
// deltas has either 2 or 3 days, if 3 then the middle one is == 0 (i.e. today) // 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<dyn Error>> {
.get("hedgedoc-next-id") .get("hedgedoc-next-id")
.expect("ID des nächsten Pads undefiniert. Bitte in der DB eintragen oder generieren."); .expect("ID des nächsten Pads undefiniert. Bitte in der DB eintragen oder generieren.");
let mut message_id: Option<String> = None; let mut message_id: String;
// let in_3_days_is_plenum = true; // let in_3_days_is_plenum = true;
//TEMPORÄR ANFANG: BEI PRODUCTION MUSS DAS HIER RAUS //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!"# Bis morgen, 20 Uhr!"#
); // ADJ_TIMEYWIMEY ); // 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!") // .expect("Plenum findet statt. Mail wurde versucht zu senden, konnte aber nicht gesendet werden!")
} else { } else {
// Mail an alle senden und absagen // 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."#, Bis zum nächsten Plenum."#,
nächster_plenumtermin 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!")) // .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 { } 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 = let old_pad_content =
@ -516,7 +507,7 @@ Und hier ist das Protokoll des letzten Plenums:
let betreff: String = let betreff: String =
format!("Plenumsprotokoll vom {}: Es gab {} TOPs", nächster_plenumtermin, top_anzahl); format!("Plenumsprotokoll vom {}: Es gab {} TOPs", nächster_plenumtermin, top_anzahl);
// XXX option x expect // 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!")); // .expect("Mail mit Plenumsprotokoll wurde versucht zu senden, konnte aber nicht gesendet werden!"));
mediawiki::pad_ins_wiki(old_pad_content_without_top_instructions); 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 get_pad_info(config: &KV, hedgedoc: &HedgeDoc) -> (String, String, usize) {
fn do_announcement(
ttp: i64, plenum_day: &NaiveDate, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail,
wiki: &Mediawiki,
) -> Result<(), Box<dyn Error>> {
let current_pad_id = &config["hedgedoc-last-id"]; let current_pad_id = &config["hedgedoc-last-id"];
let pad_content = hedgedoc.download(current_pad_id).expect("Hedgedoc: Download-Fehler"); let pad_content = hedgedoc.download(current_pad_id).expect("Hedgedoc: Download-Fehler");
let toc = hedgedoc::summarize(pad_content); let toc = hedgedoc::summarize(pad_content);
verboseln!("Zusammenfassung des aktuellen Plenum-Pads:\n{}", toc.cyan()); verboseln!("Zusammenfassung des aktuellen Plenum-Pads:\n{}", toc.cyan());
let n_topics = toc.lines().count(); let n_topics = toc.lines().count();
verboseln!("(Das sind {} Themen.)", n_topics.to_string().cyan()); verboseln!("(Also {}.)", topic_count(n_topics, false).cyan());
// TODO: if (current_pad_id.to_string(), toc, n_topics)
// summary empty: write message variant }
// summary not empty: write other message variant
// TODO: set/write state as Announced /* ***** transition actions ***** */
// TODO: write summary
todo!() // BBBBBBBBBB
fn do_announcement(
ttp: i64, plenum_day: &NaiveDate, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail,
_wiki: &Mediawiki,
) -> Result<(), Box<dyn Error>> {
// 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(&current_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( fn do_reminder(
ttp: i64, plenum_day: &NaiveDate, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, ttp: i64, plenum_day: &NaiveDate, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail,
wiki: &Mediawiki, _wiki: &Mediawiki,
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
// TODO: get pad // fetch current pad contents & summarize
// TODO: make summary let (current_pad_id, toc, n_topics) = get_pad_info(config, hedgedoc);
// TODO: if let old_toc = config.get("state-toc").unwrap_or_default();
// summary empty: write message variant // construct email
// summary not empty: make diff, send diff etc. let subject_suffix = if n_topics == 0 {
// TODO: set/write state as Reminded " fällt aus (keine Themen)".to_string()
// TODO: write summary } else {
todo!() 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(&current_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)] #[allow(unused_variables)]
@ -880,11 +932,13 @@ impl std::fmt::Display for ProgramState {
/* ***** wrappers ***** */ /* ***** wrappers ***** */
fn send_email(subject: &str, body: &str, email: &SimpleEmail, config: &KV) -> Option<String> { fn send_email(
subject: &str, body: &str, email: &SimpleEmail, config: &KV,
) -> Result<String, Box<dyn Error>> {
let full_subject = format!("[PleB] {}", subject); let full_subject = format!("[PleB] {}", subject);
let full_body = format!( let full_body = format!(
"{}\n\n{}\n\n{}", "{}\n\n{}\n\n{}",
&config["text-email-greeting"], body, &config["text-email-signature"] &config["text-email-greeting"], body, &config["text-email-signature"]
); );
email.send_email(full_subject, full_body).ok() email.send_email(full_subject, full_body)
} }