From 45d84663777ac183ce27024895df728206ced1c2 Mon Sep 17 00:00:00 2001 From: Akinator31 Date: Fri, 28 Nov 2025 13:38:47 +0100 Subject: [PATCH] feat(logs): add logs when closing tickets --- .../src/commands/close/slash_command/close.rs | 29 ++++++++++ .../src/commands/close/text_command/close.rs | 29 ++++++++++ .../force_close/slash_command/force_close.rs | 38 +++++++++++++ .../force_close/text_command/force_close.rs | 35 ++++++++++++ rustmail/src/i18n/language/en.rs | 4 ++ rustmail/src/i18n/language/fr.rs | 4 ++ rustmail/src/modules/scheduled_closures.rs | 55 +++++++++++++++++++ 7 files changed, 194 insertions(+) diff --git a/rustmail/src/commands/close/slash_command/close.rs b/rustmail/src/commands/close/slash_command/close.rs index 11fd098c..a8574a62 100644 --- a/rustmail/src/commands/close/slash_command/close.rs +++ b/rustmail/src/commands/close/slash_command/close.rs @@ -362,6 +362,35 @@ impl RegistrableCommand for CloseCommand { .await?; let _ = delete_scheduled_closure(&thread.id, db_pool).await; + if config.bot.enable_logs { + if let Some(logs_channel_id) = config.bot.logs_channel_id { + let base_url = config + .bot + .redirect_url + .trim_end_matches("/api/auth/callback") + .trim_end_matches('/'); + + let panel_url = format!("{}/panel/tickets/{}", base_url, thread.id); + + let mut params = HashMap::new(); + params.insert("username".to_string(), thread.user_name.clone()); + params.insert("user_id".to_string(), thread.user_id.to_string()); + params.insert("panel_url".to_string(), panel_url); + + let _ = MessageBuilder::system_message(&ctx, &config) + .translated_content( + "logs.ticket_closed", + Some(¶ms), + Some(command.user.id), + command.guild_id.map(|g| g.get()), + ) + .await + .to_channel(serenity::all::ChannelId::new(logs_channel_id)) + .send(true) + .await; + } + } + let _ = command.channel_id.delete(&ctx.http).await?; Ok(()) diff --git a/rustmail/src/commands/close/text_command/close.rs b/rustmail/src/commands/close/text_command/close.rs index fefd9206..82ee9d94 100644 --- a/rustmail/src/commands/close/text_command/close.rs +++ b/rustmail/src/commands/close/text_command/close.rs @@ -284,6 +284,35 @@ pub async fn close( .await?; let _ = delete_scheduled_closure(&thread.id, db_pool).await; + if config.bot.enable_logs { + if let Some(logs_channel_id) = config.bot.logs_channel_id { + let base_url = config + .bot + .redirect_url + .trim_end_matches("/api/auth/callback") + .trim_end_matches('/'); + + let panel_url = format!("{}/panel/tickets/{}", base_url, thread.id); + + let mut params = HashMap::new(); + params.insert("username".to_string(), thread.user_name.clone()); + params.insert("user_id".to_string(), thread.user_id.to_string()); + params.insert("panel_url".to_string(), panel_url); + + let _ = MessageBuilder::system_message(&ctx, config) + .translated_content( + "logs.ticket_closed", + Some(¶ms), + Some(msg.author.id), + msg.guild_id.map(|g| g.get()), + ) + .await + .to_channel(serenity::all::ChannelId::new(logs_channel_id)) + .send(true) + .await; + } + } + let _ = msg.channel_id.delete(&ctx.http).await?; Ok(()) diff --git a/rustmail/src/commands/force_close/slash_command/force_close.rs b/rustmail/src/commands/force_close/slash_command/force_close.rs index 22d8a43a..345d0d30 100644 --- a/rustmail/src/commands/force_close/slash_command/force_close.rs +++ b/rustmail/src/commands/force_close/slash_command/force_close.rs @@ -4,6 +4,7 @@ use crate::prelude::db::*; use crate::prelude::errors::*; use crate::prelude::handlers::*; use crate::prelude::i18n::*; +use crate::prelude::utils::*; use serenity::FutureExt; use serenity::all::{CommandInteraction, Context, CreateCommand, ResolvedOption}; use std::sync::Arc; @@ -82,11 +83,48 @@ impl RegistrableCommand for ForceCloseCommand { } } + let thread = get_thread_by_channel_id(&command.channel_id.to_string(), db_pool).await; + match is_orphaned_thread_channel(command.channel_id, db_pool).await { Ok(res) => { if !res { return Err(ModmailError::Thread(ThreadError::UserStillInServer)); } + + if let Some(thread_info) = thread { + if config.bot.enable_logs { + if let Some(logs_channel_id) = config.bot.logs_channel_id { + let base_url = config + .bot + .redirect_url + .trim_end_matches("/api/auth/callback") + .trim_end_matches('/'); + + let panel_url = + format!("{}/panel/tickets/{}", base_url, thread_info.id); + + let mut params = std::collections::HashMap::new(); + params + .insert("username".to_string(), thread_info.user_name.clone()); + params + .insert("user_id".to_string(), thread_info.user_id.to_string()); + params.insert("panel_url".to_string(), panel_url); + + let _ = MessageBuilder::system_message(&ctx, &config) + .translated_content( + "logs.ticket_closed", + Some(¶ms), + Some(command.user.id), + command.guild_id.map(|g| g.get()), + ) + .await + .to_channel(serenity::all::ChannelId::new(logs_channel_id)) + .send(true) + .await; + } + } + } + delete_channel(&ctx, command.channel_id).await } Err(..) => Err(ModmailError::Database(DatabaseError::QueryFailed( diff --git a/rustmail/src/commands/force_close/text_command/force_close.rs b/rustmail/src/commands/force_close/text_command/force_close.rs index 47e7624d..63e3a018 100644 --- a/rustmail/src/commands/force_close/text_command/force_close.rs +++ b/rustmail/src/commands/force_close/text_command/force_close.rs @@ -3,6 +3,7 @@ use crate::prelude::config::*; use crate::prelude::db::*; use crate::prelude::errors::*; use crate::prelude::handlers::*; +use crate::prelude::utils::*; use serenity::all::{Context, Message}; use std::sync::Arc; @@ -26,11 +27,45 @@ pub async fn force_close( }; } + let thread = get_thread_by_channel_id(&msg.channel_id.to_string(), db_pool).await; + match is_orphaned_thread_channel(msg.channel_id, db_pool).await { Ok(res) => { if !res { return Err(ModmailError::Thread(ThreadError::UserStillInServer)); } + + if let Some(thread_info) = thread { + if config.bot.enable_logs { + if let Some(logs_channel_id) = config.bot.logs_channel_id { + let base_url = config + .bot + .redirect_url + .trim_end_matches("/api/auth/callback") + .trim_end_matches('/'); + + let panel_url = format!("{}/panel/tickets/{}", base_url, thread_info.id); + + let mut params = std::collections::HashMap::new(); + params.insert("username".to_string(), thread_info.user_name.clone()); + params.insert("user_id".to_string(), thread_info.user_id.to_string()); + params.insert("panel_url".to_string(), panel_url); + + let _ = MessageBuilder::system_message(&ctx, config) + .translated_content( + "logs.ticket_closed", + Some(¶ms), + Some(msg.author.id), + msg.guild_id.map(|g| g.get()), + ) + .await + .to_channel(serenity::all::ChannelId::new(logs_channel_id)) + .send(true) + .await; + } + } + } + delete_channel(&ctx, msg.channel_id).await } Err(..) => Err(ModmailError::Database(DatabaseError::QueryFailed( diff --git a/rustmail/src/i18n/language/en.rs b/rustmail/src/i18n/language/en.rs index f5026f08..466cd28b 100644 --- a/rustmail/src/i18n/language/en.rs +++ b/rustmail/src/i18n/language/en.rs @@ -638,6 +638,10 @@ pub fn load_english_messages(dict: &mut ErrorDictionary) { "close.silent_closing".to_string(), DictionaryMessage::new("This ticket will be closed silently in {time}."), ); + dict.messages.insert( + "logs.ticket_closed".to_string(), + DictionaryMessage::new("Ticket closed for user **{username}** (ID: {user_id})\n[View log on panel]({panel_url})"), + ); dict.messages.insert( "feature.not_implemented".to_string(), DictionaryMessage::new("This feature is not yet implemented."), diff --git a/rustmail/src/i18n/language/fr.rs b/rustmail/src/i18n/language/fr.rs index 7d4cc5b3..56cb720f 100644 --- a/rustmail/src/i18n/language/fr.rs +++ b/rustmail/src/i18n/language/fr.rs @@ -660,6 +660,10 @@ pub fn load_french_messages(dict: &mut ErrorDictionary) { "close.silent_closing".to_string(), DictionaryMessage::new("Ce ticket sera fermé silencieusement dans {time}."), ); + dict.messages.insert( + "logs.ticket_closed".to_string(), + DictionaryMessage::new("Ticket fermé pour l'utilisateur **{username}** (ID: {user_id})\n[Voir le log sur le panel]({panel_url})"), + ); dict.messages.insert( "feature.not_implemented".to_string(), DictionaryMessage::new("Cette feature n'est pas encore implémentée."), diff --git a/rustmail/src/modules/scheduled_closures.rs b/rustmail/src/modules/scheduled_closures.rs index 27a7ac10..d01c577e 100644 --- a/rustmail/src/modules/scheduled_closures.rs +++ b/rustmail/src/modules/scheduled_closures.rs @@ -33,6 +33,36 @@ pub fn schedule_one(ctx: &Context, config: &Config, thread_id: String, close_at: ) .await; let _ = delete_scheduled_closure(&thread_id, pool).await; + + if config_clone.bot.enable_logs { + if let Some(logs_channel_id) = config_clone.bot.logs_channel_id { + let base_url = config_clone + .bot + .redirect_url + .trim_end_matches("/api/auth/callback") + .trim_end_matches('/'); + + let panel_url = format!("{}/panel/tickets/{}", base_url, thread.id); + + let mut params = std::collections::HashMap::new(); + params.insert("username".to_string(), thread.user_name.clone()); + params.insert("user_id".to_string(), thread.user_id.to_string()); + params.insert("panel_url".to_string(), panel_url); + + let _ = MessageBuilder::system_message(&ctx_clone, &config_clone) + .translated_content( + "logs.ticket_closed", + Some(¶ms), + None, + None, + ) + .await + .to_channel(ChannelId::new(logs_channel_id)) + .send(true) + .await; + } + } + if !current.silent { let _ = MessageBuilder::system_message(&ctx_clone, &config_clone) .content(&config_clone.bot.close_message) @@ -85,6 +115,31 @@ pub async fn hydrate_scheduled_closures(ctx: &Context, config: &Config) { ) .await; let _ = delete_scheduled_closure(&thread.id, pool).await; + + if config.bot.enable_logs { + if let Some(logs_channel_id) = config.bot.logs_channel_id { + let base_url = config + .bot + .redirect_url + .trim_end_matches("/api/auth/callback") + .trim_end_matches('/'); + + let panel_url = format!("{}/panel/tickets/{}", base_url, thread.id); + + let mut params = std::collections::HashMap::new(); + params.insert("username".to_string(), thread.user_name.clone()); + params.insert("user_id".to_string(), thread.user_id.to_string()); + params.insert("panel_url".to_string(), panel_url); + + let _ = MessageBuilder::system_message(ctx, config) + .translated_content("logs.ticket_closed", Some(¶ms), None, None) + .await + .to_channel(ChannelId::new(logs_channel_id)) + .send(true) + .await; + } + } + if !sc.silent { let _ = MessageBuilder::system_message(ctx, config) .content(&config.bot.close_message)