Compare commits
	
		
			3 commits
		
	
	
		
			5b0b6513a9
			...
			c1788330f5
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| c1788330f5 | |||
| f43c0b6b5d | |||
| 0f7cf9fd4d | 
					 9 changed files with 1317 additions and 979 deletions
				
			
		
							
								
								
									
										681
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										681
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -13,8 +13,12 @@ serde_json = "1.0" | |||
| reqwest = { version = "0.11.22", features = ["json", "multipart"] } | ||||
| clap = { version = "4.4.10", features = ["derive"] } | ||||
| tokio = { version = "1", features = ["full"] } | ||||
| ratatui = "0.26.0" | ||||
| crossterm = "0.27.0" | ||||
| color-eyre = "0.6.2" | ||||
| 
 | ||||
| [features] | ||||
| default = ["avatar"] | ||||
| avatar = [] | ||||
| discord = ["avatar"] | ||||
| fedi = ["avatar"] | ||||
|  |  | |||
							
								
								
									
										292
									
								
								src/api.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										292
									
								
								src/api.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,292 @@ | |||
| pub mod pk { | ||||
| 
 | ||||
|     use std::collections::HashMap; | ||||
| 
 | ||||
|     use reqwest::header::{USER_AGENT, AUTHORIZATION}; | ||||
| 
 | ||||
|     use serde_json::Value; | ||||
| 
 | ||||
|     use crate::api::request::*; | ||||
|     use crate::systemdata::*; | ||||
| 
 | ||||
|     use color_eyre::eyre::{eyre, Result}; | ||||
| 
 | ||||
|     pub const PK_URL: &str = "https://api.pluralkit.me/v2"; | ||||
| 
 | ||||
|     pub fn get_system(key: &str) -> Result<Value> { | ||||
|         let url = format!("{}/systems/@me", PK_URL); | ||||
| 
 | ||||
|         let response = http_get(url,key)?; | ||||
| 
 | ||||
|         Ok(serde_json::from_str(&response)?) | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_members(key: &str, sysid: &str) -> Result<Vec<Value>> { | ||||
|         let url = format!("{}/systems/{}/members", PK_URL, sysid); | ||||
| 
 | ||||
|         let response = http_get(url,key)?; | ||||
| 
 | ||||
|         let values: Vec<Value> = serde_json::from_str(&response)?; | ||||
| 
 | ||||
|         Ok(values) | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_fronters(key: &str, sys: &System) -> Result<Vec<Member>> { | ||||
|         let url = format!("{}/systems/{}/fronters", PK_URL, sys.pk_userid); | ||||
| 
 | ||||
|         let result = http_get(url,key)?; | ||||
|         let json: Value = serde_json::from_str(&result).unwrap(); | ||||
|         let json_members = &json["members"].as_array(); | ||||
| 
 | ||||
|         let mut members: Vec<Member> = Vec::new(); | ||||
|         for member in json_members { | ||||
|             for m in member.into_iter() { | ||||
|                 for dbmem in &sys.members { | ||||
|                     if m["name"].as_str().unwrap().to_string() == dbmem.name { | ||||
|                         members.push(dbmem.clone()); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         Ok(members) | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     pub fn set_fronters(key: &str, sys: &System, to_front: &Vec<Member>, fronters: &Fronters) -> Result<()> { | ||||
|         let url = format!("{}/systems/{}/switches", PK_URL, sys.pk_userid); | ||||
| 
 | ||||
|         if to_front != &fronters.pk { | ||||
|             let mut frontcodes = Vec::new(); | ||||
|             for tf in to_front { | ||||
|                 frontcodes.push(String::from(&tf.pk_id)); | ||||
|             } | ||||
| 
 | ||||
|             let mut body: HashMap<&str, Vec<String>> = HashMap::new(); | ||||
|             body.insert("members", frontcodes); | ||||
| 
 | ||||
|             let client = reqwest::Client::new(); | ||||
|             let rb = client | ||||
|                 .post(url) | ||||
|                 .json(&body) | ||||
|                 .header("content-type", "application/json; charset=utf-8") | ||||
|                 .header(USER_AGENT, "Pluralsync") | ||||
|                 .header(AUTHORIZATION, key); | ||||
| 
 | ||||
|             match http_request(rb) { | ||||
|                 Ok(_) => Ok(()), | ||||
|                 Err(e) => Err(eyre!("{}", e.to_string())), | ||||
|             } | ||||
| 
 | ||||
|         } else { | ||||
|             println!("Members already fonting"); | ||||
|             Ok(()) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub mod sp { | ||||
| 
 | ||||
|     use std::collections::HashMap; | ||||
| 
 | ||||
|     use reqwest::header::{USER_AGENT, AUTHORIZATION}; | ||||
| 
 | ||||
|     use serde::Serialize; | ||||
|     use serde_json::Value; | ||||
|     use crate::api::request::*; | ||||
|     use crate::systemdata::*; | ||||
| 
 | ||||
|     use color_eyre::eyre::{eyre, Result}; | ||||
| 
 | ||||
|     pub const SP_URL: &str = "https://api.apparyllis.com/v1"; | ||||
| 
 | ||||
|     pub fn get_user_id(key: &str) -> Result<String> { | ||||
|         let url = format!("{}/me", SP_URL); | ||||
| 
 | ||||
|         let response = http_get(url, key)?; | ||||
| 
 | ||||
|         let json: Value = serde_json::from_str(&response)?; | ||||
| 
 | ||||
|         Ok(json["id"].as_str().unwrap().to_string()) | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_member_ids(key: &str, system_id: &str) -> Result<HashMap<String, String>> { | ||||
|         let url = format!("{}/members/{}", SP_URL, system_id); | ||||
| 
 | ||||
|         let response = http_get(url,key)?; | ||||
|         let json: Vec<Value> = serde_json::from_str(&response)?; | ||||
| 
 | ||||
|         let mut sp_member_data: HashMap<String, String> = HashMap::new(); | ||||
|         for member in json { | ||||
|             sp_member_data.insert( | ||||
|                 member["content"]["name"].as_str().unwrap().to_string(), | ||||
|                 member["id"].as_str().unwrap().to_string()); | ||||
|         } | ||||
| 
 | ||||
|         Ok(sp_member_data) | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_member_id(m: &Member, ids: &HashMap<String, String>) -> Result<String> { | ||||
| 
 | ||||
|         for (sp_name, sp_id) in ids { | ||||
|             if &m.name == sp_name || m.aliases.iter().any(|e| sp_name.contains(e)) { | ||||
|                 return Ok(sp_id.to_string()); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         Err(eyre!(format!("SP ID not found for {}", &m.name))) | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_fronters(key: &str, sys: &System) -> Result<Vec<Member>> { | ||||
|         let url = format!("{}/fronters", SP_URL); | ||||
| 
 | ||||
|         let result = http_get(url,key)?; | ||||
|         let json: Vec<Value> = serde_json::from_str(&result)?; | ||||
| 
 | ||||
|         let mut members = Vec::new(); | ||||
|         for data in json { | ||||
|             let sp_id = &data["content"]["member"].as_str().unwrap().to_string(); | ||||
|             for member in &sys.members { | ||||
|                 if &member.sp_id == sp_id { | ||||
|                     members.push(member.clone()); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
|         Ok(members) | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_front_id(key: &str, to_id: &Member) -> Result<String> { | ||||
|         let url = format!("{}/fronters", SP_URL); | ||||
| 
 | ||||
|         let result = http_get(url,key)?; | ||||
|         let datas: Vec<Value> = serde_json::from_str(&result)?; | ||||
| 
 | ||||
|         for data in datas { | ||||
|             let sp_f_id = &data["id"].to_string(); | ||||
|             let sp_id = &data["content"]["member"].to_string(); | ||||
|             if sp_id.to_string() == to_id.sp_id { | ||||
|                 return Ok(sp_f_id.to_string()); | ||||
|             } | ||||
|         } | ||||
|         Err(eyre!("SP ID not found")) | ||||
|     } | ||||
| 
 | ||||
|     pub fn set_fronters(key: &str, to_front: &Vec<Member>, fronters: &Fronters) -> Result<()> { | ||||
|         if to_front == &fronters.sp { | ||||
|             println!("Members already fonting"); | ||||
|             return Ok(()); | ||||
|         } | ||||
| 
 | ||||
|         for fronting_member in &fronters.sp { | ||||
|             if !to_front.contains(&fronting_member) { | ||||
|                 let f_id = get_front_id(&key, &fronting_member)?; | ||||
| 
 | ||||
|                 let url = format!("{}/frontHistory/{}", SP_URL, f_id); | ||||
| 
 | ||||
|                 #[derive (Serialize)] | ||||
|                 #[allow (non_snake_case)] | ||||
|                 struct SpRem { | ||||
|                     live: bool, | ||||
|                     endTime: u64 | ||||
|                 } | ||||
| 
 | ||||
|                 let end_time = std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH)?.as_secs(); | ||||
|                 let rem = SpRem { | ||||
|                     live: false, | ||||
|                     endTime: end_time * 1000 | ||||
|                 }; | ||||
| 
 | ||||
|                 let body = serde_json::to_string(&rem)?; | ||||
| 
 | ||||
| 
 | ||||
|                 let client = reqwest::Client::new(); | ||||
|                 let rb = client | ||||
|                     .patch(url) | ||||
|                     .body(body) | ||||
|                     .header("content-type", "application/json; charset=utf-8") | ||||
|                     .header(USER_AGENT, "Pluralsync") | ||||
|                     .header(AUTHORIZATION, key); | ||||
| 
 | ||||
|                 match http_request(rb) { | ||||
|                     Ok(_) => (), | ||||
|                     Err(e) => println!("{}", e.to_string()), | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         for tf_member in to_front { | ||||
|             if !fronters.sp.contains(&tf_member) { | ||||
|                 let url = format!("{}/frontHistory", SP_URL); | ||||
| 
 | ||||
|                 #[derive (Serialize)] | ||||
|                 #[allow (non_snake_case)] | ||||
|                 struct SpAdd { | ||||
|                     member: String, | ||||
|                     custom: bool, | ||||
|                     live: bool, | ||||
|                     startTime: u64 | ||||
|                 } | ||||
| 
 | ||||
|                 let start_time = std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH)?.as_secs(); | ||||
|                 let rem = SpAdd { | ||||
|                     member: String::from(&tf_member.sp_id), | ||||
|                     custom: false, | ||||
|                     live: true, | ||||
|                     startTime: start_time * 1000 | ||||
|                 }; | ||||
| 
 | ||||
|                 let body = serde_json::to_string(&rem)?; | ||||
| 
 | ||||
|                 let client = reqwest::Client::new(); | ||||
|                 let rb = client | ||||
|                     .post(url) | ||||
|                     .body(body) | ||||
|                     .header("content-type", "application/json; charset=utf-8") | ||||
|                     .header(USER_AGENT, "Pluralsync") | ||||
|                     .header(AUTHORIZATION, key); | ||||
| 
 | ||||
|                 match http_request(rb) { | ||||
|                     Ok(_) => (), | ||||
|                     Err(e) => println!("{}", e.to_string()), | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub mod request { | ||||
| 
 | ||||
|     use reqwest::RequestBuilder; | ||||
|     use reqwest::header::{USER_AGENT, AUTHORIZATION}; | ||||
|     #[cfg(feature = "fedi")] | ||||
|     use reqwest::multipart::{Part, Form}; | ||||
| 
 | ||||
|     use color_eyre::eyre::Result; | ||||
| 
 | ||||
| 
 | ||||
|     #[tokio::main] | ||||
|     pub async fn http_get(url: String, key: &str) -> Result<String> { | ||||
|         let client = reqwest::Client::new(); | ||||
|         let rb = client | ||||
|             .get(url) | ||||
|             .header(USER_AGENT, "Pluralsync") | ||||
|             .header(AUTHORIZATION, key); | ||||
|         let res = rb.send() | ||||
|             .await? | ||||
|             .text() | ||||
|             .await?; | ||||
| 
 | ||||
|         Ok(res) | ||||
|     } | ||||
| 
 | ||||
|     #[tokio::main] | ||||
|     pub async fn http_request(rb: RequestBuilder) -> Result<String> { | ||||
|         let res = rb.send() | ||||
|             .await? | ||||
|             .text() | ||||
|             .await?; | ||||
|         Ok(res) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										186
									
								
								src/app.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								src/app.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,186 @@ | |||
| use crate::configdata::*; | ||||
| use crate::systemdata::*; | ||||
| use crate::clap_ps::ForceFrom; | ||||
| 
 | ||||
| use color_eyre::eyre::{eyre, Result}; | ||||
| 
 | ||||
| pub enum CurrentScreen { | ||||
|     Dash, | ||||
|     Configuration, | ||||
|     Exit, | ||||
| } | ||||
| 
 | ||||
| pub enum ConfigEdit { | ||||
|     PkKey, | ||||
|     SpKey, | ||||
|     AvatarEnabled, | ||||
|     AvatarFolder, | ||||
|     AvatarOutput, | ||||
|     AvatarBlacklist, | ||||
|     DiscEnabled, | ||||
|     DiscToken, | ||||
|     FediEnabled, | ||||
|     FediInstance, | ||||
|     FediToken, | ||||
| } | ||||
| 
 | ||||
| pub struct App { | ||||
|     pub input: String, | ||||
|     pub cfg_dir: String, | ||||
|     pub cfg: Option<Config>, | ||||
|     pub sys: Option<System>, | ||||
|     pub fronters: Option<Fronters>, | ||||
|     pub current_screen: Option<CurrentScreen>, | ||||
|     pub editing: Option<ConfigEdit>, | ||||
| } | ||||
| 
 | ||||
| impl App { | ||||
|     pub fn new(confpath: String) -> App { | ||||
|         App { | ||||
|             input: String::new(), | ||||
|             cfg_dir: confpath, | ||||
|             cfg: None, | ||||
|             sys: None, | ||||
|             fronters: None, | ||||
|             current_screen: None, | ||||
|             editing: None, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn load_system(&mut self, update: bool) -> Result<()> { | ||||
| 
 | ||||
|         match System::get_system(&self, update) { | ||||
|             Ok(s) => { | ||||
|                 self.sys = Some(s); | ||||
|                 Ok(()) | ||||
|             }, | ||||
|             Err(e) => { | ||||
|                 self.sys = None; | ||||
|                 Err(e) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn load_config(&mut self) -> Result<()> { | ||||
| 
 | ||||
|         match Config::get_config(&self.cfg_dir) { | ||||
|             Ok(c) => { | ||||
|                 self.cfg = Some(c); | ||||
|                 Ok(()) | ||||
|             }, | ||||
|             Err(e) => { | ||||
|                 self.cfg = None; | ||||
|                 Err(e) | ||||
|             }, | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     fn get(&mut self, ff: ForceFrom) -> Result<()> { | ||||
| 
 | ||||
|         self.fronters = Some(Fronters::get(self, ff)?); | ||||
| 
 | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     fn set(&mut self, to_front: Vec<Member>) -> Result<()> { | ||||
| 
 | ||||
|         self.get(ForceFrom::None)?; | ||||
| 
 | ||||
|         self.fronters = Some(Fronters::set(self, to_front)?); | ||||
| 
 | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     fn add(&mut self, to_front: Vec<Member>) -> Result<()> { | ||||
| 
 | ||||
|         let aux_fronters: Fronters = Fronters::get(self, ForceFrom::None)?; | ||||
| 
 | ||||
|         let mut addfronters: Vec<Member> = Vec::new(); | ||||
|         addfronters.append(&mut aux_fronters.pk.clone()); | ||||
|         addfronters.append(&mut to_front.clone()); | ||||
| 
 | ||||
|         self.fronters = Some(Fronters::set(self, addfronters)?); | ||||
| 
 | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     pub fn memberlist(&self) -> Result<()> { | ||||
| 
 | ||||
|         if let Some(sys) = &self.sys { | ||||
|             for member in &sys.members { | ||||
|                 println!("{} / {}", member.name, member.aliases.join(" | ")); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     pub fn set_member(&mut self, tf_members: Vec<String>, append: bool) -> Result<()> { | ||||
| 
 | ||||
|         if let Some(sys) = &self.sys { | ||||
|             let mut to_front: Vec<Member> = Vec::new(); | ||||
|             for member in &tf_members { | ||||
|                 for mem in &sys.members { | ||||
|                     let lowcase_aliases: Vec<String> = mem.aliases.iter().map(|x| x.to_lowercase()).collect(); | ||||
|                     if mem.name.to_lowercase() == member.to_lowercase() || lowcase_aliases.iter().any(|e| member.contains(e)) { | ||||
|                         println!("Member {member} found."); | ||||
| 
 | ||||
|                         to_front.push(mem.clone()); | ||||
| 
 | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if to_front.len() == tf_members.len() { | ||||
|                 if !append { | ||||
|                     self.set(to_front)?; | ||||
|                 } else { | ||||
|                     self.add(to_front)?; | ||||
|                 } | ||||
|             } else { | ||||
|                 println!("One or more members were not found. Known members:\n--------------------------"); | ||||
|                 self.memberlist()?; | ||||
|                 println!("--------------------------\nIf a member is missing from the system try running \"pluralsync update\" to refresh the local database"); | ||||
|                 return Err(eyre!("Missing member")); | ||||
|             } | ||||
| 
 | ||||
|             #[cfg(feature = "jlog")] { | ||||
|                 let mut names = Vec::new(); | ||||
|                 for m in &to_front { | ||||
|                     names.push(String::from(&m.name)); | ||||
|                 } | ||||
|                 let log_fronters = names.join(" || "); | ||||
| 
 | ||||
|                 #[cfg(target_os = "windows")] | ||||
|                 std::process::Command::new("jlog").args(["info", format!("Switch registered: {}", log_fronters)]).output().expect("Logging error"); | ||||
| 
 | ||||
|                 #[cfg(not(target_os = "windows"))] | ||||
|                 std::process::Command::new("jlog").arg("info").arg(format!("Switch registered: {}", log_fronters)).output().expect("Logging error"); | ||||
|             } | ||||
| 
 | ||||
|             return Ok(()); | ||||
|         } | ||||
|         Err(eyre!("No system found")) | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_front(&mut self, ff: ForceFrom) -> Result<Vec<String>> { | ||||
| 
 | ||||
|         self.get(ff)?; | ||||
| 
 | ||||
|         if let Some(fronters) = &self.fronters { | ||||
|             let mut names: Vec<String> = Vec::new(); | ||||
|             for member in &fronters.pk { | ||||
|                 names.push(String::from(&member.name)); | ||||
|             } | ||||
| 
 | ||||
|             let fronters = names.join(" || "); | ||||
|             println!("Currently fronting: {}", fronters); | ||||
|             std::fs::write(format!("{}/.front", &self.cfg_dir), &fronters)?; | ||||
| 
 | ||||
|             return Ok(names); | ||||
|         } | ||||
|         Err(eyre!("No fronters found")) | ||||
|     } | ||||
| } | ||||
|  | @ -19,10 +19,13 @@ pub struct Args { | |||
| 
 | ||||
| #[derive(Subcommand)] | ||||
| pub enum Commands { | ||||
|     ///Fetches the system member information from PluralKit and SimplyPlural
 | ||||
|     Sync, | ||||
|     /// Fetches the system member information from PluralKit and SimplyPlural
 | ||||
|     Update, | ||||
|     /// TUI configuration tool
 | ||||
|     TUI, | ||||
|     /// Sets one or more members to the front
 | ||||
|     #[clap(arg_required_else_help = true)] | ||||
|     #[non_exhaustive] | ||||
|     Set { | ||||
|         /// Members to set
 | ||||
|         members: Vec<String>, | ||||
|  |  | |||
|  | @ -1,6 +1,16 @@ | |||
| use serde::{Serialize, Deserialize}; | ||||
| use std::{ | ||||
|     fs::create_dir, | ||||
|     path::Path, | ||||
| }; | ||||
| 
 | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| use crate::utils::*; | ||||
| 
 | ||||
| use serde::{Serialize, Deserialize}; | ||||
| use serde_json::Value; | ||||
| 
 | ||||
| use color_eyre::eyre::{eyre, Result}; | ||||
| 
 | ||||
| #[derive(Debug, Serialize, Deserialize, Clone)] | ||||
| pub struct Config { | ||||
|     pub pk_key: String, | ||||
|     pub sp_key: String, | ||||
|  | @ -13,7 +23,7 @@ pub struct Config { | |||
| } | ||||
| 
 | ||||
| #[cfg(feature = "avatar")] | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| #[derive(Debug, Serialize, Deserialize, Clone)] | ||||
| pub struct AvatarModule { | ||||
|     pub enabled: bool, | ||||
|     pub avatar_folder: String, | ||||
|  | @ -22,7 +32,7 @@ pub struct AvatarModule { | |||
| } | ||||
| 
 | ||||
| #[cfg(feature = "discord")] | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| #[derive(Debug, Serialize, Deserialize, Clone)] | ||||
| pub struct DiscModule { | ||||
|     pub enabled: bool, | ||||
|     pub token: String, | ||||
|  | @ -31,9 +41,29 @@ pub struct DiscModule { | |||
| } | ||||
| 
 | ||||
| #[cfg(feature = "fedi")] | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| #[derive(Debug, Serialize, Deserialize, Clone)] | ||||
| pub struct FediModule { | ||||
|     pub enabled: bool, | ||||
|     pub instance: String, | ||||
|     pub token: String, | ||||
| } | ||||
| 
 | ||||
| impl Config { | ||||
|     pub fn get_config(cfg_dir: &str) -> Result<Config> { | ||||
| 
 | ||||
|         let cfg_file_path = format!("{}/config.json", cfg_dir); | ||||
| 
 | ||||
|         if !Path::new(&cfg_dir).exists() { | ||||
|             let _ = create_dir(cfg_dir); | ||||
|         } | ||||
| 
 | ||||
|         if Path::new(&cfg_file_path).exists() { | ||||
|             let json_config: Value = load_json(&cfg_file_path)?; | ||||
|             let conf: Config = serde_json::from_value(json_config)?; | ||||
|             Ok(conf) | ||||
|         } else { | ||||
|             // TODO Trigger file creation screen if there is no config file.
 | ||||
|             Err(eyre!("Config file not found")) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										831
									
								
								src/main.rs
									
										
									
									
									
								
							
							
						
						
									
										831
									
								
								src/main.rs
									
										
									
									
									
								
							|  | @ -1,839 +1,74 @@ | |||
| use std::fs; | ||||
| #[cfg(feature = "avatar")] | ||||
| use std::fs::remove_file; | ||||
| use std::fs::create_dir; | ||||
| use std::path::Path; | ||||
| use std::collections::HashMap; | ||||
| #[cfg(feature = "avatar")] | ||||
| use std::process::Command; | ||||
| 
 | ||||
| use reqwest::RequestBuilder; | ||||
| use reqwest::header::{USER_AGENT, AUTHORIZATION}; | ||||
| #[cfg(feature = "fedi")] | ||||
| use reqwest::multipart::{Part, Form}; | ||||
| 
 | ||||
| use serde::Serialize; | ||||
| use serde_json::Value; | ||||
| mod app; | ||||
| mod configdata; | ||||
| mod systemdata; | ||||
| mod api; | ||||
| mod utils; | ||||
| mod clap_ps; | ||||
| 
 | ||||
| use dirs; | ||||
| 
 | ||||
| mod configdata; | ||||
| use configdata::*; | ||||
| use app::*; | ||||
| use utils::update_avatars; | ||||
| 
 | ||||
| mod systemdata; | ||||
| use systemdata::*; | ||||
| 
 | ||||
| mod clap_ps; | ||||
| use clap_ps::*; | ||||
| use clap::Parser; | ||||
| 
 | ||||
| const PK_URL: &str = "https://api.pluralkit.me/v2"; | ||||
| const SP_URL: &str = "https://api.apparyllis.com/v1"; | ||||
| use color_eyre::eyre::{eyre, Result}; | ||||
| 
 | ||||
| const EXAMPLE_JSON: &str = r#"{
 | ||||
|   "pk_key": "// Pluralkit token", | ||||
|   "sp_key": "// Simplplural token", | ||||
|   "avatar_module": { | ||||
|     "enabled": false, | ||||
|     "avatar_folder": "// Folder to grab profile pictures, they follow they member1member2...memberX.png format", | ||||
|     "avatar_output_path": "// Path for the copied selected avatar", | ||||
|     "blacklist": "// Array of members to not include in the avatar module" | ||||
|   }, | ||||
|   "disc_module": { | ||||
|     "enabled": false, | ||||
|     "token": "// Discord user token", | ||||
|     "python_path": "// Path to the python executable", | ||||
|     "script_path": "// Path to updatediscordavatar.py" | ||||
|   }, | ||||
|   "fedi_module": { | ||||
|     "enabled": false, | ||||
|     "instance" : "// Fedi instance url", | ||||
|     "token": "// Fedi bearer token" | ||||
|   } | ||||
| }"#;
 | ||||
| 
 | ||||
| fn main() { | ||||
| fn main() -> Result<()> { | ||||
| 
 | ||||
|     let cli = Args::parse(); | ||||
| 
 | ||||
|     let config_path = match dirs::config_dir() { | ||||
|         Some(d) => format!("{}/pluralsync", d.display()), | ||||
|         None => { | ||||
|             println!("Could not fetch the config directory"); | ||||
|             return (); | ||||
|             return Err(eyre!("Could not figure out the config directory!")); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     let mut res: Result<(), &str> = Ok(()); | ||||
|     let mut app: App = App::new(config_path.clone()); | ||||
|     app.load_config()?; | ||||
|     app.load_system(false)?; | ||||
| 
 | ||||
|     match cli.cmd { | ||||
|         Commands::Sync => { | ||||
|             res = sync(config_path); | ||||
|         Commands::Update => { | ||||
|             app.load_system(true)?; | ||||
|         }, | ||||
| 
 | ||||
|         // SET MEMBER
 | ||||
|         #[cfg(all(feature = "discord", feature = "fedi"))] | ||||
|         Commands::Set { members, discord, fedi } => { | ||||
|             res = set_member(config_path.clone(), members); | ||||
| 
 | ||||
|             #[cfg(feature = "avatar")] { | ||||
|                 match res { | ||||
|                     Ok(_) => { | ||||
|                         let _ = update_avatars(config_path.clone(), discord, fedi); | ||||
|                     }, | ||||
|                     Err(_) => (), | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         #[cfg(all(feature = "discord", not(feature = "fedi")))] | ||||
|         Commands::Set { members, discord } => { | ||||
|             res = set_member(config_path.clone(), members); | ||||
| 
 | ||||
|             #[cfg(feature = "avatar")] { | ||||
|                 match res { | ||||
|                     Ok(_) => { | ||||
|                         let _ = update_avatars(config_path.clone(), discord, false); | ||||
|                     }, | ||||
|                     Err(_) => (), | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         #[cfg(all(not(feature = "discord"), feature = "fedi"))] | ||||
|         Commands::Set { members, fedi} => { | ||||
|             res = set_member(config_path.clone(), members); | ||||
| 
 | ||||
|             #[cfg(feature = "avatar")] { | ||||
|                 match res { | ||||
|                     Ok(_) => { | ||||
|                         let _ = update_avatars(config_path.clone(), false, fedi); | ||||
|                     }, | ||||
|                     Err(_) => (), | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         #[cfg(all(not(feature = "discord"), not(feature = "fedi")))] | ||||
|         Commands::Set { members } => { | ||||
|             res = set_member(config_path.clone(), members); | ||||
| 
 | ||||
|             #[cfg(feature = "avatar")] { | ||||
|                 match res { | ||||
|                     Ok(_) => { | ||||
|                         let _ = update_avatars(config_path.clone(), false, false); | ||||
|                     }, | ||||
|                     Err(_) => (), | ||||
|                 } | ||||
|             } | ||||
|         Commands::TUI => { | ||||
|             () | ||||
|         }, | ||||
| 
 | ||||
|         // ADD MEMBER
 | ||||
|         #[cfg(all(feature = "discord", feature = "fedi"))] | ||||
|         Commands::Add { members, discord, fedi } => { | ||||
|             res = add_member(config_path.clone(), members); | ||||
|         Commands::Set { members, #[cfg(feature = "discord")] discord, #[cfg(feature = "fedi")] fedi } => { | ||||
|             app.set_member(members, false)?; | ||||
| 
 | ||||
|             #[cfg(feature = "avatar")] { | ||||
|                 match res { | ||||
|                     Ok(_) => { | ||||
|                         let _ = update_avatars(config_path.clone(), discord, fedi); | ||||
|                     }, | ||||
|                     Err(_) => (), | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         #[cfg(all(feature = "discord", not(feature = "fedi")))] | ||||
|         Commands::Add { members, discord } => { | ||||
|             res = add_member(config_path.clone(), members); | ||||
| 
 | ||||
|             #[cfg(feature = "avatar")] { | ||||
|                 match res { | ||||
|                     Ok(_) => { | ||||
|                         let _ = update_avatars(config_path.clone(), discord, false); | ||||
|                     }, | ||||
|                     Err(_) => (), | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         #[cfg(all(not(feature = "discord"), feature = "fedi"))] | ||||
|         Commands::Add { members, fedi} => { | ||||
|             res = add_member(config_path.clone(), members); | ||||
| 
 | ||||
|             #[cfg(feature = "avatar")] { | ||||
|                 match res { | ||||
|                     Ok(_) => { | ||||
|                         let _ = update_avatars(config_path.clone(), false, fedi); | ||||
|                     }, | ||||
|                     Err(_) => (), | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         #[cfg(all(not(feature = "discord"), not(feature = "fedi")))] | ||||
|         Commands::Add { members } => { | ||||
|             res = add_member(config_path.clone(), members); | ||||
| 
 | ||||
|             #[cfg(feature = "avatar")] { | ||||
|                 match res { | ||||
|                     Ok(_) => { | ||||
|                         let _ = update_avatars(config_path.clone(), false, false); | ||||
|                     }, | ||||
|                     Err(_) => (), | ||||
|                 } | ||||
|             } | ||||
|             #[cfg(feature = "avatar")] | ||||
|             update_avatars(&mut app, #[cfg(feature = "discord")] discord, #[cfg(feature = "fedi")] fedi)?; | ||||
|         }, | ||||
| 
 | ||||
|         // Get MEMBER
 | ||||
|         #[cfg(all(feature = "discord", feature = "fedi"))] | ||||
|         Commands::Get { force_from, discord, fedi } => { | ||||
|         Commands::Add { members, #[cfg(feature = "discord")] discord, #[cfg(feature = "fedi")] fedi } => { | ||||
|             app.set_member(members, true)?; | ||||
| 
 | ||||
|             #[cfg(feature = "avatar")] | ||||
|             update_avatars(&mut app, #[cfg(feature = "discord")] discord, #[cfg(feature = "fedi")] fedi)?; | ||||
|         }, | ||||
| 
 | ||||
|         Commands::Get { force_from, #[cfg(feature = "discord")] discord, #[cfg(feature = "fedi")] fedi } => { | ||||
|             let ff = match force_from { | ||||
|                 Some(x) => x, | ||||
|                 None => ForceFrom::None, | ||||
|             }; | ||||
| 
 | ||||
|             let _ = get(config_path.clone(), ff); | ||||
|             app.get_front(ff)?; | ||||
| 
 | ||||
|             #[cfg(feature = "avatar")] | ||||
|             let _ = update_avatars(config_path.clone(), discord, fedi); | ||||
|         }, | ||||
|         #[cfg(all(feature = "discord", not(feature = "fedi")))] | ||||
|         Commands::Get { force_from, discord } => { | ||||
|             let ff = match force_from { | ||||
|                 Some(x) => x, | ||||
|                 None => ForceFrom::None, | ||||
|             }; | ||||
| 
 | ||||
|             let _ = get(config_path.clone(), ff); | ||||
| 
 | ||||
|             #[cfg(feature = "avatar")] | ||||
|             let _ = update_avatars(config_path.clone(), discord, false); | ||||
|         }, | ||||
|         #[cfg(all(not(feature = "discord"), feature = "fedi"))] | ||||
|         Commands::Get { force_from, fedi} => { | ||||
|             let ff = match force_from { | ||||
|                 Some(x) => x, | ||||
|                 None => ForceFrom::None, | ||||
|             }; | ||||
| 
 | ||||
|             let _ = get(config_path.clone(), ff); | ||||
| 
 | ||||
|             #[cfg(feature = "avatar")] | ||||
|             let _ = update_avatars(config_path.clone(), false, fedi); | ||||
|         }, | ||||
|         #[cfg(all(not(feature = "discord"), not(feature = "fedi")))] | ||||
|         Commands::Get { force_from } => { | ||||
|             let ff = match force_from { | ||||
|                 Some(x) => x, | ||||
|                 None => ForceFrom::None, | ||||
|             }; | ||||
| 
 | ||||
|             let _ = get(config_path.clone(), ff); | ||||
| 
 | ||||
|             #[cfg(feature = "avatar")] | ||||
|             let _ = update_avatars(config_path.clone(), false, false); | ||||
|             update_avatars(&mut app, #[cfg(feature = "discord")] discord, #[cfg(feature = "fedi")] fedi)?; | ||||
|         }, | ||||
| 
 | ||||
|         Commands::Members => { | ||||
|             res = memberlist(config_path); | ||||
|             app.memberlist()?; | ||||
|         }, | ||||
|     } | ||||
| 
 | ||||
|     match res { | ||||
|         Ok(_) => (), | ||||
|         Err(e) => println!("{}", e), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn sync(config_path: String) -> Result<(), &'static str> { | ||||
|     // Get config
 | ||||
|     let config: Config = match get_config(&config_path) { | ||||
|         Ok(c) => c, | ||||
|         Err(e) => return Err(e) | ||||
|     }; | ||||
| 
 | ||||
|     // Get Pluralkit system id
 | ||||
|     let pk_sys = pk_get_system(&config.pk_key); | ||||
|     let pk_sysid = pk_sys["id"].as_str().unwrap(); | ||||
| 
 | ||||
|     // Get Simplyplural user id
 | ||||
|     let sp_user_id = sp_get_userid(&config.sp_key); | ||||
| 
 | ||||
|     // Get Simplyplural member ids
 | ||||
|     let sp_member_ids = sp_get_memberids(&config.sp_key, &sp_user_id); | ||||
| 
 | ||||
|     // get members
 | ||||
|     let pk_members = pk_get_members(&config.pk_key, pk_sysid); | ||||
|     let mut members: Vec<Member> = Vec::new(); | ||||
|     for member in pk_members { | ||||
|         let mut m = Member { | ||||
|             pk_id: member["id"].as_str().unwrap().to_string(), | ||||
|             sp_id: String::new(), | ||||
|             name: member["name"].as_str().unwrap().to_string(), | ||||
|             alias: String::new() | ||||
|         }; | ||||
| 
 | ||||
|         if member["display_name"].as_str() != None { | ||||
|             m.alias = member["display_name"].as_str().unwrap().to_string(); | ||||
|         } else { | ||||
|             m.alias = String::from(&m.name); | ||||
|         } | ||||
| 
 | ||||
|         m.sp_id = get_sp_id(&m, &sp_member_ids); | ||||
| 
 | ||||
|         members.push(m); | ||||
|     } | ||||
| 
 | ||||
|     let sys = System { | ||||
|         pk_userid: pk_sysid.to_string(), | ||||
|         sp_userid: sp_user_id, | ||||
|         members: members.clone(), | ||||
|     }; | ||||
| 
 | ||||
|     let json = serde_json::to_string(&sys); | ||||
|     let _ = fs::write(format!("{}/system.json", config_path), &json.unwrap()); | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| fn set_member(config_path: String, tf_members: Vec<String>) -> Result<(), &'static str> { | ||||
|     let config: Config = match get_config(&config_path) { | ||||
|         Ok(c) => c, | ||||
|         Err(e) => return Err(e) | ||||
|     }; | ||||
| 
 | ||||
|     let system: System = get_system(&config_path); | ||||
| 
 | ||||
|     let mut to_front: Vec<Member> = Vec::new(); | ||||
|     for member in &tf_members { | ||||
|         for mem in &system.members { | ||||
|             if mem.name.to_lowercase() == member.to_lowercase() || mem.alias.to_lowercase() == member.to_lowercase() { | ||||
|                 println!("Member {member} found"); | ||||
|                 to_front.push(mem.clone()); | ||||
| 
 | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     if to_front.len() == tf_members.len() { | ||||
|         let fronters = get_fronters(&config.pk_key, &config.sp_key, &system, ForceFrom::None); | ||||
| 
 | ||||
|         pk_set_fronters(&config.pk_key, &system, &to_front, &fronters); | ||||
|         sp_set_fronters(&config.sp_key, &to_front, &fronters); | ||||
|     } else { | ||||
|         println!("One or more members were not found. Known members:\n--------------------------"); | ||||
|         let _ = memberlist(config_path); | ||||
|         println!("--------------------------\nIf a member is missing from the system try running \"pluralsync sync\" to refresh the local database"); | ||||
|         return Err("Missing member"); | ||||
|     } | ||||
| 
 | ||||
|     #[cfg(feature = "jlog")] { | ||||
|         let mut names = Vec::new(); | ||||
|         for m in &to_front { | ||||
|             names.push(String::from(&m.name)); | ||||
|         } | ||||
|         let log_fronters = names.join(" || "); | ||||
|         #[cfg(target_os = "windows")] | ||||
|         std::process::Command::new("jlog").args(["info", format!("Switch registered: {}", log_fronters)]).output().expect("Logging error"); | ||||
|         #[cfg(not(target_os = "windows"))] | ||||
|         std::process::Command::new("jlog").arg("info").arg(format!("Switch registered: {}", log_fronters)).output().expect("Logging error"); | ||||
|     } | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| fn add_member(config_path: String, tf_members: Vec<String>) -> Result<(), &'static str> { | ||||
|     let config: Config = match get_config(&config_path) { | ||||
|         Ok(c) => c, | ||||
|         Err(e) => return Err(e) | ||||
|     }; | ||||
| 
 | ||||
|     let system: System = get_system(&config_path); | ||||
| 
 | ||||
|     let mut to_front: Vec<Member> = Vec::new(); | ||||
|     for member in &tf_members { | ||||
|         for mem in &system.members { | ||||
|             if mem.name.to_lowercase() == member.to_lowercase() || mem.alias.to_lowercase() == member.to_lowercase() { | ||||
|                 to_front.push(mem.clone()); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     if to_front.len() == tf_members.len() { | ||||
|         let fronters = get_fronters(&config.pk_key, &config.sp_key, &system, ForceFrom::None); | ||||
| 
 | ||||
|         let mut aux: Vec<Member> = Vec::new(); | ||||
|         aux.append(&mut fronters.pk.clone()); | ||||
|         aux.append(&mut to_front); | ||||
|         to_front = aux; | ||||
| 
 | ||||
|         pk_set_fronters(&config.pk_key, &system, &to_front, &fronters); | ||||
|         sp_set_fronters(&config.sp_key, &to_front, &fronters); | ||||
| 
 | ||||
|         let _ = get(config_path, ForceFrom::None); | ||||
| 
 | ||||
|     } else { | ||||
|         println!("One or more members were not found. Known members:\n--------------------------"); | ||||
|         let _ = memberlist(config_path); | ||||
|         println!("--------------------------\nIf a member is missing from the system try running \"pluralsync sync\" to refresh the local database"); | ||||
|         return Err("Missing member"); | ||||
|     } | ||||
| 
 | ||||
|     #[cfg(feature = "jlog")] { | ||||
|         let mut names = Vec::new(); | ||||
|         for m in &to_front { | ||||
|             names.push(String::from(&m.name)); | ||||
|         } | ||||
|         let log_fronters = names.join(" || "); | ||||
|         #[cfg(target_os = "windows")] | ||||
|         std::process::Command::new("jlog").args(["info", format!("Switch registered: {}", log_fronters)]).output().expect("Logging error"); | ||||
|         #[cfg(not(target_os = "windows"))] | ||||
|         std::process::Command::new("jlog").arg("info").arg(format!("Switch registered: {}", log_fronters)).output().expect("Logging error"); | ||||
|     } | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| fn memberlist(config_path: String) -> Result<(), &'static str> { | ||||
|     let sys = get_system(&config_path); | ||||
| 
 | ||||
|     for mem in sys.members { | ||||
|         if mem.name != mem.alias { | ||||
|             println!("{} / {}", mem.name, mem.alias); | ||||
|         } else { | ||||
|             println!("{}", mem.name); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     Ok(()) | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| fn get(config_path: String, ff: ForceFrom) -> Result<Vec<String>, &'static str> { | ||||
|     let config: Config = match get_config(&config_path) { | ||||
|         Ok(c) => c, | ||||
|         Err(e) => return Err(e) | ||||
|     }; | ||||
|     let sys = get_system(&config_path); | ||||
| 
 | ||||
|     let f = get_fronters(&config.pk_key, &config.sp_key, &sys, ff); | ||||
|     let mut names = Vec::new(); | ||||
|     for m in &f.pk { | ||||
|         names.push(String::from(&m.name)); | ||||
|     } | ||||
|     let fronters = names.join(" || "); | ||||
|     println!("Currently fronting: {}", fronters); | ||||
|     let _ = fs::write(format!("{}/.front", config_path), fronters); | ||||
| 
 | ||||
|     Ok(names) | ||||
| } | ||||
| 
 | ||||
| #[cfg(feature = "avatar")] | ||||
| #[allow(unused_variables)] | ||||
| fn update_avatars(config_path: String, discord: bool, fedi: bool) -> Result<(), &'static str>{ | ||||
|     #[allow(unused_mut)] | ||||
|     let mut config: Config = match get_config(&config_path) { | ||||
|         Ok(c) => c, | ||||
|         Err(e) => return Err(e) | ||||
|     }; | ||||
| 
 | ||||
|     let names = get(config_path, ForceFrom::None).unwrap(); | ||||
| 
 | ||||
| 
 | ||||
|     #[cfg(feature = "avatar")] | ||||
|     match avatar_module(&config, &names) { | ||||
|         Ok(_) => { | ||||
|             #[cfg(feature = "discord")] { | ||||
|                 config.disc_module.enabled |= discord; | ||||
|                 discord_module(&config); | ||||
|             } | ||||
|             #[cfg(feature = "fedi")] { | ||||
|                 config.fedi_module.enabled |= fedi; | ||||
|                 fedi_module(&config); | ||||
|             } | ||||
|         }, | ||||
|         Err(e) => println!("{}", e), | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| #[cfg(feature = "avatar")] | ||||
| fn avatar_module(config: &Config, names: &Vec<String>) -> Result<(), &'static str>{ | ||||
|     if config.avatar_module.enabled { | ||||
|         let mut whitelisted_names: Vec<String> = names.iter().map(|i| i.to_lowercase()).collect(); | ||||
|         let blacklist: Vec<String> = config.avatar_module.blacklist.iter().map(|i| i.to_lowercase()).collect(); | ||||
|         for name in names { | ||||
|             if blacklist.contains(&name.to_lowercase()) { | ||||
|                 let index = whitelisted_names.iter().position(|x| *x == name.to_lowercase()).unwrap(); | ||||
|                 whitelisted_names.remove(index); | ||||
|                 println!("{} blacklisted from avatar module", name); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         let avatarnames = whitelisted_names.join("").to_lowercase() + ".png"; | ||||
| 
 | ||||
|         if Path::new(&config.avatar_module.avatar_output_path).exists() { | ||||
|             let _ = remove_file(&config.avatar_module.avatar_output_path); | ||||
|         } | ||||
| 
 | ||||
|         if cfg!(target_os = "windows") { | ||||
|             let cmdaug = format!("copy {}\\{} {}", &config.avatar_module.avatar_folder, avatarnames, &config.avatar_module.avatar_output_path); | ||||
|             Command::new("cmd").args(["/C", &cmdaug]).output().expect("Avatar module error"); | ||||
|         } else { | ||||
|             Command::new("sh").arg("-c").arg(format!("cp {}/{} {}", &config.avatar_module.avatar_folder, avatarnames, &config.avatar_module.avatar_output_path)).output().expect("Avatar module error"); | ||||
|         } | ||||
|         if Path::new(&config.avatar_module.avatar_output_path).exists() { | ||||
|             println!("Avatar module finished"); | ||||
|             return Ok(()) | ||||
|         } else { | ||||
|             return Err("Avatar module failed") | ||||
|         } | ||||
|     } else { | ||||
|         Err("Avatar mode disabled") | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #[cfg(feature = "discord")] | ||||
| fn discord_module(config: &Config) { | ||||
|     if config.disc_module.enabled { | ||||
|         if cfg!(target_os = "windows") { | ||||
|             let mut c = Command::new("cmd").args(["/C", format!("{} {}", &config.disc_module.python_path, &config.disc_module.script_path).as_str()]).spawn().expect("Discord module error"); | ||||
|             let _ = c.wait().expect("Error"); | ||||
|         } else { | ||||
|             let mut c = Command::new("sh").arg("-c").arg(format!("{} {}", &config.disc_module.python_path, &config.disc_module.script_path)).spawn().expect("Discord module error"); | ||||
|             let _ = c.wait().expect("Error"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(feature = "fedi")] | ||||
| fn fedi_module(config: &Config) { | ||||
|     if config.fedi_module.enabled { | ||||
| 
 | ||||
|         let client = reqwest::Client::new(); | ||||
|         let form = Form::new().part("avatar", Part::bytes(fs::read(config.avatar_module.avatar_output_path.clone()).unwrap()).file_name("face.png").mime_str("image/png").unwrap()); | ||||
|         let rb = client | ||||
|             .patch(config.fedi_module.instance.clone() + "/api/v1/accounts/update_credentials") | ||||
|             .multipart(form) | ||||
|             .header(USER_AGENT, "Pluralsync") | ||||
|             .header(AUTHORIZATION, format!("Bearer {}", &config.fedi_module.token).as_str()); | ||||
| 
 | ||||
|         println!("Fedi module finished"); | ||||
|         match http_request(rb) { | ||||
|             Ok(_) => (), | ||||
|             Err(e) => println!("{}", e.to_string()), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn pk_get_system(key: &str) -> Value { | ||||
|     let url = format!("{}/systems/@me", PK_URL); | ||||
| 
 | ||||
|     let res = http_get(url,key); | ||||
|     return serde_json::from_str(&res.unwrap()).unwrap(); | ||||
| } | ||||
| 
 | ||||
| fn pk_get_members(key: &str, sysid: &str) -> Vec<Value> { | ||||
|     let url = format!("{}/systems/{}/members", PK_URL, sysid); | ||||
| 
 | ||||
|     let res = http_get(url,key); | ||||
|     let datas: Vec<Value> = serde_json::from_str(&res.unwrap()).unwrap(); | ||||
| 
 | ||||
|     return datas; | ||||
| } | ||||
| 
 | ||||
| fn pk_get_fronters(key: &str, sys: &System) -> Vec<Member> { | ||||
|     let url = format!("{}/systems/{}/fronters", PK_URL, sys.pk_userid); | ||||
| 
 | ||||
|     let res = http_get(url,key); | ||||
|     let data: Value = serde_json::from_str(&res.unwrap()).unwrap(); | ||||
|     let memberdata = &data["members"].as_array(); | ||||
| 
 | ||||
|     let mut members: Vec<Member> = Vec::new(); | ||||
|     for member in memberdata { | ||||
|         for m in member.into_iter() { | ||||
|             for dbmem in &sys.members { | ||||
|                 if m["name"].as_str().unwrap() == dbmem.name { | ||||
|                     members.push(dbmem.clone()); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return members; | ||||
| } | ||||
| 
 | ||||
| fn pk_set_fronters(key: &str, sys: &System, to_front: &Vec<Member>, fronters: &Fronters) { | ||||
|     let url = format!("{}/systems/{}/switches", PK_URL, sys.pk_userid); | ||||
| 
 | ||||
|     if to_front != &fronters.pk { | ||||
|         let mut frontcodes = Vec::new(); | ||||
|         for tf in to_front { | ||||
|             frontcodes.push(String::from(&tf.pk_id)); | ||||
|         } | ||||
| 
 | ||||
|         let mut body: HashMap<&str, Vec<String>> = HashMap::new(); | ||||
|         body.insert("members", frontcodes); | ||||
| 
 | ||||
|         let client = reqwest::Client::new(); | ||||
|         let rb = client | ||||
|             .post(url) | ||||
|             .json(&body) | ||||
|             .header("content-type", "application/json; charset=utf-8") | ||||
|             .header(USER_AGENT, "Pluralsync") | ||||
|             .header(AUTHORIZATION, key); | ||||
| 
 | ||||
|         match http_request(rb) { | ||||
|             Ok(_) => (), | ||||
|             Err(e) => println!("{}", e.to_string()), | ||||
|         } | ||||
| 
 | ||||
|     } else { | ||||
|         println!("Members already fonting"); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| fn sp_get_userid(key: &str) -> String { | ||||
|     let url = format!("{}/me", SP_URL); | ||||
| 
 | ||||
|     let res = http_get(url,key); | ||||
|     let json_res : Value = serde_json::from_str(&res.unwrap()).unwrap(); | ||||
|     return json_res["id"].as_str().unwrap().to_string(); | ||||
| } | ||||
| 
 | ||||
| fn sp_get_memberids(key: &str, system_id: &str) -> HashMap<String, String> { | ||||
|     let url = format!("{}/members/{}", SP_URL, system_id); | ||||
| 
 | ||||
|     let res = http_get(url,key); | ||||
|     let datas: Vec<Value> = serde_json::from_str(&res.unwrap()).unwrap(); | ||||
| 
 | ||||
|     let mut sp_memberdata: HashMap<String, String> = HashMap::new(); | ||||
|     for data in datas { | ||||
|         sp_memberdata.insert(String::from(data["content"]["name"].as_str().unwrap()), String::from(data["id"].as_str().unwrap())); | ||||
|     } | ||||
| 
 | ||||
|     return sp_memberdata; | ||||
| } | ||||
| 
 | ||||
| fn get_sp_id(mem: &Member, ids: &HashMap<String, String>) -> String { | ||||
| 
 | ||||
|     let mut member_id = String::new(); | ||||
| 
 | ||||
|     for (mn, mid) in ids { | ||||
|         if &mem.name == mn || &mem.alias == mn { | ||||
|             member_id = String::from(mid); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return member_id; | ||||
| } | ||||
| 
 | ||||
| fn sp_get_fronters(key: &str, sys: &System) -> Vec<Member> { | ||||
|     let url = format!("{}/fronters", SP_URL); | ||||
| 
 | ||||
|     let res = http_get(url,key); | ||||
|     let datas: Vec<Value> = serde_json::from_str(&res.unwrap()).unwrap(); | ||||
| 
 | ||||
|     let mut members = Vec::new(); | ||||
|     for data in datas { | ||||
|         let sp_id = &data["content"]["member"].as_str().unwrap(); | ||||
|         for member in &sys.members { | ||||
|             if &member.sp_id == sp_id { | ||||
|                 members.push(member.clone()); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
|     return members; | ||||
| } | ||||
| 
 | ||||
| fn sp_get_frontids(key: &str, to_id: &Member) -> String { | ||||
|     let url = format!("{}/fronters", SP_URL); | ||||
| 
 | ||||
|     let res = http_get(url,key); | ||||
|     let datas: Vec<Value> = serde_json::from_str(&res.unwrap()).unwrap(); | ||||
| 
 | ||||
|     for data in datas { | ||||
|         let sp_f_id = &data["id"].as_str().unwrap(); | ||||
|         let sp_id = &data["content"]["member"].as_str().unwrap(); | ||||
|         if sp_id.to_string() == to_id.sp_id { | ||||
|             return sp_f_id.to_string(); | ||||
|         } | ||||
|     } | ||||
|     return String::new(); | ||||
| } | ||||
| 
 | ||||
| fn sp_set_fronters(key: &str, to_front: &Vec<Member>, fronters: &Fronters) { | ||||
|     if to_front == &fronters.sp { | ||||
|         println!("Members already fonting"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     for fronting_member in &fronters.sp { | ||||
|         if !to_front.contains(&fronting_member) { | ||||
|             let f_id = sp_get_frontids(&key, &fronting_member); | ||||
| 
 | ||||
|             let url = format!("{}/frontHistory/{}", SP_URL, f_id); | ||||
| 
 | ||||
|             #[derive (Serialize)] | ||||
|             #[allow (non_snake_case)] | ||||
|             struct SpRem { | ||||
|                 live: bool, | ||||
|                 endTime: u64 | ||||
|             } | ||||
| 
 | ||||
|             let end_time = std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH).expect("wa").as_secs(); | ||||
|             let rem = SpRem { | ||||
|                 live: false, | ||||
|                 endTime: end_time * 1000 | ||||
|             }; | ||||
| 
 | ||||
|             let body = serde_json::to_string(&rem).expect("Error"); | ||||
| 
 | ||||
| 
 | ||||
|             let client = reqwest::Client::new(); | ||||
|             let rb = client | ||||
|                 .patch(url) | ||||
|                 .body(body) | ||||
|                 .header("content-type", "application/json; charset=utf-8") | ||||
|                 .header(USER_AGENT, "Pluralsync") | ||||
|                 .header(AUTHORIZATION, key); | ||||
| 
 | ||||
|             match http_request(rb) { | ||||
|                 Ok(_) => (), | ||||
|                 Err(e) => println!("{}", e.to_string()), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     for tf_member in to_front { | ||||
|         if !fronters.sp.contains(&tf_member) { | ||||
|             let url = format!("{}/frontHistory", SP_URL); | ||||
| 
 | ||||
|             #[derive (Serialize)] | ||||
|             #[allow (non_snake_case)] | ||||
|             struct SpAdd { | ||||
|                 member: String, | ||||
|                 custom: bool, | ||||
|                 live: bool, | ||||
|                 startTime: u64 | ||||
|             } | ||||
| 
 | ||||
|             let start_time = std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH).expect("wa").as_secs(); | ||||
|             let rem = SpAdd { | ||||
|                 member: String::from(&tf_member.sp_id), | ||||
|                 custom: false, | ||||
|                 live: true, | ||||
|                 startTime: start_time * 1000 | ||||
|             }; | ||||
| 
 | ||||
|             let body = serde_json::to_string(&rem).expect("Error"); | ||||
| 
 | ||||
|             let client = reqwest::Client::new(); | ||||
|             let rb = client | ||||
|                 .post(url) | ||||
|                 .body(body) | ||||
|                 .header("content-type", "application/json; charset=utf-8") | ||||
|                 .header(USER_AGENT, "Pluralsync") | ||||
|                 .header(AUTHORIZATION, key); | ||||
| 
 | ||||
|             match http_request(rb) { | ||||
|                 Ok(_) => (), | ||||
|                 Err(e) => println!("{}", e.to_string()), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn load_json(path: String) -> Value { | ||||
|     if Path::new(&path).exists() { | ||||
|         let config_data = fs::read_to_string(&path).expect("File not found"); | ||||
|         return serde_json::from_str(&config_data).unwrap(); | ||||
|     } else { | ||||
|         println!("Config file in {path} not found"); | ||||
|         return Value::Null; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn get_config(config_path: &str) -> Result<Config, &'static str> { | ||||
|     let path = format!("{}/config.json", config_path); | ||||
|     if Path::new(config_path).exists() { | ||||
| 
 | ||||
|         let result = load_json(String::from(&path)); | ||||
| 
 | ||||
|         if result == Value::Null { | ||||
|             let _ = fs::write(path, EXAMPLE_JSON); | ||||
|             return Err("Config file missing, creating template in {path}"); | ||||
|         } else { | ||||
|             let config: Config = serde_json::from_value(result).expect("Error unwrapping"); | ||||
|             return Ok(config); | ||||
|         } | ||||
|     } else { | ||||
|         let _ = create_dir(config_path); | ||||
|         let _ = fs::write(path, EXAMPLE_JSON); | ||||
|         return Err("Directory {config_path} does not exist. Creating with template config"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn get_system(config_path: &str) -> System { | ||||
|     let path = format!("{}/system.json", config_path); | ||||
| 
 | ||||
|     let mut result = load_json(String::from(&path)); | ||||
| 
 | ||||
|     if result == Value::Null { | ||||
|         println!("Syncing system config"); | ||||
|         let _ = sync(String::from(config_path)); | ||||
|         result = load_json(String::from(&path)); | ||||
|     } | ||||
| 
 | ||||
|     let vec = serde_json::to_vec(&result).unwrap(); | ||||
|     let sys = serde_json::from_slice::<System>(&vec).unwrap(); | ||||
| 
 | ||||
|     return sys; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| fn get_fronters(pk_key: &str, sp_key: &str, sys: &System, ff: ForceFrom) -> Fronters { | ||||
|     let mut fronters = Fronters::new( | ||||
|         pk_get_fronters(pk_key, sys), | ||||
|         sp_get_fronters(sp_key, sys) | ||||
|     ); | ||||
| 
 | ||||
|     if fronters.pk != fronters.sp { | ||||
|         match ff { | ||||
|             ForceFrom::PK => { | ||||
|                 fronters.sp = fronters.pk.clone() | ||||
|             } | ||||
|             ForceFrom::SP => { | ||||
|                 fronters.pk = fronters.sp.clone() | ||||
|             } | ||||
|             ForceFrom::None => { | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return fronters; | ||||
| } | ||||
| 
 | ||||
| #[tokio::main] | ||||
| async fn http_get(url: String, key: &str) -> Result<String, Box<dyn std::error::Error>> { | ||||
|     let client = reqwest::Client::new(); | ||||
|     let rb = client | ||||
|         .get(url) | ||||
|         .header(USER_AGENT, "Pluralsync") | ||||
|         .header(AUTHORIZATION, key); | ||||
|     let res = rb.send() | ||||
|         .await? | ||||
|         .text() | ||||
|         .await?; | ||||
| 
 | ||||
|     Ok(res) | ||||
| } | ||||
| 
 | ||||
| #[tokio::main] | ||||
| async fn http_request(rb: RequestBuilder) -> Result<String, Box<dyn std::error::Error>> { | ||||
|     let res = rb.send() | ||||
|         .await? | ||||
|         .text() | ||||
|         .await?; | ||||
|     Ok(res) | ||||
| } | ||||
|  |  | |||
|  | @ -1,4 +1,15 @@ | |||
| use std::path::Path; | ||||
| use std::fs; | ||||
| 
 | ||||
| use serde::{Serialize, Deserialize}; | ||||
| use serde_json::Value; | ||||
| 
 | ||||
| use color_eyre::eyre::{eyre, Result}; | ||||
| 
 | ||||
| use crate::api::{pk, sp}; | ||||
| use crate::utils::load_json; | ||||
| use crate::app::App; | ||||
| use crate::clap_ps::ForceFrom; | ||||
| 
 | ||||
| #[derive(Debug, Serialize, Deserialize, Clone)] | ||||
| pub struct System { | ||||
|  | @ -12,17 +23,129 @@ pub struct Member { | |||
|     pub pk_id: String, | ||||
|     pub sp_id: String, | ||||
|     pub name: String, | ||||
|     pub alias: String | ||||
|     pub aliases: Vec<String> | ||||
| } | ||||
| 
 | ||||
| impl System { | ||||
|     pub fn get_system(app: &App, update: bool) -> Result<System>{ | ||||
| 
 | ||||
|         let sys_file_path = format!("{}/system.json", app.cfg_dir); | ||||
| 
 | ||||
|         if Path::new(&sys_file_path).exists() && !update { | ||||
|             let json_system: Value = load_json(&sys_file_path)?; | ||||
|             let sys: System = serde_json::from_value(json_system)?; | ||||
|             Ok(sys) | ||||
|         } else { | ||||
|             match Self::update_system(app) { | ||||
|                 Ok(s) => { | ||||
|                     Ok(s) | ||||
|                 }, | ||||
|                 Err(e) => { | ||||
|                     Err(e) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn update_system(app: &App) -> Result<System> { | ||||
| 
 | ||||
|         if let Some(conf) = &app.cfg { | ||||
| 
 | ||||
|             // PluralKit
 | ||||
|             let pk_sys = pk::get_system(&conf.pk_key)?; | ||||
|             let pk_id = pk_sys["id"].as_str().unwrap(); | ||||
|             let pk_members = pk::get_members(&conf.pk_key, &pk_id)?; | ||||
| 
 | ||||
|             // Simplyplural
 | ||||
|             let sp_user_id = sp::get_user_id(&conf.sp_key)?; | ||||
|             let sp_member_ids = sp::get_member_ids(&conf.sp_key, &sp_user_id)?; | ||||
| 
 | ||||
|             // Get members
 | ||||
|             let mut members: Vec<Member> = Vec::new(); | ||||
|             for pk_mem in pk_members { | ||||
|                 let mut m = Member { | ||||
|                     pk_id: pk_mem["id"].as_str().unwrap().to_string(), | ||||
|                     sp_id: String::new(), | ||||
|                     name: pk_mem["name"].as_str().unwrap().to_string(), | ||||
|                     aliases: Vec::new(), | ||||
|                 }; | ||||
| 
 | ||||
|                 if pk_mem["display_name"].as_str() != None { | ||||
|                     m.aliases.push(pk_mem["display_name"].as_str().unwrap().to_string()); | ||||
|                 } | ||||
| 
 | ||||
|                 m.sp_id = sp::get_member_id(&m, &sp_member_ids)?; | ||||
| 
 | ||||
|                 members.push(m); | ||||
|             } | ||||
| 
 | ||||
|             // Create system
 | ||||
|             let sys = System { | ||||
|                 pk_userid: pk_id.to_string(), | ||||
|                 sp_userid: sp_user_id, | ||||
|                 members: members.clone(), | ||||
|             }; | ||||
| 
 | ||||
|             let json = serde_json::to_string(&sys)?; | ||||
|             fs::write(format!("{}/system.json", &app.cfg_dir), &json)?; | ||||
| 
 | ||||
|             return Ok(sys); | ||||
|         } | ||||
| 
 | ||||
|         Err(eyre!("No configuration loaded!")) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct Fronters { | ||||
|   pub sp: Vec<Member>, | ||||
|   pub pk: Vec<Member>, | ||||
|     pub sp: Vec<Member>, | ||||
|     pub pk: Vec<Member>, | ||||
| } | ||||
| 
 | ||||
| impl Fronters { | ||||
|   pub fn new(pk: Vec<Member>, sp: Vec<Member>) -> Self { | ||||
|     Self {sp, pk} | ||||
|   } | ||||
|     pub fn new(pk: Vec<Member>, sp: Vec<Member>) -> Self { | ||||
|         Self {sp, pk} | ||||
|     } | ||||
| 
 | ||||
|     pub fn get(app: &App, ff: ForceFrom) -> Result<Self> { | ||||
|         if let Some(cfg) = &app.cfg { | ||||
|             if let Some(sys) = &app.sys { | ||||
|                 let mut fronters = Fronters::new( | ||||
|                     pk::get_fronters(&cfg.pk_key, &sys)?, | ||||
|                     sp::get_fronters(&cfg.sp_key, &sys)?, | ||||
|                 ); | ||||
| 
 | ||||
|                 if fronters.pk != fronters.sp { | ||||
|                     match ff { | ||||
|                         ForceFrom::SP => { | ||||
|                             fronters.pk = fronters.sp.clone(); | ||||
|                         } | ||||
|                         _ => { | ||||
|                             fronters.sp = fronters.pk.clone(); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 return Ok(fronters); | ||||
|             } | ||||
|         } | ||||
|         Err(eyre!("System or configuration missing")) | ||||
|     } | ||||
| 
 | ||||
|     pub fn set(app: &App, to_front: Vec<Member>) -> Result<Fronters> { | ||||
| 
 | ||||
|         if let Some(cfg) = &app.cfg { | ||||
|             if let Some(sys) = &app.sys { | ||||
|                 if let Some(fronters) = &app.fronters { | ||||
|                     pk::set_fronters(&cfg.pk_key, &sys, &to_front, &fronters)?; | ||||
|                     sp::set_fronters(&cfg.sp_key, &to_front, &fronters)?; | ||||
| 
 | ||||
|                     return Ok(Fronters::new(to_front.clone(), to_front.clone())); | ||||
|                 } | ||||
|                 return Err(eyre!("Missing Fronters")); | ||||
|             } | ||||
|             return Err(eyre!("Missing System")); | ||||
|         } | ||||
|         Err(eyre!("Missing Config")) | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										120
									
								
								src/utils.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								src/utils.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,120 @@ | |||
| use std::fs; | ||||
| use serde_json::Value; | ||||
| use color_eyre::eyre::Result; | ||||
| 
 | ||||
| #[cfg(feature = "avatar")] | ||||
| use { | ||||
|     std::{ | ||||
|         process::Command, | ||||
|         fs::remove_file, | ||||
|         path::Path, | ||||
|     }, | ||||
|     crate::{ | ||||
|         configdata::Config, | ||||
|         app::App, | ||||
|         clap_ps::ForceFrom, | ||||
|     }, | ||||
|     color_eyre::eyre::eyre, | ||||
| }; | ||||
| 
 | ||||
| pub fn load_json(file_path: &str) -> Result<Value> { | ||||
| 
 | ||||
|     let file_data = fs::read_to_string(&file_path)?; | ||||
|     let json_value: Value = serde_json::from_str(&file_data)?; | ||||
| 
 | ||||
|     Ok(json_value) | ||||
| } | ||||
| 
 | ||||
| #[cfg(feature = "avatar")] | ||||
| pub fn update_avatars(app: &mut App, #[cfg(feature = "discord")] discord: bool, #[cfg(feature = "fedi")] fedi: bool) -> Result<()>{ | ||||
| 
 | ||||
|     if let Some(cfg) = app.cfg.clone() { | ||||
|         let names = &app.get_front(ForceFrom::None)?; // TODO CHANGE INTO USING LOCAL FRONT
 | ||||
| 
 | ||||
|         match avatar_module(&cfg, &names) { | ||||
|             Ok(_) => { | ||||
|                 #[cfg(feature = "discord")] { | ||||
|                     config.disc_module.enabled |= discord; | ||||
|                     discord_module(&config); | ||||
|                 } | ||||
|                 #[cfg(feature = "fedi")] { | ||||
|                     config.fedi_module.enabled |= fedi; | ||||
|                     fedi_module(&config); | ||||
|                 } | ||||
|             }, | ||||
|             Err(e) => println!("{}", e), | ||||
|         } | ||||
|         return Ok(()); | ||||
|     } | ||||
|     Err(eyre!("No config found")) | ||||
| } | ||||
| 
 | ||||
| #[cfg(feature = "avatar")] | ||||
| fn avatar_module(config: &Config, names: &Vec<String>) -> Result<(), &'static str>{ | ||||
|     if config.avatar_module.enabled { | ||||
|         let mut whitelisted_names: Vec<String> = names.iter().map(|i| i.to_lowercase()).collect(); | ||||
|         let blacklist: Vec<String> = config.avatar_module.blacklist.iter().map(|i| i.to_lowercase()).collect(); | ||||
|         for name in names { | ||||
|             if blacklist.contains(&name.to_lowercase()) { | ||||
|                 let index = whitelisted_names.iter().position(|x| *x == name.to_lowercase()).unwrap(); | ||||
|                 whitelisted_names.remove(index); | ||||
|                 println!("{} blacklisted from avatar module", name); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         let avatarnames = whitelisted_names.join("").to_lowercase() + ".png"; | ||||
| 
 | ||||
|         if Path::new(&config.avatar_module.avatar_output_path).exists() { | ||||
|             let _ = remove_file(&config.avatar_module.avatar_output_path); | ||||
|         } | ||||
| 
 | ||||
|         if cfg!(target_os = "windows") { | ||||
|             let cmdaug = format!("copy {}\\{} {}", &config.avatar_module.avatar_folder, avatarnames, &config.avatar_module.avatar_output_path); | ||||
|             Command::new("cmd").args(["/C", &cmdaug]).output().expect("Avatar module error"); | ||||
|         } else { | ||||
|             Command::new("sh").arg("-c").arg(format!("cp {}/{} {}", &config.avatar_module.avatar_folder, avatarnames, &config.avatar_module.avatar_output_path)).output().expect("Avatar module error"); | ||||
|         } | ||||
|         if Path::new(&config.avatar_module.avatar_output_path).exists() { | ||||
|             println!("Avatar module finished"); | ||||
|             Ok(()) | ||||
|         } else { | ||||
|             Err("Avatar module failed") | ||||
|         } | ||||
|     } else { | ||||
|         Err("Avatar mode disabled") | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #[cfg(feature = "discord")] | ||||
| fn discord_module(config: &Config) { | ||||
|     if config.disc_module.enabled { | ||||
|         if cfg!(target_os = "windows") { | ||||
|             let mut c = Command::new("cmd").args(["/C", format!("{} {}", &config.disc_module.python_path, &config.disc_module.script_path).as_str()]).spawn().expect("Discord module error"); | ||||
|             let _ = c.wait().expect("Error"); | ||||
|         } else { | ||||
|             let mut c = Command::new("sh").arg("-c").arg(format!("{} {}", &config.disc_module.python_path, &config.disc_module.script_path)).spawn().expect("Discord module error"); | ||||
|             let _ = c.wait().expect("Error"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(feature = "fedi")] | ||||
| fn fedi_module(config: &Config) { | ||||
|     if config.fedi_module.enabled { | ||||
| 
 | ||||
|         let client = reqwest::Client::new(); | ||||
|         let form = Form::new().part("avatar", Part::bytes(fs::read(config.avatar_module.avatar_output_path.clone()).unwrap()).file_name("face.png").mime_str("image/png").unwrap()); | ||||
|         let rb = client | ||||
|             .patch(config.fedi_module.instance.clone() + "/api/v1/accounts/update_credentials") | ||||
|             .multipart(form) | ||||
|             .header(USER_AGENT, "Pluralsync") | ||||
|             .header(AUTHORIZATION, format!("Bearer {}", &config.fedi_module.token).as_str()); | ||||
| 
 | ||||
|         println!("Fedi module finished"); | ||||
|         match http_request(rb) { | ||||
|             Ok(_) => (), | ||||
|             Err(e) => println!("{}", e.to_string()), | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		
		Reference in a new issue