features, documentation, clap

This commit is contained in:
Junko 2024-01-16 14:02:24 +01:00
parent 34d0ccbc35
commit 27f6210669
7 changed files with 656 additions and 256 deletions

227
Cargo.lock generated
View File

@ -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",
]

View File

@ -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

View File

@ -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

75
src/clap_ps.rs Normal file
View File

@ -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 <lafresita@protonmail.com>")]
#[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<String>,
/// 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<ForceFrom>,
/// 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<String>,
/// 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,
}

38
src/configdata.rs Normal file
View File

@ -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,
}

View File

@ -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<Member>,
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
struct Member {
pk_id: String,
sp_id: String,
name: String,
alias: String
}
#[derive(Debug)]
struct Fronters {
sp: Vec<Member>,
pk: Vec<Member>,
}
impl Fronters {
fn new(pk: Vec<Member>, sp: Vec<Member>) -> 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<String> = 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<String>) -> 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<Member> = 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<String>) -> 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<Member> = 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<Member> = 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<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);
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<String>) {
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<Value> {
let url = format!("{}/systems/{}/members", PK_URL, sysid);
let res = http_get_request(url,key);
let res = http_get(url,key);
let datas: Vec<Value> = serde_json::from_str(&res.unwrap()).unwrap();
return datas;
@ -359,7 +442,7 @@ fn pk_get_members(key: &str, sysid: &str) -> Vec<Value> {
fn pk_get_fronters(key: &str, sys: &System) -> Vec<Member> {
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<Member>, fronters: &F
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 _ = 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<Member>, 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<String, String> {
let url = format!("{}/members/{}", SP_URL, system_id);
let res = http_get_request(url,key);
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();
@ -432,7 +529,7 @@ fn get_sp_id(mem: &Member, ids: &HashMap<String, String>) -> String {
fn sp_get_fronters(key: &str, sys: &System) -> Vec<Member> {
let url = format!("{}/fronters", SP_URL);
let res = http_get_request(url,key);
let res = http_get(url,key);
let datas: Vec<Value> = 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<Member> {
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<Value> = serde_json::from_str(&res.unwrap()).unwrap();
for data in datas {
@ -491,7 +588,19 @@ fn sp_set_fronters(key: &str, to_front: &Vec<Member>, 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<Member>, 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<String, Box<dyn std::error::Error>> {
async fn http_get(url: String, key: &str) -> Result<String, Box<dyn std::error::Error>> {
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<String, Box<dyn std::error::Error>> {
let res = rb.send()
.await?
.text()
.await?;
Ok(res)
}
#[tokio::main]
async fn http_post_request(url: String, key: &str, body: &HashMap<&str, Vec<String>>) -> Result<(), Box<dyn std::error::Error>> {
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<dyn std::error::Error>> {
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<dyn std::error::Error>> {
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<dyn std::error::Error>> {
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(())
}

28
src/systemdata.rs Normal file
View File

@ -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<Member>,
}
#[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<Member>,
pub pk: Vec<Member>,
}
impl Fronters {
pub fn new(pk: Vec<Member>, sp: Vec<Member>) -> Self {
Self {sp, pk}
}
}