Compare commits
15
Commits
main
...
wwrps_ranking
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
453890c776 | ||
|
|
ff1b4f4e01 | ||
|
|
9e17b7db7b | ||
|
|
9e890c9d7c | ||
|
|
afa966804f | ||
|
|
0836131045 | ||
|
|
55329505b4 | ||
|
|
4bb7262171 | ||
|
|
ce8c58a5e3 | ||
|
|
de2cf4dc64 | ||
|
|
1b3da60f76 | ||
|
|
ed8904e26c | ||
|
|
df7583263a | ||
|
|
ae9b1f3ba0 | ||
|
|
7ecabfb0c8 |
@@ -1,4 +1,3 @@
|
|||||||
debug/
|
|
||||||
target/
|
target/
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
|||||||
+1
-1
@@ -8,8 +8,8 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
clap = { version = "4.5.28", features = ["derive"] }
|
clap = { version = "4.5.28", features = ["derive"] }
|
||||||
|
dynfmt = { version = "0.1.5", features = ["curly"] }
|
||||||
flate2 = "1.1.2"
|
flate2 = "1.1.2"
|
||||||
formatx = "0.2.3"
|
|
||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
poise = "0.6.1"
|
poise = "0.6.1"
|
||||||
pyo3 = "0.28.2"
|
pyo3 = "0.28.2"
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ An automation tool primarily made for myself (Byte Dice) but publicly available
|
|||||||
|
|
||||||
> [!CAUTION]
|
> [!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 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]
|
> [!NOTE]
|
||||||
> This bot <ins>never modifies any Reddit content</ins>. All it does and will ever do is read/scrape.
|
> 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.**\
|
**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).**
|
**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
|
## Commands
|
||||||
|
|
||||||
| Name | Category | Description |
|
| 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.\
|
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.
|
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:
|
### 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`)
|
**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
|
* 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 `$ cd path/to/extracted/folder`.
|
||||||
* Run `$ cargo run`. You can view a list of options by running `$ cargo run -- --help` or `$ cargo run -- -h`.
|
* 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.
|
* 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,9 +1,14 @@
|
|||||||
{
|
{
|
||||||
"servers": {
|
"servers": {
|
||||||
"SERVER ID": {
|
"SERVER_ID": {
|
||||||
"re_posts_channel": 0,
|
"re_posts_channel": 0,
|
||||||
"re_disabled": false,
|
"re_disabled": false,
|
||||||
"wwrps_channel": 0
|
"wwrps_channel": 0
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"users": {
|
||||||
|
"USER_ID": {
|
||||||
|
"wwrps_elo": 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+96
-76
@@ -1,78 +1,98 @@
|
|||||||
{
|
{
|
||||||
"dc_btn_approve": "Approve",
|
"generic": {
|
||||||
"dc_btn_remove": "Remove",
|
"none": "None",
|
||||||
"dc_btn_unapprove": "Disapprove",
|
"success": "Success!"
|
||||||
"dc_btn_unremove": "Restore",
|
},
|
||||||
"dc_btn_unvote": "Un-vote",
|
"dc": {
|
||||||
"dc_btn_vote": "Vote",
|
"re": {
|
||||||
"dc_msg_8-ball_answer": "**8-ball Question:** {0}\n**Answer:** {1}",
|
"buttons": {
|
||||||
"dc_msg_add_to_data": "Added your server to my data! Thanks for letting me steal it! (/s)",
|
"approve": "Approve",
|
||||||
"dc_msg_bound_channel": "Successfully added channel ID `{0}` to the database!",
|
"remove": "Remove",
|
||||||
"dc_msg_cmd_404": "No command \"{0}\" found!\nHint: Try `/help` without any arguments or `/help <category>`",
|
"disapprove": "Disapprove",
|
||||||
"dc_msg_corrupted_data": "Oopsies `(。>\\\\<)`. It looks like my data i-is \\**sob*\\*... c-corrupted!",
|
"unremove": "Restore",
|
||||||
"dc_msg_data_server_404": "This server is not in the data!\n Hint: Run the command `/database AddServer` inside of a Discord server (requires administrator permission).",
|
"vote": "Vote",
|
||||||
"dc_msg_dm_python_err_socket": "Unknown internal Python error occurred: Websocket response error",
|
"unvote": "Un-vote"
|
||||||
"dc_msg_dm_python_err": "Unknown internal Python Error:\n```\n{0}\n```",
|
},
|
||||||
"dc_msg_embed_default_embed_desc": "Default english embed description.",
|
"add": {
|
||||||
"dc_msg_embed_re_post": "Spoilers and vote length anonymizer for fair review!\n## Post Data:\n**Subreddit:** {0}\n**Post upvotes:** ||`{1:>6}`||\n**Moderator votes:** ||`{2:>6}`||\n**Media type:** `{3}`\n## Listing Data:\n**Added by:** human: {4} | bot: {5}\n**Approved:** {6}",
|
"success": "Added post with URL \"<{0}>\"!",
|
||||||
"dc_msg_embed_re_removed": "## Removed by `{0}`\n**Reason:** {1}",
|
"approved": "Also approved it!"
|
||||||
"dc_msg_err_trace": "Unknown error!\nError trace: {0}",
|
},
|
||||||
"dc_msg_failed_shorturl_conversion": "Couldn't convert to shortURL: Invalid Reddit URL format.",
|
"approve": {
|
||||||
"dc_msg_mandatory_response": "Mandatory response message, please ignore.",
|
"success": "Successfully approved the post!",
|
||||||
"dc_msg_no_perms": "Missing permission in that server: {0}",
|
"disapprove": "Successfully disapproved the post!",
|
||||||
"dc_msg_not_in_guild": "I am not a part of that guild.",
|
"post_removed": "Couldn't approve the post because it has been removed!"
|
||||||
"dc_msg_owner_data_save_complete": "Saving data... Done!",
|
},
|
||||||
"dc_msg_owner_data_save": "Saving data...",
|
"remove": {
|
||||||
"dc_msg_owner_shutdown": "Shutting down...",
|
"success": "Successfully removed post with URL \"<{0}>\"!",
|
||||||
"dc_msg_owner_shutdown_failed_confirmation": "Failed to shut down: Invalid confirmation.",
|
"unremove": "Successfully restored post with URL \"<{0}>\"!"
|
||||||
"dc_msg_re_already_voted": "Couldn't cast a vote: You have already voted on this post!",
|
},
|
||||||
"dc_msg_re_also_approved": "Also approved it!",
|
"vote": {
|
||||||
"dc_msg_re_data_corrupted": "Post URL \"<{0}>\" not found: Post data is corrupted!\n Full details: Could not find key \"posts\" in data file \"re_data.json\"",
|
"success": "Successfully voted!",
|
||||||
"dc_msg_re_help_removed": "Reddit help file not found, someone likely deleted it.\n Hint: You can find the official help text at https://github.com/ByteDice/ByteDiceAssistant in `re_help.md`.\n**CAUTION:** The official help file may contain incorrect information if you're not using the official bot by Byte Dice.",
|
"removed": "Successfully removed vote!",
|
||||||
"dc_msg_re_permdeny_not_re_mod": "Permission denied: You are not a moderator of the subreddit(s) {0}",
|
"mod": "Successfully voted (as moderator vote)!",
|
||||||
"dc_msg_re_post_404": "Post URL \"<{0}>\" not found: Post doesn't exist in the data!\n Hint: Run the command `/re_addpost [URL]` in a Discord channel or `u/ByteDiceAssistant add_post` in a Reddit post.",
|
"post_removed": "Couldn't [vote / un-vote] the post because the post has been removed!",
|
||||||
"dc_msg_re_post_add_success": "Added post with URL \"<{0}>\"!",
|
"remove_hasnt_voted": "Couldn't remove your vote: You haven't voted on this post yet!",
|
||||||
"dc_msg_re_post_approve_remove": "Couldn't approve the post because it has been removed!",
|
"already": "Couldn't cast a vote: You have already voted on this post!",
|
||||||
"dc_msg_re_post_approve_success": "Successfully approved the post!",
|
"error": "Failed to [vote / un-vote]: Unknown internal error."
|
||||||
"dc_msg_re_post_disapprove_success": "Successfully disapproved the post!",
|
},
|
||||||
"dc_msg_re_post_remove_success": "Successfully removed post with URL \"<{0}>\"!",
|
"update": {
|
||||||
"dc_msg_re_post_unremove_success": "Successfully restored post with URL \"<{0}>\"!",
|
"fetch": "\nFetching new posts & updating data file...",
|
||||||
"dc_msg_re_post_update_success": "Updated post with URL \"<{0}>\"!",
|
"read": "✅\nReading messages in <#{1}>...",
|
||||||
"dc_msg_re_post_vote_removed_post": "Couldn't [vote / un-vote] the post because it has been removed!",
|
"parse": "✅\nParsing messages to JSON...",
|
||||||
"dc_msg_re_posts_channel_404": "Could not find `re_posts_channel` in data!\nHint: Run `/database RedditChannel` in a (preferably read-only) channel (requires administrator permission).",
|
"add": "✅\nAdding new posts...",
|
||||||
"dc_msg_re_vote_err": "Failed to [vote / un-vote]: Unknown internal error.",
|
"rm_dupe": "✅\nRemoving duplicate posts...",
|
||||||
"dc_msg_re_vote_mod_success": "Successfully voted (as moderator vote)!",
|
"rm": "✅\nRemoving removed posts...",
|
||||||
"dc_msg_re_vote_remove_havent": "Couldn't remove your vote: You haven't voted on this post yet!",
|
"rm_old": "✅\nRemoving old posts (threshold: {1}d)...",
|
||||||
"dc_msg_re_vote_remove_success": "Successfully removed vote!",
|
"editing": "✅\nEditing updated posts...",
|
||||||
"dc_msg_re_vote_success": "Successfully voted!",
|
"done": "✅\n## Done!"
|
||||||
"dc_msg_reload_cfg_python_fail": "Failed to reload configs: Failed-type response from Python.",
|
},
|
||||||
"dc_msg_reload_cfg_success": "Successfully reloaded the configs!\nNew configs:\n```\n{0}\n```",
|
"embed": {
|
||||||
"dc_msg_removed_square_brackets": "[REMOVED] {0}",
|
"post": "Spoilers and vote length anonymizer for fair review!\n## Post Data:\n**Subreddit:** {0}\n**Post upvotes:** ||`{1:>6}`||\n**Moderator votes:** ||`{2:>6}`||\n**Media type:** `{3}`\n## Listing Data:\n**Added by:** human: {4} | bot: {5}\n**Approved:** {6}",
|
||||||
"dc_msg_shorturl": "ShortURL: <{0}>",
|
"post_removed": "## Removed by `{0}`\n**Reason:** {1}",
|
||||||
"dc_msg_update_add": "{0}Adding new posts...",
|
"removed_square_brackets": "[REMOVED] {0}"
|
||||||
"dc_msg_update_done": "{0}Done!",
|
},
|
||||||
"dc_msg_update_editing": "{0}Editing updated posts...",
|
"update_post": "Updated post with URL \"<{0}>\"!",
|
||||||
"dc_msg_update_fetch": "{0}Fetching new posts & updating data file...",
|
"help_404": "Reddit help file not found, someone likely deleted it.\n-# Hint: You can find the official help text at https://github.com/ByteDice/ByteDiceAssistant in `re_help.md`.\n**CAUTION:** The official help file may contain incorrect information if you're not using the official bot by Byte Dice.",
|
||||||
"dc_msg_update_parse": "{0}Parsing messages to JSON...",
|
"post_404": "Post URL \"<{0}>\" not found: Post doesn't exist in the data!\n-# Hint: Run the command `/re_addpost [URL]` in a Discord channel or `u/ByteDiceAssistant add_post` in a Reddit post.",
|
||||||
"dc_msg_update_read": "{0}Reading messages in <#{1}>...",
|
"not_mod": "Permission denied: You are not a moderator of the subreddit(s) {0}",
|
||||||
"dc_msg_update_removing_dupe": "{0}Removing duplicate posts...",
|
"post_corrupted": "Post URL \"<{0}>\" not found: Post data is corrupted!\n-# Full details: Could not find key \"posts\" in data file \"re_data.json\"",
|
||||||
"dc_msg_update_removing_old": "{0}Removing old posts (threshold: {1}d)...",
|
"shorturl": "ShortURL: <{0}>",
|
||||||
"dc_msg_update_removing": "{0}Removing removed posts...",
|
"shorturl_fail": "Couldn't convert to shortURL: Invalid Reddit URL format."
|
||||||
"dc_msg_whoami": "**Bot \"owner\":** {0}\n**BK moderator:** {1}",
|
},
|
||||||
"dc_msg_wwrps_already_submitted": "You can't compete against yourself! Please wait until another player has submitted their RPS.",
|
"db": {
|
||||||
"dc_msg_wwrps_anon": "[Anonymous]",
|
"added_server": "Added your server to my database. Thanks for letting me steal it! (/s)",
|
||||||
"dc_msg_wwrps_draw": "DRAW",
|
"corrupted_data": "Couldn't add your server to the database! The database is corrupted!",
|
||||||
"dc_msg_wwrps_fight": "### {0} (P1) vs {1} (P2)...\n## {2}\n**P1:** {3}\n**P2:** {4}",
|
"server_404": "This server is not in the data!\n-# Hint: Run the command `/database AddServer` inside of a Discord server (requires administrator permission).",
|
||||||
"dc_msg_wwrps_not_in_data": "Could not find `wwrps_channel` in data!\nHint: Run `/database WWRPSChannel` in a channel (requires administrator permission).",
|
"wwrps_404": "Could not find `wwrps_channel` in data!\n-# Hint: Run `/database WWRPSChannel` in a channel (requires administrator permission).",
|
||||||
"dc_msg_wwrps_left_win": "P1 WINS",
|
"bk_404": "Could not find `re_posts_channel` in data!\n-# Hint: Run `/database RedditChannel` in a (preferably read-only) channel (requires administrator permission).",
|
||||||
"dc_msg_wwrps_right_win": "P2 WINS",
|
"channel_bind": "Successfully added channel ID `{0}` to the database!",
|
||||||
"dc_msg_wwrps_submitting": "Submitting your RPS... Please wait for another contestant to compete against you.",
|
"not_in_guild": "I am not a part of that guild.",
|
||||||
"log_lang_load_success": "Successfully loaded the english language file!",
|
"saving_progress": "Saving data...",
|
||||||
"none": "None",
|
"saving_done": "Saving data... Done!"
|
||||||
"py_re_response_suffix": "^(I am not an AI, I am just a bot. This action was performed automatically by the way. You can report bugs and view my source code [here](https://github.com/ByteDice/ByteDiceAssistant)!)",
|
},
|
||||||
"py_re_response_weekly_add": "Successfully added your post to the weekly art submissions! Thank you for participating!",
|
"wwrps": {
|
||||||
"py_re_response_weekly_exists": "Couldn't add this post to the submissions! Luckily, it's already there! Thank you for participating!",
|
"submitting": "Submitting your RPS... Please wait for another contestant to compete against you.",
|
||||||
"py_re_response_weekly_mod_add": "[MOD ACTION] Successfully added this post to the weekly art submissions!",
|
"already_submitted": "You can't compete against yourself! Please wait until another player has submitted their RPS.",
|
||||||
"py_re_response_weekly_mod_unremove": "[MOD ACTION] Successfully un-removed this post from the weekly art submissions!",
|
"anon": "[Anonymous]",
|
||||||
"success": "Success!"
|
"draw": "DRAW",
|
||||||
|
"match": "### {0} (P1) vs {1} (P2)...\n## {2}\n**P1:** {3} **[{4} +{5}] {6}**\n**P2:** {7} **[{8} +{9}] {10}**",
|
||||||
|
"p1_win": "P1 WINS",
|
||||||
|
"p2_win": "P2 WINS"
|
||||||
|
},
|
||||||
|
"8_ball": "**8-ball Question:** {0}\n**Answer:** {1}",
|
||||||
|
"no_perms_external": "Missing permission in that server: {0}",
|
||||||
|
"shutdown": "Shutting down...",
|
||||||
|
"shutdown_bad_confirm": "Failed to shut down: Invalid confirmation.",
|
||||||
|
"whoami": "**Bot \"owner\":** {0}\n**BK moderator:** {1}",
|
||||||
|
"mandatory_response": "Mandatory response message, please ignore.",
|
||||||
|
"cmd_404": "No command \"{0}\" found!\nHint: Try `/help` without any arguments or `/help <category>`"
|
||||||
|
},
|
||||||
|
"re": {
|
||||||
|
"suffix": "^(I am not an AI, I am just a bot. This action was performed automatically by the way. You can report bugs and view my source code [here](https://github.com/ByteDice/ByteDiceAssistant)!)",
|
||||||
|
"add": "Successfully added your post to the weekly art submissions! Thank you for participating!",
|
||||||
|
"exists": "Couldn't add this post to the submissions! Luckily, it's already there! Thank you for participating!",
|
||||||
|
"mod_add": "[MOD ACTION] Successfully added this post to the weekly art submissions!",
|
||||||
|
"mod_unremove": "[MOD ACTION] Successfully un-removed this post from the weekly art submissions!"
|
||||||
|
},
|
||||||
|
"lang_load": "Successfully loaded the english language file!"
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
pub mod db;
|
||||||
|
pub mod debug;
|
||||||
|
pub mod generic;
|
||||||
|
pub mod reddit;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
pub mod add_server;
|
||||||
|
pub mod main_cmd;
|
||||||
|
pub mod reddit_channel;
|
||||||
|
pub mod wwrps_channel;
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
use crate::{db::discord::add_server, messages::send_msg, Context, Error};
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn cmd(
|
||||||
|
ctx: Context<'_>
|
||||||
|
) -> Result<(), Error>
|
||||||
|
{
|
||||||
|
let r = add_server(ctx.data(), ctx.guild_id().unwrap().into()).await;
|
||||||
|
|
||||||
|
if r.is_ok()
|
||||||
|
{ send_msg(ctx, ctx.data().lang.get("dc.db.added_server", &[]), true, true).await; }
|
||||||
|
else { send_msg(ctx, ctx.data().lang.get("dc.db.corrupted_data", &[]), true, true).await; }
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
@@ -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)]
|
#[derive(poise::ChoiceParameter, PartialEq)]
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
use crate::{db::discord::bind_bk, messages::send_msg, Context, Error};
|
||||||
|
|
||||||
|
pub async fn cmd(ctx: Context<'_>) -> Result<(), Error> {
|
||||||
|
let c_id = ctx.channel_id().into();
|
||||||
|
let r = bind_bk(ctx.data(), ctx.guild_id().unwrap().into(), c_id).await;
|
||||||
|
|
||||||
|
if r.is_ok()
|
||||||
|
{ send_msg(ctx, ctx.data().lang.get("dc.db.channel_bind", &[c_id.to_string()]), true, true).await; }
|
||||||
|
else { send_msg(ctx, ctx.data().lang.get("dc.db.server_404", &[]), true, true).await; }
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
use crate::{Context, Error, db::discord::bind_wwrps, messages::send_msg};
|
||||||
|
|
||||||
|
pub async fn cmd(
|
||||||
|
ctx: Context<'_>
|
||||||
|
) -> Result<(), Error>
|
||||||
|
{
|
||||||
|
let c_id = ctx.channel_id().into();
|
||||||
|
let r = bind_wwrps(ctx.data(), ctx.guild_id().unwrap().into(), c_id).await;
|
||||||
|
|
||||||
|
if r.is_ok()
|
||||||
|
{ send_msg(ctx, ctx.data().lang.get("dc.db.channel_bind", &[c_id.to_string()]), true, true).await; }
|
||||||
|
else { send_msg(ctx, ctx.data().lang.get("dc.db.server_404", &[]), true, true).await; }
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
pub mod guild_invite;
|
||||||
|
pub mod lang;
|
||||||
|
pub mod leave_guild;
|
||||||
|
pub mod main_cmd;
|
||||||
|
pub mod ping;
|
||||||
|
pub mod reload_cfg;
|
||||||
|
pub mod save;
|
||||||
|
pub mod stop;
|
||||||
|
pub mod view_guilds;
|
||||||
|
pub mod whoami;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use poise::serenity_prelude::{CreateInvite, GuildId};
|
use poise::serenity_prelude::{CreateInvite, GuildId};
|
||||||
|
|
||||||
use crate::{Context, Error, lang, messages::send_msg};
|
use crate::{Context, Error, messages::send_msg};
|
||||||
|
|
||||||
|
|
||||||
pub async fn cmd(ctx: Context<'_>, guild_id: u64) -> Result<(), Error> {
|
pub async fn cmd(ctx: Context<'_>, guild_id: u64) -> Result<(), Error> {
|
||||||
@@ -8,7 +8,7 @@ pub async fn cmd(ctx: Context<'_>, guild_id: u64) -> Result<(), Error> {
|
|||||||
let guild = id.to_partial_guild(ctx.http()).await?;
|
let guild = id.to_partial_guild(ctx.http()).await?;
|
||||||
|
|
||||||
if !ctx.serenity_context().cache.guilds().contains(&id) {
|
if !ctx.serenity_context().cache.guilds().contains(&id) {
|
||||||
send_msg(ctx, lang!("dc_msg_not_in_guild"), true, true).await;
|
send_msg(ctx, ctx.data().lang.get("dc.db.not_in_guild", &[]), true, true).await;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,7 +22,12 @@ pub async fn cmd(ctx: Context<'_>, guild_id: u64) -> Result<(), Error> {
|
|||||||
let perms = guild.user_permissions_in(&channel, bot_member);
|
let perms = guild.user_permissions_in(&channel, bot_member);
|
||||||
|
|
||||||
if !perms.create_instant_invite() {
|
if !perms.create_instant_invite() {
|
||||||
send_msg(ctx, lang!("dc_msg_no_perms", "CreateInvite"), true, true).await;
|
send_msg(
|
||||||
|
ctx,
|
||||||
|
ctx.data().lang.get("dc.no_perms_external", &["CreateInvite".to_string()]),
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
).await;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
use crate::{Context, Error, messages::send_msg};
|
||||||
|
|
||||||
|
pub async fn cmd(ctx: Context<'_>, path: String) -> Result<(), Error> {
|
||||||
|
send_msg(ctx, ctx.data().lang.get(path.as_str(), &[]), true, true).await;
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use poise::serenity_prelude::GuildId;
|
use poise::serenity_prelude::GuildId;
|
||||||
|
|
||||||
use crate::{Context, Error, lang, messages::send_msg};
|
use crate::{Context, Error, messages::send_msg};
|
||||||
|
|
||||||
|
|
||||||
pub async fn cmd(ctx: Context<'_>, guild_id: u64) -> Result<(), Error> {
|
pub async fn cmd(ctx: Context<'_>, guild_id: u64) -> Result<(), Error> {
|
||||||
@@ -8,12 +8,12 @@ pub async fn cmd(ctx: Context<'_>, guild_id: u64) -> Result<(), Error> {
|
|||||||
let guild = id.to_partial_guild(ctx.http()).await?;
|
let guild = id.to_partial_guild(ctx.http()).await?;
|
||||||
|
|
||||||
if !ctx.serenity_context().cache.guilds().contains(&id) {
|
if !ctx.serenity_context().cache.guilds().contains(&id) {
|
||||||
send_msg(ctx, lang!("dc_msg_not_in_guild"), true, true).await;
|
send_msg(ctx, ctx.data().lang.get("dc.db.not_in_guild", &[]), true, true).await;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
guild.leave(ctx.http()).await?;
|
guild.leave(ctx.http()).await?;
|
||||||
send_msg(ctx, lang!("success"), true, true).await;
|
send_msg(ctx, ctx.data().lang.get("generic.success", &[]), true, true).await;
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
use crate::{Context, Error, debug_cmds::{guild_invite, leave_guild, ping, save, stop, view_guilds, whoami}};
|
use crate::{Context, Error, cmds::debug::{guild_invite, lang, leave_guild, ping, save, stop, view_guilds, whoami}};
|
||||||
|
|
||||||
|
|
||||||
#[derive(poise::ChoiceParameter, PartialEq)]
|
#[derive(poise::ChoiceParameter, PartialEq)]
|
||||||
pub enum Subcommands {
|
pub enum Subcommands {
|
||||||
GuildInvite,
|
GuildInvite,
|
||||||
|
Lang,
|
||||||
LeaveGuild,
|
LeaveGuild,
|
||||||
Ping,
|
Ping,
|
||||||
//ReloadCfg,
|
//ReloadCfg,
|
||||||
@@ -31,9 +32,11 @@ pub async fn cmd(
|
|||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
{
|
{
|
||||||
let u64_arg_u: u64 = u64_arg.unwrap_or("0".to_string()).as_str().parse()?;
|
let u64_arg_u: u64 = u64_arg.unwrap_or("0".to_string()).as_str().parse()?;
|
||||||
|
let str_arg_u: String = string_arg.clone().unwrap_or("".to_string());
|
||||||
|
|
||||||
match subcommand {
|
match subcommand {
|
||||||
Subcommands::GuildInvite => guild_invite::cmd(ctx, u64_arg_u).await?,
|
Subcommands::GuildInvite => guild_invite::cmd(ctx, u64_arg_u).await?,
|
||||||
|
Subcommands::Lang => lang::cmd(ctx, str_arg_u).await?,
|
||||||
Subcommands::LeaveGuild => leave_guild::cmd(ctx, u64_arg_u).await?,
|
Subcommands::LeaveGuild => leave_guild::cmd(ctx, u64_arg_u).await?,
|
||||||
Subcommands::Ping => ping::cmd(ctx).await?,
|
Subcommands::Ping => ping::cmd(ctx).await?,
|
||||||
//Subcommands::ReloadCfg => reload_cfg::cmd(ctx).await?,
|
//Subcommands::ReloadCfg => reload_cfg::cmd(ctx).await?,
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
use crate::{Context, Error, db::{discord, reddit}, messages::{edit_reply, send_msg}};
|
||||||
|
|
||||||
|
pub async fn cmd(ctx: Context<'_>) -> Result<(), Error> {
|
||||||
|
let msg = send_msg(
|
||||||
|
ctx,
|
||||||
|
ctx.data().lang.get("dc.db.saving_progress", &[]),
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
).await.unwrap();
|
||||||
|
|
||||||
|
discord::write_data(ctx.data()).await;
|
||||||
|
reddit ::write_data().await;
|
||||||
|
|
||||||
|
edit_reply(ctx, msg, ctx.data().lang.get("dc.db.saving_done", &[])).await;
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ use std::process;
|
|||||||
|
|
||||||
use poise::serenity_prelude::OnlineStatus;
|
use poise::serenity_prelude::OnlineStatus;
|
||||||
|
|
||||||
use crate::{data, lang, messages::{edit_reply, send_msg}, websocket::send_cmd_json, Context, Error};
|
use crate::{db::{discord, reddit}, messages::{edit_reply, send_msg}, websocket::send_cmd_json, Context, Error};
|
||||||
|
|
||||||
pub async fn cmd(ctx: Context<'_>, confirmation: Option<String>) -> Result<(), Error> {
|
pub async fn cmd(ctx: Context<'_>, confirmation: Option<String>) -> Result<(), Error> {
|
||||||
let stop_confirm = "i want to stop the bot now".replace(" ", "");
|
let stop_confirm = "i want to stop the bot now".replace(" ", "");
|
||||||
@@ -10,15 +10,21 @@ pub async fn cmd(ctx: Context<'_>, confirmation: Option<String>) -> Result<(), E
|
|||||||
let should_stop = ctx.data().args.dev || confirm_formatted == stop_confirm;
|
let should_stop = ctx.data().args.dev || confirm_formatted == stop_confirm;
|
||||||
|
|
||||||
if should_stop {
|
if should_stop {
|
||||||
let msg = send_msg(ctx, lang!("dc_msg_owner_data_save"), true, true).await.unwrap();
|
let msg = send_msg(
|
||||||
data::write_dc_data(ctx.data()).await;
|
ctx,
|
||||||
data::write_re_data().await;
|
ctx.data().lang.get("dc.db.saving_progress", &[]),
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
).await.unwrap();
|
||||||
|
|
||||||
|
discord::write_data(ctx.data()).await;
|
||||||
|
reddit ::write_data().await;
|
||||||
send_cmd_json("stop_praw", None, true).await;
|
send_cmd_json("stop_praw", None, true).await;
|
||||||
|
|
||||||
let complete = format!(
|
let complete = format!(
|
||||||
"{}\n{}",
|
"{}\n{}",
|
||||||
lang!("dc_msg_owner_data_save_complete"),
|
ctx.data().lang.get("dc.db.saving_done", &[]),
|
||||||
lang!("dc_msg_owner_shutdown")
|
ctx.data().lang.get("dc.shutdown", &[])
|
||||||
);
|
);
|
||||||
|
|
||||||
edit_reply(ctx, msg, complete).await;
|
edit_reply(ctx, msg, complete).await;
|
||||||
@@ -27,9 +33,7 @@ pub async fn cmd(ctx: Context<'_>, confirmation: Option<String>) -> Result<(), E
|
|||||||
|
|
||||||
process::exit(0);
|
process::exit(0);
|
||||||
}
|
}
|
||||||
else {
|
else { send_msg(ctx, ctx.data().lang.get("dc.shutdown_bad_confirm", &[]), true, true).await; }
|
||||||
send_msg(ctx, lang!("dc_msg_owner_shutdown_failed_confirmation"), true, true).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
use crate::{messages::send_msg, Context, Error};
|
||||||
|
|
||||||
|
pub async fn cmd(ctx: Context<'_>) -> Result<(), Error> {
|
||||||
|
let data = ctx.data();
|
||||||
|
let uid: u64 = ctx.author().id.into();
|
||||||
|
|
||||||
|
let is_owner = data.env_vars.bot_owners.contains(&uid);
|
||||||
|
let is_bk_mod = data.env_vars.reddit_mod_discord_ids.contains(&uid);
|
||||||
|
|
||||||
|
send_msg(
|
||||||
|
ctx,
|
||||||
|
ctx.data().lang.get("dc.whoami", &[is_owner.to_string(), is_bk_mod.to_string()]),
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
).await;
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
pub mod eight_ball;
|
||||||
|
pub mod embed;
|
||||||
|
pub mod help;
|
||||||
|
pub mod send;
|
||||||
|
pub mod wwrps;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use rand::{seq::IteratorRandom, Rng};
|
use rand::{seq::IteratorRandom, Rng};
|
||||||
|
|
||||||
use crate::{data::get_toml_mutex, lang, messages::send_msg, Context, Error};
|
use crate::{messages::send_msg, Context, Error};
|
||||||
|
|
||||||
|
|
||||||
#[poise::command(
|
#[poise::command(
|
||||||
@@ -16,14 +16,16 @@ pub async fn cmd(
|
|||||||
#[description = "Question to ask."] question: String
|
#[description = "Question to ask."] question: String
|
||||||
) -> Result<(), Error>
|
) -> 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 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 list = &ctx.data().ball_prompts[if is_quirky { 1 } else { 0 }];
|
||||||
let rand_item = list.iter().choose(&mut rand::rng());
|
let rand_item = list.iter().choose(&mut rand::rng());
|
||||||
|
|
||||||
send_msg(
|
send_msg(
|
||||||
ctx,
|
ctx,
|
||||||
lang!("dc_msg_8-ball_answer", question, rand_item.unwrap()),
|
ctx.data().lang.get("dc.8_ball", &[question, rand_item.unwrap().to_string()]),
|
||||||
false,
|
false,
|
||||||
true
|
true
|
||||||
).await;
|
).await;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use poise::serenity_prelude::Timestamp;
|
use poise::serenity_prelude::Timestamp;
|
||||||
|
|
||||||
use crate::{lang, messages::{send_embed, send_msg, Author, EmbedOptions}, Context, Error};
|
use crate::{messages::{send_embed, send_msg, Author, EmbedOptions}, Context, Error};
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
#[poise::command(
|
#[poise::command(
|
||||||
@@ -46,7 +46,7 @@ pub async fn cmd(
|
|||||||
).await;
|
).await;
|
||||||
|
|
||||||
if !reply_unwrap {
|
if !reply_unwrap {
|
||||||
send_msg(ctx, lang!("dc_msg_mandatory_response"), true, true).await;
|
send_msg(ctx, ctx.data().lang.get("dc.mandatory_response", &[]), true, true).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
|
|
||||||
use crate::{lang, messages::send_msg, Context, Cmd, Error};
|
use crate::{messages::send_msg, Context, Cmd, Error};
|
||||||
|
|
||||||
|
|
||||||
#[derive(poise::ChoiceParameter, PartialEq)]
|
#[derive(poise::ChoiceParameter, PartialEq)]
|
||||||
@@ -82,7 +82,7 @@ async fn send_single_help(ctx: Context<'_>, mut cmd_name: String) {
|
|||||||
if cmd.is_none() {
|
if cmd.is_none() {
|
||||||
send_msg(
|
send_msg(
|
||||||
ctx,
|
ctx,
|
||||||
lang!("dc_msg_cmd_404", cmd_name),
|
ctx.data().lang.get("dc.cmd_404", &[cmd_name]),
|
||||||
true,
|
true,
|
||||||
true
|
true
|
||||||
).await;
|
).await;
|
||||||
@@ -108,7 +108,7 @@ async fn send_category_help(ctx: Context<'_>, category: HelpOptions) {
|
|||||||
|
|
||||||
async fn send_bk_week_help_re(ctx: Context<'_>) {
|
async fn send_bk_week_help_re(ctx: Context<'_>) {
|
||||||
let t: String = fs::read_to_string("./bk_week_help_re.md").await
|
let t: String = fs::read_to_string("./bk_week_help_re.md").await
|
||||||
.unwrap_or(lang!("dc_msg_re_help_removed"));
|
.unwrap_or(ctx.data().lang.get("dc.re.help_404", &[]));
|
||||||
|
|
||||||
send_msg(ctx, t, true, true).await;
|
send_msg(ctx, t, true, true).await;
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{lang, messages::send_msg, Context, Error};
|
use crate::{messages::send_msg, Context, Error};
|
||||||
|
|
||||||
|
|
||||||
#[poise::command(
|
#[poise::command(
|
||||||
@@ -16,6 +16,6 @@ pub async fn cmd(
|
|||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
{
|
{
|
||||||
send_msg(ctx, msg.replace("\\n", "\n"), false, false).await;
|
send_msg(ctx, msg.replace("\\n", "\n"), false, false).await;
|
||||||
send_msg(ctx, lang!("dc_msg_mandatory_response"), true, true).await;
|
send_msg(ctx, ctx.data().lang.get("dc.mandatory_response", &[]), true, true).await;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
use poise::serenity_prelude::{ChannelId, Mentionable};
|
||||||
|
use serde_json::Value;
|
||||||
|
use tokio::sync::MutexGuard;
|
||||||
|
|
||||||
|
use crate::{Context, Error, games::wwrps::{game::{RPS, RPSGame, RPSPlayer}, ranks::{RPSStats, Ranks}}, lang::Lang, messages::{http_send_msg, send_msg}};
|
||||||
|
|
||||||
|
|
||||||
|
#[poise::command(
|
||||||
|
slash_command,
|
||||||
|
prefix_command,
|
||||||
|
category = "fun",
|
||||||
|
rename = "wwrps",
|
||||||
|
required_bot_permissions = "SEND_MESSAGES | VIEW_CHANNEL"
|
||||||
|
)]
|
||||||
|
/// World-Wide Rock Paper Scissors. Play with a completely random person!
|
||||||
|
pub async fn cmd(
|
||||||
|
ctx: Context<'_>,
|
||||||
|
selection: RPS,
|
||||||
|
#[description = "If true, replaces your username with [Anonymous]."] anonymous: bool
|
||||||
|
) -> Result<(), Error>
|
||||||
|
{
|
||||||
|
let c_o = get_wwrps_channel(ctx).await;
|
||||||
|
|
||||||
|
if c_o.is_none() { send_msg(ctx, ctx.data().lang.get("dc.db.wwrps_404", &[]), true, true).await; return Ok(()); }
|
||||||
|
let c = c_o.unwrap();
|
||||||
|
|
||||||
|
send_msg(ctx, ctx.data().lang.get("dc.wwrps.submitting", &[]), true, true).await;
|
||||||
|
|
||||||
|
let mut game = ctx.data().rps_game.lock().await;
|
||||||
|
|
||||||
|
if game.is_full_lobby() { return Ok(()) }
|
||||||
|
|
||||||
|
let data_lock = &ctx.data().discord_data.lock().await["users"];
|
||||||
|
|
||||||
|
let r = game.add_player(RPSPlayer {
|
||||||
|
selection,
|
||||||
|
user: ctx.author().clone(),
|
||||||
|
wwrps_channel: ChannelId::new(c),
|
||||||
|
anonymous,
|
||||||
|
stats: get_player_stats(ctx.author().id.get(), data_lock)
|
||||||
|
}, ctx.data().args.dev);
|
||||||
|
|
||||||
|
if let Err(_) = r {
|
||||||
|
send_msg(ctx, ctx.data().lang.get("dc.wwrps.already_submitted", &[]), true, true).await;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let full = r.unwrap();
|
||||||
|
if !full { return Ok(()); }
|
||||||
|
|
||||||
|
let winner = game.get_winner();
|
||||||
|
|
||||||
|
let p1 = game.players[0].as_ref().unwrap();
|
||||||
|
let p2 = game.players[1].as_ref().unwrap();
|
||||||
|
|
||||||
|
let old_elos = [p1.stats.elo, p2.stats.elo];
|
||||||
|
let expected = RPSStats::get_elo_expected(old_elos[0], old_elos[1]);
|
||||||
|
|
||||||
|
if let Some(w) = winner {
|
||||||
|
// 0 means p1 wins, and 1 means p2 wins
|
||||||
|
// we flip it because ELO counts 0 as a loss
|
||||||
|
game.players[0].as_mut().unwrap().stats.update_elo(expected, (!w) as f32);
|
||||||
|
game.players[1].as_mut().unwrap().stats.update_elo(expected, w as f32);
|
||||||
|
}
|
||||||
|
|
||||||
|
let r_text = results_text(&game, &ctx.data().lang, winner, old_elos);
|
||||||
|
|
||||||
|
// clone and clear here to prevent race conditions while
|
||||||
|
// sending the results
|
||||||
|
let game_clone = game.clone();
|
||||||
|
game.clear();
|
||||||
|
|
||||||
|
let mut used_channels: Vec<ChannelId> = Vec::new();
|
||||||
|
|
||||||
|
// send the results
|
||||||
|
for player in &game_clone.players {
|
||||||
|
if let Some(p) = player {
|
||||||
|
if used_channels.contains(&p.wwrps_channel) { continue; }
|
||||||
|
|
||||||
|
used_channels.push(p.wwrps_channel);
|
||||||
|
http_send_msg(ctx.http(), p.wwrps_channel, r_text.clone()).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn results_text(
|
||||||
|
game: &MutexGuard<'_, RPSGame>,
|
||||||
|
lang: &Lang,
|
||||||
|
winner: Option<i8>,
|
||||||
|
old_elos: [u16; 2]
|
||||||
|
) -> String
|
||||||
|
{
|
||||||
|
let winner_text: String;
|
||||||
|
|
||||||
|
let p1 = game.players[0].as_ref().unwrap();
|
||||||
|
let p2 = game.players[1].as_ref().unwrap();
|
||||||
|
let p1_n = if !p1.anonymous { p1.user.mention().to_string() }
|
||||||
|
else { lang.get("dc.wwrps.anon", &[]) };
|
||||||
|
let p2_n = if !p2.anonymous { p2.user.mention().to_string() }
|
||||||
|
else { lang.get("dc.wwrps.anon", &[]) };
|
||||||
|
|
||||||
|
if let Some(w) = winner {
|
||||||
|
winner_text = if w == 0 { lang.get("dc.wwrps.p1_win", &[]) }
|
||||||
|
else { lang.get("dc.wwrps.p2_win", &[]) }; }
|
||||||
|
else { winner_text = lang.get("dc.wwrps.draw", &[]); }
|
||||||
|
|
||||||
|
return lang.get(
|
||||||
|
"dc.wwrps.match",
|
||||||
|
&[
|
||||||
|
p1.selection.to_string(), // {0}
|
||||||
|
p2.selection.to_string(), // {1}
|
||||||
|
|
||||||
|
winner_text, // {2}
|
||||||
|
|
||||||
|
p1_n, // {3}
|
||||||
|
old_elos[0].to_string(), // {4}
|
||||||
|
(p1.stats.elo - old_elos[0]).to_string(), // {5}
|
||||||
|
Ranks::from_elo(p1.stats.elo).to_string(), // {6}
|
||||||
|
|
||||||
|
p2_n, // {7}
|
||||||
|
old_elos[1].to_string(), // {8}
|
||||||
|
(p2.stats.elo - old_elos[1]).to_string(), // {9}
|
||||||
|
Ranks::from_elo(p2.stats.elo).to_string(), // {10}
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async fn get_wwrps_channel(ctx: Context<'_>) -> Option<u64> {
|
||||||
|
let d = &ctx.data().discord_data.lock().await;
|
||||||
|
|
||||||
|
let is_guild = ctx.guild_channel().await.is_some();
|
||||||
|
|
||||||
|
if !is_guild { return Some(ctx.channel_id().get()); }
|
||||||
|
|
||||||
|
let Some(servers) = d.get("servers") else { return None; };
|
||||||
|
let Some(s) = servers.get(ctx.guild_id().unwrap().get().to_string()) else { return None; };
|
||||||
|
let Some(c_id) = s.get("wwrps_channel") else { return None; };
|
||||||
|
|
||||||
|
return Some(c_id.as_u64().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn get_player_stats(uid: u64, db: &Value) -> RPSStats {
|
||||||
|
if let Some(user) = db.get(uid.to_string()) {
|
||||||
|
if let Some(elo) = user.get("wwrps_elo") {
|
||||||
|
if let Some(elo_i64) = elo.as_i64()
|
||||||
|
{ return RPSStats::from(uid, elo_i64 as u16); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return RPSStats::new(uid);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
pub mod add;
|
||||||
|
pub mod approve;
|
||||||
|
pub mod generic_fns;
|
||||||
|
pub mod get;
|
||||||
|
pub mod remove;
|
||||||
|
pub mod shorturl;
|
||||||
|
pub mod top;
|
||||||
|
pub mod update;
|
||||||
|
pub mod vote;
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
use crate::data::{get_mutex_data};
|
use crate::db::reddit::{self, POSTS_KEY};
|
||||||
use crate::messages::send_msg;
|
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::{data, websocket::send_cmd_json, Context, Error, CFG_DATA_RE};
|
use crate::{websocket::send_cmd_json, Context, Error};
|
||||||
use crate::re_cmds::generic_fns::{is_bk_mod_msg, send_embed_for_post, to_shorturl};
|
use crate::cmds::reddit::generic_fns::{is_bk_mod_msg, send_embed_for_post, to_shorturl};
|
||||||
use crate::lang;
|
|
||||||
|
|
||||||
#[poise::command(
|
#[poise::command(
|
||||||
slash_command,
|
slash_command,
|
||||||
@@ -25,6 +24,7 @@ pub async fn cmd(
|
|||||||
|
|
||||||
let shorturl_u = to_shorturl(&url);
|
let shorturl_u = to_shorturl(&url);
|
||||||
let shorturl = &shorturl_u.unwrap_or(url.clone());
|
let shorturl = &shorturl_u.unwrap_or(url.clone());
|
||||||
|
let short_s = shorturl.to_string();
|
||||||
|
|
||||||
let a = approve.unwrap_or(false);
|
let a = approve.unwrap_or(false);
|
||||||
let r = send_cmd_json("add_post_url", Some(json!([&shorturl, a, true])), true).await.unwrap();
|
let r = send_cmd_json("add_post_url", Some(json!([&shorturl, a, true])), true).await.unwrap();
|
||||||
@@ -41,18 +41,18 @@ pub async fn cmd(
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
data::update_re_data(ctx.data()).await;
|
reddit::update_data().await;
|
||||||
let reddit_data = get_mutex_data(&ctx.data().reddit_data).await?;
|
let reddit_data = &ctx.data().reddit_data.lock().await;
|
||||||
|
|
||||||
if let Some(bk_week) = reddit_data.get(CFG_DATA_RE) {
|
if let Some(bk_week) = reddit_data.get(POSTS_KEY) {
|
||||||
if let Some(post) = bk_week.get(shorturl) {
|
if let Some(post) = bk_week.get(shorturl) {
|
||||||
if post["removed"]["removed"].as_bool().unwrap()
|
if post["removed"]["removed"].as_bool().unwrap()
|
||||||
{ send_msg(ctx, lang!("dc_msg_re_post_unremove_success", &shorturl), true, true).await; }
|
{ send_msg(ctx, ctx.data().lang.get("dc.re.remove.unremove", &[short_s]), true, true).await; }
|
||||||
else { send_msg(ctx, lang!("dc_msg_re_post_update_success", &shorturl), true, true).await; }
|
else { send_msg(ctx, ctx.data().lang.get("dc.re.update_post", &[short_s]), true, true).await; }
|
||||||
}
|
}
|
||||||
else { send_msg(ctx, lang!("dc_msg_re_post_add_success", &shorturl), true, true).await; }
|
else { send_msg(ctx, ctx.data().lang.get("dc.re.add.success", &[short_s]), true, true).await; }
|
||||||
|
|
||||||
if a { send_msg(ctx, lang!("dc_msg_re_also_approved"), true, true).await; }
|
if a { send_msg(ctx, ctx.data().lang.get("dc.re.add.approved", &[]), true, true).await; }
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(post) = get_post_from_data(ctx, &reddit_data, shorturl).await? {
|
if let Some(post) = get_post_from_data(ctx, &reddit_data, shorturl).await? {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
use crate::{data::{self, get_mutex_data}, lang, messages::send_msg, re_cmds::generic_fns::{is_bk_mod_msg, to_shorturl}, websocket, Context, Error, CFG_DATA_RE};
|
use crate::{Context, Error, db::{reddit::POSTS_KEY}, messages::send_msg, cmds::reddit::generic_fns::{is_bk_mod_msg, to_shorturl}, websocket};
|
||||||
|
|
||||||
use super::generic_fns::send_embed_for_removed;
|
use super::generic_fns::send_embed_for_removed;
|
||||||
|
|
||||||
@@ -23,8 +23,7 @@ pub async fn cmd(
|
|||||||
let shorturl_u = to_shorturl(&url);
|
let shorturl_u = to_shorturl(&url);
|
||||||
let shorturl = &shorturl_u.unwrap_or(url.clone());
|
let shorturl = &shorturl_u.unwrap_or(url.clone());
|
||||||
|
|
||||||
data::update_re_data(ctx.data()).await;
|
let reddit_data = &ctx.data().reddit_data.lock().await;
|
||||||
let reddit_data = get_mutex_data(&ctx.data().reddit_data).await?;
|
|
||||||
|
|
||||||
approve_cmd(ctx, shorturl, &reddit_data, !disapprove.unwrap_or(false)).await;
|
approve_cmd(ctx, shorturl, &reddit_data, !disapprove.unwrap_or(false)).await;
|
||||||
|
|
||||||
@@ -33,7 +32,7 @@ pub async fn cmd(
|
|||||||
|
|
||||||
|
|
||||||
async fn approve_cmd(ctx: Context<'_>, url: &str, reddit_data: &Value, approve: bool) {
|
async fn approve_cmd(ctx: Context<'_>, url: &str, reddit_data: &Value, approve: bool) {
|
||||||
if let Some(post) = reddit_data.get(CFG_DATA_RE).unwrap().get(url) {
|
if let Some(post) = reddit_data.get(POSTS_KEY).unwrap().get(url) {
|
||||||
if post["removed"]["removed"].as_bool().unwrap() {
|
if post["removed"]["removed"].as_bool().unwrap() {
|
||||||
send_embed_for_removed(ctx, url, post).await;
|
send_embed_for_removed(ctx, url, post).await;
|
||||||
return;
|
return;
|
||||||
@@ -42,17 +41,14 @@ async fn approve_cmd(ctx: Context<'_>, url: &str, reddit_data: &Value, approve:
|
|||||||
let r = websocket::send_cmd_json("set_approve_post", Some(json!([approve, &url])), true).await.unwrap();
|
let r = websocket::send_cmd_json("set_approve_post", Some(json!([approve, &url])), true).await.unwrap();
|
||||||
if r["value"].as_bool().unwrap() {
|
if r["value"].as_bool().unwrap() {
|
||||||
if approve {
|
if approve {
|
||||||
send_msg(ctx, lang!("dc_msg_re_post_approve_success"), true, true).await;
|
send_msg(ctx, ctx.data().lang.get("dc.re.approve.success", &[]), true, true).await;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
send_msg(ctx, lang!("dc_msg_re_post_disapprove_success"), true, true).await;
|
send_msg(ctx, ctx.data().lang.get("dc.re.approve.disapprove", &[]), true, true).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
send_msg(ctx, lang!("dc_msg_err_trace", "`re_cmds -> approve.rs -> cmd() -> unwrap websocket result error`"), true, true).await;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
send_msg(ctx, lang!("dc_msg_re_post_404"), false, false).await;
|
send_msg(ctx, ctx.data().lang.get("dc.re.post_404", &[]), false, false).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@ use poise::serenity_prelude::{self as serenity, ChannelId, ComponentInteraction,
|
|||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::{data::get_toml_mutex, lang, messages::{decode_and_decompress_json, embed_from_options, make_post_embed, make_removed_embed, send_embed, send_msg, EmbedOptions, JSON_TEXT_END, JSON_TEXT_START}, Context, Data, Error};
|
use crate::{Context, Data, Error, 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 {
|
pub fn is_bk_mod(mod_list: Vec<u64>, uid: u64) -> bool {
|
||||||
return mod_list.contains(&uid);
|
return mod_list.contains(&uid);
|
||||||
@@ -10,19 +10,19 @@ pub fn is_bk_mod(mod_list: Vec<u64>, uid: u64) -> bool {
|
|||||||
|
|
||||||
|
|
||||||
pub async fn is_bk_mod_msg(ctx: Context<'_>) -> 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();
|
let sr = get_readable_subreddits(ctx.data()).await.unwrap();
|
||||||
send_msg(ctx, lang!("dc_msg_re_permdeny_not_re_mod", sr), true, true).await;
|
send_msg(ctx, ctx.data().lang.get("dc.re.not_mod", &[sr]), true, true).await;
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn is_bk_mod_serenity(ctx: &serenity::Context, data: &Data, author: &Member, component: &ComponentInteraction) -> 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();
|
let sr = get_readable_subreddits(data).await.unwrap();
|
||||||
serenity_send_msg(ctx, component, lang!("dc_msg_re_permdeny_not_re_mod", sr), true).await;
|
serenity_send_msg(ctx, component, data.lang.get("dc.re.not_mod", &[sr]), true).await;
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ pub fn to_shorturl(url: &str) -> Result<String, &str> {
|
|||||||
|
|
||||||
|
|
||||||
pub async fn send_embed_for_post(ctx: Context<'_>, post: Value, url: &str) -> Result<(), Error> {
|
pub async fn send_embed_for_post(ctx: Context<'_>, post: Value, url: &str) -> Result<(), Error> {
|
||||||
send_embed(ctx, make_post_embed(&post, url, true), true).await;
|
send_embed(ctx, make_post_embed(&ctx.data().lang, &post, url, true), true).await;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,15 +63,14 @@ pub async fn send_embed_for_post(ctx: Context<'_>, post: Value, url: &str) -> Re
|
|||||||
pub async fn send_embed_for_removed(ctx: Context<'_>, url: &str, post: &Value) {
|
pub async fn send_embed_for_removed(ctx: Context<'_>, url: &str, post: &Value) {
|
||||||
send_embed(
|
send_embed(
|
||||||
ctx,
|
ctx,
|
||||||
make_removed_embed(post, url, true),
|
make_removed_embed(&ctx.data().lang, post, url, true),
|
||||||
true
|
true
|
||||||
).await;
|
).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn get_readable_subreddits(data: &Data) -> Result<String, Error> {
|
pub async fn get_readable_subreddits(data: &Data) -> Result<String, Error> {
|
||||||
let d = get_toml_mutex(&data.cfg).await.unwrap();
|
let sr = data.cfg["reddit"]["subreddits"].as_array().unwrap();
|
||||||
let sr = d["reddit"]["subreddits"].as_array().unwrap();
|
|
||||||
let sr_str: Vec<&str> = sr
|
let sr_str: Vec<&str> = sr
|
||||||
.iter()
|
.iter()
|
||||||
.map(|v| v.as_str().unwrap())
|
.map(|v| v.as_str().unwrap())
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::{data::{self, get_mutex_data}, lang, messages::send_msg, re_cmds::generic_fns::{send_embed_for_post, to_shorturl}, rs_println, Context, Error, CFG_DATA_RE};
|
use crate::{Context, Error, db::{reddit::{self, POSTS_KEY}}, messages::send_msg, cmds::reddit::generic_fns::{send_embed_for_post, to_shorturl}, rs_println};
|
||||||
|
|
||||||
use super::generic_fns::send_embed_for_removed;
|
use super::generic_fns::send_embed_for_removed;
|
||||||
|
|
||||||
@@ -17,12 +17,12 @@ pub async fn cmd(
|
|||||||
#[description = "The post URL."] url: String
|
#[description = "The post URL."] url: String
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
{
|
{
|
||||||
data::update_re_data(ctx.data()).await;
|
reddit::update_data().await;
|
||||||
|
|
||||||
let shorturl_u = to_shorturl(&url);
|
let shorturl_u = to_shorturl(&url);
|
||||||
let shorturl = &shorturl_u.unwrap_or(url.clone());
|
let shorturl = &shorturl_u.unwrap_or(url.clone());
|
||||||
|
|
||||||
let reddit_data = get_mutex_data(&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? {
|
if let Some(post) = get_post_from_data(ctx, &reddit_data, shorturl).await? {
|
||||||
send_embed_for_post(ctx, post, shorturl).await?;
|
send_embed_for_post(ctx, post, shorturl).await?;
|
||||||
@@ -33,7 +33,7 @@ pub async fn cmd(
|
|||||||
|
|
||||||
|
|
||||||
pub async fn get_post_from_data(ctx: Context<'_>, reddit_data: &Value, url: &str) -> Result<Option<Value>, Error> {
|
pub async fn get_post_from_data(ctx: Context<'_>, reddit_data: &Value, url: &str) -> Result<Option<Value>, Error> {
|
||||||
if let Some(bk_week) = reddit_data.get(CFG_DATA_RE) {
|
if let Some(bk_week) = reddit_data.get(POSTS_KEY) {
|
||||||
if let Some(post) = bk_week.get(url) {
|
if let Some(post) = bk_week.get(url) {
|
||||||
if post["removed"]["removed"].as_bool().unwrap() {
|
if post["removed"]["removed"].as_bool().unwrap() {
|
||||||
send_embed_for_removed(ctx, url, post).await;
|
send_embed_for_removed(ctx, url, post).await;
|
||||||
@@ -42,11 +42,11 @@ pub async fn get_post_from_data(ctx: Context<'_>, reddit_data: &Value, url: &str
|
|||||||
return Ok(Some(post.clone()));
|
return Ok(Some(post.clone()));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
send_msg(ctx, lang!("dc_msg_re_post_404", url), true, true).await;
|
send_msg(ctx, ctx.data().lang.get("dc.re.post_404", &[url.to_string()]), true, true).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
send_msg(ctx, lang!("dc_msg_re_data_corrupted", url), true, true).await;
|
send_msg(ctx, ctx.data().lang.get("cd.re.post_corrupted", &[url.to_string()]), true, true).await;
|
||||||
rs_println!("{}", serde_json::to_string_pretty(reddit_data)?);
|
rs_println!("{}", serde_json::to_string_pretty(reddit_data)?);
|
||||||
}
|
}
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
use crate::{data::{self, get_mutex_data}, lang, messages::send_msg, re_cmds::{generic_fns::{is_bk_mod_msg, send_embed_for_removed, to_shorturl}, get::get_post_from_data}, websocket::send_cmd_json, Context, Error};
|
use crate::{Context, Error, db::reddit, 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(
|
#[poise::command(
|
||||||
slash_command,
|
slash_command,
|
||||||
@@ -27,17 +27,17 @@ pub async fn cmd(
|
|||||||
if r["value"].as_bool().unwrap() {
|
if r["value"].as_bool().unwrap() {
|
||||||
send_msg(
|
send_msg(
|
||||||
ctx,
|
ctx,
|
||||||
lang!("dc_msg_re_post_remove_success", &shorturl),
|
ctx.data().lang.get("dc.re.remove.success", &[shorturl.to_string()]),
|
||||||
true,
|
true,
|
||||||
true
|
true
|
||||||
).await;
|
).await;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
send_msg(ctx, lang!("dc_msg_re_post_404"), true, true).await;
|
send_msg(ctx, ctx.data().lang.get("dc.re.post_404", &[]), true, true).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
data::update_re_data(ctx.data()).await;
|
reddit::update_data().await;
|
||||||
let reddit_data = get_mutex_data(&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? {
|
if let Some(post) = get_post_from_data(ctx, &reddit_data, shorturl).await? {
|
||||||
if post["removed"]["removed"].as_bool().unwrap() {
|
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::{messages::send_msg, cmds::reddit::generic_fns::to_shorturl, Context, Error};
|
||||||
|
|
||||||
|
|
||||||
#[poise::command(
|
#[poise::command(
|
||||||
@@ -17,10 +17,10 @@ pub async fn cmd(
|
|||||||
let shorturl = to_shorturl(&url);
|
let shorturl = to_shorturl(&url);
|
||||||
|
|
||||||
if shorturl.is_ok() {
|
if shorturl.is_ok() {
|
||||||
send_msg(ctx, lang!("dc_msg_shorturl", shorturl.unwrap()), true, true).await;
|
send_msg(ctx, ctx.data().lang.get("dc.re.shorturl", &[shorturl.unwrap()]), true, true).await;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
send_msg(ctx, lang!("dc_msg_failed_shorturl_conversion"), true, true).await;
|
send_msg(ctx, ctx.data().lang.get("dc.re.shorturl_fail", &[]), true, true).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{data::get_mutex_data, re_cmds::generic_fns::send_embed_for_post, Context, Error, CFG_DATA_RE};
|
use crate::{Context, Error, db::reddit::POSTS_KEY, cmds::reddit::generic_fns::send_embed_for_post};
|
||||||
|
|
||||||
#[derive(poise::ChoiceParameter, PartialEq)]
|
#[derive(poise::ChoiceParameter, PartialEq)]
|
||||||
enum TopCategory {
|
enum TopCategory {
|
||||||
@@ -30,7 +30,7 @@ pub async fn cmd(
|
|||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
{
|
{
|
||||||
let mut all: HashMap<&str, i32> = HashMap::new();
|
let mut all: HashMap<&str, i32> = HashMap::new();
|
||||||
let posts = &get_mutex_data(&ctx.data().reddit_data).await?[CFG_DATA_RE];
|
let posts = &ctx.data().reddit_data.lock().await[POSTS_KEY];
|
||||||
let posts_u = posts.as_object().unwrap();
|
let posts_u = posts.as_object().unwrap();
|
||||||
|
|
||||||
for (url, dat) in posts_u {
|
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 poise::{serenity_prelude::{ChannelId, EditMessage, GetMessages, Http, Message, MessageId, UserId}, ReplyHandle};
|
||||||
use serde_json::{json, Map, Value};
|
use serde_json::{json, Map, Value};
|
||||||
|
|
||||||
use crate::{data::{self, get_mutex_data, get_toml_mutex, DC_POSTS_CHANNEL_KEY}, lang, messages::{edit_reply, embed_from_options, make_post_embed, make_removed_embed, send_embed, send_msg, trim_post_json}, re_cmds::generic_fns::embed_to_json, rs_println, websocket::send_cmd_json, Context, Error, CFG_DATA_RE};
|
use crate::{Context, Error, cmds::reddit::generic_fns::embed_to_json, db::{discord::contains_server, keys::DC_POSTS_CHANNEL_KEY, reddit::{self, POSTS_KEY}}, lang::Lang, messages::{edit_reply, embed_from_options, make_post_embed, make_removed_embed, send_embed, send_msg, trim_post_json}, rs_println, websocket::send_cmd_json};
|
||||||
|
|
||||||
#[poise::command(
|
#[poise::command(
|
||||||
slash_command,
|
slash_command,
|
||||||
@@ -34,71 +34,70 @@ pub async fn cmd(
|
|||||||
let mut p_text = "`/re_updatediscord`:".to_string();
|
let mut p_text = "`/re_updatediscord`:".to_string();
|
||||||
|
|
||||||
let progress = send_msg(ctx, p_text.clone(), true, true).await.unwrap();
|
let progress = send_msg(ctx, p_text.clone(), true, true).await.unwrap();
|
||||||
p_text = update_progress(ctx, progress.clone(), p_text, lang!("dc_msg_update_fetch", "\n")).await;
|
p_text = update_progress(ctx, progress.clone(), p_text, ctx.data().lang.get("dc.re.update.fetch", &[])).await;
|
||||||
|
|
||||||
let max_age_u = max_age.unwrap_or(8);
|
let max_age_u = max_age.unwrap_or(8);
|
||||||
let max_age_secs = max_age_u as u64 * (60 * 60 * 24);
|
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 = ctx.data().cfg["reddit"]["fetch_limit"].as_integer().unwrap();
|
||||||
let max_results_pre = max_results_toml["reddit"]["fetch_limit"].as_integer().unwrap();
|
|
||||||
let max_results_final = max_results.unwrap_or(max_results_pre as u16);
|
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;
|
send_cmd_json("add_new_posts", Some(json!([max_age_secs, max_results_final])), true).await;
|
||||||
data::update_re_data(ctx.data()).await;
|
reddit::update_data().await;
|
||||||
let r_data = get_mutex_data(&ctx.data().reddit_data).await?;
|
let r_data = &ctx.data().reddit_data.lock().await;
|
||||||
|
|
||||||
let c_id_u = get_c_id(ctx).await;
|
let c_id_u = get_c_id(ctx).await;
|
||||||
|
|
||||||
if c_id_u.is_none() {
|
if c_id_u.is_none() {
|
||||||
send_msg(ctx, lang!("dc_msg_re_posts_channel_404"), true, true).await;
|
send_msg(ctx, ctx.data().lang.get("dc.db.bk_404", &[]), true, true).await;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let c_id = c_id_u.unwrap();
|
let c_id = c_id_u.unwrap();
|
||||||
|
|
||||||
// Reading messages
|
// Reading messages
|
||||||
p_text = update_progress(ctx, progress.clone(), p_text.clone(), lang!("dc_msg_update_read", "✅\n", c_id)).await;
|
p_text = update_progress(ctx, progress.clone(), p_text.clone(), ctx.data().lang.get("dc.re.update.read", &[c_id.to_string()])).await;
|
||||||
let msgs = read_msgs(http, ctx.framework().bot_id, c_id).await;
|
let msgs = read_msgs(http, ctx.framework().bot_id, c_id).await;
|
||||||
|
|
||||||
// Parsing messages to JSON
|
// Parsing messages to JSON
|
||||||
p_text = update_progress(ctx, progress.clone(), p_text.clone(), lang!("dc_msg_update_parse", "✅\n")).await;
|
p_text = update_progress(ctx, progress.clone(), p_text.clone(), ctx.data().lang.get("dc.re.update.parse", &[])).await;
|
||||||
let msgs_json = msgs_to_json(msgs, &r_data, max_age_secs).await;
|
let msgs_json = msgs_to_json(msgs, &r_data, max_age_secs).await;
|
||||||
if ctx.data().args.dev { rs_println!("Posts changelog: {}", msgs_json); }
|
if ctx.data().args.dev { rs_println!("Posts changelog: {}", msgs_json); }
|
||||||
|
|
||||||
// Adding new posts
|
// Adding new posts
|
||||||
p_text = update_progress(ctx, progress.clone(), p_text.clone(), lang!("dc_msg_update_add", "✅\n")).await;
|
p_text = update_progress(ctx, progress.clone(), p_text.clone(), ctx.data().lang.get("dc.re.update.add", &[])).await;
|
||||||
let weekly_art = r_data[CFG_DATA_RE].as_object().unwrap();
|
let weekly_art = r_data[POSTS_KEY].as_object().unwrap();
|
||||||
add_posts(ctx, weekly_art, &msgs_json, max_age_secs, max_results_final).await;
|
add_posts(ctx, weekly_art, &msgs_json, max_age_secs, max_results_final).await;
|
||||||
|
|
||||||
// Stop if only_add
|
// Stop if only_add
|
||||||
if only_add.unwrap_or(false) {
|
if only_add.unwrap_or(false) {
|
||||||
send_msg(ctx, lang!("dc_msg_update_done", "`/bk_week_update`\n## "), true, true).await;
|
send_msg(ctx, ctx.data().lang.get("dc.re.update.done", &[]), true, true).await;
|
||||||
update_progress(ctx, progress.clone(), p_text, lang!("dc_msg_update_done", "✅\n## ")).await;
|
update_progress(ctx, progress.clone(), p_text, ctx.data().lang.get("dc.re.update.done", &[])).await;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removing duplicate posts
|
// Removing duplicate posts
|
||||||
p_text = update_progress(ctx, progress.clone(), p_text.clone(), lang!("dc_msg_update_removing_dupe", "✅\n")).await;
|
p_text = update_progress(ctx, progress.clone(), p_text.clone(), ctx.data().lang.get("dc.re.update.rm_dupe", &[])).await;
|
||||||
remove_dupes(http, c_id, &msgs_json).await;
|
remove_dupes(http, c_id, &msgs_json).await;
|
||||||
|
|
||||||
// Removing removed posts
|
// Removing removed posts
|
||||||
p_text = update_progress(ctx, progress.clone(), p_text.clone(), lang!("dc_msg_update_removing", "✅\n")).await;
|
p_text = update_progress(ctx, progress.clone(), p_text.clone(), ctx.data().lang.get("dc.re.update.rm", &[])).await;
|
||||||
remove_posts(http, c_id, weekly_art, &msgs_json).await;
|
remove_posts(http, &ctx.data().lang, c_id, weekly_art, &msgs_json).await;
|
||||||
|
|
||||||
// Removing old posts
|
// Removing old posts
|
||||||
if max_age_u > 0 {
|
if max_age_u > 0 {
|
||||||
p_text = update_progress(ctx, progress.clone(), p_text.clone(), lang!("dc_msg_update_removing_old", "✅\n", max_age_u)).await;
|
p_text = update_progress(ctx, progress.clone(), p_text.clone(), ctx.data().lang.get("dc.re.update.rm_old", &[max_age_u.to_string()])).await;
|
||||||
remove_old(http, c_id, &msgs_json).await;
|
remove_old(http, c_id, &msgs_json).await;
|
||||||
send_cmd_json("remove_old_posts", Some(json!([max_age_secs])), true).await;
|
send_cmd_json("remove_old_posts", Some(json!([max_age_secs])), true).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Editing updated posts
|
// Editing updated posts
|
||||||
p_text = update_progress(ctx, progress.clone(), p_text.clone(), lang!("dc_msg_update_editing", "✅\n")).await;
|
p_text = update_progress(ctx, progress.clone(), p_text.clone(), ctx.data().lang.get("dc.re.update.editing", &[])).await;
|
||||||
edit_posts(http, c_id, weekly_art, &msgs_json).await;
|
edit_posts(http, &ctx.data().lang, c_id, weekly_art, &msgs_json).await;
|
||||||
|
|
||||||
// Done
|
// Done
|
||||||
update_progress(ctx, progress.clone(), p_text, lang!("dc_msg_update_done", "✅\n## ")).await;
|
update_progress(ctx, progress.clone(), p_text, ctx.data().lang.get("dc.re.update.done", &[])).await;
|
||||||
send_msg(ctx, lang!("dc_msg_update_done", "`/bk_week_update`\n## "), true, true).await;
|
send_msg(ctx, ctx.data().lang.get("dc.re.update.parse", &[]), true, true).await;
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -113,12 +112,12 @@ async fn update_progress(ctx: Context<'_>, p: ReplyHandle<'_>, t: String, added_
|
|||||||
|
|
||||||
|
|
||||||
async fn get_c_id(ctx: Context<'_>) -> Option<ChannelId> {
|
async fn get_c_id(ctx: Context<'_>) -> Option<ChannelId> {
|
||||||
if !data::dc_contains_server(ctx.data(), ctx.guild_id().unwrap().into()).await {
|
if !contains_server(ctx.data(), ctx.guild_id().unwrap().into()).await {
|
||||||
send_msg(ctx, lang!("dc_msg_data_server_404"), true, true).await;
|
send_msg(ctx, ctx.data().lang.get("dc.db.server_404", &[]), true, true).await;
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let d = get_mutex_data(&ctx.data().discord_data).await.unwrap();
|
let d = &ctx.data().discord_data.lock().await;
|
||||||
let c_id_u =
|
let c_id_u =
|
||||||
d["servers"]
|
d["servers"]
|
||||||
[ctx.guild_id().unwrap().to_string()]
|
[ctx.guild_id().unwrap().to_string()]
|
||||||
@@ -188,7 +187,7 @@ async fn msgs_to_json(msgs: Vec<Message>, reddit_data: &Value, max_age: u64) ->
|
|||||||
if msg_json.is_err() { continue; }
|
if msg_json.is_err() { continue; }
|
||||||
|
|
||||||
let u_json: Value = msg_json.unwrap();
|
let u_json: Value = msg_json.unwrap();
|
||||||
let re_url = &reddit_data[CFG_DATA_RE][&url];
|
let re_url = &reddit_data[POSTS_KEY][&url];
|
||||||
|
|
||||||
let json_trimmed = trim_post_json(re_url);
|
let json_trimmed = trim_post_json(re_url);
|
||||||
|
|
||||||
@@ -257,31 +256,31 @@ async fn add_posts(ctx: Context<'_>, r_data: &Map<String, Value>, msgs_json: &Va
|
|||||||
if now - post_date > max_age && max_age > 0 { continue; }
|
if now - post_date > max_age && max_age > 0 { continue; }
|
||||||
|
|
||||||
if r_data[url]["removed"]["removed"].as_bool().unwrap() {
|
if r_data[url]["removed"]["removed"].as_bool().unwrap() {
|
||||||
send_embed(ctx, make_removed_embed(&r_data[url], url, false), false).await;
|
send_embed(ctx, make_removed_embed(&ctx.data().lang, &r_data[url], url, false), false).await;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
send_embed(ctx, make_post_embed(&r_data[url], url, false), false).await;
|
send_embed(ctx, make_post_embed(&ctx.data().lang, &r_data[url], url, false), false).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async fn edit_posts(http: &Http, c_id: ChannelId, r_data: &Map<String, Value>, msgs_json: &Value) {
|
async fn edit_posts(http: &Http, lang: &Lang, c_id: ChannelId, r_data: &Map<String, Value>, msgs_json: &Value) {
|
||||||
for (url, msg_id) in msgs_json["updated"].as_object().unwrap() {
|
for (url, msg_id) in msgs_json["updated"].as_object().unwrap() {
|
||||||
let mut msg = http.get_message(c_id, MessageId::new(msg_id.as_u64().unwrap())).await.unwrap();
|
let mut msg = http.get_message(c_id, MessageId::new(msg_id.as_u64().unwrap())).await.unwrap();
|
||||||
let r = EditMessage::new()
|
let r = EditMessage::new()
|
||||||
.embeds(vec![embed_from_options(make_post_embed(&r_data[url], url, false))]);
|
.embeds(vec![embed_from_options(make_post_embed(lang, &r_data[url], url, false))]);
|
||||||
|
|
||||||
let _ = msg.edit(http, r).await;
|
let _ = msg.edit(http, r).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async fn remove_posts(http: &Http, c_id: ChannelId, r_data: &Map<String, Value>, msgs_json: &Value) {
|
async fn remove_posts(http: &Http, lang: &Lang, c_id: ChannelId, r_data: &Map<String, Value>, msgs_json: &Value) {
|
||||||
for (url, msg_id) in msgs_json["removed"].as_object().unwrap() {
|
for (url, msg_id) in msgs_json["removed"].as_object().unwrap() {
|
||||||
let mut msg = http.get_message(c_id, MessageId::new(msg_id.as_u64().unwrap())).await.unwrap();
|
let mut msg = http.get_message(c_id, MessageId::new(msg_id.as_u64().unwrap())).await.unwrap();
|
||||||
let r = EditMessage::new()
|
let r = EditMessage::new()
|
||||||
.embeds(vec![embed_from_options(make_removed_embed(&r_data[url], url, false))]);
|
.embeds(vec![embed_from_options(make_removed_embed(lang, &r_data[url], url, false))]);
|
||||||
|
|
||||||
let _ = msg.edit(http, r).await;
|
let _ = msg.edit(http, r).await;
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
use crate::{data::{self, get_mutex_data}, lang, messages::send_msg, re_cmds::generic_fns::{is_bk_mod, send_embed_for_removed, to_shorturl}, websocket::send_cmd_json, Context, Error, CFG_DATA_RE};
|
use crate::{Context, Error, db::reddit::{self, POSTS_KEY}, messages::send_msg, cmds::reddit::generic_fns::{is_bk_mod, send_embed_for_removed, to_shorturl}, websocket::send_cmd_json};
|
||||||
|
|
||||||
#[poise::command(
|
#[poise::command(
|
||||||
slash_command,
|
slash_command,
|
||||||
@@ -16,17 +16,17 @@ pub async fn cmd(
|
|||||||
#[description = "Wether to undo your vote or not"] un_vote: Option<bool>
|
#[description = "Wether to undo your vote or not"] un_vote: Option<bool>
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
{
|
{
|
||||||
data::update_re_data(ctx.data()).await;
|
reddit::update_data().await;
|
||||||
let uid = ctx.author().id.get();
|
let uid = ctx.author().id.get();
|
||||||
let re_data = get_mutex_data(&ctx.data().reddit_data).await?;
|
let re_data = &ctx.data().reddit_data.lock().await;
|
||||||
let post_data = re_data[CFG_DATA_RE].clone();
|
let post_data = re_data[POSTS_KEY].clone();
|
||||||
let unw_vote = un_vote.unwrap_or(false);
|
let unw_vote = un_vote.unwrap_or(false);
|
||||||
|
|
||||||
let shorturl_u = to_shorturl(&url);
|
let shorturl_u = to_shorturl(&url);
|
||||||
let shorturl = &shorturl_u.unwrap_or(url.clone());
|
let shorturl = &shorturl_u.unwrap_or(url.clone());
|
||||||
|
|
||||||
if post_data.get(shorturl).is_none() {
|
if post_data.get(shorturl).is_none() {
|
||||||
send_msg(ctx, lang!("dc_msg_re_post_404"), false, false).await;
|
send_msg(ctx, ctx.data().lang.get("dc.re.post_404", &[]), false, false).await;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
if post_data[&shorturl]["removed"]["removed"].as_bool().unwrap() {
|
if post_data[&shorturl]["removed"]["removed"].as_bool().unwrap() {
|
||||||
@@ -36,35 +36,31 @@ pub async fn cmd(
|
|||||||
|
|
||||||
let url_data = &post_data[&shorturl];
|
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 voters_dc = url_data["votes"]["voters_dc"].as_array().unwrap();
|
||||||
let mod_voters = url_data["votes"]["mod_voters"].as_array().unwrap();
|
let mod_voters = url_data["votes"]["mod_voters"].as_array().unwrap();
|
||||||
let voters = if is_mod { mod_voters } else { voters_dc };
|
let voters = if is_mod { mod_voters } else { voters_dc };
|
||||||
|
|
||||||
if voters.contains(&json!(uid)) && !unw_vote {
|
if voters.contains(&json!(uid)) && !unw_vote {
|
||||||
send_msg(ctx, lang!("dc_msg_re_already_voted"), true, true).await;
|
send_msg(ctx, ctx.data().lang.get("dc.re.vote.already", &[]), true, true).await;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
else if !voters.contains(&json!(uid)) && unw_vote {
|
else if !voters.contains(&json!(uid)) && unw_vote {
|
||||||
send_msg(ctx, lang!("dc_msg_re_vote_remove_havent"), true, true).await;
|
send_msg(ctx, ctx.data().lang.get("dc.re.vote.remove_hasnt_voted", &[]), true, true).await;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let r = send_cmd_json("set_vote_post", Some(json!([shorturl, uid, is_mod, true, unw_vote])), true).await.unwrap();
|
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();
|
let unw_r = r["value"].as_bool().unwrap();
|
||||||
|
|
||||||
if unw_r && !unw_vote && is_mod {
|
if unw_r && !unw_vote && is_mod
|
||||||
send_msg(ctx, lang!("dc_msg_re_vote_mod_success"), true, true).await;
|
{ send_msg(ctx, ctx.data().lang.get("dc.re.vote.mod", &[]), true, true).await; }
|
||||||
}
|
else if unw_r && !unw_vote && !is_mod
|
||||||
else if unw_r && !unw_vote && !is_mod {
|
{ send_msg(ctx, ctx.data().lang.get("dc.re.vote.success", &[]), true, true).await; }
|
||||||
send_msg(ctx, lang!("dc_msg_re_vote_success"), true, true).await;
|
else if unw_r && unw_vote
|
||||||
}
|
{ send_msg(ctx, ctx.data().lang.get("dc.re.vote.remove", &[]), true, true).await; }
|
||||||
else if unw_r && unw_vote {
|
else
|
||||||
send_msg(ctx, lang!("dc_msg_re_vote_remove_success"), true, true).await;
|
{ send_msg(ctx, ctx.data().lang.get("dc.re.vote.error", &[]), true, true).await; }
|
||||||
}
|
|
||||||
else {
|
|
||||||
send_msg(ctx, lang!("dc_msg_re_vote_err"), true, true).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
use poise::serenity_prelude::{ChannelId, Mentionable, User};
|
|
||||||
use tokio::sync::MutexGuard;
|
|
||||||
|
|
||||||
use crate::{Context, Error, data::get_mutex_data, lang, messages::{http_send_msg, send_msg}};
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(poise::ChoiceParameter, PartialEq, Clone, Debug)]
|
|
||||||
#[repr(u8)]
|
|
||||||
pub enum RPS {
|
|
||||||
Paper = 0,
|
|
||||||
Rock = 1,
|
|
||||||
Scissors = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct RPSPlayer {
|
|
||||||
pub selection: RPS,
|
|
||||||
pub user: User,
|
|
||||||
pub wwrps_channel: ChannelId,
|
|
||||||
pub anonymous: bool
|
|
||||||
}
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct RPSGame {
|
|
||||||
pub players: [Option<RPSPlayer>; 2],
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl Display for RPS {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
return match self {
|
|
||||||
Self::Paper => write!(f, "Paper"),
|
|
||||||
Self::Rock => write!(f, "Rock"),
|
|
||||||
Self::Scissors => write!(f, "Scissors")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl RPSGame {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
return RPSGame {
|
|
||||||
players: [None, None],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns wether the lobby is filled or not
|
|
||||||
pub fn add_player(&mut self, player: RPSPlayer) -> Result<bool, Error> {
|
|
||||||
if let Some(p1) = &self.players[0]
|
|
||||||
{ if p1.user == player.user { return Err(Error::from("Cannot add player, it already exists!")); }}
|
|
||||||
|
|
||||||
if self.players[0].is_none() { self.players[0] = Some(player); return Ok(false); }
|
|
||||||
else if self.players[1].is_none() { self.players[1] = Some(player); return Ok(true); }
|
|
||||||
else { return Err(Error::from("Cannot add player, list is full!")); }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear(&mut self)
|
|
||||||
{ self.players = [None, None]; }
|
|
||||||
|
|
||||||
|
|
||||||
pub fn get_winner(&self) -> Option<i8> {
|
|
||||||
if self.players.iter().any(|i| i.is_none())
|
|
||||||
{ return None; }
|
|
||||||
|
|
||||||
let Some(p1) = self.players[0].clone() else { return None; };
|
|
||||||
let Some(p2) = self.players[1].clone() else { return None; };
|
|
||||||
|
|
||||||
let i1 = p1.selection as u8;
|
|
||||||
let i2 = p2.selection as u8;
|
|
||||||
|
|
||||||
if i1 == i2 { return None; }
|
|
||||||
else if (i1 + 1) % 3 == i2 { return Some(0); }
|
|
||||||
else { return Some(1); }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_full_lobby(&self) -> bool
|
|
||||||
{ return self.players.iter().all(|i| i.is_some()); }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[poise::command(
|
|
||||||
slash_command,
|
|
||||||
prefix_command,
|
|
||||||
category = "fun",
|
|
||||||
rename = "wwrps",
|
|
||||||
required_bot_permissions = "SEND_MESSAGES | VIEW_CHANNEL"
|
|
||||||
)]
|
|
||||||
/// World-Wide Rock Paper Scissors. Play with a completely random person!
|
|
||||||
pub async fn cmd(
|
|
||||||
ctx: Context<'_>,
|
|
||||||
selection: RPS,
|
|
||||||
#[description = "If true, replaces your username with [Anonymous]."] anonymous: bool
|
|
||||||
) -> Result<(), Error>
|
|
||||||
{
|
|
||||||
let c_o = get_wwrps_channel(ctx).await;
|
|
||||||
|
|
||||||
if c_o.is_none() { send_msg(ctx, lang!("dc_msg_wwrps_not_in_data"), true, true).await; return Ok(()); }
|
|
||||||
let c = c_o.unwrap();
|
|
||||||
|
|
||||||
send_msg(ctx, lang!("dc_msg_wwrps_submitting"), true, true).await;
|
|
||||||
|
|
||||||
let mut game = ctx.data().rps_game.lock().await;
|
|
||||||
|
|
||||||
if !game.is_full_lobby() {
|
|
||||||
let r = game.add_player(
|
|
||||||
RPSPlayer { selection, user: ctx.author().clone(), wwrps_channel: ChannelId::new(c), anonymous });
|
|
||||||
|
|
||||||
if let Err(_) = r
|
|
||||||
{ send_msg(ctx, lang!("dc_msg_wwrps_already_submitted"), true, true).await; return Ok(()); }
|
|
||||||
|
|
||||||
let full = r.unwrap();
|
|
||||||
if !full { return Ok(()); }
|
|
||||||
|
|
||||||
let r_text = results_text(&game);
|
|
||||||
let game_clone = game.clone();
|
|
||||||
game.clear();
|
|
||||||
|
|
||||||
let mut used_channels: Vec<ChannelId> = Vec::new();
|
|
||||||
|
|
||||||
for player in &game_clone.players {
|
|
||||||
if let Some(p) = player {
|
|
||||||
if used_channels.contains(&p.wwrps_channel) { continue; }
|
|
||||||
used_channels.push(p.wwrps_channel);
|
|
||||||
http_send_msg(ctx.http(), p.wwrps_channel, r_text.clone()).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn results_text(game: &MutexGuard<'_, RPSGame>) -> String {
|
|
||||||
let winner = game.get_winner();
|
|
||||||
let winner_text: String;
|
|
||||||
|
|
||||||
let p1 = game.players[0].as_ref().unwrap();
|
|
||||||
let p2 = game.players[1].as_ref().unwrap();
|
|
||||||
let p1_n = if !p1.anonymous { p1.user.mention().to_string() } else { lang!("dc_msg_wwrps_anon") };
|
|
||||||
let p2_n = if !p2.anonymous { p2.user.mention().to_string() } else { lang!("dc_msg_wwrps_anon") };
|
|
||||||
|
|
||||||
if let Some(w) = winner {
|
|
||||||
winner_text = if w == 0 { lang!("dc_msg_wwrps_left_win") }
|
|
||||||
else { lang!("dc_msg_wwrps_right_win") }; }
|
|
||||||
else { winner_text = lang!("dc_msg_wwrps_draw"); }
|
|
||||||
|
|
||||||
return lang!("dc_msg_wwrps_fight", p1.selection.clone(), p2.selection.clone(), winner_text, p1_n, p2_n);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async fn get_wwrps_channel(ctx: Context<'_>) -> Option<u64> {
|
|
||||||
let d = get_mutex_data(&ctx.data().discord_data).await.unwrap();
|
|
||||||
|
|
||||||
let is_guild = ctx.guild_channel().await.is_some();
|
|
||||||
|
|
||||||
if !is_guild { return Some(ctx.channel_id().get()); }
|
|
||||||
|
|
||||||
let Some(servers) = d.get("servers") else { return None; };
|
|
||||||
let Some(s) = servers.get(ctx.guild_id().unwrap().get().to_string()) else { return None; };
|
|
||||||
let Some(c_id) = s.get("wwrps_channel") else { return None; };
|
|
||||||
|
|
||||||
return Some(c_id.as_u64().unwrap());
|
|
||||||
}
|
|
||||||
-252
@@ -1,252 +0,0 @@
|
|||||||
use std::{fs, io::Write};
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use serde_json::{self, Value, json};
|
|
||||||
use tokio::sync::Mutex;
|
|
||||||
|
|
||||||
use crate::{errln, rs_println, rs_warnln, Data, Error, CFG_DATA_RE, LANG, LANG_NAME};
|
|
||||||
use crate::websocket::send_cmd_json;
|
|
||||||
|
|
||||||
|
|
||||||
static DATA_PATH_DC: &str = "./data/db/dc_data.json";
|
|
||||||
static PRESET_PATH_DC: &str = "./data/defaults/dc_data_preset.json";
|
|
||||||
|
|
||||||
static DATA_PATH_RE: &str = "./data/db/re_data.json";
|
|
||||||
static PRESET_PATH_RE: &str = "./data/defaults/re_data_preset.json";
|
|
||||||
|
|
||||||
static DATA_PATH_CFG: &str = "./cfg/cfg.toml";
|
|
||||||
static PRESET_PATH_CFG: &str = "./data/defaults/cfg_default.toml";
|
|
||||||
|
|
||||||
static DATA_PATH_LANG: &str = "./data/lang/";
|
|
||||||
|
|
||||||
pub static DC_POSTS_CHANNEL_KEY: &str = "re_posts_channel";
|
|
||||||
pub static DC_WWRPS_CHANNEL_KEY: &str = "wwrps_channel";
|
|
||||||
|
|
||||||
|
|
||||||
pub async fn read_dc_data(data: &Data, wipe: bool) {
|
|
||||||
if !Path::new(DATA_PATH_DC).exists() || wipe {
|
|
||||||
rs_println!(
|
|
||||||
"{} creating new from preset...",
|
|
||||||
if !wipe { "discord_data.json not found," } else { "[WIPE] (discord_data.json)" }
|
|
||||||
);
|
|
||||||
generate_dc_data();
|
|
||||||
}
|
|
||||||
|
|
||||||
let str_data = fs::read_to_string(DATA_PATH_DC).unwrap();
|
|
||||||
let json_data = serde_json::from_str(&str_data).unwrap();
|
|
||||||
let mut dc_data = data.discord_data.lock().await;
|
|
||||||
*dc_data = json_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn generate_dc_data() {
|
|
||||||
let preset_str = fs::read_to_string(PRESET_PATH_DC).unwrap();
|
|
||||||
let mut preset_json: Value = serde_json::from_str(&preset_str).unwrap();
|
|
||||||
|
|
||||||
if let Some(servers) = preset_json["servers"].as_object_mut() {
|
|
||||||
servers.remove("SERVER ID");
|
|
||||||
}
|
|
||||||
|
|
||||||
let json_str = serde_json::to_string_pretty(&preset_json).unwrap();
|
|
||||||
|
|
||||||
let mut file = fs::File::create(DATA_PATH_DC).unwrap();
|
|
||||||
file.write_all(json_str.as_bytes()).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub async fn write_dc_data(data: &Data) {
|
|
||||||
if !Path::new(DATA_PATH_DC).exists() {
|
|
||||||
generate_dc_data();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut file = fs::OpenOptions::new()
|
|
||||||
.write(true)
|
|
||||||
.truncate(true)
|
|
||||||
.open(DATA_PATH_DC)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
|
|
||||||
let mut dc_data_lock = data.discord_data.lock().await;
|
|
||||||
let dc_data = dc_data_lock.as_mut().unwrap();
|
|
||||||
let json_str = serde_json::to_string_pretty(dc_data).unwrap();
|
|
||||||
|
|
||||||
file.write_all(json_str.as_bytes()).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub async fn read_re_data(data: &Data, wipe: bool) {
|
|
||||||
if !Path::new(DATA_PATH_RE).exists() || wipe {
|
|
||||||
rs_println!(
|
|
||||||
"{} creating new from preset...",
|
|
||||||
if !wipe { "reddit_data.json not found," } else { "[WIPE] (reddit_data.json)" }
|
|
||||||
);
|
|
||||||
generate_re_data();
|
|
||||||
}
|
|
||||||
|
|
||||||
let str_data = fs::read_to_string(DATA_PATH_RE).unwrap();
|
|
||||||
let json_data: Option<Value> = serde_json::from_str(&str_data).unwrap();
|
|
||||||
let mut re_data = data.reddit_data.lock().await;
|
|
||||||
*re_data = json_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn generate_re_data() {
|
|
||||||
let preset_str = fs::read_to_string(PRESET_PATH_RE).unwrap();
|
|
||||||
let mut preset_json: Value = serde_json::from_str(&preset_str).unwrap();
|
|
||||||
|
|
||||||
if let Some(bk_week) = preset_json[CFG_DATA_RE].as_object_mut() {
|
|
||||||
bk_week.remove("EXAMPLE URL");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
rs_warnln!("Couldn't find key \"{}\" in the Reddit data file ({})!", CFG_DATA_RE, DATA_PATH_RE);
|
|
||||||
}
|
|
||||||
|
|
||||||
let json_str = serde_json::to_string_pretty(&preset_json).unwrap();
|
|
||||||
|
|
||||||
let mut file = fs::File::create(DATA_PATH_RE).unwrap();
|
|
||||||
file.write_all(json_str.as_bytes()).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub async fn update_re_data(data: &Data) {
|
|
||||||
send_cmd_json("update_data_file", None, true).await;
|
|
||||||
read_re_data(data, false).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub async fn write_re_data() {
|
|
||||||
send_cmd_json("update_data_file", None, true).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub async fn read_cfg_data(data: &Data, wipe: bool) -> Option<Value> {
|
|
||||||
if !Path::new(DATA_PATH_CFG).exists() || wipe {
|
|
||||||
rs_println!(
|
|
||||||
"{} creating new from preset...",
|
|
||||||
if !wipe { "cfg.toml not found," } else { "[WIPE] (cfg.toml)" }
|
|
||||||
);
|
|
||||||
generate_cfg_data();
|
|
||||||
}
|
|
||||||
|
|
||||||
let str_data = fs::read_to_string(DATA_PATH_CFG).unwrap();
|
|
||||||
let json_data: toml::Value = str_data.parse().unwrap();
|
|
||||||
let mut cfg_data = data.cfg.lock().await;
|
|
||||||
*cfg_data = Some(json_data.clone());
|
|
||||||
|
|
||||||
let r = send_cmd_json("update_cfg", Some(json!([toml::to_string(&json_data).unwrap()])), true).await;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn generate_cfg_data() {
|
|
||||||
let preset_str = fs::read_to_string(PRESET_PATH_CFG).unwrap();
|
|
||||||
|
|
||||||
let mut file = fs::File::create(DATA_PATH_CFG).unwrap();
|
|
||||||
file.write_all(preset_str.as_bytes()).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub async fn dc_add_server(data: &Data, server_id: u64) -> Result<(), ()> {
|
|
||||||
let mut dc_data_lock = data.discord_data.lock().await;
|
|
||||||
let dc_data = dc_data_lock.as_mut().unwrap();
|
|
||||||
|
|
||||||
if dc_data.get("servers").is_none() { return Err(()); }
|
|
||||||
|
|
||||||
let servers = dc_data["servers"].as_object_mut().unwrap();
|
|
||||||
|
|
||||||
if !servers.contains_key(&server_id.to_string()) {
|
|
||||||
servers.insert(server_id.to_string(), json!({}));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub async fn dc_bind_bk(data: &Data, server_id: u64, channel_id: u64) -> Result<(), ()> {
|
|
||||||
let mut dc_data_lock = data.discord_data.lock().await;
|
|
||||||
let dc_data = dc_data_lock.as_mut().unwrap();
|
|
||||||
|
|
||||||
if dc_data.get("servers").is_none() { return Err(()); }
|
|
||||||
|
|
||||||
let servers = dc_data["servers"].as_object_mut().unwrap();
|
|
||||||
|
|
||||||
if !servers.contains_key(&server_id.to_string()) {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let server = servers[&server_id.to_string()].as_object_mut().unwrap();
|
|
||||||
|
|
||||||
server.insert(DC_POSTS_CHANNEL_KEY.to_string(), channel_id.into());
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub async fn bind_wwrps(data: &Data, server_id: u64, channel_id: u64) -> Result<(), ()> {
|
|
||||||
let mut dc_data_lock = data.discord_data.lock().await;
|
|
||||||
let dc_data = dc_data_lock.as_mut().unwrap();
|
|
||||||
|
|
||||||
if dc_data.get("servers").is_none() { return Err(()); }
|
|
||||||
|
|
||||||
let servers = dc_data["servers"].as_object_mut().unwrap();
|
|
||||||
|
|
||||||
if !servers.contains_key(&server_id.to_string())
|
|
||||||
{ return Err(()); }
|
|
||||||
|
|
||||||
let server = servers[&server_id.to_string()].as_object_mut().unwrap();
|
|
||||||
|
|
||||||
server.insert(DC_WWRPS_CHANNEL_KEY.to_string(), channel_id.into());
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub async fn dc_contains_server(data: &Data, server_id: u64) -> bool {
|
|
||||||
let dc_data_lock = data.discord_data.lock().await;
|
|
||||||
let dc_data = dc_data_lock.as_ref().unwrap();
|
|
||||||
|
|
||||||
if dc_data.get("servers").is_none() { return false; }
|
|
||||||
|
|
||||||
let mut clone = dc_data.clone();
|
|
||||||
let servers = clone["servers"].as_object_mut().unwrap();
|
|
||||||
|
|
||||||
return servers.contains_key(&server_id.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub async fn get_mutex_data(data: &Mutex<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()),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub fn load_lang_data(lang: String) {
|
|
||||||
let full_path = format!("{}{}.json", DATA_PATH_LANG, lang);
|
|
||||||
|
|
||||||
if !Path::new(&full_path).exists() {
|
|
||||||
errln!(
|
|
||||||
"File for language \"{0}\" ({0}.json) not found!\n Hint: You can download official language files at https://github.com/ByteDice/ByteDiceAssistant in the data/langs/... folder",
|
|
||||||
lang
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let str_data = fs::read_to_string(full_path).unwrap();
|
|
||||||
let json_data: Option<Value> = serde_json::from_str(&str_data).unwrap();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
LANG = json_data;
|
|
||||||
LANG_NAME = Some(lang);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
pub mod bot_data;
|
||||||
|
pub mod cfg;
|
||||||
|
pub mod discord;
|
||||||
|
pub mod env_vars;
|
||||||
|
pub mod keys;
|
||||||
|
pub mod reddit;
|
||||||
|
pub mod terminal_args;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
use serde_json::Value;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::{db::{env_vars::AssistantEnv, terminal_args::Args}, games::wwrps::game::RPSGame, lang::Lang};
|
||||||
|
|
||||||
|
|
||||||
|
pub struct Data {
|
||||||
|
pub args: Args,
|
||||||
|
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>,
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
use std::{fs, io::Write, path::Path};
|
||||||
|
|
||||||
|
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(wipe: bool) -> toml::Value {
|
||||||
|
if !Path::new(DATA_PATH).exists() || wipe {
|
||||||
|
rs_println!(
|
||||||
|
"{} creating new from preset...",
|
||||||
|
if !wipe { "cfg.toml not found," } else { "[WIPE] (cfg.toml)" }
|
||||||
|
);
|
||||||
|
generate_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
let str_data = fs::read_to_string(DATA_PATH).unwrap();
|
||||||
|
let json_data: toml::Value = str_data.parse().unwrap();
|
||||||
|
|
||||||
|
/*let r = send_cmd_json(
|
||||||
|
"update_cfg",
|
||||||
|
Some(json!([toml::to_string(&json_data).unwrap()])),
|
||||||
|
true
|
||||||
|
).await;*/
|
||||||
|
|
||||||
|
return json_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn generate_data() {
|
||||||
|
let preset_str = fs::read_to_string(PRESET_PATH).unwrap();
|
||||||
|
|
||||||
|
let mut file = fs::File::create(DATA_PATH).unwrap();
|
||||||
|
file.write_all(preset_str.as_bytes()).unwrap();
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
use std::{fs, io::Write, path::Path};
|
||||||
|
|
||||||
|
use serde_json::{Value, json};
|
||||||
|
|
||||||
|
use crate::{Data, db::keys, rs_println};
|
||||||
|
|
||||||
|
static DATA_PATH: &str = "./data/db/dc_data.json";
|
||||||
|
static PRESET_PATH: &str = "./data/defaults/dc_data_preset.json";
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn read_data(wipe: bool) -> Value {
|
||||||
|
if !Path::new(DATA_PATH).exists() || wipe {
|
||||||
|
rs_println!(
|
||||||
|
"{} creating new from preset...",
|
||||||
|
if !wipe { "discord_data.json not found," } else { "[WIPE] (discord_data.json)" }
|
||||||
|
);
|
||||||
|
generate_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
let str_data = fs::read_to_string(DATA_PATH).unwrap();
|
||||||
|
let json_data = serde_json::from_str(&str_data).unwrap();
|
||||||
|
return json_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn generate_data() {
|
||||||
|
let preset_str = fs::read_to_string(PRESET_PATH).unwrap();
|
||||||
|
let mut preset_json: Value = serde_json::from_str(&preset_str).unwrap();
|
||||||
|
|
||||||
|
// remove the examples
|
||||||
|
if let Some(servers) = preset_json["servers"].as_object_mut() {
|
||||||
|
servers.remove("SERVER_ID");
|
||||||
|
}
|
||||||
|
if let Some(users) = preset_json["users"].as_object_mut() {
|
||||||
|
users.remove("USER_ID");
|
||||||
|
}
|
||||||
|
|
||||||
|
let json_str = serde_json::to_string_pretty(&preset_json).unwrap();
|
||||||
|
|
||||||
|
let mut file = fs::File::create(DATA_PATH).unwrap();
|
||||||
|
file.write_all(json_str.as_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn write_data(data: &Data) {
|
||||||
|
if !Path::new(DATA_PATH).exists() {
|
||||||
|
generate_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut file = fs::OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.truncate(true)
|
||||||
|
.open(DATA_PATH)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
|
||||||
|
let 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 = data.discord_data.lock().await;
|
||||||
|
|
||||||
|
if dc_data.get("servers").is_none() { return Err(()); }
|
||||||
|
|
||||||
|
let servers = dc_data["servers"].as_object_mut().unwrap();
|
||||||
|
|
||||||
|
if !servers.contains_key(&server_id.to_string()) {
|
||||||
|
servers.insert(server_id.to_string(), json!({}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn bind_bk(data: &Data, server_id: u64, channel_id: u64) -> Result<(), ()> {
|
||||||
|
let mut dc_data = data.discord_data.lock().await;
|
||||||
|
|
||||||
|
if dc_data.get("servers").is_none() { return Err(()); }
|
||||||
|
|
||||||
|
let servers = dc_data["servers"].as_object_mut().unwrap();
|
||||||
|
|
||||||
|
if !servers.contains_key(&server_id.to_string()) {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let server = servers[&server_id.to_string()].as_object_mut().unwrap();
|
||||||
|
|
||||||
|
server.insert(keys::DC_POSTS_CHANNEL_KEY.to_string(), channel_id.into());
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn bind_wwrps(data: &Data, server_id: u64, channel_id: u64) -> Result<(), ()> {
|
||||||
|
let mut dc_data = data.discord_data.lock().await;
|
||||||
|
|
||||||
|
if dc_data.get("servers").is_none() { return Err(()); }
|
||||||
|
|
||||||
|
let servers = dc_data["servers"].as_object_mut().unwrap();
|
||||||
|
|
||||||
|
if !servers.contains_key(&server_id.to_string())
|
||||||
|
{ return Err(()); }
|
||||||
|
|
||||||
|
let server = servers[&server_id.to_string()].as_object_mut().unwrap();
|
||||||
|
|
||||||
|
server.insert(keys::DC_WWRPS_CHANNEL_KEY.to_string(), channel_id.into());
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn contains_server(data: &Data, server_id: u64) -> bool {
|
||||||
|
let dc_data = data.discord_data.lock().await;
|
||||||
|
|
||||||
|
if dc_data.get("servers").is_none() { return false; }
|
||||||
|
|
||||||
|
let mut clone = dc_data.clone();
|
||||||
|
let servers = clone["servers"].as_object_mut().unwrap();
|
||||||
|
|
||||||
|
return servers.contains_key(&server_id.to_string())
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
pub static DC_POSTS_CHANNEL_KEY: &str = "re_posts_channel";
|
||||||
|
pub static DC_WWRPS_CHANNEL_KEY: &str = "wwrps_channel";
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
use std::{fs, io::Write, path::Path};
|
||||||
|
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use crate::{rs_println, rs_warnln, websocket::send_cmd_json};
|
||||||
|
|
||||||
|
|
||||||
|
static DATA_PATH: &str = "./data/db/re_data.json";
|
||||||
|
static PRESET_PATH: &str = "./data/defaults/re_data_preset.json";
|
||||||
|
|
||||||
|
pub static POSTS_KEY: &str = "posts";
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn read_data(wipe: bool) -> Value {
|
||||||
|
if !Path::new(DATA_PATH).exists() || wipe {
|
||||||
|
rs_println!(
|
||||||
|
"{} creating new from preset...",
|
||||||
|
if !wipe { "reddit_data.json not found," } else { "[WIPE] (reddit_data.json)" }
|
||||||
|
);
|
||||||
|
generate_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
let str_data = fs::read_to_string(DATA_PATH).unwrap();
|
||||||
|
let json_data: Value = serde_json::from_str(&str_data).unwrap();
|
||||||
|
return json_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn generate_data() {
|
||||||
|
let preset_str = fs::read_to_string(PRESET_PATH).unwrap();
|
||||||
|
let mut preset_json: Value = serde_json::from_str(&preset_str).unwrap();
|
||||||
|
|
||||||
|
if let Some(bk_week) = preset_json[POSTS_KEY].as_object_mut() {
|
||||||
|
bk_week.remove("EXAMPLE URL");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rs_warnln!("Couldn't find key \"{}\" in the Reddit data file ({})!", POSTS_KEY, DATA_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
let json_str = serde_json::to_string_pretty(&preset_json).unwrap();
|
||||||
|
|
||||||
|
let mut file = fs::File::create(DATA_PATH).unwrap();
|
||||||
|
file.write_all(json_str.as_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn update_data() {
|
||||||
|
send_cmd_json("update_data_file", None, true).await;
|
||||||
|
read_data(false).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn write_data() {
|
||||||
|
send_cmd_json("update_data_file", None, true).await;
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
use clap::Parser;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Parser, Serialize, Clone)]
|
||||||
|
pub struct Args {
|
||||||
|
#[arg(short = 'p', long, default_value = "2920", help = "Sets the port number, e.g 2200.")]
|
||||||
|
pub port: u16,
|
||||||
|
|
||||||
|
#[arg(long, help = "Runs only the Python part of the program.")]
|
||||||
|
pub py: bool,
|
||||||
|
|
||||||
|
#[arg(long, help = "Runs only the Rust part of the program.")]
|
||||||
|
pub rs: bool,
|
||||||
|
|
||||||
|
#[arg(short = 'd', long, help = "Enables dev mode. Dev mode shows more debug info and turns off certain security measures.")]
|
||||||
|
pub dev: bool,
|
||||||
|
|
||||||
|
#[arg(short = 'w', long, help = "Wipes all data before running the program.")]
|
||||||
|
pub wipe: bool,
|
||||||
|
|
||||||
|
#[arg(short = 't', long, help = "Makes the program use the ASSISTANT_TOKEN_TEST env var instead of ASSISTANT_TOKEN. This env var should hold the token of a non-production bot.")]
|
||||||
|
pub test: bool,
|
||||||
|
|
||||||
|
#[arg(long, help = "Adds annoying prints when the websockets send a ping. Why though?")]
|
||||||
|
pub ping: bool,
|
||||||
|
|
||||||
|
#[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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
use crate::{data::dc_add_server, lang, messages::send_msg, Context, Error};
|
|
||||||
|
|
||||||
|
|
||||||
pub async fn cmd(
|
|
||||||
ctx: Context<'_>
|
|
||||||
) -> Result<(), Error>
|
|
||||||
{
|
|
||||||
let r = dc_add_server(ctx.data(), ctx.guild_id().unwrap().into()).await;
|
|
||||||
|
|
||||||
if r.is_ok() {
|
|
||||||
send_msg(ctx, lang!("dc_msg_add_to_data"), true, true).await;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
send_msg(ctx, lang!("dc_msg_corrupted_data"), true, true).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
use crate::{data::dc_bind_bk, lang, messages::send_msg, Context, Error};
|
|
||||||
|
|
||||||
pub async fn cmd(
|
|
||||||
ctx: Context<'_>
|
|
||||||
) -> Result<(), Error>
|
|
||||||
{
|
|
||||||
let c_id = ctx.channel_id().into();
|
|
||||||
let r = dc_bind_bk(ctx.data(), ctx.guild_id().unwrap().into(), c_id).await;
|
|
||||||
|
|
||||||
if r.is_ok() {
|
|
||||||
send_msg(ctx, lang!("dc_msg_bound_channel", c_id), true, true).await;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
send_msg(ctx, lang!("dc_msg_data_server_404"), true, true).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
use crate::{Context, Error, data::bind_wwrps, lang, messages::send_msg};
|
|
||||||
|
|
||||||
pub async fn cmd(
|
|
||||||
ctx: Context<'_>
|
|
||||||
) -> Result<(), Error>
|
|
||||||
{
|
|
||||||
let c_id = ctx.channel_id().into();
|
|
||||||
let r = bind_wwrps(ctx.data(), ctx.guild_id().unwrap().into(), c_id).await;
|
|
||||||
|
|
||||||
if r.is_ok() {
|
|
||||||
send_msg(ctx, lang!("dc_msg_bound_channel", c_id), true, true).await;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
send_msg(ctx, lang!("dc_msg_data_server_404"), true, true).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
use crate::{Context, Error, data, lang, messages::{edit_reply, send_msg}};
|
|
||||||
|
|
||||||
pub async fn cmd(ctx: Context<'_>) -> Result<(), Error> {
|
|
||||||
let msg = send_msg(ctx, lang!("dc_msg_owner_data_save"), true, true).await.unwrap();
|
|
||||||
|
|
||||||
data::write_dc_data(ctx.data()).await;
|
|
||||||
data::write_re_data().await;
|
|
||||||
|
|
||||||
edit_reply(ctx, msg, lang!("dc_msg_owner_data_save_complete")).await;
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
use crate::{lang, messages::send_msg, Context, Error};
|
|
||||||
|
|
||||||
pub async fn cmd(ctx: Context<'_>) -> Result<(), Error> {
|
|
||||||
let data = ctx.data();
|
|
||||||
let uid: u64 = ctx.author().id.into();
|
|
||||||
|
|
||||||
let is_owner = data.owners .contains(&uid);
|
|
||||||
let is_bk_mod = data.bk_mods.contains(&uid);
|
|
||||||
|
|
||||||
send_msg(ctx, lang!("dc_msg_whoami", is_owner, is_bk_mod), true, true).await;
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
+43
-45
@@ -1,9 +1,11 @@
|
|||||||
use crate::data::{get_mutex_data, get_toml_mutex, update_re_data};
|
use crate::db::reddit::update_data;
|
||||||
use crate::r#gen::set_status;
|
use crate::r#gen::set_status;
|
||||||
|
use crate::lang::Lang;
|
||||||
use crate::messages::{make_post_embed, make_removed_embed, EmbedOptions};
|
use crate::messages::{make_post_embed, make_removed_embed, EmbedOptions};
|
||||||
use crate::re_cmds::generic_fns::{is_bk_mod, is_bk_mod_serenity, serenity_edit_msg_embed, serenity_send_msg};
|
use crate::cmds::reddit::generic_fns::{is_bk_mod, is_bk_mod_serenity, serenity_edit_msg_embed, serenity_send_msg};
|
||||||
use crate::websocket::send_cmd_json;
|
use crate::websocket::send_cmd_json;
|
||||||
use crate::{CFG_DATA_RE, Data, Error, lang, rs_println};
|
use crate::{Data, Error, rs_println};
|
||||||
|
use crate::db::reddit::POSTS_KEY;
|
||||||
|
|
||||||
use poise::serenity_prelude::{self as serenity, ChannelId, ComponentInteraction, Interaction, Member, MessageId, Ready};
|
use poise::serenity_prelude::{self as serenity, ChannelId, ComponentInteraction, Interaction, Member, MessageId, Ready};
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
@@ -11,6 +13,10 @@ use serde_json::{json, Value};
|
|||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: clean up
|
||||||
|
|
||||||
|
|
||||||
pub fn event_handler<'a>(
|
pub fn event_handler<'a>(
|
||||||
ctx: &'a serenity::Context,
|
ctx: &'a serenity::Context,
|
||||||
event: &'a serenity::FullEvent,
|
event: &'a serenity::FullEvent,
|
||||||
@@ -35,9 +41,7 @@ async fn on_ready(ctx: &serenity::Context, data_about_bot: &Ready, data: &Data)
|
|||||||
data_about_bot.user.id
|
data_about_bot.user.id
|
||||||
);
|
);
|
||||||
|
|
||||||
let m_data = get_toml_mutex(&data.cfg).await.unwrap();
|
set_status(data.cfg.clone(), ctx).await;
|
||||||
|
|
||||||
set_status(m_data, ctx).await;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -64,11 +68,11 @@ async fn handle_buttons(ctx: &serenity::Context, data: &Data, interaction: &Inte
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async fn update_embed(ctx: &serenity::Context, url: &str, new_data: &Value, c_id: &ChannelId, m_id: &MessageId) {
|
async fn update_embed(ctx: &serenity::Context, lang: &Lang, url: &str, new_data: &Value, c_id: &ChannelId, m_id: &MessageId) {
|
||||||
let remove = new_data["removed"]["removed"].as_bool().unwrap();
|
let remove = new_data["removed"]["removed"].as_bool().unwrap();
|
||||||
let e: EmbedOptions =
|
let e: EmbedOptions =
|
||||||
if remove { make_removed_embed(new_data, url, true) }
|
if remove { make_removed_embed(lang, new_data, url, true) }
|
||||||
else { make_post_embed (new_data, url, true) };
|
else { make_post_embed (lang, new_data, url, true) };
|
||||||
|
|
||||||
serenity_edit_msg_embed(ctx, c_id, m_id, e).await;
|
serenity_edit_msg_embed(ctx, c_id, m_id, e).await;
|
||||||
}
|
}
|
||||||
@@ -82,21 +86,18 @@ async fn approve_btn(ctx: &serenity::Context, data: &Data, c_member: &Member, co
|
|||||||
let c_id = component.channel_id;
|
let c_id = component.channel_id;
|
||||||
let m_id = component.message.id;
|
let m_id = component.message.id;
|
||||||
|
|
||||||
update_re_data(data).await;
|
update_data().await;
|
||||||
let new_data = &get_mutex_data(&data.reddit_data).await.unwrap()[CFG_DATA_RE][&url];
|
let new_data = &data.reddit_data
|
||||||
update_embed(ctx, &url, new_data, &c_id, &m_id).await;
|
.lock().await[POSTS_KEY][&url];
|
||||||
|
|
||||||
|
update_embed(ctx, &data.lang, &url, new_data, &c_id, &m_id).await;
|
||||||
|
|
||||||
if r["value"].as_bool().unwrap() {
|
if r["value"].as_bool().unwrap() {
|
||||||
if approve {
|
if approve
|
||||||
serenity_send_msg(ctx, component, lang!("dc_msg_re_post_approve_success"), true).await;
|
{ serenity_send_msg(ctx, component, data.lang.get("dc.re.approve.disapprove", &[]), true).await; }
|
||||||
}
|
else { serenity_send_msg(ctx, component, data.lang.get("dc.re.approve.success", &[]), true).await; }
|
||||||
else {
|
|
||||||
serenity_send_msg(ctx, component, lang!("dc_msg_re_post_disapprove_success"), true).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
serenity_send_msg(ctx, component, lang!("dc_msg_re_post_approve_remove"), true).await;
|
|
||||||
}
|
}
|
||||||
|
else { serenity_send_msg(ctx, component, data.lang.get("dc.re.approve.post_removed", &[]), true).await; }
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -105,27 +106,22 @@ async fn approve_btn(ctx: &serenity::Context, data: &Data, c_member: &Member, co
|
|||||||
async fn remove_btn(ctx: &serenity::Context, data: &Data, c_member: &Member, component: &ComponentInteraction, url: String, remove: bool) -> Result<(), Error> {
|
async fn remove_btn(ctx: &serenity::Context, data: &Data, c_member: &Member, component: &ComponentInteraction, url: String, remove: bool) -> Result<(), Error> {
|
||||||
if !is_bk_mod_serenity(ctx, data, c_member, component).await { return Ok(()); }
|
if !is_bk_mod_serenity(ctx, data, c_member, component).await { return Ok(()); }
|
||||||
|
|
||||||
let r: Value = if remove {
|
let r: Value =
|
||||||
send_cmd_json("remove_post_url", Some(json!([&url, &c_member.user.name, None::<String>])), true).await.unwrap()
|
if remove { send_cmd_json("remove_post_url", Some(json!([&url, &c_member.user.name, None::<String>])), true).await.unwrap() }
|
||||||
}
|
else { send_cmd_json("add_post_url", Some(json!([&url, false, true])), true).await.unwrap() };
|
||||||
else {
|
|
||||||
send_cmd_json("add_post_url", Some(json!([&url, false, true])), true).await.unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
let c_id = component.channel_id;
|
let c_id = component.channel_id;
|
||||||
let m_id = component.message.id;
|
let m_id = component.message.id;
|
||||||
|
|
||||||
update_re_data(data).await;
|
update_data().await;
|
||||||
let new_data = &get_mutex_data(&data.reddit_data).await.unwrap()[CFG_DATA_RE][&url];
|
let new_data = &data.reddit_data
|
||||||
update_embed(ctx, &url, new_data, &c_id, &m_id).await;
|
.lock().await[POSTS_KEY][&url];
|
||||||
|
|
||||||
|
update_embed(ctx, &data.lang, &url, new_data, &c_id, &m_id).await;
|
||||||
|
|
||||||
if r["value"].as_bool().unwrap() {
|
if r["value"].as_bool().unwrap() {
|
||||||
if remove {
|
if remove { serenity_send_msg(ctx, component, data.lang.get("dc.re.remove.success", &[url]), true).await; }
|
||||||
serenity_send_msg(ctx, component, lang!("dc_msg_re_post_remove_success", &url), true).await;
|
else { serenity_send_msg(ctx, component, data.lang.get("dc.re.remove.unremove", &[url]), true).await; }
|
||||||
}
|
|
||||||
else {
|
|
||||||
serenity_send_msg(ctx, component, lang!("dc_msg_re_post_unremove_success", &url), true).await;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@@ -134,31 +130,33 @@ 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> {
|
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 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 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 c_id = component.channel_id;
|
||||||
let m_id = component.message.id;
|
let m_id = component.message.id;
|
||||||
|
|
||||||
update_re_data(data).await;
|
update_data().await;
|
||||||
let new_data = &get_mutex_data(&data.reddit_data).await.unwrap()[CFG_DATA_RE][&url];
|
let new_data = &data.reddit_data
|
||||||
update_embed(ctx, &url, new_data, &c_id, &m_id).await;
|
.lock().await[POSTS_KEY][&url];
|
||||||
|
|
||||||
|
update_embed(ctx, &data.lang, &url, new_data, &c_id, &m_id).await;
|
||||||
|
|
||||||
if r["value"].as_bool().unwrap() {
|
if r["value"].as_bool().unwrap() {
|
||||||
if vote {
|
if vote {
|
||||||
if is_mod { serenity_send_msg(ctx, component, lang!("dc_msg_re_vote_mod_success"), true).await; }
|
if is_mod { serenity_send_msg(ctx, component, data.lang.get("dc.re.vote.mod", &[]), true).await; }
|
||||||
else { serenity_send_msg(ctx, component, lang!("dc_msg_re_vote_success"), true).await; }
|
else { serenity_send_msg(ctx, component, data.lang.get("dc.re.vote.success", &[]), true).await; }
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
serenity_send_msg(ctx, component, lang!("dc_msg_re_vote_remove_success"), true).await;
|
serenity_send_msg(ctx, component, data.lang.get("dc.re.vote.removed", &[]), true).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if new_data["removed"]["removed"].as_bool().unwrap() {
|
else if new_data["removed"]["removed"].as_bool().unwrap() {
|
||||||
serenity_send_msg(ctx, component, lang!("dc_msg_re_post_vote_removed_post"), true).await;
|
serenity_send_msg(ctx, component, data.lang.get("dc.re.vote.post_removed", &[]), true).await;
|
||||||
}
|
}
|
||||||
else if !vote {
|
else if !vote {
|
||||||
serenity_send_msg(ctx, component, lang!("dc_msg_re_vote_remove_havent"), true).await;
|
serenity_send_msg(ctx, component, data.lang.get("dc.re.vote.remove_hasnt_voted", &[]), true).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
pub mod wwrps;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod game;
|
||||||
|
pub mod ranks;
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use poise::serenity_prelude::{ChannelId, User};
|
||||||
|
|
||||||
|
use crate::{Error, games::wwrps::ranks::RPSStats};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(poise::ChoiceParameter, PartialEq, Clone, Debug)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum RPS {
|
||||||
|
Paper = 0,
|
||||||
|
Rock = 1,
|
||||||
|
Scissors = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct RPSPlayer {
|
||||||
|
pub selection: RPS,
|
||||||
|
pub user: User,
|
||||||
|
pub wwrps_channel: ChannelId,
|
||||||
|
pub anonymous: bool,
|
||||||
|
pub stats: RPSStats
|
||||||
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct RPSGame {
|
||||||
|
pub players: [Option<RPSPlayer>; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Display for RPS {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
return match self {
|
||||||
|
Self::Paper => write!(f, "Paper"),
|
||||||
|
Self::Rock => write!(f, "Rock"),
|
||||||
|
Self::Scissors => write!(f, "Scissors")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl RPSGame {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
return RPSGame {
|
||||||
|
players: [None, None],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns wether the lobby is filled or not
|
||||||
|
pub fn add_player(&mut self, player: RPSPlayer, allow_dupes: bool) -> Result<bool, Error> {
|
||||||
|
if let Some(p1) = &self.players[0] {
|
||||||
|
if p1.user == player.user && !allow_dupes
|
||||||
|
{ return Err(Error::from("Cannot add player, it already exists!")); }
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.players[0].is_none() { self.players[0] = Some(player); return Ok(false); }
|
||||||
|
else if self.players[1].is_none() { self.players[1] = Some(player); return Ok(true); }
|
||||||
|
else { return Err(Error::from("Cannot add player, list is full!")); }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self)
|
||||||
|
{ self.players = [None, None]; }
|
||||||
|
|
||||||
|
|
||||||
|
pub fn get_winner(&self) -> Option<i8> {
|
||||||
|
if self.players.iter().any(|i| i.is_none())
|
||||||
|
{ return None; }
|
||||||
|
|
||||||
|
let Some(p1) = self.players[0].clone() else { return None; };
|
||||||
|
let Some(p2) = self.players[1].clone() else { return None; };
|
||||||
|
|
||||||
|
let i1 = p1.selection as u8;
|
||||||
|
let i2 = p2.selection as u8;
|
||||||
|
|
||||||
|
if i1 == i2 { return None; }
|
||||||
|
else if (i1 + 1) % 3 == i2 { return Some(0); } // calculate if p1 wins
|
||||||
|
else { return Some(1); } // otherwise p2 wins
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_full_lobby(&self) -> bool
|
||||||
|
{ return self.players.iter().all(|i| i.is_some()); }
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
pub enum Ranks {
|
||||||
|
PlasticScissors,
|
||||||
|
PrinterPaper,
|
||||||
|
Pebble,
|
||||||
|
|
||||||
|
ArkOfTheElements,
|
||||||
|
Origami,
|
||||||
|
Obsidian,
|
||||||
|
|
||||||
|
Dwayne,
|
||||||
|
LiterallyBrokenTheSystem
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct RPSStats {
|
||||||
|
pub uid: u64,
|
||||||
|
pub elo: u16
|
||||||
|
// Win/loss history?
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static SENSITIVITY: u16 = 32;
|
||||||
|
pub static START_ELO: u16 = 500;
|
||||||
|
|
||||||
|
|
||||||
|
impl RPSStats {
|
||||||
|
pub fn new(uid: u64) -> RPSStats
|
||||||
|
{ return RPSStats { uid: uid, elo: START_ELO } }
|
||||||
|
|
||||||
|
pub fn from(uid: u64, elo: u16) -> RPSStats
|
||||||
|
{ return RPSStats { uid: uid, elo: elo } }
|
||||||
|
|
||||||
|
pub fn update_elo(&mut self, expected: f32, score: f32)
|
||||||
|
{ self.elo += SENSITIVITY * (score - expected) as u16; }
|
||||||
|
|
||||||
|
pub fn get_elo_expected(elo_a: u16, elo_b: u16) -> f32
|
||||||
|
{ return 1.0 / (1.0 + f32::powf(10.0, (elo_b - elo_a) as f32 / 400.0)) }
|
||||||
|
|
||||||
|
// TODO: figure out a way to add RAM-efficient win/loss history before doing this
|
||||||
|
/*pub fn to_b64(&self) -> String {
|
||||||
|
return String::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_b64(b64: String, uid: u64) -> RPSStats {
|
||||||
|
return Self::new();
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Ranks {
|
||||||
|
pub fn from_elo(elo: u16) -> Ranks {
|
||||||
|
// formula: start + x * inc + step * (x * (x - 1) / 2)
|
||||||
|
// start = 100
|
||||||
|
// inc = 40
|
||||||
|
// step = 40
|
||||||
|
|
||||||
|
return match elo {
|
||||||
|
n if n > 1500 => Ranks::Dwayne,
|
||||||
|
|
||||||
|
n if n > 1180 => Ranks::Obsidian,
|
||||||
|
n if n > 900 => Ranks::Origami,
|
||||||
|
n if n > 660 => Ranks::ArkOfTheElements,
|
||||||
|
|
||||||
|
n if n > 460 => Ranks::Pebble,
|
||||||
|
n if n > 300 => Ranks::PrinterPaper,
|
||||||
|
n if n <= 180 => Ranks::PlasticScissors,
|
||||||
|
|
||||||
|
_ => Ranks::LiterallyBrokenTheSystem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl ToString for Ranks {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
return match self {
|
||||||
|
Ranks::PlasticScissors => String::from("Plastic Scissors"),
|
||||||
|
Ranks::PrinterPaper => String::from("Printer Paper"),
|
||||||
|
Ranks::Pebble => String::from("Pebble"),
|
||||||
|
|
||||||
|
Ranks::ArkOfTheElements => String::from("Ark of the Elements"),
|
||||||
|
Ranks::Origami => String::from("Origami"),
|
||||||
|
Ranks::Obsidian => String::from("Obsidian"),
|
||||||
|
|
||||||
|
Ranks::Dwayne => String::from("Dwayne"),
|
||||||
|
|
||||||
|
Ranks::LiterallyBrokenTheSystem => String::from("YOU HAVE BROKEN THE GAME SOMEHOW"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+53
-54
@@ -1,3 +1,4 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
use std::{collections::HashSet, process};
|
use std::{collections::HashSet, process};
|
||||||
|
|
||||||
use poise::serenity_prelude::{ActivityData, UserId};
|
use poise::serenity_prelude::{ActivityData, UserId};
|
||||||
@@ -6,47 +7,46 @@ use poise::serenity_prelude::Client;
|
|||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use toml::Value;
|
use toml::Value;
|
||||||
|
|
||||||
use crate::cmds::wwrps::RPSGame;
|
use crate::db::env_vars::AssistantEnv;
|
||||||
use crate::data::get_toml_mutex;
|
use crate::db::{cfg, discord, reddit};
|
||||||
use crate::{Args, Cmd, Data, cmds, data, db_cmds, debug_cmds, events, re_cmds, rs_println};
|
use crate::games::wwrps::game::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_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_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_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 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 re_data = reddit:: read_data(args.clone().wipe).await;
|
||||||
let mods_vec_str: Vec<String> = mods_env.split(",").map(String::from).collect();
|
let dc_data = discord::read_data(args.clone().wipe).await;
|
||||||
let mods_vec_u64: Vec<u64> = mods_vec_str
|
let cf_data = cfg:: read_data(args.clone().wipe).await;
|
||||||
.iter()
|
|
||||||
.map(|s| s.parse::<u64>().expect("Failed to parse ASSISTANT_BK_MODS. Invalid syntax."))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let data = Data {
|
let cf_bind = cf_data.clone();
|
||||||
owners,
|
let lang_name = cf_bind["general"]["lang"].as_str().unwrap();
|
||||||
|
let lang_path = ["./data/lang/", lang_name, ".json"].concat();
|
||||||
|
let lang_pathbuf = PathBuf::from(lang_path);
|
||||||
|
|
||||||
|
return Data {
|
||||||
|
args: args.clone(),
|
||||||
ball_prompts: [ball_classic, ball_quirk],
|
ball_prompts: [ball_classic, ball_quirk],
|
||||||
rps_game: Mutex::new(RPSGame::new()),
|
cfg: cf_data,
|
||||||
bk_mods: mods_vec_u64,
|
discord_data: dc_data.into(),
|
||||||
reddit_data: None.into(),
|
env_vars: env_vars,
|
||||||
discord_data: None.into(),
|
lang_name: lang_name.to_string(),
|
||||||
cfg: None.into(),
|
lang: Lang::from_file(lang_pathbuf).unwrap(),
|
||||||
args: args.clone()
|
reddit_data: re_data.into(),
|
||||||
|
rps_game: Mutex::new(RPSGame::new())
|
||||||
};
|
};
|
||||||
|
|
||||||
data::read_dc_data (&data, args.clone().wipe).await;
|
|
||||||
data::read_re_data (&data, args.clone().wipe).await;
|
|
||||||
data::read_cfg_data(&data, args.clone().wipe).await;
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn gen_bot(data: Data, args: Args) -> Client {
|
pub async fn gen_bot(data: Data) -> Client {
|
||||||
let token =
|
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!") };
|
else { std::env::var("ASSISTANT_TOKEN_TEST").expect("Missing ASSISTANT_TOKEN_TEST env var!") };
|
||||||
|
|
||||||
let intents = serenity::GatewayIntents::all();
|
let intents = serenity::GatewayIntents::all();
|
||||||
@@ -56,15 +56,15 @@ pub async fn gen_bot(data: Data, args: Args) -> Client {
|
|||||||
let token_end_len = token[peek_len..].len();
|
let token_end_len = token[peek_len..].len();
|
||||||
rs_println!("Token: {}{}", token_peek, "*".repeat(token_end_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()
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|i| if i == 0 { None } else { Some(UserId::from(i))})
|
.filter_map(|i| if i == 0 { None } else { Some(UserId::from(i))})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let framework = poise::Framework::builder()
|
let framework = poise::Framework::builder()
|
||||||
.options(poise::FrameworkOptions {
|
.options(poise::FrameworkOptions {
|
||||||
owners: own,
|
owners: owner_users,
|
||||||
commands: make_cmd_vec(&data).await,
|
commands: make_cmd_vec(&data).await,
|
||||||
event_handler: events::event_handler,
|
event_handler: events::event_handler,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -85,30 +85,29 @@ pub async fn gen_bot(data: Data, args: Args) -> Client {
|
|||||||
|
|
||||||
|
|
||||||
async fn make_cmd_vec(data: &Data) -> Vec<Cmd> {
|
async fn make_cmd_vec(data: &Data) -> Vec<Cmd> {
|
||||||
let mut cmds = vec![
|
let mut cmds: Vec<Cmd> = vec![
|
||||||
// GENERIC
|
// GENERIC
|
||||||
cmds::help::cmd(),
|
cmds::generic::help::cmd(),
|
||||||
cmds::eight_ball::cmd(),
|
cmds::generic::eight_ball::cmd(),
|
||||||
cmds::wwrps::cmd(),
|
cmds::generic::wwrps::cmd(),
|
||||||
// REDDIT
|
// REDDIT
|
||||||
re_cmds::add::cmd(),
|
cmds::reddit::add::cmd(),
|
||||||
re_cmds::approve::cmd(),
|
cmds::reddit::approve::cmd(),
|
||||||
re_cmds::get::cmd(),
|
cmds::reddit::get::cmd(),
|
||||||
re_cmds::remove::cmd(),
|
cmds::reddit::remove::cmd(),
|
||||||
re_cmds::top::cmd(),
|
cmds::reddit::top::cmd(),
|
||||||
re_cmds::update::cmd(),
|
cmds::reddit::update::cmd(),
|
||||||
re_cmds::vote::cmd(),
|
cmds::reddit::vote::cmd(),
|
||||||
re_cmds::shorturl::cmd(),
|
cmds::reddit::shorturl::cmd(),
|
||||||
// OWNER
|
// OWNER
|
||||||
cmds::embed::cmd(),
|
cmds::generic::embed::cmd(),
|
||||||
cmds::send::cmd(),
|
cmds::generic::send::cmd(),
|
||||||
debug_cmds::main_cmd::cmd(),
|
cmds::debug::main_cmd::cmd(),
|
||||||
// DATABASE
|
// DATABASE
|
||||||
db_cmds::main_cmd::cmd()
|
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()
|
.as_array()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.iter()
|
.iter()
|
||||||
@@ -121,12 +120,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_str: String;
|
||||||
|
|
||||||
let status = m_data["general"]["status"].as_str().unwrap();
|
let status = cfg["general"]["status"].as_str().unwrap();
|
||||||
let status_c = m_data["general"]["statusCommitNumber"].as_bool().unwrap();
|
let status_c = cfg["general"]["statusCommitNumber"].as_bool().unwrap();
|
||||||
let status_ec = m_data["general"]["statusExperimentalCommit"].as_bool().unwrap();
|
let status_ec = cfg["general"]["statusExperimentalCommit"].as_bool().unwrap();
|
||||||
|
|
||||||
if status_c {
|
if status_c {
|
||||||
let commit_num_r = process::Command::new("git")
|
let commit_num_r = process::Command::new("git")
|
||||||
|
|||||||
+75
@@ -0,0 +1,75 @@
|
|||||||
|
use std::{fs, path::PathBuf};
|
||||||
|
|
||||||
|
use serde_json::Value;
|
||||||
|
use dynfmt::{Format, SimpleCurlyFormat};
|
||||||
|
|
||||||
|
use crate::{Error, rs_warnln};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum LangErrorType {
|
||||||
|
InvalidArguments,
|
||||||
|
NotAnEndpoint,
|
||||||
|
KeyNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct Lang {
|
||||||
|
data: Value
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl From<Value> for Lang {
|
||||||
|
fn from(value: Value) -> Self
|
||||||
|
{ return Lang { data: value }; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Lang {
|
||||||
|
pub fn from_file(filepath: PathBuf) -> Result<Self, Error> {
|
||||||
|
if !filepath.exists() { return Err(Error::from("LANG filepath not found!")); }
|
||||||
|
|
||||||
|
let file_contents = fs::read_to_string(filepath).unwrap();
|
||||||
|
let json = serde_json::from_str(&file_contents).unwrap();
|
||||||
|
|
||||||
|
return Ok(Lang { data: json });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn get(&self, path: &str, args: &[String]) -> String {
|
||||||
|
let path_arr: Vec<&str> = path.split(".").collect();
|
||||||
|
return self.get_from_arr(path_arr, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn get_from_arr(&self, path: Vec<&str>, args: &[String]) -> String {
|
||||||
|
let str_path = path.join(".");
|
||||||
|
let mut search: &Value = &self.data;
|
||||||
|
|
||||||
|
for i in &path {
|
||||||
|
let Some(r) = search.get(i)
|
||||||
|
else { rs_warnln!("LANG warning ({:?})! ({})", LangErrorType::KeyNotFound, str_path); return str_path; };
|
||||||
|
|
||||||
|
let str_r = r.as_str();
|
||||||
|
|
||||||
|
if let Some(string) = str_r
|
||||||
|
{ return Lang::format_str(string, args, str_path); }
|
||||||
|
else { search = r; }
|
||||||
|
}
|
||||||
|
|
||||||
|
rs_warnln!("LANG warning ({:?})! ({})", LangErrorType::NotAnEndpoint, str_path);
|
||||||
|
return str_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn format_str(string: &str, args: &[String], fallback: String) -> String {
|
||||||
|
let cow = SimpleCurlyFormat.format(string, args);
|
||||||
|
|
||||||
|
if let Ok(ok) = cow
|
||||||
|
{ return ok.to_string(); }
|
||||||
|
else {
|
||||||
|
rs_warnln!("LANG warning ({:?})! ({})", LangErrorType::InvalidArguments, fallback);
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -59,40 +59,4 @@ macro_rules! warnln {
|
|||||||
format!($($arg)*)
|
format!($($arg)*)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! lang {
|
|
||||||
($key:expr) => {
|
|
||||||
{
|
|
||||||
use $crate::{LANG, errln};
|
|
||||||
let value = unsafe {
|
|
||||||
LANG
|
|
||||||
.as_ref()
|
|
||||||
.expect("LANG must be initialized before use")
|
|
||||||
.get($key)
|
|
||||||
};
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($key:expr, $($arg:expr),*) => {{
|
|
||||||
use $crate::{LANG, errln};
|
|
||||||
use formatx::formatx;
|
|
||||||
|
|
||||||
let value = unsafe {
|
|
||||||
LANG
|
|
||||||
.as_ref()
|
|
||||||
.expect("LANG must be initialized before use")
|
|
||||||
.get($key)
|
|
||||||
};
|
|
||||||
|
|
||||||
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()
|
|
||||||
}};
|
|
||||||
}
|
}
|
||||||
+86
-153
@@ -1,211 +1,144 @@
|
|||||||
#![warn(unused_extern_crates)]
|
|
||||||
#![allow(clippy::needless_return)]
|
#![allow(clippy::needless_return)]
|
||||||
#![allow(static_mut_refs)]
|
|
||||||
|
|
||||||
mod cmds {
|
|
||||||
pub mod eight_ball;
|
mod cmds;
|
||||||
pub mod embed;
|
mod db;
|
||||||
pub mod help;
|
|
||||||
pub mod send;
|
|
||||||
pub mod wwrps;
|
|
||||||
}
|
|
||||||
mod re_cmds {
|
|
||||||
pub mod add;
|
|
||||||
pub mod approve;
|
|
||||||
pub mod generic_fns;
|
|
||||||
pub mod get;
|
|
||||||
pub mod remove;
|
|
||||||
pub mod shorturl;
|
|
||||||
pub mod top;
|
|
||||||
pub mod update;
|
|
||||||
pub mod vote;
|
|
||||||
}
|
|
||||||
mod debug_cmds {
|
|
||||||
pub mod guild_invite;
|
|
||||||
pub mod leave_guild;
|
|
||||||
pub mod main_cmd;
|
|
||||||
pub mod ping;
|
|
||||||
pub mod reload_cfg;
|
|
||||||
pub mod save;
|
|
||||||
pub mod stop;
|
|
||||||
pub mod view_guilds;
|
|
||||||
pub mod whoami;
|
|
||||||
}
|
|
||||||
mod db_cmds {
|
|
||||||
pub mod add_server;
|
|
||||||
pub mod reddit_channel;
|
|
||||||
pub mod main_cmd;
|
|
||||||
pub mod wwrps_channel;
|
|
||||||
}
|
|
||||||
mod events;
|
mod events;
|
||||||
|
mod games;
|
||||||
|
mod gen;
|
||||||
|
mod lang;
|
||||||
|
mod macros;
|
||||||
mod messages;
|
mod messages;
|
||||||
mod python;
|
mod python;
|
||||||
mod macros;
|
|
||||||
#[allow(unknown_lints)]
|
|
||||||
mod websocket;
|
|
||||||
mod data;
|
|
||||||
mod schedule;
|
mod schedule;
|
||||||
mod gen;
|
mod websocket;
|
||||||
|
|
||||||
|
|
||||||
use std::process;
|
use std::process;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
use std::thread::JoinHandle;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::vec;
|
use std::vec;
|
||||||
use std::error::Error as StdErr;
|
use std::error::Error as StdErr;
|
||||||
|
|
||||||
use clap::Parser;
|
|
||||||
use r#gen::gen_bot;
|
use r#gen::gen_bot;
|
||||||
use r#gen::gen_data;
|
use r#gen::gen_data;
|
||||||
use poise::Command;
|
use poise::Command;
|
||||||
use schedule::run_schedules;
|
use schedule::run_schedules;
|
||||||
use serde::Serialize;
|
|
||||||
use serde_json::Value;
|
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
use tokio::sync::Mutex;
|
|
||||||
use websocket::send_cmd_json;
|
use websocket::send_cmd_json;
|
||||||
|
|
||||||
use crate::cmds::wwrps::RPSGame;
|
use crate::db::bot_data::Data;
|
||||||
use crate::data::get_toml_mutex;
|
use crate::db::env_vars::AssistantEnv;
|
||||||
|
use crate::db::terminal_args::Args;
|
||||||
use crate::schedule::Schedule;
|
use crate::schedule::Schedule;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Parser, Serialize, Clone)]
|
|
||||||
struct Args {
|
|
||||||
#[arg(short = 'p', long, default_value = "2920", help = "Sets the port number, e.g 2200.")]
|
|
||||||
port: u16,
|
|
||||||
#[arg(long, help = "Runs only the Python part of the program.")]
|
|
||||||
py: bool,
|
|
||||||
#[arg(long, help = "Runs only the Rust part of the program.")]
|
|
||||||
rs: bool,
|
|
||||||
#[arg(short = 'd', long, help = "Enables dev mode. Dev mode shows more debug info and turns off certain security measures.")]
|
|
||||||
dev: bool,
|
|
||||||
#[arg(short = 'w', long, help = "Wipes all data before running the program.")]
|
|
||||||
wipe: bool,
|
|
||||||
#[arg(short = 't', long, help = "Makes the program use the ASSISTANT_TOKEN_TEST env var instead of ASSISTANT_TOKEN. This env var should hold the token of a non-production bot.")]
|
|
||||||
test: bool,
|
|
||||||
#[arg(long, help = "Adds annoying prints when the websockets send a ping. Why though?")]
|
|
||||||
ping: bool,
|
|
||||||
#[arg(long, help = "Makes the program not use the schedule system.")]
|
|
||||||
nosched: bool
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
type Error = Box<dyn std::error::Error + Send + Sync>;
|
type Error = Box<dyn std::error::Error + Send + Sync>;
|
||||||
type Context<'a> = poise::Context<'a, Data, Error>;
|
type Context<'a> = poise::Context<'a, Data, Error>;
|
||||||
type Cmd = Command<Data, Box<dyn StdErr + Send + Sync>>;
|
type Cmd = Command<Data, Box<dyn StdErr + Send + Sync>>;
|
||||||
|
|
||||||
|
|
||||||
struct Data {
|
|
||||||
owners: Vec<u64>,
|
|
||||||
ball_prompts: [Vec<String>; 2],
|
|
||||||
rps_game: Mutex<RPSGame>,
|
|
||||||
reddit_data: Mutex<Option<Value>>,
|
|
||||||
discord_data: Mutex<Option<Value>>,
|
|
||||||
cfg: Mutex<Option<toml::Value>>,
|
|
||||||
bk_mods: Vec<u64>,
|
|
||||||
args: Args,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static CFG_DATA_RE: &str = "posts";
|
|
||||||
|
|
||||||
pub static mut LANG_NAME: Option<String> = None;
|
|
||||||
pub static mut LANG: Option<serde_json::Value> = None;
|
|
||||||
pub static mut NOPING: bool = false;
|
|
||||||
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let args = <Args as clap::Parser>::parse();
|
let args = Args::new();
|
||||||
let args_str = serde_json::to_string(&args).expect("Error serializing args to JSON");
|
rs_println!("ARGS: {}", args.to_string());
|
||||||
unsafe { NOPING = !args.ping; }
|
let env_vars = AssistantEnv::new(args.test);
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
rs_println!("Generating and/or fetching data and config...");
|
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: {}", data.lang.get("lang_load", &[]));
|
||||||
|
|
||||||
rs_println!("Fetching language file...");
|
// We start this here cuz we have all the data that we need
|
||||||
let data_binding = get_toml_mutex(&data.cfg).await.unwrap();
|
if data.args.py && !data.args.rs {
|
||||||
let lang_cfg = data_binding["general"]["lang"].as_str().unwrap();
|
let _ = python::start(
|
||||||
data::load_lang_data(lang_cfg.to_string());
|
data.args.clone(),
|
||||||
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"));
|
data.lang_name.clone(),
|
||||||
|
data.env_vars.clone(),
|
||||||
if args.test { println!("----- USING TEST BOT -----"); }
|
).await;
|
||||||
if args.dev { println!("----- DEV MODE ENABLED -----"); }
|
|
||||||
if args.dev && args.wipe { println!("----- \"DON'T WORRY ABOUT IT\" MODE ENABLED -----"); }
|
|
||||||
if args.nosched { println!("----- NO SCHEDULES -----"); }
|
|
||||||
|
|
||||||
if args.py && !args.rs {
|
|
||||||
println!("----- PYTHON ONLY MODE -----");
|
|
||||||
rs_println!("ARGS: {}", args_str);
|
|
||||||
let _ = python::start(args).await;
|
|
||||||
process::exit(0);
|
process::exit(0);
|
||||||
}
|
}
|
||||||
else if args.rs && ! args.py {
|
else if data.args.rs && ! data.args.py {
|
||||||
println!("----- RUST ONLY MODE -----");
|
start_bot(data).await;
|
||||||
rs_println!("ARGS: {}", args_str);
|
|
||||||
start(args, data).await;
|
|
||||||
process::exit(0);
|
process::exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
rs_println!("ARGS: {}", args_str);
|
let cfg_arr = data.cfg["commands"]["disabled_categories"].as_array().unwrap();
|
||||||
|
|
||||||
let cfg = get_toml_mutex(&data.cfg).await.unwrap();
|
|
||||||
let cfg_arr = cfg["commands"]["disabled_categories"].as_array().unwrap();
|
|
||||||
let run_py = !cfg_arr.iter().any(|val| val.as_str() == Some("re"));
|
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!"); }
|
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 || {
|
let nosched = data.args.nosched;
|
||||||
rt_rs.block_on(async {
|
let test = data.args.test;
|
||||||
if run_py { websocket::start(rust_args.clone(), own_vec_u64.clone()).await; }
|
|
||||||
start(rust_args, data).await;
|
let python = start_py(
|
||||||
});
|
data.args.clone(),
|
||||||
});
|
data.lang_name.clone(),
|
||||||
|
data.env_vars.clone(),
|
||||||
let python = thread::spawn(move || {
|
run_py
|
||||||
rt_py.block_on(async {
|
);
|
||||||
if run_py { let _ = python::start(python_args).await; }
|
let rust = start_rs(data, run_py);
|
||||||
});
|
|
||||||
});
|
if !nosched { start_schedules(test).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;
|
|
||||||
}
|
|
||||||
|
|
||||||
rust.join().unwrap();
|
rust.join().unwrap();
|
||||||
python.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...");
|
rs_println!("Starting Discord bot...");
|
||||||
bot.start().await.unwrap();
|
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() {
|
async fn read_reddit_inbox() {
|
||||||
unsafe {
|
unsafe {
|
||||||
if !websocket::HAS_CONNECTED { return; }
|
if !websocket::HAS_CONNECTED { return; }
|
||||||
send_cmd_json("respond_mentions", None, !NOPING).await;
|
send_cmd_json("respond_mentions", None, false).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+50
-41
@@ -1,7 +1,7 @@
|
|||||||
use std::env;
|
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
use crate::{lang, Args, Context};
|
use crate::Context;
|
||||||
|
use crate::lang::Lang;
|
||||||
|
|
||||||
use base64::engine::general_purpose;
|
use base64::engine::general_purpose;
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
@@ -10,7 +10,7 @@ use flate2::write::ZlibEncoder;
|
|||||||
use flate2::Compression;
|
use flate2::Compression;
|
||||||
use poise::serenity_prelude::json::Value;
|
use poise::serenity_prelude::json::Value;
|
||||||
use poise::{serenity_prelude::CreateMessage, CreateReply, ReplyHandle};
|
use poise::{serenity_prelude::CreateMessage, CreateReply, ReplyHandle};
|
||||||
use poise::serenity_prelude::{ChannelId, Color, CreateActionRow, CreateButton, CreateEmbed, CreateEmbedAuthor, EditMessage, Http, Message, ReactionType, Timestamp, UserId};
|
use poise::serenity_prelude::{ChannelId, Color, CreateActionRow, CreateButton, CreateEmbed, CreateEmbedAuthor, Http, Message, ReactionType, Timestamp, UserId};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ pub struct EmbedOptions {
|
|||||||
impl Default for EmbedOptions {
|
impl Default for EmbedOptions {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
return EmbedOptions {
|
return EmbedOptions {
|
||||||
desc: lang!("dc_msg_embed_default_embed_desc"),
|
desc: "Default english embed description.".to_string(),
|
||||||
title: None,
|
title: None,
|
||||||
col: None,
|
col: None,
|
||||||
url: None,
|
url: None,
|
||||||
@@ -87,7 +87,6 @@ pub async fn send_msg(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub async fn http_send_msg(
|
pub async fn http_send_msg(
|
||||||
http: &Http,
|
http: &Http,
|
||||||
c_id: ChannelId,
|
c_id: ChannelId,
|
||||||
@@ -135,8 +134,7 @@ pub async fn send_embed(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
/* pub async fn http_send_embed(
|
||||||
pub async fn http_send_embed(
|
|
||||||
http: &Http,
|
http: &Http,
|
||||||
c_id: ChannelId,
|
c_id: ChannelId,
|
||||||
options: EmbedOptions
|
options: EmbedOptions
|
||||||
@@ -152,7 +150,7 @@ pub async fn http_send_embed(
|
|||||||
|
|
||||||
let msg = c_id.send_message(http, r).await;
|
let msg = c_id.send_message(http, r).await;
|
||||||
return msg.ok();
|
return msg.ok();
|
||||||
}
|
} */
|
||||||
|
|
||||||
|
|
||||||
pub fn embed_from_options(options: EmbedOptions) -> CreateEmbed {
|
pub fn embed_from_options(options: EmbedOptions) -> CreateEmbed {
|
||||||
@@ -189,26 +187,35 @@ pub async fn edit_reply(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
/* pub async fn http_edit_msg(
|
||||||
pub async fn http_edit_msg(
|
|
||||||
http: &Http,
|
http: &Http,
|
||||||
mut msg: Message,
|
mut msg: Message,
|
||||||
new_msg: EditMessage
|
new_msg: EditMessage
|
||||||
) {
|
) {
|
||||||
let _ = msg.edit(http, new_msg).await;
|
let _ = msg.edit(http, new_msg).await;
|
||||||
}
|
} */
|
||||||
|
|
||||||
|
|
||||||
pub async fn send_dm(msg: String, args: Args, receivers: Vec<u64>) {
|
/*pub async fn send_dm(msg: String, data: &Data) {
|
||||||
let token: String =
|
let http = Http::new(&data.env_vars.token);
|
||||||
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!") };
|
|
||||||
|
|
||||||
|
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 http = Http::new(&token);
|
||||||
|
|
||||||
let c_msg = CreateMessage::new().content(msg);
|
let c_msg = CreateMessage::new().content(msg);
|
||||||
|
|
||||||
for uid in receivers {
|
for uid in bot_owners {
|
||||||
if uid == 0 { continue; }
|
if uid == 0 { continue; }
|
||||||
let user = UserId::new(uid);
|
let user = UserId::new(uid);
|
||||||
let _ = user.dm(http.as_ref(), c_msg.clone()).await;
|
let _ = user.dm(http.as_ref(), c_msg.clone()).await;
|
||||||
@@ -216,19 +223,19 @@ pub async fn send_dm(msg: String, args: Args, receivers: Vec<u64>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn make_post_embed(post_data: &Value, url: &str, ephemeral: bool) -> EmbedOptions {
|
pub fn make_post_embed(lang: &Lang, post_data: &Value, url: &str, ephemeral: bool) -> EmbedOptions {
|
||||||
let media_type = &post_data["post_data"]["media_type"];
|
let media_type = &post_data["post_data"]["media_type"];
|
||||||
|
|
||||||
let desc_str = lang!(
|
let desc_str = lang.get(
|
||||||
"dc_msg_embed_re_post",
|
"dc.re.embed.post",
|
||||||
post_data["post_data"]["subreddit"].as_str().unwrap(),
|
&[post_data["post_data"]["subreddit"].as_str().unwrap(),
|
||||||
post_data["post_data"]["upvotes"].as_i64().unwrap(),
|
post_data["post_data"]["upvotes"].as_i64().unwrap().to_string().as_str(),
|
||||||
post_data["votes"]["mod_voters"].as_array().unwrap().len(),
|
post_data["votes"]["mod_voters"].as_array().unwrap().len().to_string().as_str(),
|
||||||
if !media_type.is_null() { media_type.as_str().unwrap() } else { "None" },
|
if !media_type.is_null() { media_type.as_str().unwrap() } else { "None" },
|
||||||
|
|
||||||
if post_data["added"] ["by_human"].as_bool().unwrap() { "✅" } else { "❌" },
|
if post_data["added"] ["by_human"].as_bool().unwrap() { "✅" } else { "❌" },
|
||||||
if post_data["added"] ["by_bot"].as_bool().unwrap() { "✅" } else { "❌" },
|
if post_data["added"] ["by_bot"].as_bool().unwrap() { "✅" } else { "❌" },
|
||||||
if post_data["approved"]["by_human"].as_bool().unwrap() { "✅" } else { "❌" }
|
if post_data["approved"]["by_human"].as_bool().unwrap() { "✅" } else { "❌" }].map(String::from)
|
||||||
);
|
);
|
||||||
|
|
||||||
let trimmed = desc_str
|
let trimmed = desc_str
|
||||||
@@ -238,7 +245,7 @@ pub fn make_post_embed(post_data: &Value, url: &str, ephemeral: bool) -> EmbedOp
|
|||||||
.join("\n");
|
.join("\n");
|
||||||
|
|
||||||
let media_urls = post_data["post_data"]["media_urls"].as_array().unwrap();
|
let media_urls = post_data["post_data"]["media_urls"].as_array().unwrap();
|
||||||
let action_row = make_post_components();
|
let action_row = make_post_components(lang);
|
||||||
|
|
||||||
let json_encoded = trim_compress_and_encode_json(post_data);
|
let json_encoded = trim_compress_and_encode_json(post_data);
|
||||||
|
|
||||||
@@ -258,23 +265,25 @@ pub fn make_post_embed(post_data: &Value, url: &str, ephemeral: bool) -> EmbedOp
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn make_removed_embed(post_data: &Value, url: &str, ephemeral: bool) -> EmbedOptions {
|
pub fn make_removed_embed(lang: &Lang, post_data: &Value, url: &str, ephemeral: bool) -> EmbedOptions {
|
||||||
let action_row = make_removed_components();
|
let action_row = make_removed_components(lang);
|
||||||
|
|
||||||
let none = lang!("none");
|
let none = lang.get("generic.none", &[]);
|
||||||
|
|
||||||
let desc = lang!(
|
let desc = lang.get(
|
||||||
"dc_msg_embed_re_removed",
|
"dc.re.embed.post_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 },
|
else { &none },
|
||||||
if !post_data["removed"]["reason"].is_null() { post_data["removed"]["reason"].as_str().unwrap() }
|
if !post_data["removed"]["reason"].is_null()
|
||||||
else { &none }
|
{ post_data["removed"]["reason"].as_str().unwrap() }
|
||||||
|
else { &none }].map(String::from)
|
||||||
);
|
);
|
||||||
|
|
||||||
let json_encoded = trim_compress_and_encode_json(post_data);
|
let json_encoded = trim_compress_and_encode_json(post_data);
|
||||||
|
|
||||||
return EmbedOptions {
|
return EmbedOptions {
|
||||||
title: Some(lang!("dc_msg_removed_square_brackets", post_data["post_data"]["title"].clone())),
|
title: Some(lang.get("dc.re.embed.removed_square_brackets", &[post_data["post_data"]["title"].clone().to_string()])),
|
||||||
desc: format!("{}\n\n{}{}{}", desc, JSON_TEXT_START, json_encoded, JSON_TEXT_END),
|
desc: format!("{}\n\n{}{}{}", desc, JSON_TEXT_START, json_encoded, JSON_TEXT_END),
|
||||||
col: Some(REMOVED_DC_COL),
|
col: Some(REMOVED_DC_COL),
|
||||||
url: Some(url.to_string()),
|
url: Some(url.to_string()),
|
||||||
@@ -329,19 +338,19 @@ pub fn decode_and_decompress_json(t: String) -> Result<Value, serde_json::Error>
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn make_post_components() -> CreateActionRow {
|
fn make_post_components(lang: &Lang) -> CreateActionRow {
|
||||||
return CreateActionRow::Buttons(vec![
|
return CreateActionRow::Buttons(vec![
|
||||||
CreateButton::new("vote_btn") .label(lang!("dc_btn_vote")) .emoji(ReactionType::Unicode("⬆️".to_string())),
|
CreateButton::new("vote_btn") .label(lang.get("dc.re.buttons.vote", &[])).emoji(ReactionType::Unicode("⬆️".to_string())),
|
||||||
CreateButton::new("unvote_btn") .label(lang!("dc_btn_unvote")),
|
CreateButton::new("unvote_btn") .label(lang.get("dc.re.buttons.unvote", &[])),
|
||||||
CreateButton::new("approve_btn") .label(lang!("dc_btn_approve")) .emoji(ReactionType::Unicode("✅".to_string())),
|
CreateButton::new("approve_btn") .label(lang.get("dc.re.buttons.approve", &[])).emoji(ReactionType::Unicode("✅".to_string())),
|
||||||
CreateButton::new("unapprove_btn").label(lang!("dc_btn_unapprove")) .emoji(ReactionType::Unicode("❌".to_string())),
|
CreateButton::new("unapprove_btn").label(lang.get("dc.re.buttons.disapprove", &[])).emoji(ReactionType::Unicode("❌".to_string())),
|
||||||
CreateButton::new("remove_btn") .label(lang!("dc_btn_remove")) .emoji(ReactionType::Unicode("🗑️".to_string()))
|
CreateButton::new("remove_btn") .label(lang.get("dc.re.buttons.remove", &[])).emoji(ReactionType::Unicode("🗑️".to_string()))
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn make_removed_components() -> CreateActionRow {
|
fn make_removed_components(lang: &Lang) -> CreateActionRow {
|
||||||
return CreateActionRow::Buttons(vec![
|
return CreateActionRow::Buttons(vec![
|
||||||
CreateButton::new("unremove_btn").label(lang!("dc_btn_unremove")).emoji(ReactionType::Unicode("↩️".to_string()))
|
CreateButton::new("unremove_btn").label(lang.get("dc.re.buttons.unremove", &[])).emoji(ReactionType::Unicode("↩️".to_string()))
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
+15
-24
@@ -1,5 +1,7 @@
|
|||||||
use crate::messages::send_dm;
|
use crate::db::env_vars::AssistantEnv;
|
||||||
use crate::{errln, lang, rs_println, Args, LANG_NAME};
|
use crate::db::terminal_args::Args;
|
||||||
|
use crate::messages::send_dm_min;
|
||||||
|
use crate::{errln, rs_println};
|
||||||
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
@@ -9,7 +11,7 @@ use pyo3::prelude::*;
|
|||||||
use pyo3::types::PyList;
|
use pyo3::types::PyList;
|
||||||
|
|
||||||
|
|
||||||
pub async fn start(args: Args) -> PyResult<()> {
|
pub async fn start(args: Args, lang_name: String, env_vars: AssistantEnv) -> PyResult<()> {
|
||||||
rs_println!("Running Python program...");
|
rs_println!("Running Python program...");
|
||||||
|
|
||||||
let args_str = serde_json::to_string(&args).expect("Error serializing args to JSON");
|
let args_str = serde_json::to_string(&args).expect("Error serializing args to JSON");
|
||||||
@@ -23,15 +25,12 @@ pub async fn start(args: Args) -> PyResult<()> {
|
|||||||
let py_args = args_str.replace(":true", ":True").replace(":false", ":False");
|
let py_args = args_str.replace(":true", ":True").replace(":false", ":False");
|
||||||
let app_path: CString;
|
let app_path: CString;
|
||||||
|
|
||||||
unsafe {
|
app_path = CString::new(format!(
|
||||||
app_path = CString::new(
|
"args = {}\nlang_name = \"{}\"\n{}",
|
||||||
format!("args = {}\nlang_name = \"{}\"\n{}",
|
py_args,
|
||||||
py_args,
|
lang_name,
|
||||||
LANG_NAME.clone().unwrap(),
|
code
|
||||||
code
|
)).unwrap();
|
||||||
)
|
|
||||||
).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut traceback: String = String::new();
|
let mut traceback: String = String::new();
|
||||||
let mut is_error = false;
|
let mut is_error = false;
|
||||||
@@ -57,19 +56,11 @@ pub async fn start(args: Args) -> PyResult<()> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if is_error {
|
if is_error {
|
||||||
let own_env = std::env::var("ASSISTANT_OWNERS").unwrap_or("0".to_string());
|
send_dm_min(
|
||||||
let own_vec_str: Vec<String> = own_env.split(",").map(String::from).collect();
|
format!("Unknown internal Python Error:\n```\n{0}\n```", traceback),
|
||||||
let own_vec_u64: Vec<u64> = own_vec_str
|
env_vars.token.clone(),
|
||||||
.iter()
|
env_vars.bot_owners.clone()
|
||||||
.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
|
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
errln!("pyO3: {}", traceback);
|
errln!("pyO3: {}", traceback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+16
-5
@@ -28,11 +28,22 @@ def py_error(*args: str):
|
|||||||
|
|
||||||
|
|
||||||
def lang(k: str) -> str:
|
def lang(k: str) -> str:
|
||||||
if G_LANG == {}:
|
return lang_from_arr(k.split("."))
|
||||||
py_error("Language must be initialized before use!")
|
|
||||||
t = G_LANG.get(k)
|
|
||||||
if t is None: py_error(f"Key not found in language \"{G_LANG_NAME}\": {k}")
|
def lang_from_arr(path: list[str]) -> str:
|
||||||
return str(t)
|
if G_LANG == {}: py_error("Language must be initialized before use!")
|
||||||
|
|
||||||
|
str_path = ".".join(path)
|
||||||
|
search = G_LANG
|
||||||
|
|
||||||
|
for i in path:
|
||||||
|
r = search.get(i) # type: ignore
|
||||||
|
if r is None: return str_path
|
||||||
|
elif r is str: return str(r)
|
||||||
|
else: search = r # type: ignore
|
||||||
|
|
||||||
|
return str_path
|
||||||
|
|
||||||
|
|
||||||
def init_lang(lang_name: str):
|
def init_lang(lang_name: str):
|
||||||
|
|||||||
+1
-1
@@ -19,7 +19,7 @@ async def main():
|
|||||||
await bot.set_args(args) # type: ignore
|
await bot.set_args(args) # type: ignore
|
||||||
py_print("Fetching language file...")
|
py_print("Fetching language file...")
|
||||||
init_lang(lang_name) # type: ignore
|
init_lang(lang_name) # type: ignore
|
||||||
py_print(f"[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')}")
|
py_print(f"[IMPORTANT] The below message is a test message, it should be written in the language you've selected\nTest message: {lang('lang_load')}")
|
||||||
except NameError:
|
except NameError:
|
||||||
py_print("No command args or language name found from Rust. Don't worry though, we have backup in place.")
|
py_print("No command args or language name found from Rust. Don't worry though, we have backup in place.")
|
||||||
init_lang("en")
|
init_lang("en")
|
||||||
|
|||||||
@@ -69,18 +69,18 @@ async def bk_week_add(mention: models.Comment, bot: botPy.Bot):
|
|||||||
bd = bot.data[botPy.RE_DATA_POSTS]
|
bd = bot.data[botPy.RE_DATA_POSTS]
|
||||||
|
|
||||||
if short_url not in bd:
|
if short_url not in bd:
|
||||||
if not is_mod: r = lang("py_re_response_weekly_add")
|
if not is_mod: r = lang("re.add")
|
||||||
if is_mod: r = lang("py_re_response_weekly_mod_add")
|
if is_mod: r = lang("re.mod_add")
|
||||||
else:
|
else:
|
||||||
if bd[short_url]["removed"]["removed"] and is_mod:
|
if bd[short_url]["removed"]["removed"] and is_mod:
|
||||||
r = lang("py_re_response_weekly_mod_unremove")
|
r = lang("re.mod_unremove")
|
||||||
elif not bd[short_url]["removed"]["removed"]:
|
elif not bd[short_url]["removed"]["removed"]:
|
||||||
r = lang("py_re_response_weekly_exists")
|
r = lang("re.exists")
|
||||||
|
|
||||||
await posts.add_post_url(bot, short_url)
|
await posts.add_post_url(bot, short_url)
|
||||||
|
|
||||||
if r != "":
|
if r != "":
|
||||||
await mention.reply(r + "\n\n" + lang("py_re_response_suffix"))
|
await mention.reply(r + "\n\n" + lang("re.suffix"))
|
||||||
if bot.args["dev"]: py_print("Responded to mention.")
|
if bot.args["dev"]: py_print("Responded to mention.")
|
||||||
elif bot.args["dev"]: py_print("Response is empty.")
|
elif bot.args["dev"]: py_print("Response is empty.")
|
||||||
await mention.mark_read()
|
await mention.mark_read()
|
||||||
+29
-22
@@ -8,8 +8,9 @@ use futures::StreamExt;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use serde_json::{Value, json};
|
use serde_json::{Value, json};
|
||||||
|
|
||||||
use crate::messages::send_dm;
|
use crate::db::bot_data::Data;
|
||||||
use crate::{lang, rs_println};
|
use crate::messages::send_dm_min;
|
||||||
|
use crate::rs_println;
|
||||||
use crate::Args;
|
use crate::Args;
|
||||||
|
|
||||||
type Sender = Arc<Mutex<Option<futures::stream::SplitSink<tokio_tungstenite::WebSocketStream<tokio::net::TcpStream>, tungstenite::Message>>>>;
|
type Sender = Arc<Mutex<Option<futures::stream::SplitSink<tokio_tungstenite::WebSocketStream<tokio::net::TcpStream>, tungstenite::Message>>>>;
|
||||||
@@ -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...");
|
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();
|
let listener = TcpListener::bind(&ip).await.unwrap();
|
||||||
rs_println!("WebSocket server running on ws://{}", ip);
|
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 {
|
while let Ok((stream, _)) = listener.accept().await {
|
||||||
let ws_stream = accept_async(stream).await.unwrap();
|
let ws_stream = accept_async(stream).await.unwrap();
|
||||||
let (sender, receiver) = ws_stream.split();
|
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_sender(sender_arc.clone()).await;
|
||||||
set_receiver(receiver_arc.clone()).await;
|
set_receiver(receiver_arc.clone()).await;
|
||||||
|
|
||||||
while let Some(Ok(msg)) = receiver_arc.lock().await.as_mut().unwrap().next().await {
|
while let Some(Ok(msg)) = receiver_arc.lock().await.as_mut().unwrap().next().await
|
||||||
handle_message(msg, args.clone(), owners.clone()).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 {
|
match msg {
|
||||||
tungstenite::Message::Text(text) => {
|
tungstenite::Message::Text(text) => {
|
||||||
rs_println!("Received from Python: {}", 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:") {
|
if let Some(stripped) = text.strip_prefix("json:") {
|
||||||
let t_json: Value = serde_json::from_str(stripped).unwrap();
|
let t_json: Value = serde_json::from_str(stripped).unwrap();
|
||||||
if t_json.get("error").is_some() {
|
if t_json.get("error").is_some() {
|
||||||
send_dm(lang!("dc_msg_dm_python_err_socket"), args, owners).await;
|
send_dm_min(
|
||||||
|
"Unknown internal Python error occurred: Websocket response error".to_string(),
|
||||||
|
token,
|
||||||
|
bot_owners
|
||||||
|
).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,16 +166,7 @@ async fn handle_message(msg: tungstenite::protocol::Message, args: Args, owners:
|
|||||||
HAS_CONNECTED = true;
|
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