diff --git a/README.org b/README.org index 75687f2..4f66b1e 100644 --- a/README.org +++ b/README.org @@ -12,7 +12,18 @@ On the first run, if it does not exist, PluralSync will create a configuration d #+begin_src js { "pk_key": "// Pluralkit token", - "sp_key": "// Simplplural token" + "sp_key": "// Simplplural token", + "pfp_module": { + "enabled": false, + "pfp_folder": "// Folder to grab profile pictures, they follow they member1member2...memberX.png format", + "pfp_output_path": "// Path for the copied selected pfp" + }, + "disc_module": { + "enabled": false, + "token": "// Discord user token", + "python_path": "// Path of the python executable", + "script_path": "// Path of the pfp changer python script" + } } #+end_src @@ -36,8 +47,14 @@ Displays the current front. The result is displayed in console but also stored i *** Memberlist Writes the known list of members based on the =system.json= file. +** Modules +*** PFP module +requires docs +*** Discord module +goes against ToS be gentle + * Future Given the nature of the project as a learning exercise, there are currently a set of features and improvements I'd like to add eventually. -- [ ] Proper error handling (What I have currently is simply a mess) +- [X] Proper error handling (What I have currently is simply a mess) - [ ] Rofi and zenity support - [ ] Proper way to handle front conflicts between PluralKit and SimplyPlural diff --git a/config.example.json b/config.example.json index 7fde0d2..c82c6b2 100644 --- a/config.example.json +++ b/config.example.json @@ -1,4 +1,15 @@ { "pk_key": "// Pluralkit token", - "sp_key": "// Simplplural token" + "sp_key": "// Simplplural token", + "pfp_module": { + "enabled": false, + "pfp_folder": "// Folder to grab profile pictures, they follow they member1member2.png format", + "pfp_output_path": "// Path for the copied selected pfp" + }, + "disc_module": { + "enabled": false, + "token": "// Discord user token", + "python_path": "// Path of the python executable", + "script_path": "// Path of the pfp changer python script" + } } diff --git a/src/main.rs b/src/main.rs index bc599b3..5e7c54a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,8 @@ -use std::fs; +use std::fs::{self, remove_file}; use std::fs::create_dir; use std::path::Path; use std::collections::HashMap; +use std::process::Command; use reqwest::header::{USER_AGENT, AUTHORIZATION}; @@ -13,6 +14,45 @@ use dirs; const PK_URL: &str = "https://api.pluralkit.me/v2"; const SP_URL: &str = "https://api.apparyllis.com/v1"; +const EXAMPLE_JSON: &str = r#"{ + "pk_key": "// Pluralkit token", + "sp_key": "// Simplplural token", + "pfp_module": { + "enabled": false, + "pfp_folder": "// Folder to grab profile pictures, they follow they member1member2.png format", + "pfp_output_path": "// Path for the copied selected pfp" + }, + "disc_module": { + "enabled": false, + "token": "// Discord user token", + "python_path": "// Path of the python executable", + "script_path": "// Path of the pfp changer python script" + } +}"#; + +#[derive(Debug, Serialize, Deserialize)] +struct Config { + pk_key: String, + sp_key: String, + pfp_module: PfpModule, + disc_module: DiscModule +} + +#[derive(Debug, Serialize, Deserialize)] +struct PfpModule { + enabled: bool, + pfp_folder: String, + pfp_output_path: String, +} + +#[derive(Debug, Serialize, Deserialize)] +struct DiscModule { + enabled: bool, + token: String, + python_path: String, + script_path: String, +} + #[derive(Debug, Serialize, Deserialize, Clone)] struct System { pk_userid: String, @@ -81,7 +121,9 @@ memberlist - Shows loaded member list"#; } } - "get" => get(config_path), + "get" => { + let _ = get(config_path); + }, "memberlist" => memberlist(config_path), &_ => println!("{}", helpstring), } @@ -93,29 +135,25 @@ memberlist - Shows loaded member list"#; } } -fn sync(config_path: String) -> Result<(), String>{ +fn sync(config_path: String) -> Result<(), &'static str> { // Get config - let config = get_config(&config_path); - if config == Value::Null { - println!("Stopping."); - return Ok(()); - } - - let pk_key = &config["pk_key"].as_str().unwrap(); - let sp_key = &config["sp_key"].as_str().unwrap(); + 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(pk_key); + 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(sp_key); + let sp_user_id = sp_get_userid(&config.sp_key); // Get Simplyplural member ids - let sp_member_ids = sp_get_memberids(sp_key, &sp_user_id); + let sp_member_ids = sp_get_memberids(&config.sp_key, &sp_user_id); // get members - let pk_members = pk_get_members(pk_key, pk_sysid); + let pk_members = pk_get_members(&config.pk_key, pk_sysid); let mut members: Vec = Vec::new(); for member in pk_members { let mut m = Member { @@ -156,10 +194,10 @@ TODO } */ fn set_member(config_path: String, tf_members: &[String]) -> Result<(), &'static str> { - let config = get_config(&config_path); - if config == Value::Null { - return Err("Config not found. Stopping"); - } + let config: Config = match get_config(&config_path) { + Ok(c) => c, + Err(e) => return Err(e) + }; let system: System = get_system(&config_path); @@ -175,12 +213,12 @@ fn set_member(config_path: String, tf_members: &[String]) -> Result<(), &'static } } if to_front.len() != 0 { - let fronters = get_fronters(&config["pk_key"].as_str().unwrap(), &config["sp_key"].as_str().unwrap(), &system); + let fronters = get_fronters(&config.pk_key, &config.sp_key, &system); - pk_set_fronters(&config["pk_key"].as_str().unwrap(), &system, &to_front, &fronters); - sp_set_fronters(&config["sp_key"].as_str().unwrap(), &to_front, &fronters); + pk_set_fronters(&config.pk_key, &system, &to_front, &fronters); + sp_set_fronters(&config.sp_key, &to_front, &fronters); - get(config_path); + let _ = get(config_path); } else { println!("No members found. Known members:\n--------------------------"); memberlist(config_path); @@ -190,10 +228,10 @@ fn set_member(config_path: String, tf_members: &[String]) -> Result<(), &'static } fn add_member(config_path: String, tf_members: &[String]) -> Result<(), &'static str> { - let config = get_config(&config_path); - if config == Value::Null { - return Err("Config not found. Stopping"); - } + let config: Config = match get_config(&config_path) { + Ok(c) => c, + Err(e) => return Err(e) + }; let system: System = get_system(&config_path); @@ -207,17 +245,17 @@ fn add_member(config_path: String, tf_members: &[String]) -> Result<(), &'static } } if to_front.len() != 0 { - let fronters = get_fronters(&config["pk_key"].as_str().unwrap(), &config["sp_key"].as_str().unwrap(), &system); + let fronters = get_fronters(&config.pk_key, &config.sp_key, &system); let mut aux: Vec = Vec::new(); aux.append(&mut fronters.pk.clone()); aux.append(&mut to_front); to_front = aux; - pk_set_fronters(&config["pk_key"].as_str().unwrap(), &system, &to_front, &fronters); - sp_set_fronters(&config["sp_key"].as_str().unwrap(), &to_front, &fronters); + pk_set_fronters(&config.pk_key, &system, &to_front, &fronters); + sp_set_fronters(&config.sp_key, &to_front, &fronters); - get(config_path); + let _ = get(config_path); } else { println!("No members found. Known members:\n--------------------------"); memberlist(config_path); @@ -239,18 +277,37 @@ fn memberlist(config_path: String) { } -fn get(config_path: String) { - let config = get_config(&config_path); +fn get(config_path: String) -> Result<(), &'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"].as_str().unwrap(), &config["sp_key"].as_str().unwrap(), &sys); + let f = get_fronters(&config.pk_key, &config.sp_key, &sys); let mut names = Vec::new(); - for m in &f.pk { + for m in &f.sp { names.push(String::from(&m.name)); } let fronters = names.join(" || "); println!("Currently fronting: {}", fronters); let _ = fs::write(format!("{}/.front", config_path), fronters); + + if config.pfp_module.enabled { + let pfpnames = names.join("").to_lowercase() + ".png"; + + if Path::new(&config.pfp_module.pfp_output_path).exists() { + let _ = remove_file(&config.pfp_module.pfp_output_path); + } + + Command::new("sh").arg("-c").arg(format!("cp {}/{} {}", &config.pfp_module.pfp_folder, pfpnames, &config.pfp_module.pfp_output_path)).output().expect("pfp module error"); + + if config.disc_module.enabled { + Command::new("sh").arg("-c").arg(format!("{} {}", &config.disc_module.python_path, &config.disc_module.script_path)).output().expect("discord module error"); + } + } + + Ok(()) } fn pk_get_system(key: &str) -> Value { @@ -446,33 +503,23 @@ fn load_json(path: String) -> Value { } } -fn get_config(config_path: &str) -> Value { +fn get_config(config_path: &str) -> Result { let path = format!("{}/config.json", config_path); if Path::new(config_path).exists() { let result = load_json(String::from(&path)); if result == Value::Null { - println!("Config file missing, creating template in {path}"); - let _ = fs::write(path, r#" -{ - "pk_key": "// Pluralkit token", - "sp_key": "// Simplplural token" -} - "#); - } - - return result; + 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 { - println!("Directory {config_path} does not exist. Creating with template config"); let _ = create_dir(config_path); - let _ = fs::write(path, r#" -{ - "pk_key": "// Pluralkit token", - "sp_key": "// Simplplural token" -} - "#); - return Value::Null; + let _ = fs::write(path, EXAMPLE_JSON); + return Err("Directory {config_path} does not exist. Creating with template config"); } } diff --git a/updatepfp.py b/updatepfp.py new file mode 100644 index 0000000..46dafbf --- /dev/null +++ b/updatepfp.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 + +import os +import json +import discord + +config_dir = os.getenv('APPDATA') if os.name == "nt" else os.path.expanduser('~') + "/.config" +config_dir += "/pluralsync/config.json" + +f = open(config_dir) +json = json.load(f) + +class MyClient(discord.Client): + async def on_ready(self): + print('Logged on as', self.user) + pfp_path = json["pfp_module"]["pfp_output_path"] + fp = open(pfp_path, 'rb') + pfp = fp.read() + await self.user.edit(avatar=pfp) + await self.close() + +client = MyClient() +client.run(json["disc_module"]["token"])