mit cargo fmt alles formatiert

This commit is contained in:
murmeldin 2024-08-02 22:29:22 +02:00 committed by murmeldin
parent c026a38239
commit c8031dfc01
5 changed files with 256 additions and 154 deletions

View file

@ -3,14 +3,14 @@ use std::error::Error;
use std::io::{self, Write}; use std::io::{self, Write};
/// Text shown for an empty field. /// Text shown for an empty field.
const EMPTY_VALUE : &str = "(empty)"; const EMPTY_VALUE: &str = "(empty)";
/// Text shown in place of a password. /// Text shown in place of a password.
const HIDDEN_PASSWORD : &str = "*****"; const HIDDEN_PASSWORD: &str = "*****";
// highlight some of the info // highlight some of the info
const ANSI_GROUP: &str = "\x1b[1;31m"; const ANSI_GROUP: &str = "\x1b[1;31m";
const ANSI_FIELD: &str = "\x1b[1;33m"; const ANSI_FIELD: &str = "\x1b[1;33m";
const ANSI_NOTICE: &str= "\x1b[33m"; const ANSI_NOTICE: &str = "\x1b[33m";
const ANSI_RESET: &str = "\x1b[0m"; const ANSI_RESET: &str = "\x1b[0m";
const CONFIG_SPEC: CfgSpec<'static> = CfgSpec { const CONFIG_SPEC: CfgSpec<'static> = CfgSpec {
@ -70,7 +70,7 @@ pub fn populate_defaults(config: &KV) {
CfgField::Default { default, .. } => { CfgField::Default { default, .. } => {
config.default(&field.full_key(group.name), default) config.default(&field.full_key(group.name), default)
}, },
_ => {} _ => {},
} }
} }
} }
@ -80,33 +80,47 @@ pub fn populate_defaults(config: &KV) {
/// ///
/// Will report if the config is fine or has missing values. /// Will report if the config is fine or has missing values.
pub fn interactive_check(config: KV) -> Result<(), Box<dyn Error>> { pub fn interactive_check(config: KV) -> Result<(), Box<dyn Error>> {
let mut all_valid : bool = true; let mut all_valid: bool = true;
for group in CONFIG_SPEC.groups { for group in CONFIG_SPEC.groups {
group_header( group.name, group.description ); group_header(group.name, group.description);
let todo = needs_info(&config, group); let todo = needs_info(&config, group);
// TODO: add distinction between edit all / edit necessary only // TODO: add distinction between edit all / edit necessary only
let choices = if !todo.is_empty() { let choices = if !todo.is_empty() {
println!( "{}The following fields need adjustment: {}{}", ANSI_NOTICE, todo.join(", "), ANSI_RESET ); println!(
"{}The following fields need adjustment: {}{}",
ANSI_NOTICE,
todo.join(", "),
ANSI_RESET
);
&["[E]dit (default)", "[L]ist all", "[S]kip group"] &["[E]dit (default)", "[L]ist all", "[S]kip group"]
} else { } else {
println!( "{}This group looks fine. OK to [S]kip, unless you want to adjust values here.{}", ANSI_NOTICE, ANSI_RESET ); println!(
&["[S]kip group (default)", "[E]dit", "[L]ist all", ] "{}This group looks fine. OK to [S]kip, unless you want to adjust values here.{}",
ANSI_NOTICE, ANSI_RESET
);
&["[S]kip group (default)", "[E]dit", "[L]ist all"]
}; };
loop { loop {
let choice = prompt_action( choices ); let choice = prompt_action(choices);
match choice { match choice {
'L' => { show_group(&config, group); }, 'L' => {
show_group(&config, group);
},
'E' => { 'E' => {
for field in group.fields { for field in group.fields {
check_field( &config, group.name, field, &mut all_valid )?; check_field(&config, group.name, field, &mut all_valid)?;
} }
break; break;
}, },
'S' => { 'S' => {
if !todo.is_empty() { all_valid = false; } if !todo.is_empty() {
all_valid = false;
}
break; break;
}, },
_ => { unreachable!(); }, // (prompt already checks) _ => {
unreachable!();
}, // (prompt already checks)
} }
} }
} }
@ -117,30 +131,49 @@ pub fn interactive_check(config: KV) -> Result<(), Box<dyn Error>> {
} }
} }
fn check_field( config: &KV, grpname: &str, field: &CfgField, ok: &mut bool ) -> Result<(), Box<dyn Error>> { fn check_field(
if field.is_silent() { return Ok(()) } config: &KV, grpname: &str, field: &CfgField, ok: &mut bool,
) -> Result<(), Box<dyn Error>> {
if field.is_silent() {
return Ok(());
}
show_field(config, grpname, field); show_field(config, grpname, field);
let key = field.full_key(grpname); let key = field.full_key(grpname);
let value = config.get(&key).ok(); let value = config.get(&key).ok();
let mut actions: Vec<&'static str> = Vec::new(); let mut actions: Vec<&'static str> = Vec::new();
// TODO: adjust order: empty password should offer input first // TODO: adjust order: empty password should offer input first
// TODO: RandomId should offer generating as [R]egenerate // TODO: RandomId should offer generating as [R]egenerate
if value.is_some() | field.is_password() | field.is_optional() { actions.push("[K]eep as-is"); } if value.is_some() | field.is_password() | field.is_optional() {
if field.default_description().is_some() { actions.push("[R]eset to default"); } actions.push("[K]eep as-is");
if field.is_password() { actions.push( "[R]emove" ); } }
actions.push( "[I]nput new value" ); if field.default_description().is_some() {
if !field.is_password() { actions.push( "[L]ong (multiline) input" ) }; actions.push("[R]eset to default");
}
if field.is_password() {
actions.push("[R]emove");
}
actions.push("[I]nput new value");
if !field.is_password() {
actions.push("[L]ong (multiline) input")
};
match prompt_action(&actions) { match prompt_action(&actions) {
'K' => { 'K' => {
// we allow leaving a password empty, but that means config is incomplete // we allow leaving a password empty, but that means config is incomplete
if value.is_none() & !field.is_optional() { *ok &= false; } if value.is_none() & !field.is_optional() {
*ok &= false;
}
}, },
'R' => { 'R' => {
match field.default_value()? { match field.default_value()? {
Some(value) => { config.set(&key, &value)?; }, Some(value) => {
config.set(&key, &value)?;
},
// password again // password again
None => { config.delete(&key)?; *ok &= false; }, None => {
config.delete(&key)?;
*ok &= false;
},
} }
}, },
'I' => { 'I' => {
@ -151,43 +184,49 @@ fn check_field( config: &KV, grpname: &str, field: &CfgField, ok: &mut bool ) ->
let value = prompt_multiline(); let value = prompt_multiline();
config.set(&key, &value)?; config.set(&key, &value)?;
}, },
_ => { return Err("Wat.".into()); } _ => {
return Err("Wat.".into());
},
} }
Ok(()) Ok(())
} }
/* ***** displaying various kinds of info ***** */ /* ***** displaying various kinds of info ***** */
fn group_header( name: &str, description: &str ) { fn group_header(name: &str, description: &str) {
println!("=============================="); println!("==============================");
println!("{}{}{} - {}", ANSI_GROUP, name, ANSI_RESET, description); println!("{}{}{} - {}", ANSI_GROUP, name, ANSI_RESET, description);
println!(""); println!("");
} }
fn show_group( config: &KV, group: &CfgGroup ) { fn show_group(config: &KV, group: &CfgGroup) {
for field in group.fields { for field in group.fields {
show_field( &config, group.name, field ); show_field(&config, group.name, field);
} }
} }
fn show_field( config: &KV, grpname: &str, field: &CfgField ) { fn show_field(config: &KV, grpname: &str, field: &CfgField) {
if field.is_silent() { return } if field.is_silent() {
return;
}
let key = field.full_key(grpname); let key = field.full_key(grpname);
println!( "{}{}{} - {}", ANSI_FIELD, &key, ANSI_RESET, field.description() ); println!("{}{}{} - {}", ANSI_FIELD, &key, ANSI_RESET, field.description());
println!( " default: {}", field.default_description().unwrap_or(EMPTY_VALUE.to_string())); println!(" default: {}", field.default_description().unwrap_or(EMPTY_VALUE.to_string()));
let value = config.get(&key).ok() let value = config
.get(&key)
.ok()
.map(|s| if field.is_password() { HIDDEN_PASSWORD.to_string() } else { s }) .map(|s| if field.is_password() { HIDDEN_PASSWORD.to_string() } else { s })
.unwrap_or(EMPTY_VALUE.to_string()); .unwrap_or(EMPTY_VALUE.to_string());
println!( " current: {}", value ); println!(" current: {}", value);
} }
/* ***** basic validation ***** */ /* ***** basic validation ***** */
fn needs_info( config: &KV, group: &CfgGroup ) -> Vec<String> { fn needs_info(config: &KV, group: &CfgGroup) -> Vec<String> {
let mut acc = Vec::new(); let mut acc = Vec::new();
for field in group.fields { for field in group.fields {
if field.is_action_required(config, group.name) { if field.is_action_required(config, group.name) {
acc.push( field.key() ); acc.push(field.key());
} }
} }
acc acc
@ -197,12 +236,12 @@ fn needs_info( config: &KV, group: &CfgGroup ) -> Vec<String> {
/// Ask for a choice between several options. First option is the default, /// Ask for a choice between several options. First option is the default,
/// format options with the first character in brackets like so: `"[E]xample"`. /// format options with the first character in brackets like so: `"[E]xample"`.
fn prompt_action( choices: &[&str] ) -> char { fn prompt_action(choices: &[&str]) -> char {
let prompt_message = choices.join(", "); let prompt_message = choices.join(", ");
let default_choice = choices[0].to_uppercase().chars().nth(1).unwrap_or_default(); let default_choice = choices[0].to_uppercase().chars().nth(1).unwrap_or_default();
loop { loop {
println!( "{}", prompt_message ); println!("{}", prompt_message);
print!( "Select: " ); print!("Select: ");
io::stdout().flush().ok(); io::stdout().flush().ok();
// Read line and take first non-space character // Read line and take first non-space character
@ -211,7 +250,10 @@ fn prompt_action( choices: &[&str] ) -> char {
let input = input.trim().to_uppercase().chars().next().unwrap_or(default_choice); let input = input.trim().to_uppercase().chars().next().unwrap_or(default_choice);
// Check list of choices for match // Check list of choices for match
if choices.iter().any(|&choice| choice.to_uppercase().chars().nth(1).unwrap_or_default() == input) { if choices
.iter()
.any(|&choice| choice.to_uppercase().chars().nth(1).unwrap_or_default() == input)
{
return input; return input;
} else { } else {
println!("Invalid choice. Try again!"); println!("Invalid choice. Try again!");
@ -220,7 +262,7 @@ fn prompt_action( choices: &[&str] ) -> char {
} }
/// Read a single line of text. /// Read a single line of text.
fn prompt_single_line( ) -> String { fn prompt_single_line() -> String {
print!("New value: "); print!("New value: ");
io::stdout().flush().ok(); io::stdout().flush().ok();
let mut input = String::new(); let mut input = String::new();
@ -229,7 +271,7 @@ fn prompt_single_line( ) -> String {
} }
/// Read multiple lines of text, terminated by a line containing just a '.'. /// Read multiple lines of text, terminated by a line containing just a '.'.
fn prompt_multiline( ) -> String { fn prompt_multiline() -> String {
println!("Enter new value: (end with '.' on a new line)"); println!("Enter new value: (end with '.' on a new line)");
let mut acc = String::new(); let mut acc = String::new();
loop { loop {
@ -244,7 +286,7 @@ fn prompt_multiline( ) -> String {
} }
/// Read a password without echoing it. /// Read a password without echoing it.
fn prompt_password( ) -> String { fn prompt_password() -> String {
let pass = rpassword::prompt_password("New password (not shown) : ").unwrap(); let pass = rpassword::prompt_password("New password (not shown) : ").unwrap();
// disabled echo means the newline also isn't shown // disabled echo means the newline also isn't shown
println!(""); println!("");
@ -264,7 +306,12 @@ enum CfgField<'a> {
/// Empty by default, required, will prompt without echo. /// Empty by default, required, will prompt without echo.
Password { key: &'a str, description: &'a str }, Password { key: &'a str, description: &'a str },
/// Empty by default, can be user-provided, or will be generated randomly. /// Empty by default, can be user-provided, or will be generated randomly.
RandomId { key: &'a str, generator: fn() -> Result<String,Box<dyn Error>>, generator_description: &'a str, description: &'a str }, RandomId {
key: &'a str,
generator: fn() -> Result<String, Box<dyn Error>>,
generator_description: &'a str,
description: &'a str,
},
} }
/// A group of related config fields. The final key of an inner value will be /// A group of related config fields. The final key of an inner value will be
@ -285,17 +332,17 @@ struct CfgSpec<'a> {
impl<'a> CfgField<'a> { impl<'a> CfgField<'a> {
/// Silent fields don't get prompted or shown. /// Silent fields don't get prompted or shown.
fn is_silent(&self) -> bool { fn is_silent(&self) -> bool {
matches!( self, CfgField::Silent { .. } ) matches!(self, CfgField::Silent { .. })
} }
/// Password fields will be censored when displayed. /// Password fields will be censored when displayed.
fn is_password(&self) -> bool { fn is_password(&self) -> bool {
matches!( self, CfgField::Password { .. } ) matches!(self, CfgField::Password { .. })
} }
/// Optional fields are allowed to be (and stay) empty. /// Optional fields are allowed to be (and stay) empty.
fn is_optional(&self) -> bool { fn is_optional(&self) -> bool {
matches!( self, CfgField::Optional { .. } ) matches!(self, CfgField::Optional { .. })
} }
/// Reports if the field needs changing or is ok as-is. /// Reports if the field needs changing or is ok as-is.
@ -304,7 +351,10 @@ impl<'a> CfgField<'a> {
/// *necessarily* always have a value. Optional is optional. Only /// *necessarily* always have a value. Optional is optional. Only
/// Password and RandomId might need actions if they are missing.) /// Password and RandomId might need actions if they are missing.)
fn is_action_required(&self, config: &KV, grpname: &str) -> bool { fn is_action_required(&self, config: &KV, grpname: &str) -> bool {
if matches!( self, CfgField::Silent { .. } | CfgField::Default { .. } | CfgField::Optional { .. } ) { if matches!(
self,
CfgField::Silent { .. } | CfgField::Default { .. } | CfgField::Optional { .. }
) {
false false
} else { } else {
config.get(self.full_key(grpname).as_str()).ok().is_none() config.get(self.full_key(grpname).as_str()).ok().is_none()
@ -319,7 +369,8 @@ impl<'a> CfgField<'a> {
CfgField::Optional { key, .. } => key, CfgField::Optional { key, .. } => key,
CfgField::Password { key, .. } => key, CfgField::Password { key, .. } => key,
CfgField::RandomId { key, .. } => key, CfgField::RandomId { key, .. } => key,
}.to_string() }
.to_string()
} }
/// Full field name / key used in the DB (currently `{grpname}-{fieldname}`.) /// Full field name / key used in the DB (currently `{grpname}-{fieldname}`.)
@ -335,7 +386,8 @@ impl<'a> CfgField<'a> {
CfgField::Optional { description, .. } => description, CfgField::Optional { description, .. } => description,
CfgField::Password { description, .. } => description, CfgField::Password { description, .. } => description,
CfgField::RandomId { description, .. } => description, CfgField::RandomId { description, .. } => description,
}.to_string() }
.to_string()
} }
/// Gets a description of the default value if one exists. /// Gets a description of the default value if one exists.
@ -344,7 +396,9 @@ impl<'a> CfgField<'a> {
match self { match self {
CfgField::Silent { default, .. } => Some(default.to_string()), CfgField::Silent { default, .. } => Some(default.to_string()),
CfgField::Default { default, .. } => Some(default.to_string()), CfgField::Default { default, .. } => Some(default.to_string()),
CfgField::RandomId { generator_description, .. } => Some(format!("{}{}{}",'(',generator_description,')')), CfgField::RandomId { generator_description, .. } => {
Some(format!("{}{}{}", '(', generator_description, ')'))
},
_ => None, _ => None,
} }
} }

View file

@ -1,63 +1,61 @@
use reqwest::blocking::Response;
use reqwest::blocking::Client; use reqwest::blocking::Client;
use reqwest::blocking::Response;
use std::error::Error; use std::error::Error;
// TODO: implement dry-run logic // TODO: implement dry-run logic
pub struct HedgeDoc { pub struct HedgeDoc {
server_url : String, server_url: String,
is_dry_run : bool, is_dry_run: bool,
client : Client, client: Client,
} }
impl HedgeDoc { impl HedgeDoc {
pub fn new( server_url: &str, is_dry_run: bool ) -> Self { pub fn new(server_url: &str, is_dry_run: bool) -> Self {
Self { server_url: server_url.to_string(), is_dry_run, client: Client::new() } Self { server_url: server_url.to_string(), is_dry_run, client: Client::new() }
} }
pub fn format_url( &self, pad_name: &str ) -> String { pub fn format_url(&self, pad_name: &str) -> String {
format!( "{}/{}", self.server_url, pad_name ) format!("{}/{}", self.server_url, pad_name)
} }
fn format_action( &self, pad_name: &str, verb: &str ) -> String { fn format_action(&self, pad_name: &str, verb: &str) -> String {
format!( "{}/{}/{}", self.server_url, pad_name, verb ) format!("{}/{}/{}", self.server_url, pad_name, verb)
} }
fn do_request(&self, url : &str ) -> Result<Response, Box<dyn Error>> { fn do_request(&self, url: &str) -> Result<Response, Box<dyn Error>> {
Ok(self.client.get( url ).send().unwrap()) Ok(self.client.get(url).send().unwrap())
} }
fn get_id_from_response( &self, res : Response ) -> String { fn get_id_from_response(&self, res: Response) -> String {
res.url().to_string().trim_start_matches( &format!( "{}/", self.server_url ) ).to_string() res.url().to_string().trim_start_matches(&format!("{}/", self.server_url)).to_string()
} }
pub fn download( &self, pad_name: &str ) -> Result<String, Box<dyn Error>> { pub fn download(&self, pad_name: &str) -> Result<String, Box<dyn Error>> {
Ok(self.do_request( &self.format_action(pad_name, "download"))?.text()?) Ok(self.do_request(&self.format_action(pad_name, "download"))?.text()?)
} }
pub fn create_pad( &self ) -> Result<String, Box<dyn Error>> { pub fn create_pad(&self) -> Result<String, Box<dyn Error>> {
let res = self.do_request( &format!( "{}/new", self.server_url ) ).unwrap(); let res = self.do_request(&format!("{}/new", self.server_url)).unwrap();
if res.status().is_success() { if res.status().is_success() {
Ok(self.get_id_from_response(res)) Ok(self.get_id_from_response(res))
} else { } else {
Err( format!("Failed to create pad {}", res.status()).into() ) Err(format!("Failed to create pad {}", res.status()).into())
} }
} }
pub fn import_note( &self, id: Option<&str>, content: String ) -> Result<String, Box<dyn Error>> { pub fn import_note(&self, id: Option<&str>, content: String) -> Result<String, Box<dyn Error>> {
let url = match id { let url = match id {
Some(id) => self.format_url( &format!( "new/{id}" ) ), Some(id) => self.format_url(&format!("new/{id}")),
None => self.format_url("new"), None => self.format_url("new"),
}; };
let res = self.client.post(&url) let res =
.header( "Content-Type", "text/markdown" ) self.client.post(&url).header("Content-Type", "text/markdown").body(content).send()?;
.body(content)
.send()?;
if res.status().is_success() { if res.status().is_success() {
Ok(self.get_id_from_response(res)) Ok(self.get_id_from_response(res))
} else { } else {
Err( format!("Failed to import note: {}", res.status()).into() ) Err(format!("Failed to import note: {}", res.status()).into())
} }
} }
} }

View file

@ -32,12 +32,11 @@ mod mediawiki;
mod config_check; mod config_check;
pub mod variables_and_settings; pub mod variables_and_settings;
use std::borrow::Cow; use std::borrow::Cow;
use std::error::Error;
use std::env; use std::env;
use std::error::Error;
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
// use std::future::Future; // use std::future::Future;
@ -47,9 +46,9 @@ use clap::{Arg, Command};
use regex::Regex; use regex::Regex;
use uuid::Uuid; use uuid::Uuid;
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;
use lettre::{Message, SmtpTransport, Transport};
const FALLBACK_TEMPLATE: &str = variables_and_settings::FALLBACK_TEMPLATE; const FALLBACK_TEMPLATE: &str = variables_and_settings::FALLBACK_TEMPLATE;
@ -86,7 +85,8 @@ fn parse_args() -> Args {
.action(clap::ArgAction::Set) .action(clap::ArgAction::Set)
.default_value("config.sqlite") .default_value("config.sqlite")
.help("specifies an alternate config file"), .help("specifies an alternate config file"),
).get_matches(); )
.get_matches();
Args { Args {
check_mode: *matches.get_one::<bool>("check").unwrap(), check_mode: *matches.get_one::<bool>("check").unwrap(),
config_file: matches.get_one::<String>("config_file").unwrap().clone(), config_file: matches.get_one::<String>("config_file").unwrap().clone(),
@ -102,12 +102,12 @@ fn main() -> Result<(), Box<dyn Error>> {
let config = KV::new(config_file).unwrap(); let config = KV::new(config_file).unwrap();
config_check::populate_defaults(&config); config_check::populate_defaults(&config);
if args.check_mode { if args.check_mode {
return config_check::interactive_check( config ); return config_check::interactive_check(config);
} }
// config // config
let hedgedoc = HedgeDoc::new( &config["hedgedoc-server-url"], is_dry_run() ); 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();
// Dienstage diesen Monat // Dienstage diesen Monat
let all_tuesdays: Vec<NaiveDate> = get_all_weekdays(0, Weekday::Tue); let all_tuesdays: Vec<NaiveDate> = get_all_weekdays(0, Weekday::Tue);
@ -120,7 +120,7 @@ fn main() -> Result<(), Box<dyn Error>> {
let vierter_dienstag_nächster_monat: String = all_tuesdays_next_month[3].to_string(); let vierter_dienstag_nächster_monat: String = all_tuesdays_next_month[3].to_string();
// Daten, die später benutzt werden, definieren // Daten, die später benutzt werden, definieren
let today= Local::now(); let today = Local::now();
let today_simple = today.date_naive(); let today_simple = today.date_naive();
let yesterday = today_simple.pred_opt().unwrap(); let yesterday = today_simple.pred_opt().unwrap();
let in_1_day = today_simple.succ_opt().unwrap(); let in_1_day = today_simple.succ_opt().unwrap();
@ -129,23 +129,25 @@ fn main() -> Result<(), Box<dyn Error>> {
// Nächste Plena nachschauen: // Nächste Plena nachschauen:
let nächster_plenumtermin: &String = if all_tuesdays[1] >= yesterday { // Für das Pad rumschicken am nächsten Tag wird das Datum einen Tag nach Hinten gesetzt, let nächster_plenumtermin: &String = if all_tuesdays[1] >= yesterday {
// Für das Pad rumschicken am nächsten Tag wird das Datum einen Tag nach Hinten gesetzt,
&zweiter_dienstag &zweiter_dienstag
} else { } else {
&vierter_dienstag &vierter_dienstag
}; };
let übernächster_plenumtermin = if all_tuesdays[1] >= yesterday { // hier das Gleiche. let übernächster_plenumtermin = if all_tuesdays[1] >= yesterday {
// hier das Gleiche.
&vierter_dienstag &vierter_dienstag
} else { } else {
&zweiter_dienstag_nächster_monat &zweiter_dienstag_nächster_monat
}; };
let überübernächster_plenumtermin = if all_tuesdays[1] >= yesterday { // hier das Gleiche. let überübernächster_plenumtermin = if all_tuesdays[1] >= yesterday {
// hier das Gleiche.
&zweiter_dienstag_nächster_monat &zweiter_dienstag_nächster_monat
} else { } else {
&vierter_dienstag_nächster_monat &vierter_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. // 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(nächster_plenumtermin.clone(), in_1_day); let in_1_day_is_plenum: bool = check_if_plenum(nächster_plenumtermin.clone(), in_1_day);
@ -153,8 +155,12 @@ 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 current_pad_id = config.get("hedgedoc-last-id").expect("ID des aktuellen Pads undefiniert. Bitte in der DB eintragen oder generieren."); let current_pad_id = config
let future_pad_id = config.get("hedgedoc-next-id").expect("ID des nächsten Pads undefiniert. Bitte in der DB eintragen oder generieren."); .get("hedgedoc-last-id")
.expect("ID des aktuellen Pads undefiniert. Bitte in der DB eintragen oder generieren.");
let future_pad_id = config
.get("hedgedoc-next-id")
.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: Option<String> = None;
@ -172,7 +178,7 @@ fn main() -> Result<(), Box<dyn Error>> {
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 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)
@ -180,7 +186,8 @@ fn main() -> Result<(), Box<dyn Error>> {
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 betreff = "Morgen ist Plenum!".to_string(); // ADJ_TIMEYWIMEY let betreff = "Morgen ist Plenum!".to_string(); // ADJ_TIMEYWIMEY
let message: String = format!(r#"{email_greeting} let message: String = format!(
r#"{email_greeting}
Es gibt Themen, deshalb wird das morgige Plenum statt finden. Anbei das Plenumspad: Es gibt Themen, deshalb wird das morgige Plenum statt finden. Anbei das Plenumspad:
{current_pad_link} {current_pad_link}
@ -190,14 +197,16 @@ Und hier ein TL;DR von den aktuellen Themen:
Bis morgen, 20 Uhr! Bis morgen, 20 Uhr!
{email_signature}"#); // ADJ_TIMEYWIMEY {email_signature}"#
); // ADJ_TIMEYWIMEY
println!("---E-Mail:---\n{}\n-----------", message); println!("---E-Mail:---\n{}\n-----------", message);
// XXX option x expect // XXX option x expect
message_id = Some(mail_versenden(&config, betreff, message).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 betreff = format!("Plenum am {} fällt mangels Themen aus", nächster_plenumtermin); let betreff = format!("Plenum am {} fällt mangels Themen aus", nächster_plenumtermin);
let message: String = format!(r#"{email_greeting} let message: String = format!(
r#"{email_greeting}
Es gibt keine Themen, deshalb wird das morgige Plenum leider nicht statt finden. Es gibt keine Themen, deshalb wird das morgige Plenum leider nicht statt finden.
@ -206,7 +215,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.
{email_signature}"#, nächster_plenumtermin); {email_signature}"#,
nächster_plenumtermin
);
println!("---E-Mail:---\n{}\n-----------", message); println!("---E-Mail:---\n{}\n-----------", message);
// XXX option x expect // XXX option x expect
message_id = Some(mail_versenden(&config, betreff, message).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!"))
@ -216,29 +227,38 @@ Bis zum nächsten Plenum.
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 betreff = format!("Plenum vom {}: Bisher noch keine Themen", nächster_plenumtermin); let betreff = format!("Plenum vom {}: Bisher noch keine Themen", nächster_plenumtermin);
let message: String = format!(r#"{email_greeting} 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. 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: Hier ist der Link zum Pad, wo ihr noch Themen eintragen könnt:
{current_pad_link} {current_pad_link}
{email_signature}"#); {email_signature}"#
);
println!("---E-Mail:---\n{}\n-----------", message); println!("---E-Mail:---\n{}\n-----------", message);
// XXX option x expect // 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!")) message_id = Some(mail_versenden(&config, betreff, message).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 = hedgedoc.download(&current_pad_id).expect("Fehler beim Hedgedoc-Pad-Download!"); let old_pad_content =
hedgedoc.download(&current_pad_id).expect("Fehler beim Hedgedoc-Pad-Download!");
// 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("hedgedoc-last-id"), &config.get("hedgedoc-next-id")); 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 tldr = tldr_vec.join("\n"); let tldr = tldr_vec.join("\n");
// XXX nächstes/übernächstes? // XXX nächstes/übernächstes?
let message: String = format!(r#"{email_greeting} let message: String = format!(
r#"{email_greeting}
Anbei das gestrige Plenumspad. Hier sind die Links zum nächsten: Anbei das gestrige Plenumspad. Hier sind die Links zum nächsten:
{current_pad_link} {current_pad_link}
@ -252,8 +272,10 @@ Und hier ist das Protokoll des letzten Plenums:
{old_pad_content_without_top_instructions} {old_pad_content_without_top_instructions}
{email_signature}"#); {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);
// XXX option x expect // XXX option x expect
message_id = Some(mail_versenden(&config, betreff, message).expect("Mail mit Plenumsprotokoll wurde versucht zu senden, konnte aber nicht gesendet werden!")); message_id = Some(mail_versenden(&config, betreff, message).expect("Mail mit Plenumsprotokoll wurde versucht zu senden, konnte aber nicht gesendet werden!"));
@ -262,19 +284,24 @@ Und hier ist das Protokoll des letzten Plenums:
println!("message id: {:?}", message_id); println!("message id: {:?}", message_id);
println!("[ENDE]\nAktueller Zustand der DB:"); println!("[ENDE]\nAktueller Zustand der DB:");
config.dump_redacting(&["email-password","matrix-password","wiki-password"]).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(())
}
} }
fn get_all_weekdays(month_offset: i32, week_day: Weekday) -> Vec<NaiveDate>{ fn get_all_weekdays(month_offset: i32, week_day: Weekday) -> Vec<NaiveDate> {
let date = Local::now().date_naive(); 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(chrono::Months::new(month_offset as u32)), 1 => date.checked_add_months(chrono::Months::new(month_offset as u32)),
-1 => date.checked_sub_months(chrono::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 month = date.month(); let month = date.month();
let mut current_date = NaiveDate::from_ymd_opt(date.year(), date.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();
@ -290,30 +317,28 @@ fn get_all_weekdays(month_offset: i32, week_day: Weekday) -> Vec<NaiveDate>{
fn check_if_plenum(infrage_kommendes_plenum: String, date_to_check: NaiveDate) -> bool { fn check_if_plenum(infrage_kommendes_plenum: String, date_to_check: NaiveDate) -> bool {
// Überprüfen, ob an dem Datum Plenum ist // Überprüfen, ob an dem Datum Plenum ist
let date_to_check = date_to_check.to_string(); let date_to_check = date_to_check.to_string();
return if infrage_kommendes_plenum == date_to_check { return if infrage_kommendes_plenum == date_to_check { true } else { false };
true
} else {
false
}
} }
fn number_of_tops (pad_content: &String) -> i32 { fn number_of_tops(pad_content: &String) -> i32 {
// Logik: Wenn irgendwo TOP 2, top2, Top2 oder TOP2 steht, dann gibt es Themen // Logik: Wenn irgendwo TOP 2, top2, Top2 oder TOP2 steht, dann gibt es Themen
let re = Regex::new(r"^##+ *([Tt][Oo][Pp] +\d[.\d]*.*)$").unwrap(); let re = Regex::new(r"^##+ *([Tt][Oo][Pp] +\d[.\d]*.*)$").unwrap();
let m = re.find(&pad_content); let m = re.find(&pad_content);
m.iter().len() as i32 m.iter().len() as i32
} }
fn create_tldr (pad_content: &String) -> Vec<&str> { fn create_tldr(pad_content: &String) -> Vec<&str> {
// Logik: Wenn irgendwo TOP 2, top2, Top2 oder TOP2 steht, dann gibt es Themen // Logik: Wenn irgendwo TOP 2, top2, Top2 oder TOP2 steht, dann gibt es Themen
let re_top = Regex::new(r"^##+ *([Tt][Oo][Pp] +\d[.\d]*.*)$").unwrap(); let re_top = Regex::new(r"^##+ *([Tt][Oo][Pp] +\d[.\d]*.*)$").unwrap();
let tldr: Vec<&str> = re_top.find_iter(&pad_content).map(|m|m.as_str()).collect(); let tldr: Vec<&str> = re_top.find_iter(&pad_content).map(|m| m.as_str()).collect();
//println!("{:?}", m); //println!("{:?}", m);
tldr tldr
//tldr_vec.append() = m.iter() //tldr_vec.append() = m.iter()
} }
fn mail_versenden(config: &KV, betreff: String, inhalt: 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");
@ -328,59 +353,74 @@ fn mail_versenden(config: &KV, betreff: String, inhalt: String) -> std::result::
.in_reply_to("19de3985-05b4-42f3-9a6a-e941479be2ed@berlin.ccc.de".to_string()) .in_reply_to("19de3985-05b4-42f3-9a6a-e941479be2ed@berlin.ccc.de".to_string())
// Set the subject of the email // Set the subject of the email
.subject(betreff) .subject(betreff)
.singlepart(SinglePart::builder() .singlepart(SinglePart::builder().header(header::ContentType::TEXT_PLAIN).body(inhalt))
.header(header::ContentType::TEXT_PLAIN)
.body(inhalt))
.unwrap(); .unwrap();
if !is_dry_run() { if !is_dry_run() {
// Set up the SMTP client // Set up the SMTP client
let creds = Credentials::new(config["email-user"].to_string(), config["email-password"].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(&config["email-server"])? let mailer =
.credentials(creds) SmtpTransport::starttls_relay(&config["email-server"])?.credentials(creds).build();
.build();
// Send the email // Send the email
match mailer.send(&email) { match mailer.send(&email) {
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 { } else {
println!( "[DRY RUN - NOT] sending email\n(raw message:)\n{}", std::str::from_utf8(&email.formatted()).unwrap_or("((UTF-8 error))") ); 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()) Ok("dummy-message-id@localhost".to_string())
} }
} }
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>> { fn generate_new_pad_for_following_date(
config: KV, hedgedoc: HedgeDoc, übernächster_plenumtermin: &String,
match hedgedoc.create_pad( ) { überübernächster_plenumtermin: &String, kv: &KV,
) -> Result<(), Box<dyn Error>> {
match hedgedoc.create_pad() {
Err(e) => println!("Failed to create pad: {}", e), Err(e) => println!("Failed to create pad: {}", e),
Ok(pad_id) => { Ok(pad_id) => {
println!("Pad created successfully with ID: {}", 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_content: String = match config.get("hedgedoc-template-name") { let template_content: String = match config.get("hedgedoc-template-name") {
Ok(content) => hedgedoc.download(&content.clone()).unwrap_or_else(|_| FALLBACK_TEMPLATE.to_string()), Ok(content) => hedgedoc
.download(&content.clone())
.unwrap_or_else(|_| FALLBACK_TEMPLATE.to_string()),
Err(_) => FALLBACK_TEMPLATE.to_string(), Err(_) => FALLBACK_TEMPLATE.to_string(),
}; };
// XXX you don't just use the template as-is… // 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
match hedgedoc.import_note( Some(&pad_id), template_modified ) { match hedgedoc.import_note(Some(&pad_id), template_modified) {
Ok(_) => { Ok(_) => {
println!("Pad updated successfully with template content."); println!("Pad updated successfully with template content.");
rotate (&pad_id, kv); rotate(&pad_id, kv);
}, },
Err(e) => println!("Failed to update pad: {}", e), Err(e) => println!("Failed to update pad: {}", e),
} }
} },
} }
Ok(()) Ok(())
} }
fn replace_placeholders(template: &str, übernächster_plenumtermin: &str, überübernächster_plenumtermin: &str) -> Result<String, Box<dyn Error>> { fn replace_placeholders(
template: &str, übernächster_plenumtermin: &str, überübernächster_plenumtermin: &str,
) -> Result<String, Box<dyn Error>> {
let re_datum = Regex::new(r"\{\{Datum\}\}")?; let re_datum = Regex::new(r"\{\{Datum\}\}")?;
let result = re_datum.replace_all(template, übernächster_plenumtermin); let result = re_datum.replace_all(template, übernächster_plenumtermin);
let re_naechstes_plenum = Regex::new(r"\{\{naechstes-plenum\}\}")?; let re_naechstes_plenum = Regex::new(r"\{\{naechstes-plenum\}\}")?;
@ -388,18 +428,23 @@ fn replace_placeholders(template: &str, übernächster_plenumtermin: &str, über
Ok(result.to_string()) Ok(result.to_string())
} }
fn rotate (future_pad_id: &str, kv: &KV) { fn rotate(future_pad_id: &str, kv: &KV) {
let next_plenum_pad = kv.get("zukünftiges-plenumspad").ok(); let next_plenum_pad = kv.get("zukünftiges-plenumspad").ok();
if next_plenum_pad == None { if next_plenum_pad == None {
kv.set("zukünftiges-plenumspad", &future_pad_id).expect("Fehler beim Beschreiben der Datenbank mit neuem Plenumslink!"); // Beispiel: aktuelles-plenumspad: Ok(Some("eCH24zXGS9S8Stg5xI3aRg")) kv.set("zukünftiges-plenumspad", &future_pad_id)
.expect("Fehler beim Beschreiben der Datenbank mit neuem Plenumslink!");
// Beispiel: aktuelles-plenumspad: Ok(Some("eCH24zXGS9S8Stg5xI3aRg"))
} else { } else {
kv.set("aktuelles-plenumspad", &next_plenum_pad.unwrap()).expect("Fehler beim Beschreiben der Datenbank mit neuem Plenumslink!"); // Beispiel: aktuelles-plenumspad: Ok(Some("eCH24zXGS9S8Stg5xI3aRg")) kv.set("aktuelles-plenumspad", &next_plenum_pad.unwrap())
kv.set("zukünftiges-plenumspad", &future_pad_id).expect("Fehler beim Beschreiben der Datenbank mit neuem Plenumslink!"); // Beispiel: aktuelles-plenumspad: Ok(Some("eCH24zXGS9S8Stg5xI3aRg")) .expect("Fehler beim Beschreiben der Datenbank mit neuem Plenumslink!"); // Beispiel: aktuelles-plenumspad: Ok(Some("eCH24zXGS9S8Stg5xI3aRg"))
kv.set("zukünftiges-plenumspad", &future_pad_id)
.expect("Fehler beim Beschreiben der Datenbank mit neuem Plenumslink!");
// Beispiel: aktuelles-plenumspad: Ok(Some("eCH24zXGS9S8Stg5xI3aRg"))
} }
} }
fn try_to_remove_top_instructions (pad_content: String) -> String { fn try_to_remove_top_instructions(pad_content: String) -> String {
let re_top_instructions: Regex = Regex::new(r"(<!--(?:.||\n)*-->)").unwrap(); let re_top_instructions: Regex = Regex::new(r"(<!--(?:.||\n)*-->)").unwrap();
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
} }

View file

@ -6,18 +6,20 @@ pub fn pad_ins_wiki(old_pad_content: String) {
convert_markdown_to_mediawiki_and_save_as_txt(old_pad_content); convert_markdown_to_mediawiki_and_save_as_txt(old_pad_content);
// Textdatei wieder einlesen // Textdatei wieder einlesen
let mut file = File::open("pandoc-output.txt").expect("Fehler beim öffnen der MediaWiki-Textdatei!"); let mut file =
File::open("pandoc-output.txt").expect("Fehler beim öffnen der MediaWiki-Textdatei!");
let mut contents = String::new(); let mut contents = String::new();
file.read_to_string(&mut contents).expect("Fehler beim auslesen der MediaWiki-Textdatei!"); file.read_to_string(&mut contents).expect("Fehler beim auslesen der MediaWiki-Textdatei!");
// Passwörter aus Datenbank lesen (ToBeDone) // Passwörter aus Datenbank lesen (ToBeDone)
let plenum_bot_user = String::from("PlenumBot@PlenumBot-PW1"); let plenum_bot_user = String::from("PlenumBot@PlenumBot-PW1");
let plenum_bot_pw = String::from("**OLD_API_PW_REMOVED**"); let plenum_bot_pw = String::from("**OLD_API_PW_REMOVED**");
let login_token = login_to_mediawiki(plenum_bot_user.clone(), plenum_bot_pw.clone()).expect("Fehler beim Einloggen!"); let login_token = login_to_mediawiki(plenum_bot_user.clone(), plenum_bot_pw.clone())
.expect("Fehler beim Einloggen!");
println!("plenum_bot_user: {plenum_bot_user}, plenum_bot_pw: {plenum_bot_pw}, login_token: {login_token}") println!("plenum_bot_user: {plenum_bot_user}, plenum_bot_pw: {plenum_bot_pw}, login_token: {login_token}")
} }
fn convert_markdown_to_mediawiki_and_save_as_txt (old_pad_content: String) { fn convert_markdown_to_mediawiki_and_save_as_txt(old_pad_content: String) {
//Convert Markdown into Mediawiki //Convert Markdown into Mediawiki
// Vanilla pandoc Befehl: pandoc --from markdown --to mediawiki --no-highlight // Vanilla pandoc Befehl: pandoc --from markdown --to mediawiki --no-highlight
let mut p = pandoc::new(); let mut p = pandoc::new();
@ -25,10 +27,13 @@ fn convert_markdown_to_mediawiki_and_save_as_txt (old_pad_content: String) {
p.set_input_format(pandoc::InputFormat::Markdown, vec![]); p.set_input_format(pandoc::InputFormat::Markdown, vec![]);
p.set_output(pandoc::OutputKind::File("./pandoc-output.txt".parse().unwrap())); p.set_output(pandoc::OutputKind::File("./pandoc-output.txt".parse().unwrap()));
p.set_output_format(pandoc::OutputFormat::MediaWiki, vec![]); p.set_output_format(pandoc::OutputFormat::MediaWiki, vec![]);
p.execute().expect("Fehler beim Umwandeln des und speichern des Pads in eine mediawiki-Textdatei"); p.execute()
.expect("Fehler beim Umwandeln des und speichern des Pads in eine mediawiki-Textdatei");
} }
fn login_to_mediawiki (plenum_bot_user: String, plenum_bot_pw: String) -> Result<String, Box<dyn Error>> { fn login_to_mediawiki(
plenum_bot_user: String, plenum_bot_pw: String,
) -> Result<String, Box<dyn Error>> {
//let mut map = HashMap::new(); //let mut map = HashMap::new();
//map.insert("logintoken", "result"); //map.insert("logintoken", "result");
let username = "cccb-wiki"; let username = "cccb-wiki";
@ -50,7 +55,7 @@ fn login_to_mediawiki (plenum_bot_user: String, plenum_bot_pw: String) -> Result
let html_source = resp.text()?; let html_source = resp.text()?;
//let login_token: String = map.get("logintoken").unwrap().to_string().clone(); //let login_token: String = map.get("logintoken").unwrap().to_string().clone();
println!("---HTML:---\n{}\n-----------", html_source); println!("---HTML:---\n{}\n-----------", html_source);
*/ */
Ok(String::from("unimplemented")) Ok(String::from("unimplemented"))
} }

View file

@ -40,4 +40,4 @@ Nächstes Plenum: Am 2024-08-13 um 20 Uhr
---"#; ---"#;
pub const PLENUM_TEMPLATE_URL: &str = "https://md.berlin.ccc.de/plenum-template/download"; pub const PLENUM_TEMPLATE_URL: &str = "https://md.berlin.ccc.de/plenum-template/download";
pub const HEDGEDOC_SERVER_URL: &str = "https://md.berlin.ccc.de"; pub const HEDGEDOC_SERVER_URL: &str = "https://md.berlin.ccc.de";