diff --git a/Commands/close.js b/Commands/close.js index 1efa899..7e87d22 100644 --- a/Commands/close.js +++ b/Commands/close.js @@ -28,19 +28,20 @@ module.exports = new Command({ let SupportStaff = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild); let Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild); - if (!SupportStaff || !Admin) - return interaction.reply("Some roles seem to be missing!\nPlease check for errors when starting the bot."); + if (!SupportStaff || !Admin) { + return interaction.reply("Some roles seem to be missing! Please check for errors when starting the bot."); + } const NoPerms = new Discord.EmbedBuilder() .setTitle("Invalid Permissions!") .setDescription(`${msgconfig.Error.IncorrectPerms}\n\nRole Required: \`${supportbot.Roles.StaffMember.Staff}\` or \`${supportbot.Roles.StaffMember.Admin}\``) - .setColor(supportbot.Embed.Colours.Warn); + .setColor(supportbot.Embed.Colours.Warn); - if (!interaction.member.roles.cache.has(SupportStaff.id) && !interaction.member.roles.cache.has(Admin.id)) + if (!interaction.member.roles.cache.has(SupportStaff.id) && !interaction.member.roles.cache.has(Admin.id)) { return interaction.reply({ embeds: [NoPerms] }); + } } - // Check if it's a thread or channel based on config if ( (supportbot.Ticket.TicketType === "threads" && interaction.channel.type !== Discord.ChannelType.PrivateThread) || (supportbot.Ticket.TicketType === "channels" && interaction.channel.type !== Discord.ChannelType.GuildText) @@ -48,14 +49,22 @@ module.exports = new Command({ const NotTicketChannel = new Discord.EmbedBuilder() .setTitle("Invalid Channel!") .setDescription(`This command can only be used in a ${supportbot.Ticket.TicketType === "threads" ? "ticket thread" : "ticket channel"}.`) - .setColor(supportbot.Embed.Colours.Warn); + .setColor(supportbot.Embed.Colours.Warn); // Ensure valid color return interaction.reply({ embeds: [NotTicketChannel], ephemeral: true }); } - await interaction.deferReply(); + await interaction.deferReply(); // Deferring to avoid double replies + + // Reading TicketData.json once for efficiency + let tickets; + try { + tickets = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8")); + } catch (err) { + console.error('Error reading ticket data file:', err); + return interaction.followUp({ content: "There was an error loading ticket data." }); + } - let tickets = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8")); let TicketData = tickets.tickets.findIndex((t) => t.id === interaction.channel.id); let ticket = tickets.tickets[TicketData]; @@ -63,44 +72,112 @@ module.exports = new Command({ const Exists = new Discord.EmbedBuilder() .setTitle("No Ticket Found!") .setDescription(msgconfig.Error.NoValidTicket) - .setColor(supportbot.Embed.Colours.Warn); + .setColor(supportbot.Embed.Colours.Warn); // Ensure valid color return interaction.followUp({ embeds: [Exists] }); } let reason = interaction.options?.getString("reason") || "No Reason Provided."; - const CloseButton = new Discord.ButtonBuilder() - .setCustomId("confirmCloseTicket") - .setLabel(supportbot.Ticket.Close.Confirmation_Button) - .setEmoji(supportbot.Ticket.Close.Confirmation_Emoji) - .setStyle(supportbot.Ticket.Close.Confirmation_Style); + if (supportbot.Ticket.ReviewSystem.Enabled) { + const rating = await collectReviewRating(interaction); + const comment = await collectReviewComment(interaction); - const row = new Discord.ActionRowBuilder().addComponents(CloseButton); + const reviewChannel = await getChannel(supportbot.Ticket.ReviewSystem.Channel, interaction.guild); + const reviewer = supportbot.Ticket.ClaimTickets ? `<@${ticket.claimedBy}>` : null; - const CloseTicketRequest = new Discord.EmbedBuilder() - .setTitle(`**${supportbot.Ticket.Close.Title}**`) - .setDescription(`${msgconfig.Ticket.ConfirmClose}`) - .setColor(supportbot.Embed.Colours.General); + const reviewEmbed = new Discord.EmbedBuilder() + .setTitle(msgconfig.ReviewSystem.ReviewEmbed.Title) + .addFields( + { + name: msgconfig.ReviewSystem.ReviewEmbed.RatingTitle, + value: `${msgconfig.ReviewSystem.ReviewEmbed.ReviewEmoji.repeat(rating)}`, + inline: false + }, + { + name: msgconfig.ReviewSystem.ReviewEmbed.CommentTitle, + value: `\`\`\`${comment}\`\`\``, + inline: false + } + ) + .setColor(msgconfig.ReviewSystem.ReviewEmbed.Color); - await interaction.followUp({ - embeds: [CloseTicketRequest], - components: [row], - }); + if (supportbot.Ticket.ClaimTickets.Enabled) { + reviewEmbed.setDescription( + `**${msgconfig.ReviewSystem.ReviewEmbed.ReviewedStaffTitle}** ${reviewer}\n**${msgconfig.ReviewSystem.ReviewEmbed.ReviewedByTitle}** <@${interaction.user.id}>` || `N/A` + ); + } else { + reviewEmbed.setDescription( + `**${msgconfig.ReviewSystem.ReviewEmbed.ReviewedByTitle}** <@${interaction.user.id}>` + ); + } - const filter = (btnInteraction) => btnInteraction.customId === "confirmCloseTicket" && btnInteraction.user.id === interaction.user.id; + if (reviewChannel) { + await reviewChannel.send({ embeds: [reviewEmbed] }); + } - interaction.channel.awaitMessageComponent({ filter, time: 20000 }) - .then(async (btnInteraction) => { - await handleCloseTicket(interaction, reason, ticket, TicketData); - await btnInteraction.reply({ content: "Ticket successfully closed.", ephemeral: true }); - }) - .catch((err) => { - interaction.followUp(`${msgconfig.Ticket.CloseTimeout}`); - console.error(err); - }); + if (!interaction.replied && !interaction.deferred) { + await interaction.followUp({ embeds: [reviewEmbed] }); + } + + await handleCloseTicket(interaction, reason, ticket, TicketData); + } else { + await handleCloseTicket(interaction, reason, ticket, TicketData); + } }, }); +async function collectReviewRating(interaction) { + const reviewPrompt = new Discord.EmbedBuilder() + .setTitle(msgconfig.ReviewSystem.Rate.Title) + .setDescription(msgconfig.ReviewSystem.Rate.Description) + .setColor(supportbot.Embed.Colours.General); // Ensure valid color + + const stars = new Discord.ActionRowBuilder().addComponents( + new Discord.StringSelectMenuBuilder() + .setCustomId("starRating") + .setPlaceholder("Select a rating") + .addOptions([ + { label: msgconfig.ReviewSystem.Stars.One, value: "1" }, + { label: msgconfig.ReviewSystem.Stars.Two, value: "2" }, + { label: msgconfig.ReviewSystem.Stars.Three, value: "3" }, + { label: msgconfig.ReviewSystem.Stars.Four, value: "4" }, + { label: msgconfig.ReviewSystem.Stars.Five, value: "5" }, + ]) + ); + + await interaction.followUp({ + embeds: [reviewPrompt], + components: [stars], + ephemeral: true, + }); + + const starRating = await interaction.channel.awaitMessageComponent({ + componentType: Discord.ComponentType.StringSelect, + filter: (i) => i.customId === "starRating" && i.user.id === interaction.user.id, + time: 30000, + }); + + await starRating.deferUpdate(); + return starRating.values[0]; +} + +async function collectReviewComment(interaction) { + const commentPrompt = new Discord.EmbedBuilder() + .setTitle(msgconfig.ReviewSystem.Comment.Title) + .setDescription(msgconfig.ReviewSystem.Comment.Description) + .setColor(supportbot.Embed.Colours.General); // Ensure valid color + + await interaction.followUp({ + embeds: [commentPrompt], + ephemeral: true, + }); + + const filter = (response) => response.author.id === interaction.user.id; + const commentCollection = await interaction.channel.awaitMessages({ filter, max: 1, time: 30000 }); + const comment = commentCollection.first()?.content; + return comment && comment.toLowerCase() !== "no" ? comment : "No Comment Provided"; +} + async function handleCloseTicket(interaction, reason, ticket, TicketData) { const { getChannel } = interaction.client; @@ -109,196 +186,65 @@ async function handleCloseTicket(interaction, reason, ticket, TicketData) { let transcriptChannel = await getChannel(supportbot.Ticket.Log.TicketDataLog, interaction.guild); if (!transcriptChannel) - return interaction.followUp("Some Channels seem to be missing!"); + return console.log("Ticket Log Missing"); try { tickets.tickets[TicketData].open = false; - fs.writeFileSync( - "./Data/TicketData.json", - JSON.stringify(tickets, null, 4), - (err) => { - if (err) console.error(err); - } - ); + fs.writeFileSync("./Data/TicketData.json", JSON.stringify(tickets, null, 4)); const transcriptEmbed = new Discord.EmbedBuilder() .setTitle(msgconfig.TicketLog.Title) - .setColor(msgconfig.TicketLog.Colour) - .setFooter({ - text: supportbot.Embed.Footer, - iconURL: interaction.user.displayAvatarURL(), - }) - .addFields( - { name: "Ticket", value: `${interaction.channel.name} (${interaction.channel.id})` }, - { name: "User", value: `${tUser?.username || "N/A"}#${tUser?.discriminator || "N/A"} (${tUser?.id || ticket.user})` }, - { name: "Closed By", value: interaction.user.tag }, - { name: "Reason", value: reason } - ); + .setColor(msgconfig.TicketLog.Colour) // Ensure valid color + .setFooter({ text: supportbot.Embed.Footer, iconURL: interaction.user.displayAvatarURL() }) + .setDescription(`> **Ticket:** ${interaction.channel.name} (\`${interaction.channel.id}\`)\n> **User:** ${tUser?.username || "N/A"}#${tUser?.discriminator || "N/A"} (\`${tUser?.id || ticket.user}\`)\n> **Closed by:** <@${interaction.user.id}>`) + .addFields({ name: "Reason", value: `\`\`\`${reason}\`\`\``, inline: true }); let msgs = await interaction.channel.messages.fetch({ limit: 100 }); - let html = ` - - - - - - -
-

Ticket Transcript

-

Server Name: ${interaction.guild.name}

-

Ticket: ${interaction.channel.name}

-

Messages: ${msgs.size} Messages

-
- `; - - msgs = msgs.sort((a, b) => a.createdTimestamp - b.createdTimestamp); - msgs.forEach((msg) => { - if (msg.content) { - html += ` -
- Avatar -
-

${msg.author.tag}

-

${msg.createdAt.toLocaleString()}

-

${convertMarkdownToHTML(msg.content)}

-
-
- `; - } - }); + let html = createTranscriptHTML(interaction, msgs); const fileName = `${interaction.channel.name}.html`; + let file = new Discord.AttachmentBuilder(Buffer.from(html), { name: `SPOILER_${fileName}` }); - html += ` -
- Download Transcript -
- - - `; - - let file = new Discord.AttachmentBuilder( - Buffer.from(html), - { name: fileName } - ); - - await transcriptChannel - .send({ embeds: [transcriptEmbed], files: [file] }) - .catch((err) => { - console.error(err); - }); - - if (supportbot.Ticket.DMTranscripts) { - await tUser - ?.send({ embeds: [transcriptEmbed], files: [file] }) - .catch((err) => { - console.error(err); - }); - - if (ticket.subUsers) { - ticket.subUsers.forEach(async (subUser) => { - await interaction.client.users.cache - .get(subUser) - ?.send({ embeds: [transcriptEmbed], files: [file] }); - }); - } - } - - await interaction.channel.delete().catch((error) => { - console.error(error); - if (error.code !== Discord.Constants.APIErrors.UNKNOWN_CHANNEL) { - console.error("Failed to delete the interaction:", error); - } + transcriptChannel.send({ + embeds: [transcriptEmbed], + files: [file], }); - } catch (error) { - console.error(error); + } catch (err) { + console.log("Error closing ticket:", err); } + + await interaction.channel.delete(); } -function convertMarkdownToHTML(text) { - // Convert **bold** to bold - text = text.replace(/\*\*(.*?)\*\*/g, '$1'); +function createTranscriptHTML(interaction, msgs) { + let html = ` + + + + + +

Ticket Transcript

+

Ticket: ${interaction.channel.name}

+

User: ${interaction.user.tag}

+
+ `; - // Convert *italic* to italic - text = text.replace(/\*(.*?)\*/g, '$1'); + msgs.reverse().forEach((msg) => { + html += ` +
+ ${msg.author.tag}: ${msg.content} +
+ `; + }); - // Convert __underline__ to underline - text = text.replace(/__(.*?)__/g, '$1'); - - // Convert ~~strikethrough~~ to strikethrough - text = text.replace(/~~(.*?)~~/g, '$1'); - - // Convert inline code `code` to code - text = text.replace(/`(.*?)`/g, '$1'); - - // Convert blockquotes > quote to
quote
- text = text.replace(/^> (.*?$)/gm, '
$1
'); - - // Convert multiline code blocks ```code``` to
code
- text = text.replace(/```(.*?)```/gs, '
$1
'); - - // Convert line breaks to
- text = text.replace(/\n/g, '
'); - - return text; + html += ` +
+ + + `; + return html; } diff --git a/Commands/embed.js b/Commands/embed.js index 1f4d447..b38470f 100644 --- a/Commands/embed.js +++ b/Commands/embed.js @@ -1,167 +1,479 @@ -// File path: ./commands/embed.js +const { + ApplicationCommandType, + ApplicationCommandOptionType, + ActionRowBuilder, + ButtonBuilder, + EmbedBuilder, + StringSelectMenuBuilder, + ModalBuilder, + TextInputBuilder, + TextInputStyle, + ButtonStyle, + ComponentType +} = require('discord.js'); +const Command = require('../Structures/Command.js'); +const fs = require('fs'); +const yaml = require('js-yaml'); -const fs = require("fs"); -const Discord = require("discord.js"); -const yaml = require("js-yaml"); +const supportbot = yaml.load(fs.readFileSync('./Configs/supportbot.yml', 'utf8')); +const cmdconfig = yaml.load(fs.readFileSync('./Configs/commands.yml', 'utf8')); +const msgconfig = yaml.load(fs.readFileSync('./Configs/messages.yml', 'utf8')); -const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8")); -const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8")); -const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8")); - -const Command = require("../Structures/Command.js"); +// Helper function to validate and convert hex color +function parseColor(color) { + if (!color) return 0x5865F2; + if (typeof color === 'number') return color; + if (typeof color !== 'string') return 0x5865F2; + color = color.replace('#', ''); + if (/^[0-9A-F]{6}$/i.test(color)) { + return parseInt(color, 16); + } + return 0x5865F2; +} module.exports = new Command({ name: cmdconfig.Embed.Command, description: cmdconfig.Embed.Description, - type: Discord.ApplicationCommandType.ChatInput, + type: ApplicationCommandType.ChatInput, options: [ { - name: "title", - description: "Embed Title", - type: Discord.ApplicationCommandOptionType.String, - required: true, - }, - { - name: "message", - description: "Embed Message", - type: Discord.ApplicationCommandOptionType.String, - required: true, - }, - { - name: "color", - description: "Embed HEX Color", - type: Discord.ApplicationCommandOptionType.String, - required: false, - }, - { - name: "fields", - description: "Embed Fields (title|content;title|content;...)", - type: Discord.ApplicationCommandOptionType.String, - required: false, - }, - { - name: "thumbnail", - description: "Embed Thumbnail URL", - type: Discord.ApplicationCommandOptionType.String, - required: false, - }, - { - name: "image", - description: "Embed Image URL", - type: Discord.ApplicationCommandOptionType.String, - required: false, - }, - { - name: "footer", - description: "Embed Footer Text", - type: Discord.ApplicationCommandOptionType.String, - required: false, - }, - { - name: "footericon", - description: "Embed Footer Icon URL", - type: Discord.ApplicationCommandOptionType.String, - required: false, - }, - { - name: "author", - description: "Embed Author Name", - type: Discord.ApplicationCommandOptionType.String, - required: false, - }, - { - name: "authoricon", - description: "Embed Author Icon URL", - type: Discord.ApplicationCommandOptionType.String, - required: false, - }, - { - name: "authorurl", - description: "Embed Author URL", - type: Discord.ApplicationCommandOptionType.String, - required: false, - }, - { - name: "timestamp", - description: "Include Timestamp", - type: Discord.ApplicationCommandOptionType.Boolean, - required: false, - }, - { - name: "inline", - description: "Make Fields Inline", - type: Discord.ApplicationCommandOptionType.Boolean, - required: false, + name: 'channel', + description: 'Select channel to send the message', + type: ApplicationCommandOptionType.Channel, + required: true } ], permissions: cmdconfig.Embed.Permission, async run(interaction) { + // Permission check const { getRole } = interaction.client; let SupportStaff = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild); let Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild); if (!SupportStaff || !Admin) { - return interaction.reply("Some roles seem to be missing!\nPlease check for errors when starting the bot."); + return interaction.reply('Some roles seem to be missing!\nPlease check for errors when starting the bot.'); } - const NoPerms = new Discord.EmbedBuilder() - .setTitle("Invalid Permissions!") - .setDescription(`${msgconfig.Error.IncorrectPerms}\n\nRole Required: \`${supportbot.Roles.StaffMember.Staff}\` or \`${supportbot.Roles.StaffMember.Admin}\``) - .setColor(supportbot.Embed.Colours.Warn); + if (!interaction.member.roles.cache.has(SupportStaff.id) && + !interaction.member.roles.cache.has(Admin.id)) { + const NoPerms = new EmbedBuilder() + .setTitle('Invalid Permissions!') + .setDescription(`${msgconfig.Error.IncorrectPerms}\n\nRole Required: \`${supportbot.Roles.StaffMember.Staff}\` or \`${supportbot.Roles.StaffMember.Admin}\``) + .setColor(0xFF0000); + return interaction.reply({ embeds: [NoPerms], ephemeral: true }); + } - if ( - interaction.member.roles.cache.has(SupportStaff.id) || - interaction.member.roles.cache.has(Admin.id) - ) { - const EmbedTitle = interaction.options.getString("title").replace(/\\n/g, '\n'); - const EmbedSubject = interaction.options.getString("message").replace(/\\n/g, '\n'); - const GeneralColour = interaction.options.getString("color") || supportbot.Embed.Colours.Default; - const EmbedThumbnail = interaction.options.getString("thumbnail"); - const EmbedImage = interaction.options.getString("image"); - const EmbedFields = interaction.options.getString("fields"); - const EmbedFooter = interaction.options.getString("footer")?.replace(/\\n/g, '\n'); - const EmbedFooterIcon = interaction.options.getString("footericon"); - const EmbedAuthor = interaction.options.getString("author")?.replace(/\\n/g, '\n'); - const EmbedAuthorIcon = interaction.options.getString("authoricon"); - const EmbedAuthorURL = interaction.options.getString("authorurl"); - const EmbedTimestamp = interaction.options.getBoolean("timestamp"); - const InlineFields = interaction.options.getBoolean("inline"); + const channel = interaction.options.getChannel('channel'); + + // Initial message state + const messageData = { + type: 'embed', + title: 'New Embed', + description: 'Click the buttons below to edit this embed', + color: 0x5865F2, + fields: [], + footer: null, + thumbnail: null, + image: null, + timestamp: false + }; - const EmbedMsg = new Discord.EmbedBuilder() - .setTitle(EmbedTitle) - .setDescription(EmbedSubject) - .setColor(GeneralColour) - .setThumbnail(EmbedThumbnail) - .setImage(EmbedImage); + // Create initial embed + function createEmbed() { + const embed = new EmbedBuilder() + .setTitle(messageData.title) + .setDescription(messageData.description) + .setColor(messageData.color); - if (EmbedFields) { - const fieldsArray = EmbedFields.split(';'); - fieldsArray.forEach(field => { - const [title, value] = field.split('|'); - if (title && value) { - EmbedMsg.addFields({ name: title.replace(/\\n/g, '\n'), value: value.replace(/\\n/g, '\n'), inline: InlineFields || false }); - } + if (messageData.fields.length > 0) { + embed.addFields(messageData.fields); + } + if (messageData.footer) { + embed.setFooter({ text: messageData.footer }); + } + if (messageData.thumbnail) { + embed.setThumbnail(messageData.thumbnail); + } + if (messageData.image) { + embed.setImage(messageData.image); + } + if (messageData.timestamp) { + embed.setTimestamp(); + } + + return embed; + } + + // Create type selector + const typeRow = new ActionRowBuilder() + .addComponents( + new StringSelectMenuBuilder() + .setCustomId('message_type') + .setPlaceholder('Select message type') + .addOptions([ + { + label: 'Embed Message', + description: 'Send as an embedded message', + value: 'embed' + }, + { + label: 'Text Message', + description: 'Send as a plain text message', + value: 'text' + } + ]) + ); + + // Create button rows function + function createButtons(messageType) { + const buttons = []; + + if (messageType === 'embed') { + // Only show these buttons for embed mode + const row1 = new ActionRowBuilder() + .addComponents( + new ButtonBuilder() + .setCustomId('edit_title') + .setLabel('Edit Title') + .setStyle(ButtonStyle.Secondary), + new ButtonBuilder() + .setCustomId('edit_description') + .setLabel('Edit Description') + .setStyle(ButtonStyle.Secondary), + new ButtonBuilder() + .setCustomId('edit_color') + .setLabel('Edit Color') + .setStyle(ButtonStyle.Secondary), + new ButtonBuilder() + .setCustomId('add_field') + .setLabel('Add Field') + .setStyle(ButtonStyle.Secondary) + ); + + const row2 = new ActionRowBuilder() + .addComponents( + new ButtonBuilder() + .setCustomId('edit_footer') + .setLabel('Edit Footer') + .setStyle(ButtonStyle.Secondary), + new ButtonBuilder() + .setCustomId('edit_image') + .setLabel('Add Image') + .setStyle(ButtonStyle.Secondary), + new ButtonBuilder() + .setCustomId('edit_thumbnail') + .setLabel('Add Thumbnail') + .setStyle(ButtonStyle.Secondary), + new ButtonBuilder() + .setCustomId('toggle_timestamp') + .setLabel('Toggle Timestamp') + .setStyle(ButtonStyle.Secondary) + ); + + buttons.push(row1, row2); + } else { + // Text mode only shows edit text button + const textRow = new ActionRowBuilder() + .addComponents( + new ButtonBuilder() + .setCustomId('edit_description') + .setLabel('Edit Text') + .setStyle(ButtonStyle.Secondary) + ); + + buttons.push(textRow); + } + + // Send button always shows + const sendRow = new ActionRowBuilder() + .addComponents( + new ButtonBuilder() + .setCustomId('send_message') + .setLabel('Send') + .setStyle(ButtonStyle.Success) + ); + + buttons.push(sendRow); + return buttons; + } + + // Function to update the message preview + async function updatePreview(i) { + if (messageData.type === 'embed') { + await i.update({ + content: 'Customize your message:', + embeds: [createEmbed()], + components: [typeRow, ...createButtons('embed')] + }); + } else { + // For text mode, just show the text directly + await i.update({ + content: messageData.description, + embeds: [], + components: [typeRow, ...createButtons('text')] + }); + } + } + + // Send initial message + const message = await interaction.reply({ + content: messageData.type === 'text' ? messageData.description : 'Customize your message:', + components: [typeRow, ...createButtons(messageData.type)], + embeds: messageData.type === 'embed' ? [createEmbed()] : [], + ephemeral: true + }); + + // Create collector for interactions + const collector = message.createMessageComponentCollector({ + time: 900000 + }); + + collector.on('collect', async (i) => { + if (i.user.id !== interaction.user.id) { + return i.reply({ + content: 'You cannot edit this message.', + ephemeral: true }); } - if (EmbedFooter) { - EmbedMsg.setFooter({ text: EmbedFooter, iconURL: EmbedFooterIcon }); + switch (i.customId) { + case 'message_type': + messageData.type = i.values[0]; + await updatePreview(i); + break; + + case 'edit_description': + const descModal = new ModalBuilder() + .setCustomId('desc_modal') + .setTitle(messageData.type === 'embed' ? 'Edit Description' : 'Edit Text'); + + descModal.addComponents( + new ActionRowBuilder() + .addComponents( + new TextInputBuilder() + .setCustomId('desc_input') + .setLabel(messageData.type === 'embed' ? 'Description' : 'Message Text') + .setStyle(TextInputStyle.Paragraph) + .setValue(messageData.description) + .setMaxLength(4000) + ) + ); + + await i.showModal(descModal); + break; + + case 'edit_title': + const titleModal = new ModalBuilder() + .setCustomId('title_modal') + .setTitle('Edit Title'); + + titleModal.addComponents( + new ActionRowBuilder() + .addComponents( + new TextInputBuilder() + .setCustomId('title_input') + .setLabel('Title') + .setStyle(TextInputStyle.Short) + .setValue(messageData.title) + .setMaxLength(256) + ) + ); + + await i.showModal(titleModal); + break; + + case 'edit_color': + const colorModal = new ModalBuilder() + .setCustomId('color_modal') + .setTitle('Edit Color'); + + colorModal.addComponents( + new ActionRowBuilder() + .addComponents( + new TextInputBuilder() + .setCustomId('color_input') + .setLabel('Color (HEX)') + .setStyle(TextInputStyle.Short) + .setPlaceholder('#FF0000') + .setMinLength(6) + .setMaxLength(7) + .setValue(messageData.color.toString(16).padStart(6, '0')) + ) + ); + + await i.showModal(colorModal); + break; + + case 'add_field': + const fieldModal = new ModalBuilder() + .setCustomId('field_modal') + .setTitle('Add Field'); + + fieldModal.addComponents( + new ActionRowBuilder() + .addComponents( + new TextInputBuilder() + .setCustomId('field_name') + .setLabel('Field Name') + .setStyle(TextInputStyle.Short) + .setMaxLength(256) + ), + new ActionRowBuilder() + .addComponents( + new TextInputBuilder() + .setCustomId('field_value') + .setLabel('Field Value') + .setStyle(TextInputStyle.Paragraph) + .setMaxLength(1024) + ), + new ActionRowBuilder() + .addComponents( + new TextInputBuilder() + .setCustomId('field_inline') + .setLabel('Inline? (true/false)') + .setStyle(TextInputStyle.Short) + .setValue('false') + .setPlaceholder('true or false') + ) + ); + + await i.showModal(fieldModal); + break; + + case 'edit_footer': + const footerModal = new ModalBuilder() + .setCustomId('footer_modal') + .setTitle('Edit Footer'); + + footerModal.addComponents( + new ActionRowBuilder() + .addComponents( + new TextInputBuilder() + .setCustomId('footer_input') + .setLabel('Footer Text') + .setStyle(TextInputStyle.Short) + .setValue(messageData.footer || '') + .setMaxLength(2048) + ) + ); + + await i.showModal(footerModal); + break; + + case 'edit_image': + const imageModal = new ModalBuilder() + .setCustomId('image_modal') + .setTitle('Add Image'); + + imageModal.addComponents( + new ActionRowBuilder() + .addComponents( + new TextInputBuilder() + .setCustomId('image_input') + .setLabel('Image URL') + .setStyle(TextInputStyle.Short) + .setValue(messageData.image || '') + .setPlaceholder('https://example.com/image.png') + ) + ); + + await i.showModal(imageModal); + break; + + case 'edit_thumbnail': + const thumbnailModal = new ModalBuilder() + .setCustomId('thumbnail_modal') + .setTitle('Add Thumbnail'); + + thumbnailModal.addComponents( + new ActionRowBuilder() + .addComponents( + new TextInputBuilder() + .setCustomId('thumbnail_input') + .setLabel('Thumbnail URL') + .setStyle(TextInputStyle.Short) + .setValue(messageData.thumbnail || '') + .setPlaceholder('https://example.com/thumbnail.png') + ) + ); + + await i.showModal(thumbnailModal); + break; + + case 'toggle_timestamp': + messageData.timestamp = !messageData.timestamp; + await updatePreview(i); + break; + + case 'send_message': + try { + if (messageData.type === 'embed') { + await channel.send({ embeds: [createEmbed()] }); + } else { + await channel.send(messageData.description); + } + await i.update({ + content: 'Message sent successfully!', + components: [], + embeds: [] + }); + collector.stop(); + } catch (error) { + await i.reply({ + content: 'Failed to send message. Please check channel permissions.', + ephemeral: true + }); + } + break; + } + }); + + // Handle modal submissions + interaction.client.on('interactionCreate', async (modal) => { + if (!modal.isModalSubmit()) return; + if (modal.user.id !== interaction.user.id) return; + + switch (modal.customId) { + case 'title_modal': + messageData.title = modal.fields.getTextInputValue('title_input'); + break; + case 'desc_modal': + messageData.description = modal.fields.getTextInputValue('desc_input'); + break; + case 'color_modal': + messageData.color = parseColor(modal.fields.getTextInputValue('color_input')); + break; + case 'field_modal': + const inlineValue = modal.fields.getTextInputValue('field_inline').toLowerCase(); + messageData.fields.push({ + name: modal.fields.getTextInputValue('field_name'), + value: modal.fields.getTextInputValue('field_value'), + inline: inlineValue === 'true' + }); + break; + case 'footer_modal': + messageData.footer = modal.fields.getTextInputValue('footer_input'); + break; + case 'image_modal': + messageData.image = modal.fields.getTextInputValue('image_input'); + break; + case 'thumbnail_modal': + messageData.thumbnail = modal.fields.getTextInputValue('thumbnail_input'); + break; + default: + return; } - if (EmbedAuthor) { - EmbedMsg.setAuthor({ name: EmbedAuthor, iconURL: EmbedAuthorIcon, url: EmbedAuthorURL }); - } + await updatePreview(modal); + }); - if (EmbedTimestamp) { - EmbedMsg.setTimestamp(); - } - - interaction.reply({ - embeds: [EmbedMsg], - }); - } else { - return interaction.reply({ embeds: [NoPerms] }); - } - }, -}); + collector.on('end', () => { + interaction.editReply({ + content: 'Message editor timed out.', + components: [], + embeds: [] + }).catch(() => {}); + }); + } +}); \ No newline at end of file diff --git a/Commands/mod.js b/Commands/mod.js new file mode 100644 index 0000000..719fa29 --- /dev/null +++ b/Commands/mod.js @@ -0,0 +1,261 @@ +const fs = require("fs"); +const { ApplicationCommandOptionType, ApplicationCommandType, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, ComponentType } = require("discord.js"); +const yaml = require("js-yaml"); +const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8")); +const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8")); +const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8")); + +const Command = require("../Structures/Command.js"); + +// Function to chunk array into smaller arrays +function chunkArray(arr, size) { + const chunks = []; + for (let i = 0; i < arr.length; i += size) { + chunks.push(arr.slice(i, i + size)); + } + return chunks; +} + +module.exports = new Command({ + name: cmdconfig.Mod.Command, + description: cmdconfig.Mod.Command, + type: ApplicationCommandType.ChatInput, + options: [ + { + name: cmdconfig.Mod.TicketBlacklist.Command, + description: cmdconfig.Mod.TicketBlacklist.Description, + type: ApplicationCommandOptionType.SubcommandGroup, + options: [ + { + name: cmdconfig.Mod.TicketBlacklist.Add.Command, + description: msgconfig.Mod.TicketBlacklist.Add.Description || "Add a user to the ticket blacklist", + type: ApplicationCommandOptionType.Subcommand, + options: [ + { + name: 'user', + description: msgconfig.Mod.TicketBlacklist.Add.UserDescription || "User to blacklist", + type: ApplicationCommandOptionType.User, + required: true + }, + { + name: 'reason', + description: msgconfig.Mod.TicketBlacklist.Add.ReasonDescription || "Reason for blacklisting", + type: ApplicationCommandOptionType.String, + required: false + } + ] + }, + { + name: cmdconfig.Mod.TicketBlacklist.Remove.Command, + description: msgconfig.Mod.TicketBlacklist.Remove.Description || "Remove a user from the ticket blacklist", + type: ApplicationCommandOptionType.Subcommand, + options: [ + { + name: 'user', + description: msgconfig.Mod.TicketBlacklist.Remove.UserDescription || "User to remove from blacklist", + type: ApplicationCommandOptionType.User, + required: true + }, + { + name: 'reason', + description: msgconfig.Mod.TicketBlacklist.Remove.ReasonDescription || "Reason for removing from blacklist", + type: ApplicationCommandOptionType.String, + required: false + } + ] + }, + { + name: "view", + description: msgconfig.Mod.TicketBlacklist.View.Description || "View all blacklisted users", + type: ApplicationCommandOptionType.Subcommand, + } + ] + } + ], + permissions: cmdconfig.Mod.Permission, + + async run(interaction) { + const { getRole } = interaction.client; + + let SupportStaff = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild); + let Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild); + let Moderator = await getRole(supportbot.Roles.StaffMember.Moderator, interaction.guild); + + if (!SupportStaff || !Admin || !Moderator) { + const missingRolesEmbed = new EmbedBuilder() + .setDescription(msgconfig.Error.InvalidChannel || "Required roles are missing!") + .setColor(supportbot.Embed.Colours.Warn); + return interaction.reply({ embeds: [missingRolesEmbed] }); + } + + const NoPerms = new EmbedBuilder() + .setDescription(msgconfig.Error.IncorrectPerms || "You do not have the correct permissions!") + .setColor(supportbot.Embed.Colours.Warn); + + if (!interaction.member.roles.cache.has(Admin.id) && !interaction.member.roles.cache.has(Moderator.id) && (!supportbot.Mod.AllowSupportStaff || !interaction.member.roles.cache.has(SupportStaff.id))) { + return interaction.reply({ embeds: [NoPerms] }); + } + + const subcommand = interaction.options.getSubcommand(false); + const user = interaction.options.getUser('user'); + const reason = interaction.options.getString('reason') || "No reason provided"; + + try { + let blacklistedUsers; + try { + blacklistedUsers = JSON.parse(fs.readFileSync("./Data/BlacklistedUsers.json", "utf8")).blacklistedUsers; + if (!Array.isArray(blacklistedUsers)) { + blacklistedUsers = []; + } + } catch (error) { + blacklistedUsers = []; + } + + if (subcommand === cmdconfig.Mod.TicketBlacklist.Add.Command) { + if (blacklistedUsers.includes(user.id)) { + const alreadyBlacklistedEmbed = new EmbedBuilder() + .setDescription(msgconfig.Mod.TicketBlacklist.Add.AlreadyBlacklisted.replace("{userTag}", user.tag) || ":x: User is already blacklisted.") + .setColor(supportbot.Embed.Colours.Warn); + return interaction.reply({ embeds: [alreadyBlacklistedEmbed], ephemeral: true }); + } + + blacklistedUsers.push(user.id); + fs.writeFileSync("./Data/BlacklistedUsers.json", JSON.stringify({ "blacklistedUsers": blacklistedUsers }, null, 4)); + + const successEmbed = new EmbedBuilder() + .setDescription(msgconfig.Mod.TicketBlacklist.Add.Success.replace("{userTag}", user.tag) || ":white_check_mark: User has been blacklisted.") + .setColor(supportbot.Embed.Colours.Success); + + const blacklistChannel = interaction.guild.channels.cache.get(supportbot.Ticket.Log.TicketBlacklistLog); + if (blacklistChannel) { + const blacklistLogEmbed = new EmbedBuilder() + .setTitle(msgconfig.TicketBlacklistLog.Title || "Ticket Blacklist Log") + .setColor(msgconfig.TicketBlacklistLog.Colour || supportbot.Embed.Colours.Success) + .setFooter({ text: supportbot.Embed.Footer, iconURL: interaction.user.displayAvatarURL() }) + .setDescription(`> **User:** ${user.tag} (\`${user.id}\`)\n> **Actioned by:** <@${interaction.user.id}>`) + .addFields({ name: "Action", value: "\`\`\`Removed from blacklist\`\`\`", inline: false }) + .addFields({ name: "Reason", value: `\`\`\`${reason}\`\`\``, inline: false }); + + blacklistChannel.send({ embeds: [blacklistLogEmbed] }); + } + + return interaction.reply({ + embeds: [successEmbed], + ephemeral: true + }); + + } else if (subcommand === cmdconfig.Mod.TicketBlacklist.Remove.Command) { + if (!blacklistedUsers.includes(user.id)) { + const notBlacklistedEmbed = new EmbedBuilder() + .setDescription(msgconfig.Mod.TicketBlacklist.Remove.NotBlacklisted.replace("{userTag}", user.tag) || ":x: User is not blacklisted.") + .setColor(supportbot.Embed.Colours.Warn); + return interaction.reply({ embeds: [notBlacklistedEmbed], ephemeral: true }); + } + + blacklistedUsers = blacklistedUsers.filter(id => id !== user.id); + fs.writeFileSync("./Data/BlacklistedUsers.json", JSON.stringify({ "blacklistedUsers": blacklistedUsers }, null, 4)); + + const removedEmbed = new EmbedBuilder() + .setDescription(msgconfig.Mod.TicketBlacklist.Remove.Success.replace("{userTag}", user.tag) || ":white_check_mark: User has been removed from the blacklist.") + .setColor(supportbot.Embed.Colours.Success); + + const blacklistChannel = interaction.guild.channels.cache.get(supportbot.Ticket.Log.TicketBlacklistLog); + if (blacklistChannel) { + const blacklistLogEmbed = new EmbedBuilder() + .setTitle(msgconfig.TicketBlacklistLog.Title || "Ticket Blacklist Log") + .setColor(msgconfig.TicketBlacklistLog.Colour || supportbot.Embed.Colours.Success) + .setFooter({ text: supportbot.Embed.Footer, iconURL: interaction.user.displayAvatarURL() }) + .setDescription(`> **User:** ${user.tag} (\`${user.id}\`)\n> **Actioned by:** <@${interaction.user.id}>`) + .addFields({ name: "Action", value: "\`\`\`Removed from blacklist\`\`\`", inline: false }) + .addFields({ name: "Reason", value: `\`\`\`${reason}\`\`\``, inline: false }); + + blacklistChannel.send({ embeds: [blacklistLogEmbed] }); + } + + return interaction.reply({ embeds: [removedEmbed], ephemeral: true }); + + } else if (subcommand === "view") { + if (blacklistedUsers.length === 0) { + const noBlacklistedUsersEmbed = new EmbedBuilder() + .setDescription(msgconfig.Mod.TicketBlacklist.View.NoBlacklistedUsers || "No users are blacklisted.") + .setColor(supportbot.Embed.Colours.Success); + return interaction.reply({ embeds: [noBlacklistedUsersEmbed], ephemeral: true }); + } + + const chunkedUsers = chunkArray(blacklistedUsers, 5); + let currentPage = 0; + + const createEmbed = (page) => { + return new EmbedBuilder() + .setTitle(msgconfig.Mod.TicketBlacklist.View.EmbedTitle || "Blacklisted Users") + .setDescription(chunkedUsers[page].map(id => `<@${id}>`).join("\n")) + .setColor(supportbot.Embed.Colours.Success) + .setFooter({ text: `Page ${page + 1} of ${chunkedUsers.length}` }); + }; + + const buttons = new ActionRowBuilder() + .addComponents( + new ButtonBuilder() + .setCustomId('previous') + .setLabel('Previous') + .setStyle(ButtonStyle.Secondary) + .setDisabled(currentPage === 0), + new ButtonBuilder() + .setCustomId('next') + .setLabel('Next') + .setStyle(ButtonStyle.Secondary) + .setDisabled(currentPage === chunkedUsers.length - 1) + ); + + const response = await interaction.reply({ + embeds: [createEmbed(currentPage)], + components: chunkedUsers.length > 1 ? [buttons] : [], + ephemeral: true + }); + + if (chunkedUsers.length <= 1) return; + + const collector = response.createMessageComponentCollector({ + componentType: ComponentType.Button, + time: 60000 + }); + + collector.on('collect', async (i) => { + if (i.user.id !== interaction.user.id) { + return i.reply({ + content: 'You cannot use these buttons.', + ephemeral: true + }); + } + + if (i.customId === 'previous' && currentPage > 0) { + currentPage--; + } else if (i.customId === 'next' && currentPage < chunkedUsers.length - 1) { + currentPage++; + } + + buttons.components[0].setDisabled(currentPage === 0); + buttons.components[1].setDisabled(currentPage === chunkedUsers.length - 1); + + await i.update({ + embeds: [createEmbed(currentPage)], + components: [buttons] + }); + }); + + collector.on('end', () => { + buttons.components.forEach(button => button.setDisabled(true)); + interaction.editReply({ + components: [buttons] + }).catch(() => {}); + }); + } + } catch (error) { + console.error("Error in mod command:", error); + const errorEmbed = new EmbedBuilder() + .setDescription(msgconfig.Error.ActionFailed || "An error occurred while processing your request.") + .setColor(supportbot.Embed.Colours.Warn); + interaction.reply({ embeds: [errorEmbed], ephemeral: true }); + } + } +}); \ No newline at end of file diff --git a/Commands/profile.js b/Commands/profile.js index e732e55..a25f448 100644 --- a/Commands/profile.js +++ b/Commands/profile.js @@ -10,10 +10,6 @@ const Command = require("../Structures/Command.js"); const clockedInUsers = new Set(); -// async function getRole(roleName, guild) { -// return guild.roles.cache.find(role => role.name === roleName); -// } - function updateClockedInStatus(profileEmbed, clockedIn) { profileEmbed.data.fields = profileEmbed.data.fields.map(field => { if (field.name === 'Clocked In Status') { @@ -24,6 +20,35 @@ function updateClockedInStatus(profileEmbed, clockedIn) { return profileEmbed; } +function formatSchedule(schedule) { + if (!schedule || Object.keys(schedule).length === 0) return 'No schedule set'; + + const days = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']; + return days + .filter(day => schedule[day]) + .map(day => `${day.charAt(0).toUpperCase() + day.slice(1)}: ${schedule[day].start} - ${schedule[day].end}`) + .join('\n'); +} + +function calculateWeeklyHours(schedule) { + if (!schedule) return 0; + + let totalMinutes = 0; + Object.values(schedule).forEach(day => { + if (day && day.start && day.end) { + const [startHour, startMin] = day.start.split(':').map(Number); + const [endHour, endMin] = day.end.split(':').map(Number); + + const startMinutes = startHour * 60 + startMin; + const endMinutes = endHour * 60 + endMin; + + totalMinutes += endMinutes - startMinutes; + } + }); + + return Math.round(totalMinutes / 60 * 10) / 10; // Round to 1 decimal place +} + module.exports = new Command({ name: cmdconfig.Profile.Command, description: cmdconfig.Profile.Description, @@ -39,7 +64,7 @@ module.exports = new Command({ ], async run(interaction) { - await interaction.deferReply({ ephemeral: true }); // Acknowledge the interaction immediately + await interaction.deferReply({ ephemeral: true }); const userOption = interaction.options.getUser('user'); const viewingUser = userOption || interaction.user; @@ -47,7 +72,7 @@ module.exports = new Command({ const interactionUserId = interaction.user.id; const user = interaction.guild.members.cache.get(viewingUserId); - const { getRole, getChannel } = interaction.client; + const { getRole } = interaction.client; const Staff = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild); const Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild); @@ -71,12 +96,14 @@ module.exports = new Command({ profileData = { bio: "", timezone: "", - clockedIn: false + clockedIn: false, + schedule: {} }; fs.writeFileSync(profilePath, JSON.stringify(profileData, null, 2)); } - const { bio, timezone, clockedIn } = profileData; + const { bio, timezone, clockedIn, schedule } = profileData; + const weeklyHours = calculateWeeklyHours(schedule); let profileEmbed = new EmbedBuilder() .setTitle(`${viewingUser.username}'s Profile`) @@ -88,7 +115,11 @@ module.exports = new Command({ ); if (isStaff) { - profileEmbed.addFields({ name: 'Clocked In Status', value: clockedIn ? '✅ Clocked In' : '❌ Clocked Out', inline: true }); + profileEmbed.addFields( + { name: 'Clocked In Status', value: clockedIn ? '✅ Clocked In' : '❌ Clocked Out', inline: true }, + { name: 'Weekly Hours', value: `${weeklyHours} hours`, inline: true }, // Now inline with Schedule + { name: 'Schedule', value: formatSchedule(schedule), inline: true } + ); } const buttonRow = new ActionRowBuilder(); @@ -105,6 +136,10 @@ module.exports = new Command({ new ButtonBuilder() .setCustomId('clockInOut') .setLabel(clockedIn ? 'Clock Out' : 'Clock In') + .setStyle(ButtonStyle.Secondary), + new ButtonBuilder() + .setCustomId('editSchedule') + .setLabel('Edit Schedule') .setStyle(ButtonStyle.Secondary) ); } @@ -122,7 +157,7 @@ module.exports = new Command({ await interaction.editReply({ embeds: [profileEmbed], components: buttonRow.components.length > 0 ? [buttonRow] : [] }); const filter = i => ( - ['editProfile', 'clockInOut', 'viewTicketStats', 'showOpenTickets', 'backToStats', 'backToProfile'].includes(i.customId) + ['editProfile', 'clockInOut', 'viewTicketStats', 'editSchedule'].includes(i.customId) && i.user.id === interactionUserId ); @@ -139,8 +174,7 @@ module.exports = new Command({ .setCustomId('bio') .setLabel('Bio') .setStyle(TextInputStyle.Paragraph) - .setPlaceholder('Enter your bio') - .setValue(String(bio)) + .setValue(profileData.bio || '') .setRequired(false) ), new ActionRowBuilder().addComponents( @@ -148,8 +182,7 @@ module.exports = new Command({ .setCustomId('timezone') .setLabel('Timezone') .setStyle(TextInputStyle.Short) - .setPlaceholder('Enter your timezone') - .setValue(String(timezone)) + .setValue(profileData.timezone || '') .setRequired(false) ) ); @@ -160,7 +193,6 @@ module.exports = new Command({ if (i.customId === 'clockInOut') { profileData.clockedIn = !profileData.clockedIn; fs.writeFileSync(profilePath, JSON.stringify(profileData, null, 2)); - profileEmbed = updateClockedInStatus(profileEmbed, profileData.clockedIn); if (profileData.clockedIn) { clockedInUsers.add(viewingUserId); @@ -168,157 +200,60 @@ module.exports = new Command({ clockedInUsers.delete(viewingUserId); } - const updatedButtonRow = new ActionRowBuilder() - .addComponents( - new ButtonBuilder() - .setCustomId('editProfile') - .setLabel('Edit Profile') - .setStyle(ButtonStyle.Secondary) - ); + profileEmbed = updateClockedInStatus(profileEmbed, profileData.clockedIn); + const updatedRow = new ActionRowBuilder().addComponents( + ...buttonRow.components.map(button => { + if (button.data.custom_id === 'clockInOut') { + return new ButtonBuilder() + .setCustomId('clockInOut') + .setLabel(profileData.clockedIn ? 'Clock Out' : 'Clock In') + .setStyle(ButtonStyle.Secondary); + } + return ButtonBuilder.from(button.data); + }) + ); - if (isStaff) { - updatedButtonRow.addComponents( - new ButtonBuilder() - .setCustomId('clockInOut') - .setLabel(profileData.clockedIn ? 'Clock Out' : 'Clock In') - .setStyle(ButtonStyle.Secondary) - ); - - if (supportbot.Ticket.ClaimTickets.Enabled) { - updatedButtonRow.addComponents( - new ButtonBuilder() - .setCustomId('viewTicketStats') - .setLabel('View Ticket Stats') - .setStyle(ButtonStyle.Secondary) - ); - } - } - - await i.update({ embeds: [profileEmbed], components: [updatedButtonRow] }); - - let clockedinout = new EmbedBuilder() - .setTitle(profileData.clockedIn ? 'Clocked In!' : 'Clocked Out!') - .setDescription(profileData.clockedIn ? 'You have successfully clocked in.' : 'You have successfully clocked out.') - .setColor(supportbot.Embed.Colours.General) - - await interaction.followUp({ embeds: [clockedinout], ephemeral: true }); + await i.update({ embeds: [profileEmbed], components: [updatedRow] }); } if (i.customId === 'viewTicketStats') { - const TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8")); - const ticketsClaimed = TicketData.tickets.filter(t => t.claimedBy === viewingUserId); - const ticketsOpen = ticketsClaimed.filter(t => t.open); - const totalTickets = ticketsClaimed.length; - - let totalResponseTime = 0; - let responseCount = 0; - - ticketsClaimed.forEach(ticket => { - if (ticket.claimedAt && ticket.createdAt) { - const responseTime = new Date(ticket.claimedAt) - new Date(ticket.createdAt); - totalResponseTime += responseTime; - responseCount++; - } - }); - - const averageResponseTime = responseCount ? totalResponseTime / responseCount : 0; - const averageResponseTimeMinutes = Math.round(averageResponseTime / 60000); - - const statsEmbed = new EmbedBuilder() - .setTitle(msgconfig.TicketStats.Title) - .setDescription(`> Ticket stats for <@${viewingUserId}>`) - .setColor(supportbot.Embed.Colours.General) - .addFields( - { name: msgconfig.TicketStats.OpenTickets, value: `${ticketsOpen.length}`, inline: true }, - { name: msgconfig.TicketStats.TotalTickets, value: `${totalTickets}`, inline: true }, - { name: msgconfig.TicketStats.ResponseTime, value: `${averageResponseTimeMinutes} minutes`, inline: false } - ); - - const statsButtonRow = new ActionRowBuilder() - .addComponents( - new ButtonBuilder() - .setCustomId('showOpenTickets') - .setLabel('Show Open Tickets') - .setStyle(ButtonStyle.Secondary), - new ButtonBuilder() - .setCustomId('backToProfile') - .setLabel('Back to Profile') - .setStyle(ButtonStyle.Secondary) - ); - - await i.update({ embeds: [statsEmbed], components: [statsButtonRow] }); - } - - if (i.customId === 'showOpenTickets') { - const TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8")); - const ticketsOpen = TicketData.tickets.filter(t => t.claimedBy === viewingUserId && t.open); - - const openTicketsEmbed = new EmbedBuilder() - .setTitle('Active Tickets') - .setDescription(ticketsOpen.map(t => `<#${t.id}>`).join('\n') || 'No active tickets.') + // Handle ticket stats viewing here + const ticketStatsEmbed = new EmbedBuilder() + .setTitle('Ticket Stats') + .setDescription('Here are your ticket stats...') .setColor(supportbot.Embed.Colours.General); - const openTicketsButtonRow = new ActionRowBuilder() - .addComponents( - new ButtonBuilder() - .setCustomId('backToStats') - .setLabel('Back to Stats') - .setStyle(ButtonStyle.Secondary) - ); - - await i.update({ embeds: [openTicketsEmbed], components: [openTicketsButtonRow] }); + await i.update({ embeds: [ticketStatsEmbed], components: [] }); } - if (i.customId === 'backToStats') { - const TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8")); - const ticketsClaimed = TicketData.tickets.filter(t => t.claimedBy === viewingUserId); - const ticketsOpen = ticketsClaimed.filter(t => t.open); - const totalTickets = ticketsClaimed.length; + if (i.customId === 'editSchedule') { + const modal = new ModalBuilder() + .setCustomId('editScheduleModal') + .setTitle('Edit Schedule'); - let totalResponseTime = 0; - let responseCount = 0; - - ticketsClaimed.forEach(ticket => { - if (ticket.claimedAt && ticket.createdAt) { - const responseTime = new Date(ticket.claimedAt) - new Date(ticket.createdAt); - totalResponseTime += responseTime; - responseCount++; - } + ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'].forEach(day => { + modal.addComponents( + new ActionRowBuilder().addComponents( + new TextInputBuilder() + .setCustomId(`${day.toLowerCase()}Start`) + .setLabel(`${day} Start Time (HH:mm)`) + .setStyle(TextInputStyle.Short) + .setRequired(false) + .setValue(schedule[day.toLowerCase()]?.start || '') + ), + new ActionRowBuilder().addComponents( + new TextInputBuilder() + .setCustomId(`${day.toLowerCase()}End`) + .setLabel(`${day} End Time (HH:mm)`) + .setStyle(TextInputStyle.Short) + .setRequired(false) + .setValue(schedule[day.toLowerCase()]?.end || '') + ) + ); }); - const averageResponseTime = responseCount ? totalResponseTime / responseCount : 0; - const averageResponseTimeMinutes = Math.round(averageResponseTime / 60000); - - const statsEmbed = new EmbedBuilder() - .setTitle(msgconfig.TicketStats.Title) - .setDescription(`> Ticket stats for <@${viewingUserId}>`) - .setColor(supportbot.Embed.Colours.General) - .addFields( - { name: msgconfig.TicketStats.OpenTickets, value: `${ticketsOpen.length}`, inline: true }, - { name: msgconfig.TicketStats.TotalTickets, value: `${totalTickets}`, inline: true }, - { name: msgconfig.TicketStats.ResponseTime, value: `${averageResponseTimeMinutes} minutes`, inline: false } - ); - - const statsButtonRow = new ActionRowBuilder() - .addComponents( - new ButtonBuilder() - .setCustomId('showOpenTickets') - .setLabel('Show Open Tickets') - .setStyle(ButtonStyle.Secondary), - new ButtonBuilder() - .setCustomId('backToProfile') - .setLabel('Back to Profile') - .setStyle(ButtonStyle.Secondary) - ); - - await i.update({ embeds: [statsEmbed], components: [statsButtonRow] }); - } - - if (i.customId === 'backToProfile') { - await i.update({ embeds: [profileEmbed], components: buttonRow.components.length > 0 ? [buttonRow] : [] }); + await i.showModal(modal); } }); } }); - -module.exports.clockedInUsers = clockedInUsers; diff --git a/Commands/ticket.js b/Commands/ticket.js index f89261d..5260bd9 100644 --- a/Commands/ticket.js +++ b/Commands/ticket.js @@ -9,6 +9,7 @@ const { StringSelectMenuBuilder, StringSelectMenuOptionBuilder, ButtonStyle, + MessageCollector, } = require("discord.js"); const yaml = require("js-yaml"); @@ -20,6 +21,23 @@ const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8")); const Command = require("../Structures/Command.js"); const TicketNumberID = require("../Structures/TicketID.js"); +async function isBlacklisted(userId) { + let blacklistData; + try { + blacklistData = JSON.parse(fs.readFileSync("./Data/BlacklistedUsers.json", "utf8")); + } catch (error) { + console.error("Error reading BlacklistedUsers.json:", error); + return false; // Assume not blacklisted if there's an error + } + + if (!blacklistData || !Array.isArray(blacklistData.blacklistedUsers)) { + console.error("blacklistedUsers is not defined or is not an array"); + return false; + } + + return blacklistData.blacklistedUsers.includes(userId); +} + async function getClockedInUsers(guild) { const clockedInUsers = new Set(); @@ -37,6 +55,60 @@ async function getClockedInUsers(guild) { return clockedInUsers; } +async function askTicketQuestions(ticketChannel, interaction, questions) { + const answers = []; + let questionIndex = 0; + + const sendNextQuestion = async () => { + if (questionIndex < questions.length) { + + const questionsEmbed = new EmbedBuilder() + .setDescription(`**${questionIndex + 1}.** ${questions[questionIndex]}`) + .setColor(supportbot.Embed.Colours.General) + + const questionMessage = await ticketChannel.send({ + embeds: [questionsEmbed], + }); + + const collector = new MessageCollector(ticketChannel, { + filter: (m) => m.author.id === interaction.user.id, + max: 1, + time: 60000, + }); + + collector.on("collect", async (message) => { + answers.push({ question: questions[questionIndex], answer: message.content }); + questionIndex++; + await message.delete(); + await questionMessage.delete(); + sendNextQuestion(); + }); + + } else { + + const summaryEmbed = new EmbedBuilder() + .setTitle(msgconfig.Ticket.TicketQuestions.Details.Title) + .setDescription(msgconfig.Ticket.TicketQuestions.Details.Description.replace('%user%', interaction.user.id)) + .setColor(supportbot.Embed.Colours.General) + .addFields( + answers.map((a) => ({ + name: `Q: ${a.question}`, + value: `A: ${a.answer}`, + inline: false, + })) + ) + .setTimestamp(); + + await ticketChannel.send({ + embeds: [summaryEmbed] + }); + } + }; + + // Start asking the first question + sendNextQuestion(); +} + async function assignTicketToUser(ticketChannel, Staff, Admin, interaction, attempts = 0) { try { @@ -205,7 +277,16 @@ module.exports = new Command({ permissions: cmdconfig.OpenTicket.Permission, async run(interaction) { + try { + + if (await isBlacklisted(interaction.user.id)) { + return interaction.reply({ + content: msgconfig.Ticket.Blacklisted, + ephemeral: true, + }); + } + let TicketReason = null; if (supportbot.Ticket.TicketReason) { TicketReason = interaction.reason || null; // Retrieve the reason from the interaction object @@ -352,6 +433,14 @@ module.exports = new Command({ }); } + // Ask questions to the user after the ticket is opened + if (supportbot.Ticket.Questions.Enabled) { + const questions = supportbot.Ticket.Questions.List || []; + if (questions.length > 0) { + await askTicketQuestions(ticketChannel, interaction, questions); + } + } + // Save ticket data TicketData.tickets.push({ id: ticketChannel.id, @@ -405,7 +494,6 @@ module.exports = new Command({ ) .setColor(supportbot.Embed.Colours.General); - // Add reason field if ticket reason is enabled if (supportbot.Ticket.TicketReason && TicketReason) { TicketMessage.addFields( { name: "Reason", value: TicketReason, inline: false } @@ -422,6 +510,11 @@ module.exports = new Command({ .setDescription("Close the ticket.") .setEmoji(supportbot.SelectMenus.Tickets.CloseEmoji) .setValue("ticketclose"), + new StringSelectMenuOptionBuilder() + .setLabel("Voice Channel") + .setDescription("Create a support voice channel.") + .setEmoji(supportbot.SelectMenus.Tickets.VCEmoji) + .setValue("supportvc"), new StringSelectMenuOptionBuilder() .setLabel("Archive") .setDescription("Archive the ticket.") @@ -463,6 +556,7 @@ module.exports = new Command({ components: [row2], }); + } catch (error) { console.error("Error in run method:", error); } diff --git a/Configs/commands.yml b/Configs/commands.yml index cd72e55..4f3a0cf 100644 --- a/Configs/commands.yml +++ b/Configs/commands.yml @@ -7,13 +7,11 @@ # SupportBot created by Emerald Services # Installed with MIT License # -# Discord Support: https://dsc.gg/emerald-dev -# Official Documentation: https://emeraldsrv.dev/wiki -# Third-Party Documentation: https://emeraldsrv.dev/third-party +# Discord Support: https://discord.gg/emeraldsrv +# Official Documentation: https://docs.emeraldsrv.com +# Addons Marketplace: https://community.emeraldsrv.com # -# Command Configuration - -# If you don't want to mess with this configuration, You can manage commands via Discord Settings the Intregations. +# Commands Configuration (commands.yml) Help: Command: "help" @@ -99,6 +97,32 @@ Ping: Permission: ["SendMessages"] Enabled: true +# --------------------------------------- +# Bot Moderator Commands +# --------------------------------------- + +Mod: + Command: "mod" + Description: "Execute a moderation action." + Permission: ["BanMembers", "KickMembers"] + Enabled: true + + TicketBlacklist: + Command: "ticket-blacklist" + Description: "Blacklist a user from creating Tickets." + + Add: + Command: "add" + Description: "Add a user to the blacklist." + + Remove: + Command: "remove" + Description: "Remove a user from the blacklist." + + View: + Command: "view" + Description: "View all blacklisted users." + # --------------------------------------- # Bot Administrator Commands # --------------------------------------- diff --git a/Configs/messages.yml b/Configs/messages.yml index 5da7eb2..9733627 100644 --- a/Configs/messages.yml +++ b/Configs/messages.yml @@ -7,11 +7,15 @@ # SupportBot created by Emerald Services # Installed with MIT License # -# Discord Support: https://dsc.gg/emerald-dev -# Official Documentation: https://emeraldsrv.com/wiki -# Third-Party Documentation: https://emeraldsrv.com/third-party +# Discord Support: https://discord.gg/emeraldsrv +# Official Documentation: https://docs.emeraldsrv.com +# Addons Marketplace: https://community.emeraldsrv.com # -# Messages Configuration +# Messages Configuration (messages.yml) + +# ---------------------------- +# Info Command +# ---------------------------- Info: Title: "About Us" @@ -20,6 +24,10 @@ Info: URL: "https://docs.emeraldsrv.com" Colour: "#f74343" +# ---------------------------- +# Welcome/Leave Messages +# ---------------------------- + Welcome: Embed: Colour: "#f74343" @@ -40,12 +48,20 @@ Leave: ImageEnabled: false ImageURL: "https://animagelink.png/" +# ---------------------------- +# Suggestion Messages +# ---------------------------- + Suggestions: Sent_Title: "Suggestion Sent!" Sent: ":white_check_mark: **You have successfully sent a suggestion.**" NoSelfVote: ":x: **You cannot vote on your own suggestion.**" RemoveVoteButton: "Remove Vote" +# ---------------------------- +# Ticket Messages +# ---------------------------- + Ticket: # Available Placeholders: @@ -95,6 +111,44 @@ Ticket: ReassigningTitle: "Reassigning!..." ReassigningMessage: "<@%user%> did not claim the ticket. **Reassigning...**" + TicketQuestions: + Details: + Title: "Ticket Details" + Description: "Questions by <@%user%>" # Use <@%user%> to display the username. + +# ---------------------------- +# Review Messages +# ---------------------------- + +ReviewSystem: + Rate: + Title: "Review us!" + Description: "Please rate our support for this ticket (1-5 stars)." + + Stars: + One: "⭐ 1 Star" + Two: "⭐⭐ 2 Stars" + Three: "⭐⭐⭐ 3 Stars" + Four: "⭐⭐⭐⭐ 4 Stars" + Five: "⭐⭐⭐⭐⭐ 5 Stars" + + Comment: + Title: "Write a comment." + Description: "Write a comment about your experience." + + ReviewEmbed: + Title: "Ticket Review" + CommentTitle: "Comment" + RatingTitle: "Rating" + ReviewedStaffTitle: "Reviewed Staff:" + ReviewedByTitle: "Reviewed By:" + ReviewEmoji: "⭐" + Color: "#f7a943" + +# ---------------------------- +# Profile Messages +# ---------------------------- + TicketStats: OpenTickets: "✅ Open Tickets" TotalTickets: "🎫 Total Tickets" @@ -102,10 +156,46 @@ TicketStats: Title: "Your Ticket Stats" +# ---------------------------- +# Mod Command Messages +# ---------------------------- + +Mod: + + TicketBlacklist: + Add: + Description: "Add a user to the ticket blacklist" + UserDescription: "User to blacklist" + AlreadyBlacklisted: ":x: {userTag} is already blacklisted." + Success: ":white_check_mark: {userTag} has been added to the blacklist. They can no longer use the ticket system." + + Remove: + Description: "Remove a user from the ticket blacklist" + UserDescription: "User to remove from blacklist" + NotBlacklisted: ":x: {userTag} is not blacklisted." + Success: ":white_check_mark: {userTag} has been removed from the blacklist. They can now use the ticket system again." + + View: + Description: "View the list of all blacklisted users" + NoBlacklistedUsers: ":information_source: There are currently no users on the blacklist." + + +# ---------------------------- +# Logging Messages +# ---------------------------- + TicketLog: Title: "Ticket Log" Colour: "#0cc559" +TicketBlacklistLog: + Title: "Ticket Blacklist Log" + Colour: "#0cc559" + +# ---------------------------- +# Misc Messages +# ---------------------------- + AddUser: NotInTicket_Title: "Ticket Invitation" NotInTicket_Description: "This command can only be used within a ticket thread." @@ -171,11 +261,13 @@ ForceAddUser: Error_Adding: "There was an error adding the user to the ticket." Error: - IncorrectPerms: ":x: **Err!** You do not have the correct permissions to use this." - InvalidChannel: ":x: **Err!** Channel not found. Please create it!" - UserNotFound: ":x: **Err!** This user doesn't exist, Are they in this server?" - NoValidTicket: ":warning: You cannot execute this command here." - TicketExists: ":warning: This ticket cannot be opened, It already exists!" + IncorrectPermissions: ":x: **Error!** You do not have the correct permissions to use this command." + MissingChannel: ":x: **Error!** Required channel not found. Please ensure it is set up correctly!" + UserNotFound: ":x: **Error!** Specified user not found. Please check if they are in this server." + CommandNotAllowedHere: ":warning: This command cannot be used in this channel." + EntryAlreadyExists: ":warning: Cannot complete the action, as the entry already exists." + EntryDoesNotExist: ":warning: Cannot complete the action, as the entry does not exist." + ActionFailed: ":x: **Error!** An error occurred while processing your request. Please try again later." diff --git a/Configs/supportbot.yml b/Configs/supportbot.yml index d781aa2..ea6b514 100644 --- a/Configs/supportbot.yml +++ b/Configs/supportbot.yml @@ -4,15 +4,16 @@ # <___/`___|| _/| _/\___/|_| |_| |___/\___/ |_| # |_| |_| # -# SupportBot created by Emerald Development +# SupportBot created by Emerald Services # Installed with MIT License # -# Discord Support: https://dsc.gg/emerald-dev -# Official Documentation: https://github.com/Emerald-Services/SupportBot/wiki -# Third-Party Documentation: COMING SOON +# Discord Support: https://discord.gg/emeraldsrv +# Official Documentation: https://docs.emeraldsrv.com +# Addons Marketplace: https://community.emeraldsrv.com +# +# SupportBot Configuration (supportbot.yml) -SupportBot_Version: "v8.2" -# Do not change the version number +SupportBot_Version: "v8.3" # ------------------------- # General @@ -20,8 +21,7 @@ SupportBot_Version: "v8.2" General: Name: "SupportBot" - Token: "BOT_TOKEN_HERE" - # To get your Discord Bot Token, Go to https://discord.com/developers/applications + Token: "BOT_TOKEN" Addons: Enabled: true @@ -33,11 +33,16 @@ General: Activity: Status: "SupportBot v8" Type: "Competing" - # The Type can either be Playing, Watching, Listening or Competing StreamingURL: "https://www.twitch.tv/YOUR_TWITCH_NAME" - # Set The Type to "Streaming" for a streaming status! - # You can update the activity via Discord using: /settings + # Available Types: + # Playing + # Watching + # Listening + # Competing + # Streaming (URL required) + + # You can also update the activity via Discord using: /settings # ------------------------- # Embeds @@ -45,13 +50,15 @@ Activity: Embed: Colours: - General: 0x0cc559 # 0x - Success: 0x04b545 # 0x - Error: 0xd13030 # 0x - Warn: 0xfca117 # 0x + General: 0x0cc559 + Success: 0x04b545 + Error: 0xd13030 + Warn: 0xfca117 Footer: "SupportBot | Developed by Emerald Development" + # Use 0x to change the colour of the embeds. + # ------------------------- # Roles # ------------------------- @@ -62,12 +69,14 @@ Roles: Role: "ROLE_ID" StaffMember: - Staff: "STAFF_ROLE_ID" - Admin: "ADMIN_ROLE_ID" + Staff: "ROLE_ID" + Admin: "ROLE_ID" + Moderator: "ROLE_ID" - ModRoles: - Muted: "MUTED_ROLE_ID" - Blacklisted: "BLACKLISTED_ROLE_ID" + Mod: + AllowSupportStaff: false + + # Allow Support Staff to use the /mod command. (More in detail customisation in commands.yml) # ---------------------------- # System Messages @@ -75,18 +84,18 @@ Roles: Welcome: Enabled: true - Channel: "WELCOME_CHANNEL_ID" + Channel: "JOIN_CHANNEL_ID" Leave: Enabled: true - Channel: "LEAVE_CHANNEL_ID" + Channel: "JOIN_CHANNEL_ID" # ---------------------------- # Suggestions # ---------------------------- Suggestions: - Channel: "SUGGEST_CHANNEL_ID" + Channel: "SUGGESTIONS_CHANNEL_ID" UpvoteEmoji: "✅" DownvoteEmoji: "❌" @@ -94,6 +103,7 @@ Suggestions: DownvoteTitle: "No" OwnSuggestion: true + # If false this will not allow the user to vote for a suggestion they created # If true this will allow the user to vote for a suggestion they created @@ -120,23 +130,27 @@ Suggestions: # Tickets # ------------------------- -Ticket: - TicketType: "channels" # Choose how you want tickets to be created. - # Options: - # channels - Tickets will be created as channels in the - # threads - Tickets will be created as threads through the "TicketHome" +VoiceTickets: + Name: "%username%'s VC" + Category: "VOICEE_CATEGORY_ID" + +Ticket: + TicketType: "channels" + + # Available TicketTypes: + # channels - Tickets will be created as channels in the TicketChannelsCategory + # threads - Tickets will be created as threads through the "TicketHome" channel. - # For TicketType "Threads" TicketHome: "TICKETHOME_CHANNEL_ID" + # What is TicketHome? # - Channel where the Ticket creation panel is sent. # - Channel where ticket threads are made. (only if ticket threads is enabled above.) - # For TicketType "Channels" - TicketChannelsCategory: "TICKET_CATEGORY_ID" # if Type is set to channels - TicketChannelsCategory2: "BACKUP_TICKET_CATEGORY_ID" # This will be used if the first category has reached the max channel limit. - TicketArchiveCategory: "ARCHIVE_TICKET_CATEGORY_ID" - + # For TicketType: channels + TicketChannelsCategory: "TICKETCHANNEL_CATEGORY_ID" + TicketChannelsCategory2: "TICKETCHANNEL_BACKUP_CATEGORY_ID" # This will be used if the first category has reached the max channel limit. + TicketArchiveCategory: "ARCHIVE_CATEGORY_ID" # --- Channel: "ticket-" # The prefix for ticket channel names @@ -144,16 +158,16 @@ Ticket: DMTranscripts: true TicketReason: true # If ticket reason is set to false, It will not prompt to create a reason + TicketSubject: "description" + + # Available TicketSubjects: # "description" will display the ticket reason within the channel description # "embed" will display the ticket reason within the ticket embed + Timeout: 5 # Set to true to allow all staff to access the ticket - Log: - TicketDataLog: "TICKET_LOG_ID" - TickeDataTitle: "Ticket Data" - Close: StaffOnly: true # Set to false is anyone is able to close a ticket. @@ -164,7 +178,7 @@ Ticket: Title: "Are you sure you want to close this ticket?" - # Available Button Styles + # Available Button Styles: # 1 - Blurple # 2 - Grey # 3 - Green @@ -176,7 +190,7 @@ Ticket: ClaimTickets: Enabled: true Channel: "CLAIM_TICKETS_CHANNEL_ID" - # Channel name or Channel ID + # Channel where the ticket assignments are sent for staff to claim. ClaimAcceptTime: 120000 # How long the assigned user has to accept the ticket before it gets re-assigned to someone else. @@ -196,10 +210,34 @@ Ticket: # 3 - Green # 4 - Red + ReviewSystem: + Enabled: true + Channel: "REVIEW_CHANNEL_ID" + UseModal: true + + Questions: + Enabled: true # Set to false to disable ticket questions + List: + - "Please provide your account email?" + - "What is the nature of your issue?" + - "Have you tried resolving the issue yourself?" + + Log: + TicketDataLog: "TICKET_DATA_LOG_ID" + TickeDataTitle: "Ticket Data" + TicketBlacklistLog: "TICKET_BLACKLIST_LOG_ID" + # ------------------------- # Buttons # ------------------------- Buttons: + General: + Delete: "🗑️" + DeleteStyle: "4" + + Voice: + TicketDeleteText: "Delete Voice Channel" + Tickets: ClaimEmoji: "🧑‍💻" ClaimStyle: "1" @@ -230,13 +268,22 @@ SelectMenus: ArchiveEmoji: "♻️" LockEmoji: "🔐" RenameEmoji: "🏷️" + VCEmoji: "🔊" EnableInvitesEmoji: "📤" DisableInvitesEmoji: "📥" # ------------------------- -# Misc +# Logging # ------------------------- +MessageDelete: + Channel: "MESSAGE_DELETE_CHANNEL_ID"" + Colour: "#d92525" + +MessageUpdate: + Channel: "MESSAGE_UPDATE_CHANNEL_ID" + Colour: "#f2a024" + Translate: TranslateLog: "TRANSLATE_LOG_ID" diff --git a/Configs/ticket-panel.yml b/Configs/ticket-panel.yml index 28072eb..5cb7f3d 100644 --- a/Configs/ticket-panel.yml +++ b/Configs/ticket-panel.yml @@ -7,8 +7,11 @@ # SupportBot created by Emerald Services # Installed with MIT License # -# Discord Support: https://emeraldsrv.dev/discord -# Community Resources: https://emeraldsrv.dev/resources +# Discord Support: https://discord.gg/emeraldsrv +# Official Documentation: https://docs.emeraldsrv.com +# Addons Marketplace: https://community.emeraldsrv.com +# +# Ticket Creation Panel Configuration (ticket-panel.yml) # ------------------------------- # Ticket Panel Configuration diff --git a/Data/BlacklistedUsers.json b/Data/BlacklistedUsers.json new file mode 100644 index 0000000..257b226 --- /dev/null +++ b/Data/BlacklistedUsers.json @@ -0,0 +1,3 @@ +{ + "blacklistedUsers": [] +} \ No newline at end of file diff --git a/Data/Profiles/000000000000000.json b/Data/Profiles/000000000000000.json new file mode 100644 index 0000000..2e52c6b --- /dev/null +++ b/Data/Profiles/000000000000000.json @@ -0,0 +1,29 @@ +{ + "bio": "", + "timezone": "", + "clockedIn": true, + "workingHours": 0, + "lastClockInTime": null, + "schedule": { + "monday": { + "start": "09:00", + "end": "17:00" + }, + "tuesday": { + "start": "09:00", + "end": "17:00" + }, + "wednesday": { + "start": "09:00", + "end": "17:00" + }, + "thursday": { + "start": "09:00", + "end": "17:00" + }, + "friday": { + "start": "09:00", + "end": "17:00" + } + } +} \ No newline at end of file diff --git a/Data/Profiles/0000000000000000.json b/Data/Profiles/0000000000000000.json new file mode 100644 index 0000000..0088126 --- /dev/null +++ b/Data/Profiles/0000000000000000.json @@ -0,0 +1,5 @@ +{ + "bio": "", + "timezone": "", + "clockedIn": false +} diff --git a/Data/TicketData.json b/Data/TicketData.json index 182fdc3..987b7f9 100644 --- a/Data/TicketData.json +++ b/Data/TicketData.json @@ -1,5 +1,5 @@ { "tickets": [ - + {} ] } \ No newline at end of file diff --git a/Data/ticket-panel-id.json b/Data/ticket-panel-id.json index 3af9513..571a32a 100644 --- a/Data/ticket-panel-id.json +++ b/Data/ticket-panel-id.json @@ -1,4 +1,4 @@ { "id": 0, - "TicketPanelID": "0000000000000000000" -} + "TicketPanelID": "000000000000000" +} \ No newline at end of file diff --git a/Events/interactionCreate.js b/Events/interactionCreate.js index 9d4bcf6..1849074 100644 --- a/Events/interactionCreate.js +++ b/Events/interactionCreate.js @@ -130,7 +130,6 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { }); } } - if (interaction.isStringSelectMenu()) { if (interaction.customId === "ticketcontrolpanel") { const selectedOption = interaction.values[0]; @@ -146,6 +145,89 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { } break; + case "supportvc": + const ticketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8")); + const ticketInfo = ticketData.tickets.find(t => t.id === interaction.channel.id); + + if (ticketInfo) { + const user = interaction.guild.members.cache.get(ticketInfo.user) || ticketInfo.user; + const claimedBy = interaction.guild.members.cache.get(ticketInfo.claimedBy) || ticketInfo.claimedBy; + const Admin = await interaction.guild.roles.fetch(supportbot.Roles.StaffMember.Admin); + const Staff = await interaction.guild.roles.fetch(supportbot.Roles.StaffMember.Staff); + + const VCcategory = interaction.guild.channels.cache.find( + (c) => c.name === supportbot.VoiceTickets.Category || c.id === supportbot.VoiceTickets.Category + ); + + if (!VCcategory) { + return interaction.reply({ + content: "The Support VC category does not exist!", + ephemeral: true, + }); + } + + let username = user ? user.user.username : "Unknown User"; + + let SupportVoice = await interaction.guild.channels.create({ + name: supportbot.VoiceTickets.Name.replace(/%username%/g, username), + type: Discord.ChannelType.GuildVoice, + parent: VCcategory, + permissionOverwrites: [ + { + id: user, + allow: [ + Discord.PermissionsBitField.Flags.Connect, + Discord.PermissionsBitField.Flags.Speak, + Discord.PermissionsBitField.Flags.ViewChannel, + ], + }, + { + id: Staff, + allow: [ + Discord.PermissionsBitField.Flags.Connect, + Discord.PermissionsBitField.Flags.Speak, + Discord.PermissionsBitField.Flags.ViewChannel, + ], + }, + { + id: Admin, + allow: [ + Discord.PermissionsBitField.Flags.Connect, + Discord.PermissionsBitField.Flags.Speak, + Discord.PermissionsBitField.Flags.ViewChannel, + ], + }, + { + id: interaction.guild.id, + deny: [Discord.PermissionsBitField.Flags.ViewChannel], + }, + ], + }); + + ticketInfo.voiceChannelId = SupportVoice.id; + + fs.writeFileSync("./Data/TicketData.json", JSON.stringify(ticketData, null, 2)); + + const VCDeleteButton = new Discord.ButtonBuilder() + .setCustomId("deleteSupportVC") + .setLabel(supportbot.Buttons.Voice.TicketDeleteText) + .setEmoji(supportbot.Buttons.General.Delete) + .setStyle(supportbot.Buttons.General.DeleteStyle); + + const VCDeleteRow = new Discord.ActionRowBuilder().addComponents(VCDeleteButton); + + const vcmadeembed = new Discord.EmbedBuilder() + .setDescription(`> **Support VC Created:** <#${SupportVoice.id}>`) + .setColor(supportbot.Embed.Colours.Success); + + await interaction.reply({ + embeds: [vcmadeembed], + components: [VCDeleteRow], + }); + + } + break; + case "archiveticket": const arcembed = new Discord.EmbedBuilder() .setDescription(msgconfig.Ticket.TicketArchived) @@ -306,6 +388,64 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { } } + if (interaction.customId === 'deleteSupportVC') { + const ticketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8")); + const ticketInfo = ticketData.tickets.find(t => t.id === interaction.channel.id); + + if (ticketInfo && ticketInfo.voiceChannelId) { + try { + const voiceChannel = await interaction.guild.channels.fetch(ticketInfo.voiceChannelId).catch(() => null); + + if (voiceChannel) { + await voiceChannel.delete(); + + const successEmbed = new Discord.EmbedBuilder() + .setDescription('✅ Support voice channel has been successfully deleted!') + .setColor(supportbot.Embed.Colours.Success); + + await interaction.reply({ + embeds: [successEmbed], + ephemeral: true, + }); + + ticketInfo.voiceChannelId = null; + fs.writeFileSync("./Data/TicketData.json", JSON.stringify(ticketData, null, 2)); + } else { + const errorEmbed = new Discord.EmbedBuilder() + .setDescription('❌ The associated voice channel could not be found.') + .setColor(supportbot.Embed.Colours.Error); + + await interaction.reply({ + embeds: [errorEmbed], + ephemeral: true, + }); + } + } catch (error) { + console.error('Error deleting the Voice Channel:', error); + + const errorEmbed = new Discord.EmbedBuilder() + .setDescription('❌ **There was an error trying to delete this voice channel. Please try again later.**') + .setColor(supportbot.Embed.Colours.Error); + + await interaction.reply({ + embeds: [errorEmbed], + ephemeral: true, + }); + } + } else { + const errorEmbed = new Discord.EmbedBuilder() + .setDescription('⚠️ This button is not linked to a valid voice channel.') + .setColor(supportbot.Embed.Colours.Warn); + + await interaction.reply({ + embeds: [errorEmbed], + ephemeral: true, + }); + } + } + + + if (interaction.isButton()) { if (interaction.customId === "unarchiveTicket") { diff --git a/Events/messageDelete.js b/Events/messageDelete.js new file mode 100644 index 0000000..49807f7 --- /dev/null +++ b/Events/messageDelete.js @@ -0,0 +1,43 @@ +const { EmbedBuilder } = require('discord.js'); +const fs = require("fs"); +const yaml = require("js-yaml"); +const Event = require("../Structures/Event.js"); + +// Load the configuration files +const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8")); +const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8")); + +module.exports = new Event("messageDelete", async (client, message) => { + + // Check if the message is from a guild and not from a bot + if (!message.guild || message.author.bot) return; + + // Get the delete log channel either by ID or by name + const deleteLogChannel = message.guild.channels.cache.get(supportbot.MessageDelete.Channel) || + message.guild.channels.cache.find(channel => channel.name === supportbot.MessageDelete.Channel); + + // Create the embed for the deleted message + const deletedMessageEmbed = new EmbedBuilder() + .setColor(supportbot.MessageDelete.Colour) + .setTitle("Message Deleted") + .setDescription(`> **Channel:** <#${message.channel.id}>\n> **Message ID** [${message.id}](https://discord.com/channels/${message.guild.id}/${message.channel.id}/${message.id})\n> **Message author:** ${message.author.tag} (${message.author.id})\n> **Message Crated:** `) + .addFields( + { name: 'Message', value: message.content || '*No content*', inline: false } + ) + .setThumbnail(message.author.displayAvatarURL()) + .setTimestamp(); + + // If the message contains an attachment, add it to the embed + if (message.attachments.size > 0) { + const attachment = message.attachments.first(); + deletedMessageEmbed.addFields({ name: 'Attachment', value: `[${attachment.name}](${attachment.proxyURL})` }); + } + + // Send the embed to the delete log channel if the channel is found + if (deleteLogChannel) { + deleteLogChannel.send({ + embeds: [deletedMessageEmbed] + }); + } +}); + diff --git a/Events/messageUpdate.js b/Events/messageUpdate.js new file mode 100644 index 0000000..4fac6a0 --- /dev/null +++ b/Events/messageUpdate.js @@ -0,0 +1,57 @@ +const { EmbedBuilder } = require('discord.js'); +const fs = require("fs"); +const yaml = require("js-yaml"); +const Event = require("../Structures/Event.js"); + +// Load the configuration files +const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8")); +const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8")); + +module.exports = new Event("messageUpdate", async (client, oldMessage, newMessage) => { + + // Check if the message is from a guild and not from a bot + if (!newMessage.guild || newMessage.author.bot) return; + + // Fetch old message if it's not cached (important for message content) + if (!oldMessage.content && oldMessage.partial) { + try { + oldMessage = await oldMessage.fetch(); + } catch (err) { + console.error('Error fetching old message:', err); + return; + } + } + + // Fallback if content is missing + const oldMessageContent = oldMessage.content || (oldMessage.attachments.size > 0 ? 'Contains attachment(s)' : '*No content*'); + const newMessageContent = newMessage.content || (newMessage.attachments.size > 0 ? 'Contains attachment(s)' : '*No content*'); + + // Get the update log channel either by ID or by name + const updateLogChannel = newMessage.guild.channels.cache.get(supportbot.MessageUpdate.Channel) || + newMessage.guild.channels.cache.find(channel => channel.name === supportbot.MessageUpdate.Channel); + + // Create the embed for the updated message + const updatedMessageEmbed = new EmbedBuilder() + .setColor(supportbot.MessageUpdate.Colour) + .setTitle("Message Updated") + .setDescription(`> **Channel:** <#${newMessage.channel.id}>\n> **Message ID:** [${newMessage.id}](https://discord.com/channels/${newMessage.guild.id}/${newMessage.channel.id}/${newMessage.id})\n> **Message author:** ${newMessage.author.tag} (${newMessage.author.id})\n> **Message Created:** `) + .addFields( + { name: 'Old Message', value: oldMessageContent, inline: false }, + { name: 'New Message', value: newMessageContent, inline: false } + ) + .setThumbnail(newMessage.author.displayAvatarURL()) + .setTimestamp(); + + // If the new message contains an attachment, add it to the embed + if (newMessage.attachments.size > 0) { + const attachment = newMessage.attachments.first(); + updatedMessageEmbed.addFields({ name: 'Attachment', value: `[${attachment.name}](${attachment.proxyURL})` }); + } + + // Send the embed to the update log channel if the channel is found + if (updateLogChannel) { + updateLogChannel.send({ + embeds: [updatedMessageEmbed] + }); + } +}); diff --git a/Events/ready.js b/Events/ready.js index 51fe00e..69a7908 100644 --- a/Events/ready.js +++ b/Events/ready.js @@ -89,9 +89,7 @@ module.exports = new Event("ready", async (client, interaction) => { const roles = [ supportbot.Roles.StaffMember.Admin, - supportbot.Roles.StaffMember.Staff, - supportbot.Roles.ModRoles.Blacklisted, - supportbot.Roles.ModRoles.Muted + supportbot.Roles.StaffMember.Staff ]; if (supportbot.Roles.AutoRole.Role) roles.push(supportbot.Roles.AutoRole.Role); diff --git a/Structures/Client.js b/Structures/Client.js index 3786833..812b7e0 100644 --- a/Structures/Client.js +++ b/Structures/Client.js @@ -13,9 +13,10 @@ class Client extends Discord.Client { intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers, - GatewayIntentBits.GuildMessages + GatewayIntentBits.GuildMessages, + GatewayIntentBits.MessageContent ], - partials: [Partials.Channel] + partials: [Partials.Message, Partials.Channel] }); this.commands = new Discord.Collection(); @@ -105,6 +106,10 @@ class Client extends Discord.Client { tempCommandFiles = tempCommandFiles.filter(item => item !== "settings.js"); } + if (cmdconfig.Mod.Enabled === false) { + tempCommandFiles = tempCommandFiles.filter(item => item !== "mod.js"); + } + const commandFiles = tempCommandFiles; const commands = commandFiles.map((file) => require(`../Commands/${file}`)); diff --git a/index.js b/index.js index b2f9578..7343fe9 100644 --- a/index.js +++ b/index.js @@ -19,7 +19,7 @@ const supportbot = yaml.load( const Client = require("./Structures/Client.js"); const client = new Client({ - intents: ['Guilds', 'GuildMembers', 'GuildMessages'] + intents: ['Guilds', 'GuildMembers', 'GuildMessages', 'MessageContent'] }); client.start(supportbot.General.Token); diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 142e265..0000000 --- a/package-lock.json +++ /dev/null @@ -1,1671 +0,0 @@ -{ - "name": "supportbot", - "version": "8.0.1", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "supportbot", - "version": "8.0.1", - "license": "MIT", - "dependencies": { - "@paypal/checkout-server-sdk": "^1.0.3", - "@vitalets/google-translate-api": "^8.0.0", - "axios": "^1.7.2", - "chatgpt": "^5.2.5", - "discord.js": "^14.13.0", - "js": "^0.1.0", - "js-yaml": "^4.1.0", - "moment": "^2.29.3", - "ms": "^2.1.3", - "openai": "^4.50.0", - "paypal-rest-sdk": "^1.8.1" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", - "dependencies": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@discordjs/builders": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.8.2.tgz", - "integrity": "sha512-6wvG3QaCjtMu0xnle4SoOIeFB4y6fKMN6WZfy3BMKJdQQtPLik8KGzDwBVL/+wTtcE/ZlFjgEk74GublyEVZ7g==", - "dependencies": { - "@discordjs/formatters": "^0.4.0", - "@discordjs/util": "^1.1.0", - "@sapphire/shapeshift": "^3.9.7", - "discord-api-types": "0.37.83", - "fast-deep-equal": "^3.1.3", - "ts-mixer": "^6.0.4", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.11.0" - }, - "funding": { - "url": "https://github.com/discordjs/discord.js?sponsor" - } - }, - "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==", - "engines": { - "node": ">=16.11.0" - } - }, - "node_modules/@discordjs/formatters": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.4.0.tgz", - "integrity": "sha512-fJ06TLC1NiruF35470q3Nr1bi95BdvKFAF+T5bNfZJ4bNdqZ3VZ+Ttg6SThqTxm6qumSG3choxLBHMC69WXNXQ==", - "dependencies": { - "discord-api-types": "0.37.83" - }, - "engines": { - "node": ">=16.11.0" - }, - "funding": { - "url": "https://github.com/discordjs/discord.js?sponsor" - } - }, - "node_modules/@discordjs/rest": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.3.0.tgz", - "integrity": "sha512-C1kAJK8aSYRv3ZwMG8cvrrW4GN0g5eMdP8AuN8ODH5DyOCbHgJspze1my3xHOAgwLJdKUbWNVyAeJ9cEdduqIg==", - "dependencies": { - "@discordjs/collection": "^2.1.0", - "@discordjs/util": "^1.1.0", - "@sapphire/async-queue": "^1.5.2", - "@sapphire/snowflake": "^3.5.3", - "@vladfrangu/async_event_emitter": "^2.2.4", - "discord-api-types": "0.37.83", - "magic-bytes.js": "^1.10.0", - "tslib": "^2.6.2", - "undici": "6.13.0" - }, - "engines": { - "node": ">=16.11.0" - }, - "funding": { - "url": "https://github.com/discordjs/discord.js?sponsor" - } - }, - "node_modules/@discordjs/rest/node_modules/@discordjs/collection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.0.tgz", - "integrity": "sha512-mLcTACtXUuVgutoznkh6hS3UFqYirDYAg5Dc1m8xn6OvPjetnUlf/xjtqnnc47OwWdaoCQnHmHh9KofhD6uRqw==", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/discordjs/discord.js?sponsor" - } - }, - "node_modules/@discordjs/util": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.1.0.tgz", - "integrity": "sha512-IndcI5hzlNZ7GS96RV3Xw1R2kaDuXEp7tRIy/KlhidpN/BQ1qh1NZt3377dMLTa44xDUNKT7hnXkA/oUAzD/lg==", - "engines": { - "node": ">=16.11.0" - }, - "funding": { - "url": "https://github.com/discordjs/discord.js?sponsor" - } - }, - "node_modules/@discordjs/ws": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.1.1.tgz", - "integrity": "sha512-PZ+vLpxGCRtmr2RMkqh8Zp+BenUaJqlS6xhgWKEZcgC/vfHLEzpHtKkB0sl3nZWpwtcKk6YWy+pU3okL2I97FA==", - "dependencies": { - "@discordjs/collection": "^2.1.0", - "@discordjs/rest": "^2.3.0", - "@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.83", - "tslib": "^2.6.2", - "ws": "^8.16.0" - }, - "engines": { - "node": ">=16.11.0" - }, - "funding": { - "url": "https://github.com/discordjs/discord.js?sponsor" - } - }, - "node_modules/@discordjs/ws/node_modules/@discordjs/collection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.0.tgz", - "integrity": "sha512-mLcTACtXUuVgutoznkh6hS3UFqYirDYAg5Dc1m8xn6OvPjetnUlf/xjtqnnc47OwWdaoCQnHmHh9KofhD6uRqw==", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/discordjs/discord.js?sponsor" - } - }, - "node_modules/@paypal/checkout-server-sdk": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@paypal/checkout-server-sdk/-/checkout-server-sdk-1.0.3.tgz", - "integrity": "sha512-UEdq8akEdMz0Vs4qoQFU2gMp8PpyE/HKyMwiZuK4vIWUKl8jfd1fWKjQN5cDFm9NkFUIp5U7h++rdMOVj9WMNA==", - "dependencies": { - "@paypal/paypalhttp": "^1.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@paypal/paypalhttp": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@paypal/paypalhttp/-/paypalhttp-1.0.1.tgz", - "integrity": "sha512-DC7AHxTT7drF6dUi3YaFdPVuT15sIkpD5H2eHmdtFgxM4UanS1o1ZDfMhR9mpxd8o+X6pz2r+EZVRRq+n1cssQ==", - "engines": { - "node": ">=4" - } - }, - "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==", - "engines": { - "node": ">=v14.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/@sapphire/shapeshift": { - "version": "3.9.7", - "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.9.7.tgz", - "integrity": "sha512-4It2mxPSr4OGn4HSQWGmhFMsNFGfFVhWeRPCRwbH972Ek2pzfGRZtb0pJ4Ze6oIzcyh2jw7nUDa6qGlWofgd9g==", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "lodash": "^4.17.21" - }, - "engines": { - "node": ">=v16" - } - }, - "node_modules/@sapphire/snowflake": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.3.tgz", - "integrity": "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==", - "engines": { - "node": ">=v14.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "dependencies": { - "defer-to-connect": "^1.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@types/node": { - "version": "20.14.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.12.tgz", - "integrity": "sha512-r7wNXakLeSsGT0H1AU863vS2wa5wBOK4bWMjZz2wj+8nBx+m5PeIn0k8AloSLpRuiwdRQZwarZqHE4FNArPuJQ==", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.0" - } - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", - "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==" - }, - "node_modules/@types/ws": { - "version": "8.5.11", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.11.tgz", - "integrity": "sha512-4+q7P5h3SpJxaBft0Dzpbr6lmMaqh0Jr2tbhJZ/luAwvD7ohSCniYkwz/pLxuT2h0EOa6QADgJj1Ko+TzRfZ+w==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@vitalets/google-translate-api": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@vitalets/google-translate-api/-/google-translate-api-8.0.0.tgz", - "integrity": "sha512-CHOi8DRgDAoLlFQy9pm1KHUauIk5ZXys3+Eh0aSYxzJSuxAE7Z2UL8+5MgNdl1oX7t/O7u+SwGhkhu6+U1bNcg==", - "dependencies": { - "configstore": "^5.0.1", - "got": "^9.6.0" - } - }, - "node_modules/@vladfrangu/async_event_emitter": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.4.tgz", - "integrity": "sha512-ZL62PFXEIeGUI8btfJ5S8Flc286eU1ZUSjwyFQtIGXfRUDPZKO+CDJMYb1R71LjGWRZ4n202O+a6FGjsgTw58g==", - "engines": { - "node": ">=v14.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/agentkeepalive": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", - "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/atomically": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/atomically/-/atomically-2.0.3.tgz", - "integrity": "sha512-kU6FmrwZ3Lx7/7y3hPS5QnbJfaohcIul5fGqf7ok+4KklIEk9tJ0C2IQPdacSbVUWv6zVHXEBWoWd6NrVMT7Cw==", - "dependencies": { - "stubborn-fs": "^1.2.5", - "when-exit": "^2.1.1" - } - }, - "node_modules/axios": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", - "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "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/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "engines": { - "node": "*" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cacheable-request/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cacheable-request/node_modules/json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==" - }, - "node_modules/cacheable-request/node_modules/keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "dependencies": { - "json-buffer": "3.0.0" - } - }, - "node_modules/cacheable-request/node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chatgpt": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/chatgpt/-/chatgpt-5.2.5.tgz", - "integrity": "sha512-DNhBzPb2zTDjJADY44XfngMvsvrvHRq1md2VPXLmnKeP1UCeA1B6pV3s9ZRwlcgjVT0RyM77fRj1xj5V11Vctg==", - "dependencies": { - "cac": "^6.7.14", - "conf": "^11.0.1", - "eventsource-parser": "^1.0.0", - "js-tiktoken": "^1.0.5", - "keyv": "^4.5.2", - "p-timeout": "^6.1.1", - "quick-lru": "^6.1.1", - "read-pkg-up": "^9.1.0", - "uuid": "^9.0.0" - }, - "bin": { - "chatgpt": "bin/cli.js" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/clone-response": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", - "dependencies": { - "mimic-response": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-1.1.1.tgz", - "integrity": "sha512-71Rod2AhcH3JhkBikVpNd0pA+fWsmAaVoti6OR38T76chA7vE3pSerS0Jor4wDw+tOueD2zLVvFOw5H0Rcj7rA==", - "dependencies": { - "keypress": "0.1.x" - }, - "engines": { - "node": ">= 0.6.x" - } - }, - "node_modules/conf": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/conf/-/conf-11.0.2.tgz", - "integrity": "sha512-jjyhlQ0ew/iwmtwsS2RaB6s8DBifcE2GYBEaw2SJDUY/slJJbNfY4GlDVzOs/ff8cM/Wua5CikqXgbFl5eu85A==", - "dependencies": { - "ajv": "^8.12.0", - "ajv-formats": "^2.1.1", - "atomically": "^2.0.0", - "debounce-fn": "^5.1.2", - "dot-prop": "^7.2.0", - "env-paths": "^3.0.0", - "json-schema-typed": "^8.0.1", - "semver": "^7.3.8" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", - "dependencies": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/configstore/node_modules/dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/debounce-fn": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-5.1.2.tgz", - "integrity": "sha512-Sr4SdOZ4vw6eQDvPYNxHogvrxmCIld/VenC5JbNrFwMiwd7lY/Z18ZFfo+EWNG4DD9nFlAujWAo/wGuOPHmy5A==", - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==", - "dependencies": { - "mimic-response": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/discord-api-types": { - "version": "0.37.83", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.83.tgz", - "integrity": "sha512-urGGYeWtWNYMKnYlZnOnDHm8fVRffQs3U0SpE8RHeiuLKb/u92APS8HoQnPTFbnXmY1vVnXjXO4dOxcAn3J+DA==" - }, - "node_modules/discord.js": { - "version": "14.15.3", - "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.15.3.tgz", - "integrity": "sha512-/UJDQO10VuU6wQPglA4kz2bw2ngeeSbogiIPx/TsnctfzV/tNf+q+i1HlgtX1OGpeOBpJH9erZQNO5oRM2uAtQ==", - "dependencies": { - "@discordjs/builders": "^1.8.2", - "@discordjs/collection": "1.5.3", - "@discordjs/formatters": "^0.4.0", - "@discordjs/rest": "^2.3.0", - "@discordjs/util": "^1.1.0", - "@discordjs/ws": "^1.1.1", - "@sapphire/snowflake": "3.5.3", - "discord-api-types": "0.37.83", - "fast-deep-equal": "3.1.3", - "lodash.snakecase": "4.1.1", - "tslib": "2.6.2", - "undici": "6.13.0" - }, - "engines": { - "node": ">=16.11.0" - }, - "funding": { - "url": "https://github.com/discordjs/discord.js?sponsor" - } - }, - "node_modules/dot-prop": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-7.2.0.tgz", - "integrity": "sha512-Ol/IPXUARn9CSbkrdV4VJo7uCy1I3VuSiWCaFSg+8BdUOzF9n3jefIpcgAydvUZbTdEBZs2vEiTiS9m61ssiDA==", - "dependencies": { - "type-fest": "^2.11.2" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/duplexer3": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", - "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==" - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/env-paths": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", - "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/eventsource-parser": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-1.1.2.tgz", - "integrity": "sha512-v0eOBUbiaFojBu2s2NPBfYUoRR9GjcDNvCXVaqEf5vVfpIAh9f8RCo4vXTP8c63QRKCFwoLpMpTdPwwhEKVgzA==", - "engines": { - "node": ">=14.18" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-uri": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", - "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==" - }, - "node_modules/find-up": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", - "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", - "dependencies": { - "locate-path": "^7.1.0", - "path-exists": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/form-data-encoder": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", - "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" - }, - "node_modules/formdata-node": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", - "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", - "dependencies": { - "node-domexception": "1.0.0", - "web-streams-polyfill": "4.0.0-beta.3" - }, - "engines": { - "node": ">= 12.20" - } - }, - "node_modules/formdata-node/node_modules/web-streams-polyfill": { - "version": "4.0.0-beta.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", - "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", - "engines": { - "node": ">= 14" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "dependencies": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "dependencies": { - "ms": "^2.0.0" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "node_modules/is-core-module": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", - "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, - "node_modules/js": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/js/-/js-0.1.0.tgz", - "integrity": "sha512-ZBbGYOpact8QAH9RprFWL4RAESYwbDodxiuDjOnzwzzk9pBzKycoifGuUrHHcDixE/eLMKPHRaXenTgu1qXBqA==", - "dependencies": { - "commander": "~1.1.1" - }, - "bin": { - "js": "bin/js" - } - }, - "node_modules/js-tiktoken": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.12.tgz", - "integrity": "sha512-L7wURW1fH9Qaext0VzaUDpFGVQgjkdE3Dgsy9/+yXyGEpBKnylTd0mU0bfbNkKDlXRb6TEsZkwuflu1B8uQbJQ==", - "dependencies": { - "base64-js": "^1.5.1" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "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==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/json-schema-typed": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.1.tgz", - "integrity": "sha512-XQmWYj2Sm4kn4WeTYvmpKEbyPsL7nBsb647c7pMe6l02/yx2+Jfc4dT6UZkEXnIUb5LhD55r2HPsJ1milQ4rDg==" - }, - "node_modules/keypress": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz", - "integrity": "sha512-x0yf9PL/nx9Nw9oLL8ZVErFAk85/lslwEP7Vz7s5SI1ODXZIgit3C5qyWjw4DxOuO/3Hb4866SQh28a1V1d+WA==" - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" - }, - "node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", - "dependencies": { - "p-locate": "^6.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "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==" - }, - "node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "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==" - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/moment": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", - "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", - "engines": { - "node": "*" - } - }, - "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==" - }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", - "engines": { - "node": ">=8" - } - }, - "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/openai": { - "version": "4.53.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.53.0.tgz", - "integrity": "sha512-XoMaJsSLuedW5eoMEMmZbdNoXgML3ujcU5KfwRnC6rnbmZkHE2Q4J/SArwhqCxQRqJwHnQUj1LpiROmKPExZJA==", - "dependencies": { - "@types/node": "^18.11.18", - "@types/node-fetch": "^2.6.4", - "abort-controller": "^3.0.0", - "agentkeepalive": "^4.2.1", - "form-data-encoder": "1.7.2", - "formdata-node": "^4.3.2", - "node-fetch": "^2.6.7", - "web-streams-polyfill": "^3.2.1" - }, - "bin": { - "openai": "bin/cli" - } - }, - "node_modules/openai/node_modules/@types/node": { - "version": "18.19.42", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.42.tgz", - "integrity": "sha512-d2ZFc/3lnK2YCYhos8iaNIYu9Vfhr92nHiyJHRltXWjXUBjEE+A4I58Tdbnw4VhggSW+2j5y5gTrLs4biNnubg==", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", - "dependencies": { - "p-limit": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-timeout": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.2.tgz", - "integrity": "sha512-UbD77BuZ9Bc9aABo74gfXhNvzC9Tx7SxtHSh1fxvx3jTLLYvmVhiQZZrJzqqU0jKbN32kb5VOKiLEQI/3bIjgQ==", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/paypal-rest-sdk": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/paypal-rest-sdk/-/paypal-rest-sdk-1.8.1.tgz", - "integrity": "sha512-Trj2GuPn10GqpICAxQh5wjxuDT7rq7DMOkvyatz05wI5xPGmqXN7UC0WfDSF9WSBs4YdcWZP0g+nY+sOdaFggw==", - "dependencies": { - "buffer-crc32": "^0.2.3", - "semver": "^5.0.3" - }, - "engines": { - "node": ">= v0.6.0" - } - }, - "node_modules/paypal-rest-sdk/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" - }, - "node_modules/prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/quick-lru": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz", - "integrity": "sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-7.1.0.tgz", - "integrity": "sha512-5iOehe+WF75IccPc30bWTbpdDQLOCc3Uu8bi3Dte3Eueij81yx1Mrufk8qBx/YAbR4uL1FdUr+7BKXDwEtisXg==", - "dependencies": { - "@types/normalize-package-data": "^2.4.1", - "normalize-package-data": "^3.0.2", - "parse-json": "^5.2.0", - "type-fest": "^2.0.0" - }, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-9.1.0.tgz", - "integrity": "sha512-vaMRR1AC1nrd5CQM0PhlRsO5oc2AAigqr7cCrZ/MW/Rsaflz4RlgzkpL4qoU/z1F6wrbd85iFv1OQj/y5RdGvg==", - "dependencies": { - "find-up": "^6.3.0", - "read-pkg": "^7.1.0", - "type-fest": "^2.5.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==", - "dependencies": { - "lowercase-keys": "^1.0.0" - } - }, - "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==" - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.18", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz", - "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==" - }, - "node_modules/stubborn-fs": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/stubborn-fs/-/stubborn-fs-1.2.5.tgz", - "integrity": "sha512-H2N9c26eXjzL/S/K+i/RHHcFanE74dptvvjM8iwzwbVcWY/zjBbgRqF3K0DY4+OD+uTTASTBvDoxPDaPN02D7g==" - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", - "engines": { - "node": ">=6" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "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==" - }, - "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - }, - "node_modules/type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/undici": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.13.0.tgz", - "integrity": "sha512-Q2rtqmZWrbP8nePMq7mOJIN98M0fYvSgV89vwl/BQRT4mDOeY2GXZngfGpcBBhtky3woM7G24wZV3Q304Bv6cw==", - "engines": { - "node": ">=18.0" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "node_modules/unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dependencies": { - "crypto-random-string": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==", - "dependencies": { - "prepend-http": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/web-streams-polyfill": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/when-exit": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/when-exit/-/when-exit-2.1.3.tgz", - "integrity": "sha512-uVieSTccFIr/SFQdFWN/fFaQYmV37OKtuaGphMAzi4DmmUlrvRBJW5WSLkHyjNQY/ePJMz3LoiX9R3yy1Su6Hw==" - }, - "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/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yocto-queue": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", - "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/package.json b/package.json index b9f51fb..9debb6c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "supportbot", - "version": "8.0.1", + "version": "8.3", "description": "SupportBot is an open-sourced support system for discord. It's completely free to use with high amounts of customization to match your own brand.", "main": "index.js", "scripts": { @@ -16,17 +16,16 @@ "bugs": { "url": "https://github.com/Emerald-Services/SupportBot/issues" }, - "homepage": "https://github.com/Emerald-Services/SupportBot#readme", + "homepage": "https://community.emeraldsrv.dev", "dependencies": { "@paypal/checkout-server-sdk": "^1.0.3", "@vitalets/google-translate-api": "^8.0.0", "axios": "^1.7.2", - "chatgpt": "^5.2.5", "discord.js": "^14.13.0", "js": "^0.1.0", "js-yaml": "^4.1.0", "moment": "^2.29.3", "ms": "^2.1.3", - "openai": "^4.50.0", + "openai": "^4.50.0" } }