diff --git a/README.md b/README.md
index 6ddd3df..8fc4444 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,6 @@ An automation tool primarily made for myself (Byte Dice) but publicly available
> [!CAUTION]
> This tool is not intended for public use outside of the official *Byte Dice Assistant* bots. Expect issues if you host this yourself.\
-> This tool is only designed to run on Windows (10 and 11) and XUbuntu (24.04 and above) and may not work on any other OS.
> [!NOTE]
> This bot never modifies any Reddit content. All it does and will ever do is read/scrape.
@@ -16,6 +15,21 @@ An automation tool primarily made for myself (Byte Dice) but publicly available
**ByteDiceAssistant © 2025 by Byte Dice is licensed under CC BY-NC-SA 4.0.**\
**You can learn more about copyright by reading the full [license](/LICENSE.txt).**
+## Environment variables:
+(Any Reddit stuff can be disabled.)
+
+| **Name** | **Description** |
+| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `ASSISTANT_TOKEN` | The Discord bot token. (Create a Discord bot [here](https://discord.com/developers/docs/intro)!) |
+| `ASSISTANT_TOKEN_TEST` | (Optional) A testing Discord bot token. This is only needed when the program is run with `-t` or `--test`. |
+| `ASSISTANT_R_ID` | The id for the Reddit bot/account. (Create a Reddit bot [here](https://www.reddit.com/prefs/apps)!) |
+| `ASSISTANT_R_TOKEN` | The token for the Reddit bot/account. |
+| `ASSISTANT_R_NAME` | The username of the Reddit bot/account. |
+| `ASSISTANT_R_PASS` | The password for the Reddit bot/account. |
+| `ASSISTANT_OWNERS` | (OPTIONAL) A list of Discord user IDs that "own" the bot. Separate each ID with a single comma and **no** spaces. This will allow the specified user IDs to run root commands such as `/stop`, it will also DM these users when *certain* errors occur. |
+| `ASSISTANT_BK_MODS` | (OPTIONAL) Same format as `ASSISTANT_OWNERS` but for people who are allowed to use the `/re_*` commands. |
+
+
\ No newline at end of file
diff --git a/src/cmds/db/main_cmd.rs b/src/cmds/db/main_cmd.rs
index 92d49dd..0b5338a 100644
--- a/src/cmds/db/main_cmd.rs
+++ b/src/cmds/db/main_cmd.rs
@@ -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)]
diff --git a/src/cmds/generic/eight_ball.rs b/src/cmds/generic/eight_ball.rs
index 653d261..bc2b1aa 100644
--- a/src/cmds/generic/eight_ball.rs
+++ b/src/cmds/generic/eight_ball.rs
@@ -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());
diff --git a/src/cmds/generic/wwrps.rs b/src/cmds/generic/wwrps.rs
index 48dd4de..c6c5d91 100644
--- a/src/cmds/generic/wwrps.rs
+++ b/src/cmds/generic/wwrps.rs
@@ -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 {
- 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();
diff --git a/src/cmds/reddit/add.rs b/src/cmds/reddit/add.rs
index e0dcacc..cc8e04c 100644
--- a/src/cmds/reddit/add.rs
+++ b/src/cmds/reddit/add.rs
@@ -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) {
diff --git a/src/cmds/reddit/approve.rs b/src/cmds/reddit/approve.rs
index 03c141a..1f4c76e 100644
--- a/src/cmds/reddit/approve.rs
+++ b/src/cmds/reddit/approve.rs
@@ -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;
diff --git a/src/cmds/reddit/generic_fns.rs b/src/cmds/reddit/generic_fns.rs
index c37c2a4..46860d7 100644
--- a/src/cmds/reddit/generic_fns.rs
+++ b/src/cmds/reddit/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::{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, uid: u64) -> bool {
return mod_list.contains(&uid);
@@ -10,7 +10,7 @@ pub fn is_bk_mod(mod_list: Vec, 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 {
- 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())
diff --git a/src/cmds/reddit/get.rs b/src/cmds/reddit/get.rs
index edfb150..6112869 100644
--- a/src/cmds/reddit/get.rs
+++ b/src/cmds/reddit/get.rs
@@ -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?;
diff --git a/src/cmds/reddit/remove.rs b/src/cmds/reddit/remove.rs
index 82bd5cf..7e1f89e 100644
--- a/src/cmds/reddit/remove.rs
+++ b/src/cmds/reddit/remove.rs
@@ -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() {
diff --git a/src/cmds/reddit/shorturl.rs b/src/cmds/reddit/shorturl.rs
index a30f50d..4593f76 100644
--- a/src/cmds/reddit/shorturl.rs
+++ b/src/cmds/reddit/shorturl.rs
@@ -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(
diff --git a/src/cmds/reddit/top.rs b/src/cmds/reddit/top.rs
index 6905f26..6a4591f 100644
--- a/src/cmds/reddit/top.rs
+++ b/src/cmds/reddit/top.rs
@@ -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 {
diff --git a/src/cmds/reddit/update.rs b/src/cmds/reddit/update.rs
index cfaa591..4e79c64 100644
--- a/src/cmds/reddit/update.rs
+++ b/src/cmds/reddit/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::{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 {
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()]
diff --git a/src/cmds/reddit/vote.rs b/src/cmds/reddit/vote.rs
index 1af06f0..9b6d8e5 100644
--- a/src/cmds/reddit/vote.rs
+++ b/src/cmds/reddit/vote.rs
@@ -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
) -> 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(());
}
\ No newline at end of file
diff --git a/src/db.rs b/src/db.rs
index de3b182..fedc350 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -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;
\ No newline at end of file
+pub mod reddit;
+pub mod terminal_args;
\ No newline at end of file
diff --git a/src/db/bot_data.rs b/src/db/bot_data.rs
index 6b36d0c..a28a3df 100644
--- a/src/db/bot_data.rs
+++ b/src/db/bot_data.rs
@@ -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,
- pub ball_prompts: [Vec; 2],
- pub rps_game: Mutex,
- pub reddit_data: Mutex