Refactor profile commands to improve status handling and embed responses

- Updated `handle_idle`, `handle_invisible`, `handle_online`, `handle_listen`, `handle_playto`, `handle_stream`, `handle_watch`, and `handle_remove_activity` functions to use a unified approach for setting bot status and sending embed messages.
- Removed dependency on `botconfig_common` and replaced it with direct database interactions for status management.
- Added new helper functions for logging and moderation channel management.
- Introduced permission handling improvements in `set` command with better error messages and user feedback.
- Created new utility functions for parsing user and role IDs, ensuring better command access control.
This commit is contained in:
Puechberty Arthur
2026-04-10 06:42:46 +02:00
parent bc623a7736
commit e1016e0af1
146 changed files with 5434 additions and 4237 deletions
+41 -5
View File
@@ -1,9 +1,47 @@
use crate::commands::moderation_tools; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::admin_common::parse_user_id;
use crate::commands::common::{parse_role, send_embed, theme_color};
pub async fn handle_addrole(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_addrole(ctx: &Context, msg: &Message, args: &[&str]) {
moderation_tools::handle_add_del_role(ctx, msg, args, true).await; let Some(guild_id) = msg.guild_id else {
return;
};
if args.len() < 2 {
return;
}
let Some(target) = parse_user_id(args[0]) else {
return;
};
let Ok(guild) = guild_id.to_partial_guild(&ctx.http).await else {
return;
};
let Some(role) = parse_role(&guild, args[1]) else {
return;
};
let done = if let Ok(member) = guild_id.member(&ctx.http, target).await {
member.add_role(&ctx.http, role.id).await.is_ok()
} else {
false
};
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("AddRole")
.description(if done {
format!("Role <@&{}> ajoute a <@{}>.", role.id.get(), target.get())
} else {
"Echec de modification du role.".to_string()
})
.color(theme_color(ctx).await),
)
.await;
} }
pub struct AddroleCommand; pub struct AddroleCommand;
@@ -12,14 +50,12 @@ pub static COMMAND_DESCRIPTOR: AddroleCommand = AddroleCommand;
impl crate::commands::command_contract::CommandSpec for AddroleCommand { impl crate::commands::command_contract::CommandSpec for AddroleCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "addrole", name: "addrole",
command: "addrole",
category: "admin", category: "admin",
params: "<@membre/ID[,..]> <@role/ID>", params: "<@membre/ID[,..]> <@role/ID>",
summary: "Ajoute un role", summary: "Ajoute un role",
description: "Ajoute un role a un ou plusieurs membres.", description: "Ajoute un role a un ou plusieurs membres.",
examples: &["+addrole @User @Membre"], examples: &["+addrole @User @Membre"],
alias_source_key: "addrole",
default_aliases: &["ar"], default_aliases: &["ar"],
default_permission: 8, default_permission: 8,
} }
+62 -4
View File
@@ -1,10 +1,70 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::advanced_tools; use crate::commands::advanced_tools;
use crate::commands::common::{send_embed, theme_color};
use crate::db::DbPoolKey;
async fn pool(ctx: &Context) -> Option<sqlx::PgPool> {
let data = ctx.data.read().await;
data.get::<DbPoolKey>().cloned()
}
pub async fn handle_autobackup(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_autobackup(ctx: &Context, msg: &Message, args: &[&str]) {
advanced_tools::handle_autobackup(ctx, msg, args).await; let Some(guild_id) = msg.guild_id else {
return;
};
let Some(kind_raw) = args.first() else {
return;
};
let Some(days_raw) = args.get(1) else {
return;
};
let Some(kind) = advanced_tools::backup_kind_from_input(kind_raw) else {
return;
};
let Ok(days) = days_raw.parse::<i32>() else {
return;
};
let Some(pool) = pool(ctx).await else {
return;
};
let bot_id = ctx.cache.current_user().id;
let _ = sqlx::query(
r#"
INSERT INTO bot_autobackups (bot_id, guild_id, kind, interval_days, next_run_at)
VALUES ($1, $2, $3, $4, NOW() + make_interval(days => $4))
ON CONFLICT (bot_id, guild_id, kind)
DO UPDATE SET interval_days = EXCLUDED.interval_days,
next_run_at = NOW() + make_interval(days => EXCLUDED.interval_days);
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.bind(kind)
.bind(days.max(1))
.execute(&pool)
.await;
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("AutoBackup")
.description(format!(
"Auto-backup `{}` configuree toutes les {} jours.",
kind,
days.max(1)
))
.color(theme_color(ctx).await),
)
.await;
} }
pub struct AutoBackupCommand; pub struct AutoBackupCommand;
@@ -13,14 +73,12 @@ pub static COMMAND_DESCRIPTOR: AutoBackupCommand = AutoBackupCommand;
impl crate::commands::command_contract::CommandSpec for AutoBackupCommand { impl crate::commands::command_contract::CommandSpec for AutoBackupCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "autobackup", name: "autobackup",
command: "autobackup",
category: "admin", category: "admin",
params: "<serveur/emoji> <jours>", params: "<serveur/emoji> <jours>",
summary: "Configure les backups automatiques", summary: "Configure les backups automatiques",
description: "Definit l'intervalle en jours des backups automatiques.", description: "Definit l'intervalle en jours des backups automatiques.",
examples: &["+autobackup serveur 3", "+autobackup emoji 7"], examples: &["+autobackup serveur 3", "+autobackup emoji 7"],
alias_source_key: "autobackup",
default_aliases: &["abkp"], default_aliases: &["abkp"],
default_permission: 8, default_permission: 8,
} }
+44 -5
View File
@@ -1,9 +1,50 @@
use crate::commands::logs_service; use serenity::builder::{CreateChannel, CreateEmbed};
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::logs_command_helpers::set_log_channel;
const LOG_TYPES: &[(&str, &str)] = &[
("moderation", "modlog"),
("message", "messagelog"),
("voice", "voicelog"),
("boost", "boostlog"),
("role", "rolelog"),
("raid", "raidlog"),
("channel", "channellog"),
];
pub async fn handle_autoconfiglog(ctx: &Context, msg: &Message) { pub async fn handle_autoconfiglog(ctx: &Context, msg: &Message) {
logs_service::handle_autoconfiglog(ctx, msg).await; let Some(guild_id) = msg.guild_id else {
return;
};
let mut created = Vec::new();
for (log_type, cmd) in LOG_TYPES {
let name = format!("{}-logs", cmd.replace("log", ""));
if let Ok(channel) = guild_id
.create_channel(&ctx.http, CreateChannel::new(name).kind(ChannelType::Text))
.await
{
set_log_channel(ctx, guild_id, log_type, Some(channel.id), true).await;
created.push(format!("{} -> <#{}>", log_type, channel.id.get()));
}
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("AutoConfigLog")
.description(if created.is_empty() {
"Aucun salon cree.".to_string()
} else {
created.join("\n")
})
.color(theme_color(ctx).await),
)
.await;
} }
pub struct AutoconfiglogCommand; pub struct AutoconfiglogCommand;
@@ -12,14 +53,12 @@ pub static COMMAND_DESCRIPTOR: AutoconfiglogCommand = AutoconfiglogCommand;
impl crate::commands::command_contract::CommandSpec for AutoconfiglogCommand { impl crate::commands::command_contract::CommandSpec for AutoconfiglogCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "autoconfiglog", name: "autoconfiglog",
command: "autoconfiglog",
category: "admin", category: "admin",
params: "aucun", params: "aucun",
summary: "Cree tous les salons de logs", summary: "Cree tous les salons de logs",
description: "Cree automatiquement les salons de logs et les configure.", description: "Cree automatiquement les salons de logs et les configure.",
examples: &["+autoconfiglog"], examples: &["+autoconfiglog"],
alias_source_key: "autoconfiglog",
default_aliases: &["acl"], default_aliases: &["acl"],
default_permission: 8, default_permission: 8,
} }
+22
View File
@@ -110,3 +110,25 @@ pub async fn handle_autopublish(ctx: &Context, msg: &Message, args: &[&str]) {
send_embed(ctx, msg, embed).await; send_embed(ctx, msg, embed).await;
} }
pub struct AutopublishCommand;
pub static COMMAND_DESCRIPTOR: AutopublishCommand = AutopublishCommand;
impl crate::commands::command_contract::CommandSpec for AutopublishCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "autopublish",
category: "admin",
params: "on|off [#canal]",
summary: "Configure lautopublish",
description: "Affiche, active ou desactive la publication automatique des annonces.",
examples: &[
"+autopublish",
"+autopublish on #annonces",
"+help autopublish",
],
default_aliases: &["apb"],
default_permission: 8,
}
}
}
+133 -5
View File
@@ -1,10 +1,140 @@
use serenity::builder::{CreateActionRow, CreateButton, CreateEmbed, CreateMessage};
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::advanced_tools; use crate::commands::common::{parse_channel_id, send_embed, theme_color};
use crate::db::DbPoolKey;
fn owned_component_id(action: &str, owner_id: UserId) -> String {
format!("{}:{}", action, owner_id.get())
}
async fn pool(ctx: &Context) -> Option<sqlx::PgPool> {
let data = ctx.data.read().await;
data.get::<DbPoolKey>().cloned()
}
pub async fn handle_autoreact(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_autoreact(ctx: &Context, msg: &Message, args: &[&str]) {
advanced_tools::handle_autoreact(ctx, msg, args).await; let Some(guild_id) = msg.guild_id else {
return;
};
let Some(action) = args.first().map(|s| s.to_lowercase()) else {
let embed = CreateEmbed::new()
.title("AutoReact")
.description("Utilise les boutons pour ajouter/supprimer/lister via UI.")
.color(theme_color(ctx).await);
let components = vec![CreateActionRow::Buttons(vec![
CreateButton::new(owned_component_id("adv:autoreact:add_modal", msg.author.id))
.label("Ajouter")
.style(ButtonStyle::Success),
CreateButton::new(owned_component_id("adv:autoreact:del_modal", msg.author.id))
.label("Supprimer")
.style(ButtonStyle::Danger),
CreateButton::new(owned_component_id("adv:autoreact:list", msg.author.id))
.label("Lister")
.style(ButtonStyle::Primary),
])];
let _ = msg
.channel_id
.send_message(
&ctx.http,
CreateMessage::new().embed(embed).components(components),
)
.await;
return;
};
let Some(pool) = pool(ctx).await else {
return;
};
let bot_id = ctx.cache.current_user().id;
if action == "list" {
let rows = sqlx::query_as::<_, (i64, String)>(
r#"
SELECT channel_id, emoji
FROM bot_autoreacts
WHERE bot_id = $1 AND guild_id = $2
ORDER BY channel_id ASC, emoji ASC;
"#,
)
.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 autoreact configure.".to_string()
} else {
rows.into_iter()
.map(|(channel_id, emoji)| format!("- <#{}> -> {}", channel_id, emoji))
.collect::<Vec<_>>()
.join("\n")
};
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("AutoReact")
.description(desc)
.color(theme_color(ctx).await),
)
.await;
return;
}
if args.len() < 3 {
return;
}
let Some(channel_id) = parse_channel_id(args[1]) else {
return;
};
let emoji = args[2];
if action == "add" {
let _ = sqlx::query(
r#"
INSERT INTO bot_autoreacts (bot_id, guild_id, channel_id, emoji)
VALUES ($1, $2, $3, $4)
ON CONFLICT (bot_id, guild_id, channel_id, emoji) DO NOTHING;
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.bind(channel_id.get() as i64)
.bind(emoji)
.execute(&pool)
.await;
} else if action == "del" {
let _ = sqlx::query(
r#"
DELETE FROM bot_autoreacts
WHERE bot_id = $1 AND guild_id = $2 AND channel_id = $3 AND emoji = $4;
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.bind(channel_id.get() as i64)
.bind(emoji)
.execute(&pool)
.await;
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("AutoReact")
.description("Configuration mise a jour.")
.color(theme_color(ctx).await),
)
.await;
} }
pub struct AutoReactCommand; pub struct AutoReactCommand;
@@ -13,14 +143,12 @@ pub static COMMAND_DESCRIPTOR: AutoReactCommand = AutoReactCommand;
impl crate::commands::command_contract::CommandSpec for AutoReactCommand { impl crate::commands::command_contract::CommandSpec for AutoReactCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "autoreact", name: "autoreact",
command: "autoreact",
category: "admin", category: "admin",
params: "<add/del> <salon> <emoji> | list", params: "<add/del> <salon> <emoji> | list",
summary: "Configure les reactions automatiques", summary: "Configure les reactions automatiques",
description: "Ajoute, retire et liste les reactions automatiquement appliquees aux messages d'un salon.", description: "Ajoute, retire et liste les reactions automatiquement appliquees aux messages d'un salon.",
examples: &["+autoreact add #general 😀", "+autoreact list"], examples: &["+autoreact add #general 😀", "+autoreact list"],
alias_source_key: "autoreact",
default_aliases: &["ar", "reactauto"], default_aliases: &["ar", "reactauto"],
default_permission: 8, default_permission: 8,
} }
+152 -4
View File
@@ -1,10 +1,160 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::advanced_tools; use crate::commands::advanced_tools;
use crate::commands::common::{send_embed, theme_color};
pub async fn handle_backup(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_backup(ctx: &Context, msg: &Message, args: &[&str]) {
advanced_tools::handle_backup(ctx, msg, args).await; let Some(guild_id) = msg.guild_id else {
return;
};
if args.is_empty() {
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Backup")
.description("Utilisation: +backup <serveur/emoji> <nom> | list/delete/load")
.color(0xED4245),
)
.await;
return;
}
let mut action = "create";
let mut index = 0usize;
if let Some(first) = args.first() {
match first.to_lowercase().as_str() {
"list" | "ls" => {
action = "list";
index = 1;
}
"delete" | "del" | "rm" => {
action = "delete";
index = 1;
}
"load" => {
action = "load";
index = 1;
}
_ => {}
}
}
let Some(kind_raw) = args.get(index) else {
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Backup")
.description("Type invalide: utilise serveur ou emoji.")
.color(0xED4245),
)
.await;
return;
};
let Some(kind) = advanced_tools::backup_kind_from_input(kind_raw) else {
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Backup")
.description("Type invalide: utilise serveur ou emoji.")
.color(0xED4245),
)
.await;
return;
};
match action {
"list" => {
advanced_tools::backup_list(ctx, msg, guild_id, kind).await;
}
"delete" => {
let Some(name) = args
.get(index + 1)
.map(|s| s.trim())
.filter(|s| !s.is_empty())
else {
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Backup")
.description("Tu dois preciser un nom de backup.")
.color(0xED4245),
)
.await;
return;
};
advanced_tools::backup_delete(ctx, msg, guild_id, kind, name).await;
}
"load" => {
let Some(name) = args
.get(index + 1)
.map(|s| s.trim())
.filter(|s| !s.is_empty())
else {
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Backup")
.description("Tu dois preciser un nom de backup.")
.color(0xED4245),
)
.await;
return;
};
advanced_tools::backup_load(ctx, msg, guild_id, kind, name).await;
}
_ => {
let Some(name) = args
.get(index + 1)
.map(|s| s.trim())
.filter(|s| !s.is_empty())
else {
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Backup")
.description("Tu dois preciser un nom de backup.")
.color(0xED4245),
)
.await;
return;
};
match advanced_tools::backup_create(ctx, guild_id, kind, name).await {
Ok(()) => {
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Backup")
.description(format!("Backup `{}` de type `{}` creee.", name, kind))
.color(theme_color(ctx).await),
)
.await;
}
Err(err) => {
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Backup")
.description(format!("Erreur: {}", err))
.color(0xED4245),
)
.await;
}
}
}
}
} }
pub struct BackupCommand; pub struct BackupCommand;
@@ -13,8 +163,7 @@ pub static COMMAND_DESCRIPTOR: BackupCommand = BackupCommand;
impl crate::commands::command_contract::CommandSpec for BackupCommand { impl crate::commands::command_contract::CommandSpec for BackupCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "backup", name: "backup",
command: "backup",
category: "admin", category: "admin",
params: "<serveur/emoji> <nom> | list/delete/load", params: "<serveur/emoji> <nom> | list/delete/load",
summary: "Gere les backups serveur et emojis", summary: "Gere les backups serveur et emojis",
@@ -24,7 +173,6 @@ impl crate::commands::command_contract::CommandSpec for BackupCommand {
"+backup list serveur", "+backup list serveur",
"+backup load emoji nightly", "+backup load emoji nightly",
], ],
alias_source_key: "backup",
default_aliases: &["bkp"], default_aliases: &["bkp"],
default_permission: 8, default_permission: 8,
} }
+55 -5
View File
@@ -1,22 +1,72 @@
use crate::commands::moderation_tools; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::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]) { pub async fn handle_ban(ctx: &Context, msg: &Message, args: &[&str]) {
moderation_tools::handle_ban(ctx, msg, args, false).await; 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 struct BanCommand;
pub static COMMAND_DESCRIPTOR: BanCommand = BanCommand; pub static COMMAND_DESCRIPTOR: BanCommand = BanCommand;
impl crate::commands::command_contract::CommandSpec for BanCommand { impl crate::commands::command_contract::CommandSpec for BanCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "ban", name: "ban",
command: "ban",
category: "admin", category: "admin",
params: "<@membre/ID[,..]> [raison]", params: "<@membre/ID[,..]> [raison]",
summary: "Bannit un membre", summary: "Bannit un membre",
description: "Ban un ou plusieurs membres.", description: "Ban un ou plusieurs membres.",
examples: &["+ban @User"], examples: &["+ban @User"],
alias_source_key: "ban",
default_aliases: &["b"], default_aliases: &["b"],
default_permission: 8, default_permission: 8,
} }
+31 -5
View File
@@ -1,22 +1,48 @@
use crate::commands::moderation_tools; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
pub async fn handle_banlist(ctx: &Context, msg: &Message) { pub async fn handle_banlist(ctx: &Context, msg: &Message) {
moderation_tools::handle_banlist(ctx, msg).await; 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 struct BanlistCommand;
pub static COMMAND_DESCRIPTOR: BanlistCommand = BanlistCommand; pub static COMMAND_DESCRIPTOR: BanlistCommand = BanlistCommand;
impl crate::commands::command_contract::CommandSpec for BanlistCommand { impl crate::commands::command_contract::CommandSpec for BanlistCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "banlist", name: "banlist",
command: "banlist",
category: "admin", category: "admin",
params: "aucun", params: "aucun",
summary: "Liste les bans", summary: "Liste les bans",
description: "Affiche la liste des bannissements en cours.", description: "Affiche la liste des bannissements en cours.",
examples: &["+banlist"], examples: &["+banlist"],
alias_source_key: "banlist",
default_aliases: &["bls"], default_aliases: &["bls"],
default_permission: 8, default_permission: 8,
} }
+1 -3
View File
@@ -78,14 +78,12 @@ pub static COMMAND_DESCRIPTOR: BlCommand = BlCommand;
impl crate::commands::command_contract::CommandSpec for BlCommand { impl crate::commands::command_contract::CommandSpec for BlCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "bl", name: "bl",
command: "bl",
category: "admin", category: "admin",
params: "[<@membre/ID> [raison...]]", params: "[<@membre/ID> [raison...]]",
summary: "Gere la blacklist globale", summary: "Gere la blacklist globale",
description: "Affiche la blacklist ou ajoute un utilisateur a la blacklist globale du bot.", description: "Affiche la blacklist ou ajoute un utilisateur a la blacklist globale du bot.",
examples: &["+bl", "+help bl"], examples: &["+bl", "+help bl"],
alias_source_key: "bl",
default_aliases: &["bls"], default_aliases: &["bls"],
default_permission: 9, default_permission: 9,
} }
+1 -3
View File
@@ -83,14 +83,12 @@ pub static COMMAND_DESCRIPTOR: BlinfoCommand = BlinfoCommand;
impl crate::commands::command_contract::CommandSpec for BlinfoCommand { impl crate::commands::command_contract::CommandSpec for BlinfoCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "blinfo", name: "blinfo",
command: "blinfo",
category: "admin", category: "admin",
params: "<@membre/ID>", params: "<@membre/ID>",
summary: "Affiche les details blacklist", summary: "Affiche les details blacklist",
description: "Affiche les details de blacklist pour un utilisateur donne.", description: "Affiche les details de blacklist pour un utilisateur donne.",
examples: &["+blinfo", "+bo", "+help blinfo"], examples: &["+blinfo", "+bo", "+help blinfo"],
alias_source_key: "blinfo",
default_aliases: &["bli"], default_aliases: &["bli"],
default_permission: 9, default_permission: 9,
} }
+52 -18
View File
@@ -119,7 +119,11 @@ async fn set_boost_log_channel(
.await; .await;
} }
async fn read_settings(pool: &sqlx::PgPool, bot_id: UserId, guild_id: GuildId) -> BoostEmbedSettings { async fn read_settings(
pool: &sqlx::PgPool,
bot_id: UserId,
guild_id: GuildId,
) -> BoostEmbedSettings {
let row = sqlx::query_as::<_, (bool, Option<String>, Option<String>, Option<i32>)>( let row = sqlx::query_as::<_, (bool, Option<String>, Option<String>, Option<i32>)>(
r#" r#"
SELECT enabled, title, description, color SELECT enabled, title, description, color
@@ -235,20 +239,32 @@ fn settings_components(owner_id: UserId, settings: &BoostEmbedSettings) -> Vec<C
CreateButton::new(format!("{}:set_here:{}", BOOSTEMBED_MENU, owner_id.get())) CreateButton::new(format!("{}:set_here:{}", BOOSTEMBED_MENU, owner_id.get()))
.label("Salon = ici") .label("Salon = ici")
.style(ButtonStyle::Success), .style(ButtonStyle::Success),
CreateButton::new(format!("{}:edit_channel:{}", BOOSTEMBED_MENU, owner_id.get())) CreateButton::new(format!(
.label("Définir salon") "{}:edit_channel:{}",
.style(ButtonStyle::Secondary), BOOSTEMBED_MENU,
CreateButton::new(format!("{}:disable_channel:{}", BOOSTEMBED_MENU, owner_id.get())) owner_id.get()
.label("Couper envoi") ))
.style(ButtonStyle::Danger), .label("Définir salon")
.style(ButtonStyle::Secondary),
CreateButton::new(format!(
"{}:disable_channel:{}",
BOOSTEMBED_MENU,
owner_id.get()
))
.label("Couper envoi")
.style(ButtonStyle::Danger),
]), ]),
CreateActionRow::Buttons(vec![ CreateActionRow::Buttons(vec![
CreateButton::new(format!("{}:edit_title:{}", BOOSTEMBED_MENU, owner_id.get())) CreateButton::new(format!("{}:edit_title:{}", BOOSTEMBED_MENU, owner_id.get()))
.label("Modifier titre") .label("Modifier titre")
.style(ButtonStyle::Secondary), .style(ButtonStyle::Secondary),
CreateButton::new(format!("{}:edit_description:{}", BOOSTEMBED_MENU, owner_id.get())) CreateButton::new(format!(
.label("Modifier description") "{}:edit_description:{}",
.style(ButtonStyle::Secondary), BOOSTEMBED_MENU,
owner_id.get()
))
.label("Modifier description")
.style(ButtonStyle::Secondary),
CreateButton::new(format!("{}:edit_color:{}", BOOSTEMBED_MENU, owner_id.get())) CreateButton::new(format!("{}:edit_color:{}", BOOSTEMBED_MENU, owner_id.get()))
.label("Modifier couleur") .label("Modifier couleur")
.style(ButtonStyle::Secondary), .style(ButtonStyle::Secondary),
@@ -305,7 +321,11 @@ pub async fn handle_boostembed(ctx: &Context, msg: &Message, args: &[&str]) {
msg, msg,
CreateEmbed::new() CreateEmbed::new()
.title("BoostEmbed") .title("BoostEmbed")
.description(if action == "on" { "Activé." } else { "Désactivé." }) .description(if action == "on" {
"Activé."
} else {
"Désactivé."
})
.color(theme_color(ctx).await), .color(theme_color(ctx).await),
) )
.await; .await;
@@ -392,7 +412,11 @@ pub async fn handle_component_interaction(ctx: &Context, component: &ComponentIn
if action.ends_with(":edit_title") { if action.ends_with(":edit_title") {
let modal = CreateModal::new( let modal = CreateModal::new(
format!("{}:modal:title:{}", BOOSTEMBED_MENU, component.user.id.get()), format!(
"{}:modal:title:{}",
BOOSTEMBED_MENU,
component.user.id.get()
),
"Modifier le titre du boost embed", "Modifier le titre du boost embed",
) )
.components(vec![CreateActionRow::InputText( .components(vec![CreateActionRow::InputText(
@@ -432,7 +456,11 @@ pub async fn handle_component_interaction(ctx: &Context, component: &ComponentIn
if action.ends_with(":edit_color") { if action.ends_with(":edit_color") {
let modal = CreateModal::new( let modal = CreateModal::new(
format!("{}:modal:color:{}", BOOSTEMBED_MENU, component.user.id.get()), format!(
"{}:modal:color:{}",
BOOSTEMBED_MENU,
component.user.id.get()
),
"Modifier la couleur du boost embed", "Modifier la couleur du boost embed",
) )
.components(vec![CreateActionRow::InputText( .components(vec![CreateActionRow::InputText(
@@ -448,7 +476,11 @@ pub async fn handle_component_interaction(ctx: &Context, component: &ComponentIn
if action.ends_with(":edit_channel") { if action.ends_with(":edit_channel") {
let modal = CreateModal::new( let modal = CreateModal::new(
format!("{}:modal:channel:{}", BOOSTEMBED_MENU, component.user.id.get()), format!(
"{}:modal:channel:{}",
BOOSTEMBED_MENU,
component.user.id.get()
),
"Définir le salon boost", "Définir le salon boost",
) )
.components(vec![CreateActionRow::InputText( .components(vec![CreateActionRow::InputText(
@@ -493,7 +525,11 @@ pub async fn handle_component_interaction(ctx: &Context, component: &ComponentIn
} }
pub async fn handle_modal_interaction(ctx: &Context, modal: &ModalInteraction) -> bool { pub async fn handle_modal_interaction(ctx: &Context, modal: &ModalInteraction) -> bool {
if !modal.data.custom_id.starts_with(&format!("{}:modal:", BOOSTEMBED_MENU)) { if !modal
.data
.custom_id
.starts_with(&format!("{}:modal:", BOOSTEMBED_MENU))
{
return false; return false;
} }
@@ -641,14 +677,12 @@ pub static COMMAND_DESCRIPTOR: BoostembedCommand = BoostembedCommand;
impl crate::commands::command_contract::CommandSpec for BoostembedCommand { impl crate::commands::command_contract::CommandSpec for BoostembedCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "boostembed", name: "boostembed",
command: "boostembed",
category: "admin", category: "admin",
params: "[on|off|test|settings]", params: "[on|off|test|settings]",
summary: "Configure l embed boost avec panneau interactif", summary: "Configure l embed boost avec panneau interactif",
description: "Ouvre un panneau avec composants pour paramétrer l'embed boost et le salon où il est envoyé.", description: "Ouvre un panneau avec composants pour paramétrer l'embed boost et le salon où il est envoyé.",
examples: &["+boostembed", "+boostembed settings", "+boostembed test"], examples: &["+boostembed", "+boostembed settings", "+boostembed test"],
alias_source_key: "boostembed",
default_aliases: &["bembed"], default_aliases: &["bembed"],
default_permission: 8, default_permission: 8,
} }
+49 -5
View File
@@ -1,9 +1,55 @@
use crate::commands::logs_service; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::logs_command_helpers::{parse_target_channel, set_log_channel};
pub async fn handle_boostlog(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_boostlog(ctx: &Context, msg: &Message, args: &[&str]) {
logs_service::handle_log_toggle(ctx, msg, args, "boost", "BoostLog").await; let Some(guild_id) = msg.guild_id else {
return;
};
let Some(action) = args.first().map(|s| s.to_lowercase()) else {
let embed = CreateEmbed::new()
.title("BoostLog")
.description("Usage: +boostlog <on [salon]|off>")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
};
match action.as_str() {
"on" => {
let channel = parse_target_channel(msg, args, 1);
set_log_channel(ctx, guild_id, "boost", channel, true).await;
let embed = CreateEmbed::new()
.title("BoostLog")
.description(format!(
"Active dans {}.",
channel
.map(|c| format!("<#{}>", c.get()))
.unwrap_or_else(|| "ce salon".to_string())
))
.color(theme_color(ctx).await);
send_embed(ctx, msg, embed).await;
}
"off" => {
set_log_channel(ctx, guild_id, "boost", None, false).await;
let embed = CreateEmbed::new()
.title("BoostLog")
.description("Desactive.")
.color(theme_color(ctx).await);
send_embed(ctx, msg, embed).await;
}
_ => {
let embed = CreateEmbed::new()
.title("BoostLog")
.description("Usage: +boostlog <on [salon]|off>")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
}
}
} }
pub struct BoostlogCommand; pub struct BoostlogCommand;
@@ -12,14 +58,12 @@ pub static COMMAND_DESCRIPTOR: BoostlogCommand = BoostlogCommand;
impl crate::commands::command_contract::CommandSpec for BoostlogCommand { impl crate::commands::command_contract::CommandSpec for BoostlogCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "boostlog", name: "boostlog",
command: "boostlog",
category: "admin", category: "admin",
params: "<on [salon]|off>", params: "<on [salon]|off>",
summary: "Active les logs de boosts", summary: "Active les logs de boosts",
description: "Active ou desactive les logs de boosts.", description: "Active ou desactive les logs de boosts.",
examples: &["+boostlog on #logs", "+boostlog off"], examples: &["+boostlog on #logs", "+boostlog off"],
alias_source_key: "boostlog",
default_aliases: &["blog"], default_aliases: &["blog"],
default_permission: 8, default_permission: 8,
} }
+55 -5
View File
@@ -1,10 +1,62 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::advanced_tools; use crate::commands::common::{parse_channel_id, send_embed, theme_color};
pub async fn handle_bringall(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_bringall(ctx: &Context, msg: &Message, args: &[&str]) {
advanced_tools::handle_bringall(ctx, msg, args).await; let Some(guild_id) = msg.guild_id else {
return;
};
let target_channel = if let Some(raw) = args.first() {
parse_channel_id(raw)
} else {
let Some(guild) = guild_id.to_guild_cached(&ctx.cache) else {
return;
};
guild
.voice_states
.get(&msg.author.id)
.and_then(|v| v.channel_id)
};
let Some(target_channel) = target_channel 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)| state.channel_id.map(|_| *uid))
.collect::<Vec<_>>()
};
let mut moved = 0usize;
for user_id in user_ids {
if guild_id
.move_member(&ctx.http, user_id, target_channel)
.await
.is_ok()
{
moved += 1;
}
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("BringAll")
.description(format!("{} membres déplacés.", moved))
.color(theme_color(ctx).await),
)
.await;
} }
pub struct BringAllCommand; pub struct BringAllCommand;
@@ -13,14 +65,12 @@ pub static COMMAND_DESCRIPTOR: BringAllCommand = BringAllCommand;
impl crate::commands::command_contract::CommandSpec for BringAllCommand { impl crate::commands::command_contract::CommandSpec for BringAllCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "bringall", name: "bringall",
command: "bringall",
category: "admin", category: "admin",
params: "[salon_vocal_destination]", params: "[salon_vocal_destination]",
summary: "Rassemble tous les vocaux", summary: "Rassemble tous les vocaux",
description: "Deplace tous les membres actuellement en vocal vers un salon cible.", description: "Deplace tous les membres actuellement en vocal vers un salon cible.",
examples: &["+bringall #Event", "+bringall"], examples: &["+bringall #Event", "+bringall"],
alias_source_key: "bringall",
default_aliases: &["ball", "vbring"], default_aliases: &["ball", "vbring"],
default_permission: 8, default_permission: 8,
} }
+33 -5
View File
@@ -1,10 +1,40 @@
use serenity::builder::{CreateActionRow, CreateButton, CreateEmbed, CreateMessage};
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::advanced_tools; use crate::commands::common::{send_embed, theme_color};
pub async fn handle_button(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_button(ctx: &Context, msg: &Message, args: &[&str]) {
advanced_tools::handle_button(ctx, msg, args).await; if args.len() < 2 {
return;
}
let action = args[0].to_lowercase();
let link = args[1];
if action == "add" {
let _ = msg
.channel_id
.send_message(
&ctx.http,
CreateMessage::new()
.content("Bouton personnalisé")
.components(vec![CreateActionRow::Buttons(vec![
CreateButton::new_link(link).label("Ouvrir"),
])]),
)
.await;
} else if action == "del" {
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Button")
.description("Suppression: supprime simplement le message contenant le bouton.")
.color(theme_color(ctx).await),
)
.await;
}
} }
pub struct ButtonCommand; pub struct ButtonCommand;
@@ -13,8 +43,7 @@ pub static COMMAND_DESCRIPTOR: ButtonCommand = ButtonCommand;
impl crate::commands::command_contract::CommandSpec for ButtonCommand { impl crate::commands::command_contract::CommandSpec for ButtonCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "button", name: "button",
command: "button",
category: "admin", category: "admin",
params: "<add/del> <lien>", params: "<add/del> <lien>",
summary: "Gere des boutons decoratifs", summary: "Gere des boutons decoratifs",
@@ -23,7 +52,6 @@ impl crate::commands::command_contract::CommandSpec for ButtonCommand {
"+button add https://example.com", "+button add https://example.com",
"+button del https://example.com", "+button del https://example.com",
], ],
alias_source_key: "button",
default_aliases: &["btn"], default_aliases: &["btn"],
default_permission: 8, default_permission: 8,
} }
+18
View File
@@ -67,3 +67,21 @@ pub async fn handle_claim(ctx: &Context, msg: &Message, _args: &[&str]) {
) )
.await; .await;
} }
pub struct ClaimCommand;
pub static COMMAND_DESCRIPTOR: ClaimCommand = ClaimCommand;
impl crate::commands::command_contract::CommandSpec for ClaimCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "claim",
category: "admin",
params: "aucun",
summary: "Revendique un ticket",
description: "Assigne le ticket courant au moderateur qui execute la commande.",
examples: &["+claim", "+help claim"],
default_aliases: &[],
default_permission: 2,
}
}
}
+48 -5
View File
@@ -1,10 +1,55 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::advanced_tools; use crate::commands::common::{parse_channel_id, send_embed, theme_color};
pub async fn handle_cleanup(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_cleanup(ctx: &Context, msg: &Message, args: &[&str]) {
advanced_tools::handle_cleanup(ctx, msg, args).await; 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 struct CleanupCommand;
@@ -13,14 +58,12 @@ pub static COMMAND_DESCRIPTOR: CleanupCommand = CleanupCommand;
impl crate::commands::command_contract::CommandSpec for CleanupCommand { impl crate::commands::command_contract::CommandSpec for CleanupCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "cleanup", name: "cleanup",
command: "cleanup",
category: "admin", category: "admin",
params: "<salon_vocal>", params: "<salon_vocal>",
summary: "Vide un salon vocal", summary: "Vide un salon vocal",
description: "Deconnecte tous les utilisateurs presents dans un salon vocal cible.", description: "Deconnecte tous les utilisateurs presents dans un salon vocal cible.",
examples: &["+cleanup #General"], examples: &["+cleanup #General"],
alias_source_key: "cleanup",
default_aliases: &["vclean", "vcleanup"], default_aliases: &["vclean", "vcleanup"],
default_permission: 8, default_permission: 8,
} }
+43 -5
View File
@@ -1,10 +1,50 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::moderation_tools; use crate::commands::common::{send_embed, theme_color};
use crate::db::DbPoolKey;
pub async fn handle_clear_all_sanctions(ctx: &Context, msg: &Message) { pub async fn handle_clear_all_sanctions(ctx: &Context, msg: &Message) {
moderation_tools::handle_clear_all_sanctions(ctx, msg).await; 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 struct ClearAllSanctionsCommand;
@@ -13,14 +53,12 @@ pub static COMMAND_DESCRIPTOR: ClearAllSanctionsCommand = ClearAllSanctionsComma
impl crate::commands::command_contract::CommandSpec for ClearAllSanctionsCommand { impl crate::commands::command_contract::CommandSpec for ClearAllSanctionsCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "clear_all_sanctions", name: "clear_all_sanctions",
command: "clear all sanctions",
category: "admin", category: "admin",
params: "aucun", params: "aucun",
summary: "Supprime toutes les sanctions du serveur", summary: "Supprime toutes les sanctions du serveur",
description: "Efface toutes les sanctions de tous les membres du serveur.", description: "Efface toutes les sanctions de tous les membres du serveur.",
examples: &["+clear all sanctions"], examples: &["+clear all sanctions"],
alias_source_key: "clear_all_sanctions",
default_aliases: &["casanctions"], default_aliases: &["casanctions"],
default_permission: 8, default_permission: 8,
} }
+1 -3
View File
@@ -39,14 +39,12 @@ pub static COMMAND_DESCRIPTOR: ClearBlCommand = ClearBlCommand;
impl crate::commands::command_contract::CommandSpec for ClearBlCommand { impl crate::commands::command_contract::CommandSpec for ClearBlCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "clear_bl", name: "clear_bl",
command: "clear bl",
category: "admin", category: "admin",
params: "aucun", params: "aucun",
summary: "Vide la blacklist globale", summary: "Vide la blacklist globale",
description: "Supprime toutes les entrees de la blacklist globale.", description: "Supprime toutes les entrees de la blacklist globale.",
examples: &["+clear bl", "+cl", "+help clear bl"], examples: &["+clear bl", "+cl", "+help clear bl"],
alias_source_key: "clear_bl",
default_aliases: &["cbl"], default_aliases: &["cbl"],
default_permission: 9, default_permission: 9,
} }
+44 -5
View File
@@ -1,10 +1,51 @@
use serenity::builder::{CreateEmbed, GetMessages};
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::moderation_tools; 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]) { pub async fn handle_clear_messages(ctx: &Context, msg: &Message, args: &[&str]) {
moderation_tools::handle_clear_messages(ctx, msg, args).await; 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 struct ClearMessagesCommand;
@@ -13,14 +54,12 @@ pub static COMMAND_DESCRIPTOR: ClearMessagesCommand = ClearMessagesCommand;
impl crate::commands::command_contract::CommandSpec for ClearMessagesCommand { impl crate::commands::command_contract::CommandSpec for ClearMessagesCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "clear_messages", name: "clear_messages",
command: "clear",
category: "admin", category: "admin",
params: "<nombre> [@membre/ID]", params: "<nombre> [@membre/ID]",
summary: "Supprime des messages dans le salon", summary: "Supprime des messages dans le salon",
description: "Supprime un nombre de messages, optionnellement filtres par membre.", description: "Supprime un nombre de messages, optionnellement filtres par membre.",
examples: &["+clear 20", "+clear 20 @User"], examples: &["+clear 20", "+clear 20 @User"],
alias_source_key: "clear_messages",
default_aliases: &["purge"], default_aliases: &["purge"],
default_permission: 8, default_permission: 8,
} }
+1 -3
View File
@@ -39,14 +39,12 @@ pub static COMMAND_DESCRIPTOR: ClearOwnersCommand = ClearOwnersCommand;
impl crate::commands::command_contract::CommandSpec for ClearOwnersCommand { impl crate::commands::command_contract::CommandSpec for ClearOwnersCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "clear_owners", name: "clear_owners",
command: "clear owners",
category: "admin", category: "admin",
params: "aucun", params: "aucun",
summary: "Vide la liste des owners", summary: "Vide la liste des owners",
description: "Supprime tous les owners supplementaires en base de donnees.", description: "Supprime tous les owners supplementaires en base de donnees.",
examples: &["+clear owners", "+cs", "+help clear owners"], examples: &["+clear owners", "+cs", "+help clear owners"],
alias_source_key: "clear_owners",
default_aliases: &["cro"], default_aliases: &["cro"],
default_permission: 9, default_permission: 9,
} }
+53 -5
View File
@@ -1,10 +1,60 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::moderation_tools; 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]) { pub async fn handle_clear_sanctions(ctx: &Context, msg: &Message, args: &[&str]) {
moderation_tools::handle_clear_sanctions(ctx, msg, args).await; 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 struct ClearSanctionsCommand;
@@ -13,14 +63,12 @@ pub static COMMAND_DESCRIPTOR: ClearSanctionsCommand = ClearSanctionsCommand;
impl crate::commands::command_contract::CommandSpec for ClearSanctionsCommand { impl crate::commands::command_contract::CommandSpec for ClearSanctionsCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "clear_sanctions", name: "clear_sanctions",
command: "clear sanctions",
category: "admin", category: "admin",
params: "<@membre/ID>", params: "<@membre/ID>",
summary: "Supprime toutes les sanctions d un membre", summary: "Supprime toutes les sanctions d un membre",
description: "Efface completement les sanctions d un membre cible.", description: "Efface completement les sanctions d un membre cible.",
examples: &["+clear sanctions @User"], examples: &["+clear sanctions @User"],
alias_source_key: "clear_sanctions",
default_aliases: &["csanctions"], default_aliases: &["csanctions"],
default_permission: 8, default_permission: 8,
} }
+18
View File
@@ -74,3 +74,21 @@ pub async fn handle_close(ctx: &Context, msg: &Message, args: &[&str]) {
send_embed(ctx, msg, embed).await; send_embed(ctx, msg, embed).await;
} }
pub struct CloseCommand;
pub static COMMAND_DESCRIPTOR: CloseCommand = CloseCommand;
impl crate::commands::command_contract::CommandSpec for CloseCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "close",
category: "admin",
params: "[raison...]",
summary: "Ferme un ticket",
description: "Ferme le ticket courant et enregistre optionnellement une raison.",
examples: &["+close", "+close Raison", "+help close"],
default_aliases: &[],
default_permission: 2,
}
}
}
+51 -5
View File
@@ -1,22 +1,68 @@
use crate::commands::moderation_tools; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::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]) { pub async fn handle_cmute(ctx: &Context, msg: &Message, args: &[&str]) {
moderation_tools::handle_cmute(ctx, msg, args, false).await; 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 struct CmuteCommand;
pub static COMMAND_DESCRIPTOR: CmuteCommand = CmuteCommand; pub static COMMAND_DESCRIPTOR: CmuteCommand = CmuteCommand;
impl crate::commands::command_contract::CommandSpec for CmuteCommand { impl crate::commands::command_contract::CommandSpec for CmuteCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "cmute", name: "cmute",
command: "cmute",
category: "admin", category: "admin",
params: "<@membre/ID[,..]> [raison]", params: "<@membre/ID[,..]> [raison]",
summary: "Mute salon", summary: "Mute salon",
description: "Mute un membre sur le salon courant.", description: "Mute un membre sur le salon courant.",
examples: &["+cmute @User"], examples: &["+cmute @User"],
alias_source_key: "cmute",
default_aliases: &["cm"], default_aliases: &["cm"],
default_permission: 8, default_permission: 8,
} }
+79 -5
View File
@@ -1,10 +1,86 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::advanced_tools; use crate::commands::common::{send_embed, theme_color};
fn emoji_url_from_source(msg: &Message, source: &str) -> String {
if source.starts_with("http://") || source.starts_with("https://") {
return source.to_string();
}
if source.starts_with("<:") || source.starts_with("<a:") {
let cleaned = source.trim_matches(|c| c == '<' || c == '>');
let parts = cleaned.split(':').collect::<Vec<_>>();
if parts.len() == 3 {
let animated = parts[0] == "a";
return format!(
"https://cdn.discordapp.com/emojis/{}.{}",
parts[2],
if animated { "gif" } else { "png" }
);
}
}
if let Some(att) = msg.attachments.first() {
return att.url.clone();
}
String::new()
}
pub async fn handle_create(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_create(ctx: &Context, msg: &Message, args: &[&str]) {
advanced_tools::handle_create(ctx, msg, args).await; let Some(guild_id) = msg.guild_id else {
return;
};
if args.len() < 2 {
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Create Emoji")
.description("Usage: +create <emoji/url> <nom>")
.color(0xED4245),
)
.await;
return;
}
let image_url = emoji_url_from_source(msg, args[0]);
if image_url.is_empty() {
return;
}
let response = match reqwest::get(&image_url).await {
Ok(r) => r,
Err(_) => return,
};
let bytes = match response.bytes().await {
Ok(b) => b,
Err(_) => return,
};
let data_uri = format!("data:image/png;base64,{}", {
use base64::Engine;
base64::engine::general_purpose::STANDARD.encode(bytes)
});
let result = guild_id.create_emoji(&ctx.http, args[1], &data_uri).await;
let embed = if let Ok(emoji) = result {
CreateEmbed::new()
.title("Emoji")
.description(format!("Emoji cree: {}", emoji))
.color(theme_color(ctx).await)
} else {
CreateEmbed::new()
.title("Emoji")
.description("Impossible de creer l'emoji.")
.color(0xED4245)
};
send_embed(ctx, msg, embed).await;
} }
pub struct CreateCommand; pub struct CreateCommand;
@@ -13,8 +89,7 @@ pub static COMMAND_DESCRIPTOR: CreateCommand = CreateCommand;
impl crate::commands::command_contract::CommandSpec for CreateCommand { impl crate::commands::command_contract::CommandSpec for CreateCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "create", name: "create",
command: "create",
category: "admin", category: "admin",
params: "[emoji/url] [nom]", params: "[emoji/url] [nom]",
summary: "Cree un emoji custom", summary: "Cree un emoji custom",
@@ -23,7 +98,6 @@ impl crate::commands::command_contract::CommandSpec for CreateCommand {
"+create <:blob:123456789012345678> blobcopy", "+create <:blob:123456789012345678> blobcopy",
"+create https://... logo", "+create https://... logo",
], ],
alias_source_key: "create",
default_aliases: &["mkemoji", "ce"], default_aliases: &["mkemoji", "ce"],
default_permission: 8, default_permission: 8,
} }
+75 -5
View File
@@ -1,10 +1,82 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::moderation_tools; 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]) { pub async fn handle_del_sanction(ctx: &Context, msg: &Message, args: &[&str]) {
moderation_tools::handle_del_sanction(ctx, msg, args).await; 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 struct DelSanctionCommand;
@@ -13,14 +85,12 @@ pub static COMMAND_DESCRIPTOR: DelSanctionCommand = DelSanctionCommand;
impl crate::commands::command_contract::CommandSpec for DelSanctionCommand { impl crate::commands::command_contract::CommandSpec for DelSanctionCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "del_sanction", name: "del_sanction",
command: "del sanction",
category: "admin", category: "admin",
params: "<@membre/ID> <nombre>", params: "<@membre/ID> <nombre>",
summary: "Supprime une sanction d un membre", summary: "Supprime une sanction d un membre",
description: "Supprime une sanction specifique dans l historique d un membre.", description: "Supprime une sanction specifique dans l historique d un membre.",
examples: &["+del sanction @User 1"], examples: &["+del sanction @User 1"],
alias_source_key: "del_sanction",
default_aliases: &["delsanction"], default_aliases: &["delsanction"],
default_permission: 8, default_permission: 8,
} }
+41 -5
View File
@@ -1,9 +1,47 @@
use crate::commands::moderation_tools; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::admin_common::parse_user_id;
use crate::commands::common::{parse_role, send_embed, theme_color};
pub async fn handle_delrole(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_delrole(ctx: &Context, msg: &Message, args: &[&str]) {
moderation_tools::handle_add_del_role(ctx, msg, args, false).await; let Some(guild_id) = msg.guild_id else {
return;
};
if args.len() < 2 {
return;
}
let Some(target) = parse_user_id(args[0]) else {
return;
};
let Ok(guild) = guild_id.to_partial_guild(&ctx.http).await else {
return;
};
let Some(role) = parse_role(&guild, args[1]) else {
return;
};
let done = if let Ok(member) = guild_id.member(&ctx.http, target).await {
member.remove_role(&ctx.http, role.id).await.is_ok()
} else {
false
};
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("DelRole")
.description(if done {
format!("Role <@&{}> retire a <@{}>.", role.id.get(), target.get())
} else {
"Echec de modification du role.".to_string()
})
.color(theme_color(ctx).await),
)
.await;
} }
pub struct DelroleCommand; pub struct DelroleCommand;
@@ -12,14 +50,12 @@ pub static COMMAND_DESCRIPTOR: DelroleCommand = DelroleCommand;
impl crate::commands::command_contract::CommandSpec for DelroleCommand { impl crate::commands::command_contract::CommandSpec for DelroleCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "delrole", name: "delrole",
command: "delrole",
category: "admin", category: "admin",
params: "<@membre/ID[,..]> <@role/ID>", params: "<@membre/ID[,..]> <@role/ID>",
summary: "Retire un role", summary: "Retire un role",
description: "Retire un role a un ou plusieurs membres.", description: "Retire un role a un ou plusieurs membres.",
examples: &["+delrole @User @Membre"], examples: &["+delrole @User @Membre"],
alias_source_key: "delrole",
default_aliases: &["dr"], default_aliases: &["dr"],
default_permission: 8, default_permission: 8,
} }
+42 -5
View File
@@ -1,9 +1,48 @@
use crate::commands::moderation_tools; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::moderation_sanction_helpers::parse_targets;
pub async fn handle_derank(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_derank(ctx: &Context, msg: &Message, args: &[&str]) {
moderation_tools::handle_derank(ctx, msg, args).await; 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 let Ok(member) = guild_id.member(&ctx.http, *uid).await {
let roles = member.roles.clone();
let mut ok = true;
for role_id in roles {
if member.remove_role(&ctx.http, role_id).await.is_err() {
ok = false;
}
}
if ok {
done += 1;
}
}
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Derank")
.description(format!("{} membre(s) derank.", done))
.color(theme_color(ctx).await),
)
.await;
} }
pub struct DerankCommand; pub struct DerankCommand;
@@ -12,14 +51,12 @@ pub static COMMAND_DESCRIPTOR: DerankCommand = DerankCommand;
impl crate::commands::command_contract::CommandSpec for DerankCommand { impl crate::commands::command_contract::CommandSpec for DerankCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "derank", name: "derank",
command: "derank",
category: "admin", category: "admin",
params: "<@membre/ID[,..]>", params: "<@membre/ID[,..]>",
summary: "Retire tous les roles", summary: "Retire tous les roles",
description: "Retire tous les roles gerables d un membre.", description: "Retire tous les roles gerables d un membre.",
examples: &["+derank @User"], examples: &["+derank @User"],
alias_source_key: "derank",
default_aliases: &["drk"], default_aliases: &["drk"],
default_permission: 8, default_permission: 8,
} }
+58 -5
View File
@@ -1,10 +1,65 @@
use serenity::builder::{CreateActionRow, CreateButton, CreateEmbed, CreateMessage};
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::advanced_tools; use crate::commands::common::{send_embed, theme_color};
fn owned_component_id(action: &str, owner_id: UserId) -> String {
format!("{}:{}", action, owner_id.get())
}
pub async fn handle_embed(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_embed(ctx: &Context, msg: &Message, args: &[&str]) {
advanced_tools::handle_embed_builder(ctx, msg, args).await; if args.is_empty() {
let embed = CreateEmbed::new()
.title("Embed")
.description("Utilise le bouton pour ouvrir le generateur d'embed.")
.color(theme_color(ctx).await);
let components = vec![CreateActionRow::Buttons(vec![
CreateButton::new(owned_component_id("adv:embed:modal", msg.author.id))
.label("Ouvrir le generateur")
.style(ButtonStyle::Primary),
])];
let _ = msg
.channel_id
.send_message(
&ctx.http,
CreateMessage::new().embed(embed).components(components),
)
.await;
return;
}
let joined = args.join(" ");
let mut split = joined.splitn(2, '|').map(str::trim);
let title = split.next().unwrap_or_default();
let description = split.next().unwrap_or_default();
if title.is_empty() || description.is_empty() {
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Embed")
.description("Format attendu: +embed titre | description")
.color(0xED4245),
)
.await;
return;
}
let _ = msg
.channel_id
.send_message(
&ctx.http,
CreateMessage::new().embed(
CreateEmbed::new()
.title(title)
.description(description)
.color(theme_color(ctx).await),
),
)
.await;
} }
pub struct EmbedCommand; pub struct EmbedCommand;
@@ -13,14 +68,12 @@ pub static COMMAND_DESCRIPTOR: EmbedCommand = EmbedCommand;
impl crate::commands::command_contract::CommandSpec for EmbedCommand { impl crate::commands::command_contract::CommandSpec for EmbedCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "embed", name: "embed",
command: "embed",
category: "admin", category: "admin",
params: "title | description (v1)", params: "title | description (v1)",
summary: "Ouvre le generateur d'embed", summary: "Ouvre le generateur d'embed",
description: "Affiche un generateur d'embed interactif version rapide.", description: "Affiche un generateur d'embed interactif version rapide.",
examples: &["+embed"], examples: &["+embed"],
alias_source_key: "embed",
default_aliases: &["emb"], default_aliases: &["emb"],
default_permission: 8, default_permission: 8,
} }
+92 -5
View File
@@ -1,10 +1,99 @@
use serenity::builder::{CreateActionRow, CreateButton, CreateEmbed, CreateMessage, EditMessage};
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::advanced_tools; use crate::commands::common::{send_embed, theme_color};
fn owned_component_id(action: &str, owner_id: UserId) -> String {
format!("{}:{}", action, owner_id.get())
}
async fn handle_end_giveaway(ctx: &Context, msg: &Message, args: &[&str]) {
let message_id_raw = args
.get(1)
.or_else(|| args.first())
.copied()
.unwrap_or_default();
let Ok(message_id) = message_id_raw.trim().parse::<u64>() else {
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("End")
.description("ID du message invalide.")
.color(0xED4245),
)
.await;
return;
};
let result = msg
.channel_id
.edit_message(
&ctx.http,
MessageId::new(message_id),
EditMessage::new().content("🎉 Giveaway termine manuellement."),
)
.await;
let (description, color) = if result.is_ok() {
("Giveaway termine.", theme_color(ctx).await)
} else {
("Impossible de terminer ce giveaway.", 0xED4245)
};
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("End")
.description(description)
.color(color),
)
.await;
}
pub async fn handle_end(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_end(ctx: &Context, msg: &Message, args: &[&str]) {
advanced_tools::handle_end(ctx, msg, args).await; if args.is_empty() {
let embed = CreateEmbed::new()
.title("End")
.description("Utilise le bouton pour terminer un giveaway via modal.")
.color(theme_color(ctx).await);
let components = vec![CreateActionRow::Buttons(vec![
CreateButton::new(owned_component_id("adv:giveaway:end_modal", msg.author.id))
.label("Terminer un giveaway")
.style(ButtonStyle::Danger),
])];
let _ = msg
.channel_id
.send_message(
&ctx.http,
CreateMessage::new().embed(embed).components(components),
)
.await;
return;
}
if args
.first()
.map(|v| v.eq_ignore_ascii_case("giveaway"))
.unwrap_or(false)
{
handle_end_giveaway(ctx, msg, args).await;
return;
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("End")
.description("Usage: +end giveaway <id_message>")
.color(0xED4245),
)
.await;
} }
pub struct EndCommand; pub struct EndCommand;
@@ -13,14 +102,12 @@ pub static COMMAND_DESCRIPTOR: EndCommand = EndCommand;
impl crate::commands::command_contract::CommandSpec for EndCommand { impl crate::commands::command_contract::CommandSpec for EndCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "end", name: "end",
command: "end",
category: "admin", category: "admin",
params: "giveaway <id_message>", params: "giveaway <id_message>",
summary: "Termine un giveaway par ID", summary: "Termine un giveaway par ID",
description: "Permet de stopper instantanement un giveaway avec l'identifiant du message.", description: "Permet de stopper instantanement un giveaway avec l'identifiant du message.",
examples: &["+end giveaway 123456789012345678"], examples: &["+end giveaway 123456789012345678"],
alias_source_key: "end",
default_aliases: &["gend"], default_aliases: &["gend"],
default_permission: 0, default_permission: 0,
} }
+35 -5
View File
@@ -1,10 +1,42 @@
use serenity::builder::{
CreateActionRow, CreateButton, CreateEmbed, CreateEmbedFooter, CreateMessage,
};
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::advanced_tools; use crate::commands::common::theme_color;
fn owned_component_id(action: &str, owner_id: UserId) -> String {
format!("{}:{}", action, owner_id.get())
}
pub async fn handle_giveaway(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_giveaway(ctx: &Context, msg: &Message, args: &[&str]) {
advanced_tools::handle_giveaway(ctx, msg, args).await; let _ = args;
let embed = CreateEmbed::new()
.title("Giveaway")
.description("Utilise les boutons pour creer ou terminer un giveaway via modal.")
.color(theme_color(ctx).await)
.footer(CreateEmbedFooter::new("UI avancee: Components + Modal"));
let components = vec![CreateActionRow::Buttons(vec![
CreateButton::new(owned_component_id("adv:giveaway:open_modal", msg.author.id))
.label("Creer")
.emoji('🎉')
.style(ButtonStyle::Success),
CreateButton::new(owned_component_id("adv:giveaway:end_modal", msg.author.id))
.label("Terminer")
.emoji('🛑')
.style(ButtonStyle::Danger),
])];
let _ = msg
.channel_id
.send_message(
&ctx.http,
CreateMessage::new().embed(embed).components(components),
)
.await;
} }
pub struct GiveawayCommand; pub struct GiveawayCommand;
@@ -13,14 +45,12 @@ pub static COMMAND_DESCRIPTOR: GiveawayCommand = GiveawayCommand;
impl crate::commands::command_contract::CommandSpec for GiveawayCommand { impl crate::commands::command_contract::CommandSpec for GiveawayCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "giveaway", name: "giveaway",
command: "giveaway",
category: "admin", category: "admin",
params: "aucun", params: "aucun",
summary: "Ouvre un menu de creation de giveaway", summary: "Ouvre un menu de creation de giveaway",
description: "Affiche une interface rapide pour initier un giveaway depuis le salon courant.", description: "Affiche une interface rapide pour initier un giveaway depuis le salon courant.",
examples: &["+giveaway"], examples: &["+giveaway"],
alias_source_key: "giveaway",
default_aliases: &["gstart", "gw"], default_aliases: &["gstart", "gw"],
default_permission: 8, default_permission: 8,
} }
+30 -5
View File
@@ -1,22 +1,47 @@
use crate::commands::moderation_tools; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::common::{parse_channel_id, send_embed, theme_color};
use crate::commands::moderation_channel_helpers::edit_channel_visibility;
pub async fn handle_hide(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_hide(ctx: &Context, msg: &Message, args: &[&str]) {
moderation_tools::handle_hide_unhide(ctx, msg, args, true).await; let Some(guild_id) = msg.guild_id else {
return;
};
let target = args
.first()
.and_then(|raw| parse_channel_id(raw))
.unwrap_or(msg.channel_id);
let ok = edit_channel_visibility(ctx, guild_id, target, None, Some(true)).await;
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Hide")
.description(if ok {
format!("Salon <#{}> mis a jour.", target.get())
} else {
"Echec de mise a jour du salon.".to_string()
})
.color(theme_color(ctx).await),
)
.await;
} }
pub struct HideCommand; pub struct HideCommand;
pub static COMMAND_DESCRIPTOR: HideCommand = HideCommand; pub static COMMAND_DESCRIPTOR: HideCommand = HideCommand;
impl crate::commands::command_contract::CommandSpec for HideCommand { impl crate::commands::command_contract::CommandSpec for HideCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "hide", name: "hide",
command: "hide",
category: "admin", category: "admin",
params: "[salon]", params: "[salon]",
summary: "Cache un salon", summary: "Cache un salon",
description: "Retire la visibilite d un salon.", description: "Retire la visibilite d un salon.",
examples: &["+hide", "+hide #general"], examples: &["+hide", "+hide #general"],
alias_source_key: "hide",
default_aliases: &["hd"], default_aliases: &["hd"],
default_permission: 8, default_permission: 8,
} }
+28 -5
View File
@@ -1,9 +1,34 @@
use crate::commands::moderation_tools; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::moderation_channel_helpers::edit_channel_visibility;
pub async fn handle_hideall(ctx: &Context, msg: &Message) { pub async fn handle_hideall(ctx: &Context, msg: &Message) {
moderation_tools::handle_hideall_unhideall(ctx, msg, true).await; let Some(guild_id) = msg.guild_id else {
return;
};
let Ok(channels) = guild_id.channels(&ctx.http).await else {
return;
};
let mut changed = 0usize;
for channel_id in channels.keys() {
if edit_channel_visibility(ctx, guild_id, *channel_id, None, Some(true)).await {
changed += 1;
}
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("HideAll")
.description(format!("{} salon(s) mis a jour.", changed))
.color(theme_color(ctx).await),
)
.await;
} }
pub struct HideallCommand; pub struct HideallCommand;
@@ -12,14 +37,12 @@ pub static COMMAND_DESCRIPTOR: HideallCommand = HideallCommand;
impl crate::commands::command_contract::CommandSpec for HideallCommand { impl crate::commands::command_contract::CommandSpec for HideallCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "hideall", name: "hideall",
command: "hideall",
category: "admin", category: "admin",
params: "aucun", params: "aucun",
summary: "Cache tous les salons", summary: "Cache tous les salons",
description: "Retire la visibilite de tous les salons.", description: "Retire la visibilite de tous les salons.",
examples: &["+hideall"], examples: &["+hideall"],
alias_source_key: "hideall",
default_aliases: &["hda"], default_aliases: &["hda"],
default_permission: 8, default_permission: 8,
} }
+1 -3
View File
@@ -70,14 +70,12 @@ pub static COMMAND_DESCRIPTOR: InviteCommand = InviteCommand;
impl crate::commands::command_contract::CommandSpec for InviteCommand { impl crate::commands::command_contract::CommandSpec for InviteCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "invite", name: "invite",
command: "invite",
category: "admin", category: "admin",
params: "<ID_serveur/index>", params: "<ID_serveur/index>",
summary: "Cree une invitation serveur", summary: "Cree une invitation serveur",
description: "Cree une invitation temporaire sur un serveur cible accessible par le bot.", description: "Cree une invitation temporaire sur un serveur cible accessible par le bot.",
examples: &["+invite", "+ie", "+help invite"], examples: &["+invite", "+ie", "+help invite"],
alias_source_key: "invite",
default_aliases: &["ivt"], default_aliases: &["ivt"],
default_permission: 8, default_permission: 8,
} }
+116 -5
View File
@@ -1,9 +1,122 @@
use crate::commands::logs_service; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::logs_command_helpers::{parse_target_channel, pool};
pub async fn handle_join(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_join(ctx: &Context, msg: &Message, args: &[&str]) {
logs_service::handle_join_leave_settings(ctx, msg, args, "join").await; 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;
if args.is_empty() || !args[0].eq_ignore_ascii_case("settings") {
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("join settings")
.description("Usage: +join settings [on/off] [salon] [message...]")
.color(0xED4245),
)
.await;
return;
}
if args.len() == 1 {
let row = sqlx::query_as::<_, (bool, Option<i64>, Option<String>)>(
r#"
SELECT enabled, channel_id, custom_message
FROM bot_join_leave_settings
WHERE bot_id = $1 AND guild_id = $2 AND kind = $3
LIMIT 1;
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.bind("join")
.fetch_optional(&pool)
.await
.ok()
.flatten();
let desc = if let Some((enabled, channel_id, custom_message)) = row {
format!(
"Etat: {}\nSalon: {}\nMessage: {}",
if enabled { "on" } else { "off" },
channel_id
.map(|id| format!("<#{}>", id))
.unwrap_or_else(|| "non defini".to_string()),
custom_message.unwrap_or_else(|| "(defaut)".to_string())
)
} else {
"Aucun reglage configure.".to_string()
};
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("join settings")
.description(desc)
.color(theme_color(ctx).await),
)
.await;
return;
}
let action = args[1].to_lowercase();
let enabled = action == "on";
let channel = if enabled {
parse_target_channel(msg, args, 2)
} else {
None
};
let message_start = if enabled { 3 } else { 2 };
let custom_message = if args.len() > message_start {
Some(args[message_start..].join(" "))
} else {
None
};
let _ = sqlx::query(
r#"
INSERT INTO bot_join_leave_settings (bot_id, guild_id, kind, enabled, channel_id, custom_message)
VALUES ($1, $2, $3, $4, $5, $6)
ON CONFLICT (bot_id, guild_id, kind)
DO UPDATE SET enabled = EXCLUDED.enabled, channel_id = EXCLUDED.channel_id,
custom_message = EXCLUDED.custom_message, updated_at = NOW();
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.bind("join")
.bind(enabled)
.bind(channel.map(|c| c.get() as i64))
.bind(custom_message)
.execute(&pool)
.await;
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("join settings")
.description(format!(
"{} {}",
if enabled { "Active" } else { "Desactive" },
channel
.map(|c| format!("dans <#{}>", c.get()))
.unwrap_or_default()
))
.color(theme_color(ctx).await),
)
.await;
} }
pub struct JoinCommand; pub struct JoinCommand;
@@ -12,8 +125,7 @@ pub static COMMAND_DESCRIPTOR: JoinCommand = JoinCommand;
impl crate::commands::command_contract::CommandSpec for JoinCommand { impl crate::commands::command_contract::CommandSpec for JoinCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "join", name: "join",
command: "join",
category: "admin", category: "admin",
params: "settings [on/off] [salon] [message]", params: "settings [on/off] [salon] [message]",
summary: "Parametre les actions de join", summary: "Parametre les actions de join",
@@ -22,7 +134,6 @@ impl crate::commands::command_contract::CommandSpec for JoinCommand {
"+join settings", "+join settings",
"+join settings on #welcome Bienvenue {user}", "+join settings on #welcome Bienvenue {user}",
], ],
alias_source_key: "join",
default_aliases: &["jset"], default_aliases: &["jset"],
default_permission: 8, default_permission: 8,
} }
+55 -5
View File
@@ -1,22 +1,72 @@
use crate::commands::moderation_tools; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::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]) { pub async fn handle_kick(ctx: &Context, msg: &Message, args: &[&str]) {
moderation_tools::handle_kick(ctx, msg, args).await; 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 struct KickCommand;
pub static COMMAND_DESCRIPTOR: KickCommand = KickCommand; pub static COMMAND_DESCRIPTOR: KickCommand = KickCommand;
impl crate::commands::command_contract::CommandSpec for KickCommand { impl crate::commands::command_contract::CommandSpec for KickCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "kick", name: "kick",
command: "kick",
category: "admin", category: "admin",
params: "<@membre/ID[,..]> [raison]", params: "<@membre/ID[,..]> [raison]",
summary: "Expulse un membre", summary: "Expulse un membre",
description: "Kick un ou plusieurs membres.", description: "Kick un ou plusieurs membres.",
examples: &["+kick @User"], examples: &["+kick @User"],
alias_source_key: "kick",
default_aliases: &["k"], default_aliases: &["k"],
default_permission: 8, default_permission: 8,
} }
+1 -3
View File
@@ -34,14 +34,12 @@ pub static COMMAND_DESCRIPTOR: LeaveCommand = LeaveCommand;
impl crate::commands::command_contract::CommandSpec for LeaveCommand { impl crate::commands::command_contract::CommandSpec for LeaveCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "leave", name: "leave",
command: "leave",
category: "admin", category: "admin",
params: "[ID_serveur/index]", params: "[ID_serveur/index]",
summary: "Fait quitter un serveur", summary: "Fait quitter un serveur",
description: "Force le bot a quitter un serveur cible ou le serveur courant.", description: "Force le bot a quitter un serveur cible ou le serveur courant.",
examples: &["+leave", "+le", "+help leave"], examples: &["+leave", "+le", "+help leave"],
alias_source_key: "leave",
default_aliases: &["lvg"], default_aliases: &["lvg"],
default_permission: 9, default_permission: 9,
} }
+116 -5
View File
@@ -1,10 +1,123 @@
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::logs_service; use serenity::builder::CreateEmbed;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::logs_command_helpers::{parse_target_channel, pool};
pub async fn handle_leave_settings(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_leave_settings(ctx: &Context, msg: &Message, args: &[&str]) {
logs_service::handle_join_leave_settings(ctx, msg, args, "leave").await; 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;
if args.is_empty() || !args[0].eq_ignore_ascii_case("settings") {
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("leave settings")
.description("Usage: +leave settings [on/off] [salon] [message...]")
.color(0xED4245),
)
.await;
return;
}
if args.len() == 1 {
let row = sqlx::query_as::<_, (bool, Option<i64>, Option<String>)>(
r#"
SELECT enabled, channel_id, custom_message
FROM bot_join_leave_settings
WHERE bot_id = $1 AND guild_id = $2 AND kind = $3
LIMIT 1;
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.bind("leave")
.fetch_optional(&pool)
.await
.ok()
.flatten();
let desc = if let Some((enabled, channel_id, custom_message)) = row {
format!(
"Etat: {}\nSalon: {}\nMessage: {}",
if enabled { "on" } else { "off" },
channel_id
.map(|id| format!("<#{}>", id))
.unwrap_or_else(|| "non defini".to_string()),
custom_message.unwrap_or_else(|| "(defaut)".to_string())
)
} else {
"Aucun reglage configure.".to_string()
};
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("leave settings")
.description(desc)
.color(theme_color(ctx).await),
)
.await;
return;
}
let action = args[1].to_lowercase();
let enabled = action == "on";
let channel = if enabled {
parse_target_channel(msg, args, 2)
} else {
None
};
let message_start = if enabled { 3 } else { 2 };
let custom_message = if args.len() > message_start {
Some(args[message_start..].join(" "))
} else {
None
};
let _ = sqlx::query(
r#"
INSERT INTO bot_join_leave_settings (bot_id, guild_id, kind, enabled, channel_id, custom_message)
VALUES ($1, $2, $3, $4, $5, $6)
ON CONFLICT (bot_id, guild_id, kind)
DO UPDATE SET enabled = EXCLUDED.enabled, channel_id = EXCLUDED.channel_id,
custom_message = EXCLUDED.custom_message, updated_at = NOW();
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.bind("leave")
.bind(enabled)
.bind(channel.map(|c| c.get() as i64))
.bind(custom_message)
.execute(&pool)
.await;
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("leave settings")
.description(format!(
"{} {}",
if enabled { "Active" } else { "Desactive" },
channel
.map(|c| format!("dans <#{}>", c.get()))
.unwrap_or_default()
))
.color(theme_color(ctx).await),
)
.await;
} }
pub struct LeaveSettingsCommand; pub struct LeaveSettingsCommand;
@@ -13,8 +126,7 @@ pub static COMMAND_DESCRIPTOR: LeaveSettingsCommand = LeaveSettingsCommand;
impl crate::commands::command_contract::CommandSpec for LeaveSettingsCommand { impl crate::commands::command_contract::CommandSpec for LeaveSettingsCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "leave_settings", name: "leave_settings",
command: "leave settings",
category: "admin", category: "admin",
params: "settings [on/off] [salon] [message]", params: "settings [on/off] [salon] [message]",
summary: "Parametre les actions de leave", summary: "Parametre les actions de leave",
@@ -23,7 +135,6 @@ impl crate::commands::command_contract::CommandSpec for LeaveSettingsCommand {
"+leave settings", "+leave settings",
"+leave settings on #logs {user} a quitte", "+leave settings on #logs {user} a quitte",
], ],
alias_source_key: "leave_settings",
default_aliases: &["lset"], default_aliases: &["lset"],
default_permission: 8, default_permission: 8,
} }
+30 -5
View File
@@ -1,22 +1,47 @@
use crate::commands::moderation_tools; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::common::{parse_channel_id, send_embed, theme_color};
use crate::commands::moderation_channel_helpers::edit_channel_visibility;
pub async fn handle_lock(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_lock(ctx: &Context, msg: &Message, args: &[&str]) {
moderation_tools::handle_lock_unlock(ctx, msg, args, true).await; let Some(guild_id) = msg.guild_id else {
return;
};
let target = args
.first()
.and_then(|raw| parse_channel_id(raw))
.unwrap_or(msg.channel_id);
let ok = edit_channel_visibility(ctx, guild_id, target, Some(true), None).await;
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Lock")
.description(if ok {
format!("Salon <#{}> mis a jour.", target.get())
} else {
"Echec de mise a jour du salon.".to_string()
})
.color(theme_color(ctx).await),
)
.await;
} }
pub struct LockCommand; pub struct LockCommand;
pub static COMMAND_DESCRIPTOR: LockCommand = LockCommand; pub static COMMAND_DESCRIPTOR: LockCommand = LockCommand;
impl crate::commands::command_contract::CommandSpec for LockCommand { impl crate::commands::command_contract::CommandSpec for LockCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "lock", name: "lock",
command: "lock",
category: "admin", category: "admin",
params: "[salon]", params: "[salon]",
summary: "Ferme un salon", summary: "Ferme un salon",
description: "Verrouille un salon texte ou vocal.", description: "Verrouille un salon texte ou vocal.",
examples: &["+lock", "+lock #general"], examples: &["+lock", "+lock #general"],
alias_source_key: "lock",
default_aliases: &["lk"], default_aliases: &["lk"],
default_permission: 8, default_permission: 8,
} }
+29 -5
View File
@@ -1,22 +1,46 @@
use crate::commands::moderation_tools; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::moderation_channel_helpers::edit_channel_visibility;
pub async fn handle_lockall(ctx: &Context, msg: &Message) { pub async fn handle_lockall(ctx: &Context, msg: &Message) {
moderation_tools::handle_lockall_unlockall(ctx, msg, true).await; let Some(guild_id) = msg.guild_id else {
return;
};
let Ok(channels) = guild_id.channels(&ctx.http).await else {
return;
};
let mut changed = 0usize;
for channel_id in channels.keys() {
if edit_channel_visibility(ctx, guild_id, *channel_id, Some(true), None).await {
changed += 1;
}
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("LockAll")
.description(format!("{} salon(s) mis a jour.", changed))
.color(theme_color(ctx).await),
)
.await;
} }
pub struct LockallCommand; pub struct LockallCommand;
pub static COMMAND_DESCRIPTOR: LockallCommand = LockallCommand; pub static COMMAND_DESCRIPTOR: LockallCommand = LockallCommand;
impl crate::commands::command_contract::CommandSpec for LockallCommand { impl crate::commands::command_contract::CommandSpec for LockallCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "lockall", name: "lockall",
command: "lockall",
category: "admin", category: "admin",
params: "aucun", params: "aucun",
summary: "Ferme tous les salons", summary: "Ferme tous les salons",
description: "Verrouille tous les salons du serveur.", description: "Verrouille tous les salons du serveur.",
examples: &["+lockall"], examples: &["+lockall"],
alias_source_key: "lockall",
default_aliases: &["lka"], default_aliases: &["lka"],
default_permission: 8, default_permission: 8,
} }
+51 -5
View File
@@ -1,10 +1,58 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::advanced_tools; use crate::commands::common::{parse_role, send_embed, theme_color};
pub async fn handle_massiverole(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_massiverole(ctx: &Context, msg: &Message, args: &[&str]) {
advanced_tools::handle_massive_role(ctx, msg, args, true).await; let Some(guild_id) = msg.guild_id else {
return;
};
if args.is_empty() {
return;
}
let Ok(guild) = guild_id.to_partial_guild(&ctx.http).await else {
return;
};
let Some(target_role) = parse_role(&guild, args[0]) else {
return;
};
let filter_role = args.get(1).and_then(|raw| parse_role(&guild, raw));
let Ok(members) = guild_id.members(&ctx.http, None, None).await else {
return;
};
let mut affected = 0usize;
for member in members {
if let Some(filter) = &filter_role {
if !member.roles.contains(&filter.id) {
continue;
}
}
if member.add_role(&ctx.http, target_role.id).await.is_ok() {
affected += 1;
}
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("MassiveRole")
.description(format!(
"{} membres traités pour le rôle <@&{}>.",
affected,
target_role.id.get()
))
.color(theme_color(ctx).await),
)
.await;
} }
pub struct MassiveRoleCommand; pub struct MassiveRoleCommand;
@@ -13,14 +61,12 @@ pub static COMMAND_DESCRIPTOR: MassiveRoleCommand = MassiveRoleCommand;
impl crate::commands::command_contract::CommandSpec for MassiveRoleCommand { impl crate::commands::command_contract::CommandSpec for MassiveRoleCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "massiverole", name: "massiverole",
command: "massiverole",
category: "admin", category: "admin",
params: "<role_cible> [role_filtre]", params: "<role_cible> [role_filtre]",
summary: "Ajoute un role en masse", summary: "Ajoute un role en masse",
description: "Ajoute un role a tous les membres ou a ceux qui ont deja un role filtre.", description: "Ajoute un role a tous les membres ou a ceux qui ont deja un role filtre.",
examples: &["+massiverole @VIP", "+massiverole @VIP @Membres"], examples: &["+massiverole @VIP", "+massiverole @VIP @Membres"],
alias_source_key: "massiverole",
default_aliases: &["mrole", "mr"], default_aliases: &["mrole", "mr"],
default_permission: 8, default_permission: 8,
} }
+49 -5
View File
@@ -1,9 +1,55 @@
use crate::commands::logs_service; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::logs_command_helpers::{parse_target_channel, set_log_channel};
pub async fn handle_messagelog(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_messagelog(ctx: &Context, msg: &Message, args: &[&str]) {
logs_service::handle_log_toggle(ctx, msg, args, "message", "MessageLog").await; let Some(guild_id) = msg.guild_id else {
return;
};
let Some(action) = args.first().map(|s| s.to_lowercase()) else {
let embed = CreateEmbed::new()
.title("MessageLog")
.description("Usage: +messagelog <on [salon]|off>")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
};
match action.as_str() {
"on" => {
let channel = parse_target_channel(msg, args, 1);
set_log_channel(ctx, guild_id, "message", channel, true).await;
let embed = CreateEmbed::new()
.title("MessageLog")
.description(format!(
"Active dans {}.",
channel
.map(|c| format!("<#{}>", c.get()))
.unwrap_or_else(|| "ce salon".to_string())
))
.color(theme_color(ctx).await);
send_embed(ctx, msg, embed).await;
}
"off" => {
set_log_channel(ctx, guild_id, "message", None, false).await;
let embed = CreateEmbed::new()
.title("MessageLog")
.description("Desactive.")
.color(theme_color(ctx).await);
send_embed(ctx, msg, embed).await;
}
_ => {
let embed = CreateEmbed::new()
.title("MessageLog")
.description("Usage: +messagelog <on [salon]|off>")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
}
}
} }
pub struct MessagelogCommand; pub struct MessagelogCommand;
@@ -12,14 +58,12 @@ pub static COMMAND_DESCRIPTOR: MessagelogCommand = MessagelogCommand;
impl crate::commands::command_contract::CommandSpec for MessagelogCommand { impl crate::commands::command_contract::CommandSpec for MessagelogCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "messagelog", name: "messagelog",
command: "messagelog",
category: "admin", category: "admin",
params: "<on [salon]|off>", params: "<on [salon]|off>",
summary: "Active les logs de messages", summary: "Active les logs de messages",
description: "Active ou desactive les logs des messages supprimes et edites.", description: "Active ou desactive les logs des messages supprimes et edites.",
examples: &["+messagelog on #logs", "+messagelog off"], examples: &["+messagelog on #logs", "+messagelog off"],
alias_source_key: "messagelog",
default_aliases: &["msglog"], default_aliases: &["msglog"],
default_permission: 8, default_permission: 8,
} }
+49 -5
View File
@@ -1,9 +1,55 @@
use crate::commands::logs_service; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::logs_command_helpers::{parse_target_channel, set_log_channel};
pub async fn handle_modlog(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_modlog(ctx: &Context, msg: &Message, args: &[&str]) {
logs_service::handle_log_toggle(ctx, msg, args, "moderation", "ModLog").await; let Some(guild_id) = msg.guild_id else {
return;
};
let Some(action) = args.first().map(|s| s.to_lowercase()) else {
let embed = CreateEmbed::new()
.title("ModLog")
.description("Usage: +modlog <on [salon]|off>")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
};
match action.as_str() {
"on" => {
let channel = parse_target_channel(msg, args, 1);
set_log_channel(ctx, guild_id, "moderation", channel, true).await;
let embed = CreateEmbed::new()
.title("ModLog")
.description(format!(
"Active dans {}.",
channel
.map(|c| format!("<#{}>", c.get()))
.unwrap_or_else(|| "ce salon".to_string())
))
.color(theme_color(ctx).await);
send_embed(ctx, msg, embed).await;
}
"off" => {
set_log_channel(ctx, guild_id, "moderation", None, false).await;
let embed = CreateEmbed::new()
.title("ModLog")
.description("Desactive.")
.color(theme_color(ctx).await);
send_embed(ctx, msg, embed).await;
}
_ => {
let embed = CreateEmbed::new()
.title("ModLog")
.description("Usage: +modlog <on [salon]|off>")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
}
}
} }
pub struct ModlogCommand; pub struct ModlogCommand;
@@ -12,14 +58,12 @@ pub static COMMAND_DESCRIPTOR: ModlogCommand = ModlogCommand;
impl crate::commands::command_contract::CommandSpec for ModlogCommand { impl crate::commands::command_contract::CommandSpec for ModlogCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "modlog", name: "modlog",
command: "modlog",
category: "admin", category: "admin",
params: "<on [salon]|off>", params: "<on [salon]|off>",
summary: "Active les logs de moderation", summary: "Active les logs de moderation",
description: "Active ou desactive les logs de moderation dans un salon cible.", description: "Active ou desactive les logs de moderation dans un salon cible.",
examples: &["+modlog on #logs", "+modlog off"], examples: &["+modlog on #logs", "+modlog off"],
alias_source_key: "modlog",
default_aliases: &["mlog"], default_aliases: &["mlog"],
default_permission: 8, default_permission: 8,
} }
+51 -5
View File
@@ -1,22 +1,68 @@
use crate::commands::moderation_tools; use chrono::Utc;
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::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]) { pub async fn handle_mute(ctx: &Context, msg: &Message, args: &[&str]) {
moderation_tools::handle_mute(ctx, msg, args, false).await; 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 struct MuteCommand;
pub static COMMAND_DESCRIPTOR: MuteCommand = MuteCommand; pub static COMMAND_DESCRIPTOR: MuteCommand = MuteCommand;
impl crate::commands::command_contract::CommandSpec for MuteCommand { impl crate::commands::command_contract::CommandSpec for MuteCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "mute", name: "mute",
command: "mute",
category: "admin", category: "admin",
params: "<@membre/ID[,..]> [raison]", params: "<@membre/ID[,..]> [raison]",
summary: "Mute un membre", summary: "Mute un membre",
description: "Applique un mute a un ou plusieurs membres.", description: "Applique un mute a un ou plusieurs membres.",
examples: &["+mute @User abus"], examples: &["+mute @User abus"],
alias_source_key: "mute",
default_aliases: &["tmute"], default_aliases: &["tmute"],
default_permission: 8, default_permission: 8,
} }
+56 -5
View File
@@ -1,22 +1,73 @@
use crate::commands::moderation_tools; use chrono::Utc;
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::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) { pub async fn handle_mutelist(ctx: &Context, msg: &Message) {
moderation_tools::handle_mutelist(ctx, msg).await; 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 struct MutelistCommand;
pub static COMMAND_DESCRIPTOR: MutelistCommand = MutelistCommand; pub static COMMAND_DESCRIPTOR: MutelistCommand = MutelistCommand;
impl crate::commands::command_contract::CommandSpec for MutelistCommand { impl crate::commands::command_contract::CommandSpec for MutelistCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "mutelist", name: "mutelist",
command: "mutelist",
category: "admin", category: "admin",
params: "aucun", params: "aucun",
summary: "Liste les mutes", summary: "Liste les mutes",
description: "Affiche tous les mutes en cours.", description: "Affiche tous les mutes en cours.",
examples: &["+mutelist"], examples: &["+mutelist"],
alias_source_key: "mutelist",
default_aliases: &["ml"], default_aliases: &["ml"],
default_permission: 8, default_permission: 8,
} }
+13 -5
View File
@@ -1,10 +1,20 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::advanced_tools; use crate::commands::common::{send_embed, theme_color};
pub async fn handle_newsticker(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_newsticker(ctx: &Context, msg: &Message, args: &[&str]) {
advanced_tools::handle_new_sticker(ctx, msg, args).await; let _ = args;
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("NewSticker")
.description("Creation de sticker disponible prochainement (API sticker V2).")
.color(theme_color(ctx).await),
)
.await;
} }
pub struct NewStickerCommand; pub struct NewStickerCommand;
@@ -13,14 +23,12 @@ pub static COMMAND_DESCRIPTOR: NewStickerCommand = NewStickerCommand;
impl crate::commands::command_contract::CommandSpec for NewStickerCommand { impl crate::commands::command_contract::CommandSpec for NewStickerCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "newsticker", name: "newsticker",
command: "newsticker",
category: "admin", category: "admin",
params: "[nom]", params: "[nom]",
summary: "Cree un sticker serveur", summary: "Cree un sticker serveur",
description: "Cree un nouveau sticker a partir d'un sticker ou fichier repondu.", description: "Cree un nouveau sticker a partir d'un sticker ou fichier repondu.",
examples: &["+newsticker cool_pack"], examples: &["+newsticker cool_pack"],
alias_source_key: "newsticker",
default_aliases: &["stcreate", "nst"], default_aliases: &["stcreate", "nst"],
default_permission: 8, default_permission: 8,
} }
+100 -5
View File
@@ -1,9 +1,106 @@
use crate::commands::logs_service; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::logs_command_helpers::{parse_target_channel, pool};
pub async fn handle_nolog(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_nolog(ctx: &Context, msg: &Message, args: &[&str]) {
logs_service::handle_nolog(ctx, msg, args).await; let Some(guild_id) = msg.guild_id else {
return;
};
if args.is_empty() {
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("NoLog")
.description("Usage: +nolog <add/del> [salon] [message|voice|all]")
.color(0xED4245),
)
.await;
return;
}
let action = args[0].to_lowercase();
let channel = parse_target_channel(msg, args, 1).unwrap_or(msg.channel_id);
let scope = args
.get(2)
.map(|s| s.to_lowercase())
.unwrap_or_else(|| "all".to_string());
let set_message = scope == "all" || scope == "message";
let set_voice = scope == "all" || scope == "voice";
let Some(pool) = pool(ctx).await else {
return;
};
let bot_id = ctx.cache.current_user().id;
if action == "add" {
let _ = sqlx::query(
r#"
INSERT INTO bot_nolog_channels (bot_id, guild_id, channel_id, disable_message, disable_voice)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (bot_id, guild_id, channel_id)
DO UPDATE SET disable_message = bot_nolog_channels.disable_message OR EXCLUDED.disable_message,
disable_voice = bot_nolog_channels.disable_voice OR EXCLUDED.disable_voice,
updated_at = NOW();
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.bind(channel.get() as i64)
.bind(set_message)
.bind(set_voice)
.execute(&pool)
.await;
} else if action == "del" {
let _ = sqlx::query(
r#"
UPDATE bot_nolog_channels
SET disable_message = CASE WHEN $4 THEN FALSE ELSE disable_message END,
disable_voice = CASE WHEN $5 THEN FALSE ELSE disable_voice END,
updated_at = NOW()
WHERE bot_id = $1 AND guild_id = $2 AND channel_id = $3;
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.bind(channel.get() as i64)
.bind(set_message)
.bind(set_voice)
.execute(&pool)
.await;
let _ = sqlx::query(
r#"
DELETE FROM bot_nolog_channels
WHERE bot_id = $1 AND guild_id = $2 AND channel_id = $3
AND disable_message = FALSE AND disable_voice = FALSE;
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.bind(channel.get() as i64)
.execute(&pool)
.await;
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("NoLog")
.description(format!(
"{} applique sur <#{}> ({})",
action,
channel.get(),
scope
))
.color(theme_color(ctx).await),
)
.await;
} }
pub struct NologCommand; pub struct NologCommand;
@@ -12,14 +109,12 @@ pub static COMMAND_DESCRIPTOR: NologCommand = NologCommand;
impl crate::commands::command_contract::CommandSpec for NologCommand { impl crate::commands::command_contract::CommandSpec for NologCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "nolog", name: "nolog",
command: "nolog",
category: "admin", category: "admin",
params: "<add/del> [salon] [message|voice|all]", params: "<add/del> [salon] [message|voice|all]",
summary: "Exclut des salons des logs", summary: "Exclut des salons des logs",
description: "Desactive ou reactive les logs message/voice pour certains salons.", description: "Desactive ou reactive les logs message/voice pour certains salons.",
examples: &["+nolog add #secret all", "+nolog del #secret message"], examples: &["+nolog add #secret all", "+nolog del #secret message"],
alias_source_key: "nolog",
default_aliases: &["nlg"], default_aliases: &["nlg"],
default_permission: 8, default_permission: 8,
} }
+1 -3
View File
@@ -45,14 +45,12 @@ pub static COMMAND_DESCRIPTOR: OwnerCommand = OwnerCommand;
impl crate::commands::command_contract::CommandSpec for OwnerCommand { impl crate::commands::command_contract::CommandSpec for OwnerCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "owner", name: "owner",
command: "owner",
category: "admin", category: "admin",
params: "aucun", params: "aucun",
summary: "Liste les owners du bot", summary: "Liste les owners du bot",
description: "Affiche l owner application et les owners ajoutes en base.", description: "Affiche l owner application et les owners ajoutes en base.",
examples: &["+owner", "+or", "+help owner"], examples: &["+owner", "+or", "+help owner"],
alias_source_key: "owner",
default_aliases: &["own"], default_aliases: &["own"],
default_permission: 9, default_permission: 9,
} }
+43 -5
View File
@@ -1,9 +1,49 @@
use crate::commands::logs_service; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::logs_command_helpers::{parse_target_channel, set_log_channel};
pub async fn handle_raidlog(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_raidlog(ctx: &Context, msg: &Message, args: &[&str]) {
logs_service::handle_raidlog(ctx, msg, args).await; let Some(guild_id) = msg.guild_id else {
return;
};
if args
.first()
.map(|a| a.eq_ignore_ascii_case("off"))
.unwrap_or(false)
{
set_log_channel(ctx, guild_id, "raid", None, false).await;
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("RaidLog")
.description("Desactive.")
.color(theme_color(ctx).await),
)
.await;
return;
}
let channel = parse_target_channel(msg, args, 0);
set_log_channel(ctx, guild_id, "raid", channel, true).await;
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("RaidLog")
.description(format!(
"Active dans {}.",
channel
.map(|c| format!("<#{}>", c.get()))
.unwrap_or_else(|| "ce salon".to_string())
))
.color(theme_color(ctx).await),
)
.await;
} }
pub struct RaidlogCommand; pub struct RaidlogCommand;
@@ -12,14 +52,12 @@ pub static COMMAND_DESCRIPTOR: RaidlogCommand = RaidlogCommand;
impl crate::commands::command_contract::CommandSpec for RaidlogCommand { impl crate::commands::command_contract::CommandSpec for RaidlogCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "raidlog", name: "raidlog",
command: "raidlog",
category: "admin", category: "admin",
params: "[salon]|off", params: "[salon]|off",
summary: "Active les logs antiraid", summary: "Active les logs antiraid",
description: "Active les logs antiraid dans un salon ou les desactive.", description: "Active les logs antiraid dans un salon ou les desactive.",
examples: &["+raidlog #logs", "+raidlog off"], examples: &["+raidlog #logs", "+raidlog off"],
alias_source_key: "raidlog",
default_aliases: &["rdlog"], default_aliases: &["rdlog"],
default_permission: 8, default_permission: 8,
} }
+18
View File
@@ -122,3 +122,21 @@ pub async fn handle_rename(ctx: &Context, msg: &Message, args: &[&str]) {
) )
.await; .await;
} }
pub struct RenameCommand;
pub static COMMAND_DESCRIPTOR: RenameCommand = RenameCommand;
impl crate::commands::command_contract::CommandSpec for RenameCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "rename",
category: "admin",
params: "<nom...>",
summary: "Renomme le ticket courant",
description: "Renomme le salon du ticket et met a jour son titre en base.",
examples: &["+rename support-client", "+help rename"],
default_aliases: &[],
default_permission: 2,
}
}
}
+44 -5
View File
@@ -1,10 +1,51 @@
use serenity::builder::CreateChannel;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::advanced_tools; use crate::commands::common::parse_channel_id;
pub async fn handle_renew(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_renew(ctx: &Context, msg: &Message, args: &[&str]) {
advanced_tools::handle_renew(ctx, msg, args).await; 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 struct RenewCommand;
@@ -13,14 +54,12 @@ pub static COMMAND_DESCRIPTOR: RenewCommand = RenewCommand;
impl crate::commands::command_contract::CommandSpec for RenewCommand { impl crate::commands::command_contract::CommandSpec for RenewCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "renew", name: "renew",
command: "renew",
category: "admin", category: "admin",
params: "[salon]", params: "[salon]",
summary: "Recree un salon textuel", summary: "Recree un salon textuel",
description: "Supprime puis recree un salon textuel en conservant les options principales.", description: "Supprime puis recree un salon textuel en conservant les options principales.",
examples: &["+renew", "+renew #general"], examples: &["+renew", "+renew #general"],
alias_source_key: "renew",
default_aliases: &["nuke", "rebuildch"], default_aliases: &["nuke", "rebuildch"],
default_permission: 8, default_permission: 8,
} }
+49 -6
View File
@@ -1,10 +1,55 @@
use rand::seq::SliceRandom;
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::advanced_tools; use crate::commands::common::{send_embed, theme_color};
pub async fn handle_reroll(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_reroll(ctx: &Context, msg: &Message, _args: &[&str]) {
advanced_tools::handle_reroll(ctx, msg, args).await; let Some(referenced) = msg.referenced_message.as_ref() else {
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Reroll")
.description("Réponds à un message giveaway pour reroll.")
.color(0xED4245),
)
.await;
return;
};
let mut candidates = referenced.mentions.iter().map(|u| u.id).collect::<Vec<_>>();
candidates.sort_by_key(|u| u.get());
candidates.dedup();
if candidates.is_empty() {
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Reroll")
.description("Aucun participant détecté.")
.color(0xED4245),
)
.await;
return;
}
let winner = candidates
.choose(&mut rand::thread_rng())
.copied()
.unwrap_or(candidates[0]);
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Reroll")
.description(format!("Nouveau gagnant: <@{}>", winner.get()))
.color(theme_color(ctx).await),
)
.await;
} }
pub struct RerollCommand; pub struct RerollCommand;
@@ -13,14 +58,12 @@ pub static COMMAND_DESCRIPTOR: RerollCommand = RerollCommand;
impl crate::commands::command_contract::CommandSpec for RerollCommand { impl crate::commands::command_contract::CommandSpec for RerollCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "reroll", name: "reroll",
command: "reroll",
category: "admin", category: "admin",
params: "aucun (en reponse a un message)", params: "aucun (en reponse a un message)",
summary: "Relance un tirage giveaway", summary: "Relance un tirage giveaway",
description: "Choisit un nouveau gagnant depuis le message cible.", description: "Choisit un nouveau gagnant depuis le message cible.",
examples: &["+reroll"], examples: &["+reroll"],
alias_source_key: "reroll",
default_aliases: &["rro", "greroll"], default_aliases: &["rro", "greroll"],
default_permission: 8, default_permission: 8,
} }
+49 -5
View File
@@ -1,9 +1,55 @@
use crate::commands::logs_service; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::logs_command_helpers::{parse_target_channel, set_log_channel};
pub async fn handle_rolelog(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_rolelog(ctx: &Context, msg: &Message, args: &[&str]) {
logs_service::handle_log_toggle(ctx, msg, args, "role", "RoleLog").await; let Some(guild_id) = msg.guild_id else {
return;
};
let Some(action) = args.first().map(|s| s.to_lowercase()) else {
let embed = CreateEmbed::new()
.title("RoleLog")
.description("Usage: +rolelog <on [salon]|off>")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
};
match action.as_str() {
"on" => {
let channel = parse_target_channel(msg, args, 1);
set_log_channel(ctx, guild_id, "role", channel, true).await;
let embed = CreateEmbed::new()
.title("RoleLog")
.description(format!(
"Active dans {}.",
channel
.map(|c| format!("<#{}>", c.get()))
.unwrap_or_else(|| "ce salon".to_string())
))
.color(theme_color(ctx).await);
send_embed(ctx, msg, embed).await;
}
"off" => {
set_log_channel(ctx, guild_id, "role", None, false).await;
let embed = CreateEmbed::new()
.title("RoleLog")
.description("Desactive.")
.color(theme_color(ctx).await);
send_embed(ctx, msg, embed).await;
}
_ => {
let embed = CreateEmbed::new()
.title("RoleLog")
.description("Usage: +rolelog <on [salon]|off>")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
}
}
} }
pub struct RolelogCommand; pub struct RolelogCommand;
@@ -12,14 +58,12 @@ pub static COMMAND_DESCRIPTOR: RolelogCommand = RolelogCommand;
impl crate::commands::command_contract::CommandSpec for RolelogCommand { impl crate::commands::command_contract::CommandSpec for RolelogCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "rolelog", name: "rolelog",
command: "rolelog",
category: "admin", category: "admin",
params: "<on [salon]|off>", params: "<on [salon]|off>",
summary: "Active les logs de roles", summary: "Active les logs de roles",
description: "Active ou desactive les logs des roles.", description: "Active ou desactive les logs des roles.",
examples: &["+rolelog on #logs", "+rolelog off"], examples: &["+rolelog on #logs", "+rolelog off"],
alias_source_key: "rolelog",
default_aliases: &["rlog"], default_aliases: &["rlog"],
default_permission: 8, default_permission: 8,
} }
+91 -5
View File
@@ -1,10 +1,98 @@
use chrono::Utc;
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::moderation_tools; 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]) { pub async fn handle_sanctions(ctx: &Context, msg: &Message, args: &[&str]) {
moderation_tools::handle_sanctions(ctx, msg, args).await; 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 struct SanctionsCommand;
@@ -13,14 +101,12 @@ pub static COMMAND_DESCRIPTOR: SanctionsCommand = SanctionsCommand;
impl crate::commands::command_contract::CommandSpec for SanctionsCommand { impl crate::commands::command_contract::CommandSpec for SanctionsCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "sanctions", name: "sanctions",
command: "sanctions",
category: "admin", category: "admin",
params: "<@membre/ID>", params: "<@membre/ID>",
summary: "Affiche les sanctions d un membre", summary: "Affiche les sanctions d un membre",
description: "Liste l historique des sanctions d un membre.", description: "Liste l historique des sanctions d un membre.",
examples: &["+sanctions @User"], examples: &["+sanctions @User"],
alias_source_key: "sanctions",
default_aliases: &["sanct"], default_aliases: &["sanct"],
default_permission: 8, default_permission: 8,
} }
+1 -3
View File
@@ -27,14 +27,12 @@ pub static COMMAND_DESCRIPTOR: SayCommand = SayCommand;
impl crate::commands::command_contract::CommandSpec for SayCommand { impl crate::commands::command_contract::CommandSpec for SayCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "say", name: "say",
command: "say",
category: "admin", category: "admin",
params: "<message...>", params: "<message...>",
summary: "Fait parler le bot", summary: "Fait parler le bot",
description: "Envoie un message brut dans le salon courant via le bot.", description: "Envoie un message brut dans le salon courant via le bot.",
examples: &["+say", "+sy", "+help say"], examples: &["+say", "+sy", "+help say"],
alias_source_key: "say",
default_aliases: &["sym"], default_aliases: &["sym"],
default_permission: 8, default_permission: 8,
} }
+89 -5
View File
@@ -1,10 +1,96 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::logs_service; use crate::commands::common::{send_embed, theme_color};
use crate::commands::logs_command_helpers::pool;
pub async fn handle_set_boostembed(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_set_boostembed(ctx: &Context, msg: &Message, args: &[&str]) {
logs_service::handle_set_boostembed(ctx, msg, args).await; let Some(guild_id) = msg.guild_id else {
return;
};
if args.len() < 2 {
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Set BoostEmbed")
.description("Usage: +set boostembed <title|description|color> <valeur>")
.color(0xED4245),
)
.await;
return;
}
let field = args[0].to_lowercase();
let value = args[1..].join(" ");
let Some(pool) = pool(ctx).await else {
return;
};
let bot_id = ctx.cache.current_user().id;
let _ = sqlx::query(
r#"
INSERT INTO bot_boost_embed (bot_id, guild_id, enabled, title, description, color)
VALUES ($1, $2, TRUE, NULL, NULL, NULL)
ON CONFLICT (bot_id, guild_id)
DO NOTHING;
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.execute(&pool)
.await;
match field.as_str() {
"title" => {
let _ = sqlx::query(
"UPDATE bot_boost_embed SET title = $3, updated_at = NOW() WHERE bot_id = $1 AND guild_id = $2",
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.bind(value)
.execute(&pool)
.await;
}
"description" => {
let _ = sqlx::query(
"UPDATE bot_boost_embed SET description = $3, updated_at = NOW() WHERE bot_id = $1 AND guild_id = $2",
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.bind(value)
.execute(&pool)
.await;
}
"color" => {
let normalized = value
.trim()
.trim_start_matches('#')
.trim_start_matches("0x");
if let Ok(color) = u32::from_str_radix(normalized, 16) {
let _ = sqlx::query(
"UPDATE bot_boost_embed SET color = $3, updated_at = NOW() WHERE bot_id = $1 AND guild_id = $2",
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.bind(color as i32)
.execute(&pool)
.await;
}
}
_ => {}
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Set BoostEmbed")
.description("Configuration mise a jour.")
.color(theme_color(ctx).await),
)
.await;
} }
pub struct SetBoostembedCommand; pub struct SetBoostembedCommand;
@@ -13,8 +99,7 @@ pub static COMMAND_DESCRIPTOR: SetBoostembedCommand = SetBoostembedCommand;
impl crate::commands::command_contract::CommandSpec for SetBoostembedCommand { impl crate::commands::command_contract::CommandSpec for SetBoostembedCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "set_boostembed", name: "set_boostembed",
command: "set boostembed",
category: "admin", category: "admin",
params: "<title|description|color> <valeur>", params: "<title|description|color> <valeur>",
summary: "Parametre l embed de boost", summary: "Parametre l embed de boost",
@@ -23,7 +108,6 @@ impl crate::commands::command_contract::CommandSpec for SetBoostembedCommand {
"+set boostembed title Merci", "+set boostembed title Merci",
"+set boostembed color #FF66CC", "+set boostembed color #FF66CC",
], ],
alias_source_key: "set_boostembed",
default_aliases: &["sboostembed"], default_aliases: &["sboostembed"],
default_permission: 8, default_permission: 8,
} }
+102 -5
View File
@@ -1,10 +1,109 @@
use std::collections::BTreeSet;
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::logs_service; use crate::commands::common::{send_embed, theme_color};
use crate::commands::logs_command_helpers::pool;
pub async fn handle_set_modlogs(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_set_modlogs(ctx: &Context, msg: &Message, args: &[&str]) {
logs_service::handle_set_modlogs(ctx, msg, args).await; 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 row = sqlx::query_as::<_, (String,)>(
r#"
SELECT modlog_events
FROM bot_log_settings
WHERE bot_id = $1 AND guild_id = $2
LIMIT 1;
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.fetch_optional(&pool)
.await
.ok()
.flatten();
let mut events = row
.map(|(s,)| {
s.split(',')
.map(|v| v.trim().to_lowercase())
.filter(|v| !v.is_empty())
.collect::<BTreeSet<_>>()
})
.unwrap_or_else(|| {
[
"warn",
"mute",
"tempmute",
"unmute",
"cmute",
"tempcmute",
"uncmute",
"kick",
"ban",
"tempban",
"unban",
"lock",
"unlock",
"hide",
"unhide",
"addrole",
"delrole",
"derank",
"clear",
"sanctions",
]
.into_iter()
.map(|s| s.to_string())
.collect()
});
if args.len() >= 2 {
let event = args[0].to_lowercase();
let state = args[1].to_lowercase();
if state == "on" {
events.insert(event);
} else if state == "off" {
events.remove(&event);
}
let serialized = events.iter().cloned().collect::<Vec<_>>().join(",");
let _ = sqlx::query(
r#"
INSERT INTO bot_log_settings (bot_id, guild_id, modlog_events)
VALUES ($1, $2, $3)
ON CONFLICT (bot_id, guild_id)
DO UPDATE SET modlog_events = EXCLUDED.modlog_events, updated_at = NOW();
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.bind(serialized)
.execute(&pool)
.await;
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Set ModLogs")
.description(format!(
"Evenements actifs:\n{}\n\nUsage: +set modlogs <event> <on/off>",
events.iter().cloned().collect::<Vec<_>>().join(", ")
))
.color(theme_color(ctx).await),
)
.await;
} }
pub struct SetModlogsCommand; pub struct SetModlogsCommand;
@@ -13,14 +112,12 @@ pub static COMMAND_DESCRIPTOR: SetModlogsCommand = SetModlogsCommand;
impl crate::commands::command_contract::CommandSpec for SetModlogsCommand { impl crate::commands::command_contract::CommandSpec for SetModlogsCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "set_modlogs", name: "set_modlogs",
command: "set modlogs",
category: "admin", category: "admin",
params: "[event on/off]", params: "[event on/off]",
summary: "Parametre les evenements de modlogs", summary: "Parametre les evenements de modlogs",
description: "Affiche ou modifie les evenements qui apparaissent dans les logs de moderation.", description: "Affiche ou modifie les evenements qui apparaissent dans les logs de moderation.",
examples: &["+set modlogs", "+set modlogs warn off"], examples: &["+set modlogs", "+set modlogs warn off"],
alias_source_key: "set_modlogs",
default_aliases: &["smodlog"], default_aliases: &["smodlog"],
default_permission: 8, default_permission: 8,
} }
+22
View File
@@ -496,3 +496,25 @@ pub async fn handle_modal_interaction(ctx: &Context, modal: &ModalInteraction) -
false false
} }
pub struct SuggestionCommand;
pub static COMMAND_DESCRIPTOR: SuggestionCommand = SuggestionCommand;
impl crate::commands::command_contract::CommandSpec for SuggestionCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "suggestion",
category: "admin",
params: "<contenu...> | settings",
summary: "Publie ou configure les suggestions",
description: "Publie une suggestion utilisateur ou ouvre le panneau de configuration.",
examples: &[
"+suggestion Ameliorer le salon",
"+suggestion settings",
"+help suggestion",
],
default_aliases: &[],
default_permission: 0,
}
}
}
+68 -5
View File
@@ -1,10 +1,75 @@
use serenity::builder::{CreateEmbed, EditChannel};
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::advanced_tools; use crate::commands::common::{parse_channel_id, send_embed, theme_color};
pub async fn handle_sync(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_sync(ctx: &Context, msg: &Message, args: &[&str]) {
advanced_tools::handle_sync(ctx, msg, args).await; let Some(guild_id) = msg.guild_id else {
return;
};
let Some(scope) = args.first() else {
return;
};
let Ok(channels) = guild_id.channels(&ctx.http).await else {
return;
};
let ids_to_sync = if scope.eq_ignore_ascii_case("all") {
channels.keys().copied().collect::<Vec<_>>()
} else if let Some(channel_id) = parse_channel_id(scope) {
if let Some(target) = channels.get(&channel_id) {
if target.kind == ChannelType::Category {
channels
.values()
.filter(|ch| ch.parent_id == Some(channel_id))
.map(|ch| ch.id)
.collect::<Vec<_>>()
} else {
vec![channel_id]
}
} else {
vec![channel_id]
}
} else {
Vec::new()
};
let mut synced = 0usize;
for channel_id in ids_to_sync {
let Some(channel) = channels.get(&channel_id) else {
continue;
};
let Some(parent_id) = channel.parent_id else {
continue;
};
let Some(parent) = channels.get(&parent_id) else {
continue;
};
if channel_id
.edit(
&ctx.http,
EditChannel::new().permissions(parent.permission_overwrites.clone()),
)
.await
.is_ok()
{
synced += 1;
}
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Sync")
.description(format!("{} salons synchronisés.", synced))
.color(theme_color(ctx).await),
)
.await;
} }
pub struct SyncCommand; pub struct SyncCommand;
@@ -13,14 +78,12 @@ pub static COMMAND_DESCRIPTOR: SyncCommand = SyncCommand;
impl crate::commands::command_contract::CommandSpec for SyncCommand { impl crate::commands::command_contract::CommandSpec for SyncCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "sync", name: "sync",
command: "sync",
category: "admin", category: "admin",
params: "<salon/categorie/all>", params: "<salon/categorie/all>",
summary: "Synchronise les permissions", summary: "Synchronise les permissions",
description: "Synchronise les permissions d'un salon avec sa categorie, ou tous les salons avec all.", description: "Synchronise les permissions d'un salon avec sa categorie, ou tous les salons avec all.",
examples: &["+sync all", "+sync #general"], examples: &["+sync all", "+sync #general"],
alias_source_key: "sync",
default_aliases: &["chsync"], default_aliases: &["chsync"],
default_permission: 8, default_permission: 8,
} }
+66 -5
View File
@@ -1,22 +1,83 @@
use crate::commands::moderation_tools; use chrono::Utc;
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::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]) { pub async fn handle_tempban(ctx: &Context, msg: &Message, args: &[&str]) {
moderation_tools::handle_ban(ctx, msg, args, true).await; 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 struct TempbanCommand;
pub static COMMAND_DESCRIPTOR: TempbanCommand = TempbanCommand; pub static COMMAND_DESCRIPTOR: TempbanCommand = TempbanCommand;
impl crate::commands::command_contract::CommandSpec for TempbanCommand { impl crate::commands::command_contract::CommandSpec for TempbanCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "tempban", name: "tempban",
command: "tempban",
category: "admin", category: "admin",
params: "<@membre/ID[,..]> <duree> [raison]", params: "<@membre/ID[,..]> <duree> [raison]",
summary: "Ban temporaire", summary: "Ban temporaire",
description: "Ban temporairement un ou plusieurs membres.", description: "Ban temporairement un ou plusieurs membres.",
examples: &["+tempban @User 1d"], examples: &["+tempban @User 1d"],
alias_source_key: "tempban",
default_aliases: &["tb"], default_aliases: &["tb"],
default_permission: 8, default_permission: 8,
} }
+60 -5
View File
@@ -1,22 +1,77 @@
use crate::commands::moderation_tools; use chrono::Utc;
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::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]) { pub async fn handle_tempcmute(ctx: &Context, msg: &Message, args: &[&str]) {
moderation_tools::handle_cmute(ctx, msg, args, true).await; 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 struct TempcmuteCommand;
pub static COMMAND_DESCRIPTOR: TempcmuteCommand = TempcmuteCommand; pub static COMMAND_DESCRIPTOR: TempcmuteCommand = TempcmuteCommand;
impl crate::commands::command_contract::CommandSpec for TempcmuteCommand { impl crate::commands::command_contract::CommandSpec for TempcmuteCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "tempcmute", name: "tempcmute",
command: "tempcmute",
category: "admin", category: "admin",
params: "<@membre/ID[,..]> <duree> [raison]", params: "<@membre/ID[,..]> <duree> [raison]",
summary: "Mute salon temporaire", summary: "Mute salon temporaire",
description: "Mute temporaire sur le salon courant.", description: "Mute temporaire sur le salon courant.",
examples: &["+tempcmute @User 5m"], examples: &["+tempcmute @User 5m"],
alias_source_key: "tempcmute",
default_aliases: &["tcm"], default_aliases: &["tcm"],
default_permission: 8, default_permission: 8,
} }
+60 -5
View File
@@ -1,22 +1,77 @@
use crate::commands::moderation_tools; use chrono::Utc;
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::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]) { pub async fn handle_tempmute(ctx: &Context, msg: &Message, args: &[&str]) {
moderation_tools::handle_mute(ctx, msg, args, true).await; 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 struct TempmuteCommand;
pub static COMMAND_DESCRIPTOR: TempmuteCommand = TempmuteCommand; pub static COMMAND_DESCRIPTOR: TempmuteCommand = TempmuteCommand;
impl crate::commands::command_contract::CommandSpec for TempmuteCommand { impl crate::commands::command_contract::CommandSpec for TempmuteCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "tempmute", name: "tempmute",
command: "tempmute",
category: "admin", category: "admin",
params: "<@membre/ID[,..]> <duree> [raison]", params: "<@membre/ID[,..]> <duree> [raison]",
summary: "Mute temporaire", summary: "Mute temporaire",
description: "Mute un ou plusieurs membres pour une duree donnee.", description: "Mute un ou plusieurs membres pour une duree donnee.",
examples: &["+tempmute @User 10m"], examples: &["+tempmute @User 10m"],
alias_source_key: "tempmute",
default_aliases: &["tm"], default_aliases: &["tm"],
default_permission: 8, default_permission: 8,
} }
+106 -5
View File
@@ -1,10 +1,113 @@
use chrono::Utc;
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use std::time::Duration;
use crate::commands::advanced_tools; use crate::commands::admin_common::parse_user_id;
use crate::commands::common::{parse_role, send_embed, theme_color};
use crate::db::DbPoolKey;
fn duration_from_input(input: &str) -> Option<Duration> {
let raw = input.trim().to_lowercase();
if raw.is_empty() {
return None;
}
let mut number = String::new();
let mut suffix = String::new();
for ch in raw.chars() {
if ch.is_ascii_digit() {
if !suffix.is_empty() {
return None;
}
number.push(ch);
} else if !ch.is_whitespace() {
suffix.push(ch);
}
}
let value = number.parse::<u64>().ok()?;
let secs = match suffix.as_str() {
"s" | "sec" | "secs" | "seconde" | "secondes" => value,
"m" | "min" | "mins" | "minute" | "minutes" => value * 60,
"h" | "heure" | "heures" => value * 3600,
"j" | "d" | "jour" | "jours" => value * 86400,
_ => return None,
};
Some(Duration::from_secs(secs.max(1)))
}
pub async fn handle_temprole(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_temprole(ctx: &Context, msg: &Message, args: &[&str]) {
advanced_tools::handle_temprole(ctx, msg, args).await; let Some(guild_id) = msg.guild_id else {
return;
};
if args.len() < 3 {
return;
}
let Some(user_id) = parse_user_id(args[0]) else {
return;
};
let Ok(guild) = guild_id.to_partial_guild(&ctx.http).await else {
return;
};
let Some(role) = parse_role(&guild, args[1]) else {
return;
};
let Some(duration) = duration_from_input(args[2]) else {
return;
};
let expires_at = Utc::now() + chrono::Duration::seconds(duration.as_secs() as i64);
if let Ok(member) = guild_id.member(&ctx.http, user_id).await {
let _ = member.add_role(&ctx.http, role.id).await;
}
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;
let _ = sqlx::query(
r#"
INSERT INTO bot_temproles (bot_id, guild_id, user_id, role_id, expires_at, active, added_by)
VALUES ($1, $2, $3, $4, $5, TRUE, $6)
ON CONFLICT (bot_id, guild_id, user_id, role_id)
DO UPDATE SET expires_at = EXCLUDED.expires_at, active = TRUE, added_by = EXCLUDED.added_by;
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.bind(user_id.get() as i64)
.bind(role.id.get() as i64)
.bind(expires_at)
.bind(msg.author.id.get() as i64)
.execute(&pool)
.await;
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("TempRole")
.description(format!(
"Rôle <@&{}> ajouté à <@{}> jusqu'à <t:{}:F>.",
role.id.get(),
user_id.get(),
expires_at.timestamp()
))
.color(theme_color(ctx).await),
)
.await;
} }
pub struct TempRoleCommand; pub struct TempRoleCommand;
@@ -13,14 +116,12 @@ pub static COMMAND_DESCRIPTOR: TempRoleCommand = TempRoleCommand;
impl crate::commands::command_contract::CommandSpec for TempRoleCommand { impl crate::commands::command_contract::CommandSpec for TempRoleCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "temprole", name: "temprole",
command: "temprole",
category: "admin", category: "admin",
params: "<membre> <role> <duree>", params: "<membre> <role> <duree>",
summary: "Ajoute un role temporaire", summary: "Ajoute un role temporaire",
description: "Attribue un role a un membre pour une duree donnee puis le retire automatiquement.", description: "Attribue un role a un membre pour une duree donnee puis le retire automatiquement.",
examples: &["+temprole @User @VIP 2h"], examples: &["+temprole @User @VIP 2h"],
alias_source_key: "temprole",
default_aliases: &["trole", "tmprole"], default_aliases: &["trole", "tmprole"],
default_permission: 8, default_permission: 8,
} }
+18
View File
@@ -440,3 +440,21 @@ pub async fn handle_voice_state_update(ctx: &Context, old: Option<&VoiceState>,
} }
} }
} }
pub struct TempvocCommand;
pub static COMMAND_DESCRIPTOR: TempvocCommand = TempvocCommand;
impl crate::commands::command_contract::CommandSpec for TempvocCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "tempvoc",
category: "admin",
params: "[cmd]",
summary: "Configure les vocaux temporaires",
description: "Affiche le menu de configuration du systeme de vocaux temporaires.",
examples: &["+tempvoc", "+tempvoc cmd", "+help tempvoc"],
default_aliases: &[],
default_permission: 8,
}
}
}
+18
View File
@@ -15,3 +15,21 @@ pub async fn handle_tempvoc_cmd(ctx: &Context, msg: &Message, _args: &[&str]) {
send_embed(ctx, msg, embed).await; send_embed(ctx, msg, embed).await;
} }
pub struct TempvocCmdCommand;
pub static COMMAND_DESCRIPTOR: TempvocCmdCommand = TempvocCmdCommand;
impl crate::commands::command_contract::CommandSpec for TempvocCmdCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "tempvoc_cmd",
category: "admin",
params: "aucun",
summary: "Affiche laide tempvoc",
description: "Affiche un rappel des commandes et du fonctionnement de tempvoc.",
examples: &["+tempvoc cmd", "+help tempvoc_cmd"],
default_aliases: &[],
default_permission: 0,
}
}
}
+18
View File
@@ -441,3 +441,21 @@ pub async fn handle_modal_interaction(ctx: &Context, modal: &ModalInteraction) -
false false
} }
pub struct TicketCommand;
pub static COMMAND_DESCRIPTOR: TicketCommand = TicketCommand;
impl crate::commands::command_contract::CommandSpec for TicketCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "ticket",
category: "admin",
params: "settings",
summary: "Ouvre la gestion des tickets",
description: "Affiche le menu de configuration du systeme de tickets.",
examples: &["+ticket", "+help ticket"],
default_aliases: &[],
default_permission: 8,
}
}
}
+18
View File
@@ -150,3 +150,21 @@ pub async fn handle_ticket_add(ctx: &Context, msg: &Message, args: &[&str]) {
pub async fn handle_ticket_remove(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_ticket_remove(ctx: &Context, msg: &Message, args: &[&str]) {
let _ = ticket_member_update(ctx, msg, args, false).await; let _ = ticket_member_update(ctx, msg, args, false).await;
} }
pub struct TicketMemberCommand;
pub static COMMAND_DESCRIPTOR: TicketMemberCommand = TicketMemberCommand;
impl crate::commands::command_contract::CommandSpec for TicketMemberCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "add",
category: "admin",
params: "<@membre/ID>",
summary: "Ajoute un membre au ticket",
description: "Ajoute un membre supplementaire au ticket courant via +add.",
examples: &["+add @User", "+help add"],
default_aliases: &[],
default_permission: 2,
}
}
}
+18
View File
@@ -66,3 +66,21 @@ pub async fn handle_tickets(ctx: &Context, msg: &Message, args: &[&str]) {
) )
.await; .await;
} }
pub struct TicketsCommand;
pub static COMMAND_DESCRIPTOR: TicketsCommand = TicketsCommand;
impl crate::commands::command_contract::CommandSpec for TicketsCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata {
name: "tickets",
category: "admin",
params: "[page]",
summary: "Liste les tickets",
description: "Affiche les tickets du serveur avec pagination.",
examples: &["+tickets", "+tickets 2", "+help tickets"],
default_aliases: &[],
default_permission: 2,
}
}
}
+34 -5
View File
@@ -1,22 +1,51 @@
use crate::commands::moderation_tools; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::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]) { pub async fn handle_unban(ctx: &Context, msg: &Message, args: &[&str]) {
moderation_tools::handle_unban(ctx, msg, args).await; 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 struct UnbanCommand;
pub static COMMAND_DESCRIPTOR: UnbanCommand = UnbanCommand; pub static COMMAND_DESCRIPTOR: UnbanCommand = UnbanCommand;
impl crate::commands::command_contract::CommandSpec for UnbanCommand { impl crate::commands::command_contract::CommandSpec for UnbanCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "unban", name: "unban",
command: "unban",
category: "admin", category: "admin",
params: "<@membre/ID[,..]>", params: "<@membre/ID[,..]>",
summary: "Retire un ban", summary: "Retire un ban",
description: "Unban un ou plusieurs membres.", description: "Unban un ou plusieurs membres.",
examples: &["+unban @User"], examples: &["+unban @User"],
alias_source_key: "unban",
default_aliases: &["ub"], default_aliases: &["ub"],
default_permission: 8, default_permission: 8,
} }
+29 -6
View File
@@ -1,10 +1,35 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::advanced_tools; use crate::commands::common::{send_embed, theme_color};
pub async fn handle_unbanall(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_unbanall(ctx: &Context, msg: &Message, _args: &[&str]) {
advanced_tools::handle_unbanall(ctx, msg, args).await; 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 struct UnbanAllCommand;
@@ -13,14 +38,12 @@ pub static COMMAND_DESCRIPTOR: UnbanAllCommand = UnbanAllCommand;
impl crate::commands::command_contract::CommandSpec for UnbanAllCommand { impl crate::commands::command_contract::CommandSpec for UnbanAllCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "unbanall", name: "unbanall",
command: "unbanall",
category: "admin", category: "admin",
params: "aucun", params: "aucun",
summary: "Retire tous les bannissements", summary: "Retire tous les bannissements",
description: "Supprime tous les bans du serveur cible.", description: "Supprime tous les bans du serveur cible.",
examples: &["+unbanall"], examples: &["+unbanall"],
alias_source_key: "unbanall",
default_aliases: &["uball", "clearbans"], default_aliases: &["uball", "clearbans"],
default_permission: 8, default_permission: 8,
} }
+1 -3
View File
@@ -65,14 +65,12 @@ pub static COMMAND_DESCRIPTOR: UnblCommand = UnblCommand;
impl crate::commands::command_contract::CommandSpec for UnblCommand { impl crate::commands::command_contract::CommandSpec for UnblCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "unbl", name: "unbl",
command: "unbl",
category: "admin", category: "admin",
params: "<@membre/ID>", params: "<@membre/ID>",
summary: "Retire un utilisateur blacklist", summary: "Retire un utilisateur blacklist",
description: "Retire un utilisateur de la blacklist globale du bot.", description: "Retire un utilisateur de la blacklist globale du bot.",
examples: &["+unbl", "+ul", "+help unbl"], examples: &["+unbl", "+ul", "+help unbl"],
alias_source_key: "unbl",
default_aliases: &["unb"], default_aliases: &["unb"],
default_permission: 9, default_permission: 9,
} }
+48 -5
View File
@@ -1,22 +1,65 @@
use crate::commands::moderation_tools; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::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]) { pub async fn handle_uncmute(ctx: &Context, msg: &Message, args: &[&str]) {
moderation_tools::handle_uncmute(ctx, msg, args).await; 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 struct UncmuteCommand;
pub static COMMAND_DESCRIPTOR: UncmuteCommand = UncmuteCommand; pub static COMMAND_DESCRIPTOR: UncmuteCommand = UncmuteCommand;
impl crate::commands::command_contract::CommandSpec for UncmuteCommand { impl crate::commands::command_contract::CommandSpec for UncmuteCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "uncmute", name: "uncmute",
command: "uncmute",
category: "admin", category: "admin",
params: "<@membre/ID[,..]>", params: "<@membre/ID[,..]>",
summary: "Retire un cmute", summary: "Retire un cmute",
description: "Met fin au mute salon.", description: "Met fin au mute salon.",
examples: &["+uncmute @User"], examples: &["+uncmute @User"],
alias_source_key: "uncmute",
default_aliases: &["ucm"], default_aliases: &["ucm"],
default_permission: 8, default_permission: 8,
} }
+30 -5
View File
@@ -1,22 +1,47 @@
use crate::commands::moderation_tools; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::common::{parse_channel_id, send_embed, theme_color};
use crate::commands::moderation_channel_helpers::edit_channel_visibility;
pub async fn handle_unhide(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_unhide(ctx: &Context, msg: &Message, args: &[&str]) {
moderation_tools::handle_hide_unhide(ctx, msg, args, false).await; let Some(guild_id) = msg.guild_id else {
return;
};
let target = args
.first()
.and_then(|raw| parse_channel_id(raw))
.unwrap_or(msg.channel_id);
let ok = edit_channel_visibility(ctx, guild_id, target, None, Some(false)).await;
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("UnHide")
.description(if ok {
format!("Salon <#{}> mis a jour.", target.get())
} else {
"Echec de mise a jour du salon.".to_string()
})
.color(theme_color(ctx).await),
)
.await;
} }
pub struct UnhideCommand; pub struct UnhideCommand;
pub static COMMAND_DESCRIPTOR: UnhideCommand = UnhideCommand; pub static COMMAND_DESCRIPTOR: UnhideCommand = UnhideCommand;
impl crate::commands::command_contract::CommandSpec for UnhideCommand { impl crate::commands::command_contract::CommandSpec for UnhideCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "unhide", name: "unhide",
command: "unhide",
category: "admin", category: "admin",
params: "[salon]", params: "[salon]",
summary: "Affiche un salon", summary: "Affiche un salon",
description: "Rend a nouveau visible un salon.", description: "Rend a nouveau visible un salon.",
examples: &["+unhide", "+unhide #general"], examples: &["+unhide", "+unhide #general"],
alias_source_key: "unhide",
default_aliases: &["uhd"], default_aliases: &["uhd"],
default_permission: 8, default_permission: 8,
} }
+28 -5
View File
@@ -1,9 +1,34 @@
use crate::commands::moderation_tools; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::moderation_channel_helpers::edit_channel_visibility;
pub async fn handle_unhideall(ctx: &Context, msg: &Message) { pub async fn handle_unhideall(ctx: &Context, msg: &Message) {
moderation_tools::handle_hideall_unhideall(ctx, msg, false).await; let Some(guild_id) = msg.guild_id else {
return;
};
let Ok(channels) = guild_id.channels(&ctx.http).await else {
return;
};
let mut changed = 0usize;
for channel_id in channels.keys() {
if edit_channel_visibility(ctx, guild_id, *channel_id, None, Some(false)).await {
changed += 1;
}
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("UnHideAll")
.description(format!("{} salon(s) mis a jour.", changed))
.color(theme_color(ctx).await),
)
.await;
} }
pub struct UnhideallCommand; pub struct UnhideallCommand;
@@ -12,14 +37,12 @@ pub static COMMAND_DESCRIPTOR: UnhideallCommand = UnhideallCommand;
impl crate::commands::command_contract::CommandSpec for UnhideallCommand { impl crate::commands::command_contract::CommandSpec for UnhideallCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "unhideall", name: "unhideall",
command: "unhideall",
category: "admin", category: "admin",
params: "aucun", params: "aucun",
summary: "Affiche tous les salons", summary: "Affiche tous les salons",
description: "Rend visibles tous les salons du serveur.", description: "Rend visibles tous les salons du serveur.",
examples: &["+unhideall"], examples: &["+unhideall"],
alias_source_key: "unhideall",
default_aliases: &["uhda"], default_aliases: &["uhda"],
default_permission: 8, default_permission: 8,
} }
+30 -5
View File
@@ -1,22 +1,47 @@
use crate::commands::moderation_tools; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::common::{parse_channel_id, send_embed, theme_color};
use crate::commands::moderation_channel_helpers::edit_channel_visibility;
pub async fn handle_unlock(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_unlock(ctx: &Context, msg: &Message, args: &[&str]) {
moderation_tools::handle_lock_unlock(ctx, msg, args, false).await; let Some(guild_id) = msg.guild_id else {
return;
};
let target = args
.first()
.and_then(|raw| parse_channel_id(raw))
.unwrap_or(msg.channel_id);
let ok = edit_channel_visibility(ctx, guild_id, target, Some(false), None).await;
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Unlock")
.description(if ok {
format!("Salon <#{}> mis a jour.", target.get())
} else {
"Echec de mise a jour du salon.".to_string()
})
.color(theme_color(ctx).await),
)
.await;
} }
pub struct UnlockCommand; pub struct UnlockCommand;
pub static COMMAND_DESCRIPTOR: UnlockCommand = UnlockCommand; pub static COMMAND_DESCRIPTOR: UnlockCommand = UnlockCommand;
impl crate::commands::command_contract::CommandSpec for UnlockCommand { impl crate::commands::command_contract::CommandSpec for UnlockCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "unlock", name: "unlock",
command: "unlock",
category: "admin", category: "admin",
params: "[salon]", params: "[salon]",
summary: "Ouvre un salon", summary: "Ouvre un salon",
description: "Deverrouille un salon texte ou vocal.", description: "Deverrouille un salon texte ou vocal.",
examples: &["+unlock", "+unlock #general"], examples: &["+unlock", "+unlock #general"],
alias_source_key: "unlock",
default_aliases: &["ulk"], default_aliases: &["ulk"],
default_permission: 8, default_permission: 8,
} }
+29 -5
View File
@@ -1,22 +1,46 @@
use crate::commands::moderation_tools; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::moderation_channel_helpers::edit_channel_visibility;
pub async fn handle_unlockall(ctx: &Context, msg: &Message) { pub async fn handle_unlockall(ctx: &Context, msg: &Message) {
moderation_tools::handle_lockall_unlockall(ctx, msg, false).await; let Some(guild_id) = msg.guild_id else {
return;
};
let Ok(channels) = guild_id.channels(&ctx.http).await else {
return;
};
let mut changed = 0usize;
for channel_id in channels.keys() {
if edit_channel_visibility(ctx, guild_id, *channel_id, Some(false), None).await {
changed += 1;
}
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("UnlockAll")
.description(format!("{} salon(s) mis a jour.", changed))
.color(theme_color(ctx).await),
)
.await;
} }
pub struct UnlockallCommand; pub struct UnlockallCommand;
pub static COMMAND_DESCRIPTOR: UnlockallCommand = UnlockallCommand; pub static COMMAND_DESCRIPTOR: UnlockallCommand = UnlockallCommand;
impl crate::commands::command_contract::CommandSpec for UnlockallCommand { impl crate::commands::command_contract::CommandSpec for UnlockallCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "unlockall", name: "unlockall",
command: "unlockall",
category: "admin", category: "admin",
params: "aucun", params: "aucun",
summary: "Ouvre tous les salons", summary: "Ouvre tous les salons",
description: "Deverrouille tous les salons du serveur.", description: "Deverrouille tous les salons du serveur.",
examples: &["+unlockall"], examples: &["+unlockall"],
alias_source_key: "unlockall",
default_aliases: &["ulka"], default_aliases: &["ulka"],
default_permission: 8, default_permission: 8,
} }
+51 -5
View File
@@ -1,10 +1,58 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::advanced_tools; use crate::commands::common::{parse_role, send_embed, theme_color};
pub async fn handle_unmassiverole(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_unmassiverole(ctx: &Context, msg: &Message, args: &[&str]) {
advanced_tools::handle_massive_role(ctx, msg, args, false).await; let Some(guild_id) = msg.guild_id else {
return;
};
if args.is_empty() {
return;
}
let Ok(guild) = guild_id.to_partial_guild(&ctx.http).await else {
return;
};
let Some(target_role) = parse_role(&guild, args[0]) else {
return;
};
let filter_role = args.get(1).and_then(|raw| parse_role(&guild, raw));
let Ok(members) = guild_id.members(&ctx.http, None, None).await else {
return;
};
let mut affected = 0usize;
for member in members {
if let Some(filter) = &filter_role {
if !member.roles.contains(&filter.id) {
continue;
}
}
if member.remove_role(&ctx.http, target_role.id).await.is_ok() {
affected += 1;
}
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("UnMassiveRole")
.description(format!(
"{} membres traités pour le rôle <@&{}>.",
affected,
target_role.id.get()
))
.color(theme_color(ctx).await),
)
.await;
} }
pub struct UnMassiveRoleCommand; pub struct UnMassiveRoleCommand;
@@ -13,14 +61,12 @@ pub static COMMAND_DESCRIPTOR: UnMassiveRoleCommand = UnMassiveRoleCommand;
impl crate::commands::command_contract::CommandSpec for UnMassiveRoleCommand { impl crate::commands::command_contract::CommandSpec for UnMassiveRoleCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "unmassiverole", name: "unmassiverole",
command: "unmassiverole",
category: "admin", category: "admin",
params: "<role_cible> [role_filtre]", params: "<role_cible> [role_filtre]",
summary: "Retire un role en masse", summary: "Retire un role en masse",
description: "Retire un role a tous les membres ou a ceux qui ont un role filtre.", description: "Retire un role a tous les membres ou a ceux qui ont un role filtre.",
examples: &["+unmassiverole @VIP", "+unmassiverole @VIP @Membres"], examples: &["+unmassiverole @VIP", "+unmassiverole @VIP @Membres"],
alias_source_key: "unmassiverole",
default_aliases: &["umrole", "umr"], default_aliases: &["umrole", "umr"],
default_permission: 8, default_permission: 8,
} }
+47 -5
View File
@@ -1,22 +1,64 @@
use crate::commands::moderation_tools; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::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]) { pub async fn handle_unmute(ctx: &Context, msg: &Message, args: &[&str]) {
moderation_tools::handle_unmute(ctx, msg, args).await; 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 struct UnmuteCommand;
pub static COMMAND_DESCRIPTOR: UnmuteCommand = UnmuteCommand; pub static COMMAND_DESCRIPTOR: UnmuteCommand = UnmuteCommand;
impl crate::commands::command_contract::CommandSpec for UnmuteCommand { impl crate::commands::command_contract::CommandSpec for UnmuteCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "unmute", name: "unmute",
command: "unmute",
category: "admin", category: "admin",
params: "<@membre/ID[,..]>", params: "<@membre/ID[,..]>",
summary: "Retire un mute", summary: "Retire un mute",
description: "Met fin au mute d un ou plusieurs membres.", description: "Met fin au mute d un ou plusieurs membres.",
examples: &["+unmute @User"], examples: &["+unmute @User"],
alias_source_key: "unmute",
default_aliases: &["um"], default_aliases: &["um"],
default_permission: 8, default_permission: 8,
} }
+62 -5
View File
@@ -1,22 +1,79 @@
use crate::commands::moderation_tools; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::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) { pub async fn handle_unmuteall(ctx: &Context, msg: &Message) {
moderation_tools::handle_unmuteall(ctx, msg).await; 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 struct UnmuteallCommand;
pub static COMMAND_DESCRIPTOR: UnmuteallCommand = UnmuteallCommand; pub static COMMAND_DESCRIPTOR: UnmuteallCommand = UnmuteallCommand;
impl crate::commands::command_contract::CommandSpec for UnmuteallCommand { impl crate::commands::command_contract::CommandSpec for UnmuteallCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "unmuteall", name: "unmuteall",
command: "unmuteall",
category: "admin", category: "admin",
params: "aucun", params: "aucun",
summary: "Retire tous les mutes", summary: "Retire tous les mutes",
description: "Supprime tous les mutes en cours.", description: "Supprime tous les mutes en cours.",
examples: &["+unmuteall"], examples: &["+unmuteall"],
alias_source_key: "unmuteall",
default_aliases: &["uma"], default_aliases: &["uma"],
default_permission: 8, default_permission: 8,
} }
+1 -3
View File
@@ -74,14 +74,12 @@ pub static COMMAND_DESCRIPTOR: UnownerCommand = UnownerCommand;
impl crate::commands::command_contract::CommandSpec for UnownerCommand { impl crate::commands::command_contract::CommandSpec for UnownerCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "unowner", name: "unowner",
command: "unowner",
category: "admin", category: "admin",
params: "<@membre/ID>", params: "<@membre/ID>",
summary: "Retire un owner du bot", summary: "Retire un owner du bot",
description: "Retire un utilisateur de la liste des owners supplementaires du bot.", description: "Retire un utilisateur de la liste des owners supplementaires du bot.",
examples: &["+unowner", "+ur", "+help unowner"], examples: &["+unowner", "+ur", "+help unowner"],
alias_source_key: "unowner",
default_aliases: &["uow"], default_aliases: &["uow"],
default_permission: 9, default_permission: 9,
} }
+63 -5
View File
@@ -1,10 +1,70 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::advanced_tools; use crate::commands::admin_common::parse_user_id;
use crate::commands::common::{parse_role, send_embed, theme_color};
use crate::db::DbPoolKey;
pub async fn handle_untemprole(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_untemprole(ctx: &Context, msg: &Message, args: &[&str]) {
advanced_tools::handle_untemprole(ctx, msg, args).await; let Some(guild_id) = msg.guild_id else {
return;
};
if args.len() < 2 {
return;
}
let Some(user_id) = parse_user_id(args[0]) else {
return;
};
let Ok(guild) = guild_id.to_partial_guild(&ctx.http).await else {
return;
};
let Some(role) = parse_role(&guild, args[1]) else {
return;
};
if let Ok(member) = guild_id.member(&ctx.http, user_id).await {
let _ = member.remove_role(&ctx.http, role.id).await;
}
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;
let _ = sqlx::query(
r#"
UPDATE bot_temproles
SET active = FALSE
WHERE bot_id = $1 AND guild_id = $2 AND user_id = $3 AND role_id = $4;
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.bind(user_id.get() as i64)
.bind(role.id.get() as i64)
.execute(&pool)
.await;
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("UnTempRole")
.description(format!(
"Rôle <@&{}> retiré à <@{}>.",
role.id.get(),
user_id.get()
))
.color(theme_color(ctx).await),
)
.await;
} }
pub struct UnTempRoleCommand; pub struct UnTempRoleCommand;
@@ -13,14 +73,12 @@ pub static COMMAND_DESCRIPTOR: UnTempRoleCommand = UnTempRoleCommand;
impl crate::commands::command_contract::CommandSpec for UnTempRoleCommand { impl crate::commands::command_contract::CommandSpec for UnTempRoleCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "untemprole", name: "untemprole",
command: "untemprole",
category: "admin", category: "admin",
params: "<membre> <role>", params: "<membre> <role>",
summary: "Retire un role temporaire", summary: "Retire un role temporaire",
description: "Retire immediatement un role temporaire et desactive son expiration.", description: "Retire immediatement un role temporaire et desactive son expiration.",
examples: &["+untemprole @User @VIP"], examples: &["+untemprole @User @VIP"],
alias_source_key: "untemprole",
default_aliases: &["untrole", "deltrole"], default_aliases: &["untrole", "deltrole"],
default_permission: 8, default_permission: 8,
} }
+30 -5
View File
@@ -1,10 +1,37 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::advanced_tools; use crate::commands::admin_common::parse_user_id;
use crate::commands::common::{send_embed, theme_color};
pub async fn handle_voicekick(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_voicekick(ctx: &Context, msg: &Message, args: &[&str]) {
advanced_tools::handle_voicekick(ctx, msg, args).await; let Some(guild_id) = msg.guild_id else {
return;
};
if args.is_empty() {
return;
}
let mut kicked = 0usize;
for raw in args {
if let Some(user_id) = parse_user_id(raw) {
if guild_id.disconnect_member(&ctx.http, user_id).await.is_ok() {
kicked += 1;
}
}
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("VoiceKick")
.description(format!("{} membres déconnectés.", kicked))
.color(theme_color(ctx).await),
)
.await;
} }
pub struct VoiceKickCommand; pub struct VoiceKickCommand;
@@ -13,14 +40,12 @@ pub static COMMAND_DESCRIPTOR: VoiceKickCommand = VoiceKickCommand;
impl crate::commands::command_contract::CommandSpec for VoiceKickCommand { impl crate::commands::command_contract::CommandSpec for VoiceKickCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "voicekick", name: "voicekick",
command: "voicekick",
category: "admin", category: "admin",
params: "<membre...>", params: "<membre...>",
summary: "Deconnecte des membres du vocal", summary: "Deconnecte des membres du vocal",
description: "Deconnecte un ou plusieurs membres de leur salon vocal actuel.", description: "Deconnecte un ou plusieurs membres de leur salon vocal actuel.",
examples: &["+voicekick @User", "+voicekick @U1 @U2"], examples: &["+voicekick @User", "+voicekick @U1 @U2"],
alias_source_key: "voicekick",
default_aliases: &["vk", "vdisconnect"], default_aliases: &["vk", "vdisconnect"],
default_permission: 8, default_permission: 8,
} }
+49 -5
View File
@@ -1,9 +1,55 @@
use crate::commands::logs_service; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::commands::logs_command_helpers::{parse_target_channel, set_log_channel};
pub async fn handle_voicelog(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_voicelog(ctx: &Context, msg: &Message, args: &[&str]) {
logs_service::handle_log_toggle(ctx, msg, args, "voice", "VoiceLog").await; let Some(guild_id) = msg.guild_id else {
return;
};
let Some(action) = args.first().map(|s| s.to_lowercase()) else {
let embed = CreateEmbed::new()
.title("VoiceLog")
.description("Usage: +voicelog <on [salon]|off>")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
};
match action.as_str() {
"on" => {
let channel = parse_target_channel(msg, args, 1);
set_log_channel(ctx, guild_id, "voice", channel, true).await;
let embed = CreateEmbed::new()
.title("VoiceLog")
.description(format!(
"Active dans {}.",
channel
.map(|c| format!("<#{}>", c.get()))
.unwrap_or_else(|| "ce salon".to_string())
))
.color(theme_color(ctx).await);
send_embed(ctx, msg, embed).await;
}
"off" => {
set_log_channel(ctx, guild_id, "voice", None, false).await;
let embed = CreateEmbed::new()
.title("VoiceLog")
.description("Desactive.")
.color(theme_color(ctx).await);
send_embed(ctx, msg, embed).await;
}
_ => {
let embed = CreateEmbed::new()
.title("VoiceLog")
.description("Usage: +voicelog <on [salon]|off>")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
}
}
} }
pub struct VoicelogCommand; pub struct VoicelogCommand;
@@ -12,14 +58,12 @@ pub static COMMAND_DESCRIPTOR: VoicelogCommand = VoicelogCommand;
impl crate::commands::command_contract::CommandSpec for VoicelogCommand { impl crate::commands::command_contract::CommandSpec for VoicelogCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "voicelog", name: "voicelog",
command: "voicelog",
category: "admin", category: "admin",
params: "<on [salon]|off>", params: "<on [salon]|off>",
summary: "Active les logs vocaux", summary: "Active les logs vocaux",
description: "Active ou desactive les logs de l activite vocale.", description: "Active ou desactive les logs de l activite vocale.",
examples: &["+voicelog on #logs", "+voicelog off"], examples: &["+voicelog on #logs", "+voicelog off"],
alias_source_key: "voicelog",
default_aliases: &["vlog"], default_aliases: &["vlog"],
default_permission: 8, default_permission: 8,
} }
+55 -5
View File
@@ -1,10 +1,62 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::advanced_tools; use crate::commands::common::{parse_channel_id, send_embed, theme_color};
pub async fn handle_voicemove(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_voicemove(ctx: &Context, msg: &Message, args: &[&str]) {
advanced_tools::handle_voicemove(ctx, msg, args).await; let Some(guild_id) = msg.guild_id else {
return;
};
if args.len() < 2 {
return;
}
let Some(from_channel) = parse_channel_id(args[0]) else {
return;
};
let Some(to_channel) = parse_channel_id(args[1]) 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(from_channel) {
Some(*uid)
} else {
None
}
})
.collect::<Vec<_>>()
};
let mut moved = 0usize;
for user_id in user_ids {
if guild_id
.move_member(&ctx.http, user_id, to_channel)
.await
.is_ok()
{
moved += 1;
}
}
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("VoiceMove")
.description(format!("{} membres déplacés.", moved))
.color(theme_color(ctx).await),
)
.await;
} }
pub struct VoiceMoveCommand; pub struct VoiceMoveCommand;
@@ -13,14 +65,12 @@ pub static COMMAND_DESCRIPTOR: VoiceMoveCommand = VoiceMoveCommand;
impl crate::commands::command_contract::CommandSpec for VoiceMoveCommand { impl crate::commands::command_contract::CommandSpec for VoiceMoveCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "voicemove", name: "voicemove",
command: "voicemove",
category: "admin", category: "admin",
params: "<salon_source> <salon_destination>", params: "<salon_source> <salon_destination>",
summary: "Deplace les membres vocaux", summary: "Deplace les membres vocaux",
description: "Deplace tous les membres d'un salon vocal vers un autre salon.", description: "Deplace tous les membres d'un salon vocal vers un autre salon.",
examples: &["+voicemove #General #Event"], examples: &["+voicemove #General #Event"],
alias_source_key: "voicemove",
default_aliases: &["vmove", "vmoveall"], default_aliases: &["vmove", "vmoveall"],
default_permission: 8, default_permission: 8,
} }
+47 -5
View File
@@ -1,22 +1,64 @@
use crate::commands::moderation_tools; use serenity::builder::CreateEmbed;
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::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]) { pub async fn handle_warn(ctx: &Context, msg: &Message, args: &[&str]) {
moderation_tools::handle_warn(ctx, msg, args).await; 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 struct WarnCommand;
pub static COMMAND_DESCRIPTOR: WarnCommand = WarnCommand; pub static COMMAND_DESCRIPTOR: WarnCommand = WarnCommand;
impl crate::commands::command_contract::CommandSpec for WarnCommand { impl crate::commands::command_contract::CommandSpec for WarnCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "warn", name: "warn",
command: "warn",
category: "admin", category: "admin",
params: "<@membre/ID[,..]> [raison]", params: "<@membre/ID[,..]> [raison]",
summary: "Donne un warn", summary: "Donne un warn",
description: "Ajoute un warn a un ou plusieurs membres.", description: "Ajoute un warn a un ou plusieurs membres.",
examples: &["+warn @User spam"], examples: &["+warn @User spam"],
alias_source_key: "warn",
default_aliases: &["avert"], default_aliases: &["avert"],
default_permission: 8, default_permission: 8,
} }
File diff suppressed because it is too large Load Diff
-148
View File
@@ -1,11 +1,3 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::activity::{RotatingActivityKind, parse_status, start_rotation, stop_rotation};
use crate::commands::common::send_embed;
use crate::db::{DbPoolKey, set_bot_status};
pub fn parse_color(value: &str) -> Option<u32> { pub fn parse_color(value: &str) -> Option<u32> {
let v = value.trim().to_lowercase(); let v = value.trim().to_lowercase();
match v.as_str() { match v.as_str() {
@@ -24,143 +16,3 @@ pub fn parse_color(value: &str) -> Option<u32> {
} }
} }
} }
pub async fn save_status_if_db(ctx: &Context, status: &str) {
let bot_id = ctx.cache.current_user().id;
let pool = {
let data = ctx.data.read().await;
data.get::<DbPoolKey>().cloned()
};
if let Some(pool) = pool {
let _ = set_bot_status(&pool, bot_id, status).await;
}
}
pub async fn handle_activity(ctx: &Context, msg: &Message, command: &str, args: &[&str]) {
if args.is_empty() {
let embed = CreateEmbed::new()
.title("Erreur")
.description("Usage: `+playto|+listen|+watch|+compet|+stream <message>`")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
}
let Some(kind) = RotatingActivityKind::from_command(command) else {
return;
};
let joined = args.join(" ");
let messages: Vec<String> = joined
.split(",,")
.map(|s| s.trim())
.filter(|s| !s.is_empty())
.map(|s| s.to_string())
.collect();
if messages.is_empty() {
let embed = CreateEmbed::new()
.title("Erreur")
.description("Aucun message d'activité valide.")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
}
let bot_id = ctx.cache.current_user().id;
let status = {
let pool = {
let data = ctx.data.read().await;
data.get::<DbPoolKey>().cloned()
};
if let Some(pool) = pool {
if let Ok(Some(saved)) = crate::db::get_bot_status(&pool, bot_id).await {
parse_status(&saved)
} else {
OnlineStatus::Online
}
} else {
OnlineStatus::Online
}
};
start_rotation(ctx, kind, messages.clone(), status).await;
let pool = {
let data = ctx.data.read().await;
data.get::<DbPoolKey>().cloned()
};
if let Some(pool) = pool {
let _ =
crate::db::set_bot_activity(&pool, bot_id, kind.as_db(), &messages.join("\n")).await;
}
let embed = CreateEmbed::new()
.title("Activité mise à jour")
.description(format!("{} message(s) configuré(s).", messages.len()))
.field(
"Rotation",
"Les textes alternent toutes les 30 secondes.",
false,
)
.color(0x57F287);
send_embed(ctx, msg, embed).await;
}
pub async fn handle_remove_activity(ctx: &Context, msg: &Message) {
stop_rotation(ctx).await;
ctx.set_activity(None);
let bot_id = ctx.cache.current_user().id;
let pool = {
let data = ctx.data.read().await;
data.get::<DbPoolKey>().cloned()
};
if let Some(pool) = pool {
let _ = crate::db::clear_bot_activity(&pool, bot_id).await;
}
let embed = CreateEmbed::new()
.title("Activité supprimée")
.description("L'activité du bot a été retirée.")
.color(0x57F287);
send_embed(ctx, msg, embed).await;
}
pub async fn handle_status(ctx: &Context, msg: &Message, command: &str) {
let status_name = match command {
"+online" => {
ctx.online();
"online"
}
"+idle" => {
ctx.idle();
"idle"
}
"+dnd" => {
ctx.dnd();
"dnd"
}
"+invisible" => {
ctx.invisible();
"invisible"
}
_ => return,
};
save_status_if_db(ctx, status_name).await;
let embed = CreateEmbed::new()
.title("Statut mis à jour")
.description(format!("Nouveau statut: {}", status_name))
.color(0x57F287);
send_embed(ctx, msg, embed).await;
}
+1 -3
View File
@@ -1,14 +1,12 @@
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct CommandMetadata { pub struct CommandMetadata {
pub key: &'static str, pub name: &'static str,
pub command: &'static str,
pub category: &'static str, pub category: &'static str,
pub default_permission: u8, pub default_permission: u8,
pub params: &'static str, pub params: &'static str,
pub summary: &'static str, pub summary: &'static str,
pub description: &'static str, pub description: &'static str,
pub examples: &'static [&'static str], pub examples: &'static [&'static str],
pub alias_source_key: &'static str,
pub default_aliases: &'static [&'static str], pub default_aliases: &'static [&'static str],
} }
+1 -3
View File
@@ -136,14 +136,12 @@ pub static COMMAND_DESCRIPTOR: AlladminsCommand = AlladminsCommand;
impl crate::commands::command_contract::CommandSpec for AlladminsCommand { impl crate::commands::command_contract::CommandSpec for AlladminsCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "alladmins", name: "alladmins",
command: "alladmins",
category: "general", category: "general",
params: "aucun", params: "aucun",
summary: "Liste les administrateurs du serveur", summary: "Liste les administrateurs du serveur",
description: "Affiche les membres qui possedent des droits administrateur sur le serveur.", description: "Affiche les membres qui possedent des droits administrateur sur le serveur.",
examples: &["+alladmins", "+as", "+help alladmins"], examples: &["+alladmins", "+as", "+help alladmins"],
alias_source_key: "alladmins",
default_aliases: &["aad"], default_aliases: &["aad"],
default_permission: 0, default_permission: 0,
} }
+1 -3
View File
@@ -119,14 +119,12 @@ pub static COMMAND_DESCRIPTOR: AllbotsCommand = AllbotsCommand;
impl crate::commands::command_contract::CommandSpec for AllbotsCommand { impl crate::commands::command_contract::CommandSpec for AllbotsCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "allbots", name: "allbots",
command: "allbots",
category: "general", category: "general",
params: "aucun", params: "aucun",
summary: "Liste tous les bots du serveur", summary: "Liste tous les bots du serveur",
description: "Affiche la liste des membres bots presents sur le serveur courant.", description: "Affiche la liste des membres bots presents sur le serveur courant.",
examples: &["+allbots", "+as", "+help allbots"], examples: &["+allbots", "+as", "+help allbots"],
alias_source_key: "allbots",
default_aliases: &["abt"], default_aliases: &["abt"],
default_permission: 0, default_permission: 0,
} }
+1 -3
View File
@@ -64,14 +64,12 @@ pub static COMMAND_DESCRIPTOR: BannerCommand = BannerCommand;
impl crate::commands::command_contract::CommandSpec for BannerCommand { impl crate::commands::command_contract::CommandSpec for BannerCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "banner", name: "banner",
command: "banner",
category: "general", category: "general",
params: "<@membre/ID>", params: "<@membre/ID>",
summary: "Affiche la banniere utilisateur", summary: "Affiche la banniere utilisateur",
description: "Affiche la banniere de profil dun utilisateur cible ou de lauteur.", description: "Affiche la banniere de profil dun utilisateur cible ou de lauteur.",
examples: &["+banner", "+br", "+help banner"], examples: &["+banner", "+br", "+help banner"],
alias_source_key: "banner",
default_aliases: &["bnr"], default_aliases: &["bnr"],
default_permission: 0, default_permission: 0,
} }
+1 -3
View File
@@ -131,14 +131,12 @@ pub static COMMAND_DESCRIPTOR: BoostersCommand = BoostersCommand;
impl crate::commands::command_contract::CommandSpec for BoostersCommand { impl crate::commands::command_contract::CommandSpec for BoostersCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "boosters", name: "boosters",
command: "boosters",
category: "general", category: "general",
params: "aucun", params: "aucun",
summary: "Liste les boosters du serveur", summary: "Liste les boosters du serveur",
description: "Affiche les membres qui boostent actuellement le serveur.", description: "Affiche les membres qui boostent actuellement le serveur.",
examples: &["+boosters", "+bs", "+help boosters"], examples: &["+boosters", "+bs", "+help boosters"],
alias_source_key: "boosters",
default_aliases: &["bst"], default_aliases: &["bst"],
default_permission: 0, default_permission: 0,
} }
+1 -3
View File
@@ -135,14 +135,12 @@ pub static COMMAND_DESCRIPTOR: BotadminsCommand = BotadminsCommand;
impl crate::commands::command_contract::CommandSpec for BotadminsCommand { impl crate::commands::command_contract::CommandSpec for BotadminsCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "botadmins", name: "botadmins",
command: "botadmins",
category: "general", category: "general",
params: "aucun", params: "aucun",
summary: "Liste les admins du bot", summary: "Liste les admins du bot",
description: "Affiche les utilisateurs ayant des droits admin sur le bot.", description: "Affiche les utilisateurs ayant des droits admin sur le bot.",
examples: &["+botadmins", "+bs", "+help botadmins"], examples: &["+botadmins", "+bs", "+help botadmins"],
alias_source_key: "botadmins",
default_aliases: &["bad"], default_aliases: &["bad"],
default_permission: 0, default_permission: 0,
} }
+1 -3
View File
@@ -94,14 +94,12 @@ pub static COMMAND_DESCRIPTOR: CalcCommand = CalcCommand;
impl crate::commands::command_contract::CommandSpec for CalcCommand { impl crate::commands::command_contract::CommandSpec for CalcCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "calc", name: "calc",
command: "calc",
category: "general", category: "general",
params: "<expression>", params: "<expression>",
summary: "Calcule une expression", summary: "Calcule une expression",
description: "Evalue une expression numerique simple et renvoie le resultat.", description: "Evalue une expression numerique simple et renvoie le resultat.",
examples: &["+calc", "+cc", "+help calc"], examples: &["+calc", "+cc", "+help calc"],
alias_source_key: "calc",
default_aliases: &["clc"], default_aliases: &["clc"],
default_permission: 0, default_permission: 0,
} }
+1 -3
View File
@@ -98,14 +98,12 @@ pub static COMMAND_DESCRIPTOR: ChannelCommand = ChannelCommand;
impl crate::commands::command_contract::CommandSpec for ChannelCommand { impl crate::commands::command_contract::CommandSpec for ChannelCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "channel", name: "channel",
command: "channel",
category: "general", category: "general",
params: "<#salon/ID>", params: "<#salon/ID>",
summary: "Affiche les details dun salon", summary: "Affiche les details dun salon",
description: "Affiche les informations utiles dun salon texte ou vocal cible.", description: "Affiche les informations utiles dun salon texte ou vocal cible.",
examples: &["+channel", "+cl", "+help channel"], examples: &["+channel", "+cl", "+help channel"],
alias_source_key: "channel",
default_aliases: &["chl"], default_aliases: &["chl"],
default_permission: 0, default_permission: 0,
} }
+63 -5
View File
@@ -1,10 +1,70 @@
use rand::seq::SliceRandom;
use serenity::builder::{CreateActionRow, CreateButton, CreateEmbed, CreateMessage};
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use crate::commands::advanced_tools; use crate::commands::common::{send_embed, theme_color};
pub async fn handle_choose(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_choose(ctx: &Context, msg: &Message, args: &[&str]) {
advanced_tools::handle_choose(ctx, msg, args).await; if args.is_empty() {
let embed = CreateEmbed::new()
.title("Choose")
.description("Ouvre un modal pour saisir les options (séparées par `|`).")
.color(theme_color(ctx).await);
let components = vec![CreateActionRow::Buttons(vec![
CreateButton::new(format!("adv:choose:modal:{}", msg.author.id.get()))
.label("Saisir les options")
.style(serenity::all::ButtonStyle::Primary),
])];
let _ = msg
.channel_id
.send_message(
&ctx.http,
CreateMessage::new().embed(embed).components(components),
)
.await;
return;
}
let merged = args.join(" ");
let mut options = merged
.split('|')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect::<Vec<_>>();
if options.len() < 2 {
options = args.iter().map(|s| (*s).to_string()).collect();
}
if options.len() < 2 {
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Choose")
.description("Donne au moins 2 options.")
.color(0xED4245),
)
.await;
return;
}
let pick = options
.choose(&mut rand::thread_rng())
.cloned()
.unwrap_or_else(|| options[0].clone());
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("Tirage")
.description(format!("Résultat: **{}**", pick))
.color(theme_color(ctx).await),
)
.await;
} }
pub struct ChooseCommand; pub struct ChooseCommand;
@@ -13,14 +73,12 @@ pub static COMMAND_DESCRIPTOR: ChooseCommand = ChooseCommand;
impl crate::commands::command_contract::CommandSpec for ChooseCommand { impl crate::commands::command_contract::CommandSpec for ChooseCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "choose", name: "choose",
command: "choose",
category: "general", category: "general",
params: "<option1 | option2 | ...>", params: "<option1 | option2 | ...>",
summary: "Tire une option au hasard", summary: "Tire une option au hasard",
description: "Lance un tirage au sort instantane parmi les options donnees.", description: "Lance un tirage au sort instantane parmi les options donnees.",
examples: &["+choose rouge | bleu | vert"], examples: &["+choose rouge | bleu | vert"],
alias_source_key: "choose",
default_aliases: &["pick", "random"], default_aliases: &["pick", "random"],
default_permission: 8, default_permission: 8,
} }
+1 -3
View File
@@ -83,14 +83,12 @@ pub static COMMAND_DESCRIPTOR: EmojiCommand = EmojiCommand;
impl crate::commands::command_contract::CommandSpec for EmojiCommand { impl crate::commands::command_contract::CommandSpec for EmojiCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "emoji", name: "emoji",
command: "emoji",
category: "general", category: "general",
params: "<emoji>", params: "<emoji>",
summary: "Affiche les infos dun emoji", summary: "Affiche les infos dun emoji",
description: "Affiche les details dun emoji fourni.", description: "Affiche les details dun emoji fourni.",
examples: &["+emoji", "+ei", "+help emoji"], examples: &["+emoji", "+ei", "+help emoji"],
alias_source_key: "emoji",
default_aliases: &["emj"], default_aliases: &["emj"],
default_permission: 0, default_permission: 0,
} }
+25 -17
View File
@@ -121,7 +121,7 @@ const HELP_PAGES: &[HelpPage] = &[
fn help_page_for_command( fn help_page_for_command(
meta: &crate::commands::command_contract::CommandMetadata, meta: &crate::commands::command_contract::CommandMetadata,
) -> &'static str { ) -> &'static str {
match meta.key { match meta.name {
"modlog" | "messagelog" | "voicelog" | "boostlog" | "rolelog" | "raidlog" "modlog" | "messagelog" | "voicelog" | "boostlog" | "rolelog" | "raidlog"
| "autoconfiglog" | "nolog" | "join" | "boostembed" | "set_modlogs" | "set_boostembed" | "autoconfiglog" | "nolog" | "join" | "boostembed" | "set_modlogs" | "set_boostembed"
| "leave_settings" | "viewlogs" => "logs", | "leave_settings" | "viewlogs" => "logs",
@@ -195,11 +195,14 @@ fn help_metadata_lookup_key(input: &str) -> Option<&'static str> {
crate::commands::all_command_metadata() crate::commands::all_command_metadata()
.into_iter() .into_iter()
.find(|meta| { .find(|meta| {
meta.key.eq_ignore_ascii_case(&normalized) meta.name.eq_ignore_ascii_case(&normalized)
|| meta.key.eq_ignore_ascii_case(&underscored) || meta.name.eq_ignore_ascii_case(&underscored)
|| meta.command.eq_ignore_ascii_case(&normalized) || meta
.name
.replace('_', " ")
.eq_ignore_ascii_case(&normalized)
}) })
.map(|meta| meta.key) .map(|meta| meta.name)
} }
fn help_page_matches_input(page: &HelpPage, input: &str) -> bool { fn help_page_matches_input(page: &HelpPage, input: &str) -> bool {
@@ -277,7 +280,7 @@ async fn aliases_map(ctx: &Context) -> BTreeMap<String, Vec<String>> {
for meta in crate::commands::all_command_metadata() { for meta in crate::commands::all_command_metadata() {
if !meta.default_aliases.is_empty() { if !meta.default_aliases.is_empty() {
out.entry(meta.alias_source_key.to_string()) out.entry(meta.name.to_string())
.or_default() .or_default()
.extend(meta.default_aliases.iter().map(|alias| alias.to_string())); .extend(meta.default_aliases.iter().map(|alias| alias.to_string()));
} }
@@ -310,8 +313,8 @@ fn command_doc(key: &str) -> Option<CommandDoc> {
}; };
Some(CommandDoc { Some(CommandDoc {
key: meta.key, key: meta.name,
command: meta.command, command: meta.name,
default_permission: meta.default_permission, default_permission: meta.default_permission,
params: meta.params, params: meta.params,
summary: meta.summary, summary: meta.summary,
@@ -321,7 +324,7 @@ fn command_doc(key: &str) -> Option<CommandDoc> {
} else { } else {
meta.examples meta.examples
}, },
alias_source_key: Some(meta.alias_source_key), alias_source_key: Some(meta.name),
}) })
} }
@@ -467,14 +470,14 @@ fn help_page_content(
.into_iter() .into_iter()
.filter(|meta| help_page_for_command(meta).eq_ignore_ascii_case(page.key)) .filter(|meta| help_page_for_command(meta).eq_ignore_ascii_case(page.key))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
commands.sort_by(|a, b| a.command.to_lowercase().cmp(&b.command.to_lowercase())); commands.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
let mut lines = Vec::with_capacity(commands.len()); let mut lines = Vec::with_capacity(commands.len());
for meta in commands { for meta in commands {
let label = meta.command; let label = meta.name.replace('_', " ");
let summary = meta.summary; let summary = meta.summary;
let alias_key = meta.alias_source_key; let alias_key = meta.name;
let permission = if perms_enabled { let permission = if perms_enabled {
format!(" {}", format_permission_level(meta.default_permission)) format!(" {}", format_permission_level(meta.default_permission))
} else { } else {
@@ -745,9 +748,16 @@ pub async fn handle_help(ctx: &Context, msg: &Message, args: &[&str]) {
.join("\n"); .join("\n");
let mut embed = CreateEmbed::new() let mut embed = CreateEmbed::new()
.title(format!("Aide commande · +{}", doc.command)) .title(format!(
"Aide commande · +{}",
doc.command.replace('_', " ")
))
.description(doc.description) .description(doc.description)
.field("Commande", format!("`+{}`", doc.command), false) .field(
"Commande",
format!("`+{}`", doc.command.replace('_', " ")),
false,
)
.field("Clé ACL", format!("`{}`", doc.key), false) .field("Clé ACL", format!("`{}`", doc.key), false)
.field("Catégorie", help_page_title_for_command_key(doc.key), false) .field("Catégorie", help_page_title_for_command_key(doc.key), false)
.field("Alias", alias_text, false) .field("Alias", alias_text, false)
@@ -797,14 +807,12 @@ pub static COMMAND_DESCRIPTOR: HelpCommand = HelpCommand;
impl crate::commands::command_contract::CommandSpec for HelpCommand { impl crate::commands::command_contract::CommandSpec for HelpCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "help", name: "help",
command: "help",
category: "general", category: "general",
params: "[commande|page]", params: "[commande|page]",
summary: "Affiche laide des commandes", summary: "Affiche laide des commandes",
description: "Affiche les pages daide du bot ou la fiche detaillee dune commande avec parametres, aliases et exemples.", description: "Affiche les pages daide du bot ou la fiche detaillee dune commande avec parametres, aliases et exemples.",
examples: &["+help", "+hp", "+help help"], examples: &["+help", "+hp", "+help help"],
alias_source_key: "help",
default_aliases: &["hp"], default_aliases: &["hp"],
default_permission: 0, default_permission: 0,
} }
+89 -5
View File
@@ -1,10 +1,96 @@
use serenity::builder::{CreateActionRow, CreateButton, CreateEmbed, CreateMessage, EditMessage};
use serenity::model::prelude::*; use serenity::model::prelude::*;
use serenity::prelude::*; use serenity::prelude::*;
use std::time::Duration;
use crate::commands::advanced_tools; use crate::commands::common::theme_color;
fn duration_from_input(input: &str) -> Option<Duration> {
let raw = input.trim().to_lowercase();
if raw.is_empty() {
return None;
}
let mut number = String::new();
let mut suffix = String::new();
for ch in raw.chars() {
if ch.is_ascii_digit() {
if !suffix.is_empty() {
return None;
}
number.push(ch);
} else if !ch.is_whitespace() {
suffix.push(ch);
}
}
let value = number.parse::<u64>().ok()?;
let secs = match suffix.as_str() {
"s" | "sec" | "secs" | "seconde" | "secondes" => value,
"m" | "min" | "mins" | "minute" | "minutes" => value * 60,
"h" | "heure" | "heures" => value * 3600,
"j" | "d" | "jour" | "jours" => value * 86400,
_ => return None,
};
Some(Duration::from_secs(secs.max(1)))
}
pub async fn handle_loading(ctx: &Context, msg: &Message, args: &[&str]) { pub async fn handle_loading(ctx: &Context, msg: &Message, args: &[&str]) {
advanced_tools::handle_loading(ctx, msg, args).await; if args.len() < 2 {
let embed = CreateEmbed::new()
.title("Loading")
.description("Ouvre un modal pour saisir la durée et le message.")
.color(theme_color(ctx).await);
let components = vec![CreateActionRow::Buttons(vec![
CreateButton::new(format!("adv:loading:modal:{}", msg.author.id.get()))
.label("Configurer")
.style(serenity::all::ButtonStyle::Primary),
])];
let _ = msg
.channel_id
.send_message(
&ctx.http,
CreateMessage::new().embed(embed).components(components),
)
.await;
return;
}
let Some(duration) = duration_from_input(args[0]) else {
return;
};
let total_secs = duration.as_secs().clamp(1, 120);
let text = args[1..].join(" ");
let mut sent = match msg
.channel_id
.send_message(&ctx.http, CreateMessage::new().content("[----------] 0%"))
.await
{
Ok(m) => m,
Err(_) => return,
};
for i in 0..=10_u64 {
let done = "#".repeat(i as usize);
let todo = "-".repeat((10 - i) as usize);
let percent = i * 10;
let _ = sent
.edit(
&ctx.http,
EditMessage::new().content(format!("{} [{}{}] {}%", text, done, todo, percent)),
)
.await;
if i < 10 {
tokio::time::sleep(Duration::from_secs((total_secs / 10).max(1))).await;
}
}
} }
pub struct LoadingCommand; pub struct LoadingCommand;
@@ -13,14 +99,12 @@ pub static COMMAND_DESCRIPTOR: LoadingCommand = LoadingCommand;
impl crate::commands::command_contract::CommandSpec for LoadingCommand { impl crate::commands::command_contract::CommandSpec for LoadingCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "loading", name: "loading",
command: "loading",
category: "general", category: "general",
params: "<duree> <message>", params: "<duree> <message>",
summary: "Affiche une barre de chargement", summary: "Affiche une barre de chargement",
description: "Anime une barre de progression avec la duree et le texte fournis.", description: "Anime une barre de progression avec la duree et le texte fournis.",
examples: &["+loading 10s Traitement en cours"], examples: &["+loading 10s Traitement en cours"],
alias_source_key: "loading",
default_aliases: &["loadbar", "bar"], default_aliases: &["loadbar", "bar"],
default_permission: 8, default_permission: 8,
} }
+1 -3
View File
@@ -77,14 +77,12 @@ pub static COMMAND_DESCRIPTOR: MemberCommand = MemberCommand;
impl crate::commands::command_contract::CommandSpec for MemberCommand { impl crate::commands::command_contract::CommandSpec for MemberCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "member", name: "member",
command: "member",
category: "general", category: "general",
params: "<@membre/ID>", params: "<@membre/ID>",
summary: "Affiche le profil membre", summary: "Affiche le profil membre",
description: "Affiche les informations dun membre dans le serveur courant.", description: "Affiche les informations dun membre dans le serveur courant.",
examples: &["+member", "+mr", "+help member"], examples: &["+member", "+mr", "+help member"],
alias_source_key: "member",
default_aliases: &["mbr"], default_aliases: &["mbr"],
default_permission: 0, default_permission: 0,
} }
+1 -3
View File
@@ -64,14 +64,12 @@ pub static COMMAND_DESCRIPTOR: PicCommand = PicCommand;
impl crate::commands::command_contract::CommandSpec for PicCommand { impl crate::commands::command_contract::CommandSpec for PicCommand {
fn metadata(&self) -> crate::commands::command_contract::CommandMetadata { fn metadata(&self) -> crate::commands::command_contract::CommandMetadata {
crate::commands::command_contract::CommandMetadata { crate::commands::command_contract::CommandMetadata {
key: "pic", name: "pic",
command: "pic",
category: "general", category: "general",
params: "<@membre/ID>", params: "<@membre/ID>",
summary: "Affiche la photo de profil", summary: "Affiche la photo de profil",
description: "Affiche la photo de profil dun utilisateur cible ou de lauteur.", description: "Affiche la photo de profil dun utilisateur cible ou de lauteur.",
examples: &["+pic", "+pc", "+help pic"], examples: &["+pic", "+pc", "+help pic"],
alias_source_key: "pic",
default_aliases: &["pfp"], default_aliases: &["pfp"],
default_permission: 0, default_permission: 0,
} }

Some files were not shown because too many files have changed in this diff Show More