basic arg parsing + config check mode
This commit is contained in:
		
							parent
							
								
									97dfcd79bf
								
							
						
					
					
						commit
						9f38a41b46
					
				
					 2 changed files with 302 additions and 90 deletions
				
			
		
							
								
								
									
										208
									
								
								src/config_check.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										208
									
								
								src/config_check.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,208 @@ | |||
| use crate::key_value::KeyValueStore as KV; | ||||
| use std::error::Error; | ||||
| use std::io::{self, Write}; | ||||
| 
 | ||||
| const CONFIG_SPEC: CfgSpec<'_> = CfgSpec { | ||||
|     groups: &[ | ||||
|         CfgGroup { name: "config", | ||||
|             description: "(internal values)", | ||||
|             fields: &[ | ||||
|                 CfgField::Silent { key: "version", default: "1" }, | ||||
|             ], | ||||
|         }, | ||||
|         CfgGroup { name: "hedgedoc", | ||||
|             description: "HedgeDoc markdown pad server settings", | ||||
|             fields: &[ | ||||
|                 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." }, | ||||
|                 // TODO: make these generators?
 | ||||
|                 CfgField::Optional { key: "last-id", description: "ID of last plenum's pad." }, | ||||
|                 CfgField::Optional { key: "next-id", description: "ID of next plenum's pad." } | ||||
|             ], | ||||
|         }, | ||||
|         CfgGroup { name: "email", | ||||
|             description: "Sending emails.", | ||||
|             fields: &[ | ||||
|                 CfgField::Default { key: "server", default: "mail.berlin.ccc.de", description: "SMTP server used for sending emails." }, | ||||
|                 CfgField::Default { key: "user", default: "plenum-bot@berlin.ccc.de", description: "User name used for authenticating with the mail server." }, | ||||
|                 CfgField::Password { key: "password", description: "Password for authenticating with the mail server." }, | ||||
|                 CfgField::Default { key: "sender", default: "Plenumsbot <plenum-bot@berlin.ccc.de>", description: "Email address to use for \"From:\"." }, | ||||
|                 CfgField::Default { key: "to", default: "CCCB Intern <intern@berlin.ccc.de>", description: "Recipient of the emails sent." }, | ||||
|                 CfgField::Optional { key: "last-message-id", description: "Message-Id of last initial announcement to send In-Reply-To (if applicable)." } | ||||
|             ], | ||||
|         }, | ||||
|         // TODO: Matrix, Wiki
 | ||||
|         CfgGroup { name: "text", | ||||
|             description: "Various strings used.", | ||||
|             fields: &[ | ||||
|                 CfgField::Default { key: "email-signature", | ||||
|                     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." | ||||
|                 }, | ||||
|             ], | ||||
|         }, | ||||
|     ], | ||||
| }; | ||||
| 
 | ||||
| /// Ensure all fields with known default values exist.
 | ||||
