Inital Commit
This commit is contained in:
parent
4bab661b50
commit
ec7268f645
14
.gitignore
vendored
Normal file
14
.gitignore
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Generated by Cargo
|
||||||
|
# will have compiled files and executables
|
||||||
|
debug/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||||
|
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||||
|
Cargo.lock
|
||||||
|
|
||||||
|
# These are backup files generated by rustfmt
|
||||||
|
**/*.rs.bk
|
||||||
|
|
||||||
|
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||||
|
*.pdb
|
||||||
21
Cargo.toml
Normal file
21
Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
[package]
|
||||||
|
name = "neo"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Woljix"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
matrix-sdk = "0.2.0"
|
||||||
|
url = "2.2.1"
|
||||||
|
tokio = { version = "0.2.25", features = ["full"] }
|
||||||
|
mime = "0.3.16"
|
||||||
|
serde = { version = "1.0.126", features = ["derive"] }
|
||||||
|
serde_json = "1.0.64"
|
||||||
|
indexmap = { version = "1.6.2", features = ["serde-1"] }
|
||||||
|
rand = "0.8.3"
|
||||||
|
|
||||||
|
#[profile.release]
|
||||||
|
#opt-level = 'z'
|
||||||
|
#lto = 'thin'
|
||||||
38
src/config.rs
Normal file
38
src/config.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
use indexmap::IndexMap;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct ConfigLogin {
|
||||||
|
pub username: String,
|
||||||
|
pub password: String,
|
||||||
|
pub device_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ConfigLogin {
|
||||||
|
fn default() -> ConfigLogin {
|
||||||
|
ConfigLogin {
|
||||||
|
username: "".to_string(),
|
||||||
|
password: "".to_string(),
|
||||||
|
device_id: "".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct ConfigImages {
|
||||||
|
pub last_image: String,
|
||||||
|
pub files: Vec<String>,
|
||||||
|
pub cache: IndexMap<String, String>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ConfigImages {
|
||||||
|
fn default() -> ConfigImages {
|
||||||
|
ConfigImages {
|
||||||
|
last_image: "".to_string(),
|
||||||
|
files: Vec::new(),
|
||||||
|
cache: IndexMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
147
src/main.rs
Normal file
147
src/main.rs
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
use matrix_sdk::{self, Client};
|
||||||
|
use rand::prelude::SliceRandom;
|
||||||
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
io::{Error, ErrorKind},
|
||||||
|
path::PathBuf,
|
||||||
|
process,
|
||||||
|
};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
mod config;
|
||||||
|
use config::{ConfigImages, ConfigLogin};
|
||||||
|
mod settings;
|
||||||
|
use settings::Settings;
|
||||||
|
|
||||||
|
const HOME_SERVER: &str = "https://e1m1.xyz";
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
if let Err(e) = run().await {
|
||||||
|
println!("Application error: {}", e);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run() -> Result<(), Box<std::io::Error>> {
|
||||||
|
let dir_exe: PathBuf = {
|
||||||
|
let exe = std::env::current_exe().unwrap();
|
||||||
|
let parent = exe.parent().unwrap();
|
||||||
|
parent.to_path_buf()
|
||||||
|
};
|
||||||
|
|
||||||
|
let dir_login = &dir_exe.join("login.json");
|
||||||
|
let dir_images = &dir_exe.join("images.json");
|
||||||
|
|
||||||
|
let settings_login: Settings<ConfigLogin> =
|
||||||
|
Settings::<ConfigLogin>::load(&dir_login).unwrap();
|
||||||
|
|
||||||
|
let mut settings_images: Settings<ConfigImages> =
|
||||||
|
Settings::<ConfigImages>::load(&dir_images).unwrap();
|
||||||
|
|
||||||
|
let conf_login = &settings_login.loaded;
|
||||||
|
let conf_images = &mut settings_images.loaded;
|
||||||
|
|
||||||
|
let mut flag_debug: bool = false;
|
||||||
|
let mut flag_generate: bool = false;
|
||||||
|
|
||||||
|
for x in std::env::args() {
|
||||||
|
match x.as_str() {
|
||||||
|
"-debug" => {
|
||||||
|
flag_debug = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
"generate" => {
|
||||||
|
flag_generate = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if flag_generate {
|
||||||
|
println!("Generating config files..");
|
||||||
|
Settings::<ConfigLogin>::new(ConfigLogin::default())
|
||||||
|
.unwrap()
|
||||||
|
.save(&dir_login.with_extension("json.template").clone())
|
||||||
|
.unwrap();
|
||||||
|
Settings::<ConfigImages>::new(ConfigImages::default())
|
||||||
|
.unwrap()
|
||||||
|
.save(&dir_images.with_extension("json.template").clone())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(client) = login(
|
||||||
|
conf_login.username.as_str(),
|
||||||
|
conf_login.password.as_str(),
|
||||||
|
conf_login.device_id.as_str(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
let selection = {
|
||||||
|
if flag_debug {
|
||||||
|
// Note: This assumes that "last_image" is not empty and has a valid image path.
|
||||||
|
conf_images.last_image.to_string()
|
||||||
|
} else {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
conf_images.files.choose(&mut rng).unwrap().to_string()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("Selection was: '{}'", selection);
|
||||||
|
|
||||||
|
if conf_images.cache.contains_key(&selection) {
|
||||||
|
if let Some(url) = conf_images.cache.get(&selection) {
|
||||||
|
println!("Setting Avatar URL: '{}'", url);
|
||||||
|
client.set_avatar_url(Some(url)).await.unwrap();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let path = dir_exe.join(&selection);
|
||||||
|
if path.exists() {
|
||||||
|
conf_images.last_image = selection.clone();
|
||||||
|
|
||||||
|
let mut image = File::open(&path).unwrap();
|
||||||
|
match client.upload_avatar(&mime::IMAGE_JPEG, &mut image).await {
|
||||||
|
Ok(_) => {
|
||||||
|
println!("Successfully uploaded avatar!");
|
||||||
|
if let Some(avatar_url) = client.avatar_url().await.unwrap() {
|
||||||
|
conf_images.cache.insert(selection.clone(), avatar_url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
return Err(Box::new(Error::new(
|
||||||
|
ErrorKind::Other,
|
||||||
|
format!("Error occurred while uploading avatar: '{}'", err),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("Error: file does not exist!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
settings_images.save(&dir_images).unwrap();
|
||||||
|
} else {
|
||||||
|
return Err(Box::new(Error::new(ErrorKind::Other, "Login failed!")));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn login(
|
||||||
|
username: &str,
|
||||||
|
password: &str,
|
||||||
|
device_id: &str,
|
||||||
|
) -> Result<Client, matrix_sdk::Error> {
|
||||||
|
let client = Client::new(Url::parse(HOME_SERVER).unwrap()).unwrap();
|
||||||
|
|
||||||
|
match client
|
||||||
|
.login(username, password, Some(device_id), Some("NEO bot"))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => return Ok(client),
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/settings.rs
Normal file
45
src/settings.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
use std::{fs::OpenOptions};
|
||||||
|
use std::path::Path;
|
||||||
|
use std::io::{self, Write, prelude::*};
|
||||||
|
use serde::Serialize;
|
||||||
|
use serde::de::DeserializeOwned;
|
||||||
|
use serde_json;
|
||||||
|
|
||||||
|
pub struct Settings<T> {
|
||||||
|
pub loaded: T
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: DeserializeOwned + Serialize> Settings<T> {
|
||||||
|
pub fn new(import: T) -> io::Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
loaded: import
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load(file: &Path) -> io::Result<Self> {
|
||||||
|
let mut f = match OpenOptions::new().read(true).write(false).open(&file) {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(error) => panic!("Error opening this file: {:?}", error)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut contents = String::new();
|
||||||
|
f.read_to_string(&mut contents).unwrap();
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
loaded: serde_json::from_str(contents.as_str()).unwrap()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save(&self, file: &Path) -> io::Result<()> {
|
||||||
|
let json = serde_json::to_string_pretty(&self.loaded).unwrap();
|
||||||
|
|
||||||
|
let mut f = match OpenOptions::new().read(true).write(true).create(true).open(&file) {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(error) => panic!("Error saving this file: {:?}", error)
|
||||||
|
};
|
||||||
|
|
||||||
|
f.write_all(json.as_bytes()).unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user