From 6cf23b42f789ff9a450cfb0092d6f20b1187fbee Mon Sep 17 00:00:00 2001 From: Carl Vitullo Date: Fri, 20 Feb 2026 17:05:45 -0500 Subject: [PATCH] Log mod-deleted messages to both deletion and moderation threads When a moderator deletes a message, it's now logged to both the per-user deletion thread and the moderation thread. This provides better visibility of mod actions in the moderation log alongside kicks, bans, and timeouts. Co-Authored-By: Claude Opus 4.6 --- app/discord/deletionLogger.ts | 60 +++++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/app/discord/deletionLogger.ts b/app/discord/deletionLogger.ts index babba536..7c065e7d 100644 --- a/app/discord/deletionLogger.ts +++ b/app/discord/deletionLogger.ts @@ -13,6 +13,7 @@ import { logEffect } from "#~/effects/observability"; import { quoteMessageContent } from "#~/helpers/discord"; import { getOrCreateDeletionLogThread } from "#~/models/deletionLogThreads"; import { fetchSettingsEffect, SETTINGS } from "#~/models/guilds.server"; +import { getOrCreateUserThread } from "#~/models/userThreads"; import { MessageCacheService, @@ -110,8 +111,8 @@ export async function startDeletionLogging(client: Client) { const sent = ``; const header = uncachedAuditEntry?.executor - ? `<@${uncachedAuditEntry.executor.id}> deleted from ${channelMention}, sent ${sent}` - : `Message deleted from ${channelMention}, sent ${sent}`; + ? `-# <@${uncachedAuditEntry.executor.id}> deleted from ${channelMention}, sent ${sent}` + : `-# Message deleted from ${channelMention}, sent ${sent}`; yield* Effect.tryPromise({ try: () => @@ -164,23 +165,23 @@ export async function startDeletionLogging(client: Client) { const sent = ``; const header = auditEntry?.executor - ? `<@${auditEntry.executor.id}> deleted from ${channelMention}, sent ${sent}` - : `Message deleted from ${channelMention}, sent ${sent}`; + ? `-# <@${auditEntry.executor.id}> deleted from ${channelMention}, sent ${sent}` + : `-# Message deleted from ${channelMention}, sent ${sent}`; + + const embed = { + description: [ + header, + `<@${user.id}>`, + quoteMessageContent(content ?? "*(content not cached)*"), + ].join("\n"), + color: Colors.Red, + }; yield* Effect.tryPromise({ try: () => thread.send({ allowedMentions: { parse: [] }, - embeds: [ - { - description: [ - header, - `<@${user.id}>`, - quoteMessageContent(content ?? "*(content not cached)*"), - ].join("\n"), - color: Colors.Red, - }, - ], + embeds: [embed], }), catch: (error) => logEffect( @@ -190,6 +191,37 @@ export async function startDeletionLogging(client: Client) { { guildId: guild.id, error: String(error) }, ), }).pipe(Effect.catchAll((e) => e)); + + // If a mod deleted this message, also log to the moderation thread + if (auditEntry?.executor) { + const modThread = yield* getOrCreateUserThread(guild, user).pipe( + Effect.catchAll((error) => + logEffect( + "warn", + "DeletionLogger", + "Failed to get/create moderation thread for mod deletion", + { guildId: guild.id, userId: user.id, error: String(error) }, + ), + ), + ); + + if (modThread) { + yield* Effect.tryPromise({ + try: () => + modThread.send({ + allowedMentions: { parse: [] }, + embeds: [embed], + }), + catch: (error) => + logEffect( + "warn", + "DeletionLogger", + "Failed to post mod deletion to moderation thread", + { guildId: guild.id, error: String(error) }, + ), + }).pipe(Effect.catchAll((e) => e)); + } + } }).pipe( Effect.catchAll((e) => logEffect(