From f52c058a5445ae1a198b45a60a394b311905494b Mon Sep 17 00:00:00 2001 From: Wiktoria Van Harneveldt Date: Fri, 31 Oct 2025 12:49:34 +0100 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=90=9B=20fix:=20improve=20message=20d?= =?UTF-8?q?eletion=20logic=20in=20repel=20command=20to=20handle=20latest?= =?UTF-8?q?=20message=20timestamps=20and=20ensure=20current=20channel=20is?= =?UTF-8?q?=20included?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/commands/moderation/repel.ts | 49 +++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/src/commands/moderation/repel.ts b/src/commands/moderation/repel.ts index 80a91a0..eba2e96 100644 --- a/src/commands/moderation/repel.ts +++ b/src/commands/moderation/repel.ts @@ -13,8 +13,8 @@ import { } from 'discord.js'; import { HOUR, MINUTE, timeToString } from '../../constants/time.js'; import { config } from '../../env.js'; -import { getPublicChannels } from '../../util/channel.js'; import { logToChannel } from '../../util/channel-logging.js'; +import { getPublicChannels } from '../../util/channel.js'; import { buildCommandString, createCommand } from '../../util/commands.js'; const DEFAULT_LOOK_BACK_MS = 10 * MINUTE; @@ -130,7 +130,7 @@ const getTextChannels = (interaction: ChatInputCommandInteraction) => { console.error('Interaction is not in a guild'); return []; } - const channels = getPublicChannels(interaction.guild).values(); + const channels = Array.from(getPublicChannels(interaction.guild).values()); return [ interaction.channel as TextChannel, ...channels.filter((channel) => channel.id !== interaction.channelId), @@ -148,26 +148,43 @@ const handleDeleteMessages = async ({ }) => { let deleted = 0; const failedChannels: string[] = []; + + // Collect all target messages from all channels and find the latest timestamp + const channelMessages = channels.map((channel) => { + const messages = channel.messages.cache; + const targetMessages = messages.filter( + (message) => message.author && message.author.id === target.id && message.deletable + ); + return { channel, targetMessages }; + }); + + // Find the latest message timestamp across all channels + const latestMessageTimestamp = channelMessages.reduce((latest, { targetMessages }) => { + const channelLatest = targetMessages.reduce( + (max, msg) => Math.max(max, msg.createdTimestamp), + 0 + ); + return Math.max(latest, channelLatest); + }, 0); + + // If no messages found from the target user, return early + if (latestMessageTimestamp === 0) { + return { deleted: 0, failedChannels: [] }; + } + + // Delete messages within the lookback window from the latest message await Promise.allSettled( - channels.map(async (channel) => { + channelMessages.map(async ({ channel, targetMessages }) => { try { - const messages = channel.messages.cache; - const targetMessages = messages - .filter((message) => { - return ( - message.author && - message.author.id === target.id && - message.deletable && - Date.now() - message.createdTimestamp < lookBack - ); - }) + const messagesToDelete = targetMessages + .filter((message) => latestMessageTimestamp - message.createdTimestamp < lookBack) .map((msg) => msg.id); - if (targetMessages.length === 0) { + if (messagesToDelete.length === 0) { return; } - await channel.bulkDelete(targetMessages, true); - deleted += targetMessages.length; + await channel.bulkDelete(messagesToDelete, true); + deleted += messagesToDelete.length; } catch (error) { console.error(`Error deleting messages in channel ${channel.name}:`, error); failedChannels.push(channel.id); From 5d114b36bd949c7ec16a33edf1cc9c2a38403f85 Mon Sep 17 00:00:00 2001 From: Wiktoria Van Harneveldt Date: Fri, 31 Oct 2025 14:32:42 +0100 Subject: [PATCH 2/3] fix: optimize message deletion logic in repel command to use Map for target messages and improve timestamp handling --- src/commands/moderation/repel.ts | 44 +++++++++++++++++++------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/src/commands/moderation/repel.ts b/src/commands/moderation/repel.ts index eba2e96..fda43a5 100644 --- a/src/commands/moderation/repel.ts +++ b/src/commands/moderation/repel.ts @@ -150,22 +150,26 @@ const handleDeleteMessages = async ({ const failedChannels: string[] = []; // Collect all target messages from all channels and find the latest timestamp - const channelMessages = channels.map((channel) => { - const messages = channel.messages.cache; - const targetMessages = messages.filter( - (message) => message.author && message.author.id === target.id && message.deletable - ); - return { channel, targetMessages }; - }); + const channelMessages: Array<{ + channel: TextChannel; + targetMessages: Map; + }> = []; + let latestMessageTimestamp = 0; + + for (const channel of channels) { + const targetMessages = new Map(); + + for (const [id, message] of channel.messages.cache) { + if (message.author && message.author.id === target.id && message.deletable) { + targetMessages.set(id, message); + latestMessageTimestamp = Math.max(latestMessageTimestamp, message.createdTimestamp); + } + } - // Find the latest message timestamp across all channels - const latestMessageTimestamp = channelMessages.reduce((latest, { targetMessages }) => { - const channelLatest = targetMessages.reduce( - (max, msg) => Math.max(max, msg.createdTimestamp), - 0 - ); - return Math.max(latest, channelLatest); - }, 0); + if (targetMessages.size > 0) { + channelMessages.push({ channel, targetMessages }); + } + } // If no messages found from the target user, return early if (latestMessageTimestamp === 0) { @@ -176,9 +180,13 @@ const handleDeleteMessages = async ({ await Promise.allSettled( channelMessages.map(async ({ channel, targetMessages }) => { try { - const messagesToDelete = targetMessages - .filter((message) => latestMessageTimestamp - message.createdTimestamp < lookBack) - .map((msg) => msg.id); + const messagesToDelete: string[] = []; + + for (const [id, message] of targetMessages) { + if (latestMessageTimestamp - message.createdTimestamp < lookBack) { + messagesToDelete.push(id); + } + } if (messagesToDelete.length === 0) { return; From a940b457b56681eac02aaf13fe3062a229f671a7 Mon Sep 17 00:00:00 2001 From: Wiktoria Van Harneveldt Date: Fri, 31 Oct 2025 15:35:18 +0100 Subject: [PATCH 3/3] fix: update type for targetMessages in repel command to use Message from discord.js --- src/commands/moderation/repel.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/commands/moderation/repel.ts b/src/commands/moderation/repel.ts index fda43a5..4781b3f 100644 --- a/src/commands/moderation/repel.ts +++ b/src/commands/moderation/repel.ts @@ -5,6 +5,7 @@ import { type ChatInputCommandInteraction, EmbedBuilder, GuildMember, + type Message, MessageFlags, PermissionFlagsBits, type Role, @@ -152,12 +153,12 @@ const handleDeleteMessages = async ({ // Collect all target messages from all channels and find the latest timestamp const channelMessages: Array<{ channel: TextChannel; - targetMessages: Map; + targetMessages: Map; }> = []; let latestMessageTimestamp = 0; for (const channel of channels) { - const targetMessages = new Map(); + const targetMessages = new Map(); for (const [id, message] of channel.messages.cache) { if (message.author && message.author.id === target.id && message.deletable) {