From 713672bd7661b574535c6d4a49ba9a50a756fa43 Mon Sep 17 00:00:00 2001 From: Ryan Cao <70191398+ryanccn@users.noreply.github.com> Date: Fri, 20 Sep 2024 12:57:04 +0800 Subject: [PATCH] fix(moderation): support attachments --- Cargo.lock | 16 ++++++ Cargo.toml | 1 + src/commands/fun/mod.rs | 1 - src/commands/fun/pomelo.rs | 54 ------------------ src/commands/mod.rs | 1 - src/handlers/log.rs | 113 ++++++++++++++++++++++++++----------- src/main.rs | 15 ++++- src/storage/log.rs | 22 ++++---- 8 files changed, 122 insertions(+), 101 deletions(-) delete mode 100644 src/commands/fun/pomelo.rs diff --git a/Cargo.lock b/Cargo.lock index dd723ad..e3510cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1050,6 +1050,15 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humansize" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" +dependencies = [ + "libm", +] + [[package]] name = "humantime" version = "2.1.0" @@ -1223,6 +1232,12 @@ version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -2822,6 +2837,7 @@ dependencies = [ "chrono", "color-eyre", "dotenvy", + "humansize", "humantime", "indexmap", "nanoid", diff --git a/Cargo.toml b/Cargo.toml index e0b9bee..d199796 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ bytesize = "1.3.0" chrono = "0.4.38" color-eyre = "0.6.3" dotenvy = "0.15.7" +humansize = "2.1.3" humantime = "2.1.0" indexmap = { version = "2.5.0", features = ["serde"] } nanoid = "0.4.0" diff --git a/src/commands/fun/mod.rs b/src/commands/fun/mod.rs index f6b9498..12d6656 100644 --- a/src/commands/fun/mod.rs +++ b/src/commands/fun/mod.rs @@ -1,5 +1,4 @@ pub mod autoreply; pub mod intelligence; pub mod owo; -pub mod pomelo; pub mod shiggy; diff --git a/src/commands/fun/pomelo.rs b/src/commands/fun/pomelo.rs deleted file mode 100644 index d491da7..0000000 --- a/src/commands/fun/pomelo.rs +++ /dev/null @@ -1,54 +0,0 @@ -use color_eyre::eyre::Result; -use poise::{serenity_prelude as serenity, CreateReply}; - -use crate::Context; - -/// Get information on the username migration within the server -#[poise::command(slash_command, guild_only)] -#[tracing::instrument(skip(ctx), fields(channel = ctx.channel_id().get(), author = ctx.author().id.get()))] -pub async fn pomelo(ctx: Context<'_>) -> Result<()> { - if let Some(guild) = ctx.guild_id() { - ctx.defer().await?; - - let members: Vec<_> = guild - .members(ctx.http(), None, None) - .await? - .into_iter() - .filter(|m| !m.user.bot()) - .collect(); - - let nonmigrated_users: Vec = members - .iter() - .filter(|m| m.user.discriminator.is_some()) - .map(|m| m.user.id) - .collect(); - - let embed = serenity::CreateEmbed::default() - .title("Username migration / Pomelo") - .description(format!( - "**{}/{}** migrated", - members.len() - nonmigrated_users.len(), - members.len(), - )) - .color(0x2dd4bf) - .field( - "Unmigrated users", - if nonmigrated_users.is_empty() { - "None!".to_owned() - } else { - nonmigrated_users - .into_iter() - .map(|u| format!("<@{u}>")) - .collect::>() - .join(" ") - }, - false, - ); - - ctx.send(CreateReply::default().embed(embed)).await?; - } else { - ctx.say("Guild unavailable").await?; - } - - Ok(()) -} diff --git a/src/commands/mod.rs b/src/commands/mod.rs index f4db0ec..853d304 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -35,7 +35,6 @@ pub fn to_vec() -> Vec< command!(fun, autoreply), command!(fun, intelligence, ask), command!(fun, owo), - command!(fun, pomelo), command!(fun, shiggy), command!(utils, ping), command!(utils, presence), diff --git a/src/handlers/log.rs b/src/handlers/log.rs index ef391eb..04d8a3e 100644 --- a/src/handlers/log.rs +++ b/src/handlers/log.rs @@ -1,8 +1,10 @@ +use humansize::{format_size, FormatSizeOptions}; +use poise::serenity_prelude::{self as serenity}; + use color_eyre::eyre::Result; use once_cell::sync::Lazy; -use poise::serenity_prelude::{self as serenity}; -use crate::{utils::serenity::unique_username, Data}; +use crate::{storage::log::MessageLog, utils::serenity::unique_username, Data}; pub async fn handle_message(message: &serenity::Message, data: &Data) -> Result<()> { if let Some(storage) = &data.storage { @@ -49,6 +51,7 @@ pub async fn edit( author: &Option, prev_content: &Option, new_content: &str, + attachments: &[serenity::Attachment], timestamp: &serenity::Timestamp, ) -> Result<()> { if author == &Some(ctx.cache.current_user().id) { @@ -63,24 +66,46 @@ pub async fn edit( embed_author = embed_author.icon_url(author.to_user(&ctx).await?.face()); } + let mut embed = serenity::CreateEmbed::default() + .author(embed_author) + .field("Channel", format!("<#{channel}>"), false) + .field( + "Previous content", + prev_content.to_owned().unwrap_or("*Unknown*".to_owned()), + false, + ) + .field("New content", new_content, false) + .field("Author", format_user(author.as_ref()), false) + .color(0xffd43b) + .timestamp(timestamp); + + if !attachments.is_empty() { + embed = embed.field( + "Attachments", + attachments + .iter() + .map(|att| { + format!( + "[{}]({}) ({})", + att.filename, + att.url, + format_size( + att.size, + FormatSizeOptions::default().space_after_value(true) + ) + ) + }) + .collect::>() + .join("\n"), + false, + ); + } + logs_channel .send_message( &ctx.http, serenity::CreateMessage::default() - .embed( - serenity::CreateEmbed::default() - .author(embed_author) - .field("Channel", format!("<#{channel}>"), false) - .field( - "Previous content", - prev_content.as_ref().unwrap_or(&"*Unknown*".to_owned()), - false, - ) - .field("New content", new_content, false) - .field("Author", format_user(author.as_ref()), false) - .color(0xffd43b) - .timestamp(timestamp), - ) + .embed(embed) .components(make_link_components(&link, "Jump")), ) .await?; @@ -96,11 +121,17 @@ pub async fn delete( &serenity::ChannelId, &Option, ), - author: &Option, - content: &Option, + log: &Option, timestamp: &serenity::Timestamp, ) -> Result<()> { - if author == &Some(ctx.cache.current_user().id) { + let content = log.as_ref().and_then(|l| l.content.clone()); + let author = log.as_ref().and_then(|l| l.author); + let attachments = log + .as_ref() + .map(|l| l.attachments.clone()) + .unwrap_or_default(); + + if author == Some(ctx.cache.current_user().id) { return Ok(()); } @@ -112,23 +143,41 @@ pub async fn delete( embed_author = embed_author.icon_url(author.to_user(&ctx).await?.face()); } + let mut embed = serenity::CreateEmbed::default() + .author(embed_author) + .field("Channel", format!("<#{channel}>"), false) + .field("Content", content.unwrap_or("*Unknown*".to_owned()), false) + .field("Author", format_user(author.as_ref()), false) + .color(0xff6b6b) + .timestamp(timestamp); + + if !attachments.is_empty() { + embed = embed.field( + "Attachments", + attachments + .iter() + .map(|att| { + format!( + "[{}]({}) ({})", + att.filename, + att.url, + format_size( + att.size, + FormatSizeOptions::default().space_after_value(true) + ) + ) + }) + .collect::>() + .join("\n"), + false, + ); + } + logs_channel .send_message( &ctx.http, serenity::CreateMessage::default() - .embed( - serenity::CreateEmbed::default() - .author(embed_author) - .field("Channel", format!("<#{channel}>"), false) - .field( - "Content", - content.as_ref().unwrap_or(&"*Unknown*".to_owned()), - false, - ) - .field("Author", format_user(author.as_ref()), false) - .color(0xff6b6b) - .timestamp(timestamp), - ) + .embed(embed) .components(make_link_components(&link, "Jump")), ) .await?; diff --git a/src/main.rs b/src/main.rs index e9b3671..ab8d686 100644 --- a/src/main.rs +++ b/src/main.rs @@ -64,11 +64,20 @@ async fn event_handler( let content = event.content.clone(); let author = event.author.as_ref().map(|a| a.id); + let attachments = event + .attachments + .as_ref() + .map(|a| a.to_vec()) + .unwrap_or_default(); storage .set_message_log( &event.id.to_string(), - &MessageLog::new(content.as_ref().map(|s| s.to_string()), author), + &MessageLog::new( + content.as_ref().map(|s| s.to_string()), + author, + attachments.clone(), + ), ) .await?; @@ -80,6 +89,7 @@ async fn event_handler( &content .as_ref() .map_or("*Unknown*".to_owned(), |s| s.to_string()), + &attachments, ×tamp, ) .await?; @@ -104,8 +114,7 @@ async fn event_handler( handlers::log::delete( ctx.serenity_context, (deleted_message_id, channel_id, guild_id), - &prev.as_ref().and_then(|p| p.author), - &prev.and_then(|p| p.content), + &prev, ×tamp, ) .await?; diff --git a/src/storage/log.rs b/src/storage/log.rs index a552d26..c41cf2e 100644 --- a/src/storage/log.rs +++ b/src/storage/log.rs @@ -1,4 +1,5 @@ -use poise::serenity_prelude::{Message, UserId}; +use poise::serenity_prelude::{Attachment, Message, UserId}; + use redis_macros::{FromRedisValue, ToRedisArgs}; use serde::{Deserialize, Serialize}; @@ -6,19 +7,19 @@ use serde::{Deserialize, Serialize}; pub struct MessageLog { pub content: Option, pub author: Option, + pub attachments: Vec, } impl MessageLog { - pub const fn new(content: Option, author: Option) -> Self { - Self { content, author } - } -} - -impl From for MessageLog { - fn from(value: Message) -> Self { + pub const fn new( + content: Option, + author: Option, + attachments: Vec, + ) -> Self { Self { - content: Some(value.content.into_string()), - author: Some(value.author.id), + content, + author, + attachments, } } } @@ -28,6 +29,7 @@ impl From<&Message> for MessageLog { Self { content: Some(value.content.clone().into_string()), author: Some(value.author.id), + attachments: value.attachments.to_vec(), } } }