(LARGE COMMIT) refactored until no errors
This commit is contained in:
@@ -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 <ins>never modifies any Reddit content</ins>. 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. |
|
||||
|
||||
<!--
|
||||
## Commands
|
||||
|
||||
| Name | Category | Description |
|
||||
@@ -44,18 +58,6 @@ An automation tool primarily made for myself (Byte Dice) but publicly available
|
||||
This program uses Rust (v1.82.0) and Python (v3.11.4), you can likely use other versions if they are compatible.\
|
||||
It is required to install all used Python modules. You can find those in [req.txt](/req.txt). Installation instructions are in the *How to start the program* section.
|
||||
|
||||
### Environment variables:
|
||||
| **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. |
|
||||
|
||||
### Required permissions:
|
||||
**These are automatically set if you use the [official invite link](https://discord.com/oauth2/authorize?client_id=1212127255795335208&permissions=84992&integration_type=0&scope=bot) or an invite link with the permissions integer set to `84992`.** (The permission integer is this part of the URL `&permissions=84992`)
|
||||
* Create Invites
|
||||
@@ -89,3 +91,4 @@ You can find config files in the [cfg/](cfg/) folder. You can also find the defa
|
||||
* Run `$ cd path/to/extracted/folder`.
|
||||
* Run `$ cargo run`. You can view a list of options by running `$ cargo run -- --help` or `$ cargo run -- -h`.
|
||||
* If you only want to run the Python code, you can either run `$ cargo run -- --py`, or `$ python ./src/python/main.py`. The second option is recommended for better performance.
|
||||
-->
|
||||
@@ -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)]
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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?;
|
||||
|
||||
@@ -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,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(
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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(());
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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; }
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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]");
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user