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,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										178
									
								
								src/main.rs
									
										
									
									
									
								
							
							
						
						
									
										178
									
								
								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,8 +379,18 @@ 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);
 | 
			
		||||
    //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!");
 | 
			
		||||
| 
						 | 
				
			
			@ -360,42 +400,6 @@ fn pad_ins_wiki(old_pad_content: String) {
 | 
			
		|||
    // 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(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"))
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue