diff --git a/Cargo.lock b/Cargo.lock index 007b60c..92f728a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,54 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "anstream" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd2405b3ac1faab2990b74d728624cd9fd115651fcecc7c2d8daf01376275ba" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -83,6 +131,52 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "4.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80932e03c33999b9235edb8655bc9df3204adc9887c2f95b50cb1deb9fd54253" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6c0db58c659eef1c73e444d298c27322a1b52f6927d2ad470c0c0f96fa7b8fa" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "core-foundation" version = "0.9.3" @@ -117,7 +211,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -142,7 +236,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -262,6 +356,12 @@ version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" version = "0.3.3" @@ -464,7 +564,7 @@ checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", "wasi", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -580,7 +680,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -609,11 +709,11 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "pluralsync" -version = "1.0.0" +version = "1.2.0" dependencies = [ + "clap", "dirs", "reqwest", - "rofi", "serde", "serde_json", "tokio", @@ -696,15 +796,6 @@ dependencies = [ "winreg", ] -[[package]] -name = "rofi" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4bc8f88160318ab75508e32d837e72ecfac4270d27cdf44315495d614636df6" -dependencies = [ - "thiserror", -] - [[package]] name = "rustc-demangle" version = "0.1.23" @@ -721,7 +812,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -736,7 +827,7 @@ version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -852,9 +943,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "syn" version = "2.0.39" @@ -897,7 +994,7 @@ dependencies = [ "fastrand", "redox_syscall", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -951,7 +1048,7 @@ dependencies = [ "signal-hook-registry", "socket2 0.5.5", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1061,6 +1158,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "vcpkg" version = "0.2.15" @@ -1192,7 +1295,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", ] [[package]] @@ -1201,13 +1313,28 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] @@ -1216,42 +1343,84 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + [[package]] name = "winreg" version = "0.50.0" @@ -1259,5 +1428,5 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if", - "windows-sys", + "windows-sys 0.48.0", ] diff --git a/Cargo.toml b/Cargo.toml index 21ce04c..367b044 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "pluralsync" -version = "1.0.0" +version = "1.2.0" edition = "2021" +authors = ["jvnkyard@ak.lavenderfield.xyz"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -10,9 +11,14 @@ dirs = "5.0.1" serde = { version = "1.0.192", features = ["derive"] } serde_json = "1.0" reqwest = { version = "0.11.22", features = ["json", "multipart"] } -rofi = "0.3.0" +clap = { version = "4.4.10", features = ["derive"] } tokio = { version = "1", features = ["full"] } +[features] +avatar = [] +discord = ["avatar"] +fedi = ["avatar"] + [profile.release] opt-level = 'z' lto = true diff --git a/README.org b/README.org index 2041922..eb08208 100644 --- a/README.org +++ b/README.org @@ -66,15 +66,15 @@ Should be compatible with Mastodon, Pleroma and Akkoma. Requires the =avatar module= to be enabled. Will set the avatar of your fediverse account to the one selected by the avatar module. * Installation -Just build with cargo and move or symlink the executable wherever is more comofortable for you. +Just build with cargo and move or symlink the executable wherever is more comofortable for you. Optionally, you can add the =avatar=, =discord= and =fedi= features #+begin_src shell -cargo build -r +cargo build -r --features discord,fedi #+end_src * 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. - [X] Proper error handling (What I have currently is simply a mess) -- [ ] Allow the user to set which front is used in cased of mismatch between PK and SP -- [ ] Implement clap for command inputs -- [ ] Actually separate the modules as actual modules +- [X] Allow the user to set which front is used in cased of mismatch between PK and SP +- [X] Implement clap for command inputs +- [X] Actually separate the modules as actual modules diff --git a/src/clap_ps.rs b/src/clap_ps.rs new file mode 100644 index 0000000..37e0222 --- /dev/null +++ b/src/clap_ps.rs @@ -0,0 +1,75 @@ +use clap::{Parser, Subcommand, ValueEnum}; + +#[derive(Parser)] +#[clap(arg_required_else_help = true, verbatim_doc_comment, author)] +#[command(name = "PluralSync")] +#[command(author = "jvnkyard ")] +#[command(version = "1.2")] +#[command( + help_template = "{name} Version {version} \n {author} \n {about-section} \n {usage-heading} {usage} \n \n {all-args} {tab}" +)] +#[command(about, long_about = None)] +/// Tool written in Rust to syncronize API calls between pluralkit and SimplyPlural. +/// Sets avatars for discord and fediverse. +pub struct Args { + /// Command to execute + #[command(subcommand)] + pub cmd: Commands, +} + +#[derive(Subcommand)] +pub enum Commands { + ///Fetches the system member information from PluralKit and SimplyPlural + Sync, + /// Sets one or more members to the front + #[clap(arg_required_else_help = true)] + Set { + /// Members to set + members: Vec, + /// Overrides the configuration file to run the discord module + #[cfg(feature = "discord")] + #[arg[short, long]] + discord: bool, + /// Overrides the configuration file to run the fedi module + #[cfg(feature = "fedi")] + #[arg[short, long]] + fedi: bool, + }, + /// Refreshes the current front information + Get { + /// Selects which front takes priority in case of mismatch + #[arg(short = 'o',long)] + force_from: Option, + /// Overrides the configuration file to run the discord module + #[cfg(feature = "discord")] + #[arg[short, long]] + discord: bool, + /// Overrides the configuration file to run the fedi module + #[cfg(feature = "fedi")] + #[arg[short, long]] + fedi: bool, + }, + /// Appends a member to the existing front + #[clap(arg_required_else_help = true)] + Add { + /// Members to add + members: Vec, + /// Overrides the configuration file to run the discord module + #[cfg(feature = "discord")] + #[arg[short, long]] + discord: bool, + /// Overrides the configuration file to run the fedi module + #[cfg(feature = "fedi")] + #[arg[short, long]] + fedi: bool, + }, + /// Displays a list with all members + Members, +} + +#[derive(ValueEnum, Clone)] +pub enum ForceFrom { + SP, + PK, + None, +} diff --git a/src/configdata.rs b/src/configdata.rs new file mode 100644 index 0000000..c344f47 --- /dev/null +++ b/src/configdata.rs @@ -0,0 +1,38 @@ +use serde::{Serialize, Deserialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Config { + pub pk_key: String, + pub sp_key: String, + #[cfg(feature = "avatar")] + pub avatar_module: AvatarModule, + #[cfg(feature = "discord")] + pub disc_module: DiscModule, + #[cfg(feature = "fedi")] + pub fedi_module: FediModule, +} + +#[cfg(feature = "avatar")] +#[derive(Debug, Serialize, Deserialize)] +pub struct AvatarModule { + pub enabled: bool, + pub avatar_folder: String, + pub avatar_output_path: String, +} + +#[cfg(feature = "discord")] +#[derive(Debug, Serialize, Deserialize)] +pub struct DiscModule { + pub enabled: bool, + pub token: String, + pub python_path: String, + pub script_path: String, +} + +#[cfg(feature = "fedi")] +#[derive(Debug, Serialize, Deserialize)] +pub struct FediModule { + pub enabled: bool, + pub instance: String, + pub token: String, +} diff --git a/src/main.rs b/src/main.rs index ce629f0..b624a99 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,74 +1,31 @@ -use std::fs::{self, remove_file}; +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, Deserialize}; +use serde::Serialize; use serde_json::Value; use dirs; -#[derive(Debug, Serialize, Deserialize)] -struct Config { - pk_key: String, - sp_key: String, - avatar_module: AvatarModule, - disc_module: DiscModule, - fedi_module: FediModule, -} +mod configdata; +use configdata::*; -#[derive(Debug, Serialize, Deserialize)] -struct AvatarModule { - enabled: bool, - avatar_folder: String, - avatar_output_path: String, -} +mod systemdata; +use systemdata::*; -#[derive(Debug, Serialize, Deserialize)] -struct DiscModule { - enabled: bool, - token: String, - python_path: String, - script_path: String, -} - -#[derive(Debug, Serialize, Deserialize)] -struct FediModule { - enabled: bool, - instance: String, - token: String, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -struct System { - pk_userid: String, - sp_userid: String, - members: Vec, -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -struct Member { - pk_id: String, - sp_id: String, - name: String, - alias: String -} - -#[derive(Debug)] -struct Fronters { - sp: Vec, - pk: Vec, -} - -impl Fronters { - fn new(pk: Vec, sp: Vec) -> Self { - Self {sp, pk} - } -} +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"; @@ -94,58 +51,143 @@ const EXAMPLE_JSON: &str = r#"{ } }"#; -const HELP_STRING: &str = r#"PluralSync -Made by Jvnko@lavenderfield - -Usage:\npluralsync [COMMAND] [member1] [member2]... - -Commands: -sync - Fetches the system member information from PluralKit and SimplyPlural -set - Sets one or more members to the front -get - Refreshes the current front information -add - Adds a member to the existing front -memberlist - Shows loaded member list"#; - fn main() { - if std::env::args().len() > 1 { - let augs: Vec = std::env::args().collect(); - let command = &augs[1]; - let config_path: String; - match dirs::config_dir() { - Some(x) => { - config_path = format!("{}/pluralsync", x.display()); - match command.as_str() { - "sync" => { - let _ = sync(config_path); - }, - "set" => { - if std::env::args().len() > 2 { - let _ = set_member(config_path, &augs[2..]).map_err(|e| println!("{e}")); + let cli = Args::parse(); - } else { - //set_empty(config_path); - } - }, - "add" => { - if std::env::args().len() > 2 { - let _ = add_member(config_path, &augs[2..]).map_err(|e| println!("{e}")); - } else { - //add_empty(config_path); - } - - } - "get" => { - let _ = get(config_path); - }, - "memberlist" => memberlist(config_path), - &_ => println!("{}", HELP_STRING), - } - }, - None => println!("Something went wrong") + let config_path = match dirs::config_dir() { + Some(d) => format!("{}/pluralsync", d.display()), + None => { + println!("Could not fetch the config directory"); + return (); } - } else { - println!("{}", HELP_STRING); + }; + + let mut res: Result<(), &str> = Ok(()); + + match cli.cmd { + Commands::Sync => { + res = sync(config_path); + }, + + // SET MEMBER + #[cfg(all(feature = "discord", feature = "fedi"))] + Commands::Set { members, discord, fedi } => { + res = set_member(config_path.clone(), members); + + #[cfg(feature = "avatar")] + let _ = update_avatars(config_path.clone(), discord, fedi); + }, + #[cfg(all(feature = "discord", not(feature = "fedi")))] + Commands::Set { members, discord } => { + res = set_member(config_path.clone(), members); + + #[cfg(feature = "avatar")] + let _ = update_avatars(config_path.clone(), discord, false); + }, + #[cfg(all(not(feature = "discord"), feature = "fedi"))] + Commands::Set { members, fedi} => { + res = set_member(config_path.clone(), members); + + #[cfg(feature = "avatar")] + let _ = update_avatars(config_path.clone(), false, fedi); + }, + #[cfg(all(not(feature = "discord"), not(feature = "fedi")))] + Commands::Set { members } => { + res = set_member(config_path.clone(), members); + + #[cfg(feature = "avatar")] + let _ = update_avatars(config_path.clone(), false, false); + }, + + // ADD MEMBER + #[cfg(all(feature = "discord", feature = "fedi"))] + Commands::Add { members, discord, fedi } => { + res = add_member(config_path.clone(), members); + + #[cfg(feature = "avatar")] + let _ = update_avatars(config_path.clone(), discord, fedi); + }, + #[cfg(all(feature = "discord", not(feature = "fedi")))] + Commands::Add { members, discord } => { + res = add_member(config_path.clone(), members); + + #[cfg(feature = "avatar")] + let _ = update_avatars(config_path.clone(), discord, false); + }, + #[cfg(all(not(feature = "discord"), feature = "fedi"))] + Commands::Add { members, fedi} => { + res = add_member(config_path.clone(), members); + + #[cfg(feature = "avatar")] + let _ = update_avatars(config_path.clone(), false, fedi); + }, + #[cfg(all(not(feature = "discord"), not(feature = "fedi")))] + Commands::Add { members } => { + res = add_member(config_path.clone(), members); + + #[cfg(feature = "avatar")] + let _ = update_avatars(config_path.clone(), false, false); + }, + + // Get MEMBER + #[cfg(all(feature = "discord", feature = "fedi"))] + Commands::Get { force_from, discord, 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(), 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); + }, + + Commands::Members => { + res = memberlist(config_path); + }, + } + + match res { + Ok(_) => (), + Err(e) => println!("{}", e), } } @@ -200,14 +242,7 @@ fn sync(config_path: String) -> Result<(), &'static str> { Ok(()) } -/* -TODO fn set_empty(config_path: String) { -TODO let config = load_json(format!("{}/config.json", config_path)); -TODO let system = get_system(&config_path); -TODO } -*/ - -fn set_member(config_path: String, tf_members: &[String]) -> Result<(), &'static str> { +fn set_member(config_path: String, tf_members: Vec) -> Result<(), &'static str> { let config: Config = match get_config(&config_path) { Ok(c) => c, Err(e) => return Err(e) @@ -216,7 +251,7 @@ fn set_member(config_path: String, tf_members: &[String]) -> Result<(), &'static let system: System = get_system(&config_path); let mut to_front: Vec = Vec::new(); - for member in tf_members { + 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"); @@ -226,22 +261,20 @@ fn set_member(config_path: String, tf_members: &[String]) -> Result<(), &'static } } } - if to_front.len() != 0 { - let fronters = get_fronters(&config.pk_key, &config.sp_key, &system); + 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); - - let _ = get(config_path); } else { - println!("No members found. Known members:\n--------------------------"); - memberlist(config_path); + 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"); } Ok(()) } -fn add_member(config_path: String, tf_members: &[String]) -> Result<(), &'static str> { +fn add_member(config_path: String, tf_members: Vec) -> Result<(), &'static str> { let config: Config = match get_config(&config_path) { Ok(c) => c, Err(e) => return Err(e) @@ -250,7 +283,7 @@ fn add_member(config_path: String, tf_members: &[String]) -> Result<(), &'static let system: System = get_system(&config_path); let mut to_front: Vec = Vec::new(); - for member in tf_members { + 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()); @@ -258,8 +291,8 @@ fn add_member(config_path: String, tf_members: &[String]) -> Result<(), &'static } } } - if to_front.len() != 0 { - let fronters = get_fronters(&config.pk_key, &config.sp_key, &system); + if to_front.len() == tf_members.len() { + let fronters = get_fronters(&config.pk_key, &config.sp_key, &system, ForceFrom::None); let mut aux: Vec = Vec::new(); aux.append(&mut fronters.pk.clone()); @@ -269,16 +302,16 @@ fn add_member(config_path: String, tf_members: &[String]) -> Result<(), &'static pk_set_fronters(&config.pk_key, &system, &to_front, &fronters); sp_set_fronters(&config.sp_key, &to_front, &fronters); - let _ = get(config_path); + let _ = get(config_path, ForceFrom::None); } else { - println!("No members found. Known members:\n--------------------------"); - memberlist(config_path); + 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"); } Ok(()) } -fn memberlist(config_path: String) { +fn memberlist(config_path: String) -> Result<(), &'static str> { let sys = get_system(&config_path); for mem in sys.members { @@ -289,16 +322,18 @@ fn memberlist(config_path: String) { } } + Ok(()) + } -fn get(config_path: String) -> Result<(), &'static str> { +fn get(config_path: String, ff: ForceFrom) -> 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, &config.sp_key, &sys); + 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)); @@ -307,6 +342,37 @@ fn get(config_path: String) -> Result<(), &'static str> { 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")] + avatar_module(&config, &names); + + #[cfg(feature = "discord")] { + config.disc_module.enabled |= discord; + discord_module(&config); + } + #[cfg(feature = "fedi")] { + config.fedi_module.enabled |= fedi; + fedi_module(&config); + } + + Ok(()) +} + +#[cfg(feature = "avatar")] +fn avatar_module(config: &Config, names: &Vec) { if config.avatar_module.enabled { let avatarnames = names.join("").to_lowercase() + ".png"; @@ -317,40 +383,57 @@ fn get(config_path: String) -> Result<(), &'static str> { 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"); - - if config.disc_module.enabled { - 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 { 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 config.disc_module.enabled { - 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"); - } - - if config.fedi_module.enabled { - let _ = http_patch_request_fedi(config.fedi_module.instance + "/api/v1/accounts/update_credentials", format!("Bearer {}", &config.fedi_module.token).as_str(), config.avatar_module.avatar_output_path); - } } } - Ok(()) +} + +#[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()); + + 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_request(url,key); + let res = http_get(url,key); return serde_json::from_str(&res.unwrap()).unwrap(); } fn pk_get_members(key: &str, sysid: &str) -> Vec { let url = format!("{}/systems/{}/members", PK_URL, sysid); - let res = http_get_request(url,key); + let res = http_get(url,key); let datas: Vec = serde_json::from_str(&res.unwrap()).unwrap(); return datas; @@ -359,7 +442,7 @@ fn pk_get_members(key: &str, sysid: &str) -> Vec { fn pk_get_fronters(key: &str, sys: &System) -> Vec { let url = format!("{}/systems/{}/fronters", PK_URL, sys.pk_userid); - let res = http_get_request(url,key); + let res = http_get(url,key); let data: Value = serde_json::from_str(&res.unwrap()).unwrap(); let memberdata = &data["members"].as_array(); @@ -385,9 +468,23 @@ fn pk_set_fronters(key: &str, sys: &System, to_front: &Vec, fronters: &F for tf in to_front { frontcodes.push(String::from(&tf.pk_id)); } + let mut body: HashMap<&str, Vec> = HashMap::new(); body.insert("members", frontcodes); - let _ = http_post_request(url, key, &body); + + 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"); } @@ -397,7 +494,7 @@ fn pk_set_fronters(key: &str, sys: &System, to_front: &Vec, fronters: &F fn sp_get_userid(key: &str) -> String { let url = format!("{}/me", SP_URL); - let res = http_get_request(url,key); + 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(); } @@ -405,7 +502,7 @@ fn sp_get_userid(key: &str) -> String { fn sp_get_memberids(key: &str, system_id: &str) -> HashMap { let url = format!("{}/members/{}", SP_URL, system_id); - let res = http_get_request(url,key); + let res = http_get(url,key); let datas: Vec = serde_json::from_str(&res.unwrap()).unwrap(); let mut sp_memberdata: HashMap = HashMap::new(); @@ -432,7 +529,7 @@ fn get_sp_id(mem: &Member, ids: &HashMap) -> String { fn sp_get_fronters(key: &str, sys: &System) -> Vec { let url = format!("{}/fronters", SP_URL); - let res = http_get_request(url,key); + let res = http_get(url,key); let datas: Vec = serde_json::from_str(&res.unwrap()).unwrap(); let mut members = Vec::new(); @@ -451,7 +548,7 @@ fn sp_get_fronters(key: &str, sys: &System) -> Vec { fn sp_get_frontids(key: &str, to_id: &Member) -> String { let url = format!("{}/fronters", SP_URL); - let res = http_get_request(url,key); + let res = http_get(url,key); let datas: Vec = serde_json::from_str(&res.unwrap()).unwrap(); for data in datas { @@ -491,7 +588,19 @@ fn sp_set_fronters(key: &str, to_front: &Vec, fronters: &Fronters) { let body = serde_json::to_string(&rem).expect("Error"); - let _ = http_patch_request(url, key, body); + + 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()), + } } } @@ -518,7 +627,18 @@ fn sp_set_fronters(key: &str, to_front: &Vec, fronters: &Fronters) { let body = serde_json::to_string(&rem).expect("Error"); - let _ = http_post_request_st(url, key, body); + 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()), + } } } } @@ -571,84 +691,48 @@ fn get_system(config_path: &str) -> System { } -fn get_fronters(pk_key: &str, sp_key: &str, sys: &System) -> Fronters { - let fronters = Fronters::new( +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 { -// fronters.sp = fronters.pk.clone(); -// } + 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_request(url: String, key: &str) -> Result> { +async fn http_get(url: String, key: &str) -> Result> { let client = reqwest::Client::new(); - let res = client + let rb = client .get(url) .header(USER_AGENT, "Pluralsync") - .header(AUTHORIZATION, key) - .send() + .header(AUTHORIZATION, key); + let res = rb.send() + .await? + .text() + .await?; + + Ok(res) +} + +#[tokio::main] +async fn http_request(rb: RequestBuilder) -> Result> { + let res = rb.send() .await? .text() .await?; Ok(res) } - -#[tokio::main] -async fn http_post_request(url: String, key: &str, body: &HashMap<&str, Vec>) -> Result<(), Box> { - let client = reqwest::Client::new(); - let _= client - .post(url) - .json(body) - .header(USER_AGENT, "Pluralsync") - .header(AUTHORIZATION, key) - .send() - .await?; - Ok(()) -} - -#[tokio::main] -async fn http_post_request_st(url: String, key: &str, body: String) -> Result<(), Box> { - let client = reqwest::Client::new(); - let c = client - .post(url) - .body(body) - .header("content-type", "application/json; charset=utf-8") - .header(USER_AGENT, "Pluralsync") - .header(AUTHORIZATION, key); - let _res = c.send() - .await?; - Ok(()) -} - -#[tokio::main] -async fn http_patch_request(url: String, key: &str, body: String ) -> Result<(), Box> { - let client = reqwest::Client::new(); - let c = client - .patch(url) - .body(body) - .header("content-type", "application/json; charset=utf-8") - .header(USER_AGENT, "Pluralsync") - .header(AUTHORIZATION, key); - let _res = c.send() - .await?; - Ok(()) -} - -#[tokio::main] -async fn http_patch_request_fedi(url: String, key: &str, file_path: String ) -> Result<(), Box> { - let client = reqwest::Client::new(); - let form = Form::new().part("avatar", Part::bytes(fs::read(file_path).unwrap()).file_name("face.png").mime_str("image/png").unwrap()); - let c = client - .patch(url) - .multipart(form) - .header(USER_AGENT, "Pluralsync") - .header(AUTHORIZATION, key); - let _res = c.send() - .await?; - Ok(()) -} diff --git a/src/systemdata.rs b/src/systemdata.rs new file mode 100644 index 0000000..46e9784 --- /dev/null +++ b/src/systemdata.rs @@ -0,0 +1,28 @@ +use serde::{Serialize, Deserialize}; + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct System { + pub pk_userid: String, + pub sp_userid: String, + pub members: Vec, +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct Member { + pub pk_id: String, + pub sp_id: String, + pub name: String, + pub alias: String +} + +#[derive(Debug)] +pub struct Fronters { + pub sp: Vec, + pub pk: Vec, +} + +impl Fronters { + pub fn new(pk: Vec, sp: Vec) -> Self { + Self {sp, pk} + } +}