diff --git a/Cargo.toml b/Cargo.toml index 3b92daa..d80c996 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,8 +8,8 @@ edition = "2021" [dependencies] base64 = "0.22.1" clap = { version = "4.5.28", features = ["derive"] } +dynfmt = "0.1.5" flate2 = "1.1.2" -formatx = "0.2.3" futures = "0.3.31" poise = "0.6.1" pyo3 = "0.28.2" diff --git a/src/cmds/eight_ball.rs b/src/cmds/eight_ball.rs index 77d560f..653d261 100644 --- a/src/cmds/eight_ball.rs +++ b/src/cmds/eight_ball.rs @@ -1,6 +1,6 @@ use rand::{seq::IteratorRandom, Rng}; -use crate::{data::get_toml_mutex, lang, messages::send_msg, Context, Error}; +use crate::{db::generic::get_toml_mutex, lang, messages::send_msg, Context, Error}; #[poise::command( diff --git a/src/cmds/wwrps.rs b/src/cmds/wwrps.rs index 1bde477..d72d2f2 100644 --- a/src/cmds/wwrps.rs +++ b/src/cmds/wwrps.rs @@ -3,7 +3,7 @@ use std::fmt::Display; use poise::serenity_prelude::{ChannelId, Mentionable, User}; use tokio::sync::MutexGuard; -use crate::{Context, Error, data::get_mutex_data, lang, messages::{http_send_msg, send_msg}}; +use crate::{Context, Error, db::generic::get_json_mutex, lang, messages::{http_send_msg, send_msg}}; #[derive(poise::ChoiceParameter, PartialEq, Clone, Debug)] @@ -151,7 +151,7 @@ fn results_text(game: &MutexGuard<'_, RPSGame>) -> String { async fn get_wwrps_channel(ctx: Context<'_>) -> Option { - let d = get_mutex_data(&ctx.data().discord_data).await.unwrap(); + let d = get_json_mutex(&ctx.data().discord_data).await.unwrap(); let is_guild = ctx.guild_channel().await.is_some(); diff --git a/src/data.rs b/src/data.rs deleted file mode 100644 index 63222bb..0000000 --- a/src/data.rs +++ /dev/null @@ -1,252 +0,0 @@ -use std::{fs, io::Write}; -use std::path::Path; - -use serde_json::{self, Value, json}; -use tokio::sync::Mutex; - -use crate::{errln, rs_println, rs_warnln, Data, Error, CFG_DATA_RE, LANG, LANG_NAME}; -use crate::websocket::send_cmd_json; - - -static DATA_PATH_DC: &str = "./data/db/dc_data.json"; -static PRESET_PATH_DC: &str = "./data/defaults/dc_data_preset.json"; - -static DATA_PATH_RE: &str = "./data/db/re_data.json"; -static PRESET_PATH_RE: &str = "./data/defaults/re_data_preset.json"; - -static DATA_PATH_CFG: &str = "./cfg/cfg.toml"; -static PRESET_PATH_CFG: &str = "./data/defaults/cfg_default.toml"; - -static DATA_PATH_LANG: &str = "./data/lang/"; - -pub static DC_POSTS_CHANNEL_KEY: &str = "re_posts_channel"; -pub static DC_WWRPS_CHANNEL_KEY: &str = "wwrps_channel"; - - -pub async fn read_dc_data(data: &Data, wipe: bool) { - if !Path::new(DATA_PATH_DC).exists() || wipe { - rs_println!( - "{} creating new from preset...", - if !wipe { "discord_data.json not found," } else { "[WIPE] (discord_data.json)" } - ); - generate_dc_data(); - } - - let str_data = fs::read_to_string(DATA_PATH_DC).unwrap(); - let json_data = serde_json::from_str(&str_data).unwrap(); - let mut dc_data = data.discord_data.lock().await; - *dc_data = json_data; -} - - -fn generate_dc_data() { - let preset_str = fs::read_to_string(PRESET_PATH_DC).unwrap(); - let mut preset_json: Value = serde_json::from_str(&preset_str).unwrap(); - - if let Some(servers) = preset_json["servers"].as_object_mut() { - servers.remove("SERVER ID"); - } - - let json_str = serde_json::to_string_pretty(&preset_json).unwrap(); - - let mut file = fs::File::create(DATA_PATH_DC).unwrap(); - file.write_all(json_str.as_bytes()).unwrap(); -} - - -pub async fn write_dc_data(data: &Data) { - if !Path::new(DATA_PATH_DC).exists() { - generate_dc_data(); - } - - let mut file = fs::OpenOptions::new() - .write(true) - .truncate(true) - .open(DATA_PATH_DC) - .unwrap(); - - - let mut dc_data_lock = data.discord_data.lock().await; - let dc_data = dc_data_lock.as_mut().unwrap(); - let json_str = serde_json::to_string_pretty(dc_data).unwrap(); - - file.write_all(json_str.as_bytes()).unwrap(); -} - - -pub async fn read_re_data(data: &Data, wipe: bool) { - if !Path::new(DATA_PATH_RE).exists() || wipe { - rs_println!( - "{} creating new from preset...", - if !wipe { "reddit_data.json not found," } else { "[WIPE] (reddit_data.json)" } - ); - generate_re_data(); - } - - let str_data = fs::read_to_string(DATA_PATH_RE).unwrap(); - let json_data: Option = serde_json::from_str(&str_data).unwrap(); - let mut re_data = data.reddit_data.lock().await; - *re_data = json_data; -} - - -fn generate_re_data() { - let preset_str = fs::read_to_string(PRESET_PATH_RE).unwrap(); - let mut preset_json: Value = serde_json::from_str(&preset_str).unwrap(); - - if let Some(bk_week) = preset_json[CFG_DATA_RE].as_object_mut() { - bk_week.remove("EXAMPLE URL"); - } - else { - rs_warnln!("Couldn't find key \"{}\" in the Reddit data file ({})!", CFG_DATA_RE, DATA_PATH_RE); - } - - let json_str = serde_json::to_string_pretty(&preset_json).unwrap(); - - let mut file = fs::File::create(DATA_PATH_RE).unwrap(); - file.write_all(json_str.as_bytes()).unwrap(); -} - - -pub async fn update_re_data(data: &Data) { - send_cmd_json("update_data_file", None, true).await; - read_re_data(data, false).await; -} - - -pub async fn write_re_data() { - send_cmd_json("update_data_file", None, true).await; -} - - -pub async fn read_cfg_data(data: &Data, wipe: bool) -> Option { - if !Path::new(DATA_PATH_CFG).exists() || wipe { - rs_println!( - "{} creating new from preset...", - if !wipe { "cfg.toml not found," } else { "[WIPE] (cfg.toml)" } - ); - generate_cfg_data(); - } - - let str_data = fs::read_to_string(DATA_PATH_CFG).unwrap(); - let json_data: toml::Value = str_data.parse().unwrap(); - let mut cfg_data = data.cfg.lock().await; - *cfg_data = Some(json_data.clone()); - - let r = send_cmd_json("update_cfg", Some(json!([toml::to_string(&json_data).unwrap()])), true).await; - return r; -} - - -fn generate_cfg_data() { - let preset_str = fs::read_to_string(PRESET_PATH_CFG).unwrap(); - - let mut file = fs::File::create(DATA_PATH_CFG).unwrap(); - file.write_all(preset_str.as_bytes()).unwrap(); -} - - -pub async fn dc_add_server(data: &Data, server_id: u64) -> Result<(), ()> { - let mut dc_data_lock = data.discord_data.lock().await; - let dc_data = dc_data_lock.as_mut().unwrap(); - - if dc_data.get("servers").is_none() { return Err(()); } - - let servers = dc_data["servers"].as_object_mut().unwrap(); - - if !servers.contains_key(&server_id.to_string()) { - servers.insert(server_id.to_string(), json!({})); - } - - return Ok(()); -} - - -pub async fn dc_bind_bk(data: &Data, server_id: u64, channel_id: u64) -> Result<(), ()> { - let mut dc_data_lock = data.discord_data.lock().await; - let dc_data = dc_data_lock.as_mut().unwrap(); - - if dc_data.get("servers").is_none() { return Err(()); } - - let servers = dc_data["servers"].as_object_mut().unwrap(); - - if !servers.contains_key(&server_id.to_string()) { - return Err(()); - } - - let server = servers[&server_id.to_string()].as_object_mut().unwrap(); - - server.insert(DC_POSTS_CHANNEL_KEY.to_string(), channel_id.into()); - - return Ok(()); -} - - -pub async fn bind_wwrps(data: &Data, server_id: u64, channel_id: u64) -> Result<(), ()> { - let mut dc_data_lock = data.discord_data.lock().await; - let dc_data = dc_data_lock.as_mut().unwrap(); - - if dc_data.get("servers").is_none() { return Err(()); } - - let servers = dc_data["servers"].as_object_mut().unwrap(); - - if !servers.contains_key(&server_id.to_string()) - { return Err(()); } - - let server = servers[&server_id.to_string()].as_object_mut().unwrap(); - - server.insert(DC_WWRPS_CHANNEL_KEY.to_string(), channel_id.into()); - - return Ok(()); -} - - -pub async fn dc_contains_server(data: &Data, server_id: u64) -> bool { - let dc_data_lock = data.discord_data.lock().await; - let dc_data = dc_data_lock.as_ref().unwrap(); - - if dc_data.get("servers").is_none() { return false; } - - let mut clone = dc_data.clone(); - let servers = clone["servers"].as_object_mut().unwrap(); - - return servers.contains_key(&server_id.to_string()) -} - - -pub async fn get_mutex_data(data: &Mutex>) -> Result { - let data_lock = data.lock().await; - return match data_lock.as_ref() { - Some(data) => Ok(data.clone()), - None => Err("Cannot get mutex data: The data is corrupted!".into()), - }; -} - - -pub async fn get_toml_mutex(data: &Mutex>) -> Result { - let data_lock = data.lock().await; - return match data_lock.as_ref() { - Some(data) => Ok(data.clone()), - None => Err("Cannot get mutex data: The data is corrupted!".into()), - }; -} - - -pub fn load_lang_data(lang: String) { - let full_path = format!("{}{}.json", DATA_PATH_LANG, lang); - - if !Path::new(&full_path).exists() { - errln!( - "File for language \"{0}\" ({0}.json) not found!\n Hint: You can download official language files at https://github.com/ByteDice/ByteDiceAssistant in the data/langs/... folder", - lang - ); - } - - let str_data = fs::read_to_string(full_path).unwrap(); - let json_data: Option = serde_json::from_str(&str_data).unwrap(); - - unsafe { - LANG = json_data; - LANG_NAME = Some(lang); - }; -} \ No newline at end of file diff --git a/src/db.rs b/src/db.rs new file mode 100644 index 0000000..0936f4a --- /dev/null +++ b/src/db.rs @@ -0,0 +1,5 @@ +pub mod cfg; +pub mod discord; +pub mod generic; +pub mod keys; +pub mod reddit; \ No newline at end of file diff --git a/src/db/cfg.rs b/src/db/cfg.rs new file mode 100644 index 0000000..f23085b --- /dev/null +++ b/src/db/cfg.rs @@ -0,0 +1,41 @@ +use std::{fs, io::Write, path::Path}; + +use serde_json::{Value, json}; + +use crate::{Data, rs_println, websocket::send_cmd_json}; + + +static DATA_PATH: &str = "./cfg/cfg.toml"; +static PRESET_PATH: &str = "./data/defaults/cfg_default.toml"; + + +pub async fn read_data(data: &Data, wipe: bool) -> Option { + if !Path::new(DATA_PATH).exists() || wipe { + rs_println!( + "{} creating new from preset...", + if !wipe { "cfg.toml not found," } else { "[WIPE] (cfg.toml)" } + ); + generate_data(); + } + + let str_data = fs::read_to_string(DATA_PATH).unwrap(); + let json_data: toml::Value = str_data.parse().unwrap(); + let mut cfg_data = data.cfg.lock().await; + *cfg_data = Some(json_data.clone()); + + let r = send_cmd_json( + "update_cfg", + Some(json!([toml::to_string(&json_data).unwrap()])), + true + ).await; + + return r; +} + + +fn generate_data() { + let preset_str = fs::read_to_string(PRESET_PATH).unwrap(); + + let mut file = fs::File::create(DATA_PATH).unwrap(); + file.write_all(preset_str.as_bytes()).unwrap(); +} diff --git a/src/db/discord.rs b/src/db/discord.rs new file mode 100644 index 0000000..3e97f9d --- /dev/null +++ b/src/db/discord.rs @@ -0,0 +1,127 @@ +use std::{fs, io::Write, path::Path}; + +use serde_json::{Value, json}; + +use crate::{Data, db::keys, rs_println}; + +static DATA_PATH: &str = "./data/db/dc_data.json"; +static PRESET_PATH: &str = "./data/defaults/dc_data_preset.json"; + + +pub async fn read_data(data: &Data, wipe: bool) { + if !Path::new(DATA_PATH).exists() || wipe { + rs_println!( + "{} creating new from preset...", + if !wipe { "discord_data.json not found," } else { "[WIPE] (discord_data.json)" } + ); + generate_data(); + } + + let str_data = fs::read_to_string(DATA_PATH).unwrap(); + let json_data = serde_json::from_str(&str_data).unwrap(); + let mut dc_data = data.discord_data.lock().await; + *dc_data = json_data; +} + + +fn generate_data() { + let preset_str = fs::read_to_string(PRESET_PATH).unwrap(); + let mut preset_json: Value = serde_json::from_str(&preset_str).unwrap(); + + if let Some(servers) = preset_json["servers"].as_object_mut() { + servers.remove("SERVER ID"); + } + + let json_str = serde_json::to_string_pretty(&preset_json).unwrap(); + + let mut file = fs::File::create(DATA_PATH).unwrap(); + file.write_all(json_str.as_bytes()).unwrap(); +} + + +pub async fn write_data(data: &Data) { + if !Path::new(DATA_PATH).exists() { + generate_data(); + } + + let mut file = fs::OpenOptions::new() + .write(true) + .truncate(true) + .open(DATA_PATH) + .unwrap(); + + + let mut dc_data_lock = data.discord_data.lock().await; + let dc_data = dc_data_lock.as_mut().unwrap(); + let json_str = serde_json::to_string_pretty(dc_data).unwrap(); + + file.write_all(json_str.as_bytes()).unwrap(); +} + + +pub async fn add_server(data: &Data, server_id: u64) -> Result<(), ()> { + let mut dc_data_lock = data.discord_data.lock().await; + let dc_data = dc_data_lock.as_mut().unwrap(); + + if dc_data.get("servers").is_none() { return Err(()); } + + let servers = dc_data["servers"].as_object_mut().unwrap(); + + if !servers.contains_key(&server_id.to_string()) { + servers.insert(server_id.to_string(), json!({})); + } + + return Ok(()); +} + + +pub async fn bind_bk(data: &Data, server_id: u64, channel_id: u64) -> Result<(), ()> { + let mut dc_data_lock = data.discord_data.lock().await; + let dc_data = dc_data_lock.as_mut().unwrap(); + + if dc_data.get("servers").is_none() { return Err(()); } + + let servers = dc_data["servers"].as_object_mut().unwrap(); + + if !servers.contains_key(&server_id.to_string()) { + return Err(()); + } + + let server = servers[&server_id.to_string()].as_object_mut().unwrap(); + + server.insert(keys::DC_POSTS_CHANNEL_KEY.to_string(), channel_id.into()); + + return Ok(()); +} + + +pub async fn bind_wwrps(data: &Data, server_id: u64, channel_id: u64) -> Result<(), ()> { + let mut dc_data_lock = data.discord_data.lock().await; + let dc_data = dc_data_lock.as_mut().unwrap(); + + if dc_data.get("servers").is_none() { return Err(()); } + + let servers = dc_data["servers"].as_object_mut().unwrap(); + + if !servers.contains_key(&server_id.to_string()) + { return Err(()); } + + let server = servers[&server_id.to_string()].as_object_mut().unwrap(); + + server.insert(keys::DC_WWRPS_CHANNEL_KEY.to_string(), channel_id.into()); + + return Ok(()); +} + + +pub async fn contains_server(data: &Data, server_id: u64) -> bool { + let dc_data_lock = data.discord_data.lock().await; + let dc_data = dc_data_lock.as_ref().unwrap(); + + if dc_data.get("servers").is_none() { return false; } + + let mut clone = dc_data.clone(); + let servers = clone["servers"].as_object_mut().unwrap(); + + return servers.contains_key(&server_id.to_string()) +} \ No newline at end of file diff --git a/src/db/generic.rs b/src/db/generic.rs new file mode 100644 index 0000000..3661800 --- /dev/null +++ b/src/db/generic.rs @@ -0,0 +1,21 @@ +use serde_json::Value; +use tokio::sync::Mutex; + +use crate::Error; + +pub async fn get_json_mutex(data: &Mutex>) -> Result { + let data_lock = data.lock().await; + return match data_lock.as_ref() { + Some(data) => Ok(data.clone()), + None => Err("Cannot get mutex data: The data is corrupted!".into()), + }; +} + + +pub async fn get_toml_mutex(data: &Mutex>) -> Result { + let data_lock = data.lock().await; + return match data_lock.as_ref() { + Some(data) => Ok(data.clone()), + None => Err("Cannot get mutex data: The data is corrupted!".into()), + }; +} \ No newline at end of file diff --git a/src/db/keys.rs b/src/db/keys.rs new file mode 100644 index 0000000..9bb213a --- /dev/null +++ b/src/db/keys.rs @@ -0,0 +1,2 @@ +pub static DC_POSTS_CHANNEL_KEY: &str = "re_posts_channel"; +pub static DC_WWRPS_CHANNEL_KEY: &str = "wwrps_channel"; \ No newline at end of file diff --git a/src/db/reddit.rs b/src/db/reddit.rs new file mode 100644 index 0000000..dadf4ab --- /dev/null +++ b/src/db/reddit.rs @@ -0,0 +1,56 @@ +use std::{fs, io::Write, path::Path}; + +use serde_json::Value; + +use crate::{Data, rs_println, rs_warnln, websocket::send_cmd_json}; + + +static DATA_PATH: &str = "./data/db/re_data.json"; +static PRESET_PATH: &str = "./data/defaults/re_data_preset.json"; + +pub static POSTS_KEY: &str = "posts"; + + +pub async fn read_data(data: &Data, wipe: bool) { + if !Path::new(DATA_PATH).exists() || wipe { + rs_println!( + "{} creating new from preset...", + if !wipe { "reddit_data.json not found," } else { "[WIPE] (reddit_data.json)" } + ); + generate_data(); + } + + let str_data = fs::read_to_string(DATA_PATH).unwrap(); + let json_data: Option = serde_json::from_str(&str_data).unwrap(); + let mut re_data = data.reddit_data.lock().await; + *re_data = json_data; +} + + +fn generate_data() { + let preset_str = fs::read_to_string(PRESET_PATH).unwrap(); + let mut preset_json: Value = serde_json::from_str(&preset_str).unwrap(); + + if let Some(bk_week) = preset_json[POSTS_KEY].as_object_mut() { + bk_week.remove("EXAMPLE URL"); + } + else { + rs_warnln!("Couldn't find key \"{}\" in the Reddit data file ({})!", POSTS_KEY, DATA_PATH); + } + + let json_str = serde_json::to_string_pretty(&preset_json).unwrap(); + + let mut file = fs::File::create(DATA_PATH).unwrap(); + file.write_all(json_str.as_bytes()).unwrap(); +} + + +pub async fn update_data(data: &Data) { + send_cmd_json("update_data_file", None, true).await; + read_data(data, false).await; +} + + +pub async fn write_data() { + send_cmd_json("update_data_file", None, true).await; +} \ No newline at end of file diff --git a/src/db_cmds/add_server.rs b/src/db_cmds/add_server.rs index 4d8658f..de756dd 100644 --- a/src/db_cmds/add_server.rs +++ b/src/db_cmds/add_server.rs @@ -1,11 +1,11 @@ -use crate::{data::dc_add_server, lang, messages::send_msg, Context, Error}; +use crate::{db::discord::add_server, lang, messages::send_msg, Context, Error}; pub async fn cmd( ctx: Context<'_> ) -> Result<(), Error> { - let r = dc_add_server(ctx.data(), ctx.guild_id().unwrap().into()).await; + let r = add_server(ctx.data(), ctx.guild_id().unwrap().into()).await; if r.is_ok() { send_msg(ctx, lang!("dc_msg_add_to_data"), true, true).await; diff --git a/src/db_cmds/reddit_channel.rs b/src/db_cmds/reddit_channel.rs index 193c411..78e6828 100644 --- a/src/db_cmds/reddit_channel.rs +++ b/src/db_cmds/reddit_channel.rs @@ -1,11 +1,11 @@ -use crate::{data::dc_bind_bk, lang, messages::send_msg, Context, Error}; +use crate::{db::discord::bind_bk, lang, messages::send_msg, Context, Error}; pub async fn cmd( ctx: Context<'_> ) -> Result<(), Error> { let c_id = ctx.channel_id().into(); - let r = dc_bind_bk(ctx.data(), ctx.guild_id().unwrap().into(), c_id).await; + let r = bind_bk(ctx.data(), ctx.guild_id().unwrap().into(), c_id).await; if r.is_ok() { send_msg(ctx, lang!("dc_msg_bound_channel", c_id), true, true).await; diff --git a/src/db_cmds/wwrps_channel.rs b/src/db_cmds/wwrps_channel.rs index 40b133e..c27fcce 100644 --- a/src/db_cmds/wwrps_channel.rs +++ b/src/db_cmds/wwrps_channel.rs @@ -1,4 +1,4 @@ -use crate::{Context, Error, data::bind_wwrps, lang, messages::send_msg}; +use crate::{Context, Error, db::discord::bind_wwrps, lang, messages::send_msg}; pub async fn cmd( ctx: Context<'_> diff --git a/src/debug_cmds/save.rs b/src/debug_cmds/save.rs index ccb626e..b018c8e 100644 --- a/src/debug_cmds/save.rs +++ b/src/debug_cmds/save.rs @@ -1,10 +1,10 @@ -use crate::{Context, Error, data, lang, messages::{edit_reply, send_msg}}; +use crate::{Context, Error, db::{discord, reddit}, lang, messages::{edit_reply, send_msg}}; pub async fn cmd(ctx: Context<'_>) -> Result<(), Error> { let msg = send_msg(ctx, lang!("dc_msg_owner_data_save"), true, true).await.unwrap(); - data::write_dc_data(ctx.data()).await; - data::write_re_data().await; + discord::write_data(ctx.data()).await; + reddit ::write_data().await; edit_reply(ctx, msg, lang!("dc_msg_owner_data_save_complete")).await; diff --git a/src/debug_cmds/stop.rs b/src/debug_cmds/stop.rs index 2a1a4c6..32a85e6 100644 --- a/src/debug_cmds/stop.rs +++ b/src/debug_cmds/stop.rs @@ -2,7 +2,7 @@ use std::process; use poise::serenity_prelude::OnlineStatus; -use crate::{data, lang, messages::{edit_reply, send_msg}, websocket::send_cmd_json, Context, Error}; +use crate::{db::{discord, reddit}, lang, messages::{edit_reply, send_msg}, websocket::send_cmd_json, Context, Error}; pub async fn cmd(ctx: Context<'_>, confirmation: Option) -> Result<(), Error> { let stop_confirm = "i want to stop the bot now".replace(" ", ""); @@ -11,8 +11,8 @@ pub async fn cmd(ctx: Context<'_>, confirmation: Option) -> Result<(), E if should_stop { let msg = send_msg(ctx, lang!("dc_msg_owner_data_save"), true, true).await.unwrap(); - data::write_dc_data(ctx.data()).await; - data::write_re_data().await; + discord::write_data(ctx.data()).await; + reddit ::write_data().await; send_cmd_json("stop_praw", None, true).await; let complete = format!( diff --git a/src/events.rs b/src/events.rs index 1948519..5c52d7b 100644 --- a/src/events.rs +++ b/src/events.rs @@ -1,9 +1,11 @@ -use crate::data::{get_mutex_data, get_toml_mutex, update_re_data}; +use crate::db::generic::{get_json_mutex, get_toml_mutex}; +use crate::db::reddit::update_data; use crate::r#gen::set_status; use crate::messages::{make_post_embed, make_removed_embed, EmbedOptions}; use crate::re_cmds::generic_fns::{is_bk_mod, is_bk_mod_serenity, serenity_edit_msg_embed, serenity_send_msg}; use crate::websocket::send_cmd_json; -use crate::{CFG_DATA_RE, Data, Error, lang, rs_println}; +use crate::{Data, Error, lang, rs_println}; +use crate::db::reddit::POSTS_KEY; use poise::serenity_prelude::{self as serenity, ChannelId, ComponentInteraction, Interaction, Member, MessageId, Ready}; use serde_json::{json, Value}; @@ -11,6 +13,10 @@ use serde_json::{json, Value}; use std::future::Future; use std::pin::Pin; + +// TODO: clean up + + pub fn event_handler<'a>( ctx: &'a serenity::Context, event: &'a serenity::FullEvent, @@ -82,8 +88,10 @@ async fn approve_btn(ctx: &serenity::Context, data: &Data, c_member: &Member, co let c_id = component.channel_id; let m_id = component.message.id; - update_re_data(data).await; - let new_data = &get_mutex_data(&data.reddit_data).await.unwrap()[CFG_DATA_RE][&url]; + update_data(data).await; + let new_data = &get_json_mutex(&data.reddit_data) + .await.unwrap()[POSTS_KEY][&url]; + update_embed(ctx, &url, new_data, &c_id, &m_id).await; if r["value"].as_bool().unwrap() { @@ -115,8 +123,10 @@ async fn remove_btn(ctx: &serenity::Context, data: &Data, c_member: &Member, com let c_id = component.channel_id; let m_id = component.message.id; - update_re_data(data).await; - let new_data = &get_mutex_data(&data.reddit_data).await.unwrap()[CFG_DATA_RE][&url]; + update_data(data).await; + let new_data = &get_json_mutex(&data.reddit_data) + .await.unwrap()[POSTS_KEY][&url]; + update_embed(ctx, &url, new_data, &c_id, &m_id).await; if r["value"].as_bool().unwrap() { @@ -141,8 +151,9 @@ async fn vote_btn(ctx: &serenity::Context, data: &Data, c_member: &Member, compo let c_id = component.channel_id; let m_id = component.message.id; - update_re_data(data).await; - let new_data = &get_mutex_data(&data.reddit_data).await.unwrap()[CFG_DATA_RE][&url]; + update_data(data).await; + let new_data = &get_json_mutex(&data.reddit_data) + .await.unwrap()[POSTS_KEY][&url]; update_embed(ctx, &url, new_data, &c_id, &m_id).await; if r["value"].as_bool().unwrap() { diff --git a/src/gen.rs b/src/gen.rs index de34e51..3ce3f47 100644 --- a/src/gen.rs +++ b/src/gen.rs @@ -7,8 +7,10 @@ use tokio::sync::Mutex; use toml::Value; use crate::cmds::wwrps::RPSGame; -use crate::data::get_toml_mutex; -use crate::{Args, Cmd, Data, cmds, data, db_cmds, debug_cmds, events, re_cmds, rs_println}; +use crate::db::{cfg, discord, reddit}; +use crate::db::generic::get_toml_mutex; +use crate::lang::Lang; +use crate::{Args, Cmd, Data, cmds, db_cmds, debug_cmds, events, re_cmds, rs_println}; pub async fn gen_data(args: Args, owners: Vec) -> Data { @@ -33,12 +35,14 @@ pub async fn gen_data(args: Args, owners: Vec) -> Data { reddit_data: None.into(), discord_data: None.into(), cfg: None.into(), - args: args.clone() + args: args.clone(), + lang_name: "".to_string().into(), + lang: Lang::new().into() }; - data::read_dc_data (&data, args.clone().wipe).await; - data::read_re_data (&data, args.clone().wipe).await; - data::read_cfg_data(&data, args.clone().wipe).await; + discord::read_data(&data, args.clone().wipe).await; + reddit:: read_data(&data, args.clone().wipe).await; + cfg:: read_data(&data, args.clone().wipe).await; return data; } @@ -57,10 +61,10 @@ pub async fn gen_bot(data: Data, args: Args) -> Client { rs_println!("Token: {}{}", token_peek, "*".repeat(token_end_len)); let own: HashSet = data.owners - .clone() - .into_iter() - .filter_map(|i| if i == 0 { None } else { Some(UserId::from(i))}) - .collect(); + .clone() + .into_iter() + .filter_map(|i| if i == 0 { None } else { Some(UserId::from(i))}) + .collect(); let framework = poise::Framework::builder() .options(poise::FrameworkOptions { diff --git a/src/lang.rs b/src/lang.rs new file mode 100644 index 0000000..346a895 --- /dev/null +++ b/src/lang.rs @@ -0,0 +1,68 @@ +use std::path::PathBuf; + +use serde_json::Value; +use dynfmt::{Format, NoopFormat}; + +use crate::Error; + + +pub struct Lang { + data: Value +} + + +impl From for Lang { + fn from(value: Value) -> Self { + return Lang { data: value }; + } +} + + +impl Lang { + pub fn new() -> Self { + return Lang { data: Value::Null }; + } + + + pub fn from_file(filepath: PathBuf) -> Result { + if !filepath.exists() { return Err(Error::from("LANG filepath not found!")); } + + return Ok(Lang { data: Value::Null }); + } + + + pub fn get(&self, path: &'static str, args: &[String]) -> String { + let path_arr: Vec<&str> = path.split(".").collect(); + return self.get_from_arr(path_arr, args); + } + + + pub fn get_from_arr(&self, path: Vec<&str>, args: &[String]) -> String { + let str_path = path.join("."); + let mut search: &Value = &self.data; + + for i in &path { + let r = search.get(i); + + if let Some(some) = r { + let str_r = search.as_str(); + + if let Some(string) = str_r + { return Lang::format_str(string, args, str_path); } + else { search = some; } + } + else { return str_path; } + } + + return str_path; + } + + + fn format_str(string: &str, args: &[String], fallback: String) -> String { + let cow = NoopFormat.format(string, args); + + if let Ok(ok) = cow + { return ok.to_string(); } + else { return fallback; } + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index cf0643b..c323a90 100644 --- a/src/main.rs +++ b/src/main.rs @@ -43,9 +43,10 @@ mod python; mod macros; #[allow(unknown_lints)] mod websocket; -mod data; +mod db; mod schedule; mod gen; +mod lang; use std::process; use std::thread; @@ -65,7 +66,8 @@ use tokio::sync::Mutex; use websocket::send_cmd_json; use crate::cmds::wwrps::RPSGame; -use crate::data::get_toml_mutex; +use crate::db::generic::get_toml_mutex; +use crate::lang::Lang; use crate::schedule::Schedule; @@ -104,21 +106,15 @@ struct Data { cfg: Mutex>, bk_mods: Vec, args: Args, + lang_name: Mutex, + lang: Mutex } -static CFG_DATA_RE: &str = "posts"; - -pub static mut LANG_NAME: Option = None; -pub static mut LANG: Option = None; -pub static mut NOPING: bool = false; - - #[tokio::main] async fn main() { let args = ::parse(); let args_str = serde_json::to_string(&args).expect("Error serializing args to JSON"); - unsafe { NOPING = !args.ping; } let own_env = std::env::var("ASSISTANT_OWNERS").unwrap_or("0".to_string()); let own_vec_str: Vec = own_env.split(",").map(String::from).collect(); @@ -129,27 +125,14 @@ async fn main() { rs_println!("Generating and/or fetching data and config..."); let data = gen_data(args.clone(), own_vec_u64.clone()).await; - - rs_println!("Fetching language file..."); - let data_binding = get_toml_mutex(&data.cfg).await.unwrap(); - let lang_cfg = data_binding["general"]["lang"].as_str().unwrap(); - data::load_lang_data(lang_cfg.to_string()); + rs_println!("[IMPORTANT] The below message is a test message, it should be written in the language you've selected\nTest message: {}", lang!("log_lang_load_success")); - if args.test { println!("----- USING TEST BOT -----"); } - if args.dev { println!("----- DEV MODE ENABLED -----"); } - if args.dev && args.wipe { println!("----- \"DON'T WORRY ABOUT IT\" MODE ENABLED -----"); } - if args.nosched { println!("----- NO SCHEDULES -----"); } - if args.py && !args.rs { - println!("----- PYTHON ONLY MODE -----"); - rs_println!("ARGS: {}", args_str); - let _ = python::start(args).await; + let _ = python::start(args, *data.lang_name.lock().await).await; process::exit(0); } else if args.rs && ! args.py { - println!("----- RUST ONLY MODE -----"); - rs_println!("ARGS: {}", args_str); start(args, data).await; process::exit(0); } @@ -176,7 +159,7 @@ async fn main() { let python = thread::spawn(move || { rt_py.block_on(async { - if run_py { let _ = python::start(python_args).await; } + if run_py { let _ = python::start(python_args, *data.lang_name.lock().await).await; } }); }); @@ -206,6 +189,6 @@ async fn start(args: Args, data: Data) { async fn read_reddit_inbox() { unsafe { if !websocket::HAS_CONNECTED { return; } - send_cmd_json("respond_mentions", None, !NOPING).await; + send_cmd_json("respond_mentions", None, false).await; } } \ No newline at end of file diff --git a/src/python.rs b/src/python.rs index ce8cee0..ec8454e 100644 --- a/src/python.rs +++ b/src/python.rs @@ -1,5 +1,5 @@ use crate::messages::send_dm; -use crate::{errln, lang, rs_println, Args, LANG_NAME}; +use crate::{errln, lang, rs_println, Args}; use std::fs; use std::ffi::CString; @@ -9,7 +9,7 @@ use pyo3::prelude::*; use pyo3::types::PyList; -pub async fn start(args: Args) -> PyResult<()> { +pub async fn start(args: Args, lang_name: String) -> PyResult<()> { rs_println!("Running Python program..."); let args_str = serde_json::to_string(&args).expect("Error serializing args to JSON"); @@ -27,7 +27,7 @@ pub async fn start(args: Args) -> PyResult<()> { app_path = CString::new( format!("args = {}\nlang_name = \"{}\"\n{}", py_args, - LANG_NAME.clone().unwrap(), + lang_name.clone(), code ) ).unwrap(); diff --git a/src/re_cmds/add.rs b/src/re_cmds/add.rs index 1066ea8..e0dcacc 100644 --- a/src/re_cmds/add.rs +++ b/src/re_cmds/add.rs @@ -1,9 +1,10 @@ use serde_json::json; -use crate::data::{get_mutex_data}; +use crate::db::generic::get_json_mutex; +use crate::db::reddit::{self, POSTS_KEY}; use crate::messages::send_msg; use crate::re_cmds::get::get_post_from_data; -use crate::{data, websocket::send_cmd_json, Context, Error, CFG_DATA_RE}; +use crate::{websocket::send_cmd_json, Context, Error}; use crate::re_cmds::generic_fns::{is_bk_mod_msg, send_embed_for_post, to_shorturl}; use crate::lang; @@ -41,10 +42,10 @@ pub async fn cmd( return Ok(()); } - data::update_re_data(ctx.data()).await; - let reddit_data = get_mutex_data(&ctx.data().reddit_data).await?; + reddit::update_data(ctx.data()).await; + let reddit_data = get_json_mutex(&ctx.data().reddit_data).await?; - if let Some(bk_week) = reddit_data.get(CFG_DATA_RE) { + if let Some(bk_week) = reddit_data.get(POSTS_KEY) { if let Some(post) = bk_week.get(shorturl) { if post["removed"]["removed"].as_bool().unwrap() { send_msg(ctx, lang!("dc_msg_re_post_unremove_success", &shorturl), true, true).await; } diff --git a/src/re_cmds/approve.rs b/src/re_cmds/approve.rs index b30e79c..03c141a 100644 --- a/src/re_cmds/approve.rs +++ b/src/re_cmds/approve.rs @@ -1,6 +1,6 @@ use serde_json::{json, Value}; -use crate::{data::{self, get_mutex_data}, lang, messages::send_msg, re_cmds::generic_fns::{is_bk_mod_msg, to_shorturl}, websocket, Context, Error, CFG_DATA_RE}; +use crate::{Context, Error, db::{generic::get_json_mutex, reddit::{self, POSTS_KEY}}, lang, messages::send_msg, re_cmds::generic_fns::{is_bk_mod_msg, to_shorturl}, websocket}; use super::generic_fns::send_embed_for_removed; @@ -23,8 +23,8 @@ pub async fn cmd( let shorturl_u = to_shorturl(&url); let shorturl = &shorturl_u.unwrap_or(url.clone()); - data::update_re_data(ctx.data()).await; - let reddit_data = get_mutex_data(&ctx.data().reddit_data).await?; + reddit::update_data(ctx.data()).await; + let reddit_data = get_json_mutex(&ctx.data().reddit_data).await?; approve_cmd(ctx, shorturl, &reddit_data, !disapprove.unwrap_or(false)).await; @@ -33,7 +33,7 @@ pub async fn cmd( async fn approve_cmd(ctx: Context<'_>, url: &str, reddit_data: &Value, approve: bool) { - if let Some(post) = reddit_data.get(CFG_DATA_RE).unwrap().get(url) { + if let Some(post) = reddit_data.get(POSTS_KEY).unwrap().get(url) { if post["removed"]["removed"].as_bool().unwrap() { send_embed_for_removed(ctx, url, post).await; return; diff --git a/src/re_cmds/generic_fns.rs b/src/re_cmds/generic_fns.rs index 55939fb..c37c2a4 100644 --- a/src/re_cmds/generic_fns.rs +++ b/src/re_cmds/generic_fns.rs @@ -2,7 +2,7 @@ use poise::serenity_prelude::{self as serenity, ChannelId, ComponentInteraction, use regex::Regex; use serde_json::Value; -use crate::{data::get_toml_mutex, lang, messages::{decode_and_decompress_json, embed_from_options, make_post_embed, make_removed_embed, send_embed, send_msg, EmbedOptions, JSON_TEXT_END, JSON_TEXT_START}, Context, Data, Error}; +use crate::{Context, Data, Error, db::generic::get_toml_mutex, lang, messages::{EmbedOptions, JSON_TEXT_END, JSON_TEXT_START, decode_and_decompress_json, embed_from_options, make_post_embed, make_removed_embed, send_embed, send_msg}}; pub fn is_bk_mod(mod_list: Vec, uid: u64) -> bool { return mod_list.contains(&uid); diff --git a/src/re_cmds/get.rs b/src/re_cmds/get.rs index 5391d80..edfb150 100644 --- a/src/re_cmds/get.rs +++ b/src/re_cmds/get.rs @@ -1,6 +1,6 @@ use serde_json::Value; -use crate::{data::{self, get_mutex_data}, lang, messages::send_msg, re_cmds::generic_fns::{send_embed_for_post, to_shorturl}, rs_println, Context, Error, CFG_DATA_RE}; +use crate::{Context, Error, db::{generic::get_json_mutex, reddit::{self, POSTS_KEY}}, lang, messages::send_msg, re_cmds::generic_fns::{send_embed_for_post, to_shorturl}, rs_println}; use super::generic_fns::send_embed_for_removed; @@ -17,12 +17,12 @@ pub async fn cmd( #[description = "The post URL."] url: String ) -> Result<(), Error> { - data::update_re_data(ctx.data()).await; + reddit::update_data(ctx.data()).await; let shorturl_u = to_shorturl(&url); let shorturl = &shorturl_u.unwrap_or(url.clone()); - let reddit_data = get_mutex_data(&ctx.data().reddit_data).await?; + let reddit_data = get_json_mutex(&ctx.data().reddit_data).await?; if let Some(post) = get_post_from_data(ctx, &reddit_data, shorturl).await? { send_embed_for_post(ctx, post, shorturl).await?; @@ -33,7 +33,7 @@ pub async fn cmd( pub async fn get_post_from_data(ctx: Context<'_>, reddit_data: &Value, url: &str) -> Result, Error> { - if let Some(bk_week) = reddit_data.get(CFG_DATA_RE) { + if let Some(bk_week) = reddit_data.get(POSTS_KEY) { if let Some(post) = bk_week.get(url) { if post["removed"]["removed"].as_bool().unwrap() { send_embed_for_removed(ctx, url, post).await; diff --git a/src/re_cmds/remove.rs b/src/re_cmds/remove.rs index 399d051..82bd5cf 100644 --- a/src/re_cmds/remove.rs +++ b/src/re_cmds/remove.rs @@ -1,6 +1,6 @@ use serde_json::json; -use crate::{data::{self, get_mutex_data}, lang, messages::send_msg, re_cmds::{generic_fns::{is_bk_mod_msg, send_embed_for_removed, to_shorturl}, get::get_post_from_data}, websocket::send_cmd_json, Context, Error}; +use crate::{Context, Error, db::{generic::get_json_mutex, reddit}, lang, messages::send_msg, re_cmds::{generic_fns::{is_bk_mod_msg, send_embed_for_removed, to_shorturl}, get::get_post_from_data}, websocket::send_cmd_json}; #[poise::command( slash_command, @@ -36,8 +36,8 @@ pub async fn cmd( send_msg(ctx, lang!("dc_msg_re_post_404"), true, true).await; } - data::update_re_data(ctx.data()).await; - let reddit_data = get_mutex_data(&ctx.data().reddit_data).await?; + reddit::update_data(ctx.data()).await; + let reddit_data = get_json_mutex(&ctx.data().reddit_data).await?; if let Some(post) = get_post_from_data(ctx, &reddit_data, shorturl).await? { if post["removed"]["removed"].as_bool().unwrap() { diff --git a/src/re_cmds/top.rs b/src/re_cmds/top.rs index 4a5c5e5..6905f26 100644 --- a/src/re_cmds/top.rs +++ b/src/re_cmds/top.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use crate::{data::get_mutex_data, re_cmds::generic_fns::send_embed_for_post, Context, Error, CFG_DATA_RE}; +use crate::{Context, Error, db::{generic::get_json_mutex, reddit::POSTS_KEY}, re_cmds::generic_fns::send_embed_for_post}; #[derive(poise::ChoiceParameter, PartialEq)] enum TopCategory { @@ -30,7 +30,7 @@ pub async fn cmd( ) -> Result<(), Error> { let mut all: HashMap<&str, i32> = HashMap::new(); - let posts = &get_mutex_data(&ctx.data().reddit_data).await?[CFG_DATA_RE]; + let posts = &get_json_mutex(&ctx.data().reddit_data).await?[POSTS_KEY]; let posts_u = posts.as_object().unwrap(); for (url, dat) in posts_u { diff --git a/src/re_cmds/update.rs b/src/re_cmds/update.rs index e0237b3..cfaa591 100644 --- a/src/re_cmds/update.rs +++ b/src/re_cmds/update.rs @@ -3,7 +3,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; use poise::{serenity_prelude::{ChannelId, EditMessage, GetMessages, Http, Message, MessageId, UserId}, ReplyHandle}; use serde_json::{json, Map, Value}; -use crate::{data::{self, get_mutex_data, get_toml_mutex, DC_POSTS_CHANNEL_KEY}, lang, messages::{edit_reply, embed_from_options, make_post_embed, make_removed_embed, send_embed, send_msg, trim_post_json}, re_cmds::generic_fns::embed_to_json, rs_println, websocket::send_cmd_json, Context, Error, CFG_DATA_RE}; +use crate::{Context, Error, db::{discord::contains_server, generic::{get_json_mutex, get_toml_mutex}, keys::DC_POSTS_CHANNEL_KEY, reddit::{self, POSTS_KEY}}, lang, messages::{edit_reply, embed_from_options, make_post_embed, make_removed_embed, send_embed, send_msg, trim_post_json}, re_cmds::generic_fns::embed_to_json, rs_println, websocket::send_cmd_json}; #[poise::command( slash_command, @@ -44,8 +44,8 @@ pub async fn cmd( let max_results_final = max_results.unwrap_or(max_results_pre as u16); send_cmd_json("add_new_posts", Some(json!([max_age_secs, max_results_final])), true).await; - data::update_re_data(ctx.data()).await; - let r_data = get_mutex_data(&ctx.data().reddit_data).await?; + reddit::update_data(ctx.data()).await; + let r_data = get_json_mutex(&ctx.data().reddit_data).await?; let c_id_u = get_c_id(ctx).await; @@ -67,7 +67,7 @@ pub async fn cmd( // Adding new posts p_text = update_progress(ctx, progress.clone(), p_text.clone(), lang!("dc_msg_update_add", "✅\n")).await; - let weekly_art = r_data[CFG_DATA_RE].as_object().unwrap(); + let weekly_art = r_data[POSTS_KEY].as_object().unwrap(); add_posts(ctx, weekly_art, &msgs_json, max_age_secs, max_results_final).await; // Stop if only_add @@ -113,12 +113,12 @@ async fn update_progress(ctx: Context<'_>, p: ReplyHandle<'_>, t: String, added_ async fn get_c_id(ctx: Context<'_>) -> Option { - if !data::dc_contains_server(ctx.data(), ctx.guild_id().unwrap().into()).await { + if !contains_server(ctx.data(), ctx.guild_id().unwrap().into()).await { send_msg(ctx, lang!("dc_msg_data_server_404"), true, true).await; return None; } - let d = get_mutex_data(&ctx.data().discord_data).await.unwrap(); + let d = get_json_mutex(&ctx.data().discord_data).await.unwrap(); let c_id_u = d["servers"] [ctx.guild_id().unwrap().to_string()] @@ -188,7 +188,7 @@ async fn msgs_to_json(msgs: Vec, reddit_data: &Value, max_age: u64) -> if msg_json.is_err() { continue; } let u_json: Value = msg_json.unwrap(); - let re_url = &reddit_data[CFG_DATA_RE][&url]; + let re_url = &reddit_data[POSTS_KEY][&url]; let json_trimmed = trim_post_json(re_url); diff --git a/src/re_cmds/vote.rs b/src/re_cmds/vote.rs index 0fd9d6f..1af06f0 100644 --- a/src/re_cmds/vote.rs +++ b/src/re_cmds/vote.rs @@ -1,6 +1,6 @@ use serde_json::json; -use crate::{data::{self, get_mutex_data}, lang, messages::send_msg, re_cmds::generic_fns::{is_bk_mod, send_embed_for_removed, to_shorturl}, websocket::send_cmd_json, Context, Error, CFG_DATA_RE}; +use crate::{Context, Error, db::{generic::get_json_mutex, reddit::{self, POSTS_KEY}}, lang, messages::send_msg, re_cmds::generic_fns::{is_bk_mod, send_embed_for_removed, to_shorturl}, websocket::send_cmd_json}; #[poise::command( slash_command, @@ -16,10 +16,10 @@ pub async fn cmd( #[description = "Wether to undo your vote or not"] un_vote: Option ) -> Result<(), Error> { - data::update_re_data(ctx.data()).await; + reddit::update_data(ctx.data()).await; let uid = ctx.author().id.get(); - let re_data = get_mutex_data(&ctx.data().reddit_data).await?; - let post_data = re_data[CFG_DATA_RE].clone(); + let re_data = get_json_mutex(&ctx.data().reddit_data).await?; + let post_data = re_data[POSTS_KEY].clone(); let unw_vote = un_vote.unwrap_or(false); let shorturl_u = to_shorturl(&url);