Merge pull request 'Fedi support' (#1) from akkomadev into main
Slay queen
This commit is contained in:
		
						commit
						974ef812a4
					
				
					 6 changed files with 118 additions and 44 deletions
				
			
		
							
								
								
									
										26
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										26
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -437,6 +437,16 @@ version = "0.3.17"
 | 
			
		|||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "mime_guess"
 | 
			
		||||
version = "2.0.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "mime",
 | 
			
		||||
 "unicase",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "miniz_oxide"
 | 
			
		||||
version = "0.7.1"
 | 
			
		||||
| 
						 | 
				
			
			@ -667,6 +677,7 @@ dependencies = [
 | 
			
		|||
 "js-sys",
 | 
			
		||||
 "log",
 | 
			
		||||
 "mime",
 | 
			
		||||
 "mime_guess",
 | 
			
		||||
 "native-tls",
 | 
			
		||||
 "once_cell",
 | 
			
		||||
 "percent-encoding",
 | 
			
		||||
| 
						 | 
				
			
			@ -1009,6 +1020,15 @@ version = "0.2.4"
 | 
			
		|||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "unicase"
 | 
			
		||||
version = "2.7.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "version_check",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "unicode-bidi"
 | 
			
		||||
version = "0.3.13"
 | 
			
		||||
| 
						 | 
				
			
			@ -1047,6 +1067,12 @@ version = "0.2.15"
 | 
			
		|||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "version_check"
 | 
			
		||||
version = "0.9.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "want"
 | 
			
		||||
version = "0.3.1"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,7 +9,7 @@ edition = "2021"
 | 
			
		|||
dirs = "5.0.1"
 | 
			
		||||
serde = { version = "1.0.192", features = ["derive"] }
 | 
			
		||||
serde_json = "1.0"
 | 
			
		||||
reqwest = { version = "0.11.22", features = ["json"] }
 | 
			
		||||
reqwest = { version = "0.11.22", features = ["json", "multipart"] }
 | 
			
		||||
rofi = "0.3.0"
 | 
			
		||||
tokio = { version = "1", features = ["full"] }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										31
									
								
								README.org
									
										
									
									
									
								
							
							
						
						
									
										31
									
								
								README.org
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -13,16 +13,21 @@ On the first run, if it does not exist, PluralSync will create a configuration d
 | 
			
		|||
{
 | 
			
		||||
  "pk_key": "// Pluralkit token",
 | 
			
		||||
  "sp_key": "// Simplplural token",
 | 
			
		||||
  "pfp_module": {
 | 
			
		||||
  "avatar_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"
 | 
			
		||||
    "avatar_folder": "// Folder to grab profile pictures, they follow they member1member2...memberX.png format",
 | 
			
		||||
    "avatar_output_path": "// Path for the copied selected avatar"
 | 
			
		||||
  },
 | 
			
		||||
  "disc_module": {
 | 
			
		||||
    "enabled": false,
 | 
			
		||||
    "token": "// Discord user token",
 | 
			
		||||
    "python_path": "// Path to the python executable",
 | 
			
		||||
    "script_path": "// Path to updatepfp.py"
 | 
			
		||||
    "script_path": "// Path to updatediscordavatar.py"
 | 
			
		||||
  },
 | 
			
		||||
  "fedi_module": {
 | 
			
		||||
    "enabled": false,
 | 
			
		||||
    "instance" : "// Fedi instance url",
 | 
			
		||||
    "token": "// Fedi bearer token"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#+end_src
 | 
			
		||||
| 
						 | 
				
			
			@ -43,17 +48,22 @@ Just fill it with PluralKit and SimplyPlural tokens associated to your account.
 | 
			
		|||
- *Memberlist* - Writes the known list of members based on the =system.json= file.
 | 
			
		||||
 | 
			
		||||
** Modules
 | 
			
		||||
*** PFP module
 | 
			
		||||
*** Avatar module
 | 
			
		||||
Will get a profile picture from a folder based on the currently fronting members and will copy it to the desired location. I recommend something it's easy to have at hand like =~/face=.
 | 
			
		||||
 | 
			
		||||
The pictures in the path should be named according to the front. For example, if Bob and Alice are fronting, the module will copy ={PATH}/bobalice.png= to the location, or if it's only Bob it'll be =bob.png=.
 | 
			
		||||
Notice this will cover all combinations, if the main front is Alice the combination =alicebob.png= instead. I recommend the use of a python script to generate the possible combinations.
 | 
			
		||||
Notice this will cover all combinations, if the main front is Alice the combination =alicebob.png= instead. I personally use a python script to generate my set.
 | 
			
		||||
 | 
			
		||||
*** Discord module
 | 
			
		||||
**Use of this module goes against discord terms of service. USE AT YOUR OWN RISK.**
 | 
			
		||||
 | 
			
		||||
Requires the =PFP module= to be enabled and the latest version of [[https://github.com/dolfies/discord.py-self/][discord.py-self]].
 | 
			
		||||
Will change your discord avatar with the one selected by the PFP module.
 | 
			
		||||
Requires the =avatar module= to be enabled and the latest version of [[https://github.com/dolfies/discord.py-self/][discord.py-self]].
 | 
			
		||||
Will change your discord avatar with the one selected by the avatar module.
 | 
			
		||||
 | 
			
		||||
*** Fedi module
 | 
			
		||||
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.
 | 
			
		||||
| 
						 | 
				
			
			@ -65,5 +75,6 @@ cargo build -r
 | 
			
		|||
* 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)
 | 
			
		||||
- [ ] Rofi and zenity support
 | 
			
		||||
- [ ] Proper way to handle front conflicts between PluralKit and SimplyPlural
 | 
			
		||||
- [ ] 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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,15 +1,20 @@
 | 
			
		|||
{
 | 
			
		||||
  "pk_key": "// Pluralkit token",
 | 
			
		||||
  "sp_key": "// Simplplural token",
 | 
			
		||||
  "pfp_module": {
 | 
			
		||||
  "avatar_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"
 | 
			
		||||
    "avatar_folder": "// Folder to grab profile pictures, they follow they member1member2...memberX.png format",
 | 
			
		||||
    "avatar_output_path": "// Path for the copied selected avatar"
 | 
			
		||||
  },
 | 
			
		||||
  "disc_module": {
 | 
			
		||||
    "enabled": false,
 | 
			
		||||
    "token": "// Discord user token",
 | 
			
		||||
    "python_path": "// Path to the python executable",
 | 
			
		||||
    "script_path": "// Path to updatepfp.py"
 | 
			
		||||
    "script_path": "// Path to updatediscordavatar.py"
 | 
			
		||||
  },
 | 
			
		||||
  "fedi_module": {
 | 
			
		||||
    "enabled": false,
 | 
			
		||||
    "instance" : "// Fedi instance url",
 | 
			
		||||
    "token": "// Fedi bearer token"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										82
									
								
								src/main.rs
									
										
									
									
									
								
							
							
						
						
									
										82
									
								
								src/main.rs
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -5,6 +5,7 @@ use std::collections::HashMap;
 | 
			
		|||
use std::process::Command;
 | 
			
		||||
 | 
			
		||||
use reqwest::header::{USER_AGENT, AUTHORIZATION};
 | 
			
		||||
use reqwest::multipart::{Part, Form};
 | 
			
		||||
 | 
			
		||||
use serde::{Serialize, Deserialize};
 | 
			
		||||
use serde_json::Value;
 | 
			
		||||
| 
						 | 
				
			
			@ -15,15 +16,16 @@ use dirs;
 | 
			
		|||
struct Config {
 | 
			
		||||
    pk_key: String,
 | 
			
		||||
    sp_key: String,
 | 
			
		||||
    pfp_module: PfpModule,
 | 
			
		||||
    disc_module: DiscModule
 | 
			
		||||
    avatar_module: AvatarModule,
 | 
			
		||||
    disc_module: DiscModule,
 | 
			
		||||
    fedi_module: FediModule,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Serialize, Deserialize)]
 | 
			
		||||
struct PfpModule {
 | 
			
		||||
struct AvatarModule {
 | 
			
		||||
    enabled: bool,
 | 
			
		||||
    pfp_folder: String,
 | 
			
		||||
    pfp_output_path: String,
 | 
			
		||||
    avatar_folder: String,
 | 
			
		||||
    avatar_output_path: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Serialize, Deserialize)]
 | 
			
		||||
| 
						 | 
				
			
			@ -34,6 +36,13 @@ struct DiscModule {
 | 
			
		|||
    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,
 | 
			
		||||
| 
						 | 
				
			
			@ -65,19 +74,24 @@ 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 to the python executable",
 | 
			
		||||
        "script_path": "// Path to updatepfp.py"
 | 
			
		||||
    }
 | 
			
		||||
  "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"
 | 
			
		||||
  },
 | 
			
		||||
  "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"
 | 
			
		||||
  }
 | 
			
		||||
}"#;
 | 
			
		||||
 | 
			
		||||
const HELP_STRING: &str = r#"PluralSync
 | 
			
		||||
| 
						 | 
				
			
			@ -293,16 +307,16 @@ fn get(config_path: String) -> Result<(), &'static str> {
 | 
			
		|||
    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 config.avatar_module.enabled {
 | 
			
		||||
        let avatarnames = names.join("").to_lowercase() + ".png";
 | 
			
		||||
 | 
			
		||||
        if Path::new(&config.pfp_module.pfp_output_path).exists() {
 | 
			
		||||
            let _ = remove_file(&config.pfp_module.pfp_output_path);
 | 
			
		||||
        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.pfp_module.pfp_folder, pfpnames, &config.pfp_module.pfp_output_path);
 | 
			
		||||
            Command::new("cmd").args(["/C", &cmdaug]).output().expect("PFP module error");
 | 
			
		||||
            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");
 | 
			
		||||
| 
						 | 
				
			
			@ -310,12 +324,16 @@ fn get(config_path: String) -> Result<(), &'static str> {
 | 
			
		|||
            }
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            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");
 | 
			
		||||
            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);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -620,3 +638,17 @@ async fn http_patch_request(url: String, key: &str, body: String ) -> Result<(),
 | 
			
		|||
        .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(())
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,10 +13,10 @@ 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)
 | 
			
		||||
        avatar_path = json["avatar_module"]["avatar_output_path"]
 | 
			
		||||
        fp = open(avatar_path, 'rb')
 | 
			
		||||
        avatar = fp.read()
 | 
			
		||||
        await self.user.edit(avatar=avatar)
 | 
			
		||||
        print('Discord module finished')
 | 
			
		||||
        await self.close()
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	Add table
		
		Reference in a new issue