2024-08-05 19:29:02 +02:00
use std ::error ::Error ;
use std ::fs ::File ;
use std ::io ::Read ;
2024-08-11 22:44:28 +02:00
use colored ::Colorize ;
2024-08-03 17:13:31 +02:00
use pandoc ::{ PandocError , PandocOutput } ;
use reqwest ::blocking ::Client ;
use serde ::Deserialize ;
2024-08-05 19:29:02 +02:00
use crate ::config_spec ::{ CfgField , CfgGroup } ;
2024-08-05 04:45:34 +02:00
pub const CONFIG : CfgGroup < 'static > = CfgGroup {
name : " wiki " ,
description : " API Settings for Mediawiki " ,
fields : & [
CfgField ::Default {
key : " server-url " ,
2024-08-07 04:17:46 +02:00
default : " https://wiki.berlin.ccc.de " ,
2024-08-05 04:45:34 +02:00
description : " Server running the wiki. " ,
} ,
CfgField ::Default {
key : " http-user " ,
default : " cccb-wiki " ,
description : " HTTP basic auth user name. " ,
} ,
2024-08-11 22:44:28 +02:00
CfgField ::Password {
key : " http-password " ,
description : " HTTP basic auth password. "
} ,
2024-08-05 04:45:34 +02:00
CfgField ::Default {
key : " api-user " ,
2024-08-11 22:44:28 +02:00
default : " PlenumBot@PlenumBot-PW2 " ,
2024-08-05 04:45:34 +02:00
description : " API Username associated with the bot account used for edits. " ,
} ,
CfgField ::Password {
key : " api-secret " ,
description : " API secret / \" password \" used for authenticating as the bot. " ,
} ,
2024-08-12 22:23:21 +02:00
CfgField ::Default {
key : " eta " ,
default : " no ETA, program never ran " ,
description : " ETA message for estimating time the program takes "
}
2024-08-05 04:45:34 +02:00
] ,
} ;
2024-08-01 18:30:14 +02:00
2024-08-07 04:17:46 +02:00
pub struct Mediawiki {
server_url : String ,
http_user : String ,
http_password : String ,
2024-08-11 22:44:28 +02:00
api_user : String ,
api_secret : String ,
2024-08-07 04:17:46 +02:00
is_dry_run : bool ,
2024-08-11 22:44:28 +02:00
login_token : String ,
csrf_token : String ,
2024-08-07 04:17:46 +02:00
client : Client ,
}
2024-08-18 02:30:39 +02:00
impl std ::fmt ::Debug for Mediawiki {
fn fmt ( & self , f : & mut std ::fmt ::Formatter < '_ > ) -> std ::fmt ::Result {
f . debug_struct ( " Mediawiki " )
. field ( " server_url " , & self . server_url )
. field ( " http_user " , & self . http_user )
. field ( " http_password " , & " ***** " )
. field ( " is_dry_run " , & self . is_dry_run )
. field ( " client " , & self . client )
. finish ( )
}
}
2024-08-12 22:23:21 +02:00
pub enum RequestType {
Get ,
Post ,
PostForEditing
}
2024-08-07 04:17:46 +02:00
impl Mediawiki {
pub fn new (
2024-08-11 22:44:28 +02:00
server_url : & str , http_auth_user : & str , http_auth_password : & str , api_user : & str , api_secret : & str , is_dry_run : bool ,
2024-08-07 04:17:46 +02:00
) -> Self {
Self {
server_url : server_url . to_string ( ) ,
http_user : http_auth_user . to_string ( ) ,
http_password : http_auth_password . to_string ( ) ,
2024-08-11 22:44:28 +02:00
api_user : api_user . to_string ( ) ,
api_secret : api_secret . to_string ( ) ,
2024-08-07 04:17:46 +02:00
is_dry_run ,
2024-08-11 22:44:28 +02:00
login_token : String ::new ( ) ,
csrf_token : String ::new ( ) ,
client : Client ::builder ( ) . cookie_store ( true ) . build ( ) . unwrap ( ) ,
2024-08-07 04:17:46 +02:00
}
}
pub fn get_login_token ( & self ) -> Result < String , Box < dyn Error > > {
let url =
2024-08-12 22:23:21 +02:00
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 ) ? ;
2024-08-11 22:44:28 +02:00
Ok ( response_deserialized . query . tokens . logintoken )
}
pub fn login ( & self ) -> Result < String , Box < dyn Error > > {
2024-08-12 22:23:21 +02:00
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 ( ) )
2024-08-11 22:44:28 +02:00
}
pub fn get_csrf_token ( & self ) -> Result < String , Box < dyn Error > > { // HAS TO BE FIXED
let url =
format! ( " {} /api.php? " , self . server_url ) ;
2024-08-12 22:23:21 +02:00
let params : Box < [ ( & str , & str ) ] > = Box ::from ( [
2024-08-11 22:44:28 +02:00
( " format " , " json " ) ,
( " meta " , " tokens " ) ,
( " formatversion " , " 2 " ) ,
2024-08-12 22:23:21 +02:00
( " 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 )
2024-08-07 04:17:46 +02:00
}
2024-08-11 22:44:28 +02:00
2024-08-12 22:23:21 +02:00
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 ( & params )
. send ( )
}
RequestType ::Post | RequestType ::PostForEditing = > {
self
. client
. post ( url )
//.basic_auth(&self.http_user, Some(&self.http_password)) ZU TESTZWECKEN ENTFERNT
. form ( & params )
. 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 )
}
2024-08-07 04:17:46 +02:00
}
2024-08-03 17:13:31 +02:00
#[ derive(Deserialize) ]
2024-08-12 22:23:21 +02:00
struct QueryResponseLogin {
2024-08-03 17:13:31 +02:00
batchcomplete : String ,
2024-08-12 22:23:21 +02:00
query : QueryTokensLogin ,
2024-08-03 17:13:31 +02:00
}
#[ derive(Deserialize) ]
2024-08-12 22:23:21 +02:00
struct QueryTokensLogin {
tokens : TokensLogin ,
2024-08-03 17:13:31 +02:00
}
#[ derive(Deserialize) ]
2024-08-12 22:23:21 +02:00
struct TokensLogin {
2024-08-03 17:13:31 +02:00
logintoken : String ,
}
2024-08-01 18:30:14 +02:00
2024-08-12 22:23:21 +02:00
#[ derive(Deserialize) ]
struct QueryResponseCsrf {
batchcomplete : bool ,
query : crate ::mediawiki ::QueryTokensCsrf ,
}
#[ derive(Deserialize) ]
struct QueryTokensCsrf {
tokens : crate ::mediawiki ::TokensCsrf ,
}
2024-08-11 22:44:28 +02:00
2024-08-12 22:23:21 +02:00
#[ 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 ) ;
2024-08-11 22:44:28 +02:00
println! ( " AUTH Success " ) ;
let login_result = wiki . login ( ) ? ;
println! ( " LOGIN Success " ) ;
let csrf_token = wiki . get_csrf_token ( ) ;
2024-08-12 22:23:21 +02:00
let csrf_token = csrf_token . unwrap_or_else ( | e | {
println! ( " Error while trying to get csrf: {:?} " , e ) ;
String ::new ( )
} ) ;
2024-08-11 22:44:28 +02:00
println! ( " CSRF Success " ) ;
2024-08-12 22:23:21 +02:00
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
2024-08-01 18:30:14 +02:00
// Textdatei wieder einlesen
2024-08-03 17:13:31 +02:00
2024-08-01 18:30:14 +02:00
// Passwörter aus Datenbank lesen (ToBeDone)
2024-08-03 17:13:31 +02:00
/*
2024-08-01 18:30:14 +02:00
let plenum_bot_user = String ::from ( " PlenumBot@PlenumBot-PW1 " ) ;
let plenum_bot_pw = String ::from ( " **OLD_API_PW_REMOVED** " ) ;
2024-08-02 22:29:22 +02:00
let login_token = login_to_mediawiki ( plenum_bot_user . clone ( ) , plenum_bot_pw . clone ( ) )
. expect ( " Fehler beim Einloggen! " ) ;
2024-08-02 21:19:54 +02:00
println! ( " plenum_bot_user: {plenum_bot_user} , plenum_bot_pw: {plenum_bot_pw} , login_token: {login_token} " )
2024-08-03 17:13:31 +02:00
* /
2024-08-11 22:44:28 +02:00
Ok ( ( ) )
2024-08-02 21:19:54 +02:00
}
2024-08-03 17:13:31 +02:00
/// Converts one file type into another using pandoc and saves the result as a txt file
2024-08-17 21:52:54 +02:00
fn pandoc_convert (
old_pad_content : String , output_filepath : & str , input_format : pandoc ::InputFormat ,
output_format : pandoc ::OutputFormat ,
) -> Result < PandocOutput , PandocError > {
2024-08-02 21:19:54 +02:00
//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 ) ) ;
2024-08-03 17:13:31 +02:00
p . set_input_format ( input_format , vec! [ ] ) ;
p . set_output ( pandoc ::OutputKind ::File ( output_filepath . parse ( ) . unwrap ( ) ) ) ;
p . set_output_format ( output_format , vec! [ ] ) ;
2024-08-02 22:29:22 +02:00
p . execute ( )
2024-08-02 21:19:54 +02:00
}
2024-08-01 18:30:14 +02:00
2024-08-11 22:44:28 +02:00
2024-08-03 17:13:31 +02:00
/// Reads a text file from a specified path and returns it as a String
2024-08-07 04:17:46 +02:00
fn read_txt_file ( filepath : & str ) -> String {
let mut file = File ::open ( filepath )
2024-08-12 22:23:21 +02:00
. unwrap_or_else ( | _ | panic! ( " Fehler beim öffnen der Textdatei mit Pfad {filepath} ! " ) ) ;
2024-08-03 17:13:31 +02:00
let mut contents = String ::new ( ) ;
2024-08-07 04:17:46 +02:00
file . read_to_string ( & mut contents )
2024-08-12 22:23:21 +02:00
. expect ( " Fehler beim auslesen der MediaWiki-Textdatei! " ) ;
contents
2024-08-03 17:13:31 +02:00
}
2024-08-02 22:29:22 +02:00
2024-08-03 17:13:31 +02:00
/// Takes a Sting in the Markdown format and returns a String in the mediawiki Format
2024-08-07 04:17:46 +02:00
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
2024-08-03 17:13:31 +02:00
let output_filepath : & str = " ./pandoc_mediawiki.txt " ;
2024-08-12 22:23:21 +02:00
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
2024-08-02 22:29:22 +02:00
}