more cleanup, dry run mode, more config usage
- email templates as raw strings w/ inline vars - email info from config db now - dry run mode is honored by email - hedgedoc interfacing in a separate file - mediawiki stuff (incl.) pandoc moved to a separate file …and more
This commit is contained in:
parent
de57e2c316
commit
8a00931f49
|
@ -24,7 +24,7 @@ const CONFIG_SPEC: CfgSpec<'static> = CfgSpec {
|
||||||
CfgGroup { name: "hedgedoc",
|
CfgGroup { name: "hedgedoc",
|
||||||
description: "HedgeDoc markdown pad server settings",
|
description: "HedgeDoc markdown pad server settings",
|
||||||
fields: &[
|
fields: &[
|
||||||
CfgField::Default { key: "server-url", default: "https://md.berlin.ccc.de/", description: "Hedgedoc server storing the pads." },
|
CfgField::Default { key: "server-url", default: "https://md.berlin.ccc.de", description: "Hedgedoc server storing the pads." },
|
||||||
CfgField::Default { key: "template-name", default: "plenum-template", description: "Name of the pad containing the template to use." },
|
CfgField::Default { key: "template-name", default: "plenum-template", description: "Name of the pad containing the template to use." },
|
||||||
// TODO: make these generators?
|
// TODO: make these generators?
|
||||||
CfgField::Optional { key: "last-id", description: "ID of last plenum's pad." },
|
CfgField::Optional { key: "last-id", description: "ID of last plenum's pad." },
|
||||||
|
@ -46,6 +46,10 @@ const CONFIG_SPEC: CfgSpec<'static> = CfgSpec {
|
||||||
CfgGroup { name: "text",
|
CfgGroup { name: "text",
|
||||||
description: "Various strings used.",
|
description: "Various strings used.",
|
||||||
fields: &[
|
fields: &[
|
||||||
|
CfgField::Default { key: "email-greeting",
|
||||||
|
default: "Hallo liebe Mitreisende,",
|
||||||
|
description: "\"Hello\"-greeting added at the start of every email."
|
||||||
|
},
|
||||||
CfgField::Default { key: "email-signature",
|
CfgField::Default { key: "email-signature",
|
||||||
default: "\n\n[Diese Nachricht wurde automatisiert vom Plenumsbot erstellt und ist daher ohne Unterschrift gültig.]",
|
default: "\n\n[Diese Nachricht wurde automatisiert vom Plenumsbot erstellt und ist daher ohne Unterschrift gültig.]",
|
||||||
description: "Text added at the bottom of every email."
|
description: "Text added at the bottom of every email."
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
use reqwest::blocking::Client;
|
|
||||||
use std::error::Error;
|
|
||||||
|
|
||||||
pub fn create_pad(client: &Client, hedgedoc_url: &str) -> Result<String, Box<dyn Error>> {
|
|
||||||
let resp = client.get(format!("{}/new", hedgedoc_url)).send().unwrap();
|
|
||||||
|
|
||||||
if resp.status().is_success() {
|
|
||||||
let pad_url: String = resp.url().to_string();
|
|
||||||
Ok(pad_url)
|
|
||||||
} else {
|
|
||||||
Err(format!("Failed to create pad: {} - {:?}", resp.status(), resp.text()).into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn import_note(client: &Client, content: String, note_id: Option<&str>, hedgedoc_url: &str) -> Result<String, Box<dyn Error>> {
|
|
||||||
let post_url = match note_id {
|
|
||||||
Some(id) => format!("{}/new/{}", hedgedoc_url, id),
|
|
||||||
None => format!("{}/new", hedgedoc_url),
|
|
||||||
};
|
|
||||||
|
|
||||||
let content_parsed: String = content.clone().to_string();
|
|
||||||
|
|
||||||
println!("Content Parsed:");
|
|
||||||
println!("{}", content_parsed);
|
|
||||||
let resp = client.post(&post_url)
|
|
||||||
.header("Content-Type", "text/markdown")
|
|
||||||
.body(content_parsed)
|
|
||||||
.send()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if resp.status().is_success() {
|
|
||||||
let final_url = resp.url().to_string();
|
|
||||||
Ok(final_url)
|
|
||||||
} else {
|
|
||||||
Err(format!("Failed to import note: {} - {}", resp.status(), resp.text()?).into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() -> Result<(), Box<dyn Error>> {
|
|
||||||
let client = Client::new();
|
|
||||||
|
|
||||||
match create_pad(&client).await {
|
|
||||||
Ok(pad_url) => {
|
|
||||||
println!("Pad created successfully at URL: {}", pad_url);
|
|
||||||
|
|
||||||
let pad_id = pad_url.trim_start_matches(&format!("{}/", HEDGEDOC_URL));
|
|
||||||
match import_note(&client, TEMPLATE_CONTENT, Some(pad_id)).await {
|
|
||||||
Ok(_) => println!("Pad updated successfully with template content."),
|
|
||||||
Err(e) => println!("Failed to update pad: {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => println!("Failed to create pad: {}", e),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
63
src/hedgedoc.rs
Normal file
63
src/hedgedoc.rs
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
use reqwest::{Client,Response};
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
// TODO: implement dry-run logic
|
||||||
|
pub struct HedgeDoc {
|
||||||
|
server_url : String,
|
||||||
|
is_dry_run : bool,
|
||||||
|
client : Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HedgeDoc {
|
||||||
|
pub fn new( server_url: &str, is_dry_run: bool ) -> Self {
|
||||||
|
Self { server_url: server_url.to_string(), is_dry_run, client: Client::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format_url( &self, pad_name: &str ) -> String {
|
||||||
|
format!( "{}/{}", self.server_url, pad_name )
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_action( &self, pad_name: &str, verb: &str ) -> String {
|
||||||
|
format!( "{}/{}/{}", self.server_url, pad_name, verb )
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn do_request( &self, url : &str ) -> Result<Response, Box<dyn Error>> {
|
||||||
|
Ok(self.client.get( url ).send().await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_id_from_response( &self, res : Response ) -> String {
|
||||||
|
res.url().to_string().trim_start_matches( &format!( "{}/", self.server_url ) ).to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn download( &self, pad_name: &str ) -> Result<String, Box<dyn Error>> {
|
||||||
|
Ok(self.do_request( &self.format_action(pad_name, "download") ).await?.text().await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_pad( &self ) -> Result<String, Box<dyn Error>> {
|
||||||
|
let res = self.do_request( &format!( "{}/new", self.server_url ) ).await?;
|
||||||
|
if res.status().is_success() {
|
||||||
|
Ok(self.get_id_from_response(res))
|
||||||
|
} else {
|
||||||
|
Err( format!("Failed to create pad {}\n{}", res.status(), res.text().await?).into() )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn import_note( &self, id: Option<&str>, content: String ) -> Result<String, Box<dyn Error>> {
|
||||||
|
let url = match id {
|
||||||
|
Some(id) => self.format_url( &format!( "new/{id}" ) ),
|
||||||
|
None => self.format_url("new"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = self.client.post(&url)
|
||||||
|
.header( "Content-Type", "text/markdown" )
|
||||||
|
.body(content)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if res.status().is_success() {
|
||||||
|
Ok(self.get_id_from_response(res))
|
||||||
|
} else {
|
||||||
|
Err( format!("Failed to import note: {}\n{}", res.status(), res.text().await?).into() )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
219
src/main.rs
219
src/main.rs
|
@ -19,44 +19,37 @@ Pad-ins-Wiki-und-versenden-Skript
|
||||||
• Neue Wiki-Seite erstellen und dort das umgewandelte Pad hochladen
|
• Neue Wiki-Seite erstellen und dort das umgewandelte Pad hochladen
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// future improvements:
|
||||||
|
// - search ADJ_TIMEYWIMEY to find places that need adjusting if the bot might run late
|
||||||
|
// (that's an incomplete list, but tag things as you notice them…)
|
||||||
|
|
||||||
// Import other .rs files as modules
|
// Import other .rs files as modules
|
||||||
mod key_value;
|
mod key_value;
|
||||||
use key_value::KeyValueStore as KV;
|
use key_value::KeyValueStore as KV;
|
||||||
|
mod hedgedoc;
|
||||||
|
use hedgedoc::HedgeDoc;
|
||||||
|
mod mediawiki;
|
||||||
|
|
||||||
mod config_check;
|
mod config_check;
|
||||||
|
|
||||||
mod create_new_pads;
|
|
||||||
pub mod variables_and_settings;
|
pub mod variables_and_settings;
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::io;
|
|
||||||
use std::io::prelude::*;
|
|
||||||
use std::fs::File;
|
|
||||||
// use std::future::Future;
|
// use std::future::Future;
|
||||||
|
|
||||||
use chrono::{Datelike, Local, Months, NaiveDate, Weekday};
|
use chrono::{Datelike, Local, NaiveDate, Weekday};
|
||||||
use clap::{Arg, Command};
|
use clap::{Arg, Command};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use reqwest::Client;
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use pandoc::{MarkdownExtension, Pandoc, PandocError, PandocOutput};
|
|
||||||
|
|
||||||
use lettre::{Message, SmtpTransport, Transport};
|
use lettre::{Message, SmtpTransport, Transport};
|
||||||
use lettre::message::{header, SinglePart};
|
use lettre::message::{header, SinglePart};
|
||||||
use lettre::transport::smtp::authentication::Credentials;
|
use lettre::transport::smtp::authentication::Credentials;
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
|
||||||
enum EMailOperationStates {
|
|
||||||
Test,
|
|
||||||
ProductionToIntern
|
|
||||||
}
|
|
||||||
|
|
||||||
const HEDGEDOC_SERVER_URL: &str = variables_and_settings::HEDGEDOC_SERVER_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: EMailOperationStates = EMailOperationStates::Test;
|
|
||||||
|
|
||||||
/* ***** Runtime Configuration (Arguments & Environment Variables) ***** */
|
/* ***** Runtime Configuration (Arguments & Environment Variables) ***** */
|
||||||
|
|
||||||
|
@ -111,7 +104,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
return config_check::interactive_check( config );
|
return config_check::interactive_check( config );
|
||||||
}
|
}
|
||||||
// config
|
// config
|
||||||
let hedgedoc_server = &config["hedgedoc-server-url"];
|
let hedgedoc = HedgeDoc::new( &config["hedgedoc-server-url"], is_dry_run() );
|
||||||
println!("[START]\nAktueller Zustand der DB:");
|
println!("[START]\nAktueller Zustand der DB:");
|
||||||
config.dump_redacting(&["email-password","wiki-password","matrix-password"]).ok();
|
config.dump_redacting(&["email-password","wiki-password","matrix-password"]).ok();
|
||||||
|
|
||||||
|
@ -159,101 +152,130 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
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 = &config["aktuelles-plenumspad"];
|
let current_pad_id = config.get("hedgedoc-last-id").expect("ID des aktuellen Pads undefiniert. Bitte in der DB eintragen oder generieren.");
|
||||||
let current_pad_link: String = format!("{}{}", hedgedoc_server, pad_id);
|
let future_pad_id = config.get("hedgedoc-next-id").expect("ID des nächsten Pads undefiniert. Bitte in der DB eintragen oder generieren.");
|
||||||
let future_pad_id = &config["zukünftiges-plenumspad"];
|
|
||||||
let future_pad_link: String = format!("{}{}", hedgedoc_server, future_pad_id);
|
|
||||||
|
|
||||||
let mut message_id: String = String::from("FEHLER"); // message id initialisieren
|
let mut message_id: Option<String> = None;
|
||||||
|
|
||||||
// let in_3_days_is_plenum = true;
|
// let in_3_days_is_plenum = true;
|
||||||
|
|
||||||
let top_anzahl: i32 = 0; // Muss noch gecodet werden
|
let top_anzahl: i32 = 0; // Muss noch gecodet werden
|
||||||
let yesterday_was_plenum = true; // Das ist nur zu Testzwecken, kommt noch weg
|
let yesterday_was_plenum = true; // Das ist nur zu Testzwecken, kommt noch weg
|
||||||
|
let pad_content = hedgedoc.download(¤t_pad_id).await.expect("Fehler beim Download des Pads!");
|
||||||
|
let pad_content_without_top_instructions = try_to_remove_top_instructions(pad_content);
|
||||||
|
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 {
|
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(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 tldr_vec = create_tldr(&pad_content_without_top_instructions);
|
||||||
let mut tldr = String::new();
|
let mut tldr = String::new();
|
||||||
for element in tldr_vec {
|
for element in tldr_vec {
|
||||||
tldr.push_str("\n");
|
tldr.push_str("\n");
|
||||||
tldr.push_str(&element)
|
tldr.push_str(&element)
|
||||||
}
|
}
|
||||||
println!("Pad-content geladen!");
|
|
||||||
if number_of_tops(&pad_content_without_top_instructions) != 0 {
|
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}\nUnd hier ein TL;DR von den aktuellen Themen:{tldr}\n\nBis morgen, 20 Uhr!");
|
let betreff = format!("Morgen ist Plenum!"); // ADJ_TIMEYWIMEY
|
||||||
|
let message: String = format!(r#"{email_greeting}
|
||||||
|
|
||||||
|
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}"#); // ADJ_TIMEYWIMEY
|
||||||
println!("---E-Mail:---\n{}\n-----------", message);
|
println!("---E-Mail:---\n{}\n-----------", message);
|
||||||
let betreff = format!("Plenum vom {} findet statt", nächster_plenumtermin);
|
// XXX option x expect
|
||||||
message_id = mail_versenden(message, betreff).expect("Plenum findet statt. Mail wurde versucht zu senden, konnte aber nicht gesendet werden!")
|
message_id = Some(mail_versenden(&config, betreff, message).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
|
||||||
let message: String = format!("Hallo liebe Mitreisenden,\nEs sind 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);
|
let betreff = format!("Plenum am {nächster_plenumtermin} fällt mangels Themen aus");
|
||||||
|
let message: String = format!(r#"{email_greeting}
|
||||||
|
|
||||||
|
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 {nächster_plenumtermin} statt finden wird:
|
||||||
|
{future_pad_link}
|
||||||
|
|
||||||
|
Bis zum nächsten Plenum.
|
||||||
|
|
||||||
|
{email_signature}"#);
|
||||||
println!("---E-Mail:---\n{}\n-----------", message);
|
println!("---E-Mail:---\n{}\n-----------", message);
|
||||||
let betreff = format!("Plenum vom {} muss leider ausfallen", nächster_plenumtermin);
|
// XXX option x expect
|
||||||
message_id = mail_versenden(message, betreff).expect("Plenum wird abgesagt. Mail wurde versucht zu senden, konnte aber nicht gesendet werden!")
|
message_id = Some(mail_versenden(&config, betreff, message).expect("Plenum wird abgesagt. Mail wurde versucht zu senden, konnte aber nicht gesendet werden!"))
|
||||||
}
|
}
|
||||||
} 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_without_top_instructions = try_to_remove_top_instructions(pad_content);
|
|
||||||
println!("Pad-content geladen!");
|
|
||||||
if number_of_tops(&pad_content_without_top_instructions) == 0 {
|
if number_of_tops(&pad_content_without_top_instructions) == 0 {
|
||||||
// Mail an alle senden und sagen, dass es noch keine Themen gibt
|
// 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 betreff = format!("Plenum vom {nächster_plenumtermin}: Bisher noch keine Themen");
|
||||||
let betreff = format!("Plenum vom {}:Bisher noch keine Plenumsthemen", 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.
|
||||||
|
|
||||||
|
Hier ist der Link zum Pad, wo ihr noch Themen eintragen könnt:
|
||||||
|
{current_pad_link}
|
||||||
|
|
||||||
|
{email_signature}"#);
|
||||||
println!("---E-Mail:---\n{}\n-----------", message);
|
println!("---E-Mail:---\n{}\n-----------", message);
|
||||||
message_id = mail_versenden(message, betreff).expect("Noch nicht genug Themen. Mail wurde versucht zu senden, konnte aber nicht gesendet werden!")
|
// XXX option x expect
|
||||||
|
message_id = Some(mail_versenden(&config, betreff, message).expect("Noch nicht genug Themen. Mail wurde versucht zu senden, konnte aber nicht gesendet werden!"))
|
||||||
}
|
}
|
||||||
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(format!("{}/download", current_pad_link.clone())).await.expect("Fehler beim Download des Pads!");
|
let old_pad_content = hedgedoc.download(¤t_pad_id).await.expect("Fehler beim Download des Pads!");
|
||||||
// MUSS WIEDER REIN NACH DEM TESTEN: generate_new_pad_for_following_date(übernächster_plenumtermin, überübernächster_plenumtermin, &config).await.expect("Fehler! Plenumspad konnte nicht generiert werden!");
|
// MUSS WIEDER REIN NACH DEM TESTEN: 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("hedgedoc-last-id"), &config.get("hedgedoc-next-id"));
|
||||||
|
|
||||||
let old_pad_content_without_top_instructions = try_to_remove_top_instructions(old_pad_content);
|
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 tldr_vec = create_tldr(&old_pad_content_without_top_instructions);
|
||||||
let mut tldr = String::new();
|
let tldr = tldr_vec.join("\n");
|
||||||
for element in tldr_vec {
|
// XXX nächstes/übernächstes?
|
||||||
tldr.push_str("\n");
|
let message: String = format!(r#"{email_greeting}
|
||||||
tldr.push_str(&element)
|
|
||||||
}
|
Anbei das gestrige Plenumspad. Hier sind die Links zum nächsten:
|
||||||
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());
|
{current_pad_link}
|
||||||
|
und zum übernächsten Plenum:
|
||||||
|
{future_pad_link}
|
||||||
|
|
||||||
|
TL;DR:
|
||||||
|
{tldr}
|
||||||
|
|
||||||
|
Und hier ist das Protokoll des letzten Plenums:
|
||||||
|
|
||||||
|
{old_pad_content_without_top_instructions}
|
||||||
|
|
||||||
|
{email_signature}"#);
|
||||||
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!");
|
// XXX option x expect
|
||||||
pad_ins_wiki(old_pad_content_without_top_instructions);
|
message_id = Some(mail_versenden(&config, betreff, message).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]\nAktueller Zustand der DB:");
|
println!("[ENDE]\nAktueller Zustand der DB:");
|
||||||
config.dump_redacting(&["email-pass","matrix-pass"]).ok();
|
config.dump_redacting(&["email-password","matrix-password","wiki-password"]).ok();
|
||||||
|
|
||||||
if config.has_errors() { Err("There were errors.".into()) } else { Ok(()) }
|
if config.has_errors() { Err("There were errors.".into()) } else { Ok(()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async fn download_and_return_pad(pad_link: String) -> Result<String, Box<dyn std::error::Error>> {
|
|
||||||
//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 get_tuesdays(month_offset: i32) -> Vec<NaiveDate>{
|
fn get_tuesdays(month_offset: i32) -> Vec<NaiveDate>{
|
||||||
let date = Local::now();
|
let date = Local::now().date_naive();
|
||||||
let date = match month_offset.signum() {
|
let date = match month_offset.signum() {
|
||||||
0 => Some(date),
|
0 => Some(date),
|
||||||
1 => date.checked_add_months(Months::new(month_offset as u32)),
|
1 => date.checked_add_months(chrono::Months::new(month_offset as u32)),
|
||||||
-1 => date.checked_sub_months(Months::new((-month_offset) as u32)),
|
-1 => date.checked_sub_months(chrono::Months::new((-month_offset) as u32)),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}.expect("(very) invalid month offset");
|
}.expect("(very) invalid month offset");
|
||||||
let year = date.year();
|
|
||||||
let month = date.month();
|
let month = date.month();
|
||||||
let mut current_date = NaiveDate::from_ymd_opt(year, month, 1);
|
let mut current_date = NaiveDate::from_ymd_opt(date.year(), date.month(), 1);
|
||||||
let mut dates = Vec::new();
|
let mut dates = Vec::new();
|
||||||
while let Some(date) = current_date {
|
while let Some(date) = current_date {
|
||||||
if date.month() == month && date.weekday() == Weekday::Tue {
|
if date.month() == month && date.weekday() == Weekday::Tue {
|
||||||
|
@ -290,15 +312,15 @@ fn create_tldr (pad_content: &String) -> Vec<&str> {
|
||||||
//tldr_vec.append() = m.iter()
|
//tldr_vec.append() = m.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mail_versenden(inhalt: String, betreff: String) -> std::result::Result<String, Box<dyn std::error::Error>> {
|
fn mail_versenden(config: &KV, betreff: String, inhalt: String) -> std::result::Result<String, Box<dyn std::error::Error>> {
|
||||||
// Define the email
|
// Define the email
|
||||||
let message_id: String = Uuid::new_v4().to_string() + &String::from("@berlin.ccc.de");
|
let message_id: String = Uuid::new_v4().to_string() + &String::from("@berlin.ccc.de");
|
||||||
|
|
||||||
let mail_to: &str = if TESTING_MODE == EMailOperationStates::Test { "Marek Krug <mail@marekkrug.de>"} else {"CCCB Intern <intern@berlin.ccc.de>"};
|
let mail_to: &str = &config["email-to"];
|
||||||
|
|
||||||
let email = Message::builder()
|
let email = Message::builder()
|
||||||
// Set the sender's name and email address
|
// Set the sender's name and email address
|
||||||
.from("Plenum Bot <plenum-bot@berlin.ccc.de>".parse().unwrap())
|
.from(config["email-sender"].parse().unwrap())
|
||||||
// Set the recipient's name and email address
|
// Set the recipient's name and email address
|
||||||
.to(mail_to.parse().unwrap())
|
.to(mail_to.parse().unwrap())
|
||||||
.message_id(Option::from(message_id.clone()))
|
.message_id(Option::from(message_id.clone()))
|
||||||
|
@ -309,11 +331,13 @@ fn mail_versenden(inhalt: String, betreff: String) -> std::result::Result<String
|
||||||
.header(header::ContentType::TEXT_PLAIN)
|
.header(header::ContentType::TEXT_PLAIN)
|
||||||
.body(inhalt))
|
.body(inhalt))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
if !is_dry_run() {
|
||||||
// Set up the SMTP client
|
// Set up the SMTP client
|
||||||
let creds = Credentials::new("plenum-bot@berlin.ccc.de".to_string(), "m9yBn16pUtbC".to_string());
|
let creds = Credentials::new(config["email-user"].to_string(), config["email-password"].to_string());
|
||||||
// Open a remote connection to gmail
|
// Open a remote connection to gmail
|
||||||
|
|
||||||
let mailer = SmtpTransport::starttls_relay("mail.berlin.ccc.de")?
|
let mailer = SmtpTransport::starttls_relay(&config["email-server"])?
|
||||||
.credentials(creds)
|
.credentials(creds)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
@ -322,30 +346,38 @@ fn mail_versenden(inhalt: String, betreff: String) -> std::result::Result<String
|
||||||
Ok(_) => println!("Email sent successfully!"),
|
Ok(_) => println!("Email sent successfully!"),
|
||||||
Err(e) => eprintln!("Could not send email: {:?}", e), }
|
Err(e) => eprintln!("Could not send email: {:?}", e), }
|
||||||
Ok(message_id)
|
Ok(message_id)
|
||||||
|
} else {
|
||||||
|
println!( "[DRY RUN - NOT] sending email\n(raw message:)\n{}", std::str::from_utf8(&email.formatted()).unwrap_or("((UTF-8 error))") );
|
||||||
|
Ok("dummy-message-id@localhost".to_string())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn generate_new_pad_for_following_date(übernächster_plenumtermin: &String, überübernächster_plenumtermin: &String, kv: &KV) -> Result<(), Box<dyn Error>> {
|
async fn generate_new_pad_for_following_date(config : KV, hedgedoc : HedgeDoc, übernächster_plenumtermin: &String, überübernächster_plenumtermin: &String, kv: &KV) -> Result<(), Box<dyn Error>> {
|
||||||
let client = Client::new();
|
|
||||||
|
|
||||||
match create_new_pads::create_pad(&client, HEDGEDOC_SERVER_URL).await {
|
match hedgedoc.create_pad( ).await {
|
||||||
Ok(pad_url) => {
|
Err(e) => println!("Failed to create pad: {}", e),
|
||||||
println!("Pad created successfully at URL: {}", pad_url);
|
Ok(pad_id) => {
|
||||||
|
println!("Pad created successfully with ID: {}", pad_id);
|
||||||
|
|
||||||
// Get the most recent plenum template and replace the placeholders:
|
// Get the most recent plenum template and replace the placeholders:
|
||||||
let template_from_pad = download_and_return_pad(PLENUM_TEMPLATE_URL.to_string()).await; // Download Pad
|
let template_content = match config.get("hedgedoc-template-name") {
|
||||||
let template_content: String = template_from_pad.unwrap_or_else(|error| FALLBACK_TEMPLATE.to_string()); // If Download wasn't successful, use offline Template
|
Ok(str) => match hedgedoc.download(&str).await {
|
||||||
|
Ok(content) => content,
|
||||||
|
Err(_) => FALLBACK_TEMPLATE.to_string(),
|
||||||
|
}
|
||||||
|
Err(_) => FALLBACK_TEMPLATE.to_string(),
|
||||||
|
};
|
||||||
|
// XXX you don't just use the template as-is…
|
||||||
let template_modified: String = replace_placeholders(&template_content, übernächster_plenumtermin, überübernächster_plenumtermin).unwrap_or_else(|error |template_content); // Try regex, if not successful use without regex
|
let template_modified: String = replace_placeholders(&template_content, übernächster_plenumtermin, überübernächster_plenumtermin).unwrap_or_else(|error |template_content); // Try regex, if not successful use without regex
|
||||||
|
|
||||||
let future_pad_id: &str = pad_url.trim_start_matches(&format!("{}/", HEDGEDOC_SERVER_URL));
|
match hedgedoc.import_note( Some(&pad_id), template_modified ).await {
|
||||||
match create_new_pads::import_note(&client, template_modified, Some(future_pad_id), HEDGEDOC_SERVER_URL).await {
|
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
println!("Pad updated successfully with template content.");
|
println!("Pad updated successfully with template content.");
|
||||||
rotate (future_pad_id, kv);
|
rotate (&pad_id, kv);
|
||||||
},
|
},
|
||||||
Err(e) => println!("Failed to update pad: {}", e),
|
Err(e) => println!("Failed to update pad: {}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => println!("Failed to create pad: {}", e),
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -373,28 +405,3 @@ fn try_to_remove_top_instructions (pad_content: String) -> String {
|
||||||
let result: Cow<str> = re_top_instructions.replace_all(&pad_content, "---");
|
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
|
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) {
|
|
||||||
//Convert Markdown into Mediawiki
|
|
||||||
// Vanilla pandoc Befehl: pandoc --from markdown --to mediawiki --no-highlight
|
|
||||||
let mut p = pandoc::new();
|
|
||||||
p.set_input(pandoc::InputKind::Pipe(old_pad_content));
|
|
||||||
p.set_input_format(pandoc::InputFormat::Markdown, vec![]);
|
|
||||||
// p.set_output_format(Pandoc::OutputFormat::mediawiki, vec![MarkdownExtension::Smart]);
|
|
||||||
p.set_output(pandoc::OutputKind::File("./pandoc-output.txt".parse().unwrap()));
|
|
||||||
p.set_output_format(pandoc::OutputFormat::MediaWiki, vec![]);
|
|
||||||
p.execute().expect("Fehler beim Umwandeln des und speichern des Pads in eine mediawiki-Textdatei");
|
|
||||||
|
|
||||||
// Textdatei wieder einlesen
|
|
||||||
let mut file = File::open("pandoc-output.txt").expect("Fehler beim öffnen der MediaWiki-Textdatei!");
|
|
||||||
let mut contents = String::new();
|
|
||||||
file.read_to_string(&mut contents).expect("Fehler beim auslesen der MediaWiki-Textdatei!");
|
|
||||||
|
|
||||||
// Passwörter aus Datenbank lesen (ToBeDone)
|
|
||||||
let plenum_bot_user = String::from("PlenumBot@PlenumBot-PW1");
|
|
||||||
let plenum_bot_pw = String::from("**OLD_API_PW_REMOVED**");
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
25
src/mediawiki.rs
Normal file
25
src/mediawiki.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
use pandoc::{MarkdownExtension, Pandoc, PandocError, PandocOutput};
|
||||||
|
use std::io::prelude::*;
|
||||||
|
use std::fs::File;
|
||||||
|
|
||||||
|
pub fn pad_ins_wiki(old_pad_content: String) {
|
||||||
|
//Convert Markdown into Mediawiki
|
||||||
|
// Vanilla pandoc Befehl: pandoc --from markdown --to mediawiki --no-highlight
|
||||||
|
let mut p = pandoc::new();
|
||||||
|
p.set_input(pandoc::InputKind::Pipe(old_pad_content));
|
||||||
|
p.set_input_format(pandoc::InputFormat::Markdown, vec![]);
|
||||||
|
// p.set_output_format(Pandoc::OutputFormat::mediawiki, vec![MarkdownExtension::Smart]);
|
||||||
|
p.set_output(pandoc::OutputKind::File("./pandoc-output.txt".parse().unwrap()));
|
||||||
|
p.set_output_format(pandoc::OutputFormat::MediaWiki, vec![]);
|
||||||
|
p.execute().expect("Fehler beim Umwandeln des und speichern des Pads in eine mediawiki-Textdatei");
|
||||||
|
|
||||||
|
// Textdatei wieder einlesen
|
||||||
|
let mut file = File::open("pandoc-output.txt").expect("Fehler beim öffnen der MediaWiki-Textdatei!");
|
||||||
|
let mut contents = String::new();
|
||||||
|
file.read_to_string(&mut contents).expect("Fehler beim auslesen der MediaWiki-Textdatei!");
|
||||||
|
|
||||||
|
// Passwörter aus Datenbank lesen (ToBeDone)
|
||||||
|
let plenum_bot_user = String::from("PlenumBot@PlenumBot-PW1");
|
||||||
|
let plenum_bot_pw = String::from("**OLD_API_PW_REMOVED**");
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue