(empty) state machine exists
This commit is contained in:
parent
124c1b2a55
commit
77842f1f6d
190
src/main.rs
190
src/main.rs
|
@ -179,14 +179,22 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||
is_dry_run(),
|
||||
);
|
||||
// figure out where we are
|
||||
let last_state = ProgramState::parse(&config["state-name"]);
|
||||
let mut last_state = ProgramState::parse(&config["state-name"]);
|
||||
let last_run = config.get("state-last-run").unwrap_or_default();
|
||||
let last_run = NaiveDate::parse_from_str(&last_run, "%Y-%m-%d").unwrap_or_default();
|
||||
// figure out where we should be
|
||||
let plenum_spec = date::parse_spec(&config["date-spec"])?;
|
||||
let today = Local::now().date_naive();
|
||||
if (today - last_run).num_days() > 10 {
|
||||
if !matches!(last_state, ProgramState::Normal) {
|
||||
eprintln!("WARNING: last run was a long time ago, resetting state.");
|
||||
last_state = ProgramState::Normal;
|
||||
do_cleanup(999, &config, &hedgedoc, &email, &wiki)?;
|
||||
}
|
||||
}
|
||||
let last_state = last_state;
|
||||
let plenum_spec = date::parse_spec(&config["date-spec"])?;
|
||||
let nearest_plenum_days = date::get_matching_dates_around(today, plenum_spec);
|
||||
// deltas has either 2 or 3 days, if 3 then the middle one is == 0
|
||||
// deltas has either 2 or 3 days, if 3 then the middle one is == 0 (i.e. today)
|
||||
let deltas: Vec<i64> = nearest_plenum_days.iter().map(|&d| (d - today).num_days()).collect();
|
||||
// find the relevant one:
|
||||
let delta = *match last_state {
|
||||
|
@ -215,20 +223,16 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||
ProgramState::Logged // after that, we want to log it to the list & the wiki
|
||||
};
|
||||
|
||||
println!("aktueller Zustand: {}", last_state);
|
||||
println!("letzter Durchlauf: {}", last_run);
|
||||
println!("relevante Tage: {:#?}", nearest_plenum_days);
|
||||
println!("Abweichungen: {:#?}", deltas);
|
||||
println!("Geschätzter Termin (Δ): {}", delta);
|
||||
println!("Gewollter Zustand: {}", intended_state);
|
||||
let action: TransitionFunction = TRANSITION_LUT[last_state.index()][intended_state.index()];
|
||||
action(delta, &config, &hedgedoc, &email, &wiki)?;
|
||||
|
||||
// TODO: state-matrix: was macht sinn?
|
||||
// Normal Announced Reminded Waiting Logged
|
||||
// Normal nop announce reminder(!) nop log
|
||||
// Announced log nop reminder nop log
|
||||
// Reminded log log;announce nop nop log
|
||||
// Waiting log log;announce log;reminder(!) nop log
|
||||
// Logged nop announce reminder(!) nop nop
|
||||
// TODO: cleanup / write new state
|
||||
|
||||
if config.has_errors() {
|
||||
return Err("There were errors while writing config values.".into());
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Dienstage diesen Monat
|
||||
let all_tuesdays: Vec<NaiveDate> = get_all_weekdays(0, Weekday::Tue);
|
||||
|
@ -299,8 +303,6 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||
println!("Pad-content geladen!");
|
||||
let current_pad_link = hedgedoc.format_url(¤t_pad_id);
|
||||
let future_pad_link = hedgedoc.format_url(&future_pad_id);
|
||||
let email_greeting = &config["text-email-greeting"];
|
||||
let email_signature = &config["text-email-signature"];
|
||||
if in_1_day_is_plenum {
|
||||
println!("In 1 Tag ist Plenum, deshalb wird eine Erinnerung raus geschickt!");
|
||||
let tldr_vec = create_tldr(&pad_content_without_top_instructions);
|
||||
|
@ -313,37 +315,29 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||
// Mail an alle senden, findet statt
|
||||
let betreff = "Morgen ist Plenum!".to_string(); // ADJ_TIMEYWIMEY
|
||||
let message: String = format!(
|
||||
r#"{email_greeting}
|
||||
|
||||
Es gibt Themen, deshalb wird das morgige Plenum statt finden. Anbei das Plenumspad:
|
||||
r#"Es gibt Themen, deshalb wird das morgige Plenum statt finden. Anbei das Plenumspad:
|
||||
{current_pad_link}
|
||||
|
||||
Und hier ein TL;DR von den aktuellen Themen:
|
||||
{tldr}
|
||||
|
||||
Bis morgen, 20 Uhr!
|
||||
|
||||
{email_signature}"#
|
||||
Bis morgen, 20 Uhr!"#
|
||||
); // ADJ_TIMEYWIMEY
|
||||
message_id = email.send_email(betreff, message).ok()
|
||||
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
|
||||
let betreff = format!("Plenum am {} fällt mangels Themen aus", nächster_plenumtermin);
|
||||
let message: String = format!(
|
||||
r#"{email_greeting}
|
||||
|
||||
Es gibt keine Themen, deshalb wird das morgige Plenum leider nicht statt finden.
|
||||
r#"Es gibt keine Themen, deshalb wird das morgige Plenum leider nicht statt finden.
|
||||
|
||||
Hier ist der Link zum Pad vom nächsten Plenum, das am {} statt finden wird:
|
||||
{future_pad_link}
|
||||
|
||||
Bis zum nächsten Plenum.
|
||||
|
||||
{email_signature}"#,
|
||||
Bis zum nächsten Plenum."#,
|
||||
nächster_plenumtermin
|
||||
);
|
||||
message_id = email.send_email(betreff, message).ok()
|
||||
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 {
|
||||
|
@ -352,16 +346,12 @@ Bis zum nächsten Plenum.
|
|||
// 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#"{email_greeting}
|
||||
|
||||
Es sind bisher leider keine Themen zusammengekommen. Wenn es bis Sonntag Abend keine Themen gibt, wird das Plenum voraussichtlich nicht statt finden.
|
||||
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}
|
||||
|
||||
{email_signature}"#
|
||||
{current_pad_link}"#
|
||||
);
|
||||
message_id = email.send_email(betreff, message).ok()
|
||||
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 {
|
||||
|
@ -381,9 +371,7 @@ Hier ist der Link zum Pad, wo ihr noch Themen eintragen könnt:
|
|||
let tldr = tldr_vec.join("\n");
|
||||
// XXX nächstes/übernächstes?
|
||||
let message: String = format!(
|
||||
r#"{email_greeting}
|
||||
|
||||
Anbei das gestrige Plenumspad. Hier sind die Links zum nächsten:
|
||||
r#"Anbei das gestrige Plenumspad. Hier sind die Links zum nächsten:
|
||||
{current_pad_link}
|
||||
und zum übernächsten Plenum:
|
||||
{future_pad_link}
|
||||
|
@ -393,26 +381,25 @@ TL;DR:
|
|||
|
||||
Und hier ist das Protokoll des letzten Plenums:
|
||||
|
||||
{old_pad_content_without_top_instructions}
|
||||
|
||||
{email_signature}"#
|
||||
{old_pad_content_without_top_instructions}"#
|
||||
);
|
||||
let betreff: String =
|
||||
format!("Plenumsprotokoll vom {}: Es gab {} TOPs", nächster_plenumtermin, top_anzahl);
|
||||
// XXX option x expect
|
||||
message_id = email.send_email(betreff, message).ok();
|
||||
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);
|
||||
}
|
||||
println!("message id: {:?}", message_id);
|
||||
println!("[ENDE]\n{}", "Aktueller Zustand der DB:".bold());
|
||||
config.dump_redacting(&["email-password", "wiki-http-password", "wiki-api-secret", "matrix-password"]).ok();
|
||||
|
||||
if config.has_errors() {
|
||||
Err("There were errors.".into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
config
|
||||
.dump_redacting(&[
|
||||
"email-password",
|
||||
"wiki-http-password",
|
||||
"wiki-api-secret",
|
||||
"matrix-password",
|
||||
])
|
||||
.ok();
|
||||
}
|
||||
|
||||
fn get_all_weekdays(month_offset: i32, week_day: Weekday) -> Vec<NaiveDate> {
|
||||
|
@ -525,12 +512,82 @@ fn try_to_remove_top_instructions(pad_content: String) -> String {
|
|||
result.to_string() // Wenn es nicht geklappt hat, wird einfach das Pad mit dem Kommentar zurückgegeben
|
||||
}
|
||||
|
||||
/// State machine of the announcement logic, ensuring we can deal with inconsistent runs
|
||||
/// (e.g. multiple runs in a day, or skipping days). The states are:
|
||||
/* ***** transition actions ***** */
|
||||
|
||||
fn do_announcement(
|
||||
ttp: i64, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, wiki: &Mediawiki,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
// use TTP to adjust text if needed (in {ttp} days)
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn do_reminder(
|
||||
ttp: i64, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, wiki: &Mediawiki,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
// use TTP to adjust text if needed (tomorrow or today / in {ttp} days)
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn do_protocol(
|
||||
ttp: i64, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, wiki: &Mediawiki,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
// use TTP to adjust text if needed ({-ttp} days ago)
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn do_cleanup(
|
||||
ttp: i64, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, wiki: &Mediawiki,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/* ***** state machine ***** */
|
||||
|
||||
type TransitionFunction = fn(
|
||||
ttp: i64,
|
||||
config: &KV,
|
||||
hedgedoc: &HedgeDoc,
|
||||
email: &SimpleEmail,
|
||||
wiki: &Mediawiki,
|
||||
) -> Result<(), Box<dyn Error>>;
|
||||
|
||||
#[rustfmt::skip]
|
||||
const TRANSITION_LUT: [[TransitionFunction; 5]; 5] = [
|
||||
/* NORMAL ANNOUNCED REMINDED WAITING LOGGED */
|
||||
/* NORMAL */ [nop, do_announcement, do_reminder, nop, nop],
|
||||
/* ANNOUNCED */ [do_cleanup, nop, do_reminder, nop, do_protocol],
|
||||
/* REMINDED */ [do_cleanup, do_clean_announcement, nop, nop, do_protocol],
|
||||
/* WAITING */ [do_cleanup, do_clean_announcement, do_clean_reminder, nop, do_protocol],
|
||||
/* LOGGED */ [do_cleanup, do_clean_announcement, do_clean_reminder, nop, do_cleanup],
|
||||
];
|
||||
|
||||
fn nop(_: i64, _: &KV, _: &HedgeDoc, _: &SimpleEmail, _: &Mediawiki) -> Result<(), Box<dyn Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_clean_announcement(
|
||||
ttp: i64, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, wiki: &Mediawiki,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
do_cleanup(ttp, config, hedgedoc, email, wiki)?;
|
||||
do_announcement(ttp, config, hedgedoc, email, wiki)
|
||||
}
|
||||
|
||||
fn do_clean_reminder(
|
||||
ttp: i64, config: &KV, hedgedoc: &HedgeDoc, email: &SimpleEmail, wiki: &Mediawiki,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
do_cleanup(ttp, config, hedgedoc, email, wiki)?;
|
||||
do_reminder(ttp, config, hedgedoc, email, wiki)
|
||||
}
|
||||
|
||||
/// State machine type for the announcement logic, ensuring we can deal with
|
||||
/// inconsistent runs (e.g. multiple runs in a day, or skipping days). The
|
||||
/// states are:
|
||||
///
|
||||
/// - Normal (default) – in between dates, with nothing to do
|
||||
/// - Announced – the first announcement was sent
|
||||
/// - Reminded – the reminder was sent
|
||||
/// - Waiting – just a day of delay (after it makes sense to send a reminder)
|
||||
/// - Logged – protocol was written to wiki & mailing list
|
||||
///
|
||||
/// The bot knows in which state it is at all times. It knows this because it
|
||||
/// knows in which state it isn't. By comparing where it is with where it
|
||||
|
@ -574,10 +631,20 @@ impl ProgramState {
|
|||
"announced" => ProgramState::Announced,
|
||||
"reminded" => ProgramState::Reminded,
|
||||
"waiting" => ProgramState::Waiting,
|
||||
// "logged" also becomes Normal
|
||||
"logged" => ProgramState::Logged,
|
||||
_ => ProgramState::Normal,
|
||||
}
|
||||
}
|
||||
|
||||
fn index(&self) -> usize {
|
||||
match self {
|
||||
ProgramState::Normal => 0,
|
||||
ProgramState::Announced => 1,
|
||||
ProgramState::Reminded => 2,
|
||||
ProgramState::Waiting => 3,
|
||||
ProgramState::Logged => 4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ProgramState {
|
||||
|
@ -592,3 +659,14 @@ impl std::fmt::Display for ProgramState {
|
|||
write!(f, "{}", str)
|
||||
}
|
||||
}
|
||||
|
||||
/* ***** wrappers ***** */
|
||||
|
||||
fn send_email(subject: &str, body: &str, email: &SimpleEmail, config: &KV) -> Option<String> {
|
||||
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()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue