From 4d10b07979f0321896e5e3e561e4b1499800fa96 Mon Sep 17 00:00:00 2001 From: Miki <57331129+TheFoxInBoots@users.noreply.github.com> Date: Fri, 6 Mar 2026 20:26:06 +0100 Subject: [PATCH] added logging --- commands/custom/verify.js | 3 +- commands/mod/clear.js | 88 +++ commands/mod/setLog.js | 42 ++ commands/random/choose.js | 2 +- commands/random/coinflip.js | 2 +- commands/utility/ping.js | 2 +- events/{ready.js => clientReady.js} | 4 +- index.js | 69 ++- logger/embeds/bulkDeleteEmbed.js | 86 +++ logger/embeds/deleteEmbed.js | 99 +++ logger/embeds/editEmbed.js | 59 ++ logger/embeds/joinEmbed.js | 40 ++ logger/embeds/leaveEmbed.js | 72 +++ logger/embeds/userUpdateEmbed.js | 122 ++++ logger/extractMessage.js | 32 + logger/getLogChannel.js | 10 + logger/index.js | 222 +++++++ logger/logConfig.js | 14 + logger/logConfig.json | 7 + logger/logUserJoinLeave.js | 126 ++++ logger/logUserUpdate.js | 15 + logger/logUserUpdateFromUserEvent.js | 54 ++ logger/setupLogger.js | 21 + package-lock.json | 865 ++++++++++++++++++++------- package.json | 5 +- 25 files changed, 1823 insertions(+), 238 deletions(-) create mode 100644 commands/mod/clear.js create mode 100644 commands/mod/setLog.js rename events/{ready.js => clientReady.js} (76%) create mode 100644 logger/embeds/bulkDeleteEmbed.js create mode 100644 logger/embeds/deleteEmbed.js create mode 100644 logger/embeds/editEmbed.js create mode 100644 logger/embeds/joinEmbed.js create mode 100644 logger/embeds/leaveEmbed.js create mode 100644 logger/embeds/userUpdateEmbed.js create mode 100644 logger/extractMessage.js create mode 100644 logger/getLogChannel.js create mode 100644 logger/index.js create mode 100644 logger/logConfig.js create mode 100644 logger/logConfig.json create mode 100644 logger/logUserJoinLeave.js create mode 100644 logger/logUserUpdate.js create mode 100644 logger/logUserUpdateFromUserEvent.js create mode 100644 logger/setupLogger.js diff --git a/commands/custom/verify.js b/commands/custom/verify.js index 9d593d0..8081efd 100644 --- a/commands/custom/verify.js +++ b/commands/custom/verify.js @@ -1,5 +1,4 @@ -const { SlashCommandBuilder } = require("@discordjs/builders"); -const { PermissionFlagsBits } = require("discord-api-types/v10"); +const { SlashCommandBuilder, PermissionFlagsBits } = require("discord.js"); module.exports = { data: new SlashCommandBuilder() diff --git a/commands/mod/clear.js b/commands/mod/clear.js new file mode 100644 index 0000000..02d6755 --- /dev/null +++ b/commands/mod/clear.js @@ -0,0 +1,88 @@ +const { SlashCommandBuilder, PermissionFlagsBits } = require("discord.js"); + +module.exports = { + data: new SlashCommandBuilder() + .setName("clear") + .setDescription("Delete messages from the current channel") + .setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages) + .setDMPermission(false) + .addIntegerOption((option) => + option + .setName("amount") + .setDescription("Number of messages to delete (1-100)") + .setRequired(false) + .setMinValue(1) + .setMaxValue(100) + ) + .addUserOption((option) => + option + .setName("target") + .setDescription("Only delete messages from this user") + .setRequired(false) + ) + .addStringOption((option) => + option + .setName("reason") + .setDescription("Reason for deletion") + .setRequired(false) + ), + + async execute(interaction) { + const amount = interaction.options.getInteger("amount") || 1; + const target = interaction.options.getUser("target"); + const reason = + interaction.options.getString("reason") || "No reason provided"; + + await interaction.deferReply({ flags: 64 }); + + const channel = interaction.channel; + + if (!channel.isTextBased() || channel.isDMBased()) { + return interaction.editReply( + "This command can only be used in text channels." + ); + } + + try { + const messages = await channel.messages.fetch({ limit: amount }); + + let messagesToDelete = messages; + + if (target) { + messagesToDelete = messages.filter( + (msg) => msg.author.id === target.id + ); + } + + if (messagesToDelete.size === 0) { + return interaction.editReply("No messages found to delete."); + } + + const deleted = await channel.bulkDelete(messagesToDelete, true); + + // Only log bulk delete if more than 1 message are deleted + if (deleted.size > 1) { + const { logBulkDelete } = require("../../logger/index.js"); + await logBulkDelete(interaction, deleted, target, reason); + } + // If single message, let the messageDelete event handle it naturally + + const targetInfo = target ? ` from ${target.tag}` : ""; + await interaction.editReply( + `✅ Deleted ${deleted.size} message(s)${targetInfo}. Reason: ${reason}` + ); + } catch (error) { + console.error("Clear command error:", error); + + if (error.code === 50034) { + await interaction.editReply( + "❌ Cannot delete messages older than 14 days." + ); + } else { + await interaction.editReply( + "❌ Failed to delete messages. Check bot permissions." + ); + } + } + }, +}; diff --git a/commands/mod/setLog.js b/commands/mod/setLog.js new file mode 100644 index 0000000..5435e12 --- /dev/null +++ b/commands/mod/setLog.js @@ -0,0 +1,42 @@ +const { SlashCommandBuilder, PermissionFlagsBits } = require("discord.js"); +const { loadLogConfig, saveLogConfig } = require("../../logger/logConfig"); + +module.exports = { + data: new SlashCommandBuilder() + .setName("setlog") + .setDescription("Set a log channel for a specific log type") + .setDefaultMemberPermissions(PermissionFlagsBits.Administrator) + .addStringOption((option) => + option + .setName("type") + .setDescription("Which log type to configure") + .setRequired(true) + .addChoices( + { name: "Message Logs", value: "message" }, + { name: "User Updates", value: "userUpdate" }, + { name: "Join/Leave", value: "joinLeave" }, + { name: "Role Changes", value: "roles" }, + { name: "Boosting", value: "boost" } + ) + ) + .addChannelOption((option) => + option + .setName("channel") + .setDescription("The channel to send logs to") + .setRequired(true) + ), + + async execute(interaction) { + const type = interaction.options.getString("type"); + const channel = interaction.options.getChannel("channel"); + + const config = loadLogConfig(); + config[type] = channel.id; + saveLogConfig(config); + + await interaction.reply({ + content: `Log channel for **${type}** set to ${channel}`, + flags: 64, + }); + }, +}; diff --git a/commands/random/choose.js b/commands/random/choose.js index c22a608..cb4ffc2 100644 --- a/commands/random/choose.js +++ b/commands/random/choose.js @@ -1,4 +1,4 @@ -const { SlashCommandBuilder } = require("@discordjs/builders"); +const { SlashCommandBuilder } = require("discord.js"); module.exports = { data: new SlashCommandBuilder() diff --git a/commands/random/coinflip.js b/commands/random/coinflip.js index dce777f..476692b 100644 --- a/commands/random/coinflip.js +++ b/commands/random/coinflip.js @@ -1,4 +1,4 @@ -const { SlashCommandBuilder } = require("@discordjs/builders"); +const { SlashCommandBuilder } = require("discord.js"); module.exports = { data: new SlashCommandBuilder() diff --git a/commands/utility/ping.js b/commands/utility/ping.js index ac9572c..e7ba5fa 100644 --- a/commands/utility/ping.js +++ b/commands/utility/ping.js @@ -1,4 +1,4 @@ -const { SlashCommandBuilder } = require("@discordjs/builders"); +const { SlashCommandBuilder } = require("discord.js"); module.exports = { data: new SlashCommandBuilder() diff --git a/events/ready.js b/events/clientReady.js similarity index 76% rename from events/ready.js rename to events/clientReady.js index 67b808e..3860ed0 100644 --- a/events/ready.js +++ b/events/clientReady.js @@ -1,11 +1,9 @@ module.exports = { - name: "ready", + name: "clientReady", once: true, execute(client) { const { PresenceUpdateStatus } = require("discord.js"); - console.log(`Ready! Logged in as ${client.user.tag}`); - client.user.setPresence({ activities: [{ name: "Dragon Age" }], status: PresenceUpdateStatus.Online, diff --git a/index.js b/index.js index 994a755..62bc6a7 100644 --- a/index.js +++ b/index.js @@ -2,15 +2,39 @@ require("dotenv").config(); const fs = require("node:fs"); const path = require("node:path"); -const { Client, Collection, GatewayIntentBits } = require("discord.js"); +const { + Client, + Collection, + GatewayIntentBits, + Events, + Partials, + Options, +} = require("discord.js"); +const { + logMessageEdit, + logMessageDelete, + logUserUpdate, + logUserJoin, + logUserLeave, + logUserUpdateFromUserEvent, + cacheInvites, + setupLogger, +} = require("./logger/index.js"); const client = new Client({ intents: [ GatewayIntentBits.Guilds, + GatewayIntentBits.GuildMembers, GatewayIntentBits.GuildMessages, + GatewayIntentBits.MessageContent, GatewayIntentBits.GuildMessageReactions, GatewayIntentBits.GuildPresences, + GatewayIntentBits.GuildInvites, // Required for invite tracking ], + makeCache: Options.cacheWithLimits({ + MessageManager: 10000, + }), + partials: [Partials.Message, Partials.Channel, Partials.Reaction], }); client.commands = new Collection(); @@ -26,7 +50,6 @@ for (const folder of commandFolders) { for (const file of commandFiles) { const filePath = path.join(commandsPath, file); const command = require(filePath); - // Set a new item in the Collection with the key as the command name and the value as the exported module if ("data" in command && "execute" in command) { client.commands.set(command.data.name, command); } else { @@ -65,9 +88,49 @@ client.on("interactionCreate", async (interaction) => { console.error(error); await interaction.reply({ content: "There was an error while executing this command!", - ephemeral: true, + flags: 64, }); } }); +// Cache invites when bot joins a new guild +client.on("guildCreate", async (guild) => { + await cacheInvites(guild); +}); + +// Cache invites when bot starts up +client.on(Events.ClientReady, async (readyClient) => { + console.log(`Ready! Logged in as ${readyClient.user.tag}`); + setupLogger(client); + + // Cache invites for all guilds + for (const guild of client.guilds.cache.values()) { + await cacheInvites(guild); + } +}); + +client.on("guildMemberAdd", (member) => { + logUserJoin(member); +}); + +client.on("guildMemberRemove", (member) => { + logUserLeave(member); +}); + +client.on("guildMemberUpdate", (oldMember, newMember) => { + logUserUpdate(oldMember, newMember); +}); + +client.on("userUpdate", (oldUser, newUser) => { + logUserUpdateFromUserEvent(oldUser, newUser); +}); + +client.on("messageUpdate", (oldMessage, newMessage) => { + logMessageEdit(oldMessage, newMessage); +}); + +client.on("messageDelete", (message) => { + logMessageDelete(message); +}); + client.login(process.env.TOKEN); diff --git a/logger/embeds/bulkDeleteEmbed.js b/logger/embeds/bulkDeleteEmbed.js new file mode 100644 index 0000000..c65b13b --- /dev/null +++ b/logger/embeds/bulkDeleteEmbed.js @@ -0,0 +1,86 @@ +const { EmbedBuilder, AttachmentBuilder } = require("discord.js"); + +function buildBulkDeleteEmbed( + interaction, + deletedMessages, + targetUser, + reason, + fullLogContent +) { + const deleter = interaction.user; + const channel = interaction.channel; + + const authorCounts = new Map(); + for (const msg of deletedMessages.values()) { + const count = authorCounts.get(msg.author.tag) || 0; + authorCounts.set(msg.author.tag, count + 1); + } + + const authorSummary = [...authorCounts.entries()] + .map(([tag, count]) => `${tag}: ${count}`) + .join("\n"); + + const embed = new EmbedBuilder() + .setColor("DarkPurple") + .setTitle("Bulk Message Delete") + .setAuthor({ + name: deleter.tag, + iconURL: deleter.displayAvatarURL(), + }) + .setDescription( + `**${deletedMessages.size}** messages deleted in ${channel}` + ) + .addFields( + { + name: "Deleted by", + value: `${deleter} (${deleter.id})`, + inline: true, + }, + { + name: "Channel", + value: `<#${channel.id}>`, + inline: true, + }, + { + name: "Reason", + value: reason, + inline: true, + } + ) + .setFooter({ text: `User ID: ${deleter.id}` }) + .setTimestamp(); + + if (targetUser) { + embed.addFields({ + name: "Filter", + value: `Only messages from ${targetUser.tag} (${targetUser.id})`, + inline: false, + }); + } + + embed.addFields({ + name: "Message Breakdown", + value: authorSummary.substring(0, 1024) || "No data", + }); + + const files = []; + + if (fullLogContent) { + const logBuffer = Buffer.from(fullLogContent, "utf-8"); + const timestamp = new Date().toISOString().replace(/[:.]/g, "-"); + files.push( + new AttachmentBuilder(logBuffer, { + name: `deleted-messages-${timestamp}.txt`, + }) + ); + + embed.addFields({ + name: "Full Log", + value: "See attached file for complete message content", + }); + } + + return { embed, files }; +} + +module.exports = buildBulkDeleteEmbed; diff --git a/logger/embeds/deleteEmbed.js b/logger/embeds/deleteEmbed.js new file mode 100644 index 0000000..2b16cd6 --- /dev/null +++ b/logger/embeds/deleteEmbed.js @@ -0,0 +1,99 @@ +const { EmbedBuilder } = require("discord.js"); + +async function buildDeleteEmbed(message, oldData, deleteType, executor) { + const authorName = oldData.author?.username || "Unknown"; + const authorAvatar = oldData.author?.avatar || null; + const authorId = oldData.author?.id || "Unknown"; + const authorProfile = oldData.author + ? `https://discord.com/users/${authorId}` + : null; + + const createdTimestamp = oldData.createdTimestamp + ? new Date(oldData.createdTimestamp).toLocaleString() + : "Unknown"; + + // Color based on delete type + const colors = { + self: "Red", // Self-delete + mod: "DarkRed", // Moderator deleted + bot: "Purple", // Bot command deleted + }; + + const titles = { + self: "Message Deleted (Self)", + mod: "Message Deleted by Moderator", + bot: "Message Deleted by Bot", + }; + + const embed = new EmbedBuilder() + .setAuthor({ name: authorName, iconURL: authorAvatar }) + .setTitle(titles[deleteType] || titles.self) + .setColor(colors[deleteType] || colors.self) + .setDescription(`Message ID: ${oldData.messageId || message.id}`) + .addFields( + { + name: "Content", + value: oldData.content || "No content", + inline: false, + }, + { + name: "Author", + value: oldData.author ? `[${authorName}](${authorProfile})` : "Unknown", + inline: false, + }, + { name: "Created At", value: createdTimestamp, inline: false } + ) + .setFooter({ text: `User ID: ${authorId}` }) + .setTimestamp(); + + // Add images + if (oldData.images?.length > 0) { + oldData.images.forEach((url, index) => { + embed.addFields({ name: `Image ${index + 1}`, value: `[Image](${url})` }); + }); + embed.setImage(oldData.images[0]); + } + + // Add embed-data if present + if (oldData.embeds?.length > 0) { + oldData.embeds.forEach((e, index) => { + const embedContent = ` + **Title:** ${e.title || "No title"} + **Description:** ${e.description || "No description"} + **Fields:** ${e.fields?.length > 0 ? e.fields.map((f) => `${f.name}: ${f.value}`).join("\n") : "No fields"} + **Footer:** ${e.footer || "No footer"} + **URL:** ${e.url || "No URL"} + **Color:** ${e.color || "No color"} + **Image:** ${e.image || "No image"} + **Thumbnail:** ${e.thumbnail || "No thumbnail"} + `; + + embed.addFields({ + name: `Embed ${index + 1}`, + value: embedContent, + }); + }); + } + + // Add deletion info based on type + if (deleteType === "self") { + embed.addFields({ + name: "Deleted by", + value: "User (self-delete)", + }); + } else if (deleteType === "bot") { + embed.addFields({ + name: "Deleted by", + value: "Bot (via command)", + }); + } else if (deleteType === "mod" && executor) { + embed.addFields({ + name: "Deleted by", + value: `Moderator: ${executor.tag} (${executor.id})`, + }); + } + + return embed; +} + +module.exports = buildDeleteEmbed; diff --git a/logger/embeds/editEmbed.js b/logger/embeds/editEmbed.js new file mode 100644 index 0000000..c5a7aef --- /dev/null +++ b/logger/embeds/editEmbed.js @@ -0,0 +1,59 @@ +const { EmbedBuilder } = require("discord.js"); + +function buildEditEmbed(oldData, newMessage) { + const embed = new EmbedBuilder() + .setAuthor({ + name: oldData.author?.username || "Unknown", + iconURL: oldData.author?.avatar || null, + }) + .setTitle(`Message Edited in <#${oldData.channelId}>`) + .setColor("Orange") + .setDescription( + `[Jump to Message](https://discord.com/channels/${oldData.guildId}/${oldData.channelId}/${oldData.messageId})` + ) + .setFooter({ text: `User ID: ${oldData.author?.id}` }) + .setTimestamp(); + + // Build Before field with content and/or image links + let beforeValue = oldData.content || "No content"; + + // Add image links if images were present + if (oldData.images?.length > 0) { + const imageLinks = oldData.images + .map((url) => `[Image](${url})`) + .join(", "); + beforeValue = + beforeValue === "No content" + ? imageLinks + : `${beforeValue}\n${imageLinks}`; + } + + // Build After field + let afterValue = newMessage.content || "No content"; + + // Check if new message still has attachments + if (newMessage.attachments?.size > 0) { + const newImageLinks = [...newMessage.attachments.values()] + .map((att) => `[Image](${att.url})`) + .join(", "); + afterValue = + afterValue === "No content" + ? newImageLinks + : `${afterValue}\n${newImageLinks}`; + } + + embed.addFields( + { name: "Before", value: beforeValue.substring(0, 1024), inline: false }, + { name: "After", value: afterValue.substring(0, 1024), inline: false }, + { + name: "Author", + value: oldData.author + ? `[${oldData.author.username}](https://discord.com/users/${oldData.author.id})` + : "Unknown", + } + ); + + return embed; +} + +module.exports = buildEditEmbed; diff --git a/logger/embeds/joinEmbed.js b/logger/embeds/joinEmbed.js new file mode 100644 index 0000000..31fb784 --- /dev/null +++ b/logger/embeds/joinEmbed.js @@ -0,0 +1,40 @@ +const { EmbedBuilder } = require("discord.js"); + +function buildJoinEmbed(member, usedInvite) { + const embed = new EmbedBuilder() + .setColor("Green") + .setTitle("Member Joined") + .setAuthor({ + name: member.user.username, + iconURL: member.user.displayAvatarURL(), + }) + .setDescription(`${member} joined the server`) + .addFields( + { name: "User ID", value: member.id }, + { + name: "Account Created", + value: ``, + } + ) + .setThumbnail(member.user.displayAvatarURL({ size: 1024 })) + .setTimestamp(); + + if (usedInvite) { + const inviter = usedInvite.inviter; + embed.addFields({ + name: "Invite Used", + value: inviter + ? `discord.gg/${usedInvite.code} (created by ${inviter.tag})` + : `discord.gg/${usedInvite.code} (vanity/unknown)`, + }); + } else { + embed.addFields({ + name: "Invite Used", + value: "Unable to determine (missing permissions or no invite data)", + }); + } + + return embed; +} + +module.exports = buildJoinEmbed; diff --git a/logger/embeds/leaveEmbed.js b/logger/embeds/leaveEmbed.js new file mode 100644 index 0000000..78f7bbf --- /dev/null +++ b/logger/embeds/leaveEmbed.js @@ -0,0 +1,72 @@ +const { EmbedBuilder } = require("discord.js"); + +function buildLeaveEmbed(member, leaveType, executor, reason) { + const colors = { + leave: "Red", + kick: "Orange", + ban: "DarkRed", + }; + + const titles = { + leave: "Member Left", + kick: "Member Kicked", + ban: "Member Banned", + }; + + const descriptions = { + leave: `<@${member.user.id}> left the server`, + kick: `<@${member.user.id}> was kicked from the server`, + ban: `<@${member.user.id}> was banned from the server`, + }; + + const embed = new EmbedBuilder() + .setColor(colors[leaveType] || "Red") + .setTitle(titles[leaveType] || "Member Left") + .setAuthor({ + name: member.user.username, + iconURL: member.user.displayAvatarURL(), + }) + .setDescription(descriptions[leaveType] || descriptions.leave) + .addFields( + { name: "User ID", value: member.id }, + { + name: "Roles", + value: + member.roles.cache.size > 0 + ? member.roles.cache.map((r) => r.toString()).join(", ") + : "No roles", + }, + { + name: "Joined Server", + value: member.joinedTimestamp + ? `` + : "Unknown", + } + ) + .setThumbnail(member.user.displayAvatarURL({ size: 1024 })) + .setTimestamp(); + + if (executor && (leaveType === "kick" || leaveType === "ban")) { + embed.addFields({ + name: leaveType === "ban" ? "Banned by" : "Kicked by", + value: `${executor.tag} (${executor.id})`, + }); + } + + // Add reason if available + if (reason) { + embed.addFields({ + name: "Reason", + value: reason, + }); + } else if (leaveType === "kick" || leaveType === "ban") { + embed.addFields({ + name: "Reason", + value: "*No reason provided*", + }); + } + + return embed; +} + +module.exports = buildLeaveEmbed; diff --git a/logger/embeds/userUpdateEmbed.js b/logger/embeds/userUpdateEmbed.js new file mode 100644 index 0000000..7ada384 --- /dev/null +++ b/logger/embeds/userUpdateEmbed.js @@ -0,0 +1,122 @@ +const { EmbedBuilder } = require("discord.js"); + +function buildUserUpdateEmbeds(oldMember, newMember) { + const embeds = []; + + // BOOSTING / UNBOOSTING + if (!oldMember.premiumSince && newMember.premiumSince) { + embeds.push( + new EmbedBuilder() + .setColor("Fuchsia") + .setTitle("Server Boosted") + .setAuthor({ + name: newMember.user.username, + iconURL: newMember.user.displayAvatarURL(), + }) + .setDescription(`${newMember} has boosted the server!`) + .setFooter({ text: `User ID: ${newMember.id}` }) + .setTimestamp() + ); + } + + if (oldMember.premiumSince && !newMember.premiumSince) { + embeds.push( + new EmbedBuilder() + .setColor("Grey") + .setTitle("Boost Removed") + .setAuthor({ + name: newMember.user.username, + iconURL: newMember.user.displayAvatarURL(), + }) + .setDescription(`${newMember} has stopped boosting the server.`) + .setFooter({ text: `User ID: ${newMember.id}` }) + .setTimestamp() + ); + } + + // USERNAME CHANGE + if (oldMember.user.username !== newMember.user.username) { + embeds.push( + new EmbedBuilder() + .setColor("Blue") + .setTitle("Username Changed") + .setAuthor({ + name: newMember.user.username, + iconURL: newMember.user.displayAvatarURL(), + }) + .addFields( + { name: "Before", value: oldMember.user.username }, + { name: "After", value: newMember.user.username } + ) + .setFooter({ text: `User ID: ${newMember.id}` }) + .setTimestamp() + ); + } + + // AVATAR CHANGE + if (oldMember.user.displayAvatarURL() !== newMember.user.displayAvatarURL()) { + embeds.push( + new EmbedBuilder() + .setColor("Green") + .setTitle("Avatar Changed") + .setAuthor({ + name: newMember.user.username, + iconURL: newMember.user.displayAvatarURL(), + }) + .addFields( + { name: "Before", value: oldMember.user.displayAvatarURL() }, + { name: "After", value: newMember.user.displayAvatarURL() } + ) + .setImage(newMember.user.displayAvatarURL({ size: 1024 })) + .setFooter({ text: `User ID: ${newMember.id}` }) + .setTimestamp() + ); + } + + // ROLE CHANGES + const oldRoles = oldMember.roles.cache; + const newRoles = newMember.roles.cache; + + const addedRoles = newRoles.filter((r) => !oldRoles.has(r.id)); + const removedRoles = oldRoles.filter((r) => !newRoles.has(r.id)); + + if (addedRoles.size > 0) { + embeds.push( + new EmbedBuilder() + .setColor("Gold") + .setTitle("Roles Added") + .setAuthor({ + name: newMember.user.username, + iconURL: newMember.user.displayAvatarURL(), + }) + .addFields({ + name: "Added", + value: addedRoles.map((r) => r.toString()).join(", "), + }) + .setFooter({ text: `User ID: ${newMember.id}` }) + .setTimestamp() + ); + } + + if (removedRoles.size > 0) { + embeds.push( + new EmbedBuilder() + .setColor("DarkRed") + .setTitle("Roles Removed") + .setAuthor({ + name: newMember.user.username, + iconURL: newMember.user.displayAvatarURL(), + }) + .addFields({ + name: "Removed", + value: removedRoles.map((r) => r.toString()).join(", "), + }) + .setFooter({ text: `User ID: ${newMember.id}` }) + .setTimestamp() + ); + } + + return embeds; +} + +module.exports = buildUserUpdateEmbeds; diff --git a/logger/extractMessage.js b/logger/extractMessage.js new file mode 100644 index 0000000..e4f5fce --- /dev/null +++ b/logger/extractMessage.js @@ -0,0 +1,32 @@ +function extractMessageData(message) { + return { + messageId: message.id, + author: message.author ? { + id: message.author.id, + username: message.author.username, + avatar: message.author.displayAvatarURL(), + } : null, + content: message.content || null, + images: [...message.attachments.values()].map(a => a.url), + embeds: message.embeds.map(embed => ({ + title: embed.title, + description: embed.description, + fields: embed.fields.map(f => ({ + name: f.name, + value: f.value, + inline: f.inline + })), + footer: embed.footer?.text || null, + timestamp: embed.timestamp, + url: embed.url, + color: embed.color, + image: embed.image?.url || null, + thumbnail: embed.thumbnail?.url || null, + })), + createdTimestamp: message.createdTimestamp, + channelId: message.channelId, + guildId: message.guildId, + }; +} + +module.exports = extractMessageData; \ No newline at end of file diff --git a/logger/getLogChannel.js b/logger/getLogChannel.js new file mode 100644 index 0000000..1894991 --- /dev/null +++ b/logger/getLogChannel.js @@ -0,0 +1,10 @@ +const { loadLogConfig } = require('./logConfig'); + +function getLogChannel(guild, type) { + const config = loadLogConfig(); + const channelId = config[type]; + if (!channelId) return null; + return guild.channels.cache.get(channelId) || null; +} + +module.exports = getLogChannel; \ No newline at end of file diff --git a/logger/index.js b/logger/index.js new file mode 100644 index 0000000..93223b0 --- /dev/null +++ b/logger/index.js @@ -0,0 +1,222 @@ +require("dotenv").config(); +const { AttachmentBuilder } = require("discord.js"); +const extractMessageData = require("./extractMessage.js"); +const buildEditEmbed = require("./embeds/editEmbed.js"); +const buildDeleteEmbed = require("./embeds/deleteEmbed.js"); +const buildBulkDeleteEmbed = require("./embeds/bulkDeleteEmbed.js"); +const getLogChannel = require("./getLogChannel.js"); + +// Track recently bulk-deleted message IDs to prevent double-logging +const recentlyBulkDeleted = new Map(); + +async function fetchImageBuffer(url) { + try { + const response = await fetch(url); + if (!response.ok) return null; + return Buffer.from(await response.arrayBuffer()); + } catch (error) { + console.error("Failed to fetch image:", error); + return null; + } +} + +async function logMessageEdit(oldMessage, newMessage) { + if (oldMessage.partial) return; + + const contentChanged = oldMessage.content !== newMessage.content; + const attachmentsChanged = + oldMessage.attachments.size !== newMessage.attachments.size; + + if (!contentChanged && !attachmentsChanged) return; + + const logChannel = getLogChannel(oldMessage.guild, "message"); + if (!logChannel) return; + + try { + const embed = buildEditEmbed(extractMessageData(oldMessage), newMessage); + + const files = []; + for (const attachment of oldMessage.attachments.values()) { + if (attachment.contentType?.startsWith("image/")) { + const buffer = await fetchImageBuffer(attachment.url); + if (buffer) { + files.push(new AttachmentBuilder(buffer, { name: attachment.name })); + } + } + } + + await logChannel.send({ embeds: [embed], files }); + } catch (error) { + console.error("Failed to send edit log:", error); + } +} + +async function logMessageDelete(message) { + if (message.partial) return; + if ( + !message.content && + message.attachments.size === 0 && + message.embeds.length === 0 + ) + return; + + // Check if this was part of a bulk delete (bot command) + const bulkDeleteInfo = recentlyBulkDeleted.get(message.id); + if (bulkDeleteInfo) { + recentlyBulkDeleted.delete(message.id); + return; + } + + const logChannel = getLogChannel(message.guild, "message"); + if (!logChannel) return; + + try { + // Fetch audit log to find who deleted this message + let executor = null; + let deleteType = "self"; // default: self-delete + + try { + const auditLogs = await message.guild.fetchAuditLogs({ + limit: 1, + type: 72, + }); + const deleteLog = auditLogs.entries.first(); + + if (deleteLog && deleteLog.target.id === message.author.id) { + const logTime = deleteLog.createdTimestamp; + const now = Date.now(); + + if (now - logTime < 5000) { + executor = deleteLog.executor; + + if (executor.id === message.client.user.id) { + deleteType = "bot"; + } else { + deleteType = "mod"; + } + } + } + } catch (auditError) { + console.error( + "Failed to fetch audit log for delete:", + auditError.message + ); + } + + const oldData = extractMessageData(message); + const embed = await buildDeleteEmbed( + message, + oldData, + deleteType, + executor + ); + + const files = []; + for (const attachment of message.attachments.values()) { + if (attachment.contentType?.startsWith("image/")) { + const buffer = await fetchImageBuffer(attachment.url); + if (buffer) { + files.push(new AttachmentBuilder(buffer, { name: attachment.name })); + } + } + } + + await logChannel.send({ embeds: [embed], files }); + } catch (error) { + console.error("Failed to send delete log:", error); + } +} + +async function logBulkDelete(interaction, deletedMessages, targetUser, reason) { + const logChannel = getLogChannel(interaction.guild, "message"); + if (!logChannel) return; + + const botId = interaction.client.user.id; + + // Mark all these messages as bulk-deleted with bot executor info + for (const id of deletedMessages.keys()) { + recentlyBulkDeleted.set(id, { + executorId: botId, + timestamp: Date.now(), + }); + } + + // Clear from map after 10 seconds + setTimeout(() => { + for (const id of deletedMessages.keys()) { + recentlyBulkDeleted.delete(id); + } + }, 10000); + + try { + const fullLogLines = []; + + for (const [id, msg] of deletedMessages) { + const timestamp = new Date(msg.createdTimestamp).toISOString(); + const content = msg.content || "[No text content]"; + const attachments = + msg.attachments.size > 0 + ? [...msg.attachments.values()].map((a) => a.url).join(", ") + : "None"; + + fullLogLines.push(`[${timestamp}] ${msg.author.tag} (${msg.author.id}):`); + fullLogLines.push(`Content: ${content}`); + fullLogLines.push(`Attachments: ${attachments}`); + fullLogLines.push(`Message ID: ${id}`); + fullLogLines.push("---"); + } + + const fullLogContent = fullLogLines.join("\n"); + + // Collect images (up to Discord limit of 10) + const imageFiles = []; + for (const message of deletedMessages.values()) { + for (const attachment of message.attachments.values()) { + if ( + attachment.contentType?.startsWith("image/") && + imageFiles.length < 10 + ) { + const buffer = await fetchImageBuffer(attachment.url); + if (buffer) { + imageFiles.push( + new AttachmentBuilder(buffer, { name: attachment.name }) + ); + } + } + } + } + + const { embed, files } = buildBulkDeleteEmbed( + interaction, + deletedMessages, + targetUser, + reason, + fullLogContent + ); + + const allFiles = [...imageFiles, ...files]; + + await logChannel.send({ embeds: [embed], files: allFiles }); + } catch (error) { + console.error("Failed to log bulk delete:", error); + } +} + +let setupLogger; +try { + setupLogger = require("./setupLogger.js"); +} catch (err) { + setupLogger = () => console.log("Logger setup skipped"); +} + +module.exports = { + logMessageEdit, + logMessageDelete, + logBulkDelete, + logUserUpdate: require("./logUserUpdate.js"), + logUserJoin: require("./logUserJoinLeave.js").logUserJoin, + logUserLeave: require("./logUserJoinLeave.js").logUserLeave, + cacheInvites: require("./logUserJoinLeave.js").cacheInvites, + logUserUpdateFromUserEvent: require("./logUserUpdateFromUserEvent.js"), + setupLogger, +}; diff --git a/logger/logConfig.js b/logger/logConfig.js new file mode 100644 index 0000000..12670a8 --- /dev/null +++ b/logger/logConfig.js @@ -0,0 +1,14 @@ +const fs = require("fs"); +const path = require("path"); + +const filePath = path.join(__dirname, "logConfig.json"); + +function loadLogConfig() { + return JSON.parse(fs.readFileSync(filePath, "utf8")); +} + +function saveLogConfig(config) { + fs.writeFileSync(filePath, JSON.stringify(config, null, 2)); +} + +module.exports = { loadLogConfig, saveLogConfig }; diff --git a/logger/logConfig.json b/logger/logConfig.json new file mode 100644 index 0000000..ce1c0e7 --- /dev/null +++ b/logger/logConfig.json @@ -0,0 +1,7 @@ +{ + "message": "1341874229951467540", + "userUpdate": "1219971610916950037", + "joinLeave": "1341874229951467540", + "roles": "1341874229951467540", + "boost": "1341874229951467540" +} \ No newline at end of file diff --git a/logger/logUserJoinLeave.js b/logger/logUserJoinLeave.js new file mode 100644 index 0000000..f689ba3 --- /dev/null +++ b/logger/logUserJoinLeave.js @@ -0,0 +1,126 @@ +const buildJoinEmbed = require("./embeds/joinEmbed.js"); +const buildLeaveEmbed = require("./embeds/leaveEmbed.js"); +const getLogChannel = require("./getLogChannel.js"); + +const inviteCache = new Map(); + +async function cacheInvites(guild) { + const botMember = guild.members.me; + if (!botMember.permissions.has("ManageGuild")) { + console.warn( + `⚠️ Missing "Manage Server" permission in ${guild.name} — invite tracking disabled` + ); + return; + } + + try { + const invites = await guild.invites.fetch(); + inviteCache.set( + guild.id, + new Map(invites.map((inv) => [inv.code, inv.uses])) + ); + console.log(`✅ Cached ${invites.size} invites for ${guild.name}`); + } catch (error) { + if (error.code === 50013) { + console.warn(`⚠️ Missing permissions to fetch invites in ${guild.name}`); + } else { + console.error( + `Failed to cache invites for ${guild.name}:`, + error.message + ); + } + } +} + +function getInviteCache(guildId) { + return inviteCache.get(guildId) || new Map(); +} + +async function logUserJoin(member) { + const logChannel = getLogChannel(member.guild, "joinLeave"); + if (!logChannel) return; + + let usedInvite = null; + + if (inviteCache.has(member.guild.id)) { + try { + const newInvites = await member.guild.invites.fetch(); + const oldInvites = getInviteCache(member.guild.id); + + for (const [code, invite] of newInvites) { + const oldUses = oldInvites.get(code) || 0; + if (invite.uses > oldUses) { + usedInvite = invite; + break; + } + } + + inviteCache.set( + member.guild.id, + new Map(newInvites.map((inv) => [inv.code, inv.uses])) + ); + } catch (error) { + console.error("Failed to track invite:", error.message); + } + } + + const embed = buildJoinEmbed(member, usedInvite); + logChannel.send({ embeds: [embed] }); +} + +async function logUserLeave(member) { + const logChannel = getLogChannel(member.guild, "joinLeave"); + if (!logChannel) return; + + let leaveType = "leave"; + let executor = null; + let reason = null; + + try { + const banLogs = await member.guild.fetchAuditLogs({ + limit: 1, + type: 22, + }); + const banLog = banLogs.entries.first(); + + if (banLog && banLog.target.id === member.id) { + const logTime = banLog.createdTimestamp; + const now = Date.now(); + if (now - logTime < 5000) { + leaveType = "ban"; + executor = banLog.executor; + reason = banLog.reason; + } + } + + if (leaveType === "leave") { + const kickLogs = await member.guild.fetchAuditLogs({ + limit: 1, + type: 20, + }); + const kickLog = kickLogs.entries.first(); + + if (kickLog && kickLog.target.id === member.id) { + const logTime = kickLog.createdTimestamp; + const now = Date.now(); + if (now - logTime < 5000) { + leaveType = "kick"; + executor = kickLog.executor; + reason = kickLog.reason; + } + } + } + } catch (error) { + console.error("Failed to fetch audit logs:", error.message); + } + + const embed = buildLeaveEmbed(member, leaveType, executor, reason); + logChannel.send({ embeds: [embed] }); +} + +module.exports = { + logUserJoin, + logUserLeave, + cacheInvites, + getInviteCache, +}; diff --git a/logger/logUserUpdate.js b/logger/logUserUpdate.js new file mode 100644 index 0000000..8795d61 --- /dev/null +++ b/logger/logUserUpdate.js @@ -0,0 +1,15 @@ +const buildUserUpdateEmbeds = require('./embeds/userUpdateEmbed'); +const getLogChannel = require('./getLogChannel'); + +async function logUserUpdate(oldMember, newMember) { + const logChannel = getLogChannel(newMember.guild, "userUpdate"); + if (!logChannel) return; + + const embeds = buildUserUpdateEmbeds(oldMember, newMember); + + for (const embed of embeds) { + logChannel.send({ embeds: [embed] }); + } +} + +module.exports = logUserUpdate; \ No newline at end of file diff --git a/logger/logUserUpdateFromUserEvent.js b/logger/logUserUpdateFromUserEvent.js new file mode 100644 index 0000000..2bb179a --- /dev/null +++ b/logger/logUserUpdateFromUserEvent.js @@ -0,0 +1,54 @@ +const { EmbedBuilder } = require('discord.js'); +const getLogChannel = require('./getLogChannel'); + +function logUserUpdateFromUserEvent(oldUser, newUser) { + // Loop through all guilds the bot is in + newUser.client.guilds.cache.forEach(guild => { + const member = guild.members.cache.get(newUser.id); + if (!member) return; + + const logChannel = getLogChannel(guild, "userUpdate"); + if (!logChannel) return; + + // --- USERNAME CHANGE --- + if (oldUser.username !== newUser.username) { + const embed = new EmbedBuilder() + .setColor('Blue') + .setTitle('Username Changed') + .setAuthor({ + name: newUser.username, + iconURL: newUser.displayAvatarURL() + }) + .addFields( + { name: 'Before', value: oldUser.username }, + { name: 'After', value: newUser.username } + ) + .setFooter({ text: `User ID: ${newUser.id}` }) + .setTimestamp(); + + logChannel.send({ embeds: [embed] }); + } + + // --- AVATAR CHANGE --- + if (oldUser.displayAvatarURL() !== newUser.displayAvatarURL()) { + const embed = new EmbedBuilder() + .setColor('Green') + .setTitle('Avatar Changed') + .setAuthor({ + name: newUser.username, + iconURL: newUser.displayAvatarURL() + }) + .addFields( + { name: 'Before', value: oldUser.displayAvatarURL() }, + { name: 'After', value: newUser.displayAvatarURL() } + ) + .setImage(newUser.displayAvatarURL({ size: 1024 })) + .setFooter({ text: `User ID: ${newUser.id}` }) + .setTimestamp(); + + logChannel.send({ embeds: [embed] }); + } + }); +} + +module.exports = logUserUpdateFromUserEvent; diff --git a/logger/setupLogger.js b/logger/setupLogger.js new file mode 100644 index 0000000..6717014 --- /dev/null +++ b/logger/setupLogger.js @@ -0,0 +1,21 @@ +function setupLogger(client) { + const channelId = process.env.LOG_CHANNEL; + + console.log("🟡 Logger starting..."); + + if (!channelId) { + console.warn("⚠️ LOG_CHANNEL is missing in .env"); + return; + } + + const channel = client.channels.cache.get(channelId); + + if (!channel) { + console.warn(`⚠️ Logger could not find channel with ID ${channelId}`); + return; + } + + console.log(`🟢 Logger active. Logging to #${channel.name} (${channelId})`); +} + +module.exports = setupLogger; diff --git a/package-lock.json b/package-lock.json index fdd3874..d9e962d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,12 +9,11 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@discordjs/builders": "^1.10.1", - "@discordjs/rest": "^2.4.3", "@eslint/js": "^9.20.0", + "better-sqlite3": "^12.6.2", "dayjs": "^1.11.13", "discord-api-types": "^0.37.119", - "discord.js": "^14.18.0", + "discord.js": "^14.25.1", "dotenv": "^16.4.7" }, "devDependencies": { @@ -24,15 +23,14 @@ } }, "node_modules/@discordjs/builders": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.10.1.tgz", - "integrity": "sha512-OWo1fY4ztL1/M/DUyRPShB4d/EzVfuUvPTRRHRIt/YxBrUYSz0a+JicD5F5zHFoNs2oTuWavxCOVFV1UljHTng==", - "license": "Apache-2.0", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.13.1.tgz", + "integrity": "sha512-cOU0UDHc3lp/5nKByDxkmRiNZBpdp0kx55aarbiAfakfKJHlxv/yFW1zmIqCAmwH5CRlrH9iMFKJMpvW4DPB+w==", "dependencies": { - "@discordjs/formatters": "^0.6.0", - "@discordjs/util": "^1.1.1", + "@discordjs/formatters": "^0.6.2", + "@discordjs/util": "^1.2.0", "@sapphire/shapeshift": "^4.0.0", - "discord-api-types": "^0.37.119", + "discord-api-types": "^0.38.33", "fast-deep-equal": "^3.1.3", "ts-mixer": "^6.0.4", "tslib": "^2.6.3" @@ -44,25 +42,25 @@ "url": "https://github.com/discordjs/discord.js?sponsor" } }, + "node_modules/@discordjs/builders/node_modules/discord-api-types": { + "version": "0.38.41", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.41.tgz", + "integrity": "sha512-yMECyR8j9c2fVTvCQ+Qc24pweYFIZk/XoxDOmt1UvPeSw5tK6gXBd/2hhP+FEAe9Y6ny8pRMaf618XDK4U53OQ==" + }, "node_modules/@discordjs/collection": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz", - "integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==", - "license": "Apache-2.0", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz", + "integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==", "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/discordjs/discord.js?sponsor" + "node": ">=16.11.0" } }, "node_modules/@discordjs/formatters": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.6.0.tgz", - "integrity": "sha512-YIruKw4UILt/ivO4uISmrGq2GdMY6EkoTtD0oS0GvkJFRZbTSdPhzYiUILbJ/QslsvC9H9nTgGgnarnIl4jMfw==", - "license": "Apache-2.0", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.6.2.tgz", + "integrity": "sha512-y4UPwWhH6vChKRkGdMB4odasUbHOUwy7KL+OVwF86PvT6QVOwElx+TiI1/6kcmcEe+g5YRXJFiXSXUdabqZOvQ==", "dependencies": { - "discord-api-types": "^0.37.114" + "discord-api-types": "^0.38.33" }, "engines": { "node": ">=16.11.0" @@ -71,22 +69,37 @@ "url": "https://github.com/discordjs/discord.js?sponsor" } }, + "node_modules/@discordjs/formatters/node_modules/discord-api-types": { + "version": "0.38.41", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.41.tgz", + "integrity": "sha512-yMECyR8j9c2fVTvCQ+Qc24pweYFIZk/XoxDOmt1UvPeSw5tK6gXBd/2hhP+FEAe9Y6ny8pRMaf618XDK4U53OQ==" + }, "node_modules/@discordjs/rest": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.4.3.tgz", - "integrity": "sha512-+SO4RKvWsM+y8uFHgYQrcTl/3+cY02uQOH7/7bKbVZsTfrfpoE62o5p+mmV+s7FVhTX82/kQUGGbu4YlV60RtA==", - "license": "Apache-2.0", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.6.0.tgz", + "integrity": "sha512-RDYrhmpB7mTvmCKcpj+pc5k7POKszS4E2O9TYc+U+Y4iaCP+r910QdO43qmpOja8LRr1RJ0b3U+CqVsnPqzf4w==", "dependencies": { "@discordjs/collection": "^2.1.1", "@discordjs/util": "^1.1.1", "@sapphire/async-queue": "^1.5.3", "@sapphire/snowflake": "^3.5.3", "@vladfrangu/async_event_emitter": "^2.4.6", - "discord-api-types": "^0.37.119", + "discord-api-types": "^0.38.16", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", - "undici": "6.21.1" + "undici": "6.21.3" + }, + "engines": { + "node": ">=18" }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } + }, + "node_modules/@discordjs/rest/node_modules/@discordjs/collection": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz", + "integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==", "engines": { "node": ">=18" }, @@ -94,11 +107,18 @@ "url": "https://github.com/discordjs/discord.js?sponsor" } }, + "node_modules/@discordjs/rest/node_modules/discord-api-types": { + "version": "0.38.41", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.41.tgz", + "integrity": "sha512-yMECyR8j9c2fVTvCQ+Qc24pweYFIZk/XoxDOmt1UvPeSw5tK6gXBd/2hhP+FEAe9Y6ny8pRMaf618XDK4U53OQ==" + }, "node_modules/@discordjs/util": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.1.1.tgz", - "integrity": "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g==", - "license": "Apache-2.0", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.2.0.tgz", + "integrity": "sha512-3LKP7F2+atl9vJFhaBjn4nOaSWahZ/yWjOvA4e5pnXkt2qyXRCHLxoBQy81GFtLGCq7K9lPm9R517M1U+/90Qg==", + "dependencies": { + "discord-api-types": "^0.38.33" + }, "engines": { "node": ">=18" }, @@ -106,19 +126,23 @@ "url": "https://github.com/discordjs/discord.js?sponsor" } }, + "node_modules/@discordjs/util/node_modules/discord-api-types": { + "version": "0.38.41", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.41.tgz", + "integrity": "sha512-yMECyR8j9c2fVTvCQ+Qc24pweYFIZk/XoxDOmt1UvPeSw5tK6gXBd/2hhP+FEAe9Y6ny8pRMaf618XDK4U53OQ==" + }, "node_modules/@discordjs/ws": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.2.1.tgz", - "integrity": "sha512-PBvenhZG56a6tMWF/f4P6f4GxZKJTBG95n7aiGSPTnodmz4N5g60t79rSIAq7ywMbv8A4jFtexMruH+oe51aQQ==", - "license": "Apache-2.0", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.2.3.tgz", + "integrity": "sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw==", "dependencies": { "@discordjs/collection": "^2.1.0", - "@discordjs/rest": "^2.4.3", + "@discordjs/rest": "^2.5.1", "@discordjs/util": "^1.1.0", "@sapphire/async-queue": "^1.5.2", "@types/ws": "^8.5.10", "@vladfrangu/async_event_emitter": "^2.2.4", - "discord-api-types": "^0.37.119", + "discord-api-types": "^0.38.1", "tslib": "^2.6.2", "ws": "^8.17.0" }, @@ -129,18 +153,36 @@ "url": "https://github.com/discordjs/discord.js?sponsor" } }, + "node_modules/@discordjs/ws/node_modules/@discordjs/collection": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz", + "integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } + }, + "node_modules/@discordjs/ws/node_modules/discord-api-types": { + "version": "0.38.41", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.41.tgz", + "integrity": "sha512-yMECyR8j9c2fVTvCQ+Qc24pweYFIZk/XoxDOmt1UvPeSw5tK6gXBd/2hhP+FEAe9Y6ny8pRMaf618XDK4U53OQ==" + }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, - "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } @@ -150,7 +192,6 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -169,13 +210,12 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", - "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", "dev": true, - "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.6", + "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -183,12 +223,23 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/core": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.11.0.tgz", - "integrity": "sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" }, @@ -197,20 +248,19 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", - "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.4.tgz", + "integrity": "sha512-4h4MVF8pmBsncB60r0wSJiIeUKTSD4m7FmTFThG8RHlsg9ajqckLm9OraguFGZE4vVdpiI1Q4+hFnisopmG6gQ==", "dev": true, - "license": "MIT", "dependencies": { - "ajv": "^6.12.4", + "ajv": "^6.14.0", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.3", "strip-json-comments": "^3.1.1" }, "engines": { @@ -225,7 +275,6 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=18" }, @@ -234,32 +283,32 @@ } }, "node_modules/@eslint/js": { - "version": "9.20.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.20.0.tgz", - "integrity": "sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ==", - "license": "MIT", + "version": "9.39.3", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.3.tgz", + "integrity": "sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw==", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, - "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.6.tgz", - "integrity": "sha512-+0TjwR1eAUdZtvv/ir1mGX+v0tUoR3VEPB8Up0LLJC+whRW0GgBBtpbOkg/a/U4Dxa6l5a3l9AJ1aWIQVyoWJA==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, - "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.11.0", + "@eslint/core": "^0.17.0", "levn": "^0.4.1" }, "engines": { @@ -319,11 +368,10 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", - "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">=18.18" }, @@ -333,10 +381,9 @@ } }, "node_modules/@sapphire/async-queue": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.3.tgz", - "integrity": "sha512-x7zadcfJGxFka1Q3f8gCts1F0xMwCKbZweM85xECGI0hBTeIZJGGCrHgLggihBoprlQ/hBmDR5LKfIPqnmHM3w==", - "license": "MIT", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.5.tgz", + "integrity": "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg==", "engines": { "node": ">=v14.0.0", "npm": ">=7.0.0" @@ -346,7 +393,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-4.0.0.tgz", "integrity": "sha512-d9dUmWVA7MMiKobL3VpLF8P2aeanRTu6ypG2OIaEv/ZHH/SUQ2iHOVyi5wAPjQ+HmnMuL0whK9ez8I/raWbtIg==", - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "lodash": "^4.17.21" @@ -359,7 +405,6 @@ "version": "3.5.3", "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.3.tgz", "integrity": "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==", - "license": "MIT", "engines": { "node": ">=v14.0.0", "npm": ">=7.0.0" @@ -376,43 +421,38 @@ "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@types/node": { - "version": "22.13.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.4.tgz", - "integrity": "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==", - "license": "MIT", + "version": "25.3.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.5.tgz", + "integrity": "sha512-oX8xrhvpiyRCQkG1MFchB09f+cXftgIXb3a7UUa4Y3wpmZPw5tyZGTLWhlESOLq1Rq6oDlc8npVU2/9xiCuXMA==", "dependencies": { - "undici-types": "~6.20.0" + "undici-types": "~7.18.0" } }, "node_modules/@types/ws": { - "version": "8.5.14", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.14.tgz", - "integrity": "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==", - "license": "MIT", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", "dependencies": { "@types/node": "*" } }, "node_modules/@vladfrangu/async_event_emitter": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.6.tgz", - "integrity": "sha512-RaI5qZo6D2CVS6sTHFKg1v5Ohq/+Bo2LZ5gzUEwZ/WkHhwtGTCB/sVLw8ijOkAUxasZ+WshN/Rzj4ywsABJ5ZA==", - "license": "MIT", + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.7.tgz", + "integrity": "sha512-Xfe6rpCTxSxfbswi/W/Pz7zp1WWSNn4A0eW4mLkQUewCrXXtMj31lCg+iQyTkh/CkusZSq9eDflu7tjEDXUY6g==", "engines": { "node": ">=v14.0.0", "npm": ">=7.0.0" } }, "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, - "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -425,17 +465,15 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -465,33 +503,102 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" + "dev": true }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/better-sqlite3": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.6.2.tgz", + "integrity": "sha512-8VYKM3MjCa9WcaSAI3hzwhmyHVlH8tiGFwf0RlTsZPWJ1I5MkzjiudCo4KC4DxOaL/53A5B1sI/IbldNFDbsKA==", + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + }, + "engines": { + "node": "20.x || 22.x || 23.x || 24.x || 25.x" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } @@ -511,6 +618,11 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, "node_modules/color-convert": { "version": "2.0.1", "dev": true, @@ -531,8 +643,7 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/cross-spawn": { "version": "7.0.6", @@ -556,11 +667,10 @@ "license": "MIT" }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -573,6 +683,28 @@ } } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -580,6 +712,14 @@ "dev": true, "license": "MIT" }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "engines": { + "node": ">=8" + } + }, "node_modules/discord-api-types": { "version": "0.37.119", "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.119.tgz", @@ -587,23 +727,23 @@ "license": "MIT" }, "node_modules/discord.js": { - "version": "14.18.0", - "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.18.0.tgz", - "integrity": "sha512-SvU5kVUvwunQhN2/+0t55QW/1EHfB1lp0TtLZUSXVHDmyHTrdOj5LRKdR0zLcybaA15F+NtdWuWmGOX9lE+CAw==", - "license": "Apache-2.0", + "version": "14.25.1", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.25.1.tgz", + "integrity": "sha512-2l0gsPOLPs5t6GFZfQZKnL1OJNYFcuC/ETWsW4VtKVD/tg4ICa9x+jb9bkPffkMdRpRpuUaO/fKkHCBeiCKh8g==", "dependencies": { - "@discordjs/builders": "^1.10.1", + "@discordjs/builders": "^1.13.0", "@discordjs/collection": "1.5.3", - "@discordjs/formatters": "^0.6.0", - "@discordjs/rest": "^2.4.3", - "@discordjs/util": "^1.1.1", - "@discordjs/ws": "^1.2.1", + "@discordjs/formatters": "^0.6.2", + "@discordjs/rest": "^2.6.0", + "@discordjs/util": "^1.2.0", + "@discordjs/ws": "^1.2.3", "@sapphire/snowflake": "3.5.3", - "discord-api-types": "^0.37.119", + "discord-api-types": "^0.38.33", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", + "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", - "undici": "6.21.1" + "undici": "6.21.3" }, "engines": { "node": ">=18" @@ -612,20 +752,15 @@ "url": "https://github.com/discordjs/discord.js?sponsor" } }, - "node_modules/discord.js/node_modules/@discordjs/collection": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz", - "integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=16.11.0" - } + "node_modules/discord.js/node_modules/discord-api-types": { + "version": "0.38.41", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.41.tgz", + "integrity": "sha512-yMECyR8j9c2fVTvCQ+Qc24pweYFIZk/XoxDOmt1UvPeSw5tK6gXBd/2hhP+FEAe9Y6ny8pRMaf618XDK4U53OQ==" }, "node_modules/dotenv": { "version": "16.4.7", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", - "license": "BSD-2-Clause", "engines": { "node": ">=12" }, @@ -633,6 +768,14 @@ "url": "https://dotenvx.com" } }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "dev": true, @@ -645,32 +788,31 @@ } }, "node_modules/eslint": { - "version": "9.20.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.20.1.tgz", - "integrity": "sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==", + "version": "9.39.3", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.3.tgz", + "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==", "dev": true, - "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.11.0", - "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.20.0", - "@eslint/plugin-kit": "^0.2.5", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.3", + "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.1", + "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.2.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -705,11 +847,10 @@ } }, "node_modules/eslint-scope": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", - "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -722,11 +863,10 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, - "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -735,15 +875,14 @@ } }, "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.14.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -770,7 +909,6 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -798,16 +936,24 @@ "node": ">=0.10.0" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "engines": { + "node": ">=6" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "license": "MIT" + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -829,6 +975,11 @@ "node": ">=16.0.0" } }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -867,6 +1018,16 @@ "dev": true, "license": "ISC" }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" + }, "node_modules/glob-parent": { "version": "6.0.2", "dev": true, @@ -899,12 +1060,30 @@ "node": ">=8" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, - "license": "MIT", "engines": { "node": ">= 4" } @@ -914,7 +1093,6 @@ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, - "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -934,6 +1112,16 @@ "node": ">=0.8.19" } }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, "node_modules/is-extglob": { "version": "2.1.1", "dev": true, @@ -961,11 +1149,10 @@ "license": "ISC" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -984,8 +1171,7 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -1033,10 +1219,9 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==" }, "node_modules/lodash.merge": { "version": "4.6.2", @@ -1046,21 +1231,29 @@ "node_modules/lodash.snakecase": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", - "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", - "license": "MIT" + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" }, "node_modules/magic-bytes.js": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz", - "integrity": "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ==", - "license": "MIT" + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.13.0.tgz", + "integrity": "sha512-afO2mnxW7GDTXMm5/AoN1WuOcdoKhtgXjIvHmobqTD1grNplhGdv3PFOyjCVmrnOZBIT/gD/koDKpYG+0mvHcg==" + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1068,18 +1261,54 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" + "dev": true + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==" }, "node_modules/natural-compare": { "version": "1.4.0", "dev": true, "license": "MIT" }, + "node_modules/node-abi": { + "version": "3.87.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.87.0.tgz", + "integrity": "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -1135,7 +1364,6 @@ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -1163,6 +1391,32 @@ "node": ">=8" } }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -1189,26 +1443,98 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -1232,12 +1558,62 @@ "node": ">=8" } }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" }, @@ -1256,17 +1632,52 @@ "node": ">=8" } }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/ts-mixer": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz", - "integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==", - "license": "MIT" + "integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==" }, "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", - "license": "0BSD" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } }, "node_modules/type-check": { "version": "0.4.0", @@ -1282,30 +1693,32 @@ } }, "node_modules/undici": { - "version": "6.21.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.1.tgz", - "integrity": "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==", - "license": "MIT", + "version": "6.21.3", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz", + "integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==", "engines": { "node": ">=18.17" } }, "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "license": "MIT" + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==" }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -1332,11 +1745,15 @@ "node": ">=0.10.0" } }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "license": "MIT", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", "engines": { "node": ">=10.0.0" }, diff --git a/package.json b/package.json index 0e16dc0..b977053 100644 --- a/package.json +++ b/package.json @@ -7,12 +7,11 @@ "start": "node deploy-commands.js && node index.js" }, "dependencies": { - "@discordjs/builders": "^1.10.1", - "@discordjs/rest": "^2.4.3", "@eslint/js": "^9.20.0", + "better-sqlite3": "^12.6.2", "dayjs": "^1.11.13", "discord-api-types": "^0.37.119", - "discord.js": "^14.18.0", + "discord.js": "^14.25.1", "dotenv": "^16.4.7" }, "devDependencies": {