From d01323cf87844fd9dbae5cf12f4e66bc7c04ae5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dorian=20Oszcz=C4=99da?= Date: Sun, 18 Aug 2024 21:24:14 +0100 Subject: [PATCH 1/2] feat: Log bulk message deletes. --- assets/localisations/events/eng-GB.json | 5 +++ source/constants/contexts.ts | 4 +++ source/library/commands/handlers/purge.ts | 4 +-- source/library/stores/journalling.ts | 14 +++++++++ .../discord/message-delete-bulk.ts | 31 +++++++++++++++++++ source/library/stores/journalling/loggers.ts | 2 ++ .../stores/journalling/logos/purge-end.ts | 9 ++---- source/types.d.ts | 8 +---- 8 files changed, 60 insertions(+), 17 deletions(-) create mode 100644 source/library/stores/journalling/discord/message-delete-bulk.ts diff --git a/assets/localisations/events/eng-GB.json b/assets/localisations/events/eng-GB.json index 2580f34ff..421ac02dd 100644 --- a/assets/localisations/events/eng-GB.json +++ b/assets/localisations/events/eng-GB.json @@ -10,6 +10,11 @@ "events.messageDelete.title": "Message deleted", "events.messageDelete.description": "{user} has deleted their message in {channel}.", "events.messageDelete.fields.content": "Content", + "events.messageDeleteBulk.title": "Messages deleted", + "events.messageDeleteBulk.description": "{messages} in {channel}.", + "events.messageDeleteBulk.description.messages.one": "{one} message has been deleted", + "events.messageDeleteBulk.description.messages.two": "{two} messages have been deleted", + "events.messageDeleteBulk.description.messages.many": "{many} messages have been deleted", "events.messageUpdate.title": "Message updated", "events.messageUpdate.description": "{user} has updated their message in {channel}.", "events.messageUpdate.fields.before": "Before", diff --git a/source/constants/contexts.ts b/source/constants/contexts.ts index 589d864de..c1d7b251b 100644 --- a/source/constants/contexts.ts +++ b/source/constants/contexts.ts @@ -1206,6 +1206,10 @@ export default Object.freeze({ content: localise("events.messageDelete.fields.content", locale)(), }, }), + messageDeleteBulk: ({ localise, locale }) => ({ + title: localise("events.messageDeleteBulk.title", locale)(), + description: localise("events.messageDeleteBulk.description", locale), + }), messageUpdate: ({ localise, locale }) => ({ title: localise("events.messageUpdate.title", locale)(), description: localise("events.messageUpdate.description", locale), diff --git a/source/library/commands/handlers/purge.ts b/source/library/commands/handlers/purge.ts index e168c732a..6e2e63242 100644 --- a/source/library/commands/handlers/purge.ts +++ b/source/library/commands/handlers/purge.ts @@ -3,7 +3,6 @@ import { mention, timestamp, trim } from "logos:core/formatting"; import type { Client } from "logos/client"; import { InteractionCollector } from "logos/collectors"; import { Guild } from "logos/models/guild"; -import { JournallingStore } from "logos/stores/journalling"; async function handlePurgeMessagesAutocomplete( client: Client, @@ -553,11 +552,10 @@ async function handlePurgeMessages( )} as requested by ${client.diagnostics.user(interaction.user)}.`, ); - const messageLog = JournallingStore.generateMessageLog(client, { messages }); await client.tryLog("purgeEnd", { guildId: guild.id, journalling: configuration.journaling, - args: [member, channel, deletedCount, messageLog], + args: [member, channel, deletedCount], }); clearTimeout(responseDeletionTimeoutId); diff --git a/source/library/stores/journalling.ts b/source/library/stores/journalling.ts index 427c86e72..975830eb5 100644 --- a/source/library/stores/journalling.ts +++ b/source/library/stores/journalling.ts @@ -15,6 +15,7 @@ class JournallingStore { readonly #guildMemberAddCollector: Collector<"guildMemberAdd">; readonly #guildMemberRemoveCollector: Collector<"guildMemberRemove">; readonly #messageDeleteCollector: Collector<"messageDelete">; + readonly #messageDeleteBulkCollector: Collector<"messageDeleteBulk">; readonly #messageUpdateCollector: Collector<"messageUpdate">; constructor(client: Client) { @@ -26,6 +27,7 @@ class JournallingStore { this.#guildMemberAddCollector = new Collector(); this.#guildMemberRemoveCollector = new Collector(); this.#messageDeleteCollector = new Collector(); + this.#messageDeleteBulkCollector = new Collector(); this.#messageUpdateCollector = new Collector(); } @@ -37,6 +39,7 @@ class JournallingStore { this.#guildMemberAddCollector.onCollect(this.#guildMemberAdd.bind(this)); this.#guildMemberRemoveCollector.onCollect(this.#guildMemberRemove.bind(this)); this.#messageDeleteCollector.onCollect(this.#messageDelete.bind(this)); + this.#messageDeleteBulkCollector.onCollect(this.#messageDeleteBulk.bind(this)); this.#messageUpdateCollector.onCollect(this.#messageUpdate.bind(this)); await this.#client.registerCollector("guildBanAdd", this.#guildBanAddCollector); @@ -44,6 +47,7 @@ class JournallingStore { await this.#client.registerCollector("guildMemberAdd", this.#guildMemberAddCollector); await this.#client.registerCollector("guildMemberRemove", this.#guildMemberRemoveCollector); await this.#client.registerCollector("messageDelete", this.#messageDeleteCollector); + await this.#client.registerCollector("messageDeleteBulk", this.#messageDeleteBulkCollector); await this.#client.registerCollector("messageUpdate", this.#messageUpdateCollector); this.log.info("Journalling store set up."); @@ -57,6 +61,7 @@ class JournallingStore { this.#guildMemberAddCollector.close(); this.#guildMemberRemoveCollector.close(); this.#messageDeleteCollector.close(); + this.#messageDeleteBulkCollector.close(); this.#messageUpdateCollector.close(); this.log.info("Journalling store torn down."); @@ -183,6 +188,15 @@ class JournallingStore { await this.tryLog("messageDelete", { guildId, args: [payload, message] }); } + async #messageDeleteBulk(payload: Discord.Events["messageDeleteBulk"][0]): Promise { + const guildId = payload.guildId; + if (guildId === undefined) { + return; + } + + await this.tryLog("messageDeleteBulk", { guildId, args: [payload] }); + } + async #messageUpdate(message: Discord.Message): Promise { const guildId = message.guildId; if (guildId === undefined) { diff --git a/source/library/stores/journalling/discord/message-delete-bulk.ts b/source/library/stores/journalling/discord/message-delete-bulk.ts new file mode 100644 index 000000000..d3084c4ce --- /dev/null +++ b/source/library/stores/journalling/discord/message-delete-bulk.ts @@ -0,0 +1,31 @@ +import { mention } from "logos:core/formatting"; +import type { EventLogger } from "logos/stores/journalling/loggers"; +import { JournallingStore } from "logos/stores/journalling.ts"; +import { isDefined } from "logos:core/utilities.ts"; + +const logger: EventLogger<"messageDeleteBulk"> = (client, [payload], { guildLocale }) => { + const messages = payload.ids + .toReversed() + .map((messageId) => client.entities.messages.latest.get(messageId)) + .filter(isDefined); + const messageLog = JournallingStore.generateMessageLog(client, { messages }); + + const strings = constants.contexts.messageDeleteBulk({ localise: client.localise, locale: guildLocale }); + return { + embeds: [ + { + title: `${constants.emojis.events.message.deleted} ${strings.title}`, + color: constants.colours.failure, + description: strings.description({ + messages: client.pluralise("events.messageDeleteBulk.description.messages", guildLocale, { + quantity: messages.length, + }), + channel: mention(payload.channelId, { type: "channel" }), + }), + }, + ], + files: [{ name: "log.txt", blob: new Blob([messageLog]) } as Discord.FileContent], + }; +}; + +export default logger; diff --git a/source/library/stores/journalling/loggers.ts b/source/library/stores/journalling/loggers.ts index f1d5a6fdb..13cf52f9c 100644 --- a/source/library/stores/journalling/loggers.ts +++ b/source/library/stores/journalling/loggers.ts @@ -7,6 +7,7 @@ import guildBanRemove from "logos/stores/journalling/discord/guild-ban-remove"; import guildMemberAdd from "logos/stores/journalling/discord/guild-member-add"; import guildMemberRemove from "logos/stores/journalling/discord/guild-member-remove"; import messageDelete from "logos/stores/journalling/discord/message-delete"; +import messageDeleteBulk from "logos/stores/journalling/discord/message-delete-bulk"; import messageUpdate from "logos/stores/journalling/discord/message-update"; import entryRequestAccept from "logos/stores/journalling/logos/entry-request-accept"; import entryRequestReject from "logos/stores/journalling/logos/entry-request-reject"; @@ -37,6 +38,7 @@ const loggers = Object.freeze({ guildMemberAdd, guildMemberRemove, messageDelete, + messageDeleteBulk, messageUpdate, guildMemberKick, entryRequestSubmit, diff --git a/source/library/stores/journalling/logos/purge-end.ts b/source/library/stores/journalling/logos/purge-end.ts index 3c25cfdea..6e190518c 100644 --- a/source/library/stores/journalling/logos/purge-end.ts +++ b/source/library/stores/journalling/logos/purge-end.ts @@ -1,11 +1,7 @@ import { mention } from "logos:core/formatting"; import type { EventLogger } from "logos/stores/journalling/loggers"; -const logger: EventLogger<"purgeEnd"> = ( - client, - [member, channel, messageCount, messageLog, author], - { guildLocale }, -) => { +const logger: EventLogger<"purgeEnd"> = (client, [member, channel, messageCount, author], { guildLocale }) => { const strings = constants.contexts.purgeEnd({ localise: client.localise, locale: guildLocale }); return { embeds: [ @@ -14,7 +10,7 @@ const logger: EventLogger<"purgeEnd"> = ( color: constants.colours.success, description: strings.description({ moderator: client.diagnostics.member(member), - messages: client.pluralise("events.purgeBegin.description.messages", guildLocale, { + messages: client.pluralise("events.purgeEnd.description.messages", guildLocale, { quantity: messageCount, }), channel: mention(channel.id, { type: "channel" }), @@ -30,7 +26,6 @@ const logger: EventLogger<"purgeEnd"> = ( : undefined, }, ], - files: [{ name: "log.txt", blob: new Blob([messageLog]) } as Discord.FileContent], }; }; diff --git a/source/types.d.ts b/source/types.d.ts index f2e56fe34..cc006b49a 100644 --- a/source/types.d.ts +++ b/source/types.d.ts @@ -144,13 +144,7 @@ declare global { purgeBegin: [member: Logos.Member, channel: Logos.Channel, messageCount: number, author?: Logos.User]; /** A purging of messages is complete. */ - purgeEnd: [ - member: Logos.Member, - channel: Logos.Channel, - messageCount: number, - messageLog: string, - author?: Logos.User, - ]; + purgeEnd: [member: Logos.Member, channel: Logos.Channel, messageCount: number, author?: Logos.User]; /** A user has enabled slowmode in a channel. */ slowmodeEnable: [user: Logos.User, channel: Logos.Channel, level: SlowmodeLevel]; From 2687929c0be3853c7c29f586e472b3fab7e3c761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dorian=20Oszcz=C4=99da?= Date: Sun, 18 Aug 2024 21:24:58 +0100 Subject: [PATCH 2/2] chore: Bump version. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 30111a708..8f27632db 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "name": "logos", "description": "A multi-purpose community bot built to cater to language-learning communities on Discord.", "license": "Apache-2.0", - "version": "4.20.3", + "version": "4.21.0", "type": "module", "keywords": [ "discord",