| pub fn populate_defaults(config: &KV) { | ||||
|     for group in CONFIG_SPEC.groups { | ||||
|         for field in group.fields { | ||||
|             match field { | ||||
|                 CfgField::Silent { key, default, .. } => { | ||||
|                     config.default(&format!("{}-{}", group.name, key), default) | ||||
|                 }, | ||||
|                 CfgField::Default { key, default, .. } => { | ||||
|                     config.default(&format!("{}-{}", group.name, key), default) | ||||
|                 }, | ||||
|                 _ => {} | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| const ANSI_GROUP: &str = "\x1b[1;31m"; | ||||
| const ANSI_FIELD: &str = "\x1b[1;33m"; | ||||
| const ANSI_RESET: &str = "\x1b[0m"; | ||||
| 
 | ||||
| pub fn interactive_check(config: KV) -> Result<(), Box<dyn Error>> { | ||||
|     for group in CONFIG_SPEC.groups { | ||||
|         group_header( group.name, group.description ); | ||||
|         // TODO: skip category option?
 | ||||
|         for field in group.fields { | ||||
|             check_field( &config, group.name, field )?; | ||||
|         } | ||||
|     } | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| fn check_field( config: &KV, grpname: &str, field: &CfgField ) -> Result<(), Box<dyn Error>> { | ||||
|     if field.is_silent() { return Ok(()) } | ||||
|     let key = field.full_key(grpname); | ||||
|     println!( "{}{}{} - {}", ANSI_FIELD, &key, ANSI_RESET, field.description() ); | ||||
|     println!( "  default: {}", field.default().unwrap_or("(empty)".to_string())); | ||||
|     let value = config.get(&key).ok(); | ||||
|     if field.is_password() { | ||||
|         match value { | ||||
|             Some(_) => println!("  current: *****"), | ||||
|             None => println!("  current: (empty)"), | ||||
|         }; | ||||
|         //todo!()
 | ||||
|         // TODO: [K]eep as-is (if exists), [R]emove, [I]nput
 | ||||
|     } else { | ||||
|         println!( "  current: {}", value.unwrap_or("(empty)".to_string()) ); | ||||
|         // TODO: [K]eep as-is, [R]eset to default, [I]nput, [L]ong (multiline) input
 | ||||
|     } | ||||
|     // Action - 
 | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| fn group_header( name: &str, description: &str ) { | ||||
|     println!("=============================="); | ||||
|     println!("{}{}{} - {}", ANSI_GROUP, name, ANSI_RESET, description); | ||||
|     println!(""); | ||||
| } | ||||
| 
 | ||||
| fn prompt_single_line( ) -> String { | ||||
|     print!("New value: "); | ||||
|     io::stdout().flush().ok(); | ||||
|     let mut input = String::new(); | ||||
|     io::stdin().read_line(&mut input).unwrap(); | ||||
|     input.trim().to_string() | ||||
| } | ||||
| 
 | ||||
| fn prompt_multiline( ) -> String { | ||||
|     println!("Enter new value: (end with '.' on a new line)"); | ||||
|     let mut acc = String::new(); | ||||
|     loop { | ||||
|         let mut line = String::new(); | ||||
|         io::stdin().read_line(&mut line).unwrap(); | ||||
|         if line.trim() == "." { | ||||
|             break; | ||||
|         } | ||||
|         acc.push_str(&line); | ||||
|     } | ||||
|     acc | ||||
| } | ||||
| 
 | ||||
| fn prompt_password( ) -> String { | ||||
|     rpassword::prompt_password("New password (not shown) : ").unwrap() | ||||
| } | ||||
| 
 | ||||
| /// Describes a field in the configuration.
 | ||||
| enum CfgField<'a> { | ||||
|     /// Silently writes a default to the DB if absent, never prompts for changes.
 | ||||
|     Silent { key: &'a str, default: &'a str }, | ||||
|     /// Writes a default, asks for changes.
 | ||||
|     Default { key: &'a str, default: &'a str, description: &'a str }, | ||||
|     /// No default, asks for value, but can stay empty.
 | ||||
|     Optional { key: &'a str, description: &'a str }, | ||||
|     /// Empty by default, required, will prompt without echo.
 | ||||
|     Password { key: &'a str, description: &'a str }, | ||||
|     /// Empty by default, can be user-provided, or will be generated randomly.
 | ||||
|     RandomId { key: &'a str, generator: fn() -> String, generator_description: &'a str, description: &'a str }, | ||||
| } | ||||
| 
 | ||||
| /// A group of related config fields. The final key of an inner value will be
 | ||||
| /// `{group.name}-{key}`.
 | ||||
| struct CfgGroup<'a> { | ||||
|     name: &'a str, | ||||
|     description: &'a str, | ||||
|     fields: &'a [CfgField<'a>], | ||||
| } | ||||
| 
 | ||||
| /// A description of the entire config, as a collection of named groups.
 | ||||
| ///
 | ||||
| /// Used both to populate defaults and to interactively adjust the configuration.
 | ||||
| struct CfgSpec<'a> { | ||||
|     groups: &'a [CfgGroup<'a>], | ||||
| } | ||||
| 
 | ||||
| impl<'a> CfgField<'a> { | ||||
|     fn is_silent(&self) -> bool { | ||||
|         match self { | ||||
|             CfgField::Silent { .. } => true, | ||||
|             _ => false | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn is_password(&self) -> bool { | ||||
|         match self { | ||||
|             CfgField::Password { .. } => true, | ||||
|             _ => false | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn key(&self) -> String { | ||||
|         match self { | ||||
|             CfgField::Silent { key, .. } => key, | ||||
|             CfgField::Default { key, .. } => key, | ||||
|             CfgField::Optional { key, .. } => key, | ||||
|             CfgField::Password { key, .. } => key, | ||||
|             CfgField::RandomId { key, .. } => key, | ||||
|         }.to_string() | ||||
|     } | ||||
| 
 | ||||
|     fn full_key(&self, grpname: &str) -> String { | ||||
|         format!("{}-{}", grpname, self.key()) | ||||
|     } | ||||
| 
 | ||||
|     fn description(&self) -> String { | ||||
|         match self { | ||||
|             CfgField::Silent { .. } => "(none)", | ||||
|             CfgField::Default { description, .. } => description, | ||||
|             CfgField::Optional { description, .. } => description, | ||||
|             CfgField::Password { description, .. } => description, | ||||
|             CfgField::RandomId { description, .. } => description, | ||||
|         }.to_string() | ||||
|     } | ||||
| 
 | ||||
|     fn default(&self) -> Option<String> { | ||||
|         match self { | ||||
|             CfgField::Silent { default, .. } => Some(default.to_string()), | ||||
|             CfgField::Default { default, .. } => Some(default.to_string()), | ||||
|             CfgField::RandomId { generator_description, .. } => Some(format!("{}{}{}",'(',generator_description,')')), | ||||
|             _ => None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										184
									
								
								src/main.rs
									
										
									
									
									
								
							
							
						
						
									
										184
									
								
								src/main.rs
									
										
									
									
									
								
							|  | @ -19,42 +19,33 @@ Pad-ins-Wiki-und-versenden-Skript | |||
|     •	Neue Wiki-Seite erstellen und dort das umgewandelte Pad hochladen | ||||
| */ | ||||
| 
 | ||||
| use std::borrow::Cow; | ||||
| use std::collections::HashMap; | ||||
| use std::error::Error; | ||||
| use std::fs::File; | ||||
| use std::io::prelude::*; | ||||
| 
 | ||||
| use api::Api; | ||||
| use chrono::{Datelike, Local, NaiveDate, Weekday}; | ||||
| // MAIL START
 | ||||
| use lettre::{Message, SmtpTransport, Transport}; | ||||
| use lettre::message::{header, SinglePart}; | ||||
| use lettre::transport::smtp::authentication::Credentials; | ||||
| // For MediaWiki-conversion
 | ||||
| use pandoc; | ||||
| use regex::Regex; | ||||
| use reqwest::blocking::{Client, get, Response}; | ||||
| use reqwest::header as rqwheader; | ||||
| use serde::Deserialize; | ||||
| use uuid::Uuid; | ||||
| use mediawiki; | ||||
| use base64::Engine; | ||||
| 
 | ||||
| use key_value::KeyValueStore as KV; | ||||
| 
 | ||||
| // Import other .rs files as modules
 | ||||
| mod key_value; | ||||
| use key_value::KeyValueStore as KV; | ||||
| 
 | ||||
| mod config_check; | ||||
| 
 | ||||
| mod create_new_pads; | ||||
| pub mod variables_and_settings; | ||||
| 
 | ||||
| use std::borrow::Cow; | ||||
| use std::error::Error; | ||||
| use std::env; | ||||
| use std::io; | ||||
| use std::io::prelude::*; | ||||
| use std::fs::File; | ||||
| // use std::future::Future;
 | ||||
| 
 | ||||
| // use std::process::Command;
 | ||||
| // use headers::ContentType;
 | ||||
| use chrono::{Datelike, Local, NaiveDate, Weekday}; | ||||
| use clap::{Arg, Command}; | ||||
| use regex::Regex; | ||||
| use reqwest::Client; | ||||
| use uuid::Uuid; | ||||
| use pandoc::{MarkdownExtension, Pandoc, PandocError, PandocOutput}; | ||||
| 
 | ||||
| // MAIL END
 | ||||
| use lettre::{Message, SmtpTransport, Transport}; | ||||
| use lettre::message::{header, SinglePart}; | ||||
| use lettre::transport::smtp::authentication::Credentials; | ||||
| 
 | ||||
| #[derive(PartialEq)] | ||||
| enum EMailOperationStates { | ||||
|  | @ -67,22 +58,59 @@ const PLENUM_TEMPLATE_URL: &str = variables_and_settings::PLENUM_TEMPLATE_URL; | |||
| const FALLBACK_TEMPLATE: &str = variables_and_settings::FALLBACK_TEMPLATE; | ||||
| const TESTING_MODE: EMailOperationStates = EMailOperationStates::Test; | ||||
| 
 | ||||
| fn kv_defaults (kv: &KV) { | ||||
|     kv.default("hedgedoc-server-url","https://md.berlin.ccc.de/"); | ||||
|     kv.default("hedgedoc-template-name","plenum-template"); | ||||
|     // hedgedoc-last-id, hedgedoc-next-id have no defaults
 | ||||
|     kv.default("email-server","mail.berlin.ccc.de"); | ||||
|     kv.default("email-user","plenum-bot@berlin.ccc.de"); | ||||
|     kv.default("email-name","Plenumsbot"); | ||||
|     // add email-pass and email-to manually
 | ||||
|     // email-last-message-id has no default
 | ||||
|     kv.default("email-signature", "\n\n[Diese Nachricht wurde automatisiert vom Plenumsbot erstellt und ist daher ohne Unterschrift gültig.]"); | ||||
| /* ***** Runtime Configuration (Arguments & Environment Variables) ***** */ | ||||
| 
 | ||||
| /// Checks environment variable `DRY_RUN` to see if any external operations
 | ||||
| /// should *actually* be done.
 | ||||
| ///
 | ||||
| /// If `is_dry_run` returns `true`, just report what you *would* do, and
 | ||||
| /// don't actually e.g. send emails.
 | ||||
| fn is_dry_run() -> bool { | ||||
|     env::var("DRY_RUN").map(|v| !v.is_empty()).unwrap_or(false) | ||||
| } | ||||
| 
 | ||||
| fn main() -> Result<(), Box<dyn Error>> { | ||||
| struct Args { | ||||
|     check_mode: bool, | ||||
|     config_file: String, | ||||
| } | ||||
| fn parse_args() -> Args { | ||||
|     let matches = Command::new("Plenum-Bot") | ||||
|         .about("CCCB Plenumsbot") | ||||
|         .arg( | ||||
|             Arg::new("check") | ||||
|                 .short('c') | ||||
|                 .long("check") | ||||
|                 .action(clap::ArgAction::SetTrue) | ||||
|                 .help("interactively check the config for missing values"), | ||||
|         ) | ||||
|         .arg( | ||||
|             Arg::new("config_file") | ||||
|                 .short('f') | ||||
|                 .long("config-file") | ||||
|                 .value_name("FILE") | ||||
|                 .action(clap::ArgAction::Set) | ||||
|                 .default_value("config.sqlite") | ||||
|                 .help("specifies an alternate config file"), | ||||
|         ).get_matches(); | ||||
|     Args { | ||||
|         check_mode: *matches.get_one::<bool>("check").unwrap(), | ||||
|         config_file: matches.get_one::<String>("config_file").unwrap().clone(), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* ***** Main ***** */ | ||||
| 
 | ||||
| #[tokio::main] | ||||
| async fn main() -> Result<(), Box<dyn Error>> { | ||||
|     // set up config file access
 | ||||
|     let args = parse_args(); | ||||
|     let config_file = args.config_file.as_str(); | ||||
|     let config = KV::new(config_file).unwrap(); | ||||
|     config_check::populate_defaults(&config); | ||||
|     if args.check_mode { | ||||
|         return config_check::interactive_check( config ); | ||||
|     } | ||||
|     // config
 | ||||
|     let config = KV::new("plenum_config.sqlite").unwrap(); | ||||
|     kv_defaults(&config); | ||||
|     let hedgedoc_server = &config["hedgedoc-server-url"]; | ||||
|     println!("[START]\nAktueller Zustand der DB:"); | ||||
|     config.dump_redacting(&["email-pass","matrix-pass"]).ok(); | ||||
|  | @ -145,7 +173,7 @@ fn main() -> Result<(), Box<dyn Error>> { | |||
|     let yesterday_was_plenum = true; // Das ist nur zu Testzwecken, kommt noch weg
 | ||||
|     if in_1_day_is_plenum { | ||||
|         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())).expect("Fehler beim Download des Pads!"); | ||||
|         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 mut tldr  = String::new(); | ||||
|  | @ -169,7 +197,7 @@ fn main() -> Result<(), Box<dyn Error>> { | |||
|         } | ||||
|     } else if in_3_days_is_plenum { | ||||
|         println!("In 3 Tagen ist Plenum, deshalb wird eine Erinnerung raus geschickt!"); | ||||
|         let pad_content = download_and_return_pad(current_pad_link.clone()).expect("Fehler beim Download des Pads!"); | ||||
|         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 { | ||||
|  | @ -182,8 +210,8 @@ fn main() -> Result<(), Box<dyn Error>> { | |||
|         println!("message id: {}", message_id) | ||||
|     } else if yesterday_was_plenum { | ||||
|         // 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())).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).expect("Fehler! Plenumspad konnte nicht generiert werden!");
 | ||||
|         let old_pad_content = download_and_return_pad(format!("{}/download", current_pad_link.clone())).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!");
 | ||||
|         println!("DATENBANK: aktuelles-plenumspad: {:?} und zukünftiges plenumspad: {:?}", &config.get("aktuelles-plenumspad"), &config.get("zukünftiges-plenumspad")); | ||||
| 
 | ||||
|         let old_pad_content_without_top_instructions = try_to_remove_top_instructions(old_pad_content); | ||||
|  | @ -206,10 +234,12 @@ fn main() -> Result<(), Box<dyn Error>> { | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| fn download_and_return_pad(pad_link: String) -> Result<String, Box<dyn std::error::Error>> { | ||||
| 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::blocking::get(pad_link)? | ||||
|         .text()?; | ||||
|     let pad_content: String = reqwest::get(pad_link) | ||||
|         .await? | ||||
|         .text() | ||||
|         .await?; | ||||
|     //println!("{}", pad_content);
 | ||||
|     Ok(pad_content) | ||||
| } | ||||
|  | @ -302,17 +332,17 @@ fn mail_versenden(inhalt: String, betreff: String) -> std::result::Result<String | |||
| async fn generate_new_pad_for_following_date(ü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) { | ||||
|     match create_new_pads::create_pad(&client, HEDGEDOC_SERVER_URL).await { | ||||
|         Ok(pad_url) => { | ||||
|             println!("Pad created successfully at URL: {}", pad_url); | ||||
| 
 | ||||
|             // Get the most recent plenum template and replace the placeholders:
 | ||||
|             let template_from_pad = download_and_return_pad(PLENUM_TEMPLATE_URL.to_string()); // Download Pad
 | ||||
|             let template_from_pad = download_and_return_pad(PLENUM_TEMPLATE_URL.to_string()).await; // Download Pad
 | ||||
|             let template_content: String = template_from_pad.unwrap_or_else(|error| FALLBACK_TEMPLATE.to_string()); // If Download wasn't successful, use offline Template
 | ||||
|             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 create_new_pads::import_note(&client, template_modified, Some(future_pad_id), HEDGEDOC_SERVER_URL) { | ||||
|             match create_new_pads::import_note(&client, template_modified, Some(future_pad_id), HEDGEDOC_SERVER_URL).await { | ||||
|                 Ok(_) => { | ||||
|                     println!("Pad updated successfully with template content."); | ||||
|                     rotate (future_pad_id, kv); | ||||
|  | @ -349,53 +379,27 @@ 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
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| fn pad_ins_wiki(old_pad_content: String) { | ||||
|     convert_markdown_to_mediawiki_and_save_as_txt(old_pad_content); | ||||
| 
 | ||||
|     // 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**"); | ||||
|     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}") | ||||
| } | ||||
| 
 | ||||
| fn convert_markdown_to_mediawiki_and_save_as_txt (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"); | ||||
| } | ||||
| 
 | ||||
| fn login_to_mediawiki (plenum_bot_user: String, plenum_bot_pw: String) -> Result<String, Box<dyn Error>> { | ||||
|     //let mut map = HashMap::new();
 | ||||
|     //map.insert("logintoken", "result");
 | ||||
|     let username = "cccb-wiki"; | ||||
|     let password = "**OLD_PW_REMOVED**"; | ||||
|     let auth_header_value = format!("{}:{}", username, password); | ||||
|     // let auth_header_value = format!("Basic {}", Engine::encode(&auth_value, ()));
 | ||||
|     
 | ||||
|     let client = reqwest::blocking::Client::new(); | ||||
|     let resp = client | ||||
|         .get("https://wiki.berlin.ccc.de/api.php?action=query&meta=tokens&type=login&format=json") | ||||
|         .send()? | ||||
|         .text()?;   
 | ||||
|     //let response = client
 | ||||
|     //    .post("https://wiki.berlin.ccc.de/api.php?action=query&meta=tokens&type=login&format=json")
 | ||||
|     //    .form(&[("Username", "cccb-wiki"), ("Password", "**OLD_PW_REMOVED**")])
 | ||||
|     //    .send()
 | ||||
|     //    .unwrap();
 | ||||
|         //.json(&map);
 | ||||
|     let html_source = resp.text()?; | ||||
|     //let login_token: String = map.get("logintoken").unwrap().to_string().clone();
 | ||||
|     println!("---HTML:---\n{}\n-----------", html_source); | ||||
|     Ok(String::from("unimplemented")) | ||||
| } | ||||
|     // 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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 nobody
						nobody