handling old posts & /bk_week_top
This commit is contained in:
@@ -42,5 +42,5 @@ You can install Python modules by running `$ pip install {module}` or `$ python
|
|||||||
### How to run:
|
### How to run:
|
||||||
* Download the code (and extract if needed).
|
* Download the code (and extract if needed).
|
||||||
* Open a terminal and CD to the downloaded folder.
|
* Open a terminal and CD to the downloaded folder.
|
||||||
* Run `$ cargo run`. There are more options when running. You can view a list of those using `$ cargo run -- --help`.
|
* Run `$ cargo run`. There are more options when running. You can view a list of those using `$ cargo run -- --help` or `$ cargo run -- -h`.
|
||||||
* If you want to only run the Python code, you can either run `$ cargo run -- --py`, or `$ python ./src/python/main.py`. The second option is recommended for better output.
|
* If you want to only run the Python code, you can either run `$ cargo run -- --py`, or `$ python ./src/python/main.py`. The second option is recommended for better error output.
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
<!-- - [x] ~~Multithread so it can run both Discord and Reddit bot!!!~~ -->
|
<!-- - [x] ~~Multithread so it can run both Discord and Reddit bot!!!~~ -->
|
||||||
<!-- - [x] 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. -->
|
<!-- - [x] Some kind of voting system. -->
|
||||||
- [ ] `/bk_week_top [category] [amount]` to get the top N posts in a category (e.g upvotes)
|
<!-- - [x] ~~`/bk_week_top [category] [amount]` to get the top N posts in a category (e.g upvotes)~~ -->
|
||||||
<!-- - [x] ~~`/bk_cfg_sr [subreddit]` to change the target subreddit(s)~~ -->
|
<!-- - [x] ~~`/bk_cfg_sr [subreddit]` to change the target subreddit(s)~~ -->
|
||||||
- [ ] Allow updating the data autonomously and via manual commands.
|
- [ ] Allow updating the data autonomously and via manual commands.
|
||||||
<!-- - [ ] 10-minute schedule for updating Discord channel (IMPOSSIBLE / REALLY FUCKING HARD) -->
|
<!-- - [ ] 10-minute schedule for updating Discord channel (IMPOSSIBLE / REALLY FUCKING HARD) -->
|
||||||
@@ -19,9 +19,7 @@
|
|||||||
<!-- - [x] ~~Manually approve posts via `/bk_week_approve [url]`~~ -->
|
<!-- - [x] ~~Manually approve posts via `/bk_week_approve [url]`~~ -->
|
||||||
<!-- - [x] ~~Manually un-approve posts via `/bk_week_disapprove [url]`~~ -->
|
<!-- - [x] ~~Manually un-approve posts via `/bk_week_disapprove [url]`~~ -->
|
||||||
<!-- - [x] ~~Automatically add scraped posts to JSON~~ -->
|
<!-- - [x] ~~Automatically add scraped posts to JSON~~ -->
|
||||||
- [ ] Automatically remove posts older than 7 days from JSON
|
<!-- - [x] ~~Remove posts (from data) that are older than 7 days~~ --> // need python function to remove data about them
|
||||||
- [x] ~~Function~~
|
|
||||||
- [ ] Automate
|
|
||||||
- [ ] Automatically approve posts that don't 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~~ -->
|
||||||
|
|||||||
+10
-10
@@ -1,29 +1,29 @@
|
|||||||
# Discord Commands
|
# Discord Commands
|
||||||
|
`[this means it's a required argument]`
|
||||||
|
`<this means it's an optional argument>`
|
||||||
|
|
||||||
## `/bk_week_get [url]`
|
## `/bk_week_get [url]`
|
||||||
Shows info about a single post.
|
Shows info about a single post.
|
||||||
## `/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>`**: (OPTIONAL) `true` or `false`, determines whether to approve the post when added.
|
- **`<approve>`**: `true` or `false`, determines whether to automatically approve the post when added.
|
||||||
## `/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] <disapprove>`
|
## `/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.
|
||||||
- **`<disapprove>`**: (OPTIONAL) `true` or `false`, set to `true` if you want to undo an approval.
|
- **`<disapprove>`**: set to `true` if you want to undo an approval.
|
||||||
## `/bk_week_vote [url] <remove_vote>`
|
## `/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.
|
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.
|
You can vote on as many posts as you want, but only once per post.
|
||||||
## `/bk_week_update`
|
## `/bk_week_update <only_add> <max_age>`
|
||||||
Updates all data in the bound Discord channel. It's recommended to only run this if absolutely needed to.
|
Updates all data in the bound Discord channel. It's recommended to only run this if absolutely needed to.
|
||||||
|
- **`<only_add>`**: If it's `true`, the bot will only add new posts, not remove or update them.
|
||||||
|
- **`<max_age>`**: The bot will remove any post older than `max_age` days. (0 means infinite.)
|
||||||
|
## `/bk_week_top [category] <amount>`
|
||||||
|
Shows you the top posts within a category, such as upvotes. (max 10 posts, default is 3)
|
||||||
## `/bk_admin_bind`
|
## `/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`.
|
||||||
## `/bk_week_help [option]`
|
## `/bk_week_help [option]`
|
||||||
Shows a help text.
|
Shows a help text.
|
||||||
- **`[option]`**: `Discord` or `Reddit`, determines what help text to send.
|
|
||||||
### **Examples**
|
|
||||||
```
|
|
||||||
/bk_week_add https://reddit.com/post_url false
|
|
||||||
/bk_week_add https://reddit.com/post_url
|
|
||||||
/bk_week_approve https://reddit.com/post_url
|
|
||||||
```
|
|
||||||
# Things to note
|
# Things to note
|
||||||
* This bot uses shortURLs when storing posts. If you want to access posts, you should use their shortURL. You can get a Reddit shortURL by running `/re_shorturl [url]`.
|
* This bot uses shortURLs when storing posts. If you want to access posts, you should use their shortURL. You can get a Reddit shortURL by running `/re_shorturl [url]`.
|
||||||
+133
-29
@@ -3,9 +3,12 @@ use crate::{cmds, rs_println, websocket, Context, Data, Error, BK_WEEK};
|
|||||||
use crate::messages::*;
|
use crate::messages::*;
|
||||||
use crate::data::{self, dc_bind_bk};
|
use crate::data::{self, dc_bind_bk};
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
use poise::serenity_prelude::{ChannelId, EditMessage, GetMessages, Http, Message, MessageId, UserId};
|
use poise::serenity_prelude::{ChannelId, EditMessage, GetMessages, Http, Message, MessageId, UserId};
|
||||||
|
use poise::ReplyHandle;
|
||||||
use serde_json::{json, Map, Value};
|
use serde_json::{json, Map, Value};
|
||||||
|
|
||||||
|
|
||||||
@@ -14,6 +17,13 @@ enum HelpOptions {
|
|||||||
Discord,
|
Discord,
|
||||||
Reddit
|
Reddit
|
||||||
}
|
}
|
||||||
|
#[derive(poise::ChoiceParameter, PartialEq)]
|
||||||
|
enum TopCategory {
|
||||||
|
Upvotes,
|
||||||
|
ModVotes,
|
||||||
|
Oldest,
|
||||||
|
Newest
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fn is_bk_mod(mod_list: Value, uid: u64) -> bool {
|
fn is_bk_mod(mod_list: Value, uid: u64) -> bool {
|
||||||
@@ -342,20 +352,21 @@ async fn send_server_not_in_data_msg(ctx: Context<'_>) {
|
|||||||
/// Updates all logs
|
/// Updates all logs
|
||||||
pub async fn bk_week_update(
|
pub async fn bk_week_update(
|
||||||
ctx: Context<'_>,
|
ctx: Context<'_>,
|
||||||
#[description = "Only adds new posts, leaves everything else unchanged."] only_add: Option<bool>
|
#[description = "Only adds new posts, leaves everything else unchanged."] only_add: Option<bool>,
|
||||||
|
#[description = "The max age of a post (in days). Any post older than this will be removed. (0 is infinite.)"] max_age: Option<u16>
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error>
|
||||||
{
|
{
|
||||||
let http = ctx.http();
|
let http = ctx.http();
|
||||||
|
|
||||||
let executed = format!("(Executed `/bk_week_update`, author: `{}`)", ctx.author().name);
|
let mut p_text = "`/bk_week_update`:".to_string();
|
||||||
let mut p_text = executed.clone();
|
|
||||||
|
|
||||||
send_msg(ctx, MANDATORY_MSG.to_string(), true, true).await;
|
let progress = send_msg(ctx, p_text.clone(), true, true).await.unwrap();
|
||||||
|
p_text = update_progress(ctx, progress.clone(), p_text, "\nFetching new posts & updating data file...".to_string()).await;
|
||||||
|
|
||||||
let progress = http_send_msg(http, ctx.channel_id(), p_text.clone()).await.unwrap();
|
let max_age_u = max_age.unwrap_or_else(|| 8);
|
||||||
p_text = update_progress(ctx.http(), progress.clone(), p_text, "\nFetching new posts & updating data file...".to_string()).await;
|
let max_age_secs = max_age_u as u64 * (60 * 60 * 24);
|
||||||
|
|
||||||
send_cmd_json("add_new_posts", None).await;
|
send_cmd_json("add_new_posts", Some(json!([max_age_secs]))).await;
|
||||||
data::update_re_data(ctx.data()).await;
|
data::update_re_data(ctx.data()).await;
|
||||||
let r_data = get_reddit_data(ctx.data()).await.unwrap();
|
let r_data = get_reddit_data(ctx.data()).await.unwrap();
|
||||||
|
|
||||||
@@ -369,51 +380,56 @@ pub async fn bk_week_update(
|
|||||||
let c_id = c_id_u.unwrap();
|
let c_id = c_id_u.unwrap();
|
||||||
|
|
||||||
// Reading messages
|
// Reading messages
|
||||||
p_text = update_progress(http, progress.clone(), p_text.clone(), format!("✅\nReading messages in <#{}>...", c_id)).await;
|
p_text = update_progress(ctx, progress.clone(), p_text.clone(), format!("✅\nReading messages in <#{}>...", c_id)).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(http, progress.clone(), p_text.clone(), "✅\nParsing messages to JSON...".to_string()).await;
|
p_text = update_progress(ctx, progress.clone(), 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, max_age_secs).await;
|
||||||
|
|
||||||
// Adding new posts
|
// Adding new posts
|
||||||
p_text = update_progress(http, progress.clone(), p_text.clone(), "✅\nAdding new posts...".to_string()).await;
|
p_text = update_progress(ctx, progress.clone(), p_text.clone(), "✅\nAdding new posts...".to_string()).await;
|
||||||
let weekly_art = r_data[BK_WEEK].as_object().unwrap();
|
let weekly_art = r_data[BK_WEEK].as_object().unwrap();
|
||||||
add_posts(http, c_id, weekly_art, &msgs_json).await;
|
add_posts(http, c_id, weekly_art, &msgs_json).await;
|
||||||
|
|
||||||
// Stop if only_add
|
// Stop if only_add
|
||||||
if only_add.unwrap_or_else(|| false) {
|
if only_add.unwrap_or_else(|| false) {
|
||||||
send_msg(ctx, "`/bk_week_update`\n## Done!".to_string(), true, true).await;
|
send_msg(ctx, "`/bk_week_update`\n## Done!".to_string(), true, true).await;
|
||||||
update_progress(http, progress.clone(), String::new(), executed).await;
|
update_progress(ctx, progress.clone(), p_text, "✅\n## Done!".to_string()).await;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Editing updated posts
|
// Editing updated posts
|
||||||
p_text = update_progress(http, progress.clone(), p_text.clone(), "✅\nEditing updated posts...".to_string()).await;
|
p_text = update_progress(ctx, progress.clone(), p_text.clone(), "✅\nEditing updated posts...".to_string()).await;
|
||||||
edit_posts(http, c_id, weekly_art, &msgs_json).await;
|
edit_posts(http, c_id, weekly_art, &msgs_json).await;
|
||||||
|
|
||||||
// Removing removed posts
|
// Removing removed posts
|
||||||
p_text = update_progress(http, progress.clone(), p_text.clone(), "✅\nRemoving removed posts...".to_string()).await;
|
p_text = update_progress(ctx, progress.clone(), p_text.clone(), "✅\nRemoving removed posts...".to_string()).await;
|
||||||
remove_posts(http, c_id, weekly_art, &msgs_json).await;
|
remove_posts(http, c_id, weekly_art, &msgs_json).await;
|
||||||
|
|
||||||
|
// Removing old posts
|
||||||
|
if max_age_u > 0 {
|
||||||
|
p_text = update_progress(ctx, progress.clone(), p_text.clone(), format!("✅\nRemoving old posts (threshold: {}d)...", max_age_u)).await;
|
||||||
|
remove_old(http, c_id, &msgs_json).await;
|
||||||
|
send_cmd_json("remove_old_posts", Some(json!([max_age_secs]))).await;
|
||||||
|
}
|
||||||
|
|
||||||
// Removing duplicate posts
|
// Removing duplicate posts
|
||||||
update_progress(http, progress.clone(), p_text.clone(), "✅\nRemoving duplicate posts...".to_string()).await;
|
p_text = update_progress(ctx, progress.clone(), p_text.clone(), "✅\nRemoving duplicate posts...".to_string()).await;
|
||||||
remove_dupes(http, c_id, &msgs_json).await;
|
remove_dupes(http, c_id, &msgs_json).await;
|
||||||
|
|
||||||
// Done
|
// Done
|
||||||
|
update_progress(ctx, progress.clone(), p_text, "✅\n## Done!".to_string()).await;
|
||||||
send_msg(ctx, "`/bk_week_update`\n## Done!".to_string(), true, true).await;
|
send_msg(ctx, "`/bk_week_update`\n## Done!".to_string(), true, true).await;
|
||||||
update_progress(http, progress.clone(), String::new(), executed).await;
|
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async fn update_progress(http: &Http, p: Message, t: String, added_t: String) -> String {
|
async fn update_progress(ctx: Context<'_>, p: ReplyHandle<'_>, t: String, added_t: String) -> String {
|
||||||
let p_text = format!("{} {}", t, added_t);
|
let p_text = format!("{} {}", t, added_t);
|
||||||
|
|
||||||
let new_msg = EditMessage::new().content(&p_text);
|
edit_reply(ctx, p, p_text.clone()).await;
|
||||||
|
|
||||||
http_edit_msg(http, p, new_msg).await;
|
|
||||||
return p_text;
|
return p_text;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,7 +453,7 @@ async fn get_c_id(ctx: Context<'_>) -> Option<ChannelId> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn read_msgs(http: &Http, bot_id: UserId, c_id: ChannelId) -> Vec<Message> {
|
async fn read_msgs(http: &Http, bot_id: UserId, c_id: ChannelId) -> Vec<Message> {
|
||||||
let b = GetMessages::new().limit(100);
|
let b = GetMessages::new().limit(100);
|
||||||
let mut msgs = c_id.messages(http, b).await.unwrap();
|
let mut msgs = c_id.messages(http, b).await.unwrap();
|
||||||
msgs = msgs.into_iter().filter(|item| item.author.id == bot_id).collect();
|
msgs = msgs.into_iter().filter(|item| item.author.id == bot_id).collect();
|
||||||
@@ -466,8 +482,12 @@ pub async fn read_msgs(http: &Http, bot_id: UserId, c_id: ChannelId) -> Vec<Mess
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub 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, max_age: u64) -> Value {
|
||||||
let mut msgs_json: Value = json!({"no_change": {}, "updated": {}, "removed": {}, "duplicates": {}});
|
let mut msgs_json: Value = json!({"no_change": {}, "updated": {}, "removed": {}, "duplicates": {}, "old": {}});
|
||||||
|
let now = SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.expect("Time went backwards")
|
||||||
|
.as_secs() as u64;
|
||||||
|
|
||||||
for msg in msgs {
|
for msg in msgs {
|
||||||
if msg.embeds.len() == 0 { continue; }
|
if msg.embeds.len() == 0 { continue; }
|
||||||
@@ -475,7 +495,8 @@ pub async fn msgs_to_json<'a>(msgs: Vec<Message>, reddit_data: &'a Value) -> Val
|
|||||||
|
|
||||||
let url = msg.embeds[0].url.clone().unwrap();
|
let url = msg.embeds[0].url.clone().unwrap();
|
||||||
|
|
||||||
if ["no_change", "updated", "removed"]
|
// duplicates
|
||||||
|
if ["no_change", "updated", "removed", "old"]
|
||||||
.iter()
|
.iter()
|
||||||
.any(|key| msgs_json[key].as_object().unwrap().contains_key(&url))
|
.any(|key| msgs_json[key].as_object().unwrap().contains_key(&url))
|
||||||
{
|
{
|
||||||
@@ -500,20 +521,34 @@ pub async fn msgs_to_json<'a>(msgs: Vec<Message>, reddit_data: &'a Value) -> Val
|
|||||||
let mut u_json: Value = msg_json.unwrap();
|
let mut u_json: Value = msg_json.unwrap();
|
||||||
let re_url = &reddit_data[BK_WEEK][&url];
|
let re_url = &reddit_data[BK_WEEK][&url];
|
||||||
|
|
||||||
|
let post_date = re_url["post_data"]["date_unix"].as_u64().unwrap_or_else(|| 0);
|
||||||
|
|
||||||
|
// old
|
||||||
|
if now - post_date > max_age {
|
||||||
|
if let Some(obj) = msgs_json["old"].as_object_mut() {
|
||||||
|
obj.insert(url.clone(), json!(msg.id.get()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// removed
|
||||||
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() {
|
||||||
|
// no change
|
||||||
if let Some(obj) = msgs_json["no_change"].as_object_mut() {
|
if let Some(obj) = msgs_json["no_change"].as_object_mut() {
|
||||||
obj.insert(url.clone(), json!(msg.id.get()));
|
obj.insert(url.clone(), json!(msg.id.get()));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// removed
|
||||||
if let Some(obj) = msgs_json["removed"].as_object_mut() {
|
if let Some(obj) = msgs_json["removed"].as_object_mut() {
|
||||||
obj.insert(url.clone(), json!(msg.id.get()));
|
obj.insert(url.clone(), json!(msg.id.get()));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updated
|
||||||
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"]
|
||||||
@@ -527,6 +562,7 @@ pub async fn msgs_to_json<'a>(msgs: Vec<Message>, reddit_data: &'a Value) -> Val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// no change
|
||||||
if let Some(obj) = msgs_json["no_change"].as_object_mut() {
|
if let Some(obj) = msgs_json["no_change"].as_object_mut() {
|
||||||
obj.insert(url.clone(), json!(msg.id.get()));
|
obj.insert(url.clone(), json!(msg.id.get()));
|
||||||
}
|
}
|
||||||
@@ -536,9 +572,9 @@ pub async fn msgs_to_json<'a>(msgs: Vec<Message>, reddit_data: &'a Value) -> Val
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn add_posts(http: &Http, c_id: ChannelId, r_data: &Map<String, Value>, msgs_json: &Value) {
|
async fn add_posts(http: &Http, c_id: ChannelId, r_data: &Map<String, Value>, msgs_json: &Value) {
|
||||||
for url in r_data.keys() {
|
for url in r_data.keys() {
|
||||||
if ["no_change", "updated", "removed"]
|
if ["no_change", "updated", "removed", "old"]
|
||||||
.iter()
|
.iter()
|
||||||
.any(|key| msgs_json[key].as_object().unwrap().contains_key(url))
|
.any(|key| msgs_json[key].as_object().unwrap().contains_key(url))
|
||||||
{ continue; }
|
{ continue; }
|
||||||
@@ -554,7 +590,7 @@ pub async fn add_posts(http: &Http, c_id: ChannelId, r_data: &Map<String, Value>
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn edit_posts(http: &Http, c_id: ChannelId, r_data: &Map<String, Value>, msgs_json: &Value) {
|
async fn edit_posts(http: &Http, 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()
|
||||||
@@ -565,7 +601,7 @@ pub async fn edit_posts(http: &Http, c_id: ChannelId, r_data: &Map<String, Value
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn remove_posts(http: &Http, c_id: ChannelId, r_data: &Map<String, Value>, msgs_json: &Value) {
|
async fn remove_posts(http: &Http, 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()
|
||||||
@@ -576,7 +612,15 @@ pub async fn remove_posts(http: &Http, c_id: ChannelId, r_data: &Map<String, Val
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub async fn remove_dupes(http: &Http, c_id: ChannelId, msgs_json: &Value) {
|
async fn remove_old(http: &Http, c_id: ChannelId, msgs_json: &Value) {
|
||||||
|
for (_url, msg_id) in msgs_json["old"].as_object().unwrap() {
|
||||||
|
let msg = http.get_message(c_id, MessageId::new(msg_id.as_u64().unwrap())).await.unwrap();
|
||||||
|
let _ = msg.delete(http).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async fn remove_dupes(http: &Http, c_id: ChannelId, msgs_json: &Value) {
|
||||||
for (_url, msgs) in msgs_json["duplicates"].as_object().unwrap() {
|
for (_url, msgs) in msgs_json["duplicates"].as_object().unwrap() {
|
||||||
for msg_id in msgs.as_array().unwrap() {
|
for msg_id in msgs.as_array().unwrap() {
|
||||||
let msg = http.get_message(c_id, MessageId::new(msg_id.as_u64().unwrap())).await.unwrap();
|
let msg = http.get_message(c_id, MessageId::new(msg_id.as_u64().unwrap())).await.unwrap();
|
||||||
@@ -646,6 +690,66 @@ pub async fn bk_week_vote(
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[poise::command(slash_command, prefix_command)]
|
||||||
|
/// Gets the top N (up to 10) posts within a certain category, such as upvotes. (Sorted descending.)
|
||||||
|
pub async fn bk_week_top(
|
||||||
|
ctx: Context<'_>,
|
||||||
|
#[description = "The sorting criteria, such as upvotes."] category: TopCategory,
|
||||||
|
#[description = "The amount of posts to show (max 10)."] amount: Option<u8>
|
||||||
|
) -> Result<(), Error>
|
||||||
|
{
|
||||||
|
let mut all: HashMap<&str, i32> = HashMap::new();
|
||||||
|
let posts = &get_reddit_data(ctx.data()).await.unwrap()[BK_WEEK];
|
||||||
|
let posts_u = posts.as_object().unwrap();
|
||||||
|
|
||||||
|
for (url, dat) in posts_u {
|
||||||
|
if dat.get("removed").is_some() { continue; }
|
||||||
|
|
||||||
|
let val: i32 = match category {
|
||||||
|
TopCategory::Upvotes => dat["post_data"]["upvotes"].as_i64().unwrap() as i32,
|
||||||
|
TopCategory::ModVotes => dat["votes"]["mod_voters"].as_array().unwrap().len() as i32,
|
||||||
|
TopCategory::Oldest
|
||||||
|
| TopCategory::Newest => dat["post_data"]["date_unix"].as_i64().unwrap() as i32,
|
||||||
|
};
|
||||||
|
|
||||||
|
all.insert(url, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
let amount_u = amount.unwrap_or_else(|| 3);
|
||||||
|
let amount_clamped =
|
||||||
|
if amount_u > 10 { 10 }
|
||||||
|
else if amount_u < 1 { 1 }
|
||||||
|
else { amount_u };
|
||||||
|
|
||||||
|
let top =
|
||||||
|
if category != TopCategory::Oldest
|
||||||
|
{ largest_n (&all, amount_clamped as usize) }
|
||||||
|
else { smallest_n(&all, amount_clamped as usize) };
|
||||||
|
|
||||||
|
for post in top {
|
||||||
|
let url = post.0;
|
||||||
|
let _ = send_embed_for_post(ctx, posts_u[url].clone(), &url).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn largest_n<'a>(map: &'a HashMap<&'a str, i32>, n: usize) -> Vec<(&'a str, i32)> {
|
||||||
|
let mut vec: Vec<_> = map.iter().collect();
|
||||||
|
vec.sort_unstable_by(|a, b| b.1.cmp(a.1));
|
||||||
|
vec.into_iter().take(n).map(|(&k, &v)| (k, v)).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn smallest_n<'a>(map: &'a HashMap<&'a str, i32>, n: usize) -> Vec<(&'a str, i32)> {
|
||||||
|
let mut vec: Vec<_> = map.iter().collect();
|
||||||
|
vec.sort_unstable_by(|a, b| a.1.cmp(b.1));
|
||||||
|
vec.into_iter().take(n).map(|(&k, &v)| (k, v)).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[poise::command(slash_command, prefix_command, default_member_permissions = "ADMINISTRATOR")]
|
#[poise::command(slash_command, prefix_command, default_member_permissions = "ADMINISTRATOR")]
|
||||||
/// Changes the subreddit(s) the bot patrols in.
|
/// Changes the subreddit(s) the bot patrols in.
|
||||||
|
|||||||
@@ -197,6 +197,7 @@ async fn gen_bot(data: Data, args: Args) -> Client {
|
|||||||
bk_week_cmds::bk_week_approve(),
|
bk_week_cmds::bk_week_approve(),
|
||||||
bk_week_cmds::bk_week_update(),
|
bk_week_cmds::bk_week_update(),
|
||||||
bk_week_cmds::bk_week_vote(),
|
bk_week_cmds::bk_week_vote(),
|
||||||
|
bk_week_cmds::bk_week_top(),
|
||||||
// bk_admin
|
// bk_admin
|
||||||
bk_week_cmds::bk_admin_bind(),
|
bk_week_cmds::bk_admin_bind(),
|
||||||
// bk_cfg
|
// bk_cfg
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ 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,
|
||||||
@@ -167,6 +168,7 @@ 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,
|
||||||
@@ -265,6 +267,7 @@ pub fn embed_post_removed(post_data: &Value, url: &str, empheral: bool) -> Embed
|
|||||||
),
|
),
|
||||||
col: Some(REMOVED_DC_COL),
|
col: Some(REMOVED_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()),
|
||||||
empheral,
|
empheral,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|||||||
+25
-2
@@ -1,6 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
from typing import Final
|
from typing import Final
|
||||||
|
import time
|
||||||
|
|
||||||
import bot as botPy
|
import bot as botPy
|
||||||
from macros import *
|
from macros import *
|
||||||
@@ -136,13 +137,35 @@ def set_approve_post(bot: botPy.Bot, approved: bool, url: str) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
def remove_post(bot: botPy.Bot, url: str, removed_by: str = "UNKNOWN", reason: str = "None") -> bool:
|
def remove_post(bot: botPy.Bot, url: str, removed_by: str = "UNKNOWN", reason: str = "None") -> bool:
|
||||||
if url in bot.data[BK_WEEKLY]:
|
weekly = bot.data[BK_WEEKLY]
|
||||||
bot.data[BK_WEEKLY][url] = { "removed": True, "removed_by": removed_by, "remove_reason": reason }
|
|
||||||
|
if url in weekly:
|
||||||
|
weekly[url] = {
|
||||||
|
"removed": True,
|
||||||
|
"removed_by": removed_by,
|
||||||
|
"remove_reason": reason,
|
||||||
|
"post_data": { "date_unix": weekly[url]["post_data"]["date_unix"] }
|
||||||
|
}
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def remove_old_posts(bot: botPy.Bot, max_age: int) -> bool:
|
||||||
|
now = int(time.time())
|
||||||
|
weekly = bot.data[BK_WEEKLY]
|
||||||
|
remove: list[str] = []
|
||||||
|
|
||||||
|
for url, post in weekly.items():
|
||||||
|
if now - post["post_data"]["date_unix"] > max_age:
|
||||||
|
remove.append(url)
|
||||||
|
|
||||||
|
for key in remove:
|
||||||
|
weekly.pop(key)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def set_vote_post(
|
def set_vote_post(
|
||||||
bot: botPy.Bot,
|
bot: botPy.Bot,
|
||||||
url: str,
|
url: str,
|
||||||
|
|||||||
+15
-3
@@ -2,13 +2,14 @@ import emoji
|
|||||||
from asyncpraw import models
|
from asyncpraw import models
|
||||||
import asyncprawcore as prawcore
|
import asyncprawcore as prawcore
|
||||||
import asyncpraw.exceptions as exc
|
import asyncpraw.exceptions as exc
|
||||||
|
import time
|
||||||
|
|
||||||
import data
|
import data
|
||||||
import bot as botPy
|
import bot as botPy
|
||||||
from macros import *
|
from macros import *
|
||||||
|
|
||||||
|
|
||||||
async def add_new_posts(bot: botPy.Bot) -> bool:
|
async def add_new_posts(bot: botPy.Bot, max_age: int) -> bool:
|
||||||
check_emoji = emoji.emojize(":check_mark_button:")
|
check_emoji = emoji.emojize(":check_mark_button:")
|
||||||
cross_emoji = emoji.emojize(":cross_mark:")
|
cross_emoji = emoji.emojize(":cross_mark:")
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ async def add_new_posts(bot: botPy.Bot) -> bool:
|
|||||||
added_posts = 0
|
added_posts = 0
|
||||||
without_media = 0
|
without_media = 0
|
||||||
not_added = 0
|
not_added = 0
|
||||||
|
old_posts = 0
|
||||||
for post in posts:
|
for post in posts:
|
||||||
media = has_media(post)
|
media = has_media(post)
|
||||||
|
|
||||||
@@ -33,13 +35,22 @@ async def add_new_posts(bot: botPy.Bot) -> bool:
|
|||||||
f"\n {media_urls}\n"
|
f"\n {media_urls}\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
details = get_post_details(post)
|
||||||
|
now = int(time.time())
|
||||||
|
|
||||||
|
if now - details.date_unix > max_age and max_age > 0:
|
||||||
|
old_posts += 1
|
||||||
|
continue
|
||||||
|
|
||||||
if not media[0]:
|
if not media[0]:
|
||||||
without_media += 1
|
without_media += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
post_added = False
|
||||||
|
|
||||||
post_added = data.add_post_to_data(
|
post_added = data.add_post_to_data(
|
||||||
bot,
|
bot,
|
||||||
get_post_details(post)
|
details
|
||||||
)
|
)
|
||||||
|
|
||||||
if post_added: added_posts += 1
|
if post_added: added_posts += 1
|
||||||
@@ -48,7 +59,8 @@ async def add_new_posts(bot: botPy.Bot) -> bool:
|
|||||||
py_print(f"Successfully fetched {len(posts)} posts.\n" +
|
py_print(f"Successfully fetched {len(posts)} posts.\n" +
|
||||||
f" Out of which were {added_posts} added.\n" +
|
f" Out of which were {added_posts} added.\n" +
|
||||||
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"{not_added} are removed or already existed, " +
|
||||||
|
f"and {old_posts} were older than the max age threshold.")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@@ -75,12 +75,13 @@ async def json_to_func(v: dict, bot: botPy.Bot) -> dict:
|
|||||||
|
|
||||||
match v["value"]:
|
match v["value"]:
|
||||||
case "update_data_file": r = data .write_data (bot)
|
case "update_data_file": r = data .write_data (bot)
|
||||||
case "add_new_posts": r = await posts.add_new_posts (bot)
|
|
||||||
case "respond_mentions": r = await cmds .respond_to_mention(bot)
|
case "respond_mentions": r = await cmds .respond_to_mention(bot)
|
||||||
|
case "add_new_posts": r = await posts.add_new_posts (bot, *v["args"])
|
||||||
case "add_post_url": r = 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": r = data .remove_post (bot, *v["args"])
|
case "remove_post_url": r = data .remove_post (bot, *v["args"])
|
||||||
case "set_approve_post": r = data .set_approve_post (bot, *v["args"])
|
case "set_approve_post": r = data .set_approve_post (bot, *v["args"])
|
||||||
case "set_vote_post": r = data .set_vote_post (bot, *v["args"])
|
case "set_vote_post": r = data .set_vote_post (bot, *v["args"])
|
||||||
|
case "remove_old_posts": r = data .remove_old_posts (bot, *v["args"])
|
||||||
case "change_sr": r = await bot .change_sr (*v["args"])
|
case "change_sr": r = await bot .change_sr (*v["args"])
|
||||||
case "stop_praw": r = await bot .stop ()
|
case "stop_praw": r = await bot .stop ()
|
||||||
case _: value_supported = False
|
case _: value_supported = False
|
||||||
|
|||||||
Reference in New Issue
Block a user