modify commands categories

This commit is contained in:
Puechberty Arthur
2026-04-10 06:54:51 +02:00
parent e1016e0af1
commit b6d8953c46
126 changed files with 259 additions and 251 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: "moderation",
params: "<@membre/ID[,..]> [raison]",
summary: "Bannit un membre",
description: "Ban un ou plusieurs membres.",
examples: &["+ban @User"],
default_aliases: &["b"],
default_permission: 8,
}
}
}
+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: "moderation",
params: "aucun",
summary: "Liste les bans",
description: "Affiche la liste des bannissements en cours.",
examples: &["+banlist"],
default_aliases: &["bls"],
default_permission: 8,
}
}
}
+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: "moderation",
params: "<salon_vocal>",
summary: "Vide un salon vocal",
description: "Deconnecte tous les utilisateurs presents dans un salon vocal cible.",
examples: &["+cleanup #General"],
default_aliases: &["vclean", "vcleanup"],
default_permission: 8,
}
}
}
@@ -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: "moderation",
params: "aucun",
summary: "Supprime toutes les sanctions du serveur",
description: "Efface toutes les sanctions de tous les membres du serveur.",
examples: &["+clear all sanctions"],
default_aliases: &["casanctions"],
default_permission: 8,
}
}
}
+67
View File
@@ -0,0 +1,67 @@
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};
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;
}
amount = amount.clamp(1, 100);
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: "moderation",
params: "<nombre> [@membre/ID]",
summary: "Supprime des messages dans le salon",
description: "Supprime un nombre de messages, optionnellement filtres par membre.",
examples: &["+clear 20", "+clear 20 @User"],
default_aliases: &["purge"],
default_permission: 8,
}
}
}
@@ -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: "moderation",
params: "<@membre/ID>",
summary: "Supprime toutes les sanctions d un membre",
description: "Efface completement les sanctions d un membre cible.",
examples: &["+clear sanctions @User"],
default_aliases: &["csanctions"],
default_permission: 8,
}
}
}
+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: "moderation",
params: "<@membre/ID[,..]> [raison]",
summary: "Mute salon",
description: "Mute un membre sur le salon courant.",
examples: &["+cmute @User"],
default_aliases: &["cm"],
default_permission: 8,
}
}
}
+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: "moderation",
params: "<@membre/ID> <nombre>",
summary: "Supprime une sanction d un membre",
description: "Supprime une sanction specifique dans l historique d un membre.",
examples: &["+del sanction @User 1"],
default_aliases: &["delsanction"],
default_permission: 8,
}
}
}
+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: "moderation",
params: "<@membre/ID[,..]> [raison]",
summary: "Expulse un membre",
description: "Kick un ou plusieurs membres.",
examples: &["+kick @User"],
default_aliases: &["k"],
default_permission: 8,
}
}
}
+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: "moderation",
params: "<@membre/ID[,..]> [raison]",
summary: "Mute un membre",
description: "Applique un mute a un ou plusieurs membres.",
examples: &["+mute @User abus"],
default_aliases: &["tmute"],
default_permission: 8,
}
}
}
+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: "moderation",
params: "aucun",
summary: "Liste les mutes",
description: "Affiche tous les mutes en cours.",
examples: &["+mutelist"],
default_aliases: &["ml"],
default_permission: 8,
}
}
}
+67
View File
@@ -0,0 +1,67 @@
use serenity::builder::CreateChannel;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::common::parse_channel_id;
pub async fn handle_renew(ctx: &Context, msg: &Message, args: &[&str]) {
let Some(guild_id) = msg.guild_id else {
return;
};
let channel_id = args
.first()
.and_then(|raw| parse_channel_id(raw))
.unwrap_or(msg.channel_id);
let Ok(channel) = channel_id.to_channel(&ctx.http).await else {
return;
};
let Channel::Guild(text_channel) = channel else {
return;
};
if text_channel.kind != ChannelType::Text && text_channel.kind != ChannelType::News {
return;
}
let parent_id = text_channel.parent_id;
let topic = text_channel.topic.clone();
let nsfw = text_channel.nsfw;
let slowmode = text_channel.rate_limit_per_user;
let name = text_channel.name.clone();
let _ = text_channel.delete(&ctx.http).await;
let mut builder = CreateChannel::new(name)
.kind(ChannelType::Text)
.nsfw(nsfw)
.rate_limit_per_user(slowmode.unwrap_or(0));
if let Some(parent) = parent_id {
builder = builder.category(parent);
}
if let Some(topic) = topic {
builder = builder.topic(topic);
}
let _ = guild_id.create_channel(&ctx.http, builder).await;
}
pub struct RenewCommand;
pub static COMMAND_DESCRIPTOR: RenewCommand = RenewCommand;
impl crate::commands::command_contract::CommandSpec for RenewCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "renew",
category: "moderation",
params: "[salon]",
summary: "Recree un salon textuel",
description: "Supprime puis recree un salon textuel en conservant les options principales.",
examples: &["+renew", "+renew #general"],
default_aliases: &["nuke", "rebuildch"],
default_permission: 8,
}
}
}
+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: "moderation",
params: "<@membre/ID>",
summary: "Affiche les sanctions d un membre",
description: "Liste l historique des sanctions d un membre.",
examples: &["+sanctions @User"],
default_aliases: &["sanct"],
default_permission: 8,
}
}
}
+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: "moderation",
params: "<@membre/ID[,..]> <duree> [raison]",
summary: "Ban temporaire",
description: "Ban temporairement un ou plusieurs membres.",
examples: &["+tempban @User 1d"],
default_aliases: &["tb"],
default_permission: 8,
}
}
}
+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: "moderation",
params: "<@membre/ID[,..]> <duree> [raison]",
summary: "Mute salon temporaire",
description: "Mute temporaire sur le salon courant.",
examples: &["+tempcmute @User 5m"],
default_aliases: &["tcm"],
default_permission: 8,
}
}
}
+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: "moderation",
params: "<@membre/ID[,..]> <duree> [raison]",
summary: "Mute temporaire",
description: "Mute un ou plusieurs membres pour une duree donnee.",
examples: &["+tempmute @User 10m"],
default_aliases: &["tm"],
default_permission: 8,
}
}
}
+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: "moderation",
params: "<@membre/ID[,..]>",
summary: "Retire un ban",
description: "Unban un ou plusieurs membres.",
examples: &["+unban @User"],
default_aliases: &["ub"],
default_permission: 8,
}
}
}
+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: "moderation",
params: "aucun",
summary: "Retire tous les bannissements",
description: "Supprime tous les bans du serveur cible.",
examples: &["+unbanall"],
default_aliases: &["uball", "clearbans"],
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: "moderation",
params: "<@membre/ID[,..]>",
summary: "Retire un cmute",
description: "Met fin au mute salon.",
examples: &["+uncmute @User"],
default_aliases: &["ucm"],
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::{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: "moderation",
params: "<@membre/ID[,..]>",
summary: "Retire un mute",
description: "Met fin au mute d un ou plusieurs membres.",
examples: &["+unmute @User"],
default_aliases: &["um"],
default_permission: 8,
}
}
}
+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: "moderation",
params: "aucun",
summary: "Retire tous les mutes",
description: "Supprime tous les mutes en cours.",
examples: &["+unmuteall"],
default_aliases: &["uma"],
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: "moderation",
params: "<@membre/ID[,..]> [raison]",
summary: "Donne un warn",
description: "Ajoute un warn a un ou plusieurs membres.",
examples: &["+warn @User spam"],
default_aliases: &["avert"],
default_permission: 8,
}
}
}