(LARGE COMMIT) refactored until no errors

This commit is contained in:
2026-03-01 14:15:55 +01:00
parent df7583263a
commit ed8904e26c
28 changed files with 322 additions and 289 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
use crate::{Context, Error, db_cmds::{add_server, reddit_channel, wwrps_channel}};
use crate::{Context, Error, cmds::db::{add_server, reddit_channel, wwrps_channel}};
#[derive(poise::ChoiceParameter, PartialEq)]
+4 -2
View File
@@ -1,6 +1,6 @@
use rand::{seq::IteratorRandom, Rng};
use crate::{db::generic::get_toml_mutex, lang, messages::send_msg, Context, Error};
use crate::{lang, messages::send_msg, Context, Error};
#[poise::command(
@@ -16,7 +16,9 @@ pub async fn cmd(
#[description = "Question to ask."] question: String
) -> Result<(), Error>
{
let quirky_chance = get_toml_mutex(&ctx.data().cfg).await.unwrap()["commands"]["eight_ball_quirky_chance"].as_float().unwrap();
let quirky_chance = &ctx.data().cfg
["commands"]["eight_ball_quirky_chance"]
.as_float().unwrap();
let is_quirky = rand::rng().random_bool(quirky_chance.clamp(0.0, 1.0));
let list = &ctx.data().ball_prompts[if is_quirky { 1 } else { 0 }];
let rand_item = list.iter().choose(&mut rand::rng());
+2 -2
View File
@@ -1,7 +1,7 @@
use poise::serenity_prelude::{ChannelId, Mentionable};
use tokio::sync::MutexGuard;
use crate::{Context, Error, db::generic::get_json_mutex, games::wwrps::{RPS, RPSGame, RPSPlayer}, lang, messages::{http_send_msg, send_msg}};
use crate::{Context, Error, games::wwrps::{RPS, RPSGame, RPSPlayer}, lang, messages::{http_send_msg, send_msg}};
#[poise::command(
@@ -75,7 +75,7 @@ fn results_text(game: &MutexGuard<'_, RPSGame>) -> String {
async fn get_wwrps_channel(ctx: Context<'_>) -> Option<u64> {
let d = get_json_mutex(&ctx.data().discord_data).await.unwrap();
let d = &ctx.data().discord_data.lock().await;
let is_guild = ctx.guild_channel().await.is_some();
+4 -5
View File
@@ -1,11 +1,10 @@
use serde_json::json;
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::cmds::reddit::get::get_post_from_data;
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::cmds::reddit::generic_fns::{is_bk_mod_msg, send_embed_for_post, to_shorturl};
use crate::lang;
#[poise::command(
@@ -42,8 +41,8 @@ pub async fn cmd(
return Ok(());
}
reddit::update_data(ctx.data()).await;
let reddit_data = get_json_mutex(&ctx.data().reddit_data).await?;
reddit::update_data().await;
let reddit_data = &ctx.data().reddit_data.lock().await;
if let Some(bk_week) = reddit_data.get(POSTS_KEY) {
if let Some(post) = bk_week.get(shorturl) {
+2 -3
View File
@@ -1,6 +1,6 @@
use serde_json::{json, Value};
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 crate::{Context, Error, db::{reddit::POSTS_KEY}, lang, messages::send_msg, cmds::reddit::generic_fns::{is_bk_mod_msg, to_shorturl}, websocket};
use super::generic_fns::send_embed_for_removed;
@@ -23,8 +23,7 @@ pub async fn cmd(
let shorturl_u = to_shorturl(&url);
let shorturl = &shorturl_u.unwrap_or(url.clone());
reddit::update_data(ctx.data()).await;
let reddit_data = get_json_mutex(&ctx.data().reddit_data).await?;
let reddit_data = &ctx.data().reddit_data.lock().await;
approve_cmd(ctx, shorturl, &reddit_data, !disapprove.unwrap_or(false)).await;
+4 -5
View File
@@ -2,7 +2,7 @@ use poise::serenity_prelude::{self as serenity, ChannelId, ComponentInteraction,
use regex::Regex;
use serde_json::Value;
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}};
use crate::{Context, Data, Error, 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<u64>, uid: u64) -> bool {
return mod_list.contains(&uid);
@@ -10,7 +10,7 @@ pub fn is_bk_mod(mod_list: Vec<u64>, uid: u64) -> bool {
pub async fn is_bk_mod_msg(ctx: Context<'_>) -> bool {
if is_bk_mod(ctx.data().bk_mods.clone(), ctx.author().id.get()) { return true; }
if is_bk_mod(ctx.data().env_vars.reddit_mod_discord_ids.clone(), ctx.author().id.get()) { return true; }
let sr = get_readable_subreddits(ctx.data()).await.unwrap();
send_msg(ctx, lang!("dc_msg_re_permdeny_not_re_mod", sr), true, true).await;
@@ -19,7 +19,7 @@ pub async fn is_bk_mod_msg(ctx: Context<'_>) -> bool {
pub async fn is_bk_mod_serenity(ctx: &serenity::Context, data: &Data, author: &Member, component: &ComponentInteraction) -> bool {
if is_bk_mod(data.bk_mods.clone(), author.user.id.get()) { return true; }
if is_bk_mod(data.env_vars.reddit_mod_discord_ids.clone(), author.user.id.get()) { return true; }
let sr = get_readable_subreddits(data).await.unwrap();
serenity_send_msg(ctx, component, lang!("dc_msg_re_permdeny_not_re_mod", sr), true).await;
@@ -70,8 +70,7 @@ pub async fn send_embed_for_removed(ctx: Context<'_>, url: &str, post: &Value) {
pub async fn get_readable_subreddits(data: &Data) -> Result<String, Error> {
let d = get_toml_mutex(&data.cfg).await.unwrap();
let sr = d["reddit"]["subreddits"].as_array().unwrap();
let sr = data.cfg["reddit"]["subreddits"].as_array().unwrap();
let sr_str: Vec<&str> = sr
.iter()
.map(|v| v.as_str().unwrap())
+3 -3
View File
@@ -1,6 +1,6 @@
use serde_json::Value;
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 crate::{Context, Error, db::{reddit::{self, POSTS_KEY}}, lang, messages::send_msg, cmds::reddit::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>
{
reddit::update_data(ctx.data()).await;
reddit::update_data().await;
let shorturl_u = to_shorturl(&url);
let shorturl = &shorturl_u.unwrap_or(url.clone());
let reddit_data = get_json_mutex(&ctx.data().reddit_data).await?;
let reddit_data = &ctx.data().reddit_data.lock().await;
if let Some(post) = get_post_from_data(ctx, &reddit_data, shorturl).await? {
send_embed_for_post(ctx, post, shorturl).await?;
+3 -3
View File
@@ -1,6 +1,6 @@
use serde_json::json;
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};
use crate::{Context, Error, db::reddit, lang, messages::send_msg, cmds::reddit::{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;
}
reddit::update_data(ctx.data()).await;
let reddit_data = get_json_mutex(&ctx.data().reddit_data).await?;
reddit::update_data().await;
let reddit_data = &ctx.data().reddit_data.lock().await;
if let Some(post) = get_post_from_data(ctx, &reddit_data, shorturl).await? {
if post["removed"]["removed"].as_bool().unwrap() {
+1 -1
View File
@@ -1,4 +1,4 @@
use crate::{lang, messages::send_msg, re_cmds::generic_fns::to_shorturl, Context, Error};
use crate::{lang, messages::send_msg, cmds::reddit::generic_fns::to_shorturl, Context, Error};
#[poise::command(
+2 -2
View File
@@ -1,6 +1,6 @@
use std::collections::HashMap;
use crate::{Context, Error, db::{generic::get_json_mutex, reddit::POSTS_KEY}, re_cmds::generic_fns::send_embed_for_post};
use crate::{Context, Error, db::reddit::POSTS_KEY, cmds::reddit::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_json_mutex(&ctx.data().reddit_data).await?[POSTS_KEY];
let posts = &ctx.data().reddit_data.lock().await[POSTS_KEY];
let posts_u = posts.as_object().unwrap();
for (url, dat) in posts_u {
+5 -6
View File
@@ -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::{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};
use crate::{Context, Error, db::{discord::contains_server, 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}, cmds::reddit::generic_fns::embed_to_json, rs_println, websocket::send_cmd_json};
#[poise::command(
slash_command,
@@ -39,13 +39,12 @@ pub async fn cmd(
let max_age_u = max_age.unwrap_or(8);
let max_age_secs = max_age_u as u64 * (60 * 60 * 24);
let max_results_toml = &get_toml_mutex(&ctx.data().cfg).await.unwrap();
let max_results_pre = max_results_toml["reddit"]["fetch_limit"].as_integer().unwrap();
let max_results_pre = ctx.data().cfg["reddit"]["fetch_limit"].as_integer().unwrap();
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;
reddit::update_data(ctx.data()).await;
let r_data = get_json_mutex(&ctx.data().reddit_data).await?;
reddit::update_data().await;
let r_data = &ctx.data().reddit_data.lock().await;
let c_id_u = get_c_id(ctx).await;
@@ -118,7 +117,7 @@ async fn get_c_id(ctx: Context<'_>) -> Option<ChannelId> {
return None;
}
let d = get_json_mutex(&ctx.data().discord_data).await.unwrap();
let d = &ctx.data().discord_data.lock().await;
let c_id_u =
d["servers"]
[ctx.guild_id().unwrap().to_string()]
+12 -16
View File
@@ -1,6 +1,6 @@
use serde_json::json;
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};
use crate::{Context, Error, db::reddit::{self, POSTS_KEY}, lang, messages::send_msg, cmds::reddit::generic_fns::{is_bk_mod, send_embed_for_removed, to_shorturl}, websocket::send_cmd_json};
#[poise::command(
slash_command,
@@ -16,9 +16,9 @@ pub async fn cmd(
#[description = "Wether to undo your vote or not"] un_vote: Option<bool>
) -> Result<(), Error>
{
reddit::update_data(ctx.data()).await;
reddit::update_data().await;
let uid = ctx.author().id.get();
let re_data = get_json_mutex(&ctx.data().reddit_data).await?;
let re_data = &ctx.data().reddit_data.lock().await;
let post_data = re_data[POSTS_KEY].clone();
let unw_vote = un_vote.unwrap_or(false);
@@ -36,7 +36,7 @@ pub async fn cmd(
let url_data = &post_data[&shorturl];
let is_mod = is_bk_mod(ctx.data().bk_mods.clone(), ctx.author().id.get());
let is_mod = is_bk_mod(ctx.data().env_vars.reddit_mod_discord_ids.clone(), ctx.author().id.get());
let voters_dc = url_data["votes"]["voters_dc"].as_array().unwrap();
let mod_voters = url_data["votes"]["mod_voters"].as_array().unwrap();
let voters = if is_mod { mod_voters } else { voters_dc };
@@ -53,18 +53,14 @@ pub async fn cmd(
let r = send_cmd_json("set_vote_post", Some(json!([shorturl, uid, is_mod, true, unw_vote])), true).await.unwrap();
let unw_r = r["value"].as_bool().unwrap();
if unw_r && !unw_vote && is_mod {
send_msg(ctx, lang!("dc_msg_re_vote_mod_success"), true, true).await;
}
else if unw_r && !unw_vote && !is_mod {
send_msg(ctx, lang!("dc_msg_re_vote_success"), true, true).await;
}
else if unw_r && unw_vote {
send_msg(ctx, lang!("dc_msg_re_vote_remove_success"), true, true).await;
}
else {
send_msg(ctx, lang!("dc_msg_re_vote_err"), true, true).await;
}
if unw_r && !unw_vote && is_mod
{ send_msg(ctx, lang!("dc_msg_re_vote_mod_success"), true, true).await; }
else if unw_r && !unw_vote && !is_mod
{ send_msg(ctx, lang!("dc_msg_re_vote_success"), true, true).await; }
else if unw_r && unw_vote
{ send_msg(ctx, lang!("dc_msg_re_vote_remove_success"), true, true).await; }
else
{ send_msg(ctx, lang!("dc_msg_re_vote_err"), true, true).await; }
return Ok(());
}
+3 -3
View File
@@ -1,7 +1,7 @@
pub mod bot_data;
pub mod cfg;
pub mod terminal_args;
pub mod discord;
pub mod generic;
pub mod env_vars;
pub mod keys;
pub mod reddit;
pub mod reddit;
pub mod terminal_args;
+9 -10
View File
@@ -1,18 +1,17 @@
use serde_json::Value;
use tokio::sync::Mutex;
use crate::{db::terminal_args::Args, games::wwrps::RPSGame, lang::Lang};
use crate::{db::{env_vars::AssistantEnv, terminal_args::Args}, games::wwrps::RPSGame, lang::Lang};
pub struct Data {
pub owners: Vec<u64>,
pub ball_prompts: [Vec<String>; 2],
pub rps_game: Mutex<RPSGame>,
pub reddit_data: Mutex<Option<Value>>,
pub discord_data: Mutex<Option<Value>>,
pub cfg: Mutex<Option<toml::Value>>,
pub bk_mods: Vec<u64>,
pub args: Args,
pub lang_name: Mutex<String>,
pub lang: Mutex<Lang>
pub ball_prompts: [Vec<String>; 2],
pub cfg: toml::Value,
pub discord_data: Mutex<Value>,
pub env_vars: AssistantEnv,
pub lang_name: String,
pub lang: Lang,
pub reddit_data: Mutex<Value>,
pub rps_game: Mutex<RPSGame>,
}
+5 -9
View File
@@ -1,15 +1,13 @@
use std::{fs, io::Write, path::Path};
use serde_json::{Value, json};
use crate::{Data, rs_println, websocket::send_cmd_json};
use crate::rs_println;
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<Value> {
pub async fn read_data(wipe: bool) -> toml::Value {
if !Path::new(DATA_PATH).exists() || wipe {
rs_println!(
"{} creating new from preset...",
@@ -20,16 +18,14 @@ pub async fn read_data(data: &Data, wipe: bool) -> Option<Value> {
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(
/*let r = send_cmd_json(
"update_cfg",
Some(json!([toml::to_string(&json_data).unwrap()])),
true
).await;
).await;*/
return r;
return json_data;
}
+8 -14
View File
@@ -8,7 +8,7 @@ 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) {
pub async fn read_data(wipe: bool) -> Value {
if !Path::new(DATA_PATH).exists() || wipe {
rs_println!(
"{} creating new from preset...",
@@ -19,8 +19,7 @@ pub async fn read_data(data: &Data, wipe: bool) {
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;
return json_data;
}
@@ -51,17 +50,15 @@ pub async fn write_data(data: &Data) {
.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();
let dc_data = data.discord_data.lock().await;
let json_str = serde_json::to_string_pretty(&dc_data.clone()).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();
let mut dc_data = data.discord_data.lock().await;
if dc_data.get("servers").is_none() { return Err(()); }
@@ -76,8 +73,7 @@ pub async fn add_server(data: &Data, server_id: u64) -> Result<(), ()> {
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();
let mut dc_data = data.discord_data.lock().await;
if dc_data.get("servers").is_none() { return Err(()); }
@@ -96,8 +92,7 @@ pub async fn bind_bk(data: &Data, server_id: u64, channel_id: u64) -> Result<(),
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();
let mut dc_data = data.discord_data.lock().await;
if dc_data.get("servers").is_none() { return Err(()); }
@@ -115,8 +110,7 @@ pub async fn bind_wwrps(data: &Data, server_id: u64, channel_id: u64) -> Result<
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();
let dc_data = data.discord_data.lock().await;
if dc_data.get("servers").is_none() { return false; }
+38
View File
@@ -0,0 +1,38 @@
#[derive(Clone)]
pub struct AssistantEnv {
pub token: String,
pub bot_owners: Vec<u64>,
pub reddit_mod_discord_ids: Vec<u64>
}
impl AssistantEnv {
pub fn new(test: bool) -> Self {
let token_name = if test { "ASSISTANT_TOKEN" }
else { "ASSISTANT_TOKEN_TEST" };
return AssistantEnv {
token: string_env(token_name),
bot_owners: vec_u64_env("ASSISTANT_OWNERS"),
reddit_mod_discord_ids: vec_u64_env("ASSISTANT_BK_MODS")
};
}
}
fn string_env(name: &str) -> String {
return std::env::var(name)
.expect(format!("Environment variable \"{}\" not found!", name).as_str())
}
fn vec_u64_env(name: &str) -> Vec<u64> {
let var = std::env::var(name)
.unwrap_or("0".to_string());
return var
.split(",")
.map(|s| s.parse::<u64>()
.expect(format!("Failed to parse {}. Invalid syntax.", name).as_str()))
.collect();
}
-21
View File
@@ -1,21 +0,0 @@
use serde_json::Value;
use tokio::sync::Mutex;
use crate::Error;
pub async fn get_json_mutex(data: &Mutex<Option<Value>>) -> Result<Value, Error> {
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<Option<toml::Value>>) -> Result<toml::Value, Error> {
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()),
};
}
+6 -7
View File
@@ -2,7 +2,7 @@ use std::{fs, io::Write, path::Path};
use serde_json::Value;
use crate::{Data, rs_println, rs_warnln, websocket::send_cmd_json};
use crate::{rs_println, rs_warnln, websocket::send_cmd_json};
static DATA_PATH: &str = "./data/db/re_data.json";
@@ -11,7 +11,7 @@ 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) {
pub async fn read_data(wipe: bool) -> Value {
if !Path::new(DATA_PATH).exists() || wipe {
rs_println!(
"{} creating new from preset...",
@@ -21,9 +21,8 @@ pub async fn read_data(data: &Data, wipe: bool) {
}
let str_data = fs::read_to_string(DATA_PATH).unwrap();
let json_data: Option<Value> = serde_json::from_str(&str_data).unwrap();
let mut re_data = data.reddit_data.lock().await;
*re_data = json_data;
let json_data: Value = serde_json::from_str(&str_data).unwrap();
return json_data;
}
@@ -45,9 +44,9 @@ fn generate_data() {
}
pub async fn update_data(data: &Data) {
pub async fn update_data() {
send_cmd_json("update_data_file", None, true).await;
read_data(data, false).await;
read_data(false).await;
}
+14
View File
@@ -28,3 +28,17 @@ pub struct Args {
#[arg(long, help = "Makes the program not use the schedule system.")]
pub nosched: bool
}
impl Args {
pub fn new() -> Self
{ return <Args as clap::Parser>::parse(); }
}
impl ToString for Args {
fn to_string(&self) -> String {
return serde_json::to_string(self)
.unwrap_or("[FAILED TO CONVERT TO STRING]".to_string());
}
}
+12 -14
View File
@@ -1,4 +1,3 @@
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};
@@ -41,9 +40,7 @@ async fn on_ready(ctx: &serenity::Context, data_about_bot: &Ready, data: &Data)
data_about_bot.user.id
);
let m_data = get_toml_mutex(&data.cfg).await.unwrap();
set_status(m_data, ctx).await;
set_status(data.cfg.clone(), ctx).await;
}
@@ -88,9 +85,9 @@ 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_data(data).await;
let new_data = &get_json_mutex(&data.reddit_data)
.await.unwrap()[POSTS_KEY][&url];
update_data().await;
let new_data = &data.reddit_data
.lock().await[POSTS_KEY][&url];
update_embed(ctx, &url, new_data, &c_id, &m_id).await;
@@ -123,9 +120,9 @@ 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_data(data).await;
let new_data = &get_json_mutex(&data.reddit_data)
.await.unwrap()[POSTS_KEY][&url];
update_data().await;
let new_data = &data.reddit_data
.lock().await[POSTS_KEY][&url];
update_embed(ctx, &url, new_data, &c_id, &m_id).await;
@@ -144,16 +141,17 @@ async fn remove_btn(ctx: &serenity::Context, data: &Data, c_member: &Member, com
async fn vote_btn(ctx: &serenity::Context, data: &Data, c_member: &Member, component: &ComponentInteraction, url: String, vote: bool) -> Result<(), Error> {
let uid: u64 = c_member.user.id.into();
let is_mod = is_bk_mod(data.bk_mods.clone(), uid);
let is_mod = is_bk_mod(data.env_vars.reddit_mod_discord_ids.clone(), uid);
let r = send_cmd_json("set_vote_post", Some(json!([&url, uid, is_mod, true, !vote])), true).await.unwrap();
let c_id = component.channel_id;
let m_id = component.message.id;
update_data(data).await;
let new_data = &get_json_mutex(&data.reddit_data)
.await.unwrap()[POSTS_KEY][&url];
update_data().await;
let new_data = &data.reddit_data
.lock().await[POSTS_KEY][&url];
update_embed(ctx, &url, new_data, &c_id, &m_id).await;
if r["value"].as_bool().unwrap() {
+23 -34
View File
@@ -6,51 +6,41 @@ use poise::serenity_prelude::Client;
use tokio::sync::Mutex;
use toml::Value;
use crate::db::env_vars::AssistantEnv;
use crate::db::{cfg, discord, reddit};
use crate::db::generic::get_toml_mutex;
use crate::games::wwrps::RPSGame;
use crate::lang::Lang;
use crate::{Args, Cmd, Data, cmds, events, rs_println};
pub async fn gen_data(args: Args, owners: Vec<u64>) -> Data {
pub async fn gen_data(args: Args, env_vars: AssistantEnv) -> Data {
let ball_classic_str = std::fs::read_to_string("./cfg/8-ball_classic.txt").unwrap();
let ball_quirk_str = std::fs::read_to_string("./cfg/8-ball_quirky.txt").unwrap();
let ball_classic: Vec<String> = ball_classic_str.lines().map(String::from).collect();
let ball_quirk: Vec<String> = ball_quirk_str .lines().map(String::from).collect();
let mods_env = std::env::var("ASSISTANT_BK_MODS").unwrap_or("0".to_string());
let mods_vec_str: Vec<String> = mods_env.split(",").map(String::from).collect();
let mods_vec_u64: Vec<u64> = mods_vec_str
.iter()
.map(|s| s.parse::<u64>().expect("Failed to parse ASSISTANT_BK_MODS. Invalid syntax."))
.collect();
let re_data = reddit:: read_data(args.clone().wipe).await;
let dc_data = discord::read_data(args.clone().wipe).await;
let cf_data = cfg:: read_data(args.clone().wipe).await;
let data = Data {
owners,
ball_prompts: [ball_classic, ball_quirk],
rps_game: Mutex::new(RPSGame::new()),
bk_mods: mods_vec_u64,
reddit_data: None.into(),
discord_data: None.into(),
cfg: None.into(),
return Data {
args: args.clone(),
ball_prompts: [ball_classic, ball_quirk],
cfg: cf_data,
discord_data: dc_data.into(),
env_vars: env_vars,
lang_name: "".to_string().into(),
lang: Lang::new().into()
lang: Lang::new().into(),
reddit_data: re_data.into(),
rps_game: Mutex::new(RPSGame::new())
};
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;
}
pub async fn gen_bot(data: Data, args: Args) -> Client {
pub async fn gen_bot(data: Data) -> Client {
let token =
if !args.test { std::env::var("ASSISTANT_TOKEN").expect("Missing ASSISTANT_TOKEN env var!") }
if !data.args.test { std::env::var("ASSISTANT_TOKEN").expect("Missing ASSISTANT_TOKEN env var!") }
else { std::env::var("ASSISTANT_TOKEN_TEST").expect("Missing ASSISTANT_TOKEN_TEST env var!") };
let intents = serenity::GatewayIntents::all();
@@ -60,7 +50,7 @@ pub async fn gen_bot(data: Data, args: Args) -> Client {
let token_end_len = token[peek_len..].len();
rs_println!("Token: {}{}", token_peek, "*".repeat(token_end_len));
let own: HashSet<UserId> = data.owners
let owner_users: HashSet<UserId> = data.env_vars.bot_owners
.clone()
.into_iter()
.filter_map(|i| if i == 0 { None } else { Some(UserId::from(i))})
@@ -68,7 +58,7 @@ pub async fn gen_bot(data: Data, args: Args) -> Client {
let framework = poise::Framework::builder()
.options(poise::FrameworkOptions {
owners: own,
owners: owner_users,
commands: make_cmd_vec(&data).await,
event_handler: events::event_handler,
..Default::default()
@@ -89,7 +79,7 @@ pub async fn gen_bot(data: Data, args: Args) -> Client {
async fn make_cmd_vec(data: &Data) -> Vec<Cmd> {
let mut cmds = vec![
let mut cmds: Vec<Cmd> = vec![
// GENERIC
cmds::generic::help::cmd(),
cmds::generic::eight_ball::cmd(),
@@ -110,9 +100,8 @@ async fn make_cmd_vec(data: &Data) -> Vec<Cmd> {
// DATABASE
cmds::db::main_cmd::cmd()
];
let cfg = get_toml_mutex(&data.cfg).await.unwrap();
let disabled = cfg["commands"]["disabled_categories"]
let disabled = data.cfg["commands"]["disabled_categories"]
.as_array()
.unwrap()
.iter()
@@ -125,12 +114,12 @@ async fn make_cmd_vec(data: &Data) -> Vec<Cmd> {
}
pub async fn set_status(m_data: Value, ctx: &serenity::Context) {
pub async fn set_status(cfg: Value, ctx: &serenity::Context) {
let status_str: String;
let status = m_data["general"]["status"].as_str().unwrap();
let status_c = m_data["general"]["statusCommitNumber"].as_bool().unwrap();
let status_ec = m_data["general"]["statusExperimentalCommit"].as_bool().unwrap();
let status = cfg["general"]["status"].as_str().unwrap();
let status_c = cfg["general"]["statusCommitNumber"].as_bool().unwrap();
let status_ec = cfg["general"]["statusExperimentalCommit"].as_bool().unwrap();
if status_c {
let commit_num_r = process::Command::new("git")
+7 -4
View File
@@ -66,7 +66,7 @@ macro_rules! warnln {
macro_rules! lang {
($key:expr) => {
{
use $crate::{LANG, errln};
/*use $crate::{LANG, errln};
let value = unsafe {
LANG
.as_ref()
@@ -76,11 +76,13 @@ macro_rules! lang {
if value.is_none() { errln!("Key not found in LANG JSON: \"{}\"", $key); }
value.unwrap().as_str().expect("LANG JSON value is not a string!").to_string()
value.unwrap().as_str().expect("LANG JSON value is not a string!").to_string()*/
"hello".to_string()
}
};
($key:expr, $($arg:expr),*) => {{
use $crate::{LANG, errln};
/*use $crate::{LANG, errln};
use formatx::formatx;
let value = unsafe {
@@ -93,6 +95,7 @@ macro_rules! lang {
if value.is_none() { errln!("Key not found in LANG JSON: \"{}\"", $key); }
let format_str = value.unwrap().as_str().expect("LANG JSON value is not a string!");
formatx!(format_str, $($arg),*).unwrap()
formatx!(format_str, $($arg),*).unwrap()*/
"hello".to_string()
}};
}
+74 -57
View File
@@ -1,24 +1,22 @@
#![warn(unused_extern_crates)]
#![allow(clippy::needless_return)]
#![allow(static_mut_refs)]
mod events;
mod messages;
mod python;
mod macros;
#[allow(unknown_lints)]
mod websocket;
mod cmds;
mod db;
mod events;
mod games;
mod schedule;
mod gen;
mod lang;
mod macros;
mod messages;
mod python;
mod schedule;
mod websocket;
use std::process;
use std::thread;
use std::thread::JoinHandle;
use std::time::Duration;
use std::vec;
use std::error::Error as StdErr;
@@ -31,8 +29,8 @@ use tokio::runtime::Runtime;
use websocket::send_cmd_json;
use crate::db::bot_data::Data;
use crate::db::env_vars::AssistantEnv;
use crate::db::terminal_args::Args;
use crate::db::generic::get_toml_mutex;
use crate::schedule::Schedule;
@@ -43,79 +41,98 @@ type Cmd = Command<Data, Box<dyn StdErr + Send + Sync>>;
#[tokio::main]
async fn main() {
let args = <Args as clap::Parser>::parse();
let args_str = serde_json::to_string(&args).expect("Error serializing args to JSON");
let own_env = std::env::var("ASSISTANT_OWNERS").unwrap_or("0".to_string());
let own_vec_str: Vec<String> = own_env.split(",").map(String::from).collect();
let own_vec_u64: Vec<u64> = own_vec_str
.iter()
.map(|s| s.parse::<u64>().expect("Failed to parse ASSISTANT_OWNERS. Invalid syntax."))
.collect();
let args = Args::new();
rs_println!("ARGS: {}", args.to_string());
let env_vars = AssistantEnv::new(args.test);
rs_println!("Generating and/or fetching data and config...");
let data = gen_data(args.clone(), own_vec_u64.clone()).await;
let data = gen_data(args, env_vars).await;
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.py && !args.rs {
let _ = python::start(args, *data.lang_name.lock().await).await;
// We start this here cuz we have all the data that we need
if data.args.py && !data.args.rs {
let _ = python::start(
data.args.clone(),
data.lang_name.clone(),
data.env_vars.clone(),
).await;
process::exit(0);
}
else if args.rs && ! args.py {
start(args, data).await;
else if data.args.rs && ! data.args.py {
start_bot(data).await;
process::exit(0);
}
rs_println!("ARGS: {}", args_str);
let cfg = get_toml_mutex(&data.cfg).await.unwrap();
let cfg_arr = cfg["commands"]["disabled_categories"].as_array().unwrap();
let cfg_arr = data.cfg["commands"]["disabled_categories"].as_array().unwrap();
let run_py = !cfg_arr.iter().any(|val| val.as_str() == Some("re"));
let rt_rs = Runtime::new().unwrap();
let rt_py = Runtime::new().unwrap();
let python_args = args.clone();
let rust_args = args.clone();
if !run_py { rs_println!("[IMPORTANT] You have disabled the \"re\" commands in the CFG. The app will not run the Python code and the websockets to save resources!"); }
let rust = thread::spawn(move || {
rt_rs.block_on(async {
if run_py { websocket::start(rust_args.clone(), own_vec_u64.clone()).await; }
start(rust_args, data).await;
});
});
let python = thread::spawn(move || {
rt_py.block_on(async {
if run_py { let _ = python::start(python_args, *data.lang_name.lock().await).await; }
});
});
if !args.nosched {
let dur = if args.test { Duration::from_secs(60) } else { Duration::from_secs(60 * 10) };
let schedules: Vec<Schedule> = vec![
(dur, || Box::pin(read_reddit_inbox()))
];
run_schedules(schedules).await;
}
if !data.args.nosched { start_schedules(data.args.test); }
let python = start_py(
data.args.clone(),
data.lang_name.clone(),
data.env_vars.clone(),
run_py
);
let rust = start_rs(data, run_py);
rust.join().unwrap();
python.join().unwrap();
}
async fn start(args: Args, data: Data) {
let mut bot = gen_bot(data, args).await;
fn start_rs(data: Data, run_py: bool) -> JoinHandle<()> {
let rt = Runtime::new().unwrap();
return thread::spawn(move || {
rt.block_on(async {
if run_py { websocket::start(&data).await; }
start_bot(data).await;
});
});
}
fn start_py(
args: Args,
lang_name: String,
env_vars: AssistantEnv,
run_py: bool
) -> JoinHandle<()> {
let rt = Runtime::new().unwrap();
return thread::spawn(move || {
rt.block_on(async {
if run_py { let _ = python::start(args, lang_name, env_vars).await; }
});
});
}
async fn start_bot(data: Data) {
let mut bot = gen_bot(data).await;
rs_println!("Starting Discord bot...");
bot.start().await.unwrap();
}
async fn start_schedules(test: bool) {
let dur = if test { Duration::from_secs(60) }
else { Duration::from_secs(60 * 10) };
let schedules: Vec<Schedule> = vec![
(dur, || Box::pin(read_reddit_inbox()))
];
run_schedules(schedules).await;
}
async fn read_reddit_inbox() {
unsafe {
if !websocket::HAS_CONNECTED { return; }
+21 -9
View File
@@ -1,7 +1,7 @@
use std::env;
use std::io::{Read, Write};
use crate::{lang, Args, Context};
use crate::db::bot_data::Data;
use crate::{lang, Context};
use base64::engine::general_purpose;
use base64::Engine;
@@ -199,16 +199,26 @@ pub async fn http_edit_msg(
}
pub async fn send_dm(msg: String, args: Args, receivers: Vec<u64>) {
let token: String =
if !args.test { env::var("ASSISTANT_TOKEN") .expect("Missing ASSISTANT_TOKEN env var!") }
else { env::var("ASSISTANT_TOKEN_TEST").expect("Missing ASSISTANT_TOKEN_TEST env var!") };
pub async fn send_dm(msg: String, data: &Data) {
let http = Http::new(&data.env_vars.token);
let c_msg = CreateMessage::new().content(msg);
for uid in data.env_vars.bot_owners.clone() {
if uid == 0 { continue; }
let user = UserId::new(uid);
let _ = user.dm(http.as_ref(), c_msg.clone()).await;
}
}
pub async fn send_dm_min(msg: String, token: String, bot_owners: Vec<u64>) {
let http = Http::new(&token);
let c_msg = CreateMessage::new().content(msg);
for uid in receivers {
for uid in bot_owners {
if uid == 0 { continue; }
let user = UserId::new(uid);
let _ = user.dm(http.as_ref(), c_msg.clone()).await;
@@ -265,9 +275,11 @@ pub fn make_removed_embed(post_data: &Value, url: &str, ephemeral: bool) -> Embe
let desc = lang!(
"dc_msg_embed_re_removed",
if !post_data["removed"]["by"].is_null() { post_data["removed"]["by"].as_str().unwrap() }
if !post_data["removed"]["by"].is_null()
{ post_data["removed"]["by"].as_str().unwrap() }
else { &none },
if !post_data["removed"]["reason"].is_null() { post_data["removed"]["reason"].as_str().unwrap() }
if !post_data["removed"]["reason"].is_null()
{ post_data["removed"]["reason"].as_str().unwrap() }
else { &none }
);
+15 -24
View File
@@ -1,5 +1,7 @@
use crate::messages::send_dm;
use crate::{errln, lang, rs_println, Args};
use crate::db::env_vars::AssistantEnv;
use crate::db::terminal_args::Args;
use crate::messages::send_dm_min;
use crate::{errln, lang, rs_println};
use std::fs;
use std::ffi::CString;
@@ -9,7 +11,7 @@ use pyo3::prelude::*;
use pyo3::types::PyList;
pub async fn start(args: Args, lang_name: String) -> PyResult<()> {
pub async fn start(args: Args, lang_name: String, env_vars: AssistantEnv) -> PyResult<()> {
rs_println!("Running Python program...");
let args_str = serde_json::to_string(&args).expect("Error serializing args to JSON");
@@ -23,15 +25,12 @@ pub async fn start(args: Args, lang_name: String) -> PyResult<()> {
let py_args = args_str.replace(":true", ":True").replace(":false", ":False");
let app_path: CString;
unsafe {
app_path = CString::new(
format!("args = {}\nlang_name = \"{}\"\n{}",
py_args,
lang_name.clone(),
code
)
).unwrap();
}
app_path = CString::new(format!(
"args = {}\nlang_name = \"{}\"\n{}",
py_args,
lang_name,
code
)).unwrap();
let mut traceback: String = String::new();
let mut is_error = false;
@@ -57,19 +56,11 @@ pub async fn start(args: Args, lang_name: String) -> PyResult<()> {
});
if is_error {
let own_env = std::env::var("ASSISTANT_OWNERS").unwrap_or("0".to_string());
let own_vec_str: Vec<String> = own_env.split(",").map(String::from).collect();
let own_vec_u64: Vec<u64> = own_vec_str
.iter()
.map(|s| s.parse::<u64>().expect("Failed to parse ASSISTANT_OWNERS. Invalid syntax."))
.collect();
send_dm(
lang!("dc_msg_dm_python_err", format!("{}", traceback)),
args,
own_vec_u64
send_dm_min(
lang!("dc_msg_dm_python_err", traceback),
env_vars.token.clone(),
env_vars.bot_owners.clone()
).await;
errln!("pyO3: {}", traceback);
}
+28 -21
View File
@@ -8,7 +8,8 @@ use futures::StreamExt;
use std::sync::Arc;
use serde_json::{Value, json};
use crate::messages::send_dm;
use crate::db::bot_data::Data;
use crate::messages::send_dm_min;
use crate::{lang, rs_println};
use crate::Args;
@@ -103,17 +104,25 @@ async fn receive_response() -> Option<Value> {
}
pub async fn start(args: Args, owners: Vec<u64>) {
pub async fn start(data: &Data) {
rs_println!("Running local websocket...");
let ip = format!("127.0.0.1:{}", args.port);
let ip = format!("127.0.0.1:{}", data.args.port);
let listener = TcpListener::bind(&ip).await.unwrap();
rs_println!("WebSocket server running on ws://{}", ip);
tokio::spawn(handle_connections(listener, args, owners));
tokio::spawn(handle_connections(
listener,
data.env_vars.token.clone(),
data.env_vars.bot_owners.clone()
));
}
async fn handle_connections(listener: TcpListener, args: Args, owners: Vec<u64>) {
async fn handle_connections(
listener: TcpListener,
token: String,
bot_owners: Vec<u64>
) {
while let Ok((stream, _)) = listener.accept().await {
let ws_stream = accept_async(stream).await.unwrap();
let (sender, receiver) = ws_stream.split();
@@ -124,14 +133,17 @@ async fn handle_connections(listener: TcpListener, args: Args, owners: Vec<u64>)
set_sender(sender_arc.clone()).await;
set_receiver(receiver_arc.clone()).await;
while let Some(Ok(msg)) = receiver_arc.lock().await.as_mut().unwrap().next().await {
handle_message(msg, args.clone(), owners.clone()).await;
}
while let Some(Ok(msg)) = receiver_arc.lock().await.as_mut().unwrap().next().await
{ handle_message(msg, token.clone(), bot_owners.clone()).await; }
}
}
async fn handle_message(msg: tungstenite::protocol::Message, args: Args, owners: Vec<u64>) {
async fn handle_message(
msg: tungstenite::protocol::Message,
token: String,
bot_owners: Vec<u64>
) {
match msg {
tungstenite::Message::Text(text) => {
rs_println!("Received from Python: {}", text);
@@ -139,7 +151,11 @@ async fn handle_message(msg: tungstenite::protocol::Message, args: Args, owners:
if let Some(stripped) = text.strip_prefix("json:") {
let t_json: Value = serde_json::from_str(stripped).unwrap();
if t_json.get("error").is_some() {
send_dm(lang!("dc_msg_dm_python_err_socket"), args, owners).await;
send_dm_min(
lang!("dc_msg_dm_python_err_socket"),
token,
bot_owners
).await;
}
}
@@ -150,16 +166,7 @@ async fn handle_message(msg: tungstenite::protocol::Message, args: Args, owners:
HAS_CONNECTED = true;
}
}
}
tungstenite::Message::Binary(bytes) => {
if args.dev && args.ping {
rs_println!("[Binary] from Python: {:?}", bytes);
}
}
_ => {
if args.dev && args.ping {
rs_println!("Received from Python: [UNKNOWN / OTHER]");
}
}
},
_ => {}
}
}