Refactor code for improved readability and consistency across various command files

- Adjusted formatting and indentation in `rename.rs`, `suggestion.rs`, `tempvoc.rs`, `ticket.rs`, `ticket_member.rs`, and `tickets.rs` for better clarity.
- Consolidated `if` statements and method calls for cleaner code in `suggestion.rs`, `tempvoc.rs`, and `ticket.rs`.
- Updated the `LOGS_PER_PAGE` constant in `viewlogs.rs` to increase the number of logs displayed per page.
- Removed unused `handle_boostembed` function from `logs_service.rs`.
- Added new modules in `mod.rs` for better organization of command files.
- Enhanced the `handle_show_pics` function in `showpics.rs` for improved member filtering.
- Updated the `handle_message` function in `message_event.rs` to streamline command handling.
This commit is contained in:
Puechberty Arthur
2026-04-10 03:48:38 +02:00
parent 572cfa17b2
commit bc623a7736
16 changed files with 831 additions and 210 deletions
+635 -6
View File
@@ -1,9 +1,638 @@
use crate::commands::logs_service;
use serenity::builder::{
CreateActionRow, CreateButton, CreateEmbed, CreateInputText, CreateInteractionResponse,
CreateInteractionResponseMessage, CreateMessage, CreateModal,
};
use serenity::model::application::{
ActionRowComponent, ButtonStyle, ComponentInteraction, InputTextStyle, ModalInteraction,
};
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::common::{parse_channel_id, send_embed, theme_color};
use crate::commands::logs_service;
use crate::db::DbPoolKey;
const BOOSTEMBED_MENU: &str = "boostembed:settings";
#[derive(Clone)]
struct BoostEmbedSettings {
enabled: bool,
title: Option<String>,
description: Option<String>,
color: Option<i32>,
boost_channel_id: Option<i64>,
boost_channel_enabled: bool,
}
fn default_settings() -> BoostEmbedSettings {
BoostEmbedSettings {
enabled: true,
title: None,
description: None,
color: None,
boost_channel_id: None,
boost_channel_enabled: false,
}
}
fn parse_owner_id(custom_id: &str) -> Option<(String, u64)> {
let mut parts = custom_id.rsplitn(2, ':');
let owner = parts.next()?.parse::<u64>().ok()?;
let action = parts.next()?.to_string();
Some((action, owner))
}
fn modal_value(modal: &ModalInteraction, wanted_id: &str) -> Option<String> {
for row in &modal.data.components {
for component in &row.components {
if let ActionRowComponent::InputText(input) = component {
if input.custom_id == wanted_id {
return input.value.clone();
}
}
}
}
None
}
async fn pool(ctx: &Context) -> Option<sqlx::PgPool> {
let data = ctx.data.read().await;
data.get::<DbPoolKey>().cloned()
}
async fn ensure_boost_embed_row(pool: &sqlx::PgPool, bot_id: UserId, guild_id: GuildId) {
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;
}
async fn set_boost_embed_enabled(
pool: &sqlx::PgPool,
bot_id: UserId,
guild_id: GuildId,
enabled: bool,
) {
let _ = sqlx::query(
r#"
INSERT INTO bot_boost_embed (bot_id, guild_id, enabled)
VALUES ($1, $2, $3)
ON CONFLICT (bot_id, guild_id)
DO UPDATE SET enabled = EXCLUDED.enabled, updated_at = NOW();
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.bind(enabled)
.execute(pool)
.await;
}
async fn set_boost_log_channel(
pool: &sqlx::PgPool,
bot_id: UserId,
guild_id: GuildId,
channel_id: Option<ChannelId>,
enabled: bool,
) {
let _ = sqlx::query(
r#"
INSERT INTO bot_log_channels (bot_id, guild_id, log_type, channel_id, enabled)
VALUES ($1, $2, 'boost', $3, $4)
ON CONFLICT (bot_id, guild_id, log_type)
DO UPDATE SET channel_id = EXCLUDED.channel_id, enabled = EXCLUDED.enabled, updated_at = NOW();
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.bind(channel_id.map(|c| c.get() as i64))
.bind(enabled)
.execute(pool)
.await;
}
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>)>(
r#"
SELECT enabled, title, description, color
FROM bot_boost_embed
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 channel_row = sqlx::query_as::<_, (Option<i64>, bool)>(
r#"
SELECT channel_id, enabled
FROM bot_log_channels
WHERE bot_id = $1 AND guild_id = $2 AND log_type = 'boost'
LIMIT 1;
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.fetch_optional(pool)
.await
.ok()
.flatten();
let mut settings = default_settings();
if let Some((enabled, title, description, color)) = row {
settings.enabled = enabled;
settings.title = title;
settings.description = description;
settings.color = color;
}
if let Some((channel_id, enabled)) = channel_row {
settings.boost_channel_id = channel_id;
settings.boost_channel_enabled = enabled;
}
settings
}
fn settings_embed(settings: &BoostEmbedSettings) -> CreateEmbed {
let channel_text = if settings.boost_channel_enabled {
settings
.boost_channel_id
.map(|id| format!("<#{}>", id))
.unwrap_or_else(|| "activé mais salon non défini".to_string())
} else {
"désactivé".to_string()
};
CreateEmbed::new()
.title("Configuration Boost Embed")
.description("Utilise les boutons/modals ci-dessous pour paramétrer l'embed de boost et son salon d'envoi.")
.field("Embed", if settings.enabled { "on" } else { "off" }, true)
.field("Salon d'envoi boost", channel_text, true)
.field(
"Titre",
settings
.title
.clone()
.unwrap_or_else(|| "(défaut)".to_string()),
false,
)
.field(
"Description",
settings
.description
.clone()
.unwrap_or_else(|| "(défaut)".to_string()),
false,
)
.field(
"Couleur",
settings
.color
.map(|v| format!("#{:06X}", v.max(0) as u32))
.unwrap_or_else(|| "(défaut)".to_string()),
true,
)
.color(0xF47FFF)
}
fn settings_components(owner_id: UserId, settings: &BoostEmbedSettings) -> Vec<CreateActionRow> {
let toggle_style = if settings.enabled {
ButtonStyle::Danger
} else {
ButtonStyle::Success
};
vec![
CreateActionRow::Buttons(vec![
CreateButton::new(format!("{}:toggle:{}", BOOSTEMBED_MENU, owner_id.get()))
.label(if settings.enabled {
"Désactiver embed"
} else {
"Activer embed"
})
.style(toggle_style),
CreateButton::new(format!("{}:test:{}", BOOSTEMBED_MENU, owner_id.get()))
.label("Envoyer test")
.style(ButtonStyle::Primary),
CreateButton::new(format!("{}:refresh:{}", BOOSTEMBED_MENU, owner_id.get()))
.label("Rafraîchir")
.style(ButtonStyle::Secondary),
]),
CreateActionRow::Buttons(vec![
CreateButton::new(format!("{}:set_here:{}", BOOSTEMBED_MENU, owner_id.get()))
.label("Salon = ici")
.style(ButtonStyle::Success),
CreateButton::new(format!("{}:edit_channel:{}", BOOSTEMBED_MENU, owner_id.get()))
.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![
CreateButton::new(format!("{}:edit_title:{}", BOOSTEMBED_MENU, owner_id.get()))
.label("Modifier titre")
.style(ButtonStyle::Secondary),
CreateButton::new(format!("{}:edit_description:{}", BOOSTEMBED_MENU, owner_id.get()))
.label("Modifier description")
.style(ButtonStyle::Secondary),
CreateButton::new(format!("{}:edit_color:{}", BOOSTEMBED_MENU, owner_id.get()))
.label("Modifier couleur")
.style(ButtonStyle::Secondary),
]),
]
}
async fn show_panel(
ctx: &Context,
msg: &Message,
pool: &sqlx::PgPool,
bot_id: UserId,
guild_id: GuildId,
) {
ensure_boost_embed_row(pool, bot_id, guild_id).await;
let settings = read_settings(pool, bot_id, guild_id).await;
let _ = msg
.channel_id
.send_message(
&ctx.http,
CreateMessage::new()
.embed(settings_embed(&settings))
.components(settings_components(msg.author.id, &settings)),
)
.await;
}
pub async fn handle_boostembed(ctx: &Context, msg: &Message, args: &[&str]) {
logs_service::handle_boostembed(ctx, msg, args).await;
let Some(guild_id) = msg.guild_id else {
return;
};
let Some(pool) = pool(ctx).await else {
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("BoostEmbed")
.description("DB indisponible.")
.color(0xED4245),
)
.await;
return;
};
let bot_id = ctx.cache.current_user().id;
if let Some(action) = args.first().map(|v| v.to_lowercase()) {
match action.as_str() {
"on" | "off" => {
set_boost_embed_enabled(&pool, bot_id, guild_id, action == "on").await;
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("BoostEmbed")
.description(if action == "on" { "Activé." } else { "Désactivé." })
.color(theme_color(ctx).await),
)
.await;
return;
}
"test" => {
logs_service::send_boost_embed(ctx, guild_id, &msg.author).await;
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("BoostEmbed")
.description("Test envoyé.")
.color(theme_color(ctx).await),
)
.await;
return;
}
"settings" | "panel" => {
show_panel(ctx, msg, &pool, bot_id, guild_id).await;
return;
}
_ => {
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("BoostEmbed")
.description("Usage: +boostembed [on|off|test|settings]")
.color(0xED4245),
)
.await;
return;
}
}
}
show_panel(ctx, msg, &pool, bot_id, guild_id).await;
}
pub async fn handle_component_interaction(ctx: &Context, component: &ComponentInteraction) -> bool {
if !component.data.custom_id.starts_with(BOOSTEMBED_MENU) {
return false;
}
let Some((action, owner_id)) = parse_owner_id(&component.data.custom_id) else {
return false;
};
if component.user.id.get() != owner_id {
let _ = component
.create_response(
&ctx.http,
CreateInteractionResponse::Message(
CreateInteractionResponseMessage::new()
.content("Seul l'auteur du panneau peut l'utiliser.")
.ephemeral(true),
),
)
.await;
return true;
}
let Some(guild_id) = component.guild_id else {
return true;
};
let Some(pool) = pool(ctx).await else {
let _ = component
.create_response(
&ctx.http,
CreateInteractionResponse::Message(
CreateInteractionResponseMessage::new()
.content("DB indisponible.")
.ephemeral(true),
),
)
.await;
return true;
};
let bot_id = ctx.cache.current_user().id;
ensure_boost_embed_row(&pool, bot_id, guild_id).await;
if action.ends_with(":edit_title") {
let modal = CreateModal::new(
format!("{}:modal:title:{}", BOOSTEMBED_MENU, component.user.id.get()),
"Modifier le titre du boost embed",
)
.components(vec![CreateActionRow::InputText(
CreateInputText::new(InputTextStyle::Short, "Titre (vide = défaut)", "title")
.required(false),
)]);
let _ = component
.create_response(&ctx.http, CreateInteractionResponse::Modal(modal))
.await;
return true;
}
if action.ends_with(":edit_description") {
let modal = CreateModal::new(
format!(
"{}:modal:description:{}",
BOOSTEMBED_MENU,
component.user.id.get()
),
"Modifier la description du boost embed",
)
.components(vec![CreateActionRow::InputText(
CreateInputText::new(
InputTextStyle::Paragraph,
"Description (vide = défaut)",
"description",
)
.required(false),
)]);
let _ = component
.create_response(&ctx.http, CreateInteractionResponse::Modal(modal))
.await;
return true;
}
if action.ends_with(":edit_color") {
let modal = CreateModal::new(
format!("{}:modal:color:{}", BOOSTEMBED_MENU, component.user.id.get()),
"Modifier la couleur du boost embed",
)
.components(vec![CreateActionRow::InputText(
CreateInputText::new(InputTextStyle::Short, "Couleur hex (#FF66CC)", "color")
.required(false),
)]);
let _ = component
.create_response(&ctx.http, CreateInteractionResponse::Modal(modal))
.await;
return true;
}
if action.ends_with(":edit_channel") {
let modal = CreateModal::new(
format!("{}:modal:channel:{}", BOOSTEMBED_MENU, component.user.id.get()),
"Définir le salon boost",
)
.components(vec![CreateActionRow::InputText(
CreateInputText::new(
InputTextStyle::Short,
"Salon (#mention ou ID, vide = désactiver)",
"channel",
)
.required(false),
)]);
let _ = component
.create_response(&ctx.http, CreateInteractionResponse::Modal(modal))
.await;
return true;
}
if action.ends_with(":toggle") {
let settings = read_settings(&pool, bot_id, guild_id).await;
set_boost_embed_enabled(&pool, bot_id, guild_id, !settings.enabled).await;
} else if action.ends_with(":set_here") {
set_boost_log_channel(&pool, bot_id, guild_id, Some(component.channel_id), true).await;
} else if action.ends_with(":disable_channel") {
set_boost_log_channel(&pool, bot_id, guild_id, None, false).await;
} else if action.ends_with(":test") {
logs_service::send_boost_embed(ctx, guild_id, &component.user).await;
}
let settings = read_settings(&pool, bot_id, guild_id).await;
let _ = component
.create_response(
&ctx.http,
CreateInteractionResponse::UpdateMessage(
CreateInteractionResponseMessage::new()
.embed(settings_embed(&settings))
.components(settings_components(component.user.id, &settings)),
),
)
.await;
true
}
pub async fn handle_modal_interaction(ctx: &Context, modal: &ModalInteraction) -> bool {
if !modal.data.custom_id.starts_with(&format!("{}:modal:", BOOSTEMBED_MENU)) {
return false;
}
let Some((action, owner_id)) = parse_owner_id(&modal.data.custom_id) else {
return false;
};
if modal.user.id.get() != owner_id {
let _ = modal
.create_response(
&ctx.http,
CreateInteractionResponse::Message(
CreateInteractionResponseMessage::new()
.content("Seul l'auteur du panneau peut l'utiliser.")
.ephemeral(true),
),
)
.await;
return true;
}
let Some(guild_id) = modal.guild_id else {
return true;
};
let Some(pool) = pool(ctx).await else {
let _ = modal
.create_response(
&ctx.http,
CreateInteractionResponse::Message(
CreateInteractionResponseMessage::new()
.content("DB indisponible.")
.ephemeral(true),
),
)
.await;
return true;
};
let bot_id = ctx.cache.current_user().id;
ensure_boost_embed_row(&pool, bot_id, guild_id).await;
if action.ends_with(":modal:title") {
let title = modal_value(modal, "title").unwrap_or_default();
let title_value = if title.trim().is_empty() {
None
} else {
Some(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(title_value)
.execute(&pool)
.await;
} else if action.ends_with(":modal:description") {
let description = modal_value(modal, "description").unwrap_or_default();
let desc_value = if description.trim().is_empty() {
None
} else {
Some(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(desc_value)
.execute(&pool)
.await;
} else if action.ends_with(":modal:color") {
let raw = modal_value(modal, "color").unwrap_or_default();
let color_value = if raw.trim().is_empty() {
None
} else {
let normalized = raw.trim().trim_start_matches('#').trim_start_matches("0x");
match u32::from_str_radix(normalized, 16) {
Ok(value) => Some(value as i32),
Err(_) => {
let _ = modal
.create_response(
&ctx.http,
CreateInteractionResponse::Message(
CreateInteractionResponseMessage::new()
.content("Couleur invalide. Exemple: `#FF66CC`")
.ephemeral(true),
),
)
.await;
return true;
}
}
};
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_value)
.execute(&pool)
.await;
} else if action.ends_with(":modal:channel") {
let raw = modal_value(modal, "channel").unwrap_or_default();
if raw.trim().is_empty() {
set_boost_log_channel(&pool, bot_id, guild_id, None, false).await;
} else if let Some(channel) = parse_channel_id(&raw) {
set_boost_log_channel(&pool, bot_id, guild_id, Some(channel), true).await;
} else {
let _ = modal
.create_response(
&ctx.http,
CreateInteractionResponse::Message(
CreateInteractionResponseMessage::new()
.content("Salon invalide. Donne une mention `#salon` ou un ID.")
.ephemeral(true),
),
)
.await;
return true;
}
}
let _ = modal
.create_response(
&ctx.http,
CreateInteractionResponse::Message(
CreateInteractionResponseMessage::new()
.content("Configuration boost embed mise à jour. Clique sur `Rafraîchir` dans le panneau.")
.ephemeral(true),
),
)
.await;
true
}
pub struct BoostembedCommand;
@@ -15,10 +644,10 @@ impl crate::commands::command_contract::CommandSpec for BoostembedCommand {
key: "boostembed",
command: "boostembed",
category: "admin",
params: "<on|off|test>",
summary: "Active, coupe ou teste l embed boost",
description: "Controle l embed de boost et permet un test rapide.",
examples: &["+boostembed on", "+boostembed test"],
params: "[on|off|test|settings]",
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é.",
examples: &["+boostembed", "+boostembed settings", "+boostembed test"],
alias_source_key: "boostembed",
default_aliases: &["bembed"],
default_permission: 8,
+4 -1
View File
@@ -90,7 +90,10 @@ pub async fn handle_rename(ctx: &Context, msg: &Message, args: &[&str]) {
if msg
.channel_id
.edit(&ctx.http, serenity::builder::EditChannel::new().name(new_name.clone()))
.edit(
&ctx.http,
serenity::builder::EditChannel::new().name(new_name.clone()),
)
.await
.is_err()
{
+50 -24
View File
@@ -3,10 +3,10 @@ use serenity::builder::{
CreateActionRow, CreateButton, CreateEmbed, CreateInputText, CreateInteractionResponse,
CreateInteractionResponseMessage, CreateMessage, CreateModal,
};
use serenity::model::Colour;
use serenity::model::application::{
ActionRowComponent, ButtonStyle, ComponentInteraction, InputTextStyle, ModalInteraction,
};
use serenity::model::Colour;
use serenity::model::prelude::*;
use serenity::prelude::*;
@@ -40,9 +40,7 @@ fn suggestion_embed(author: &User, content: &str) -> CreateEmbed {
.title("💡 Suggestion")
.description(content)
.colour(Colour::from_rgb(255, 200, 0))
.author(
serenity::builder::CreateEmbedAuthor::new(&author.name).icon_url(author.face()),
)
.author(serenity::builder::CreateEmbedAuthor::new(&author.name).icon_url(author.face()))
.timestamp(Utc::now())
}
@@ -52,7 +50,11 @@ fn suggestion_settings_embed(settings: &db::SuggestionSettings) -> CreateEmbed {
.description("Configure le système de suggestions du serveur.")
.colour(Colour::from_rgb(255, 200, 0))
.timestamp(Utc::now())
.field("Statut", if settings.enabled { "Actif" } else { "Inactif" }, true);
.field(
"Statut",
if settings.enabled { "Actif" } else { "Inactif" },
true,
);
if let Some(channel_id) = settings.channel_id {
embed = embed.field("Canal", format!("<#{}>", channel_id), true);
@@ -65,8 +67,15 @@ fn suggestion_settings_embed(settings: &db::SuggestionSettings) -> CreateEmbed {
embed
}
fn suggestion_components(owner_id: UserId, settings: &db::SuggestionSettings) -> Vec<CreateActionRow> {
let toggle_label = if settings.enabled { "Désactiver" } else { "Activer" };
fn suggestion_components(
owner_id: UserId,
settings: &db::SuggestionSettings,
) -> Vec<CreateActionRow> {
let toggle_label = if settings.enabled {
"Désactiver"
} else {
"Activer"
};
vec![CreateActionRow::Buttons(vec![
CreateButton::new(format!("{}:submit:{}", SUGGESTION_MENU, owner_id.get()))
@@ -124,7 +133,9 @@ async fn submit_suggestion(
author: &User,
content: String,
) -> Result<(), String> {
let pool = pool(ctx).await.ok_or_else(|| "Base de données indisponible".to_string())?;
let pool = pool(ctx)
.await
.ok_or_else(|| "Base de données indisponible".to_string())?;
let bot_id = ctx.cache.current_user().id.get() as i64;
let settings = db::get_or_create_suggestion_settings(&pool, bot_id, guild_id.get() as i64)
.await
@@ -134,12 +145,16 @@ async fn submit_suggestion(
return Err("Le système de suggestions est désactivé.".to_string());
}
let channel_id = settings.channel_id.ok_or_else(|| "Canal de suggestions non configuré".to_string())?;
let channel_id = settings
.channel_id
.ok_or_else(|| "Canal de suggestions non configuré".to_string())?;
let channel = ChannelId::new(channel_id as u64)
.to_channel(&ctx.http)
.await
.map_err(|e| format!("Erreur: {e}"))?;
let guild_channel = channel.guild().ok_or_else(|| "Canal de suggestions introuvable".to_string())?;
let guild_channel = channel
.guild()
.ok_or_else(|| "Canal de suggestions introuvable".to_string())?;
let message = guild_channel
.send_message(
@@ -184,7 +199,11 @@ async fn submit_suggestion(
}
pub async fn handle_suggestion(ctx: &Context, msg: &Message, args: &[&str]) {
if args.first().map(|value| value.eq_ignore_ascii_case("settings")).unwrap_or(false) {
if args
.first()
.map(|value| value.eq_ignore_ascii_case("settings"))
.unwrap_or(false)
{
show_menu(ctx, msg).await;
return;
}
@@ -316,17 +335,24 @@ pub async fn handle_component_interaction(ctx: &Context, component: &ComponentIn
}
if action.ends_with(":configure") {
let modal = CreateModal::new(component.data.custom_id.clone(), "Configurer les suggestions")
.components(vec![
CreateActionRow::InputText(
CreateInputText::new(InputTextStyle::Short, "Canal des suggestions", "channel_id")
.required(false),
),
CreateActionRow::InputText(
CreateInputText::new(InputTextStyle::Short, "Canal d'approbation", "approve_channel_id")
.required(false),
),
]);
let modal = CreateModal::new(
component.data.custom_id.clone(),
"Configurer les suggestions",
)
.components(vec![
CreateActionRow::InputText(
CreateInputText::new(InputTextStyle::Short, "Canal des suggestions", "channel_id")
.required(false),
),
CreateActionRow::InputText(
CreateInputText::new(
InputTextStyle::Short,
"Canal d'approbation",
"approve_channel_id",
)
.required(false),
),
]);
let _ = component
.create_response(&ctx.http, CreateInteractionResponse::Modal(modal))
@@ -392,8 +418,8 @@ pub async fn handle_modal_interaction(ctx: &Context, modal: &ModalInteraction) -
};
if action.ends_with(":configure") {
let channel_id = modal_value(modal, "channel_id")
.and_then(|value| value.trim().parse::<i64>().ok());
let channel_id =
modal_value(modal, "channel_id").and_then(|value| value.trim().parse::<i64>().ok());
let approve_channel_id = modal_value(modal, "approve_channel_id")
.and_then(|value| value.trim().parse::<i64>().ok());
+23 -9
View File
@@ -3,10 +3,10 @@ use serenity::builder::{
CreateActionRow, CreateButton, CreateChannel, CreateEmbed, CreateInputText,
CreateInteractionResponse, CreateInteractionResponseMessage, CreateMessage, CreateModal,
};
use serenity::model::Colour;
use serenity::model::application::{
ActionRowComponent, ButtonStyle, ComponentInteraction, InputTextStyle, ModalInteraction,
};
use serenity::model::Colour;
use serenity::model::prelude::*;
use serenity::prelude::*;
@@ -40,7 +40,11 @@ fn tempvoc_embed(settings: &db::TempvocSettings) -> CreateEmbed {
.description("Gère les vocaux temporaires du serveur.")
.colour(Colour::from_rgb(100, 180, 255))
.timestamp(Utc::now())
.field("Statut", if settings.enabled { "Actif" } else { "Inactif" }, true);
.field(
"Statut",
if settings.enabled { "Actif" } else { "Inactif" },
true,
);
if let Some(trigger) = settings.trigger_channel_id {
embed = embed.field("Canal déclencheur", format!("<#{}>", trigger), true);
@@ -54,7 +58,11 @@ fn tempvoc_embed(settings: &db::TempvocSettings) -> CreateEmbed {
}
fn tempvoc_components(owner_id: UserId, settings: &db::TempvocSettings) -> Vec<CreateActionRow> {
let toggle_label = if settings.enabled { "Désactiver" } else { "Activer" };
let toggle_label = if settings.enabled {
"Désactiver"
} else {
"Activer"
};
vec![CreateActionRow::Buttons(vec![
CreateButton::new(format!("{}:toggle:{}", TEMPVOC_MENU, owner_id.get()))
@@ -254,10 +262,10 @@ pub async fn handle_modal_interaction(ctx: &Context, modal: &ModalInteraction) -
};
let bot_id = ctx.cache.current_user().id.get() as i64;
let trigger_channel_id = modal_value(modal, "trigger_channel_id")
.and_then(|value| value.trim().parse::<i64>().ok());
let category_id = modal_value(modal, "category_id")
.and_then(|value| value.trim().parse::<i64>().ok());
let trigger_channel_id =
modal_value(modal, "trigger_channel_id").and_then(|value| value.trim().parse::<i64>().ok());
let category_id =
modal_value(modal, "category_id").and_then(|value| value.trim().parse::<i64>().ok());
let updated = db::update_tempvoc_settings(
&pool,
@@ -317,14 +325,20 @@ async fn cached_room_members(ctx: &Context, guild_id: GuildId, channel_id: Chann
.unwrap_or(0)
}
async fn create_temp_channel(ctx: &Context, guild_id: GuildId, user: &User, settings: &db::TempvocSettings) {
async fn create_temp_channel(
ctx: &Context,
guild_id: GuildId,
user: &User,
settings: &db::TempvocSettings,
) {
let Some(trigger_channel_id) = settings.trigger_channel_id else {
return;
};
let Ok(trigger_channel) = ChannelId::new(trigger_channel_id as u64)
.to_channel(&ctx.http)
.await else {
.await
else {
return;
};
+19 -9
View File
@@ -1,13 +1,13 @@
use chrono::Utc;
use serenity::all::{PermissionOverwrite, PermissionOverwriteType, Permissions};
use serenity::builder::{
CreateActionRow, CreateButton, CreateChannel, CreateEmbed, CreateInputText,
CreateInteractionResponse, CreateInteractionResponseMessage, CreateMessage, CreateModal,
};
use serenity::model::Colour;
use serenity::model::application::{
ActionRowComponent, ButtonStyle, ComponentInteraction, InputTextStyle, ModalInteraction,
};
use serenity::all::{PermissionOverwrite, PermissionOverwriteType, Permissions};
use serenity::model::Colour;
use serenity::model::prelude::*;
use serenity::prelude::*;
@@ -58,7 +58,11 @@ fn ticket_embed(settings: &db::TicketSettings) -> CreateEmbed {
.description("Utilise les boutons ci-dessous pour gérer le système de tickets.")
.colour(Colour::from_rgb(90, 160, 255))
.timestamp(Utc::now())
.field("Statut", if settings.enabled { "Actif" } else { "Inactif" }, true);
.field(
"Statut",
if settings.enabled { "Actif" } else { "Inactif" },
true,
);
if let Some(category_id) = settings.category_id {
embed = embed.field("Catégorie", format!("<#{}>", category_id), true);
@@ -72,7 +76,11 @@ fn ticket_embed(settings: &db::TicketSettings) -> CreateEmbed {
}
fn ticket_components(owner_id: UserId, settings: &db::TicketSettings) -> Vec<CreateActionRow> {
let toggle_label = if settings.enabled { "Désactiver" } else { "Activer" };
let toggle_label = if settings.enabled {
"Désactiver"
} else {
"Activer"
};
vec![CreateActionRow::Buttons(vec![
CreateButton::new(format!("{}:create:{}", TICKET_MENU, owner_id.get()))
@@ -130,7 +138,9 @@ async fn create_ticket_channel(
title: String,
settings: &db::TicketSettings,
) -> Result<ChannelId, String> {
let pool = pool(ctx).await.ok_or_else(|| "Base de données indisponible".to_string())?;
let pool = pool(ctx)
.await
.ok_or_else(|| "Base de données indisponible".to_string())?;
let name = sanitize_channel_name(&title);
if name.is_empty() {
@@ -344,10 +354,10 @@ pub async fn handle_modal_interaction(ctx: &Context, modal: &ModalInteraction) -
};
if action.ends_with(":configure") {
let category_id = modal_value(modal, "category_id")
.and_then(|value| value.trim().parse::<i64>().ok());
let log_channel_id = modal_value(modal, "log_channel_id")
.and_then(|value| value.trim().parse::<i64>().ok());
let category_id =
modal_value(modal, "category_id").and_then(|value| value.trim().parse::<i64>().ok());
let log_channel_id =
modal_value(modal, "log_channel_id").and_then(|value| value.trim().parse::<i64>().ok());
if let Ok(updated) = db::update_ticket_settings(
&pool,
+12 -7
View File
@@ -16,9 +16,10 @@ const TICKET_ALLOW: Permissions = Permissions::VIEW_CHANNEL
.union(Permissions::ADD_REACTIONS);
fn ticket_member_id(args: &[&str], msg: &Message) -> Option<UserId> {
msg.mentions.first().map(|user| user.id).or_else(|| {
args.first().and_then(|value| parse_user_id(value))
})
msg.mentions
.first()
.map(|user| user.id)
.or_else(|| args.first().and_then(|value| parse_user_id(value)))
}
async fn ticket_member_update(
@@ -81,9 +82,9 @@ async fn ticket_member_update(
};
let mut overwrites = guild_channel.permission_overwrites.clone();
overwrites.retain(|overwrite| {
!matches!(overwrite.kind, PermissionOverwriteType::Member(id) if id == user_id)
});
overwrites.retain(
|overwrite| !matches!(overwrite.kind, PermissionOverwriteType::Member(id) if id == user_id),
);
if allow {
overwrites.push(PermissionOverwrite {
@@ -117,7 +118,11 @@ async fn ticket_member_update(
let _ = db::remove_ticket_member(&pool, ticket.id, user_id.get() as i64).await;
}
let title = if allow { "Membre ajouté" } else { "Membre retiré" };
let title = if allow {
"Membre ajouté"
} else {
"Membre retiré"
};
let description = if allow {
format!("<@{}> a été ajouté au ticket.", user_id.get())
} else {
+3 -9
View File
@@ -29,15 +29,9 @@ pub async fn handle_tickets(ctx: &Context, msg: &Message, args: &[&str]) {
let offset = (page - 1) * limit;
let bot_id = ctx.cache.current_user().id.get() as i64;
let tickets = db::get_guild_tickets(
&pool,
bot_id,
guild_id.get() as i64,
limit,
offset,
)
.await
.unwrap_or_default();
let tickets = db::get_guild_tickets(&pool, bot_id, guild_id.get() as i64, limit, offset)
.await
.unwrap_or_default();
if tickets.is_empty() {
send_embed(
+6 -3
View File
@@ -161,8 +161,9 @@ fn help_page_for_command(
"owner" | "unowner" | "clear_owners" | "bl" | "unbl" | "blinfo" | "clear_bl"
| "allbots" | "alladmins" | "botadmins" | "mainprefix" | "prefix" | "mp" | "invite"
| "leave" | "discussion" => "administration",
"perms" | "del" | "clear_perms" | "allperms" | "alias" | "help"
| "helpsetting" => "permissions",
"perms" | "del" | "clear_perms" | "allperms" | "alias" | "help" | "helpsetting" => {
"permissions"
}
_ => match meta.category {
"general" => "infos",
"profile" => "bot",
@@ -487,7 +488,9 @@ fn help_page_content(
} else {
lines.push(format!(
"`+{}`{} - {} · alias: `{}`",
label, permission, summary,
label,
permission,
summary,
aliases.join("`, `")
));
}
+8 -2
View File
@@ -15,8 +15,14 @@ pub async fn handle_show_pics(ctx: &Context, msg: &Message, args: &[&str]) {
return;
};
let members = guild.members(ctx, Some(200), None).await.unwrap_or_default();
let members: Vec<_> = members.into_iter().filter(|member| !member.user.bot).collect();
let members = guild
.members(ctx, Some(200), None)
.await
.unwrap_or_default();
let members: Vec<_> = members
.into_iter()
.filter(|member| !member.user.bot)
.collect();
if members.is_empty() {
send_embed(
+1 -1
View File
@@ -6,7 +6,7 @@ use serenity::prelude::*;
use crate::commands::common::{send_embed, theme_color};
use crate::db::DbPoolKey;
const LOGS_PER_PAGE: i64 = 5;
const LOGS_PER_PAGE: i64 = 10;
pub async fn pool(ctx: &Context) -> Option<sqlx::PgPool> {
let data = ctx.data.read().await;
-96
View File
@@ -569,102 +569,6 @@ async fn run_join_leave_action(ctx: &Context, guild_id: GuildId, kind: &str, use
let _ = channel_id.say(&ctx.http, content).await;
}
pub async fn handle_boostembed(ctx: &Context, msg: &Message, args: &[&str]) {
let Some(guild_id) = msg.guild_id else {
return;
};
let Some(pool) = pool(ctx).await else {
return;
};
let bot_id = ctx.cache.current_user().id;
if let Some(action) = args.first().map(|v| v.to_lowercase()) {
match action.as_str() {
"on" | "off" => {
let enabled = action == "on";
let _ = sqlx::query(
r#"
INSERT INTO bot_boost_embed (bot_id, guild_id, enabled)
VALUES ($1, $2, $3)
ON CONFLICT (bot_id, guild_id)
DO UPDATE SET enabled = EXCLUDED.enabled, updated_at = NOW();
"#,
)
.bind(bot_id.get() as i64)
.bind(guild_id.get() as i64)
.bind(enabled)
.execute(&pool)
.await;
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("BoostEmbed")
.description(if enabled { "Activé" } else { "Désactivé" })
.color(theme_color(ctx).await),
)
.await;
return;
}
"test" => {
send_boost_embed(ctx, guild_id, &msg.author).await;
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("BoostEmbed")
.description("Test envoyé.")
.color(theme_color(ctx).await),
)
.await;
return;
}
_ => {}
}
}
let row = sqlx::query_as::<_, (bool, Option<String>, Option<String>, Option<i32>)>(
r#"
SELECT enabled, title, description, color
FROM bot_boost_embed
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 desc = if let Some((enabled, title, description, color)) = row {
format!(
"État: {}\nTitle: {}\nDescription: {}\nColor: {}",
if enabled { "on" } else { "off" },
title.unwrap_or_else(|| "(défaut)".to_string()),
description.unwrap_or_else(|| "(défaut)".to_string()),
color
.map(|v| format!("#{:06X}", v.max(0) as u32))
.unwrap_or_else(|| "(thème)".to_string())
)
} else {
"Aucun réglage boost embed.".to_string()
};
send_embed(
ctx,
msg,
CreateEmbed::new()
.title("BoostEmbed")
.description(desc)
.color(theme_color(ctx).await),
)
.await;
}
pub async fn handle_set_boostembed(ctx: &Context, msg: &Message, args: &[&str]) {
let Some(guild_id) = msg.guild_id else {
return;
+22 -22
View File
@@ -17,6 +17,8 @@ pub mod allperms;
pub mod autobackup;
#[path = "admin/autoconfiglog.rs"]
pub mod autoconfiglog;
#[path = "admin/autopublish.rs"]
pub mod autopublish;
#[path = "admin/autoreact.rs"]
pub mod autoreact;
#[path = "admin/backup.rs"]
@@ -55,6 +57,8 @@ pub mod changeall;
pub mod channel;
#[path = "general/choose.rs"]
pub mod choose;
#[path = "admin/claim.rs"]
pub mod claim;
#[path = "admin/cleanup.rs"]
pub mod cleanup;
#[path = "admin/clear_all_sanctions.rs"]
@@ -69,6 +73,8 @@ pub mod clear_owners;
pub mod clear_perms;
#[path = "admin/clear_sanctions.rs"]
pub mod clear_sanctions;
#[path = "admin/close.rs"]
pub mod close;
#[path = "admin/cmute.rs"]
pub mod cmute;
pub mod command_contract;
@@ -168,6 +174,8 @@ pub mod prefix;
pub mod raidlog;
#[path = "profile/remove_activity.rs"]
pub mod remove_activity;
#[path = "admin/rename.rs"]
pub mod rename;
#[path = "admin/renew.rs"]
pub mod renew;
#[path = "admin/reroll.rs"]
@@ -194,10 +202,14 @@ pub mod set_boostembed;
pub mod set_modlogs;
#[path = "general/shadowbot.rs"]
pub mod shadowbot;
#[path = "general/showpics.rs"]
pub mod showpics;
#[path = "general/snipe.rs"]
pub mod snipe;
#[path = "profile/stream.rs"]
pub mod stream;
#[path = "admin/suggestion.rs"]
pub mod suggestion;
#[path = "admin/sync.rs"]
pub mod sync;
#[path = "admin/tempban.rs"]
@@ -208,8 +220,18 @@ pub mod tempcmute;
pub mod tempmute;
#[path = "admin/temprole.rs"]
pub mod temprole;
#[path = "admin/tempvoc.rs"]
pub mod tempvoc;
#[path = "admin/tempvoc_cmd.rs"]
pub mod tempvoc_cmd;
#[path = "profile/theme.rs"]
pub mod theme;
#[path = "admin/ticket.rs"]
pub mod ticket;
#[path = "admin/ticket_member.rs"]
pub mod ticket_member;
#[path = "admin/tickets.rs"]
pub mod tickets;
#[path = "admin/unban.rs"]
pub mod unban;
#[path = "admin/unbanall.rs"]
@@ -244,34 +266,12 @@ pub mod viewlogs;
pub mod vocinfo;
#[path = "admin/voicekick.rs"]
pub mod voicekick;
#[path = "admin/ticket.rs"]
pub mod ticket;
#[path = "admin/tickets.rs"]
pub mod tickets;
#[path = "general/showpics.rs"]
pub mod showpics;
#[path = "admin/suggestion.rs"]
pub mod suggestion;
#[path = "admin/autopublish.rs"]
pub mod autopublish;
#[path = "admin/tempvoc.rs"]
pub mod tempvoc;
#[path = "admin/tempvoc_cmd.rs"]
pub mod tempvoc_cmd;
#[path = "admin/voicelog.rs"]
pub mod voicelog;
#[path = "admin/voicemove.rs"]
pub mod voicemove;
#[path = "admin/warn.rs"]
pub mod warn;
#[path = "admin/claim.rs"]
pub mod claim;
#[path = "admin/close.rs"]
pub mod close;
#[path = "admin/rename.rs"]
pub mod rename;
#[path = "admin/ticket_member.rs"]
pub mod ticket_member;
#[path = "profile/watch.rs"]
pub mod watch;
+13 -2
View File
@@ -77,7 +77,14 @@ fn build_embed(settings: &HelpSettingsData) -> CreateEmbed {
.field("Mode d'affichage", format!("`{}`", settings.layout), true)
.field(
"Aliases",
format!("`{}`", if settings.aliases_enabled { "on" } else { "off" }),
format!(
"`{}`",
if settings.aliases_enabled {
"on"
} else {
"off"
}
),
true,
)
.field(
@@ -166,7 +173,11 @@ fn build_components(owner_id: UserId, settings: &HelpSettingsData) -> Vec<Create
)
.placeholder("Action rapide (select)");
vec![mode_row, toggle_row, CreateActionRow::SelectMenu(quick_menu)]
vec![
mode_row,
toggle_row,
CreateActionRow::SelectMenu(quick_menu),
]
}
async fn send_settings_panel(
-1
View File
@@ -2741,4 +2741,3 @@ pub async fn delete_tempvoc_room(pool: &PgPool, channel_id: i64) -> Result<(), s
Ok(())
}
+10 -3
View File
@@ -2,7 +2,8 @@ use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::{
advanced_tools, help, helpsetting, mp, perms_service, suggestion, tempvoc, ticket,
advanced_tools, boostembed, help, helpsetting, mp, perms_service, suggestion, tempvoc,
ticket,
};
pub async fn handle_interaction_create(ctx: &Context, interaction: &Interaction) {
@@ -21,6 +22,10 @@ pub async fn handle_interaction_create(ctx: &Context, interaction: &Interaction)
return;
}
if boostembed::handle_component_interaction(ctx, component).await {
return;
}
if tempvoc::handle_component_interaction(ctx, component).await {
return;
}
@@ -54,6 +59,10 @@ pub async fn handle_interaction_create(ctx: &Context, interaction: &Interaction)
return;
}
if boostembed::handle_modal_interaction(ctx, modal).await {
return;
}
if tempvoc::handle_modal_interaction(ctx, modal).await {
return;
}
@@ -61,5 +70,3 @@ pub async fn handle_interaction_create(ctx: &Context, interaction: &Interaction)
let _ = advanced_tools::handle_modal_interaction(ctx, modal).await;
}
}
+25 -15
View File
@@ -6,19 +6,19 @@ use std::sync::{Mutex, OnceLock};
use crate::commands::moderation_tools;
use crate::commands::remove_activity;
use crate::commands::{
addrole, alias, autobackup, autoconfiglog, autoreact, backup, ban, banlist, banner, bl, blinfo,
boostembed, boosters, boostlog, bringall, button, calc, change, changeall, channel, choose,
cleanup, clear_all_sanctions, clear_bl, clear_messages, clear_owners, clear_perms,
clear_sanctions, claim, cmute, compet, create, del, del_sanction, delrole, derank, discussion,
dnd, embed, emoji, end, giveaway, help, helpsetting, hide, hideall, idle, invisible,
invite, join, kick, leave, leave_settings, listen, loading, lock, lockall, mainprefix,
massiverole, member, messagelog, modlog, mp, mute, mutelist, newsticker, nolog, online, owner,
perms, pic, ping, playto, prefix, raidlog, renew, rename, reroll, role, rolelog, rolemembers,
sanctions, say, server, serverinfo, set, set_boostembed, set_modlogs, shadowbot, showpics,
snipe, stream, sync, suggestion, tempban, tempcmute, tempmute, temprole, tempvoc, theme, tickets,
unban, unbanall, unbl, uncmute, unhide, unhideall, unlock, unlockall, unmassiverole, unmute,
unmuteall, unowner, untemprole, user, viewlogs, vocinfo, voicekick, voicelog, voicemove, warn,
watch, autopublish, ticket, ticket_member, close, tempvoc_cmd,
addrole, alias, autobackup, autoconfiglog, autopublish, autoreact, backup, ban, banlist,
banner, bl, blinfo, boostembed, boosters, boostlog, bringall, button, calc, change, changeall,
channel, choose, claim, cleanup, clear_all_sanctions, clear_bl, clear_messages, clear_owners,
clear_perms, clear_sanctions, close, cmute, compet, create, del, del_sanction, delrole, derank,
discussion, dnd, embed, emoji, end, giveaway, help, helpsetting, hide, hideall, idle,
invisible, invite, join, kick, leave, leave_settings, listen, loading, lock, lockall,
mainprefix, massiverole, member, messagelog, modlog, mp, mute, mutelist, newsticker, nolog,
online, owner, perms, pic, ping, playto, prefix, raidlog, rename, renew, reroll, role, rolelog,
rolemembers, sanctions, say, server, serverinfo, set, set_boostembed, set_modlogs, shadowbot,
showpics, snipe, stream, suggestion, sync, tempban, tempcmute, tempmute, temprole, tempvoc,
tempvoc_cmd, theme, ticket, ticket_member, tickets, unban, unbanall, unbl, uncmute, unhide,
unhideall, unlock, unlockall, unmassiverole, unmute, unmuteall, unowner, untemprole, user,
viewlogs, vocinfo, voicekick, voicelog, voicemove, warn, watch,
};
use crate::commands::{alladmins, allbots, allperms, botadmins};
use crate::db::{DbPoolKey, upsert_message_observed};
@@ -145,12 +145,22 @@ pub async fn handle_message(ctx: &Context, msg: &Message) {
"del" => ticket_member::handle_ticket_remove(ctx, msg, &args).await,
"close" => close::handle_close(ctx, msg, &args).await,
"tickets" => tickets::handle_tickets(ctx, msg, &args).await,
"show" if args.first().map(|s| s.eq_ignore_ascii_case("pics")).unwrap_or(false) => {
"show"
if args
.first()
.map(|s| s.eq_ignore_ascii_case("pics"))
.unwrap_or(false) =>
{
showpics::handle_show_pics(ctx, msg, &args[1..]).await
}
"suggestion" => suggestion::handle_suggestion(ctx, msg, &args).await,
"autopublish" => autopublish::handle_autopublish(ctx, msg, &args).await,
"tempvoc" if args.first().map(|s| s.eq_ignore_ascii_case("cmd")).unwrap_or(false) => {
"tempvoc"
if args
.first()
.map(|s| s.eq_ignore_ascii_case("cmd"))
.unwrap_or(false) =>
{
tempvoc_cmd::handle_tempvoc_cmd(ctx, msg, &args[1..]).await
}
"tempvoc" => tempvoc::handle_tempvoc(ctx, msg, &args).await,