diff --git a/src/commands/moderation.rs b/src/commands/moderation.rs index 8cb8079..dc7b608 100644 --- a/src/commands/moderation.rs +++ b/src/commands/moderation.rs @@ -1,30 +1,17 @@ - - - use bson::{doc, Document}; use chrono::Utc; use mongodb::options::{UpdateModifications, UpdateOptions}; use poise::serenity_prelude::{ - self as serenity, - CreateEmbed, - CreateEmbedFooter, - EditMessage, - GetMessages, - Mentionable, - UserId, + self as serenity, CreateEmbed, CreateEmbedFooter, EditMessage, GetMessages, Mentionable, UserId, }; use poise::CreateReply; use tracing::{debug, trace}; -use crate::db::model::{Muted}; +use crate::db::model::Muted; use crate::utils::bot::get_member; use crate::utils::macros::to_user; use crate::utils::moderation::{ - ban_moderation, - queue_unmute_member, - respond_moderation, - BanKind, - ModerationKind, + ban_moderation, queue_unmute_member, respond_moderation, BanKind, ModerationKind, }; use crate::utils::parse_duration; use crate::{Context, Error}; @@ -299,6 +286,23 @@ pub async fn purge( Ok(()) } +/// Kick a member. +#[poise::command(slash_command)] +pub async fn kick( + ctx: Context<'_>, + #[description = "Member"] user: UserId, + #[description = "Reason for the ban"] reason: Option, +) -> Result<(), Error> { + // We cannot use `User` as a parameter for the moderation commands because of a bug in serenity. See: https://github.com/revanced/revanced-discord-bot/issues/38 + let user = to_user!(user, ctx); + + let kick_result = kick_moderation(ctx).await; + + let author = ctx.author(); + + ModerationKind::Kick(user.clone(), author.clone(), reason.clone(), kick_result) +} + /// Ban a member. #[poise::command(slash_command)] pub async fn ban( diff --git a/src/utils/moderation.rs b/src/utils/moderation.rs index b1c8c8c..b9d96a3 100644 --- a/src/utils/moderation.rs +++ b/src/utils/moderation.rs @@ -3,14 +3,7 @@ use std::sync::{Arc, Mutex}; use mongodb::options::FindOptions; use poise::serenity_prelude::{ - ChannelId, - CreateEmbed, - CreateEmbedFooter, - CreateMessage, - GuildId, - Mentionable, - User, - UserId, + ChannelId, CreateEmbed, CreateEmbedFooter, CreateMessage, GuildId, Mentionable, User, UserId, }; use poise::CreateReply; use serenity::prelude::SerenityError; @@ -28,8 +21,9 @@ use crate::{Context, Error}; pub enum ModerationKind { Mute(User, User, String, Option, Option), /* User, Command author, Reason, Expires, Error */ Unmute(User, User, Option), // User, Command author, Error - Ban(User, User, Option, Option), // User, Command author, Reason, Error - Unban(User, User, Option), // User, Command author, Error + Kick(User, User, Option, Option), // User, Command author, Reason, Error + Ban(User, User, Option, Option), // User, Command author, Reason, Error + Unban(User, User, Option), // User, Command author, Error } pub enum BanKind { Ban(User, Option, Option), // User, Amount of days to delete messages, Reason @@ -191,6 +185,35 @@ pub async fn respond_moderation<'a>( false, ), }, + ModerationKind::Kick(user, author, reason, error) => { + let f = match error { + Some(err) => { + *send_ephemeral.lock().unwrap() = true; + + f.title(format!("Failed to kick {}", user.tag())) + .field("Exception", err.to_string(), false) + .field( + "Action", + format!( + "{} was kicked by {} but failed", + user.mention(), + author.mention() + ), + false, + ) + }, + None => f.title(format!("Kicked {}", user.tag())).field( + "Action", + format!("{} was kicked by {}", user.mention(), author.mention()), + false, + ), + }; + if let Some(reason) = reason { + f.field("Reason", reason, true) + } else { + f + } + }, ModerationKind::Ban(user, author, reason, error) => { let f = match error { Some(err) => { @@ -283,6 +306,23 @@ pub async fn respond_moderation<'a>( Ok(()) } +pub async fn kick_moderation(ctx: &Context<'_>) -> Option { + let guild_id = ctx.guild_id().unwrap(); + + let http = &ctx.serenity_context().http; + + let reason = reason.as_deref().or(Some("None specified")); + + let kick_result = http.kick_member(guild_id, user.id, reason).await; + + if let Err(err) = kick_result { + error!("Failed to kick user {}: {}", user.id, err); + Some(err) + } else { + None + } +} + pub async fn ban_moderation(ctx: &Context<'_>, kind: &BanKind) -> Option { let guild_id = ctx.guild_id().unwrap();