added voting system, bug fixes, and a ton more

This commit is contained in:
2025-02-17 22:48:03 +01:00
parent 896a455d6c
commit 7fc23896cb
11 changed files with 262 additions and 86 deletions
+8 -6
View File
@@ -5,11 +5,13 @@
<!-- - [x] ~~Scrape the data~~ --> <!-- - [x] ~~Scrape the data~~ -->
<!-- - [x] ~~Put it in a JSON~~ --> <!-- - [x] ~~Put it in a JSON~~ -->
<!-- - [x] ~~Multithread so it can run both Discord and Reddit bot!!!~~ -->¨ <!-- - [x] ~~Multithread so it can run both Discord and Reddit bot!!!~~ -->¨
- [ ] Security that only allows bk mods to run these commands <!-- - [x] Security that only allows bk mods to run these commands. -->
<!-- - [x] Some kind of voting system. -->
- [ ] Allow updating the data autonomously and via manual commands. - [ ] Allow updating the data autonomously and via manual commands.
- [ ] 30-minute schedule - [ ] 30-minute schedule when bot adds posts
- [ ] Manually add posts - [ ] Manually add posts
- [ ] via `u/[bot] add` - [ ] via `u/[bot] add`
- [ ] 5 minute schedule when user adds post
<!-- - [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]`~~ -->
@@ -18,7 +20,7 @@
- [ ] Automatically remove posts older than 7 days from JSON - [ ] Automatically remove posts older than 7 days from JSON
- [x] ~~Function~~ - [x] ~~Function~~
- [ ] Automate - [ ] Automate
- [ ] Automatically approve posts that dont get caught by reverse image search (ris) - [ ] Automatically approve posts that don't get caught by reverse image search (ris)
<!-- - [x] Log all posts in a Discord thread --> <!-- - [x] Log all posts in a Discord thread -->
<!-- - [x] ~~`/bk_week_bind` to bind a channel for bk_week logs~~ --> <!-- - [x] ~~`/bk_week_bind` to bind a channel for bk_week logs~~ -->
<!-- - [x] Add post if it exists in data but not in channel --> <!-- - [x] Add post if it exists in data but not in channel -->
@@ -29,9 +31,9 @@
### Medium priority: ### Medium priority:
<!-- - [x] ~~JSON -> Rules list~~ --> <!-- - [x] ~~JSON -> Rules list~~ -->
- [ ] View single rule (/rule {rulename}) - [ ] View single rule (/rule {rule_name})
- [ ] Postfix calculator - [ ] Postfix calculator
- [ ] Postfic generator - [ ] Postfix generator
- [ ] JSON -> BPS class init - [ ] JSON -> BPS class init
- [ ] BPS args -> JSON - [ ] BPS args -> JSON
- [ ] Random tip (from ByteDice.net/data/loadingScreenTips.json) - [ ] Random tip (from ByteDice.net/data/loadingScreenTips.json)
@@ -44,7 +46,7 @@
- [ ] Weekly coding competition - [ ] Weekly coding competition
* Same as particle of the week but with coding * Same as particle of the week but with coding
- [ ] Content update sender - [ ] Content update sender
* Automatically sends sneek peeks (like commit history or manual) of projects when theyre updated * Automatically sends sneak peeks (like commit history or manual) of projects when they're updated
- [ ] Language TLDR command - [ ] Language TLDR command
* Shows a TLDR with pros/cons on a programming language * Shows a TLDR with pros/cons on a programming language
- [ ] PowerPlate info viewer - [ ] PowerPlate info viewer
+9 -7
View File
@@ -1,16 +1,18 @@
# Discord Commands # Discord Commands
## `/bk_week_add [url] [approve]` ## `/bk_week_add [url] <approve>`
Adds a URL to the list of posts. This is done automatically by the bot for certain posts. Adds a URL to the list of posts. This is done automatically by the bot for certain posts.
- **`[approve]`**: `true` or `false`, determines whether to pre-approve the post. - **`<approve>`**: (OPTIONAL) `true` or `false`, determines whether to pre-approve the post.
## `/bk_week_remove [url]` ## `/bk_week_remove [url]`
Removes an existing URL from the list of posts. Removes an existing URL from the list of posts.
## `/bk_week_approve [url]` ## `/bk_week_approve [url] <disapprove>`
Flags the post as **human_approved**, confirming that the artwork is original. Flags the post as **human_approved**, confirming that the artwork is original.
## `/bk_week_disapprove [url]` - **`<disapprove>`**: (OPTIONAL) `true` or `false`, set to `true` if you want to undo an approval.
Reverses the effect of `/bk_week_approve`.
## `/bk_week_update` ## `/bk_week_update`
Updates all data in the binded Discord channel. It's reccommended to run this rarely. Updates all data in the bound Discord channel. It's recommended to run this rarely.
## `/bk_week_bind` ## `/bk_week_vote [url] <remove_vote>`
Adds a vote to a post. The intended use for votes is for a "moderator picks" and a "community picks" section of the weekly art.
You can vote on as many posts as you want, but only once per post.
## `/bk_admin_bind`
Binds the current channel as the channel where the bk_week data is sent and updated in. No binding means the only way to view the data is using `/bk_week_get`. Binds the current channel as the channel where the bk_week data is sent and updated in. No binding means the only way to view the data is using `/bk_week_get`.
### **Examples** ### **Examples**
``` ```
+3
View File
@@ -4,6 +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`
Adds a vote to the post. (see `/bk_week_vote` in the Discord help).
- **Anyone can use this command.**
### **Examples** ### **Examples**
``` ```
"u/ByteDiceAssistant bk_week_add" "u/ByteDiceAssistant bk_week_add"
+29
View File
@@ -0,0 +1,29 @@
{
"bk1": {
"discord": [
697149665166229614,
526842473062989864,
1171015493537247273,
1156779840956010496,
1021944396473716847,
1085622021024653362,
1223480842525872148,
1155349278655520858
],
"reddit": [
]
},
"bk2": {
"discord": [
1004132854227292272,
1031352334745354282,
1173039143245332520,
1281040971710201857,
772049174672703528
],
"reddit": [
]
}
}
+7 -3
View File
@@ -1,5 +1,4 @@
{ {
"file_created_correctly": true,
"bk_weekly_art_posts": { "bk_weekly_art_posts": {
"EXAMPLE VALUE": { "EXAMPLE VALUE": {
"post_data": { "post_data": {
@@ -9,7 +8,11 @@
"media_type": null, "media_type": null,
"media_urls": [] "media_urls": []
}, },
"nominated_by_human": true, "votes": {
"voters_re": ["bytedice"],
"voters_dc": [0],
"mod_voters": [0]
},
"added": { "added": {
"by_human": false, "by_human": false,
"by_bot": true "by_bot": true
@@ -21,7 +24,8 @@
}, },
"EXAMPLE VALUE DELETED": { "EXAMPLE VALUE DELETED": {
"removed": true, "removed": true,
"removed_by": "ME!!!!" "removed_by": "ME!!!!",
"remove_reason": "i HATED that post >:("
} }
} }
} }
+107 -34
View File
@@ -17,6 +17,20 @@ enum HelpOptions {
} }
fn is_bk_mod(mod_list: Value, uid: u64) -> bool {
let obj = mod_list.as_object().unwrap();
let bk1_arr = obj["bk1"]["discord"].as_array().unwrap();
let bk2_arr = obj["bk2"]["discord"].as_array().unwrap();
return bk1_arr.contains(&json!(uid)) || bk2_arr.contains(&json!(uid));
}
async fn not_bk_mod_msg(ctx: Context<'_>) {
send_msg(ctx, "Permission denied: You are not a moderator of r/boykisser or r/boykisser2".to_string(), true, true).await;
}
#[poise::command( #[poise::command(
slash_command, slash_command,
prefix_command prefix_command
@@ -48,11 +62,11 @@ pub async fn bk_week_help(
#[poise::command(slash_command, prefix_command, guild_only)] #[poise::command(slash_command, prefix_command)]
/// Retrieves the data of a single post just for you. The data has to be within the database to work. /// Retrieves the data of a single post just for you. The data has to be within the database to work.
pub async fn bk_week_get( pub async fn bk_week_get(
ctx: Context<'_>, ctx: Context<'_>,
#[description = "The post URL"] url: String #[description = "The post URL."] url: String
) -> Result<(), Error> ) -> Result<(), Error>
{ {
data::update_re_data(ctx.data()).await; data::update_re_data(ctx.data()).await;
@@ -68,10 +82,10 @@ pub async fn bk_week_get(
async fn get_reddit_data(ctx: Context<'_>) -> Result<Value, Error> { async fn get_reddit_data(ctx: Context<'_>) -> Result<Value, Error> {
let data_lock = ctx.data().reddit_data.lock().await; let data_lock = ctx.data().reddit_data.lock().await;
match data_lock.as_ref() { return match data_lock.as_ref() {
Some(data) => Ok(data.clone()), Some(data) => Ok(data.clone()),
None => Err("Reddit data is corrupted".into()), None => Err("Reddit data is corrupted".into()),
} };
} }
@@ -146,14 +160,19 @@ async fn send_data_corrupted_message(ctx: Context<'_>, url: &str) {
#[poise::command(slash_command, prefix_command, guild_only)] #[poise::command(slash_command, prefix_command)]
/// Fetches a post from Reddit and adds it to the database. /// Fetches a post from Reddit and adds it to the database.
pub async fn bk_week_add( pub async fn bk_week_add(
ctx: Context<'_>, ctx: Context<'_>,
#[description = "The post URL"] url: String, #[description = "The post URL."] url: String,
#[description = "Wether to approve it after adding it"] approve: Option<bool> #[description = "Wether to approve it after adding it"] approve: Option<bool>
) -> Result<(), Error> ) -> Result<(), Error>
{ {
if !is_bk_mod(ctx.data().bk_mods_json.clone(), ctx.author().id.get()) {
not_bk_mod_msg(ctx).await;
return Ok(());
}
data::update_re_data(ctx.data()).await; data::update_re_data(ctx.data()).await;
let reddit_data = get_reddit_data(ctx).await.unwrap(); let reddit_data = get_reddit_data(ctx).await.unwrap();
@@ -206,15 +225,21 @@ async fn send_updated_msg(ctx: Context<'_>, url: &str) {
#[poise::command(slash_command, prefix_command, guild_only)] #[poise::command(slash_command, prefix_command)]
/// Removes a post from the database. It will show who last removed it. /// Removes a post from the database. It will show who last removed it.
pub async fn bk_week_remove( pub async fn bk_week_remove(
ctx: Context<'_>, ctx: Context<'_>,
#[description = "The post URL"] url: String #[description = "The post URL."] url: String,
#[description = "The reason of the removal."] reason: Option<String>
) -> Result<(), Error> ) -> Result<(), Error>
{ {
if !is_bk_mod(ctx.data().bk_mods_json.clone(), ctx.author().id.get()) {
not_bk_mod_msg(ctx).await;
return Ok(());
}
let auth = &ctx.author().name; let auth = &ctx.author().name;
let r = send_cmd_json("remove_post_url", json!([&url, &auth])).await.unwrap(); let r = send_cmd_json("remove_post_url", json!([&url, &auth, &reason])).await.unwrap();
if r["value"].as_bool().unwrap() { if r["value"].as_bool().unwrap() {
send_msg( send_msg(
@@ -234,17 +259,23 @@ pub async fn bk_week_remove(
#[poise::command(slash_command, prefix_command, guild_only)] #[poise::command(slash_command, prefix_command)]
/// Approves a post in the database. Approving posts tells the bot that it's original. /// Approves a post in the database. Approving posts tells the bot that it's original.
pub async fn bk_week_approve( pub async fn bk_week_approve(
ctx: Context<'_>, ctx: Context<'_>,
#[description = "The post URL"] url: String #[description = "The post URL."] url: String,
#[description = "Wether to approve or disapprove the post"] disapprove: Option<bool>
) -> Result<(), Error> ) -> Result<(), Error>
{ {
if !is_bk_mod(ctx.data().bk_mods_json.clone(), ctx.author().id.get()) {
not_bk_mod_msg(ctx).await;
return Ok(());
}
data::update_re_data(ctx.data()).await; data::update_re_data(ctx.data()).await;
let reddit_data = get_reddit_data(ctx).await.unwrap(); let reddit_data = get_reddit_data(ctx).await.unwrap();
approve_cmd(ctx, &url, &reddit_data, true).await; approve_cmd(ctx, &url, &reddit_data, !disapprove.unwrap_or_else(|| false)).await;
return Ok(()); return Ok(());
} }
@@ -277,27 +308,9 @@ async fn approve_cmd(ctx: Context<'_>, url: &str, reddit_data: &Value, approve:
#[poise::command(slash_command, prefix_command, guild_only)]
/// Opposite effects of `/bk_week_approve`.
pub async fn bk_week_disapprove(
ctx: Context<'_>,
#[description = "The post URL"] url: String
) -> Result<(), Error>
{
data::update_re_data(ctx.data()).await;
let reddit_data = get_reddit_data(ctx).await.unwrap();
approve_cmd(ctx, &url, &reddit_data, false).await;
return Ok(());
}
#[poise::command(slash_command, prefix_command, default_member_permissions = "ADMINISTRATOR", guild_only)] #[poise::command(slash_command, prefix_command, default_member_permissions = "ADMINISTRATOR", guild_only)]
/// Sets the channel where the bot will dump all log info. It's recommended to only run this once. /// Sets the channel where the bot will dump all log info. It's recommended to only run this once.
pub async fn bk_week_bind( pub async fn bk_admin_bind(
ctx: Context<'_> ctx: Context<'_>
) -> Result<(), Error> ) -> Result<(), Error>
{ {
@@ -333,23 +346,23 @@ pub async fn bk_week_update(
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", json!([])).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();
let c_id = get_c_id(ctx).await.unwrap_or_else(|| 0); let c_id = get_c_id(ctx).await.unwrap_or_else(|| 0);
p_text = update_progress(ctx, progress.clone().unwrap(), p_text.clone(), format!("\nReading messages in <#{}>...", c_id)).await;
if c_id == 0 { if c_id == 0 {
send_msg(ctx, "Could not find bk_week_channel in data!\nHint: Run `/bk_week_bind` in a (preferably read-only) channel.".to_string(), true, true).await; send_msg(ctx, "Could not find bk_week_channel in data!\nHint: Run (or tell an admin to run) `/bk_admin_bind` in a (preferably read-only) channel.".to_string(), true, true).await;
return Ok(()); return Ok(());
} }
p_text = update_progress(ctx, progress.clone().unwrap(), p_text.clone(), format!("\nReading messages in <#{}>...", c_id)).await;
let msgs = read_msgs(ctx, c_id).await; let msgs = read_msgs(ctx, c_id).await;
p_text = update_progress(ctx, progress.clone().unwrap(), p_text.clone(), "\nParsing messages to JSON...".to_string()).await; p_text = update_progress(ctx, progress.clone().unwrap(), p_text.clone(), "\nParsing messages to JSON...".to_string()).await;
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_weekly_art_posts"].as_object().unwrap();
for url in weekly_art.keys() { for url in weekly_art.keys() {
@@ -399,6 +412,7 @@ pub async fn bk_week_update(
} }
update_progress(ctx, progress.clone().unwrap(), p_text.clone(), "\n## Done!".to_string()).await; update_progress(ctx, progress.clone().unwrap(), p_text.clone(), "\n## Done!".to_string()).await;
return Ok(()); return Ok(());
} }
@@ -458,6 +472,8 @@ async fn read_msgs(ctx: Context<'_>, c_id: u64) -> Vec<Message> {
} }
// TODO: all posts get updated for some reason (i think, debug print pls)
// TODO: also doesn't edit messages if they got removed
async fn msgs_to_json<'a>(msgs: Vec<Message>, reddit_data: &'a Value) -> Value { async fn msgs_to_json<'a>(msgs: Vec<Message>, reddit_data: &'a Value) -> Value {
let mut msgs_json: Value = json!({"no_change": {}, "updated": {}, "removed": {}, "duplicates": {}}); let mut msgs_json: Value = json!({"no_change": {}, "updated": {}, "removed": {}, "duplicates": {}});
@@ -509,6 +525,7 @@ async fn msgs_to_json<'a>(msgs: Vec<Message>, reddit_data: &'a Value) -> Value {
if u_json["added"] != re_url["added"] if u_json["added"] != re_url["added"]
|| u_json["approved"] != re_url["approved"] || u_json["approved"] != re_url["approved"]
|| u_json["post_data"]["upvotes"] != re_url["post_data"]["upvotes"] || u_json["post_data"]["upvotes"] != re_url["post_data"]["upvotes"]
|| u_json["votes"] != re_url["votes"]
{ {
u_json.as_object_mut().unwrap().insert("msg_id".to_string(), Value::String(msg.id.clone().to_string())); u_json.as_object_mut().unwrap().insert("msg_id".to_string(), Value::String(msg.id.clone().to_string()));
@@ -526,3 +543,59 @@ async fn msgs_to_json<'a>(msgs: Vec<Message>, reddit_data: &'a Value) -> Value {
return msgs_json; return msgs_json;
} }
#[poise::command(slash_command, prefix_command)]
pub async fn bk_week_vote(
ctx: Context<'_>,
#[description = "The post URL."] url: String,
#[description = "Wether to undo your vote or not"] un_vote: Option<bool>
) -> Result<(), Error>
{
data::update_re_data(ctx.data()).await;
let uid = ctx.author().id.get();
let re_data = get_reddit_data(ctx).await.unwrap();
let post_data = re_data["bk_weekly_art_posts"].clone();
let unw_vote = un_vote.unwrap_or_else(|| false);
if post_data.get(&url).is_none() {
send_post_not_found_message(ctx, &url).await;
return Ok(());
}
let url_data = &post_data[&url];
let is_mod = is_bk_mod(ctx.data().bk_mods_json.clone(), ctx.author().id.get());
let voters_dc = url_data["votes"]["voters_dc"].as_array().unwrap();
let mod_voters = url_data["votes"]["mod_voters"].as_array().unwrap();
let voters = if is_mod { mod_voters } else { voters_dc };
if voters.contains(&json!(uid)) && !unw_vote {
send_msg(ctx, "Couldn't cast a vote: You have already voted on this post!".to_string(), true, true).await;
return Ok(());
}
else if !voters.contains(&json!(uid)) && unw_vote {
send_msg(ctx, "Couldn't remove your vote: You haven't voted on this post yet!".to_string(), true, true).await;
return Ok(());
}
let r = send_cmd_json("set_vote_post", json!([url, uid, is_mod, true, unw_vote])).await.unwrap();
let unw_r = r["value"].as_bool().unwrap();
if unw_r && !unw_vote && is_mod {
send_msg(ctx, "Successfully voted (as moderator vote)!".to_string(), true, true).await;
}
else if unw_r && !unw_vote && !is_mod {
send_msg(ctx, "Successfully voted!".to_string(), true, true).await;
}
else if unw_r && unw_vote {
send_msg(ctx, "Successfully removed vote!".to_string(), true, true).await;
}
else {
send_msg(ctx, "Failed to vote/un-vote: Unknown internal error".to_string(), true, true).await;
}
return Ok(());
}
+8 -4
View File
@@ -23,7 +23,6 @@ use serde_json;
// TODO: bot command permissions // TODO: bot command permissions
// TODO: bk_mod verification system
#[derive(Parser, Serialize, Clone)] #[derive(Parser, Serialize, Clone)]
@@ -49,6 +48,7 @@ struct Data {
byte_dice_id: u64, byte_dice_id: u64,
reddit_data: Mutex<Option<Value>>, reddit_data: Mutex<Option<Value>>,
discord_data: Mutex<Option<Value>>, discord_data: Mutex<Option<Value>>,
bk_mods_json: Value,
args: Args args: Args
// TODO: schedules // TODO: schedules
} }
@@ -66,6 +66,7 @@ async fn main() {
if args.test { println!("----- USING TEST BOT -----"); } if args.test { println!("----- USING TEST BOT -----"); }
if args.dev { println!("----- DEV MODE ENABLED -----"); } if args.dev { println!("----- DEV MODE ENABLED -----"); }
if args.dev && args.wipe { println!("----- \"DON'T WORRY ABOUT IT\" MODE ENABLED -----"); }
if args.py && !args.rs { if args.py && !args.rs {
println!("----- PYTHON ONLY MODE -----"); println!("----- PYTHON ONLY MODE -----");
@@ -116,12 +117,15 @@ async fn start(args: Args) {
async fn gen_data(args: Args) -> Data { async fn gen_data(args: Args) -> Data {
let ball_classic_str = std::fs::read_to_string("./data/8-ball_classic.txt").unwrap(); let ball_classic_str = std::fs::read_to_string("./data/8-ball_classic.txt").unwrap();
let ball_quirk_str = std::fs::read_to_string("./data/8-ball_quirky.txt").unwrap(); let ball_quirk_str = std::fs::read_to_string("./data/8-ball_quirky.txt").unwrap();
let bk_mods_str = std::fs::read_to_string("./data/bk_mods.json").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 bk_mods: Value = serde_json::from_str(&bk_mods_str).unwrap();
let data = Data { let data = Data {
ball_prompts: [ball_classic, ball_quirk], ball_prompts: [ball_classic, ball_quirk],
bk_mods_json: bk_mods,
byte_dice_id: 697149665166229614, byte_dice_id: 697149665166229614,
reddit_data: None.into(), reddit_data: None.into(),
discord_data: None.into(), discord_data: None.into(),
@@ -168,9 +172,9 @@ async fn gen_bot(data: Data, args: Args) -> Client {
bk_week_cmds::bk_week_add(), bk_week_cmds::bk_week_add(),
bk_week_cmds::bk_week_remove(), bk_week_cmds::bk_week_remove(),
bk_week_cmds::bk_week_approve(), bk_week_cmds::bk_week_approve(),
bk_week_cmds::bk_week_disapprove(), bk_week_cmds::bk_admin_bind(),
bk_week_cmds::bk_week_bind(), bk_week_cmds::bk_week_update(),
bk_week_cmds::bk_week_update() bk_week_cmds::bk_week_vote()
], ],
event_handler: events::event_handler, event_handler: events::event_handler,
..Default::default() ..Default::default()
+24 -3
View File
@@ -137,15 +137,25 @@ pub async fn edit_msg(
pub fn embed_post(post_data: &Value, url: &str, empheral: bool) -> EmbedOptions { pub fn embed_post(post_data: &Value, url: &str, empheral: bool) -> EmbedOptions {
let media_type = &post_data["post_data"]["media_type"]; let media_type = &post_data["post_data"]["media_type"];
let re_votes = &post_data["votes"]["voters_re"].as_array().unwrap().len();
let dc_votes = &post_data["votes"]["voters_dc"].as_array().unwrap().len();
let desc_str = format!( let desc_str = format!(
r#"Sorted by what I think will be most important r#"Sorted by what I think will be most important
Spoilers and vote length anonymizer for fair review! Spoilers and vote length anonymizer for fair review!
## Post Data: ## Post Data:
**Media type:** `{}` **Media type:** `{}`
**Upvotes:** ||`{:>6}`|| **Post upvotes:** ||`{:>6}`||
**URL:** ||<{}>|| **URL:** ||<{}>||
**Media URLS:** **Media URLS:**
{} {}
## Voting data:
**Moderator votes:** ||`{:>6}`||
**Community votes:** ||`{:>6}`||
* ||**From Reddit:** `{:>6}`||
* ||**From Discord:** `{:>6}`||
## Listing Data: ## Listing Data:
**Added by:** `{{ human: {}, bot: {} }}` **Added by:** `{{ human: {}, bot: {} }}`
**Approved by:** `{{ human: {}, bot: [not implemented] }}`"#, **Approved by:** `{{ human: {}, bot: [not implemented] }}`"#,
@@ -153,6 +163,12 @@ pub fn embed_post(post_data: &Value, url: &str, empheral: bool) -> EmbedOptions
post_data["post_data"]["upvotes"].as_i64().unwrap(), post_data["post_data"]["upvotes"].as_i64().unwrap(),
url, url,
post_data["post_data"]["media_urls"].as_array().unwrap().iter().map(|s| format!("* ||<{}>||", s.as_str().unwrap())).collect::<Vec<_>>().join("\n"), post_data["post_data"]["media_urls"].as_array().unwrap().iter().map(|s| format!("* ||<{}>||", s.as_str().unwrap())).collect::<Vec<_>>().join("\n"),
post_data["votes"]["mod_voters"].as_array().unwrap().len(),
re_votes + dc_votes,
re_votes,
dc_votes,
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 { "" }
@@ -169,7 +185,7 @@ pub fn embed_post(post_data: &Value, url: &str, empheral: bool) -> EmbedOptions
return EmbedOptions { return EmbedOptions {
title: Some(post_data["post_data"]["title"].as_str().unwrap().to_string()), title: Some(post_data["post_data"]["title"].as_str().unwrap().to_string()),
desc: format!("{}\nJSON: ||`{}`||", trimmed, serde_json::to_string(&json_min).unwrap()), desc: format!("{}\n\nJSON: ||`{}`||", trimmed, serde_json::to_string(&json_min).unwrap()),
col: Some(DEFAULT_DC_COL), col: Some(DEFAULT_DC_COL),
url: Some(url.to_string()), url: Some(url.to_string()),
ts: Some(Timestamp::from_unix_timestamp(post_data["post_data"]["date_unix"].as_i64().unwrap()).unwrap()), ts: Some(Timestamp::from_unix_timestamp(post_data["post_data"]["date_unix"].as_i64().unwrap()).unwrap()),
@@ -185,7 +201,12 @@ pub fn embed_post(post_data: &Value, url: &str, empheral: bool) -> EmbedOptions
pub fn embed_post_removed(post_data: &Value, url: &str, empheral: bool) -> EmbedOptions { pub fn embed_post_removed(post_data: &Value, url: &str, empheral: bool) -> EmbedOptions {
return EmbedOptions { return EmbedOptions {
title: Some("REMOVED!".to_string()), title: Some("REMOVED!".to_string()),
desc: format!("## Removed by `{}`\nJSON: ||`{}`||", post_data["removed_by"].as_str().unwrap(), serde_json::to_string(&post_data).unwrap()), desc: format!(
"## Removed by `{}`\n**Reason:** {}\n\nJSON: ||`{}`||",
post_data["removed_by"].as_str().unwrap(),
post_data["remove_reason"].as_str().unwrap(),
serde_json::to_string(&post_data).unwrap()
),
col: Some(REMOVED_DC_COL), col: Some(REMOVED_DC_COL),
url: Some(url.to_string()), url: Some(url.to_string()),
empheral, empheral,
+52 -14
View File
@@ -18,7 +18,9 @@ class PostData:
date_unix: int, date_unix: int,
media_type: str, media_type: str,
media_urls: list[str], media_urls: list[str],
nominated_by_human: bool = False, voters_re: list[str] = [],
voters_dc: list[int] = [],
mod_voters: list[int] = [],
added_by_human: bool = False, added_by_human: bool = False,
added_by_bot: bool = False, added_by_bot: bool = False,
approved_by_human: bool = False, approved_by_human: bool = False,
@@ -30,7 +32,9 @@ class PostData:
self.date_unix = date_unix self.date_unix = date_unix
self.media_type = media_type self.media_type = media_type
self.media_urls = media_urls self.media_urls = media_urls
self.nominated_by_human = nominated_by_human self.voters_re = voters_re
self.voters_dc = voters_dc
self.mod_voters = mod_voters
self.added_by_human = added_by_human self.added_by_human = added_by_human
self.added_by_bot = added_by_bot self.added_by_bot = added_by_bot
self.approved_by_human = approved_by_human self.approved_by_human = approved_by_human
@@ -45,7 +49,11 @@ class PostData:
"media_type": self.media_type, "media_type": self.media_type,
"media_urls": self.media_urls "media_urls": self.media_urls
}, },
"nominated_by_human": self.nominated_by_human, "votes": {
"voters_re": self.voters_re,
"voters_dc": self.voters_dc,
"mod_voters": self.mod_voters
},
"added": { "added": {
"by_human": self.added_by_human, "by_human": self.added_by_human,
"by_bot": self.added_by_bot "by_bot": self.added_by_bot
@@ -84,9 +92,6 @@ def read_data(bot: botPy.Bot) -> bool:
json_data = json.loads(data_str) json_data = json.loads(data_str)
bot.data = json_data bot.data = json_data
if not bot.data["file_created_correctly"]:
raise Exception("reddit_data.json file wasn't created properly. Delete the file and retry.")
return True return True
@@ -104,9 +109,8 @@ def add_post_to_data(bot: botPy.Bot, new_data: PostData, bypass_conditions: bool
py_print(f"Added post \"{new_data.url}\" (Conditions bypassed)") py_print(f"Added post \"{new_data.url}\" (Conditions bypassed)")
return True return True
# not sure what this is for
updated = False updated = False
if "removed" not in bot.data[BK_WEEKLY][new_data.url]:
updated = new_data.upvotes != bot.data[BK_WEEKLY][new_data.url]["post_data"]
if new_data.url not in bot.data[BK_WEEKLY] or updated: if new_data.url not in bot.data[BK_WEEKLY] or updated:
bot.data[BK_WEEKLY][new_data.url] = new_data.to_json() bot.data[BK_WEEKLY][new_data.url] = new_data.to_json()
@@ -114,12 +118,11 @@ def add_post_to_data(bot: botPy.Bot, new_data: PostData, bypass_conditions: bool
py_print(f"Added post \"{new_data.url}\"") py_print(f"Added post \"{new_data.url}\"")
return True return True
elif "removed" in bot.data[BK_WEEKLY][new_data.url]: if "removed" not in bot.data[BK_WEEKLY][new_data.url]:
py_print(f"Failed to add post \"{new_data.url}\": Removed flag is True.") updated = new_data.upvotes != bot.data[BK_WEEKLY][new_data.url]["post_data"]
return False
else: else:
py_print(f"Failed to add post \"{new_data.url}\": Already exists.") py_print(f"Failed to add post \"{new_data.url}\": Removed flag is True.")
return False return False
@@ -131,9 +134,44 @@ def set_approve_post(bot: botPy.Bot, approved: bool, url: str) -> bool:
return False return False
def remove_post(bot: botPy.Bot, url: str, removed_by: str = "UNKNOWN") -> bool: def remove_post(bot: botPy.Bot, url: str, removed_by: str = "UNKNOWN", reason: str = "None") -> bool:
if url in bot.data[BK_WEEKLY]: if url in bot.data[BK_WEEKLY]:
bot.data[BK_WEEKLY][url] = { "removed": True, "removed_by": removed_by } bot.data[BK_WEEKLY][url] = { "removed": True, "removed_by": removed_by, "remove_reason": reason }
return True return True
else: else:
return False return False
def set_vote_post(
bot: botPy.Bot,
url: str,
user: str | int,
mod_vote: bool = False,
from_dc: bool = False,
remove_vote: bool = False,
) -> bool:
if url not in bot.data[BK_WEEKLY]:
return False
votes = bot.data[BK_WEEKLY][url]["votes"]
re_voters: set[str] = set(votes["voters_re"])
dc_voters: set[int] = set(votes["voters_dc"])
mod_voters: set[int] = set(votes["mod_voters"])
target_voters = mod_voters if mod_vote else (dc_voters if from_dc else re_voters)
if remove_vote:
if user not in target_voters:
return False
target_voters.remove(user)
else:
if user in target_voters:
return False
target_voters.add(user)
bot.data[BK_WEEKLY][url]["votes"]["voters_re"] = list(re_voters)
bot.data[BK_WEEKLY][url]["votes"]["voters_dc"] = list(dc_voters)
bot.data[BK_WEEKLY][url]["votes"]["mod_voters"] = list(mod_voters)
return True
-1
View File
@@ -50,7 +50,6 @@ async def add_new_posts(bot: botPy.Bot) -> bool:
f" {without_media} had no media, " + f" {without_media} had no media, " +
f"and {not_added} weren't added because they are removed or already existed") f"and {not_added} weren't added because they are removed or already existed")
data.write_data(bot)
return True return True
+1
View File
@@ -68,6 +68,7 @@ async def json_to_func(v: dict, bot: botPy.Bot) -> dict:
case "add_post_url": result = result_json(await posts.add_post_url(bot, *v["args"])) case "add_post_url": result = result_json(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": result = result_json(data.remove_post(bot, *v["args"]))
case "set_approve_post": result = result_json(data.set_approve_post(bot, *v["args"])) case "set_approve_post": result = result_json(data.set_approve_post(bot, *v["args"]))
case "set_vote_post": result = result_json(data.set_vote_post(bot, *v["args"]))
case "stop_praw": result = result_json(await bot.stop()) case "stop_praw": result = result_json(await bot.stop())
case _: value_supported = False case _: value_supported = False