chore(commands): reorganize command files by metadata categories

This commit is contained in:
Puechberty Arthur
2026-04-10 15:25:21 +02:00
parent 23dcc69977
commit 1b5e51c428
154 changed files with 399 additions and 399 deletions
+74
View File
@@ -0,0 +1,74 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::moderation_sanction_helpers::{add_sanction, parse_targets};
pub async fn handle_ban(ctx: &Context, msg: &Message, args: &[&str]) {
let Some(guild_id) = msg.guild_id else {
return;
};
if args.is_empty() {
return;
}
let targets = parse_targets(args[0]).await;
if targets.is_empty() {
return;
}
let reason = if args.len() > 1 {
args[1..].join(" ")
} else {
"Aucune raison".to_string()
};
let mut done = 0usize;
for uid in &targets {
if guild_id
.ban_with_reason(&ctx.http, *uid, 0, &reason)
.await
.is_ok()
{
done += 1;
add_sanction(
ctx,
guild_id,
*uid,
msg.author.id,
"ban",
&reason,
None,
None,
)
.await;
}
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Ban")
.description(format!("{} membre(s) banni(s).", done))
.color(theme_color(ctx).await),
)
.await;
}
pub struct BanCommand;
pub static COMMAND_DESCRIPTOR: BanCommand = BanCommand;
impl crate::commands::command_contract::CommandSpec for BanCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "ban",
category: "mod",
params: "<@membre/ID[,..]> [raison]",
description: "Ban un ou plusieurs membres.",
examples: &["+ban @User"],
default_aliases: &["b"],
allow_in_dm: false,
default_permission: 7,
}
}
}
+50
View File
@@ -0,0 +1,50 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
pub async fn handle_banlist(ctx: &Context, msg: &Message) {
let Some(guild_id) = msg.guild_id else {
return;
};
let bans = guild_id
.bans(&ctx.http, None, None)
.await
.unwrap_or_default();
let desc = if bans.is_empty() {
"Aucun ban en cours.".to_string()
} else {
bans.into_iter()
.map(|ban| format!("- <@{}> ({})", ban.user.id.get(), ban.user.tag()))
.collect::<Vec<_>>()
.join("\n")
};
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("BanList")
.description(desc)
.color(theme_color(ctx).await),
)
.await;
}
pub struct BanlistCommand;
pub static COMMAND_DESCRIPTOR: BanlistCommand = BanlistCommand;
impl crate::commands::command_contract::CommandSpec for BanlistCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "banlist",
category: "mod",
params: "aucun",
description: "Affiche la liste des bannissements en cours.",
examples: &["+banlist"],
default_aliases: &["bls"],
allow_in_dm: false,
default_permission: 5,
}
}
}
+71
View File
@@ -0,0 +1,71 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::common::{parse_channel_id, send_embed, theme_color};
pub async fn handle_cleanup(ctx: &Context, msg: &Message, args: &[&str]) {
let Some(guild_id) = msg.guild_id else {
return;
};
let Some(channel_raw) = args.first() else {
return;
};
let Some(channel_id) = parse_channel_id(channel_raw) else {
return;
};
let user_ids = {
let Some(guild) = guild_id.to_guild_cached(&ctx.cache) else {
return;
};
guild
.voice_states
.iter()
.filter_map(|(uid, state)| {
if state.channel_id == Some(channel_id) {
Some(*uid)
} else {
None
}
})
.collect::<Vec<_>>()
};
let mut kicked = 0usize;
for user_id in user_ids {
if guild_id.disconnect_member(&ctx.http, user_id).await.is_ok() {
kicked += 1;
}
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Cleanup")
.description(format!("{} utilisateurs déconnectés.", kicked))
.color(theme_color(ctx).await),
)
.await;
}
pub struct CleanupCommand;
pub static COMMAND_DESCRIPTOR: CleanupCommand = CleanupCommand;
impl crate::commands::command_contract::CommandSpec for CleanupCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "cleanup",
category: "mod",
params: "<salon_vocal>",
description: "Deconnecte tous les utilisateurs presents dans un salon vocal cible.",
examples: &["+cleanup #General"],
default_aliases: &["vclean", "vcleanup"],
allow_in_dm: false,
default_permission: 6,
}
}
}
+66
View File
@@ -0,0 +1,66 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::db::DbPoolKey;
pub async fn handle_clear_all_sanctions(ctx: &Context, msg: &Message) {
let Some(guild_id) = msg.guild_id else {
return;
};
let pool = {
let data = ctx.data.read().await;
data.get::<DbPoolKey>().cloned()
};
let Some(pool) = pool else {
return;
};
let bot_id = ctx.cache.current_user().id;
let removed = sqlx::query(
r#"
DELETE FROM bot_sanctions
WHERE bot_id = $1 AND guild_id = $2;
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.execute(&pool)
.await
.ok()
.map(|r| r.rows_affected())
.unwrap_or(0);
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Sanctions")
.description(format!(
"{} sanction(s) supprimée(s) sur le serveur.",
removed
))
.color(theme_color(ctx).await),
)
.await;
}
pub struct ClearAllSanctionsCommand;
pub static COMMAND_DESCRIPTOR: ClearAllSanctionsCommand = ClearAllSanctionsCommand;
impl crate::commands::command_contract::CommandSpec for ClearAllSanctionsCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "clear_all_sanctions",
category: "mod",
params: "aucun",
description: "Efface toutes les sanctions de tous les membres du serveur.",
examples: &["+clear all sanctions"],
default_aliases: &["casanctions"],
allow_in_dm: false,
default_permission: 8,
}
}
}
+50
View File
@@ -0,0 +1,50 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::automod_service::pool;
use crate::commands::common::send_embed;
use crate::db;
pub async fn handle_clear_badwords(ctx: &Context, msg: &Message, _args: &[&str]) {
let Some(guild_id) = msg.guild_id else {
return;
};
let Some(pool) = pool(ctx).await else {
return;
};
let bot_id = ctx.cache.current_user().id.get() as i64;
let cleared = db::clear_badwords(&pool, bot_id, guild_id.get() as i64)
.await
.unwrap_or(0);
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Clear BadWords")
.description(format!("{} mot(s) interdit(s) supprime(s).", cleared))
.color(0x57F287),
)
.await;
}
pub struct ClearBadwordsCommand;
pub static COMMAND_DESCRIPTOR: ClearBadwordsCommand = ClearBadwordsCommand;
impl crate::commands::command_contract::CommandSpec for ClearBadwordsCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "clear_badwords",
category: "mod",
params: "badwords",
description: "Supprime l ensemble des mots interdits enregistres.",
examples: &["+clear badwords", "+help clear badwords"],
default_aliases: &["cbw"],
allow_in_dm: false,
default_permission: 7,
}
}
}
+74
View File
@@ -0,0 +1,74 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::automod_service::pool;
use crate::commands::common::send_embed;
use crate::db;
pub async fn handle_clear_limit(ctx: &Context, msg: &Message, args: &[&str]) {
let Some(guild_id) = msg.guild_id else {
return;
};
let Some(raw_value) = args.get(1) else {
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Clear Limit")
.description("Usage: +clear limit <nombre>")
.color(0xED4245),
)
.await;
return;
};
let Ok(value) = raw_value.parse::<i32>() else {
return;
};
let clamped = value.clamp(1, 1_000);
let Some(pool) = pool(ctx).await else {
return;
};
let bot_id = ctx.cache.current_user().id.get() as i64;
if db::set_clear_limit(&pool, bot_id, guild_id.get() as i64, clamped)
.await
.is_err()
{
return;
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Clear Limit")
.description(format!(
"Limite de suppression definie a **{}** message(s) par commande clear.",
clamped
))
.color(0x57F287),
)
.await;
}
pub struct ClearLimitCommand;
pub static COMMAND_DESCRIPTOR: ClearLimitCommand = ClearLimitCommand;
impl crate::commands::command_contract::CommandSpec for ClearLimitCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "clear_limit",
category: "mod",
params: "limit <nombre>",
description: "Definit la limite max de messages supprimables avec +clear.",
examples: &["+clear limit 100", "+help clear limit"],
default_aliases: &["climit"],
allow_in_dm: false,
default_permission: 7,
}
}
}
+89
View File
@@ -0,0 +1,89 @@
use serenity::builder::{CreateEmbed, GetMessages};
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::admin_common::parse_user_id;
use crate::commands::common::{send_embed, theme_color};
use crate::db::{self, DbPoolKey};
pub async fn handle_clear_messages(ctx: &Context, msg: &Message, args: &[&str]) {
let Ok(mut amount) = args.first().unwrap_or(&"0").parse::<u64>() else {
return;
};
if amount == 0 {
return;
}
let max_limit = if let Some(guild_id) = msg.guild_id {
let pool = {
let data = ctx.data.read().await;
data.get::<DbPoolKey>().cloned()
};
if let Some(pool) = pool {
let bot_id = ctx.cache.current_user().id.get() as i64;
db::get_or_create_moderation_settings(&pool, bot_id, guild_id.get() as i64)
.await
.ok()
.map(|settings| settings.clear_limit.max(1) as u64)
.unwrap_or(100)
} else {
100
}
} else {
100
};
amount = amount.clamp(1, max_limit);
let filter_user = args.get(1).and_then(|raw| parse_user_id(raw));
let mut deleted = 0usize;
if let Ok(messages) = msg
.channel_id
.messages(&ctx.http, GetMessages::new().limit(amount as u8 + 1))
.await
{
for m in messages {
if m.id == msg.id {
continue;
}
if let Some(user_id) = filter_user {
if m.author.id != user_id {
continue;
}
}
if msg.channel_id.delete_message(&ctx.http, m.id).await.is_ok() {
deleted += 1;
}
}
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Clear")
.description(format!("{} message(s) supprime(s).", deleted))
.color(theme_color(ctx).await),
)
.await;
}
pub struct ClearMessagesCommand;
pub static COMMAND_DESCRIPTOR: ClearMessagesCommand = ClearMessagesCommand;
impl crate::commands::command_contract::CommandSpec for ClearMessagesCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "clear_messages",
category: "mod",
params: "<nombre> [@membre/ID]",
description: "Supprime un nombre de messages, optionnellement filtres par membre.",
examples: &["+clear 20", "+clear 20 @User"],
default_aliases: &["purge"],
allow_in_dm: false,
default_permission: 5,
}
}
}
+76
View File
@@ -0,0 +1,76 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::admin_common::parse_user_id;
use crate::commands::common::{send_embed, theme_color};
use crate::db::DbPoolKey;
pub async fn handle_clear_sanctions(ctx: &Context, msg: &Message, args: &[&str]) {
let Some(guild_id) = msg.guild_id else {
return;
};
if args.len() < 2 {
return;
}
let Some(target) = parse_user_id(args[1]) else {
return;
};
let pool = {
let data = ctx.data.read().await;
data.get::<DbPoolKey>().cloned()
};
let Some(pool) = pool else {
return;
};
let bot_id = ctx.cache.current_user().id;
let removed = sqlx::query(
r#"
DELETE FROM bot_sanctions
WHERE bot_id = $1 AND guild_id = $2 AND user_id = $3;
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.bind(target.get() as i64)
.execute(&pool)
.await
.ok()
.map(|r| r.rows_affected())
.unwrap_or(0);
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Sanctions")
.description(format!(
"{} sanction(s) supprimée(s) pour <@{}>.",
removed,
target.get()
))
.color(theme_color(ctx).await),
)
.await;
}
pub struct ClearSanctionsCommand;
pub static COMMAND_DESCRIPTOR: ClearSanctionsCommand = ClearSanctionsCommand;
impl crate::commands::command_contract::CommandSpec for ClearSanctionsCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "clear_sanctions",
category: "mod",
params: "<@membre/ID>",
description: "Efface completement les sanctions d un membre cible.",
examples: &["+clear sanctions @User"],
default_aliases: &["csanctions"],
allow_in_dm: false,
default_permission: 6,
}
}
}
+70
View File
@@ -0,0 +1,70 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::moderation_sanction_helpers::{
add_sanction, channel_mute_users, parse_targets,
};
pub async fn handle_cmute(ctx: &Context, msg: &Message, args: &[&str]) {
let Some(guild_id) = msg.guild_id else {
return;
};
if args.is_empty() {
return;
}
let targets = parse_targets(args[0]).await;
if targets.is_empty() {
return;
}
let reason = if args.len() > 1 {
args[1..].join(" ")
} else {
"Aucune raison".to_string()
};
let affected = channel_mute_users(ctx, msg.channel_id, &targets, true).await;
for uid in &targets {
add_sanction(
ctx,
guild_id,
*uid,
msg.author.id,
"cmute",
&reason,
Some(msg.channel_id),
None,
)
.await;
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("CMute")
.description(format!("{} membre(s) cmute.", affected))
.color(theme_color(ctx).await),
)
.await;
}
pub struct CmuteCommand;
pub static COMMAND_DESCRIPTOR: CmuteCommand = CmuteCommand;
impl crate::commands::command_contract::CommandSpec for CmuteCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "cmute",
category: "mod",
params: "<@membre/ID[,..]> [raison]",
description: "Mute un membre sur le salon courant.",
examples: &["+cmute @User"],
default_aliases: &["cm"],
allow_in_dm: false,
default_permission: 6,
}
}
}
+98
View File
@@ -0,0 +1,98 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::admin_common::parse_user_id;
use crate::commands::common::{send_embed, theme_color};
use crate::db::DbPoolKey;
pub async fn handle_del_sanction(ctx: &Context, msg: &Message, args: &[&str]) {
let Some(guild_id) = msg.guild_id else {
return;
};
if args.len() < 3 {
return;
}
let Some(target) = parse_user_id(args[1]) else {
return;
};
let Ok(index) = args[2].parse::<usize>() else {
return;
};
if index == 0 {
return;
}
let pool = {
let data = ctx.data.read().await;
data.get::<DbPoolKey>().cloned()
};
let Some(pool) = pool else {
return;
};
let bot_id = ctx.cache.current_user().id;
let rows = sqlx::query_as::<_, (i64,)>(
r#"
SELECT id
FROM bot_sanctions
WHERE bot_id = $1 AND guild_id = $2 AND user_id = $3
ORDER BY created_at DESC;
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.bind(target.get() as i64)
.fetch_all(&pool)
.await
.unwrap_or_default();
let Some((sanction_id,)) = rows.get(index - 1).copied() else {
return;
};
let _ = sqlx::query(
r#"
DELETE FROM bot_sanctions
WHERE id = $1 AND bot_id = $2 AND guild_id = $3;
"#,
)
.bind(sanction_id)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.execute(&pool)
.await;
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Sanctions")
.description(format!(
"Sanction #{} supprimée pour <@{}>.",
sanction_id,
target.get()
))
.color(theme_color(ctx).await),
)
.await;
}
pub struct DelSanctionCommand;
pub static COMMAND_DESCRIPTOR: DelSanctionCommand = DelSanctionCommand;
impl crate::commands::command_contract::CommandSpec for DelSanctionCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "del_sanction",
category: "mod",
params: "<@membre/ID> <nombre>",
description: "Supprime une sanction specifique dans l historique d un membre.",
examples: &["+del sanction @User 1"],
default_aliases: &["delsanction"],
allow_in_dm: false,
default_permission: 6,
}
}
}
+74
View File
@@ -0,0 +1,74 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::moderation_sanction_helpers::{add_sanction, parse_targets};
pub async fn handle_kick(ctx: &Context, msg: &Message, args: &[&str]) {
let Some(guild_id) = msg.guild_id else {
return;
};
if args.is_empty() {
return;
}
let targets = parse_targets(args[0]).await;
if targets.is_empty() {
return;
}
let reason = if args.len() > 1 {
args[1..].join(" ")
} else {
"Aucune raison".to_string()
};
let mut done = 0usize;
for uid in &targets {
if guild_id
.kick_with_reason(&ctx.http, *uid, &reason)
.await
.is_ok()
{
done += 1;
add_sanction(
ctx,
guild_id,
*uid,
msg.author.id,
"kick",
&reason,
None,
None,
)
.await;
}
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Kick")
.description(format!("{} membre(s) expulse(s).", done))
.color(theme_color(ctx).await),
)
.await;
}
pub struct KickCommand;
pub static COMMAND_DESCRIPTOR: KickCommand = KickCommand;
impl crate::commands::command_contract::CommandSpec for KickCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "kick",
category: "mod",
params: "<@membre/ID[,..]> [raison]",
description: "Kick un ou plusieurs membres.",
examples: &["+kick @User"],
default_aliases: &["k"],
allow_in_dm: false,
default_permission: 6,
}
}
}
+70
View File
@@ -0,0 +1,70 @@
use chrono::Utc;
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::moderation_sanction_helpers::{add_sanction, handle_timeout, parse_targets};
pub async fn handle_mute(ctx: &Context, msg: &Message, args: &[&str]) {
let Some(guild_id) = msg.guild_id else {
return;
};
if args.is_empty() {
return;
}
let targets = parse_targets(args[0]).await;
if targets.is_empty() {
return;
}
let expires_at = Some(Utc::now() + chrono::Duration::seconds(28 * 24 * 3600));
let reason = if args.len() > 1 {
args[1..].join(" ")
} else {
"Aucune raison".to_string()
};
let affected = handle_timeout(ctx, guild_id, &targets, expires_at).await;
for uid in &targets {
add_sanction(
ctx,
guild_id,
*uid,
msg.author.id,
"mute",
&reason,
None,
expires_at,
)
.await;
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Mute")
.description(format!("{} membre(s) mute.", affected))
.color(theme_color(ctx).await),
)
.await;
}
pub struct MuteCommand;
pub static COMMAND_DESCRIPTOR: MuteCommand = MuteCommand;
impl crate::commands::command_contract::CommandSpec for MuteCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "mute",
category: "mod",
params: "<@membre/ID[,..]> [raison]",
description: "Applique un mute a un ou plusieurs membres.",
examples: &["+mute @User abus"],
default_aliases: &["tmute"],
allow_in_dm: false,
default_permission: 6,
}
}
}
+75
View File
@@ -0,0 +1,75 @@
use chrono::Utc;
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::moderation_sanction_helpers::pool;
pub async fn handle_mutelist(ctx: &Context, msg: &Message) {
let Some(guild_id) = msg.guild_id else {
return;
};
let Some(pool) = pool(ctx).await else {
return;
};
let bot_id = ctx.cache.current_user().id;
let rows = sqlx::query_as::<_, (i64, String, Option<i64>, Option<chrono::DateTime<Utc>>)>(
r#"
SELECT user_id, kind, channel_id, expires_at
FROM bot_sanctions
WHERE bot_id = $1 AND guild_id = $2 AND active = TRUE AND kind IN ('mute','tempmute','cmute','tempcmute')
ORDER BY created_at DESC
LIMIT 60;
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.fetch_all(&pool)
.await
.unwrap_or_default();
let desc = if rows.is_empty() {
"Aucun mute en cours.".to_string()
} else {
rows.into_iter()
.map(|(uid, kind, channel_id, exp)| {
let channel = channel_id
.map(|c| format!(" dans <#{}>", c))
.unwrap_or_default();
let until = exp
.map(|d| format!(" jusqu'a <t:{}:R>", d.timestamp()))
.unwrap_or_default();
format!("- <@{}> `{}`{}{}", uid, kind, channel, until)
})
.collect::<Vec<_>>()
.join("\n")
};
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("MuteList")
.description(desc)
.color(theme_color(ctx).await),
)
.await;
}
pub struct MutelistCommand;
pub static COMMAND_DESCRIPTOR: MutelistCommand = MutelistCommand;
impl crate::commands::command_contract::CommandSpec for MutelistCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "mutelist",
category: "mod",
params: "aucun",
description: "Affiche tous les mutes en cours.",
examples: &["+mutelist"],
default_aliases: &["ml"],
allow_in_dm: false,
default_permission: 5,
}
}
}
+141
View File
@@ -0,0 +1,141 @@
use serenity::builder::{CreateEmbed, EditRole};
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::automod_service::pool;
use crate::commands::common::send_embed;
use crate::db;
fn mute_permissions() -> Permissions {
Permissions::SEND_MESSAGES | Permissions::ADD_REACTIONS | Permissions::SPEAK
}
pub async fn handle_muterole(ctx: &Context, msg: &Message, _args: &[&str]) {
let Some(guild_id) = msg.guild_id else {
return;
};
let Some(pool) = pool(ctx).await else {
return;
};
let bot_id = ctx.cache.current_user().id.get() as i64;
let Ok(settings) =
db::get_or_create_moderation_settings(&pool, bot_id, guild_id.get() as i64).await
else {
return;
};
let mut role_id = settings
.mute_role_id
.and_then(|raw| u64::try_from(raw).ok())
.map(RoleId::new);
let Ok(partial_guild) = guild_id.to_partial_guild(&ctx.http).await else {
return;
};
if role_id.is_none() {
role_id = partial_guild
.roles
.values()
.find(|role| role.name.eq_ignore_ascii_case("Muted"))
.map(|role| role.id);
}
if role_id.is_none() {
let created = guild_id
.create_role(
&ctx.http,
EditRole::new()
.name("Muted")
.permissions(Permissions::empty()),
)
.await
.ok();
role_id = created.map(|role| role.id);
}
let Some(role_id) = role_id else {
return;
};
let mut failed_channels = Vec::new();
if let Ok(channels) = guild_id.channels(&ctx.http).await {
for channel in channels.values() {
let result = channel
.create_permission(
&ctx.http,
PermissionOverwrite {
allow: Permissions::empty(),
deny: mute_permissions(),
kind: PermissionOverwriteType::Role(role_id),
},
)
.await;
if result.is_err() {
failed_channels.push(channel.id.get());
}
}
}
let _ = db::set_mute_role(
&pool,
bot_id,
guild_id.get() as i64,
Some(role_id.get() as i64),
)
.await;
let mut description = format!(
"Role muet configure: <@&{}>.\nPermissions appliquees sur les salons du serveur.",
role_id.get()
);
if !failed_channels.is_empty() {
let list = failed_channels
.iter()
.take(10)
.map(|id| format!("<#{}>", id))
.collect::<Vec<_>>()
.join(", ");
description.push_str(&format!(
"\nErreurs permissions: {} salon(s). {}",
failed_channels.len(),
list
));
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("MuteRole")
.description(description)
.color(if failed_channels.is_empty() {
0x57F287
} else {
0xFEE75C
}),
)
.await;
}
pub struct MuteRoleCommand;
pub static COMMAND_DESCRIPTOR: MuteRoleCommand = MuteRoleCommand;
impl crate::commands::command_contract::CommandSpec for MuteRoleCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "muterole",
category: "mod",
params: "aucun",
description: "Cree ou met a jour le role muet et tente de corriger les permissions des salons.",
examples: &["+muterole", "+help muterole"],
default_aliases: &["mr"],
allow_in_dm: false,
default_permission: 6,
}
}
}
+176
View File
@@ -0,0 +1,176 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::automod_service::{
format_duration, parse_duration_to_seconds, parse_sanction, pool,
};
use crate::commands::common::send_embed;
use crate::db;
fn describe_rule(index: usize, rule: &db::PunishRule) -> String {
let sanction = if let Some(duration) = rule.sanction_seconds {
format!("{} {}", rule.sanction, format_duration(duration))
} else {
rule.sanction.clone()
};
format!(
"{}. {} strikes / {} -> {}",
index,
rule.threshold,
format_duration(rule.window_seconds),
sanction
)
}
pub async fn handle_punish(ctx: &Context, msg: &Message, args: &[&str]) {
let Some(guild_id) = msg.guild_id else {
return;
};
let Some(pool) = pool(ctx).await else {
return;
};
let bot_id = ctx.cache.current_user().id.get() as i64;
let guild_id_raw = guild_id.get() as i64;
if args.is_empty() {
let rules = db::list_punish_rules(&pool, bot_id, guild_id_raw)
.await
.unwrap_or_default();
let description = if rules.is_empty() {
"Aucune regle.".to_string()
} else {
rules
.iter()
.enumerate()
.map(|(idx, rule)| describe_rule(idx + 1, rule))
.collect::<Vec<_>>()
.join("\n")
};
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Punish")
.description(description)
.color(0x5865F2),
)
.await;
return;
}
if args[0].eq_ignore_ascii_case("setup") {
let _ = db::setup_default_punish_rules(&pool, bot_id, guild_id_raw).await;
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Punish")
.description("Regles par defaut restaurees.")
.color(0x57F287),
)
.await;
return;
}
if args[0].eq_ignore_ascii_case("add") {
if args.len() < 4 {
return;
}
let Ok(threshold) = args[1].parse::<i32>() else {
return;
};
let Some(window_seconds) = parse_duration_to_seconds(args[2]) else {
return;
};
let Some(sanction) = parse_sanction(args[3]) else {
return;
};
let sanction_seconds = args.get(4).and_then(|raw| parse_duration_to_seconds(raw));
let _ = db::upsert_punish_rule(
&pool,
bot_id,
guild_id_raw,
threshold.clamp(1, 200),
window_seconds,
sanction,
sanction_seconds,
)
.await;
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Punish")
.description("Regle ajoutee ou mise a jour.")
.color(0x57F287),
)
.await;
return;
}
if args[0].eq_ignore_ascii_case("del") {
let Some(raw_index) = args.get(1) else {
return;
};
let Ok(index) = raw_index.parse::<usize>() else {
return;
};
let rules = db::list_punish_rules(&pool, bot_id, guild_id_raw)
.await
.unwrap_or_default();
if index == 0 || index > rules.len() {
return;
}
let rule = &rules[index - 1];
let _ = db::delete_punish_rule_by_id(&pool, bot_id, guild_id_raw, rule.id).await;
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Punish")
.description(format!("Regle {} supprimee.", index))
.color(0x57F287),
)
.await;
return;
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Punish")
.description("Usage: +punish | +punish add <nombre> <duree> <sanction> [duree] | +punish del <numero> | +punish setup")
.color(0xED4245),
)
.await;
}
pub struct PunishCommand;
pub static COMMAND_DESCRIPTOR: PunishCommand = PunishCommand;
impl crate::commands::command_contract::CommandSpec for PunishCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "punish",
category: "mod",
params: "[add <nombre> <duree> <sanction> [duree] | del <numero> | setup]",
description: "Affiche et gere les sanctions automatiques appliquees selon les strikes.",
examples: &["+punish", "+punish add 8 1h mute 30m", "+punish setup"],
default_aliases: &["pn"],
allow_in_dm: false,
default_permission: 7,
}
}
}
+114
View File
@@ -0,0 +1,114 @@
use chrono::Utc;
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::admin_common::parse_user_id;
use crate::commands::common::{send_embed, theme_color};
use crate::db::DbPoolKey;
pub async fn handle_sanctions(ctx: &Context, msg: &Message, args: &[&str]) {
let Some(guild_id) = msg.guild_id else {
return;
};
let Some(target_raw) = args.first() else {
let _ = send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Sanctions")
.description("Usage: +sanctions <membre>")
.color(0xED4245),
)
.await;
return;
};
let Some(target) = parse_user_id(target_raw) else {
return;
};
let pool = {
let data = ctx.data.read().await;
data.get::<DbPoolKey>().cloned()
};
let Some(pool) = pool else {
return;
};
let bot_id = ctx.cache.current_user().id;
let rows = sqlx::query_as::<
_,
(
i64,
String,
String,
chrono::DateTime<Utc>,
Option<chrono::DateTime<Utc>>,
bool,
),
>(
r#"
SELECT id, kind, reason, created_at, expires_at, active
FROM bot_sanctions
WHERE bot_id = $1 AND guild_id = $2 AND user_id = $3
ORDER BY created_at DESC
LIMIT 30;
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.bind(target.get() as i64)
.fetch_all(&pool)
.await
.unwrap_or_default();
let desc = if rows.is_empty() {
"Aucune sanction.".to_string()
} else {
rows.into_iter()
.map(|(id, kind, reason, created_at, expires_at, active)| {
let until = expires_at
.map(|d| format!(" · jusqu'à <t:{}:R>", d.timestamp()))
.unwrap_or_default();
format!(
"`#{}` `{}` {} · <t:{}:R>{} · {}",
id,
kind,
if active { "(active)" } else { "(inactive)" },
created_at.timestamp(),
until,
reason
)
})
.collect::<Vec<_>>()
.join("\n")
};
send_embed(
ctx,
msg,
CreateEmbed::new()
.title(format!("Sanctions de <@{}>", target.get()))
.description(desc)
.color(theme_color(ctx).await),
)
.await;
}
pub struct SanctionsCommand;
pub static COMMAND_DESCRIPTOR: SanctionsCommand = SanctionsCommand;
impl crate::commands::command_contract::CommandSpec for SanctionsCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "sanctions",
category: "mod",
params: "<@membre/ID>",
description: "Liste l historique des sanctions d un membre.",
examples: &["+sanctions @User"],
default_aliases: &["sanct"],
allow_in_dm: false,
default_permission: 5,
}
}
}
+79
View File
@@ -0,0 +1,79 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::automod_service::pool;
use crate::commands::common::{parse_role, send_embed};
use crate::db;
pub async fn handle_set_muterole(ctx: &Context, msg: &Message, args: &[&str]) {
let Some(guild_id) = msg.guild_id else {
return;
};
let Some(raw_role) = args.get(1) else {
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Set MuteRole")
.description("Usage: +set muterole <@role/ID/nom>")
.color(0xED4245),
)
.await;
return;
};
let Ok(guild) = guild_id.to_partial_guild(&ctx.http).await else {
return;
};
let Some(role) = parse_role(&guild, raw_role) else {
return;
};
let Some(pool) = pool(ctx).await else {
return;
};
let bot_id = ctx.cache.current_user().id.get() as i64;
if db::set_mute_role(
&pool,
bot_id,
guild_id.get() as i64,
Some(role.id.get() as i64),
)
.await
.is_err()
{
return;
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("MuteRole")
.description(format!("Role muet defini sur <@&{}>.", role.id.get()))
.color(0x57F287),
)
.await;
}
pub struct SetMuteRoleCommand;
pub static COMMAND_DESCRIPTOR: SetMuteRoleCommand = SetMuteRoleCommand;
impl crate::commands::command_contract::CommandSpec for SetMuteRoleCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "set_muterole",
category: "mod",
params: "muterole <@role/ID/nom>",
description: "Definit le role utilise pour le mute lorsque le mode timeout est desactive.",
examples: &["+set muterole @Muted", "+help set muterole"],
default_aliases: &["smr"],
allow_in_dm: false,
default_permission: 7,
}
}
}
+85
View File
@@ -0,0 +1,85 @@
use chrono::Utc;
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::moderation_sanction_helpers::{
add_sanction, duration_from_input, parse_targets,
};
pub async fn handle_tempban(ctx: &Context, msg: &Message, args: &[&str]) {
let Some(guild_id) = msg.guild_id else {
return;
};
if args.is_empty() {
return;
}
let targets = parse_targets(args[0]).await;
if targets.is_empty() {
return;
}
let Some(duration_raw) = args.get(1) else {
return;
};
let Some(duration) = duration_from_input(duration_raw) else {
return;
};
let expires_at = Some(Utc::now() + chrono::Duration::seconds(duration.as_secs() as i64));
let reason = if args.len() > 2 {
args[2..].join(" ")
} else {
"Aucune raison".to_string()
};
let mut done = 0usize;
for uid in &targets {
if guild_id
.ban_with_reason(&ctx.http, *uid, 0, &reason)
.await
.is_ok()
{
done += 1;
add_sanction(
ctx,
guild_id,
*uid,
msg.author.id,
"tempban",
&reason,
None,
expires_at,
)
.await;
}
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("TempBan")
.description(format!("{} membre(s) banni(s).", done))
.color(theme_color(ctx).await),
)
.await;
}
pub struct TempbanCommand;
pub static COMMAND_DESCRIPTOR: TempbanCommand = TempbanCommand;
impl crate::commands::command_contract::CommandSpec for TempbanCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "tempban",
category: "mod",
params: "<@membre/ID[,..]> <duree> [raison]",
description: "Ban temporairement un ou plusieurs membres.",
examples: &["+tempban @User 1d"],
default_aliases: &["tb"],
allow_in_dm: false,
default_permission: 7,
}
}
}
+79
View File
@@ -0,0 +1,79 @@
use chrono::Utc;
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::moderation_sanction_helpers::{
add_sanction, channel_mute_users, duration_from_input, parse_targets,
};
pub async fn handle_tempcmute(ctx: &Context, msg: &Message, args: &[&str]) {
let Some(guild_id) = msg.guild_id else {
return;
};
if args.is_empty() {
return;
}
let targets = parse_targets(args[0]).await;
if targets.is_empty() {
return;
}
let Some(duration_raw) = args.get(1) else {
return;
};
let Some(duration) = duration_from_input(duration_raw) else {
return;
};
let expires_at = Some(Utc::now() + chrono::Duration::seconds(duration.as_secs() as i64));
let reason = if args.len() > 2 {
args[2..].join(" ")
} else {
"Aucune raison".to_string()
};
let affected = channel_mute_users(ctx, msg.channel_id, &targets, true).await;
for uid in &targets {
add_sanction(
ctx,
guild_id,
*uid,
msg.author.id,
"tempcmute",
&reason,
Some(msg.channel_id),
expires_at,
)
.await;
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("TempCMute")
.description(format!("{} membre(s) cmute.", affected))
.color(theme_color(ctx).await),
)
.await;
}
pub struct TempcmuteCommand;
pub static COMMAND_DESCRIPTOR: TempcmuteCommand = TempcmuteCommand;
impl crate::commands::command_contract::CommandSpec for TempcmuteCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "tempcmute",
category: "mod",
params: "<@membre/ID[,..]> <duree> [raison]",
description: "Mute temporaire sur le salon courant.",
examples: &["+tempcmute @User 5m"],
default_aliases: &["tcm"],
allow_in_dm: false,
default_permission: 6,
}
}
}
+79
View File
@@ -0,0 +1,79 @@
use chrono::Utc;
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::moderation_sanction_helpers::{
add_sanction, duration_from_input, handle_timeout, parse_targets,
};
pub async fn handle_tempmute(ctx: &Context, msg: &Message, args: &[&str]) {
let Some(guild_id) = msg.guild_id else {
return;
};
if args.is_empty() {
return;
}
let targets = parse_targets(args[0]).await;
if targets.is_empty() {
return;
}
let Some(duration_raw) = args.get(1) else {
return;
};
let Some(duration) = duration_from_input(duration_raw) else {
return;
};
let expires_at = Some(Utc::now() + chrono::Duration::seconds(duration.as_secs() as i64));
let reason = if args.len() > 2 {
args[2..].join(" ")
} else {
"Aucune raison".to_string()
};
let affected = handle_timeout(ctx, guild_id, &targets, expires_at).await;
for uid in &targets {
add_sanction(
ctx,
guild_id,
*uid,
msg.author.id,
"tempmute",
&reason,
None,
expires_at,
)
.await;
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("TempMute")
.description(format!("{} membre(s) mute.", affected))
.color(theme_color(ctx).await),
)
.await;
}
pub struct TempmuteCommand;
pub static COMMAND_DESCRIPTOR: TempmuteCommand = TempmuteCommand;
impl crate::commands::command_contract::CommandSpec for TempmuteCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "tempmute",
category: "mod",
params: "<@membre/ID[,..]> <duree> [raison]",
description: "Mute un ou plusieurs membres pour une duree donnee.",
examples: &["+tempmute @User 10m"],
default_aliases: &["tm"],
allow_in_dm: false,
default_permission: 6,
}
}
}
+74
View File
@@ -0,0 +1,74 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::automod_service::{parse_on_off, pool};
use crate::commands::common::send_embed;
use crate::db;
pub async fn handle_timeout_toggle(ctx: &Context, msg: &Message, args: &[&str]) {
let Some(guild_id) = msg.guild_id else {
return;
};
let Some(value) = args.first().and_then(|raw| parse_on_off(raw)) else {
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Timeout")
.description("Usage: +timeout <on/off>")
.color(0xED4245),
)
.await;
return;
};
let Some(pool) = pool(ctx).await else {
return;
};
let bot_id = ctx.cache.current_user().id.get() as i64;
let Ok(settings) =
db::set_use_timeout_for_mute(&pool, bot_id, guild_id.get() as i64, value).await
else {
return;
};
let mode = if settings.use_timeout {
"Timeout Discord"
} else {
"Role mute"
};
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Timeout")
.description(format!(
"Mode mute mis a jour: **{}**.\nNote: les timeouts Discord sont limites a 28 jours.",
mode
))
.color(0x57F287),
)
.await;
}
pub struct TimeoutCommand;
pub static COMMAND_DESCRIPTOR: TimeoutCommand = TimeoutCommand;
impl crate::commands::command_contract::CommandSpec for TimeoutCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "timeout",
category: "mod",
params: "<on/off>",
description: "Active ou desactive l utilisation du timeout Discord pour les mutes.",
examples: &["+timeout on", "+timeout off", "+help timeout"],
default_aliases: &["to"],
allow_in_dm: false,
default_permission: 6,
}
}
}
+53
View File
@@ -0,0 +1,53 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::moderation_sanction_helpers::parse_targets;
pub async fn handle_unban(ctx: &Context, msg: &Message, args: &[&str]) {
let Some(guild_id) = msg.guild_id else {
return;
};
if args.is_empty() {
return;
}
let targets = parse_targets(args[0]).await;
if targets.is_empty() {
return;
}
let mut done = 0usize;
for uid in &targets {
if guild_id.unban(&ctx.http, *uid).await.is_ok() {
done += 1;
}
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("UnBan")
.description(format!("{} membre(s) unban.", done))
.color(theme_color(ctx).await),
)
.await;
}
pub struct UnbanCommand;
pub static COMMAND_DESCRIPTOR: UnbanCommand = UnbanCommand;
impl crate::commands::command_contract::CommandSpec for UnbanCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "unban",
category: "mod",
params: "<@membre/ID[,..]>",
description: "Unban un ou plusieurs membres.",
examples: &["+unban @User"],
default_aliases: &["ub"],
allow_in_dm: false,
default_permission: 6,
}
}
}
+51
View File
@@ -0,0 +1,51 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
pub async fn handle_unbanall(ctx: &Context, msg: &Message, _args: &[&str]) {
let Some(guild_id) = msg.guild_id else {
return;
};
let bans = guild_id
.bans(&ctx.http, None, None)
.await
.unwrap_or_default();
let mut unbanned = 0usize;
for ban in bans {
if guild_id.unban(&ctx.http, ban.user.id).await.is_ok() {
unbanned += 1;
}
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("UnbanAll")
.description(format!("{} bannissements retirés.", unbanned))
.color(theme_color(ctx).await),
)
.await;
}
pub struct UnbanAllCommand;
pub static COMMAND_DESCRIPTOR: UnbanAllCommand = UnbanAllCommand;
impl crate::commands::command_contract::CommandSpec for UnbanAllCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "unbanall",
category: "mod",
params: "aucun",
description: "Supprime tous les bans du serveur cible.",
examples: &["+unbanall"],
default_aliases: &["uball", "clearbans"],
allow_in_dm: false,
default_permission: 8,
}
}
}
+67
View File
@@ -0,0 +1,67 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::moderation_sanction_helpers::{channel_mute_users, parse_targets, pool};
pub async fn handle_uncmute(ctx: &Context, msg: &Message, args: &[&str]) {
let Some(guild_id) = msg.guild_id else {
return;
};
if args.is_empty() {
return;
}
let targets = parse_targets(args[0]).await;
if targets.is_empty() {
return;
}
let affected = channel_mute_users(ctx, msg.channel_id, &targets, false).await;
if let Some(pool) = pool(ctx).await {
let bot_id = ctx.cache.current_user().id;
for uid in &targets {
let _ = sqlx::query(
r#"
UPDATE bot_sanctions
SET active = FALSE
WHERE bot_id = $1 AND guild_id = $2 AND user_id = $3 AND active = TRUE AND kind IN ('cmute','tempcmute') AND channel_id = $4;
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.bind(uid.get() as i64)
.bind(msg.channel_id.get() as i64)
.execute(&pool)
.await;
}
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("UnCMute")
.description(format!("{} membre(s) uncmute.", affected))
.color(theme_color(ctx).await),
)
.await;
}
pub struct UncmuteCommand;
pub static COMMAND_DESCRIPTOR: UncmuteCommand = UncmuteCommand;
impl crate::commands::command_contract::CommandSpec for UncmuteCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "uncmute",
category: "mod",
params: "<@membre/ID[,..]>",
description: "Met fin au mute salon.",
examples: &["+uncmute @User"],
default_aliases: &["ucm"],
allow_in_dm: false,
default_permission: 6,
}
}
}
+66
View File
@@ -0,0 +1,66 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::moderation_sanction_helpers::{handle_timeout, parse_targets, pool};
pub async fn handle_unmute(ctx: &Context, msg: &Message, args: &[&str]) {
let Some(guild_id) = msg.guild_id else {
return;
};
if args.is_empty() {
return;
}
let targets = parse_targets(args[0]).await;
if targets.is_empty() {
return;
}
let affected = handle_timeout(ctx, guild_id, &targets, None).await;
if let Some(pool) = pool(ctx).await {
let bot_id = ctx.cache.current_user().id;
for uid in &targets {
let _ = sqlx::query(
r#"
UPDATE bot_sanctions
SET active = FALSE
WHERE bot_id = $1 AND guild_id = $2 AND user_id = $3 AND active = TRUE AND kind IN ('mute','tempmute');
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.bind(uid.get() as i64)
.execute(&pool)
.await;
}
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("UnMute")
.description(format!("{} membre(s) unmute.", affected))
.color(theme_color(ctx).await),
)
.await;
}
pub struct UnmuteCommand;
pub static COMMAND_DESCRIPTOR: UnmuteCommand = UnmuteCommand;
impl crate::commands::command_contract::CommandSpec for UnmuteCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "unmute",
category: "mod",
params: "<@membre/ID[,..]>",
description: "Met fin au mute d un ou plusieurs membres.",
examples: &["+unmute @User"],
default_aliases: &["um"],
allow_in_dm: false,
default_permission: 6,
}
}
}
+81
View File
@@ -0,0 +1,81 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::moderation_sanction_helpers::{channel_mute_users, handle_timeout, pool};
pub async fn handle_unmuteall(ctx: &Context, msg: &Message) {
let Some(guild_id) = msg.guild_id else {
return;
};
let Some(pool) = pool(ctx).await else {
return;
};
let bot_id = ctx.cache.current_user().id;
let rows = sqlx::query_as::<_, (i64, String, Option<i64>)>(
r#"
SELECT user_id, kind, channel_id
FROM bot_sanctions
WHERE bot_id = $1 AND guild_id = $2 AND active = TRUE AND kind IN ('mute','tempmute','cmute','tempcmute');
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.fetch_all(&pool)
.await
.unwrap_or_default();
let mut changed = 0usize;
for (uid, kind, channel_id) in rows {
let user_id = UserId::new(uid as u64);
if kind == "mute" || kind == "tempmute" {
changed += handle_timeout(ctx, guild_id, &[user_id], None).await;
} else if let Some(cid) = channel_id {
changed += channel_mute_users(ctx, ChannelId::new(cid as u64), &[user_id], false).await;
}
}
let _ = sqlx::query(
r#"
UPDATE bot_sanctions
SET active = FALSE
WHERE bot_id = $1 AND guild_id = $2 AND active = TRUE AND kind IN ('mute','tempmute','cmute','tempcmute');
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.execute(&pool)
.await;
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("UnMuteAll")
.description(format!(
"{} operation(s) de unmute/cmute annule(es).",
changed
))
.color(theme_color(ctx).await),
)
.await;
}
pub struct UnmuteallCommand;
pub static COMMAND_DESCRIPTOR: UnmuteallCommand = UnmuteallCommand;
impl crate::commands::command_contract::CommandSpec for UnmuteallCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "unmuteall",
category: "mod",
params: "aucun",
description: "Supprime tous les mutes en cours.",
examples: &["+unmuteall"],
default_aliases: &["uma"],
allow_in_dm: false,
default_permission: 8,
}
}
}
+66
View File
@@ -0,0 +1,66 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::moderation_sanction_helpers::{add_sanction, parse_targets};
pub async fn handle_warn(ctx: &Context, msg: &Message, args: &[&str]) {
let Some(guild_id) = msg.guild_id else {
return;
};
if args.is_empty() {
return;
}
let targets = parse_targets(args[0]).await;
if targets.is_empty() {
return;
}
let reason = if args.len() > 1 {
args[1..].join(" ")
} else {
"Aucune raison".to_string()
};
for uid in &targets {
add_sanction(
ctx,
guild_id,
*uid,
msg.author.id,
"warn",
&reason,
None,
None,
)
.await;
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Warn")
.description(format!("{} membre(s) warn.", targets.len()))
.color(theme_color(ctx).await),
)
.await;
}
pub struct WarnCommand;
pub static COMMAND_DESCRIPTOR: WarnCommand = WarnCommand;
impl crate::commands::command_contract::CommandSpec for WarnCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "warn",
category: "mod",
params: "<@membre/ID[,..]> [raison]",
description: "Ajoute un warn a un ou plusieurs membres.",
examples: &["+warn @User spam"],
default_aliases: &["avert"],
allow_in_dm: false,
default_permission: 5,
}
}
}