diff --git a/README.md b/README.md index da3801d..430a66f 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,10 @@ An automation tool primarily made for myself (Byte Dice) but publicly available > This tool is not intended for public use outside of the official *Byte Dice Assistant* bots. Expect issues if you host this yourself.\ > This tool is only designed to run on Windows (10 and 11) and XUbuntu (24.04 and above) and may not work on any other OS. +> [!NOTE] +> This bot never modifies any Reddit content. All it does and will ever do is read/scrape. +> The data will never be sold and will only be shared with permission from subreddit moderators. + ## Open-source - Copyright **ByteDiceAssistant © 2025 by Byte Dice is licensed under CC BY-NC-SA 4.0.**\ diff --git a/data/lang/en.json b/data/lang/en.json index 1a779b9..eca80b3 100644 --- a/data/lang/en.json +++ b/data/lang/en.json @@ -32,7 +32,7 @@ "dc_msg_re_post_approve_success": "Successfully approved the post!", "dc_msg_re_post_disapprove_success": "Successfully disapproved the post!", "dc_msg_re_post_remove_success": "Successfully removed post with URL \"<{0}>\"!", - "dc_msg_re_post_unremove_success": "Successfully un-removed post with URL \"<{0}>\"!", + "dc_msg_re_post_unremove_success": "Successfully restored post with URL \"<{0}>\"!", "dc_msg_re_post_update_success": "Updated post with URL \"<{0}>\"!", "dc_msg_re_posts_channel_404": "Could not find `re_posts_channel` in data!\nHint: Run `/admin_re_bindchannel` in a (preferably read-only) channel (requires administrator permission).", "dc_msg_re_vote_err": "Failed to [vote / un-vote]: Unknown internal error.", diff --git a/src/events.rs b/src/events.rs index d58259e..0bcda76 100644 --- a/src/events.rs +++ b/src/events.rs @@ -1,10 +1,11 @@ use crate::data::{get_mutex_data, update_re_data}; -use crate::messages::make_post_embed; +use crate::messages::{make_post_embed, make_removed_embed, EmbedOptions}; use crate::re_cmds::generic_fns::{is_bk_mod_serenity, serenity_edit_msg_embed, serenity_send_msg}; -use crate::{lang, rs_println, websocket, Data, Error, CFG_DATA_RE}; +use crate::websocket::send_cmd_json; +use crate::{lang, rs_println, Data, Error, CFG_DATA_RE}; use poise::serenity_prelude::{self as serenity, ActivityData, ComponentInteraction, Interaction, Member, Ready}; -use serde_json::json; +use serde_json::{json, Value}; use std::future::Future; use std::pin::Pin; @@ -54,9 +55,9 @@ async fn handle_buttons(ctx: &serenity::Context, data: &Data, interaction: &Inte return match component.data.custom_id.as_str() { "approve_btn" => approve_btn(ctx, data, &component.member.as_ref().unwrap(), component, url, true).await, - "remove_btn" => Ok(()), + "remove_btn" => remove_btn(ctx, data, &component.member.as_ref().unwrap(), component, url, true).await, "unapprove_btn" => approve_btn(ctx, data, &component.member.as_ref().unwrap(), component, url, false).await, - "unremove_btn" => Ok(()), + "unremove_btn" => remove_btn(ctx, data, &component.member.as_ref().unwrap(), component, url, false).await, "unvote_btn" => Ok(()), "vote_btn" => Ok(()), _ => Err("Message button with that ID isn't handled.".into()) @@ -67,7 +68,7 @@ async fn handle_buttons(ctx: &serenity::Context, data: &Data, interaction: &Inte async fn approve_btn(ctx: &serenity::Context, data: &Data, c_member: &Member, component: &ComponentInteraction, url: String, approve: bool) -> Result<(), Error> { if !is_bk_mod_serenity(ctx, data, c_member, component).await { return Ok(()); } - let r = websocket::send_cmd_json("set_approve_post", Some(json!([approve, url])), true).await.unwrap(); + let r = send_cmd_json("set_approve_post", Some(json!([approve, url])), true).await.unwrap(); let c_id = component.channel_id; let m_id = component.message.id; @@ -87,5 +88,41 @@ async fn approve_btn(ctx: &serenity::Context, data: &Data, c_member: &Member, co } } + return Ok(()); +} + + +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(()); } + + let r: Value; + + if remove { + r = send_cmd_json("remove_post_url", Some(json!([&url, &c_member.user.name, None::])), true).await.unwrap(); + } + else { + r = send_cmd_json("add_post_url", Some(json!([&url, false, true])), true).await.unwrap(); + } + + let c_id = component.channel_id; + let m_id = component.message.id; + + if r["value"].as_bool().unwrap() { + update_re_data(data).await; + let new_data = &get_mutex_data(&data.reddit_data).await.unwrap()[CFG_DATA_RE][&url]; + let e: EmbedOptions; + if remove { e = make_removed_embed(new_data, &url, true); } + else { e = make_post_embed (new_data, &url, true); } + + if remove { + serenity_edit_msg_embed(ctx, &c_id, &m_id, e).await; + serenity_send_msg(ctx, component, lang!("dc_msg_re_post_remove_success", &url), true).await; + } + else { + serenity_edit_msg_embed(ctx, &c_id, &m_id, e).await; + serenity_send_msg(ctx, component, lang!("dc_msg_re_post_unremove_success", &url), true).await; + } + } + return Ok(()); } \ No newline at end of file diff --git a/src/messages.rs b/src/messages.rs index b058933..6c514c3 100644 --- a/src/messages.rs +++ b/src/messages.rs @@ -231,13 +231,7 @@ pub fn make_post_embed(post_data: &Value, url: &str, ephemeral: bool) -> EmbedOp let media_urls = post_data["post_data"]["media_urls"].as_array().unwrap(); - let action_row = CreateActionRow::Buttons(vec![ - CreateButton::new("vote_btn") .label(lang!("dc_btn_vote")) .emoji(ReactionType::Unicode("⬆️".to_string())), - CreateButton::new("unvote_btn") .label(lang!("dc_btn_unvote")), - CreateButton::new("approve_btn") .label(lang!("dc_btn_approve")) .emoji(ReactionType::Unicode("✅".to_string())), - CreateButton::new("unapprove_btn").label(lang!("dc_btn_unapprove")) .emoji(ReactionType::Unicode("❌".to_string())), - CreateButton::new("remove_btn") .label(lang!("dc_btn_remove")) .emoji(ReactionType::Unicode("🗑️".to_string())) - ]); + let action_row = make_post_components(); return EmbedOptions { title: Some(post_data["post_data"]["title"].as_str().unwrap().to_string()), @@ -256,9 +250,7 @@ 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 { - let action_row = CreateActionRow::Buttons(vec![ - CreateButton::new("unremove_btn").label(lang!("dc_btn_unremove")).emoji(ReactionType::Unicode("↩️".to_string())) - ]); + let action_row = make_removed_components(); let none = lang!("none"); @@ -280,4 +272,22 @@ pub fn make_removed_embed(post_data: &Value, url: &str, ephemeral: bool) -> Embe actionrows: Some(vec![action_row]), ..Default::default() }; +} + + +fn make_post_components() -> CreateActionRow { + return CreateActionRow::Buttons(vec![ + CreateButton::new("vote_btn") .label(lang!("dc_btn_vote")) .emoji(ReactionType::Unicode("⬆️".to_string())), + CreateButton::new("unvote_btn") .label(lang!("dc_btn_unvote")), + CreateButton::new("approve_btn") .label(lang!("dc_btn_approve")) .emoji(ReactionType::Unicode("✅".to_string())), + CreateButton::new("unapprove_btn").label(lang!("dc_btn_unapprove")) .emoji(ReactionType::Unicode("❌".to_string())), + CreateButton::new("remove_btn") .label(lang!("dc_btn_remove")) .emoji(ReactionType::Unicode("🗑️".to_string())) + ]); +} + + +fn make_removed_components() -> CreateActionRow { + return CreateActionRow::Buttons(vec![ + CreateButton::new("unremove_btn").label(lang!("dc_btn_unremove")).emoji(ReactionType::Unicode("↩️".to_string())) + ]); } \ No newline at end of file diff --git a/src/python/data.py b/src/python/data.py index bfcc516..e783bc6 100644 --- a/src/python/data.py +++ b/src/python/data.py @@ -190,7 +190,7 @@ def remove_old_posts(bot: botPy.Bot, max_age: int) -> bool: remove.append(url) for key in remove: - weekly.pop(key) + weekly[key]["removed"]["removed"] = True return True diff --git a/src/re_cmds/generic_fns.rs b/src/re_cmds/generic_fns.rs index df38e0c..be65d58 100644 --- a/src/re_cmds/generic_fns.rs +++ b/src/re_cmds/generic_fns.rs @@ -34,7 +34,9 @@ pub async fn serenity_send_msg(ctx: &serenity::Context, component: &ComponentInt pub async fn serenity_edit_msg_embed(ctx: &serenity::Context, c_id: &ChannelId, m_id: &MessageId, e: EmbedOptions) { - let r = EditMessage::new().embed(embed_from_options(e)); + let r = EditMessage::new() + .embed(embed_from_options(e.clone())) + .components(e.actionrows.unwrap()); let _ = c_id.edit_message(ctx.http.clone(), m_id, r).await; } diff --git a/src/re_cmds/update.rs b/src/re_cmds/update.rs index 0093867..9aeaef1 100644 --- a/src/re_cmds/update.rs +++ b/src/re_cmds/update.rs @@ -55,6 +55,7 @@ pub async fn cmd( // Parsing messages to JSON p_text = update_progress(ctx, progress.clone(), p_text.clone(), lang!("dc_msg_update_parse", "✅\n")).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); } // Adding new posts p_text = update_progress(ctx, progress.clone(), p_text.clone(), lang!("dc_msg_update_add", "✅\n")).await; @@ -68,9 +69,9 @@ pub async fn cmd( return Ok(()); } - // Editing updated posts - p_text = update_progress(ctx, progress.clone(), p_text.clone(), lang!("dc_msg_update_editing", "✅\n")).await; - edit_posts(http, c_id, weekly_art, &msgs_json).await; + // Removing duplicate posts + p_text = update_progress(ctx, progress.clone(), p_text.clone(), lang!("dc_msg_update_removing_dupe", "✅\n")).await; + remove_dupes(http, c_id, &msgs_json).await; // Removing removed posts p_text = update_progress(ctx, progress.clone(), p_text.clone(), lang!("dc_msg_update_removing", "✅\n")).await; @@ -83,9 +84,9 @@ pub async fn cmd( send_cmd_json("remove_old_posts", Some(json!([max_age_secs])), true).await; } - // Removing duplicate posts - p_text = update_progress(ctx, progress.clone(), p_text.clone(), lang!("dc_msg_update_removing_dupe", "✅\n")).await; - remove_dupes(http, c_id, &msgs_json).await; + // Editing updated posts + p_text = update_progress(ctx, progress.clone(), p_text.clone(), lang!("dc_msg_update_editing", "✅\n")).await; + edit_posts(http, c_id, weekly_art, &msgs_json).await; // Done update_progress(ctx, progress.clone(), p_text, lang!("dc_msg_update_done", "✅\n## ")).await; @@ -193,8 +194,8 @@ async fn msgs_to_json(msgs: Vec, reddit_data: &Value, max_age: u64) -> } // removed - if re_url.get("removed").is_some() { - if u_json.get("removed").is_some() { + if re_url["removed"]["removed"].as_bool().unwrap() { + if u_json["removed"]["removed"].as_bool().unwrap() { // no change if let Some(obj) = msgs_json["no_change"].as_object_mut() { obj.insert(url.clone(), json!(msg.id.get()));