diff --git a/src/events/clean-moderation-messages/index.ts b/src/events/clean-moderation-messages/index.ts new file mode 100644 index 0000000..0d5bdd6 --- /dev/null +++ b/src/events/clean-moderation-messages/index.ts @@ -0,0 +1,22 @@ +import { Events } from "discord.js"; +import { isDynoModerationMessage, isMessageInAPublicChannel } from "../../utils/messages.js"; +import { createEvent } from "../helpers.js"; + +const DYNO_ID = "155149108183695360"; + +export const cleanModerationMessagesEvent = createEvent( + { + name: Events.MessageCreate, + }, + async (message) => { + const isDynoMessage = message.author.bot && message.author.id === DYNO_ID; + + if (isDynoMessage && isMessageInAPublicChannel(message) && isDynoModerationMessage(message)) { + try { + await message.delete(); + } catch (error) { + console.error("Failed to delete message:", error); + } + } + } +); diff --git a/src/events/index.ts b/src/events/index.ts index dcc8039..7755cd0 100644 --- a/src/events/index.ts +++ b/src/events/index.ts @@ -1,6 +1,12 @@ +import { cleanModerationMessagesEvent } from "./clean-moderation-messages/index.js"; import { interactionCreateEvent } from "./interaction-create/index.js"; import { readyEvent } from "./ready/index.js"; import { spamDetection } from "./spam-detection/index.js"; import type { DiscordEvent } from "./types.js"; -export const events: DiscordEvent[] = [readyEvent, interactionCreateEvent, spamDetection]; +export const events: DiscordEvent[] = [ + readyEvent, + interactionCreateEvent, + spamDetection, + cleanModerationMessagesEvent, +]; diff --git a/src/utils/messages.ts b/src/utils/messages.ts index 0e840fe..1bdb738 100644 --- a/src/utils/messages.ts +++ b/src/utils/messages.ts @@ -1,4 +1,4 @@ -import { type Message, MessageType, type PartialMessage } from "discord.js"; +import { type Message, MessageType, type PartialMessage, PermissionFlagsBits } from "discord.js"; export type GuildMessage = Message; @@ -44,3 +44,31 @@ const normalizeText = (text: string) => { .split(/\s+/) .filter(Boolean); }; + +export const isDynoModerationMessage = (message: Message) => { + const phrases = [ + "was banned", + "was unbanned", + "was kicked", + "was muted", + "was unmuted", + "has been warned", + ]; + return phrases.some((phrase) => + message.embeds.some((embed) => embed.description?.includes(phrase)) + ); +}; + +/** + * Checks if the message is in a channel where `@everyone` has permission to view + * @param message + * @returns true if the message is in a public channel, false otherwise + */ +export const isMessageInAPublicChannel = (message: Message | PartialMessage) => { + return ( + message.inGuild() && + message.channel + .permissionsFor(message.guild.roles.everyone) + .has(PermissionFlagsBits.ViewChannel) + ); +};