From 5365c1df7947235b32f5d72e039e501f39a10c25 Mon Sep 17 00:00:00 2001 From: Lleyton Gray Date: Sun, 10 Nov 2024 14:49:11 -0800 Subject: [PATCH] feat: logger module --- .env.example | 1 + src/index.ts | 1 + src/modules/logger.ts | 136 ++++++++++++++++++++++++++++++++++++++++++ src/util.ts | 6 ++ 4 files changed, 144 insertions(+) create mode 100644 src/modules/logger.ts diff --git a/.env.example b/.env.example index 17ad232..f6e0330 100644 --- a/.env.example +++ b/.env.example @@ -6,6 +6,7 @@ PRIMARY_GUILD_ID= ANNOUNCEMENTS_CHANNEL_ID= UPDATES_CHANNEL_ID= GENERAL_CHANNEL_ID= +LOGGING_CHANNEL_ID= MEMBER_ROLE_ID= SUPPORT_FORUM_ID= HEALTH_PORT=8080 diff --git a/src/index.ts b/src/index.ts index d11295d..986a688 100644 --- a/src/index.ts +++ b/src/index.ts @@ -63,3 +63,4 @@ import './modules/guildMemberAdd'; import './modules/funAI'; import './modules/cutefishInfo'; import './modules/support'; +import './modules/logger'; diff --git a/src/modules/logger.ts b/src/modules/logger.ts new file mode 100644 index 0000000..0a07d86 --- /dev/null +++ b/src/modules/logger.ts @@ -0,0 +1,136 @@ +import client from '../client'; +import { EmbedBuilder, Events, time, TimestampStyles } from 'discord.js'; +import { getLoggingChannel, userURL } from '../util'; + +// Right now we log the following for moderation purposes: +// - MessageDelete +// - MessageUpdate +// - GuildMemberAdd +// - GuildMemberRemove + +client.on(Events.MessageDelete, async (message) => { + if (!message.member) return; + + const loggingChannel = await getLoggingChannel(); + if (!loggingChannel.isSendable()) { + throw new Error('Logging channel is not a text channel.'); + } + + const embed = new EmbedBuilder() + .setTitle('Message Delete') + .setAuthor({ + name: message.member.displayName, + iconURL: message.member.displayAvatarURL(), + url: userURL(message.member.user.id), + }) + .setColor('#ff0000') + .setDescription(message.content) + .setFooter({ + text: `Message ID: ${message.id}`, + }).data; + + await loggingChannel.send({ + embeds: [embed], + }); +}); + +client.on(Events.MessageUpdate, async (oldMessage, newMessage) => { + if (!oldMessage.member) return; + + const loggingChannel = await getLoggingChannel(); + if (!loggingChannel.isSendable()) { + throw new Error('Logging channel is not a text channel.'); + } + + const embed = new EmbedBuilder() + .setTitle('Message Update') + .setAuthor({ + name: oldMessage.member.displayName, + iconURL: oldMessage.member.displayAvatarURL(), + url: userURL(oldMessage.member.user.id), + }) + .setColor('#ffff00') + .addFields( + { + name: 'Old Content', + value: oldMessage.content ?? 'Unknown', + }, + { + name: 'New Content', + value: newMessage.content ?? 'Unknown', + }, + ) + .setFooter({ + text: `Message ID: ${oldMessage.id}`, + }).data; + + await loggingChannel.send({ + embeds: [embed], + }); +}); + +client.on(Events.GuildMemberAdd, async (member) => { + const loggingChannel = await getLoggingChannel(); + if (!loggingChannel.isSendable()) { + throw new Error('Logging channel is not a text channel.'); + } + + const embed = new EmbedBuilder() + .setTitle('Member Join') + .setAuthor({ + name: member.displayName, + iconURL: member.displayAvatarURL(), + url: userURL(member.user.id), + }) + .setColor('#00ff00') + .addFields( + { + name: 'Discord User Since', + value: time(member.user.createdAt, TimestampStyles.ShortDateTime), + }, + { + name: 'User ID', + value: member.user.id, + }, + ) + .setFooter({ + text: `Member ID: ${member.id}`, + }).data; + + await loggingChannel.send({ + embeds: [embed], + }); +}); + +client.on(Events.GuildMemberRemove, async (member) => { + const loggingChannel = await getLoggingChannel(); + if (!loggingChannel.isSendable()) { + throw new Error('Logging channel is not a text channel.'); + } + + const embed = new EmbedBuilder() + .setTitle('Member Leave') + .setAuthor({ + name: member.displayName, + iconURL: member.displayAvatarURL(), + url: userURL(member.user.id), + }) + .setColor('#ff0000') + .addFields( + { + name: 'Member Since', + value: member.joinedAt ? time(member.joinedAt, TimestampStyles.ShortDateTime) : 'Unknown', + }, + { + name: 'User ID', + value: member.user.id, + }, + ) + .setFooter({ + text: `Member ID: ${member.id}`, + }).data; + + await loggingChannel.send({ + embeds: [embed], + }); +}); diff --git a/src/util.ts b/src/util.ts index dc7fdd6..240ddaa 100644 --- a/src/util.ts +++ b/src/util.ts @@ -29,6 +29,10 @@ export const getGeneralChannel = async (): Promise => (await client.channels.fetch(process.env.GENERAL_CHANNEL_ID!)) ?? throwError('General channel not found'); +export const getLoggingChannel = async (): Promise => + (await client.channels.fetch(process.env.LOGGING_CHANNEL_ID!)) ?? + throwError('Logging channel not found'); + export const getRedisConnection = (): ConnectionOptions => ({ host: process.env.REDIS_HOST!, port: Number.parseInt(process.env.REDIS_PORT!, 10), @@ -40,3 +44,5 @@ export const containsWord = (msg: Message, word: string): boolean => { const matches = msg.content.match(new RegExp(`\\b${word}\\b`, 'i')); return matches != null && matches.length > 0; }; + +export const userURL = (id: string) => `https://discord.com/users/${id}`;