diff --git a/src/modules/logging.js b/src/modules/logging.js index d31e93b..d02d546 100644 --- a/src/modules/logging.js +++ b/src/modules/logging.js @@ -81,6 +81,9 @@ const WHITELISTED_EVENTS = new Set([ "GUILD_PROFILE_UPDATE", ]); +const cacheBanTimeout = {}; +const leaveTimeout = {}; + events.add("guildAuditLogEntryCreate", "logging", async function (entry) { const channel = await getLoggingChannel(entry.guild); if (!channel) return; @@ -94,9 +97,6 @@ events.add("guildAuditLogEntryCreate", "logging", async function (entry) { case AuditLogActions.CHANNEL_OVERWRITE_CREATE: case AuditLogActions.CHANNEL_OVERWRITE_UPDATE: case AuditLogActions.CHANNEL_OVERWRITE_DELETE: - case AuditLogActions.MEMBER_KICK: - case AuditLogActions.MEMBER_BAN_ADD: - case AuditLogActions.MEMBER_BAN_REMOVE: case AuditLogActions.BOT_ADD: case AuditLogActions.ROLE_CREATE: case AuditLogActions.ROLE_UPDATE: @@ -185,7 +185,7 @@ events.add("guildAuditLogEntryCreate", "logging", async function (entry) { footer: { text: `Channel ID: ${entry.targetID}`, }, - timestamp: new Date().toISOString(), + timestamp: new Date(snowflakeToTimestamp(entry.id)).toISOString(), }, ], }); @@ -297,7 +297,8 @@ events.add("guildAuditLogEntryCreate", "logging", async function (entry) { embeds: [ { color: COLOR_CHANGED, - title: `Server updated`, + title: `Server Updated`, + timestamp: new Date(snowflakeToTimestamp(entry.id)).toISOString(), fields, thumbnail: thumbnail != null @@ -311,10 +312,6 @@ events.add("guildAuditLogEntryCreate", "logging", async function (entry) { url: image, } : null, - footer: { - text: `Guild ID: ${entry.targetID}`, - }, - timestamp: new Date().toISOString(), }, ], }); @@ -340,6 +337,22 @@ events.add("guildAuditLogEntryCreate", "logging", async function (entry) { value: formatChange(oldVisibility, newVisibility), inline: true, }); + break; + } + case "server_tag": { + fields.push({ + name: "Server Tag", + value: formatChange(oldValue, newValue), + inline: true, + }); + break; + } + default: { + fields.push({ + name: `\`${key}\``, + value: formatChange(oldValue, newValue), + inline: true, + }); } } } @@ -361,11 +374,8 @@ events.add("guildAuditLogEntryCreate", "logging", async function (entry) { { color: COLOR_CHANGED, title: `Server Profile Updated`, + timestamp: new Date(snowflakeToTimestamp(entry.id)).toISOString(), fields, - footer: { - text: `Guild ID: ${entry.targetID}`, - }, - timestamp: new Date().toISOString(), }, ], }); @@ -420,11 +430,11 @@ events.add("guildAuditLogEntryCreate", "logging", async function (entry) { ? "themselves" : `member <@${entry.targetID}> (${formatUsername(target)})` }`, + timestamp: new Date(snowflakeToTimestamp(entry.id)).toISOString(), fields, footer: { text: `User ID: ${entry.targetID}`, }, - timestamp: new Date().toISOString(), }, ], }); @@ -453,7 +463,7 @@ events.add("guildAuditLogEntryCreate", "logging", async function (entry) { } ${roles.length > 1 ? `${roles.length} ` : ""}role${plural} ${added ? "to" : "from"} ${ isSelf ? "self" : `<@${entry.targetID}> (${formatUsername(entry.target.user)})` }`, - timestamp: new Date().toISOString(), + timestamp: new Date(snowflakeToTimestamp(entry.id)).toISOString(), fields: [ !addAndRemove && { name: `Role${plural}`, @@ -493,6 +503,7 @@ events.add("guildAuditLogEntryCreate", "logging", async function (entry) { )}) created webhook \`${after.name.replaceAll("`", "\u02cb")}\` in <#${entry.after.channel_id}> (${ entry.guild.channels.get(after.channel_id)?.name ?? "" })`, + timestamp: new Date(snowflakeToTimestamp(entry.id)).toISOString(), fields: [ after.application_id != null && { name: "Application ID", @@ -525,7 +536,7 @@ events.add("guildAuditLogEntryCreate", "logging", async function (entry) { color: COLOR_ADDED, title: `${after.invitable != null ? "Private " : ""}Thread Created`, description: `<#${entry.targetID}> (${after.name})`, - timestamp: new Date().toISOString(), + timestamp: new Date(snowflakeToTimestamp(entry.id)).toISOString(), fields: [ { name: "Created by", @@ -616,7 +627,7 @@ events.add("guildAuditLogEntryCreate", "logging", async function (entry) { description: `<@${entry.user.id}> (${formatUsername(entry.user)}) ${verb} <#${entry.targetID}>${ entry.target?.name != null ? ` (${entry.target.name})` : "" }`, - timestamp: new Date().toISOString(), + timestamp: new Date(snowflakeToTimestamp(entry.id)).toISOString(), fields: [ after.name != null && { name: "Name", @@ -663,7 +674,7 @@ events.add("guildAuditLogEntryCreate", "logging", async function (entry) { color: COLOR_REMOVED, title: `${entry.before.invitable != null ? "Private " : ""}Thread Deleted`, description: `<#${entry.targetID}> (${entry.before.name})`, - timestamp: new Date().toISOString(), + timestamp: new Date(snowflakeToTimestamp(entry.id)).toISOString(), fields: [ { name: "Deleted by", @@ -690,6 +701,150 @@ events.add("guildAuditLogEntryCreate", "logging", async function (entry) { }); break; } + case AuditLogActions.MEMBER_KICK: { + const leaveData = leaveTimeout[entry.guild.id]; + let member; + if (leaveData.user === entry.targetID) { + clearTimeout(leaveData.timeout); + member = leaveData.member; + } + + const user = await hf.bot.requestHandler.request("GET", APIEndpoints.USER(entry.targetID), true); + const defaultAvatar = getDefaultAvatar(user.id, user.discriminator ?? 0); + const avatar = user.avatar ? CDNEndpoints.USER_AVATAR(user.id, user.avatar) : defaultAvatar; + + channel.createMessage({ + embeds: [ + { + color: COLOR_REMOVED, + title: "User Kicked", + description: `<@${entry.user.id}> (${formatUsername(entry.user)}) kicked <@${user.id}> (${formatUsername( + user + )})`, + timestamp: new Date(snowflakeToTimestamp(entry.id)).toISOString(), + fields: [ + member?.joinedAt && { + name: "Joined", + value: ``, + inline: true, + }, + member?.nick && { + name: "Nickname", + value: member.nick, + inline: true, + }, + entry.integrationType != null && { + name: "Integration Type", + value: `\`${entry.integrationType}\``, + inline: true, + }, + member?.roles?.length > 0 && { + name: "Roles", + value: member.roles + .sort((a, b) => entry.guild.roles.get(b).position - entry.guild.roles.get(a).position) + .map((role) => `<@&${role}>`) + .join(", "), + inline: false, + }, + entry.reason != null && { + name: "Reason", + value: `\`${entry.reason.replaceAll("`", "\u02cb")}\``, + }, + ].filter((x) => !!x), + footer: { + text: `User ID: ${entry.targetID}`, + }, + thumbnail: { + url: avatar, + }, + }, + ], + }); + + break; + } + case AuditLogActions.MEMBER_BAN_ADD: { + const callback = async () => { + const user = await hf.bot.requestHandler.request("GET", APIEndpoints.USER(entry.targetID), true); + const defaultAvatar = getDefaultAvatar(user.id, user.discriminator ?? 0); + const avatar = user.avatar ? CDNEndpoints.USER_AVATAR(user.id, user.avatar) : defaultAvatar; + + channel.createMessage({ + embeds: [ + { + color: COLOR_REMOVED, + title: "User Banned", + description: `<@${entry.user.id}> (${formatUsername(entry.user)}) banned <@${ + user.id + }> (${formatUsername(user)})`, + timestamp: new Date(snowflakeToTimestamp(entry.id)).toISOString(), + fields: [ + entry.reason != null && { + name: "Reason", + value: `\`${entry.reason.replaceAll("`", "\u02cb")}\``, + }, + ].filter((x) => !!x), + footer: { + text: `User ID: ${entry.targetID}`, + }, + thumbnail: { + url: avatar, + }, + }, + ], + }); + }; + + if (entry.user.bot) { + cacheBanTimeout[entry.guild.id] = { + user: entry.user.id, + timeout: setTimeout(callback, 1500), + }; + break; + } else { + await callback(); + } + break; + } + case AuditLogActions.MEMBER_BAN_REMOVE: { + const cacheBan = cacheBanTimeout[entry.guild.id]; + if (cacheBan?.user === entry.user.id) { + clearTimeout(cacheBan.timeout); + delete cacheBanTimeout[entry.guild.id]; + break; + } + + const user = await hf.bot.requestHandler.request("GET", APIEndpoints.USER(entry.targetID), true); + const defaultAvatar = getDefaultAvatar(user.id, user.discriminator ?? 0); + const avatar = user.avatar ? CDNEndpoints.USER_AVATAR(user.id, user.avatar) : defaultAvatar; + + channel.createMessage({ + embeds: [ + { + color: COLOR_ADDED, + title: "User Unbanned", + description: `<@${entry.user.id}> (${formatUsername(entry.user)}) unbanned <@${ + user.id + }> (${formatUsername(user)})`, + timestamp: new Date(snowflakeToTimestamp(entry.id)).toISOString(), + fields: [ + entry.reason != null && { + name: "Reason", + value: `\`${entry.reason.replaceAll("`", "\u02cb")}\``, + }, + ].filter((x) => !!x), + footer: { + text: `User ID: ${entry.targetID}`, + }, + thumbnail: { + url: avatar, + }, + }, + ], + }); + + break; + } } } catch (err) { const eventName = Object.entries(AuditLogActions).find(([name, val]) => val === entry.actionType)[0]; @@ -823,48 +978,56 @@ events.add("guildMemberRemove", "logging", async function (guild, member) { const timestamp = new Date().toISOString(); if (member instanceof Member) { - const user = member.user; - const defaultAvatar = getDefaultAvatar(user.id, user.discriminator ?? 0); - const avatar = user.avatar ? CDNEndpoints.USER_AVATAR(user.id, user.avatar) : defaultAvatar; + const callback = async () => { + const user = member.user; + const defaultAvatar = getDefaultAvatar(user.id, user.discriminator ?? 0); + const avatar = user.avatar ? CDNEndpoints.USER_AVATAR(user.id, user.avatar) : defaultAvatar; - channel - .createMessage({ - embeds: [ - { - color: COLOR_REMOVED, - title: "Member Left", - description: `<@${user.id}> (${formatUsername(user)})`, - timestamp, - thumbnail: { - url: avatar, + channel + .createMessage({ + embeds: [ + { + color: COLOR_REMOVED, + title: "Member Left", + description: `<@${user.id}> (${formatUsername(user)})`, + timestamp, + thumbnail: { + url: avatar, + }, + fields: [ + member?.joinedAt && { + name: "Joined", + value: ``, + inline: true, + }, + member?.nick && { + name: "Nickname", + value: member.nick, + inline: true, + }, + member?.roles?.length > 0 && { + name: "Roles", + value: member.roles + .sort((a, b) => guild.roles.get(b).position - guild.roles.get(a).position) + .map((role) => `<@&${role}>`) + .join(", "), + inline: false, + }, + ].filter((x) => !!x), + footer: { + text: `User ID: ${user.id}`, + }, }, - fields: [ - { - name: "Joined", - value: ``, - inline: true, - }, - member?.nick && { - name: "Nickname", - value: member.nick, - inline: true, - }, - member?.roles?.length > 0 && { - name: "Roles", - value: member.roles - .sort((a, b) => guild.roles.get(b).position - guild.roles.get(a).position) - .map((role) => `<@&${role}>`) - .join(", "), - inline: false, - }, - ].filter((x) => !!x), - footer: { - text: `User ID: ${user.id}`, - }, - }, - ], - }) - .catch(() => {}); + ], + }) + .catch(() => {}); + }; + + leaveTimeout[guild.id] = { + user: member.user.id, + member, + timeout: setTimeout(callback, 2000), + }; } else { const user = await hf.bot.requestHandler.request("GET", APIEndpoints.USER(member.user.id), true); const defaultAvatar = getDefaultAvatar(user.id, user.discriminator ?? 0);