From d64a29b55b5e4a4d2710d75b651708eb7c563ee3 Mon Sep 17 00:00:00 2001 From: Tarrgon <61888458+Tarrgon@users.noreply.github.com> Date: Sat, 7 Jun 2025 08:57:06 -0400 Subject: [PATCH] Add custom event logs --- src/commands/ban.ts | 1 + src/commands/notes.ts | 52 ++++++++++++-- src/commands/phrases.ts | 71 ++++++++++++++++++- src/commands/softban.ts | 1 + src/events/handle-message.ts | 2 +- src/modals/add-note-modal.ts | 20 ++++++ src/shared/Database.ts | 4 ++ .../{message-logger.ts => event-log-utils.ts} | 53 ++++++++++---- src/utils/index.ts | 2 +- 9 files changed, 184 insertions(+), 22 deletions(-) rename src/utils/{message-logger.ts => event-log-utils.ts} (72%) diff --git a/src/commands/ban.ts b/src/commands/ban.ts index c7619c2..c981723 100644 --- a/src/commands/ban.ts +++ b/src/commands/ban.ts @@ -22,6 +22,7 @@ export default { .setName('reason') .setDescription('The reason for the ban') .setRequired(false) + .setMaxLength(400) ) .addNumberOption(option => option diff --git a/src/commands/notes.ts b/src/commands/notes.ts index d8ef8cf..37ad9d1 100644 --- a/src/commands/notes.ts +++ b/src/commands/notes.ts @@ -1,6 +1,6 @@ import { ApplicationCommandOptionType, ApplicationIntegrationType, AutocompleteInteraction, ChatInputCommandInteraction, Client, GuildBasedChannel, InteractionContextType, MessageFlags, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js'; import { Database } from '../shared/Database'; -import { channelIsInStaffCategory, deferInteraction } from '../utils'; +import { channelIsInStaffCategory, deferInteraction, logCustomEvent } from '../utils'; import { getNoteMessage } from '../utils/note-utils'; export default { @@ -66,18 +66,58 @@ export default { if (subcommand == 'add') { const reason = interaction.options.getString('reason', true); + logCustomEvent(interaction.guild!, { + title: 'Note Added', + description: null, + color: 0x00FF00, + timestamp: new Date(), + fields: [ + { + name: 'User', + value: `<@${interaction.user.id}>\n${interaction.user.username}`, + inline: true + }, + { + name: 'Note', + value: reason, + inline: true + } + ] + }); + await Database.putNote(user.id, reason, interaction.user.id); interaction.editReply('Added note.'); } else if (subcommand == 'remove') { + const user = interaction.options.getUser('user', true); const noteId = interaction.options.getInteger('note', true); - if (await Database.removeNote(noteId)) { - interaction.editReply('Removed note.'); + const notes = await Database.getNotes(user.id); + const note = notes.find(n => n.id == noteId); - } else { - interaction.editReply('Note not found.'); - } + if (!note) return interaction.editReply('Note not found.'); + + logCustomEvent(interaction.guild!, { + title: 'Note Removed', + description: null, + color: 0xFF0000, + timestamp: new Date(), + fields: [ + { + name: 'User', + value: `<@${interaction.user.id}>\n${interaction.user.username}`, + inline: true + }, + { + name: 'Note', + value: `${note.reason}\nBy: <@${note.mod_id}>`, + inline: true + } + ] + }); + + await Database.removeNote(noteId); + interaction.editReply('Removed note.'); } else if (subcommand == 'list') { const noteMessage = await getNoteMessage(user.id, 1); diff --git a/src/commands/phrases.ts b/src/commands/phrases.ts index d1ea98b..0bf87ec 100644 --- a/src/commands/phrases.ts +++ b/src/commands/phrases.ts @@ -1,6 +1,6 @@ import { ApplicationIntegrationType, AutocompleteInteraction, ChatInputCommandInteraction, Client, InteractionContextType, MessageFlags, PermissionFlagsBits, SlashCommandBuilder, User } from 'discord.js'; import { Database } from '../shared/Database'; -import { getE621User } from '../utils'; +import { getE621User, logCustomEvent } from '../utils'; import { config } from '../config'; import { TicketPhrase } from '../types'; @@ -142,8 +142,33 @@ export default { async function purgePhrases(interaction: ChatInputCommandInteraction, user: User) { const phrases: TicketPhrase[] = await Database.getTicketPhrasesFor(user.id); + const count = await Database.removeAllTicketPhrasesFor(user.id); + logCustomEvent(interaction.guild!, { + title: 'Ticket Phrases Purged', + description: null, + color: 0xFF0000, + timestamp: new Date(), + fields: [ + { + name: 'User', + value: `<@${interaction.user.id}>\n${interaction.user.username}`, + inline: true + }, + { + name: 'Target User', + value: `<@${user.id}>\n${user.username}`, + inline: true + }, + { + name: 'Count', + value: count.toString(), + inline: true + } + ] + }); + interaction.reply(`Purged the following phrases (${count}): ${phrases.map(p => `\`${p.phrase}\``).join('\n')}`); } @@ -167,11 +192,55 @@ async function dumpPhrases(interaction: ChatInputCommandInteraction) { async function addPhrase(interaction: ChatInputCommandInteraction, phrase: string, group: SubcommandGroup) { await Database.putTicketPhrase(group == 'admin' ? 'admin' : interaction.user.id, phrase); + + logCustomEvent(interaction.guild!, { + title: `${group == 'admin' ? 'Admin ' : ''}Ticket Phrase Added`, + description: null, + color: 0x00FF00, + timestamp: new Date(), + fields: [ + { + name: 'User', + value: `<@${interaction.user.id}>\n${interaction.user.username}`, + inline: true + }, + { + name: 'Phrase', + value: phrase, + inline: true + } + ] + }); + interaction.reply(`Phrases matching "${phrase}" will now alert ${group == 'admin' ? 'admins' : 'you'}.`); } async function removePhrase(interaction: ChatInputCommandInteraction, phraseId: number, group: SubcommandGroup) { + const phrase = await Database.getTicketPhrase(phraseId); + + if (!phrase) return interaction.reply('Phrase not found'); + await Database.removeTicketPhrase(phraseId); + + logCustomEvent(interaction.guild!, { + title: `${group == 'admin' ? 'Admin ' : ''}Ticket Phrase Removed`, + description: null, + color: 0xFF0000, + timestamp: new Date(), + fields: [ + { + name: 'User', + value: `<@${interaction.user.id}>\n${interaction.user.username}`, + inline: true + }, + { + name: 'Phrase', + value: phrase.phrase, + inline: true + } + ] + }); + interaction.reply(`Phrase will no longer alert ${group == 'admin' ? 'admins' : 'you'}.`); } diff --git a/src/commands/softban.ts b/src/commands/softban.ts index 4d95804..17bdbd7 100644 --- a/src/commands/softban.ts +++ b/src/commands/softban.ts @@ -21,6 +21,7 @@ export default { .setName('reason') .setDescription('The reason for the softban') .setRequired(false) + .setMaxLength(400) ) .addNumberOption(option => option diff --git a/src/events/handle-message.ts b/src/events/handle-message.ts index efc4360..98c1d1f 100644 --- a/src/events/handle-message.ts +++ b/src/events/handle-message.ts @@ -3,7 +3,7 @@ import { config } from '../config'; import { E621Post } from '../types'; import { getE621Post, getE621PostByMd5, getPostUrl, PostAction, spoilerOrBlacklist } from '../utils/e621-utils'; import { Database } from '../shared/Database'; -import { logDeletion, logEdit } from '../utils/message-logger'; +import { logDeletion, logEdit } from '../utils/event-log-utils'; import { isEdited } from '../utils/message-utils'; import { ALLOWED_MIMETYPES, blipIDRegex, calculateMD5FromURL, channelIgnoresLinks, channelIsInStaffCategory, channelIsSafe, commentIDRegex, forumTopicIDRegex, poolIDRegex, postIDRegex, recordIDRegex, searchLinkRegex, setIDRegex, takedownIDRegex, ticketIDRegex, userIDRegex, wikiLinkRegex } from '../utils'; diff --git a/src/modals/add-note-modal.ts b/src/modals/add-note-modal.ts index 5c5c28d..fcb296f 100644 --- a/src/modals/add-note-modal.ts +++ b/src/modals/add-note-modal.ts @@ -1,12 +1,32 @@ import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelType, Client, GuildTextBasedChannel, MessageFlags, ModalSubmitInteraction, TextChannel, ThreadAutoArchiveDuration } from 'discord.js'; import { ticketCooldownMap } from '../shared/ticket-cooldown'; import { Database } from '../shared/Database'; +import { logCustomEvent } from '../utils'; export default { name: 'add-note-modal', handler: async function (client: Client, interaction: ModalSubmitInteraction, id: string) { const message = interaction.fields.getTextInputValue('note-message'); + logCustomEvent(interaction.guild!, { + title: 'Note Added', + description: null, + color: 0x00FF00, + timestamp: new Date(), + fields: [ + { + name: 'User', + value: `<@${interaction.user.id}>\n${interaction.user.username}`, + inline: true + }, + { + name: 'Note', + value: message, + inline: true + } + ] + }); + await Database.putNote(id, message, interaction.user.id); interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Note added' }); diff --git a/src/shared/Database.ts b/src/shared/Database.ts index 0b63737..f99fccc 100644 --- a/src/shared/Database.ts +++ b/src/shared/Database.ts @@ -259,6 +259,10 @@ export class Database { await Database.db.run('INSERT INTO ticket_phrases(user_id, phrase) VALUES (?, ?)', userId, phrase); } + static async getTicketPhrase(id: number): Promise { + return await Database.db.get('SELECT * FROM ticket_phrases WHERE id = ?', id); + } + static async removeTicketPhrase(id: number) { await Database.db.run('DELETE from ticket_phrases WHERE id = ?', id); } diff --git a/src/utils/message-logger.ts b/src/utils/event-log-utils.ts similarity index 72% rename from src/utils/message-logger.ts rename to src/utils/event-log-utils.ts index 22a8344..a7b282f 100644 --- a/src/utils/message-logger.ts +++ b/src/utils/event-log-utils.ts @@ -1,17 +1,21 @@ -import { APIEmbedField, Channel, EmbedBuilder, GuildTextBasedChannel, messageLink } from 'discord.js'; +import { APIEmbedField, Channel, EmbedBuilder, Guild, GuildTextBasedChannel, messageLink, TextBasedChannel } from 'discord.js'; import { Database } from '../shared/Database'; import { Message } from '../events'; import { deserializeMessagePart, getModifiedAttachments, getModifiedStickers } from './message-utils'; import { LoggedMessage } from '../types'; +type CustomEventLogData = { + title: string + description: string | null + color: number | null + timestamp: Date | number | null + fields: APIEmbedField[] | null +} + export async function logEdit(loggedMessage: LoggedMessage, newMessage: Message) { - const settings = await Database.getGuildSettings(newMessage.guildId); + const channel = await getEventLogChannel(newMessage.guild); - if (!settings || !settings.event_logs_channel_id) return; - - const channel = await newMessage.guild.channels.fetch(settings.event_logs_channel_id); - - if (!channel || !channel.isSendable()) return; + if (!channel) return; const fields: APIEmbedField[] = []; fields.push(...getMainEmbeds(loggedMessage, newMessage)); @@ -27,13 +31,9 @@ export async function logEdit(loggedMessage: LoggedMessage, newMessage: Message< } export async function logDeletion(loggedMessage: LoggedMessage, deletedMessage: Message) { - const settings = await Database.getGuildSettings(deletedMessage.guildId); + const channel = await getEventLogChannel(deletedMessage.guild); - if (!settings || !settings.event_logs_channel_id) return; - - const channel = await deletedMessage.guild.channels.fetch(settings.event_logs_channel_id); - - if (!channel || !channel.isSendable()) return; + if (!channel) return; const fields: APIEmbedField[] = []; fields.push(...getMainEmbeds(loggedMessage, deletedMessage)); @@ -48,6 +48,33 @@ export async function logDeletion(loggedMessage: LoggedMessage, deletedMessage: channel.send({ embeds: [embed] }); } +export async function logCustomEvent(guild: Guild, data: CustomEventLogData) { + const channel = await getEventLogChannel(guild); + + if (!channel) return; + + const embed = new EmbedBuilder() + .setTitle(data.title) + .setColor(data.color) + .setTimestamp(data.timestamp); + + if (data.fields) embed.addFields(...data.fields); + + channel.send({ embeds: [embed] }); +} + +async function getEventLogChannel(guild: Guild): Promise { + const settings = await Database.getGuildSettings(guild.id); + + if (!settings || !settings.event_logs_channel_id) return null; + + const channel = await guild.channels.fetch(settings.event_logs_channel_id); + + if (!channel || !channel.isSendable()) return null; + + return channel; +} + function getMainEmbeds(loggedMessage: LoggedMessage, newMessage: Message): APIEmbedField[] { const channelString = `${newMessage.channel.toString()}\n${newMessage.channel.name}`; const userString = `<@${loggedMessage.author_id}>\n${loggedMessage.author_name}`; diff --git a/src/utils/index.ts b/src/utils/index.ts index 9ee0f48..2f45323 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -7,7 +7,7 @@ export * from './commands'; export * from './e621-utils'; export * from './file-utils'; export * from './interaction-utils'; -export * from './message-logger'; +export * from './event-log-utils'; export * from './message-utils'; export * from './ms-to-human'; export * from './note-utils';