mediawiki progress
- mediawiki.rs: - new make_request function for easier post- and get-requests - new new_wiki_page function that creates new wiki pages - working csrf-Token-function - fixed read_txt_file function: Now properly working - pad_ins_wiki-function is now the main trigger for all the actions that should be done on day after plenum - hedgedoc.rs + mediawiki.rs: Better error handling for common offline edge case - main: ansi art moved in function, Timer and eta for time prediction during development - still to do in mediawiki.rs: Date logic for pad generation and function for modifying the main Plenum Page
This commit is contained in:
parent
f12f6d05b1
commit
e4444b8118
|
@ -22,6 +22,7 @@ serde = {version = "1.0.204", features = ["derive"]}
|
|||
serde_json = "1.0.122"
|
||||
colored = "2.1.0"
|
||||
nom = "7.1.3"
|
||||
mediawiki = "0.3.1"
|
||||
|
||||
[[bin]]
|
||||
name = "Plenum-Bot"
|
||||
|
|
|
@ -54,9 +54,25 @@ impl HedgeDoc {
|
|||
}
|
||||
|
||||
fn do_request(&self, url: &str) -> Result<Response, Box<dyn Error>> {
|
||||
Ok(self.client.get(url).send().unwrap())
|
||||
match self.client.get(url).send() {
|
||||
Ok(response) => {
|
||||
if response.status().is_success() {
|
||||
Ok(response)
|
||||
} else {
|
||||
Err(format!("Failed to connect to hedgedoc server: HTTP status code {}", response.status()).into())
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
if e.is_connect() {
|
||||
Err("Failed to connect to hedgedoc server. Please check your internet connection or the server URL.".into())
|
||||
} else {
|
||||
Err(format!("An error occurred while sending the request to the hedgedoc server: {}", e).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn get_id_from_response(&self, res: Response) -> String {
|
||||
res.url().to_string().trim_start_matches(&format!("{}/", self.server_url)).to_string()
|
||||
}
|
||||
|
|
25
src/main.rs
25
src/main.rs
|
@ -22,25 +22,25 @@ Pad-ins-Wiki-und-versenden-Skript
|
|||
main.rs
|
||||
- [ ] Add logic for top_anzahl in main.rs:172
|
||||
MediaWiki
|
||||
- [ ] Add "get_csrf_token-function" for getting a write token to allow write operations in the wiki (murmeldin)
|
||||
- [ ] Add "create_page" function for creating new pages in the wiki that is called on every day after plenum (murmeldin)
|
||||
- [X] Add "get_csrf_token-function" for getting a write token to allow write operations in the wiki (murmeldin)
|
||||
- [X] Add "create_page" function for creating new pages in the wiki that is called on every day after plenum (murmeldin)
|
||||
- [ ] Add "modify_plenum_main_page" function for creating new Links on the Plenum main page whenever the create_page function is being called (murmeldin)
|
||||
- [ ] Date Logic for create_page-function (maybe nobody or murmeldin when date_logic branch is done and merged)
|
||||
|
||||
future improvements:
|
||||
- search ADJ_TIMEYWIMEY to find places that need adjusting if the bot might run late
|
||||
(that's an incomplete list, but tag things as you notice them…)
|
||||
*/
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::time::Instant;
|
||||
|
||||
use chrono::{Local, NaiveDate};
|
||||
use clap::{Arg, Command};
|
||||
use colored::Colorize;
|
||||
use regex::Regex;
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
|
||||
use std::fmt::Display;
|
||||
use std::io::IsTerminal;
|
||||
|
||||
|
@ -160,6 +160,7 @@ fn parse_args() -> Args {
|
|||
/* ***** Main ***** */
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let start = Instant::now();
|
||||
if std::io::stdout().is_terminal() {
|
||||
println!(include_str!("chaosknoten.txt"), VERSION = env!("CARGO_PKG_VERSION"));
|
||||
}
|
||||
|
@ -275,6 +276,11 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||
|
||||
// shutdown
|
||||
config.set("state-last-run", &today.to_string())?;
|
||||
|
||||
let duration = format!("{:?}", start.elapsed());
|
||||
config.set("state-eta", &duration).ok();
|
||||
verboseln!("Der Bot ist fertig und hat für die Ausführung {} gebraucht.", duration);
|
||||
|
||||
if config.has_errors() {
|
||||
Err("There were errors while writing config values.".into())
|
||||
} else {
|
||||
|
@ -679,3 +685,12 @@ fn send_email(
|
|||
);
|
||||
email.send_email(full_subject, full_body)
|
||||
}
|
||||
|
||||
fn display_logo(eta: &str) {
|
||||
let ansi_art = r#"
|
||||
▐▛▀ ▐▛▀ ▐▛▀ ▐▛▚ ▐▛▚ ▐▌ ▐▛▀ ▐▙ ▉ ▐▌ ▐▌ ▐▙ ▟▌ ▐▛▚ ▐▛▀▜▌▝▀█▀
|
||||
▐▌ ▐▌ ▐▌ ▐█▙ ▐▙▞ ▐▌ ▐▙▄ ▐▌▚ ▉ ▐▌ ▐▌ ▐▌▀▐▌ ▐█▙ ▐▌ ▐▌ █
|
||||
▐▙▄ ▐▙▄ ▐▙▄ ▐▙▞ ▐▌ ▐▙▄ ▐▙▄ ▐▌ ▚▉ ▐▙▄▟▌ ▐▌ ▐▌ ▐▙▞ ▐▙▄▟▌ █ Version 1.1,"#;
|
||||
let ansi_art = format!("{ansi_art} ETA = {eta}\n");
|
||||
println!("{}", ansi_art.red());
|
||||
}
|
194
src/mediawiki.rs
194
src/mediawiki.rs
|
@ -36,6 +36,11 @@ pub const CONFIG: CfgGroup<'static> = CfgGroup {
|
|||
key: "api-secret",
|
||||
description: "API secret / \"password\" used for authenticating as the bot.",
|
||||
},
|
||||
CfgField::Default {
|
||||
key: "eta",
|
||||
default: "no ETA, program never ran",
|
||||
description: "ETA message for estimating time the program takes"
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
|
@ -63,6 +68,12 @@ impl std::fmt::Debug for Mediawiki {
|
|||
}
|
||||
}
|
||||
|
||||
pub enum RequestType {
|
||||
Get,
|
||||
Post,
|
||||
PostForEditing
|
||||
}
|
||||
|
||||
impl Mediawiki {
|
||||
pub fn new(
|
||||
server_url: &str, http_auth_user: &str, http_auth_password: &str, api_user: &str, api_secret: &str, is_dry_run: bool,
|
||||
|
@ -81,84 +92,155 @@ impl Mediawiki {
|
|||
}
|
||||
pub fn get_login_token(&self) -> Result<String, Box<dyn Error>> {
|
||||
let url =
|
||||
format!("{}/api.php?action=query&meta=tokens&type=login&format=json", self.server_url);
|
||||
let resp = self
|
||||
.client
|
||||
.get(url)
|
||||
//.basic_auth(&self.http_user, Some(&self.http_password)) ZU TESTZWECKEN ENTFERNT
|
||||
.send()?
|
||||
.text()?;
|
||||
let response_deserialized: QueryResponse = serde_json::from_str(&resp)?;
|
||||
format!("{}/api.php?", self.server_url);
|
||||
let params: Box<[(&str, &str)]> = Box::from( [
|
||||
("format", "json"),
|
||||
("meta", "tokens"),
|
||||
("type", "login"),
|
||||
("action", "query")
|
||||
]);
|
||||
let resp = self.make_request(url, params, RequestType::Get).unwrap();
|
||||
let response_deserialized: QueryResponseLogin = serde_json::from_str(&resp)?;
|
||||
Ok(response_deserialized.query.tokens.logintoken)
|
||||
}
|
||||
pub fn login (&self) -> Result<String, Box<dyn Error>> {
|
||||
let url = format!("{}/api.php?action=login", self.server_url);
|
||||
let params = [
|
||||
("lgname", &self.api_user),
|
||||
("lgpassword", &self.api_secret),
|
||||
("lgtoken", &self.login_token)
|
||||
];
|
||||
let resp = self
|
||||
.client
|
||||
.post(url)
|
||||
.form(¶ms)
|
||||
.send()?
|
||||
.text()?;
|
||||
Ok(resp)
|
||||
let url = format!("{}/api.php?", self.server_url);
|
||||
let params: Box<[(&str, &str)]> = Box::from([
|
||||
("lgname", self.api_user.as_str()),
|
||||
("lgpassword", self.api_secret.as_str()),
|
||||
("lgtoken", &self.login_token),
|
||||
("action", "login")
|
||||
]);
|
||||
let resp: Result<String, Box<dyn Error>> = self.make_request(url, params, RequestType::Post);
|
||||
Ok(resp.unwrap())
|
||||
}
|
||||
pub fn get_csrf_token(&self) -> Result<String, Box<dyn Error>> { // HAS TO BE FIXED
|
||||
// action=query&meta=tokens
|
||||
let url =
|
||||
format!("{}/api.php?", self.server_url);
|
||||
let params = [
|
||||
let params: Box<[(&str, &str)]> = Box::from([
|
||||
("format", "json"),
|
||||
("meta", "tokens"),
|
||||
("formatversion", "2"),
|
||||
("lgtoken", &self.login_token)
|
||||
];
|
||||
let resp = self
|
||||
.client
|
||||
.get(url)
|
||||
//.basic_auth(&self.http_user, Some(&self.http_password)) ZU TESTZWECKEN ENTFERNT
|
||||
.query(¶ms)
|
||||
.send()?
|
||||
.text()?;
|
||||
println!(" --- \n{}\n ---", resp.green());
|
||||
let response_deserialized: QueryResponse = serde_json::from_str(&resp)?;
|
||||
Ok(response_deserialized.query.tokens.logintoken)
|
||||
("action", "query")
|
||||
]);
|
||||
let resp: Result<String, Box<dyn Error>> = self.make_request(url, params, RequestType::Get);
|
||||
let resp = resp.unwrap();
|
||||
let response_deserialized: QueryResponseCsrf = serde_json::from_str(&resp)?;
|
||||
Ok(response_deserialized.query.tokens.csrftoken)
|
||||
}
|
||||
|
||||
pub fn make_request(&self, url: String, params: Box<[(&str, &str)]>, request_type: RequestType) -> Result<String, Box<dyn Error>> {
|
||||
let resp: Result<String, Box<dyn Error>> = match
|
||||
match request_type {
|
||||
RequestType::Get => {
|
||||
self
|
||||
.client
|
||||
.get(url)
|
||||
//.basic_auth(&self.http_user, Some(&self.http_password)) ZU TESTZWECKEN ENTFERNT
|
||||
.query(¶ms)
|
||||
.send()
|
||||
}
|
||||
RequestType::Post | RequestType::PostForEditing => {
|
||||
self
|
||||
.client
|
||||
.post(url)
|
||||
//.basic_auth(&self.http_user, Some(&self.http_password)) ZU TESTZWECKEN ENTFERNT
|
||||
.form(¶ms)
|
||||
.send()
|
||||
}
|
||||
}
|
||||
{
|
||||
Ok(response) => {
|
||||
if response.status().is_success() {
|
||||
match request_type {
|
||||
RequestType::PostForEditing => Ok(response.text().unwrap()),
|
||||
_ => Ok(response.text().unwrap())
|
||||
}
|
||||
}
|
||||
else {
|
||||
Err(format!("Failed to connect to wiki server: HTTP status code {}", response.status()).into())
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
if e.is_connect() {
|
||||
Err(format!("Failed to connect to wiki server. Please check your internet connection or the server URL.\n(Error: {})", e).into())
|
||||
} else {
|
||||
Err(format!("An error occurred while sending the request to the wiki server: {}", e).into())
|
||||
}
|
||||
}
|
||||
};
|
||||
resp
|
||||
}
|
||||
/// Creates a completely new wiki page with page_content and page_title as inputs
|
||||
pub fn new_wiki_page (&self, page_title: &str, page_content: &str) -> Result<String, Box<dyn Error>> {
|
||||
// action=edit&format=json&title=Wikipedia:Sandbox&appendtext=Hello&token=sampleCsrfToken123+\
|
||||
let url =
|
||||
format!("{}/api.php?", self.server_url);
|
||||
let params: Box<[(&str, &str)]> = Box::from([
|
||||
("action", "edit"), // Create and edit pages.
|
||||
("format", "json"),
|
||||
("title", page_title), // Title of the page to edit. Cannot be used together with pageid.
|
||||
("appendtext", page_content), // Add this text to the end of the page or section. Overrides text.
|
||||
("token", self.csrf_token.as_str()), // A "csrf" token retrieved from action=query&meta=tokens
|
||||
("bot", "true"), // Mark this edit as a bot edit.
|
||||
]);
|
||||
self.make_request(url, params, RequestType::Post)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct QueryResponse {
|
||||
struct QueryResponseLogin {
|
||||
batchcomplete: String,
|
||||
query: QueryTokens,
|
||||
query: QueryTokensLogin,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct QueryTokens {
|
||||
tokens: Tokens,
|
||||
struct QueryTokensLogin {
|
||||
tokens: TokensLogin,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Tokens {
|
||||
struct TokensLogin {
|
||||
logintoken: String,
|
||||
}
|
||||
|
||||
pub fn pad_ins_wiki(old_pad_content: String, wiki: &mut Mediawiki) -> Result<(), Box<dyn Error>> {
|
||||
convert_md_to_mediawiki(old_pad_content);
|
||||
#[derive(Deserialize)]
|
||||
struct QueryResponseCsrf {
|
||||
batchcomplete: bool,
|
||||
query: crate::mediawiki::QueryTokensCsrf,
|
||||
}
|
||||
|
||||
let auth_result = wiki.get_login_token().unwrap();
|
||||
wiki.login_token = auth_result.clone();
|
||||
#[derive(Deserialize)]
|
||||
struct QueryTokensCsrf {
|
||||
tokens: crate::mediawiki::TokensCsrf,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct TokensCsrf {
|
||||
csrftoken: String,
|
||||
}
|
||||
|
||||
pub fn pad_ins_wiki(old_pad_content: String, wiki: &mut Mediawiki) -> Result<(), Box<dyn Error>> {
|
||||
// Login to Wiki and get required tokens for logging in and writing
|
||||
let auth_result = wiki.get_login_token()?;
|
||||
wiki.login_token.clone_from(&auth_result);
|
||||
println!("AUTH Success");
|
||||
let login_result = wiki.login()?;
|
||||
println!("LOGIN Success");
|
||||
let csrf_token = wiki.get_csrf_token();
|
||||
let csrf_token = csrf_token.unwrap_or_else(|e| {
|
||||
println!("Error while trying to get csrf: {:?}", e);
|
||||
String::new()
|
||||
});
|
||||
println!("CSRF Success");
|
||||
//wiki.csrf_token.clone_from(&csrf_token); HAS TO BE FIXED
|
||||
println!("---AUTH RESULT:---\n{}\n---LOGIN RESULT:---\n{:?}\n---CSRF RESULT:---\n\n-----------", auth_result, login_result,/* csrf_token*/);
|
||||
|
||||
wiki.csrf_token.clone_from(&csrf_token);
|
||||
println!("---AUTH RESULT:---\n{}\n---LOGIN RESULT:---\n{:?}\n---CSRF RESULT:---\n{}\n-----------", auth_result, login_result, csrf_token);
|
||||
|
||||
// Convert to mediawiki and make new page
|
||||
let pad_converted = convert_md_to_mediawiki(old_pad_content);
|
||||
println!("Das kommt ins Wiki: {}", pad_converted);
|
||||
//wiki.new_wiki_page("Page Test 5", &pad_converted)?; JUST AN EXAMPLE
|
||||
|
||||
// Textdatei wieder einlesen
|
||||
|
||||
// Passwörter aus Datenbank lesen (ToBeDone)
|
||||
|
@ -192,11 +274,11 @@ fn pandoc_convert(
|
|||
/// Reads a text file from a specified path and returns it as a String
|
||||
fn read_txt_file(filepath: &str) -> String {
|
||||
let mut file = File::open(filepath)
|
||||
.expect(&*format!("Fehler beim öffnen der Textdatei mit Pfad {filepath}!"));
|
||||
.unwrap_or_else(|_| panic!("Fehler beim öffnen der Textdatei mit Pfad {filepath}!"));
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents)
|
||||
.expect("Fehler beim auslesen der MediaWiki-Textdatei!")
|
||||
.to_string()
|
||||
.expect("Fehler beim auslesen der MediaWiki-Textdatei!");
|
||||
contents
|
||||
}
|
||||
|
||||
/// Takes a Sting in the Markdown format and returns a String in the mediawiki Format
|
||||
|
@ -204,12 +286,8 @@ fn convert_md_to_mediawiki(old_pad_content: String) -> String {
|
|||
// TODO: use tempfile="3.3", make it a NamedTempFile::new()?;
|
||||
// or alternatively use piped stdout to avoid files entirely
|
||||
let output_filepath: &str = "./pandoc_mediawiki.txt";
|
||||
pandoc_convert(
|
||||
old_pad_content,
|
||||
output_filepath,
|
||||
pandoc::InputFormat::Markdown,
|
||||
pandoc::OutputFormat::MediaWiki,
|
||||
)
|
||||
.expect("Fehler beim Umwandeln des und speichern des Pads in eine mediawiki-Textdatei");
|
||||
read_txt_file(output_filepath)
|
||||
pandoc_convert(old_pad_content, output_filepath, pandoc::InputFormat::Markdown, pandoc::OutputFormat::MediaWiki).expect("Fehler beim Umwandeln des und speichern des Pads in eine mediawiki-Textdatei");
|
||||
let temp = read_txt_file(output_filepath);
|
||||
println!("TEMP: {}", temp.purple());
|
||||
temp
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue