Skip to content
This repository has been archived by the owner on Aug 7, 2024. It is now read-only.

Commit

Permalink
feat: unban command
Browse files Browse the repository at this point in the history
  • Loading branch information
oSumAtrIX committed Aug 28, 2022
1 parent 042fc8c commit b7d333c
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 51 deletions.
84 changes: 38 additions & 46 deletions src/commands/moderation.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
use std::cmp;

use bson::{doc, Document};
use chrono::{Duration, Utc};
use mongodb::options::{UpdateModifications, UpdateOptions};
use poise::serenity_prelude::{self as serenity, Member, RoleId};
use poise::serenity_prelude::{self as serenity, Member, RoleId, User};
use tracing::{debug, trace};

use crate::db::model::Muted;
use crate::utils::moderation::{queue_unmute_member, respond_mute_command, ModerationKind};
use crate::utils::moderation::{
ban_moderation,
queue_unmute_member,
respond_moderation,
BanKind,
ModerationKind,
};
use crate::{Context, Error};

/// Unmute a member.
Expand All @@ -26,7 +30,7 @@ pub async fn unmute(
pending_unmute.abort();
}

respond_mute_command(
respond_moderation(
&ctx,
ModerationKind::Unmute(
queue_unmute_member(
Expand All @@ -40,7 +44,6 @@ pub async fn unmute(
.unwrap(),
),
&member.user,
configuration.general.embed_color,
)
.await
}
Expand Down Expand Up @@ -88,7 +91,6 @@ pub async fn mute(

let data = &mut *ctx.data().write().await;
let configuration = &data.configuration;
let embed_color = configuration.general.embed_color;
let mute = &configuration.general.mute;
let mute_role_id = mute.role;
let take = &mute.take;
Expand Down Expand Up @@ -170,11 +172,10 @@ pub async fn mute(
),
);

respond_mute_command(
respond_moderation(
&ctx,
ModerationKind::Mute(reason, format!("<t:{}:F>", unmute_time.timestamp()), result),
&member.user,
embed_color,
)
.await
}
Expand All @@ -183,7 +184,7 @@ pub async fn mute(
#[poise::command(slash_command)]
pub async fn purge(
ctx: Context<'_>,
#[description = "User"] member: Option<Member>,
#[description = "User"] user: Option<User>,
#[description = "Until message"] until: Option<String>,
#[min = 1]
#[max = 1000]
Expand All @@ -203,10 +204,10 @@ pub async fn purge(
let channel = ctx.channel_id();
let too_old_timestamp = Utc::now().timestamp() - MAX_BULK_DELETE_AGO_SECS;

let user = ctx.discord().http.get_current_user().await?;
let image = user
let current_user = ctx.discord().http.get_current_user().await?;
let image = current_user
.avatar_url()
.unwrap_or_else(|| user.default_avatar_url());
.unwrap_or_else(|| current_user.default_avatar_url());

let handle = ctx
.send(|f| {
Expand Down Expand Up @@ -238,13 +239,13 @@ pub async fn purge(
.collect::<Vec<_>>();

// Filter for messages from the user
if let Some(ref member) = member {
if let Some(ref user) = user {
messages = messages
.into_iter()
.filter(|msg| msg.author.id == member.user.id)
.filter(|msg| msg.author.id == user.id)
.collect::<Vec<_>>();

debug!("Filtered messages by {}. Left: {}", member, messages.len());
debug!("Filtered messages by {}. Left: {}", user, messages.len());
}

// Filter for messages until the g/mutiven id
Expand Down Expand Up @@ -291,41 +292,32 @@ pub async fn purge(
Ok(())
}

/// Ban a member.
/// Ban a user.
#[poise::command(slash_command)]
pub async fn ban(
ctx: Context<'_>,
#[description = "User"] member: Member,
#[description = "User"] user: User,
#[description = "Amount of days to delete messages"] dmd: Option<u8>,
#[description = "Reason for the ban"] reason: Option<String>,
) -> Result<(), Error> {
let reason = &reason
.or_else(|| Some("None specified".to_string()))
.unwrap();

let ban_result = member
.ban_with_reason(&ctx.discord().http, cmp::min(dmd.unwrap_or(0), 7), reason)
.await;

let embed_color = ctx.data().read().await.configuration.general.embed_color;
respond_moderation(
&ctx,
ModerationKind::Ban(
reason.clone(),
ban_moderation(&ctx, BanKind::Ban(user.clone(), dmd, reason)).await,
),
&user,
)
.await
}

ctx.send(|f| {
f.embed(|e| {
if let Err(error) = ban_result {
e.title(format!("Failed to ban {}", member.user.tag()))
.field("Error", error, false)
} else {
e.title(format!("Banned {}", member.user.tag()))
.thumbnail(
member
.avatar_url()
.unwrap_or_else(|| member.user.default_avatar_url()),
)
.field("Reason", reason, false)
}
.color(embed_color)
})
})
.await?;
Ok(())
/// Unban a user.
#[poise::command(slash_command)]
pub async fn unban(ctx: Context<'_>, #[description = "User"] user: User) -> Result<(), Error> {
respond_moderation(
&ctx,
ModerationKind::Unban(ban_moderation(&ctx, BanKind::Unban(user.clone())).await),
&user,
)
.await
}
75 changes: 70 additions & 5 deletions src/utils/moderation.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
use std::cmp;
use std::sync::Arc;

use mongodb::options::FindOptions;
use poise::serenity_prelude::Http;
use poise::serenity_prelude::{Http, User};
use tokio::task::JoinHandle;
use tracing::{debug, error, trace};

use super::bot::get_data_lock;
use super::*;
use crate::db::database::Database;
use crate::db::model::Muted;
use crate::serenity::SerenityError;
use crate::{Context, Error};

pub enum ModerationKind {
Mute(String, String, Option<Error>), // Reason, Expires, Error
Unmute(Option<Error>), // Error
Ban(Option<String>, Option<SerenityError>), // Reason, Error
Unban(Option<SerenityError>), // Error
}
pub enum BanKind {
Ban(User, Option<u8>, Option<String>), // User, Amount of days to delete messages, Reason
Unban(User), // User
}

pub async fn mute_on_join(ctx: &serenity::Context, new_member: &mut serenity::Member) {
let data = get_data_lock(ctx).await;
let data = data.read().await;
Expand Down Expand Up @@ -105,17 +111,19 @@ pub fn queue_unmute_member(
})
}

pub async fn respond_mute_command(
// TODO: refactor
pub async fn respond_moderation(
ctx: &Context<'_>,
moderation: ModerationKind,
user: &serenity::User,
embed_color: i32,
) -> Result<(), Error> {
let tag = user.tag();
let image = user
.avatar_url()
.unwrap_or_else(|| user.default_avatar_url());

let embed_color = ctx.data().read().await.configuration.general.embed_color;

ctx.send(|f| {
f.embed(|f| {
match moderation {
Expand All @@ -137,6 +145,29 @@ pub async fn respond_mute_command(
),
None => f.title(format!("Unmuted {}", tag)),
},
ModerationKind::Ban(reason, error) => {
let f = match error {
Some(err) => f.title(format!("Failed to ban {}", tag)).field(
"Exception",
err.to_string(),
false,
),
None => f.title(format!("Banned {}", tag)),
};
if let Some(reason) = reason {
f.field("Reason", reason, false)
} else {
f
}
},
ModerationKind::Unban(error) => match error {
Some(err) => f.title(format!("Failed to unban {}", tag)).field(
"Exception",
err.to_string(),
false,
),
None => f.title(format!("Unbanned {}", tag)),
},
}
.color(embed_color)
.thumbnail(image)
Expand All @@ -146,3 +177,37 @@ pub async fn respond_mute_command(

Ok(())
}

pub async fn ban_moderation(ctx: &Context<'_>, kind: BanKind) -> Option<SerenityError> {
let guild_id = ctx.guild_id().unwrap().0;
let http = &ctx.discord().http;

match kind {
BanKind::Ban(user, dmd, reason) => {
let reason = &reason
.or_else(|| Some("None specified".to_string()))
.unwrap();

let ban_result = http
.ban_user(guild_id, user.id.0, cmp::min(dmd.unwrap_or(0), 7), reason)
.await;

if let Err(err) = ban_result {
error!("Failed to ban user {}: {}", user.id.0, err);
Some(err)
} else {
None
}
},
BanKind::Unban(user) => {
let unban_result = http.remove_ban(guild_id, user.id.0, None).await;

if let Err(err) = unban_result {
error!("Failed to unban user {}: {}", user.id.0, err);
Some(err)
} else {
None
}
},
}
}

0 comments on commit b7d333c

Please sign in to comment.