Add custom event logs
This commit is contained in:
@@ -22,6 +22,7 @@ export default {
|
|||||||
.setName('reason')
|
.setName('reason')
|
||||||
.setDescription('The reason for the ban')
|
.setDescription('The reason for the ban')
|
||||||
.setRequired(false)
|
.setRequired(false)
|
||||||
|
.setMaxLength(400)
|
||||||
)
|
)
|
||||||
.addNumberOption(option =>
|
.addNumberOption(option =>
|
||||||
option
|
option
|
||||||
|
|||||||
+46
-6
@@ -1,6 +1,6 @@
|
|||||||
import { ApplicationCommandOptionType, ApplicationIntegrationType, AutocompleteInteraction, ChatInputCommandInteraction, Client, GuildBasedChannel, InteractionContextType, MessageFlags, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
|
import { ApplicationCommandOptionType, ApplicationIntegrationType, AutocompleteInteraction, ChatInputCommandInteraction, Client, GuildBasedChannel, InteractionContextType, MessageFlags, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
|
||||||
import { Database } from '../shared/Database';
|
import { Database } from '../shared/Database';
|
||||||
import { channelIsInStaffCategory, deferInteraction } from '../utils';
|
import { channelIsInStaffCategory, deferInteraction, logCustomEvent } from '../utils';
|
||||||
import { getNoteMessage } from '../utils/note-utils';
|
import { getNoteMessage } from '../utils/note-utils';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -66,18 +66,58 @@ export default {
|
|||||||
if (subcommand == 'add') {
|
if (subcommand == 'add') {
|
||||||
const reason = interaction.options.getString('reason', true);
|
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);
|
await Database.putNote(user.id, reason, interaction.user.id);
|
||||||
|
|
||||||
interaction.editReply('Added note.');
|
interaction.editReply('Added note.');
|
||||||
} else if (subcommand == 'remove') {
|
} else if (subcommand == 'remove') {
|
||||||
|
const user = interaction.options.getUser('user', true);
|
||||||
const noteId = interaction.options.getInteger('note', true);
|
const noteId = interaction.options.getInteger('note', true);
|
||||||
|
|
||||||
if (await Database.removeNote(noteId)) {
|
const notes = await Database.getNotes(user.id);
|
||||||
interaction.editReply('Removed note.');
|
const note = notes.find(n => n.id == noteId);
|
||||||
|
|
||||||
} else {
|
if (!note) return interaction.editReply('Note not found.');
|
||||||
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') {
|
} else if (subcommand == 'list') {
|
||||||
const noteMessage = await getNoteMessage(user.id, 1);
|
const noteMessage = await getNoteMessage(user.id, 1);
|
||||||
|
|
||||||
|
|||||||
+70
-1
@@ -1,6 +1,6 @@
|
|||||||
import { ApplicationIntegrationType, AutocompleteInteraction, ChatInputCommandInteraction, Client, InteractionContextType, MessageFlags, PermissionFlagsBits, SlashCommandBuilder, User } from 'discord.js';
|
import { ApplicationIntegrationType, AutocompleteInteraction, ChatInputCommandInteraction, Client, InteractionContextType, MessageFlags, PermissionFlagsBits, SlashCommandBuilder, User } from 'discord.js';
|
||||||
import { Database } from '../shared/Database';
|
import { Database } from '../shared/Database';
|
||||||
import { getE621User } from '../utils';
|
import { getE621User, logCustomEvent } from '../utils';
|
||||||
import { config } from '../config';
|
import { config } from '../config';
|
||||||
import { TicketPhrase } from '../types';
|
import { TicketPhrase } from '../types';
|
||||||
|
|
||||||
@@ -142,8 +142,33 @@ export default {
|
|||||||
|
|
||||||
async function purgePhrases(interaction: ChatInputCommandInteraction, user: User) {
|
async function purgePhrases(interaction: ChatInputCommandInteraction, user: User) {
|
||||||
const phrases: TicketPhrase[] = await Database.getTicketPhrasesFor(user.id);
|
const phrases: TicketPhrase[] = await Database.getTicketPhrasesFor(user.id);
|
||||||
|
|
||||||
const count = await Database.removeAllTicketPhrasesFor(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')}`);
|
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) {
|
async function addPhrase(interaction: ChatInputCommandInteraction, phrase: string, group: SubcommandGroup) {
|
||||||
await Database.putTicketPhrase(group == 'admin' ? 'admin' : interaction.user.id, phrase);
|
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'}.`);
|
interaction.reply(`Phrases matching "${phrase}" will now alert ${group == 'admin' ? 'admins' : 'you'}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removePhrase(interaction: ChatInputCommandInteraction, phraseId: number, group: SubcommandGroup) {
|
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);
|
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'}.`);
|
interaction.reply(`Phrase will no longer alert ${group == 'admin' ? 'admins' : 'you'}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export default {
|
|||||||
.setName('reason')
|
.setName('reason')
|
||||||
.setDescription('The reason for the softban')
|
.setDescription('The reason for the softban')
|
||||||
.setRequired(false)
|
.setRequired(false)
|
||||||
|
.setMaxLength(400)
|
||||||
)
|
)
|
||||||
.addNumberOption(option =>
|
.addNumberOption(option =>
|
||||||
option
|
option
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { config } from '../config';
|
|||||||
import { E621Post } from '../types';
|
import { E621Post } from '../types';
|
||||||
import { getE621Post, getE621PostByMd5, getPostUrl, PostAction, spoilerOrBlacklist } from '../utils/e621-utils';
|
import { getE621Post, getE621PostByMd5, getPostUrl, PostAction, spoilerOrBlacklist } from '../utils/e621-utils';
|
||||||
import { Database } from '../shared/Database';
|
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 { 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';
|
import { ALLOWED_MIMETYPES, blipIDRegex, calculateMD5FromURL, channelIgnoresLinks, channelIsInStaffCategory, channelIsSafe, commentIDRegex, forumTopicIDRegex, poolIDRegex, postIDRegex, recordIDRegex, searchLinkRegex, setIDRegex, takedownIDRegex, ticketIDRegex, userIDRegex, wikiLinkRegex } from '../utils';
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,32 @@
|
|||||||
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelType, Client, GuildTextBasedChannel, MessageFlags, ModalSubmitInteraction, TextChannel, ThreadAutoArchiveDuration } from 'discord.js';
|
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelType, Client, GuildTextBasedChannel, MessageFlags, ModalSubmitInteraction, TextChannel, ThreadAutoArchiveDuration } from 'discord.js';
|
||||||
import { ticketCooldownMap } from '../shared/ticket-cooldown';
|
import { ticketCooldownMap } from '../shared/ticket-cooldown';
|
||||||
import { Database } from '../shared/Database';
|
import { Database } from '../shared/Database';
|
||||||
|
import { logCustomEvent } from '../utils';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'add-note-modal',
|
name: 'add-note-modal',
|
||||||
handler: async function (client: Client, interaction: ModalSubmitInteraction, id: string) {
|
handler: async function (client: Client, interaction: ModalSubmitInteraction, id: string) {
|
||||||
const message = interaction.fields.getTextInputValue('note-message');
|
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);
|
await Database.putNote(id, message, interaction.user.id);
|
||||||
|
|
||||||
interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Note added' });
|
interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Note added' });
|
||||||
|
|||||||
@@ -259,6 +259,10 @@ export class Database {
|
|||||||
await Database.db.run('INSERT INTO ticket_phrases(user_id, phrase) VALUES (?, ?)', userId, phrase);
|
await Database.db.run('INSERT INTO ticket_phrases(user_id, phrase) VALUES (?, ?)', userId, phrase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async getTicketPhrase(id: number): Promise<TicketPhrase | undefined> {
|
||||||
|
return await Database.db.get<TicketPhrase>('SELECT * FROM ticket_phrases WHERE id = ?', id);
|
||||||
|
}
|
||||||
|
|
||||||
static async removeTicketPhrase(id: number) {
|
static async removeTicketPhrase(id: number) {
|
||||||
await Database.db.run('DELETE from ticket_phrases WHERE id = ?', id);
|
await Database.db.run('DELETE from ticket_phrases WHERE id = ?', id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 { Database } from '../shared/Database';
|
||||||
import { Message } from '../events';
|
import { Message } from '../events';
|
||||||
import { deserializeMessagePart, getModifiedAttachments, getModifiedStickers } from './message-utils';
|
import { deserializeMessagePart, getModifiedAttachments, getModifiedStickers } from './message-utils';
|
||||||
import { LoggedMessage } from '../types';
|
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<true>) {
|
export async function logEdit(loggedMessage: LoggedMessage, newMessage: Message<true>) {
|
||||||
const settings = await Database.getGuildSettings(newMessage.guildId);
|
const channel = await getEventLogChannel(newMessage.guild);
|
||||||
|
|
||||||
if (!settings || !settings.event_logs_channel_id) return;
|
if (!channel) return;
|
||||||
|
|
||||||
const channel = await newMessage.guild.channels.fetch(settings.event_logs_channel_id);
|
|
||||||
|
|
||||||
if (!channel || !channel.isSendable()) return;
|
|
||||||
|
|
||||||
const fields: APIEmbedField[] = [];
|
const fields: APIEmbedField[] = [];
|
||||||
fields.push(...getMainEmbeds(loggedMessage, newMessage));
|
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<true>) {
|
export async function logDeletion(loggedMessage: LoggedMessage, deletedMessage: Message<true>) {
|
||||||
const settings = await Database.getGuildSettings(deletedMessage.guildId);
|
const channel = await getEventLogChannel(deletedMessage.guild);
|
||||||
|
|
||||||
if (!settings || !settings.event_logs_channel_id) return;
|
if (!channel) return;
|
||||||
|
|
||||||
const channel = await deletedMessage.guild.channels.fetch(settings.event_logs_channel_id);
|
|
||||||
|
|
||||||
if (!channel || !channel.isSendable()) return;
|
|
||||||
|
|
||||||
const fields: APIEmbedField[] = [];
|
const fields: APIEmbedField[] = [];
|
||||||
fields.push(...getMainEmbeds(loggedMessage, deletedMessage));
|
fields.push(...getMainEmbeds(loggedMessage, deletedMessage));
|
||||||
@@ -48,6 +48,33 @@ export async function logDeletion(loggedMessage: LoggedMessage, deletedMessage:
|
|||||||
channel.send({ embeds: [embed] });
|
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<GuildTextBasedChannel | null> {
|
||||||
|
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<true>): APIEmbedField[] {
|
function getMainEmbeds(loggedMessage: LoggedMessage, newMessage: Message<true>): APIEmbedField[] {
|
||||||
const channelString = `${newMessage.channel.toString()}\n${newMessage.channel.name}`;
|
const channelString = `${newMessage.channel.toString()}\n${newMessage.channel.name}`;
|
||||||
const userString = `<@${loggedMessage.author_id}>\n${loggedMessage.author_name}`;
|
const userString = `<@${loggedMessage.author_id}>\n${loggedMessage.author_name}`;
|
||||||
+1
-1
@@ -7,7 +7,7 @@ export * from './commands';
|
|||||||
export * from './e621-utils';
|
export * from './e621-utils';
|
||||||
export * from './file-utils';
|
export * from './file-utils';
|
||||||
export * from './interaction-utils';
|
export * from './interaction-utils';
|
||||||
export * from './message-logger';
|
export * from './event-log-utils';
|
||||||
export * from './message-utils';
|
export * from './message-utils';
|
||||||
export * from './ms-to-human';
|
export * from './ms-to-human';
|
||||||
export * from './note-utils';
|
export * from './note-utils';
|
||||||
|
|||||||
Reference in New Issue
Block a user