Added Reddit commandsgit add .git add .!

This commit is contained in:
2025-02-19 00:43:58 +01:00
parent 413cae9c75
commit c24b0d5d5b
11 changed files with 134 additions and 45 deletions
+3 -3
View File
@@ -9,10 +9,10 @@
<!-- - [x] Some kind of voting system. --> <!-- - [x] Some kind of voting system. -->
- [ ] `/bk_week_top [category] [amount]` to get the top N posts in a category (e.g upvotes) - [ ] `/bk_week_top [category] [amount]` to get the top N posts in a category (e.g upvotes)
- [ ] Allow updating the data autonomously and via manual commands. - [ ] Allow updating the data autonomously and via manual commands.
- [ ] 30-minute schedule when bot adds posts - [ ] 10-minute schedule for updating Discord channel
- [ ] Manually add posts - [ ] Manually add posts
- [ ] via `u/[bot] add` - [ ] via `u/[bot] add`
- [ ] 5 minute schedule when user adds post - [ ] 2 minute schedule for responding to commands
<!-- - [x] ~~via `/bk_week_add [url]`~~ --> <!-- - [x] ~~via `/bk_week_add [url]`~~ -->
<!-- - [x] ~~Manually remove posts via `/bk_week_remove [url]`~~ --> <!-- - [x] ~~Manually remove posts via `/bk_week_remove [url]`~~ -->
<!-- - [x] ~~Manually approve posts via `/bk_week_approve [url]`~~ --> <!-- - [x] ~~Manually approve posts via `/bk_week_approve [url]`~~ -->
+2 -2
View File
@@ -4,9 +4,9 @@ To execute a command on the Reddit bot, include `u/ByteDiceAssistant [args]` in
## `bk_week_add` ## `bk_week_add`
Adds the post to the list of posts. Adds the post to the list of posts.
- **Only moderators of a subreddit or the OP (Original Poster) can use this command.** - **Only moderators of a subreddit or the OP (Original Poster) can use this command.**
## `bk_week_vote` <!-- ## `bk_week_vote`
Adds a vote to the post. (see `/bk_week_vote` in the Discord help). Adds a vote to the post. (see `/bk_week_vote` in the Discord help).
- **Anyone can use this command. If a moderator uses it the vote will be flagged as a moderator vote.** - **Anyone can use this command. If a moderator uses it the vote will be flagged as a moderator vote.** -->
### **Examples** ### **Examples**
``` ```
"u/ByteDiceAssistant bk_week_add" "u/ByteDiceAssistant bk_week_add"
+10 -9
View File
@@ -77,7 +77,7 @@ pub async fn bk_week_get(
send_embed_for_post(ctx, post, &url).await?; send_embed_for_post(ctx, post, &url).await?;
} }
Ok(()) return Ok(());
} }
async fn get_reddit_data(ctx: Context<'_>) -> Result<Value, Error> { async fn get_reddit_data(ctx: Context<'_>) -> Result<Value, Error> {
@@ -178,7 +178,7 @@ pub async fn bk_week_add(
if let Some(bk_week) = reddit_data.get(BK_WEEK) { if let Some(bk_week) = reddit_data.get(BK_WEEK) {
let a = approve.unwrap_or_else(|| false); let a = approve.unwrap_or_else(|| false);
let r = websocket::send_cmd_json("add_post_url", json!([&url, a, true])).await.unwrap(); let r = websocket::send_cmd_json("add_post_url", Some(json!([&url, a, true]))).await.unwrap();
if !r["value"].as_bool().unwrap() { if !r["value"].as_bool().unwrap() {
send_msg( send_msg(
@@ -239,7 +239,7 @@ pub async fn bk_week_remove(
} }
let auth = &ctx.author().name; let auth = &ctx.author().name;
let r = send_cmd_json("remove_post_url", json!([&url, &auth, &reason])).await.unwrap(); let r = send_cmd_json("remove_post_url", Some(json!([&url, &auth, &reason]))).await.unwrap();
if r["value"].as_bool().unwrap() { if r["value"].as_bool().unwrap() {
send_msg( send_msg(
@@ -287,7 +287,7 @@ async fn approve_cmd(ctx: Context<'_>, url: &str, reddit_data: &Value, approve:
send_post_removed_message(ctx, &url, post.get("removed_by").unwrap().as_str().unwrap()).await; send_post_removed_message(ctx, &url, post.get("removed_by").unwrap().as_str().unwrap()).await;
} }
let r = websocket::send_cmd_json("set_approve_post", json!([approve, &url])).await.unwrap(); let r = websocket::send_cmd_json("set_approve_post", Some(json!([approve, &url]))).await.unwrap();
if r.get("value").is_some() { if r.get("value").is_some() {
if approve { if approve {
send_msg(ctx, format!("Successfully flagged URL \"<{}>\" as `approved:by_human`!", &url), true, true).await; send_msg(ctx, format!("Successfully flagged URL \"<{}>\" as `approved:by_human`!", &url), true, true).await;
@@ -345,7 +345,7 @@ pub async fn bk_week_update(
let mut p_text = "Fetching new posts & updating data file...".to_string(); let mut p_text = "Fetching new posts & updating data file...".to_string();
let progress = send_msg(ctx, p_text.clone(), true, true).await; let progress = send_msg(ctx, p_text.clone(), true, true).await;
send_cmd_json("add_new_posts", json!([])).await; send_cmd_json("add_new_posts", None).await;
data::update_re_data(ctx.data()).await; data::update_re_data(ctx.data()).await;
let r_data = get_reddit_data(ctx).await.unwrap(); let r_data = get_reddit_data(ctx).await.unwrap();
@@ -363,7 +363,7 @@ pub async fn bk_week_update(
let msgs_json = msgs_to_json(msgs, &r_data).await; let msgs_json = msgs_to_json(msgs, &r_data).await;
p_text = update_progress(ctx, progress.clone().unwrap(), p_text.clone(), "\nAdding new posts...".to_string()).await; p_text = update_progress(ctx, progress.clone().unwrap(), p_text.clone(), "\nAdding new posts...".to_string()).await;
let weekly_art = r_data["bk_weekly_art_posts"].as_object().unwrap(); let weekly_art = r_data[BK_WEEK].as_object().unwrap();
for url in weekly_art.keys() { for url in weekly_art.keys() {
if ["no_change", "updated", "removed"] if ["no_change", "updated", "removed"]
@@ -504,7 +504,7 @@ async fn msgs_to_json<'a>(msgs: Vec<Message>, reddit_data: &'a Value) -> Value {
if msg_json.is_err() { continue; } if msg_json.is_err() { continue; }
let mut u_json: Value = msg_json.unwrap(); let mut u_json: Value = msg_json.unwrap();
let re_url = &reddit_data["bk_weekly_art_posts"][&url]; let re_url = &reddit_data[BK_WEEK][&url];
if re_url.get("removed").is_some() { if re_url.get("removed").is_some() {
if u_json.get("removed").is_some() { if u_json.get("removed").is_some() {
@@ -545,6 +545,7 @@ async fn msgs_to_json<'a>(msgs: Vec<Message>, reddit_data: &'a Value) -> Value {
#[poise::command(slash_command, prefix_command)] #[poise::command(slash_command, prefix_command)]
/// Adds/removes a vote from a post. These votes are not tied to Reddit upvotes.
pub async fn bk_week_vote( pub async fn bk_week_vote(
ctx: Context<'_>, ctx: Context<'_>,
#[description = "The post URL."] url: String, #[description = "The post URL."] url: String,
@@ -554,7 +555,7 @@ pub async fn bk_week_vote(
data::update_re_data(ctx.data()).await; data::update_re_data(ctx.data()).await;
let uid = ctx.author().id.get(); let uid = ctx.author().id.get();
let re_data = get_reddit_data(ctx).await.unwrap(); let re_data = get_reddit_data(ctx).await.unwrap();
let post_data = re_data["bk_weekly_art_posts"].clone(); let post_data = re_data[BK_WEEK].clone();
let unw_vote = un_vote.unwrap_or_else(|| false); let unw_vote = un_vote.unwrap_or_else(|| false);
if post_data.get(&url).is_none() { if post_data.get(&url).is_none() {
@@ -582,7 +583,7 @@ pub async fn bk_week_vote(
return Ok(()); return Ok(());
} }
let r = send_cmd_json("set_vote_post", json!([url, uid, is_mod, true, unw_vote])).await.unwrap(); let r = send_cmd_json("set_vote_post", Some(json!([url, uid, is_mod, true, unw_vote]))).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 {
+18 -8
View File
@@ -8,7 +8,6 @@ use crate::messages::{edit_msg, send_embed, send_msg, Author, EmbedOptions};
use poise::serenity_prelude::{OnlineStatus, Timestamp, UserId}; use poise::serenity_prelude::{OnlineStatus, Timestamp, UserId};
use rand::{seq::IteratorRandom, Rng}; use rand::{seq::IteratorRandom, Rng};
use regex::Regex; use regex::Regex;
use serde_json::json;
#[poise::command(slash_command, prefix_command)] #[poise::command(slash_command, prefix_command)]
@@ -40,7 +39,7 @@ pub async fn stop(
let msg = send_msg(ctx, "Saving data...".to_string(), true, true).await.unwrap(); let msg = send_msg(ctx, "Saving data...".to_string(), true, true).await.unwrap();
data::write_dc_data(ctx.data()).await; data::write_dc_data(ctx.data()).await;
data::write_re_data().await; data::write_re_data().await;
send_cmd_json("stop_praw", json!([])).await; send_cmd_json("stop_praw", None).await;
edit_msg(ctx, msg, "Saving data... Done!\nShutting down...".to_string()).await; edit_msg(ctx, msg, "Saving data... Done!\nShutting down...".to_string()).await;
ctx.serenity_context().set_presence(None, OnlineStatus::Invisible); ctx.serenity_context().set_presence(None, OnlineStatus::Invisible);
@@ -153,12 +152,10 @@ pub async fn re_shorturl(
#[description = "A Reddit post URL"] url: String #[description = "A Reddit post URL"] url: String
) -> Result<(), Error> ) -> Result<(), Error>
{ {
let re = Regex::new(r"comments/([a-zA-Z0-9]+)").unwrap(); let shorturl = to_shorturl(&url);
if let Some(caps) = re.captures(&url) { if shorturl.is_ok() {
let post_id = &caps[1]; send_msg(ctx, format!("ShortURL: <{}>", shorturl.unwrap()), true, true).await;
let short_url = format!("https://redd.it/{}", post_id);
send_msg(ctx, format!("ShortURL: <{}>", short_url), true, true).await;
} }
else { else {
send_msg(ctx, "Couldn't convert to shortURL: Invalid URL".to_string(), true, true).await; send_msg(ctx, "Couldn't convert to shortURL: Invalid URL".to_string(), true, true).await;
@@ -168,6 +165,19 @@ pub async fn re_shorturl(
} }
fn to_shorturl(url: &str) -> Result<String, &str> {
let re = Regex::new(r"comments/([a-zA-Z0-9]+)").unwrap();
if let Some(caps) = re.captures(url) {
let post_id = &caps[1];
let short_url = format!("https://redd.it/{}", post_id);
return Ok(short_url);
}
return Err("Invalid URL");
}
#[poise::command(slash_command, prefix_command, default_member_permissions = "ADMINISTRATOR", guild_only)] #[poise::command(slash_command, prefix_command, default_member_permissions = "ADMINISTRATOR", guild_only)]
/// Add your server to my database so I can sell it! (/s), I only store some minimal data the bot needs. /// Add your server to my database so I can sell it! (/s), I only store some minimal data the bot needs.
pub async fn add_server( pub async fn add_server(
+2 -2
View File
@@ -96,13 +96,13 @@ fn generate_re_data() {
pub async fn update_re_data(data: &Data) { pub async fn update_re_data(data: &Data) {
send_cmd_json("update_data_file", json!([])).await; send_cmd_json("update_data_file", None).await;
read_re_data(data, false).await; read_re_data(data, false).await;
} }
pub async fn write_re_data() { pub async fn write_re_data() {
send_cmd_json("update_data_file", json!([])).await; send_cmd_json("update_data_file", None).await;
} }
+78
View File
@@ -0,0 +1,78 @@
import asyncpraw as praw
import asyncpraw.models as models
from macros import *
import bot as botPy
import data
import posts
BOT_ACTION_POSTFIX = "\n\n^(I am not an AI, I am just a bot. This action was preformed automatically by the way.)"
async def make_cmd(cmd: str, bot: botPy.Bot) -> str:
return f"u/{await bot.r.user.me()} {cmd}"
async def is_cmd(cmd: str, text: str, bot: botPy.Bot) -> bool:
command: str = await make_cmd(cmd, bot)
return command.lower() in text.lower()
async def respond_to_mention(bot: botPy.Bot) -> bool:
async for mention in bot.r.inbox.mentions(limit=25):
if not mention.new:
continue
max_len = 100
body = mention.body
truncated = body[:max_len] + "..." if len(body) > max_len else body
py_print(f"New mention: {truncated}")
if await is_cmd("bk_week_add", body, bot):
await bk_week_add(mention, bot)
else:
await mention.mark_read()
return True
async def bk_week_add(mention: models.Comment, bot: botPy.Bot):
await mention.submission.load()
author = mention.author
is_op = author == mention.submission.author
is_mod = author in await mention.subreddit.moderator()
if not is_op and not is_mod:
await mention.mark_read()
return
short_url = mention.submission.shortlink
r = ""
bd = bot.data[data.BK_WEEKLY]
if short_url not in bd:
posts.add_post_url(bot, short_url)
r = "Successfully added this post to the data!"
if short_url in bd and is_mod:
if "removed" in bd[short_url]:
r = "(Mod action) Successfully un-removed this post from the data! Glad to see you back!"
else:
r = "(Mod action) Successfully added this post to the data!"
post = await posts.from_url(bot, short_url)
post_data = posts.get_post_details(post[1])
data.add_post_to_data(bot, post_data, True)
elif short_url in bd:
r = "Could not add this post to the data. Luckily, it's already there, so there's nothing to worry about!"
await mention.reply(r + " Thank you for participating!" + BOT_ACTION_POSTFIX)
await mention.mark_read()
+2 -2
View File
@@ -1,13 +1,13 @@
from printColors import PrintColors from printColors import PrintColors
def py_print(*args: str): def py_print(*args):
print( print(
PrintColors.FG.blue + "Py", PrintColors.FG.blue + "Py",
"-", "-",
" ".join(args) + PrintColors.Special.reset " ".join(args) + PrintColors.Special.reset
) )
def py_error(*args: str): def py_error(*args):
print( print(
PrintColors.BG.red + "ERROR" + PrintColors.Special.reset, PrintColors.BG.red + "ERROR" + PrintColors.Special.reset,
PrintColors.FG.blue + "Py", PrintColors.FG.blue + "Py",
-2
View File
@@ -1,13 +1,11 @@
import sys import sys
import asyncio import asyncio
import threading
import time import time
from macros import * from macros import *
import bot as botPy import bot as botPy
import data import data
import py_websocket import py_websocket
import posts
async def main(): async def main():
+1 -1
View File
@@ -119,7 +119,7 @@ def get_post_details(post: models.Submission, added_by_h: bool = False) -> data.
) )
async def add_post_url(bot, url: str, approve: bool, added_by_h: bool = False) -> bool: async def add_post_url(bot, url: str, approve: bool = False, added_by_h: bool = False) -> bool:
result, post = await from_url(bot, url) result, post = await from_url(bot, url)
if not result: if not result:
+13 -13
View File
@@ -6,6 +6,7 @@ from macros import *
import bot as botPy import bot as botPy
import data import data
import posts import posts
import cmds
ws_global = None ws_global = None
is_connected = False is_connected = False
@@ -66,26 +67,25 @@ async def json_to_func(v: dict, bot: botPy.Bot) -> dict:
return return
value_supported = True value_supported = True
result = {"type": "result", "value": False} r = False
match v["value"]: match v["value"]:
case "update_data_file": result = result_json(data.write_data(bot)) case "update_data_file": r = data .write_data (bot)
case "add_new_posts": result = result_json(await posts.add_new_posts(bot)) case "add_new_posts": r = await posts.add_new_posts (bot)
case "add_post_url": result = result_json(await posts.add_post_url(bot, *v["args"])) case "add_post_url": r = await posts.add_post_url (bot, *v["args"])
case "remove_post_url": result = result_json(data.remove_post(bot, *v["args"])) case "remove_post_url": r = data .remove_post (bot, *v["args"])
case "set_approve_post": result = result_json(data.set_approve_post(bot, *v["args"])) case "set_approve_post": r = data .set_approve_post (bot, *v["args"])
case "set_vote_post": result = result_json(data.set_vote_post(bot, *v["args"])) case "set_vote_post": r = data .set_vote_post (bot, *v["args"])
case "stop_praw": result = result_json(await bot.stop()) case "respond_mentions": r = await cmds .respond_to_mention(bot)
case "stop_praw": r = await bot .stop()
case _: value_supported = False case _: value_supported = False
if not isinstance(result.get("value"), bool): if not value_supported:
py_print("Result JSON is invalid:", str(result))
if bot.args["dev"] and not value_supported:
val = v["value"] val = v["value"]
py_print(f"Value \"{val}\" is not supported") py_print(f"Value \"{val}\" is not supported")
return {"type": "result", "value": False}
return result return result_json(r)
def result_json(bool: bool) -> dict: def result_json(bool: bool) -> dict:
+5 -3
View File
@@ -6,7 +6,7 @@ use tokio_tungstenite::WebSocketStream;
use tokio_tungstenite::{accept_async, tungstenite}; use tokio_tungstenite::{accept_async, tungstenite};
use futures::StreamExt; use futures::StreamExt;
use std::sync::Arc; use std::sync::Arc;
use serde_json::Value; use serde_json::{Value, json};
use crate::messages::send_dm; use crate::messages::send_dm;
use crate::rs_println; use crate::rs_println;
@@ -46,15 +46,17 @@ pub async fn send_msg(msg: &str) {
#[allow(static_mut_refs)] #[allow(static_mut_refs)]
pub async fn send_cmd_json(func_name: &str, func_args: Value) -> Option<Value> { pub async fn send_cmd_json(func_name: &str, func_args: Option<Value>) -> Option<Value> {
unsafe { unsafe {
let Some(sender) = &GLOBAL_SENDER else { return None }; let Some(sender) = &GLOBAL_SENDER else { return None };
let mut sender = sender.lock().await; let mut sender = sender.lock().await;
let Some(s) = sender.as_mut() else { return None }; let Some(s) = sender.as_mut() else { return None };
let unw_args = if func_args.is_some() { func_args.unwrap() } else { json!([]) };
let json_str = format!( let json_str = format!(
"json:{{\"type\": \"function\", \"value\":\"{}\", \"args\": {}}}", "json:{{\"type\": \"function\", \"value\":\"{}\", \"args\": {}}}",
func_name, func_args func_name, unw_args
); );
if s.send(tungstenite::Message::Text(json_str.into())).await.is_err() { if s.send(tungstenite::Message::Text(json_str.into())).await.is_err() {