From fd65d0a9a70e90b0f7269825913a5b65e1767767 Mon Sep 17 00:00:00 2001 From: 1tzEmerald Date: Wed, 3 Jul 2024 13:46:57 +0100 Subject: [PATCH 1/3] 8.1-BETA --- Addons/addon.js | 26 ++ Commands/genImage.js | 79 ++++ Commands/help.js | 31 +- Commands/profile.js | 323 +++++++++++++ Commands/ticket.js | 649 ++++++++++++++++---------- Commands/ticketstats.js | 56 --- Configs/messages.yml | 3 + Configs/supportbot-ai.yml | 6 +- Configs/supportbot.yml | 10 +- Data/Profiles.json | 7 + Data/Profiles/294146279456636929.json | 5 + Data/Profiles/419821884431925260.json | 5 + Data/Profiles/682708736359399524.json | 5 + Data/StaffData.json | 3 + Data/SuggestionData.json | 17 +- Data/TicketData.json | 181 ++++++- Data/ticket-panel-id.json | 4 +- Events/interactionCreate.js | 47 ++ Events/messageCreate.js | 180 ++++--- LICENSE.md | 2 +- Structures/Client.js | 37 +- 21 files changed, 1252 insertions(+), 424 deletions(-) create mode 100644 Addons/addon.js create mode 100644 Commands/genImage.js create mode 100644 Commands/profile.js delete mode 100644 Commands/ticketstats.js create mode 100644 Data/Profiles.json create mode 100644 Data/Profiles/294146279456636929.json create mode 100644 Data/Profiles/419821884431925260.json create mode 100644 Data/Profiles/682708736359399524.json create mode 100644 Data/StaffData.json diff --git a/Addons/addon.js b/Addons/addon.js new file mode 100644 index 0000000..7609fd7 --- /dev/null +++ b/Addons/addon.js @@ -0,0 +1,26 @@ +const Discord = require("discord.js"); +const fs = require("fs"); +const yaml = require("js-yaml"); +const { Command } = require('../Structures/Addon.js'); // Adjust the path as needed +const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8")); + +module.exports = new Command({ + name: "addon", + description: "This is a sample addon!", + options: [], + permissions: ["Administrator"], + + async run(interaction) { + let disableCommand = true; + + const PingEmbed = new Discord.EmbedBuilder() + .setDescription( + `This is a sample addon.\n\nPong! \`${interaction.client.ws.ping} ms\`` + ) + .setColor(supportbot.Embed.Colours.General); + + interaction.reply({ + embeds: [PingEmbed], + }); + }, + }); \ No newline at end of file diff --git a/Commands/genImage.js b/Commands/genImage.js new file mode 100644 index 0000000..de8e94c --- /dev/null +++ b/Commands/genImage.js @@ -0,0 +1,79 @@ +const fs = require("fs"); +const Discord = require("discord.js"); +const yaml = require("js-yaml"); +const generateImage = require('../Structures/generateImage.js'); + +const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8")); +const Command = require("../Structures/Command.js"); + +module.exports = new Command({ + name: "image", + description: "Create an image through SupportBot AI", + type: Discord.ApplicationCommandType.ChatInput, + options: [ + { + name: "prompt", + description: "The prompt to generate an image from", + type: Discord.ApplicationCommandOptionType.String, + required: true, + }, + { + name: "negativeprompt", + description: "The negative prompt to exclude from the image", + type: Discord.ApplicationCommandOptionType.String, + required: false, + }, + { + name: "cfgscale", + description: "The cfg scale for the image generation", + type: Discord.ApplicationCommandOptionType.Number, + required: false, + } + ], + permissions: "SendMessages", + + async run(interaction) { + const prompt = interaction.options.getString("prompt"); + const negativePrompt = interaction.options.getString("negativeprompt") || ""; + const cfgScale = interaction.options.getNumber("cfgscale") || 7.5; // Default value + + try { + const imageUrl = await generateImage(prompt, negativePrompt, cfgScale); + + const ImageEmbed = new Discord.EmbedBuilder() + .setTitle("Generated Image") + .setDescription(`Here is the image generated for your prompt: "${prompt}"`) + .setImage(imageUrl) + .setColor(supportbot.Embed.Colours.General) + .setFooter({ + text: supportbot.Embed.Footer, + iconURL: interaction.user.displayAvatarURL(), + }); + + if (!interaction.replied) { + await interaction.reply({ + ephemeral: true, + embeds: [ImageEmbed], + }); + } else { + await interaction.followUp({ + ephemeral: true, + embeds: [ImageEmbed], + }); + } + } catch (error) { + console.error('Error generating image:', error); + if (!interaction.replied) { + await interaction.reply({ + ephemeral: true, + content: `An error occurred while generating the image: ${error.message}. Please try again later.`, + }); + } else { + await interaction.followUp({ + ephemeral: true, + content: `An error occurred while generating the image: ${error.message}. Please try again later.`, + }); + } + } + }, +}); diff --git a/Commands/help.js b/Commands/help.js index edea11b..7e0f48a 100644 --- a/Commands/help.js +++ b/Commands/help.js @@ -1,17 +1,9 @@ -// SupportBot | Emerald Services -// Help Command - 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 supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8")); +const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8")); const Command = require("../Structures/Command.js"); @@ -32,11 +24,17 @@ module.exports = new Command({ ); } - // Fetch all commands from the client's commands collection + // Separate regular commands and addon commands let botCommands = ""; + let addonCommands = ""; + interaction.client.commands.forEach((command) => { if (command.permissions) { - botCommands += `\`${command.name}\` `; + if (command.addon) { + addonCommands += `\`${command.name}\` `; + } else { + botCommands += `\`${command.name}\` `; + } } }); @@ -51,15 +49,6 @@ module.exports = new Command({ inline: false, }); - // Conditionally add Addons section - if (supportbot.General.Addons.Enabled) { - HelpEmbed1.addFields({ - name: "🛠️ Addons\n", - value: "No addons available.", - inline: false, - }); - } - HelpEmbed1.setFooter({ text: supportbot.Embed.Footer, iconURL: interaction.user.displayAvatarURL(), diff --git a/Commands/profile.js b/Commands/profile.js new file mode 100644 index 0000000..582d042 --- /dev/null +++ b/Commands/profile.js @@ -0,0 +1,323 @@ +const { EmbedBuilder, ModalBuilder, TextInputBuilder, TextInputStyle, ApplicationCommandType, ActionRowBuilder, ButtonBuilder, ButtonStyle, ApplicationCommandOptionType } = require("discord.js"); +const fs = require("fs"); +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"); + +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') { + field.value = clockedIn ? '✅ Clocked In' : '❌ Clocked Out'; + } + return field; + }); + return profileEmbed; +} + +module.exports = new Command({ + name: 'profile', + description: 'Show user profile', + type: ApplicationCommandType.ChatInput, + permissions: ["SendMessages"], + options: [ + { + name: 'user', + description: 'The user to view the profile of', + type: ApplicationCommandOptionType.User, + required: false + } + ], + + async run(interaction) { + await interaction.deferReply({ ephemeral: true }); // Acknowledge the interaction immediately + + const userOption = interaction.options.getUser('user'); + const viewingUser = userOption || interaction.user; + const viewingUserId = viewingUser.id; + const interactionUserId = interaction.user.id; + + const user = interaction.guild.members.cache.get(viewingUserId); + + const Staff = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild); + const Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild); + + if (!Staff || !Admin) { + return interaction.editReply({ + content: "Some roles seem to be missing!\nPlease check for errors when starting the bot.", + ephemeral: true, + }); + } + + const isStaff = user.roles.cache.has(Staff.id) || user.roles.cache.has(Admin.id); + const isOwnProfile = interactionUserId === viewingUserId; + + const profilePath = `./Data/Profiles/${viewingUserId}.json`; + let profileData; + + if (fs.existsSync(profilePath)) { + profileData = JSON.parse(fs.readFileSync(profilePath, "utf8")); + } else { + profileData = { + bio: "", + timezone: "", + clockedIn: false + }; + fs.writeFileSync(profilePath, JSON.stringify(profileData, null, 2)); + } + + const { bio, timezone, clockedIn } = profileData; + + let profileEmbed = new EmbedBuilder() + .setTitle(`${viewingUser.username}'s Profile`) + .setColor(supportbot.Embed.Colours.General) + .setThumbnail(viewingUser.displayAvatarURL()) + .addFields( + { name: 'Bio', value: bio || 'No bio set.', inline: false }, + { name: 'Timezone', value: timezone || 'No timezone set.', inline: true } + ); + + if (isStaff) { + profileEmbed.addFields({ name: 'Clocked In Status', value: clockedIn ? '✅ Clocked In' : '❌ Clocked Out', inline: true }); + } + + const buttonRow = new ActionRowBuilder(); + if (isOwnProfile) { + buttonRow.addComponents( + new ButtonBuilder() + .setCustomId('editProfile') + .setLabel('Edit Profile') + .setStyle(ButtonStyle.Secondary) + ); + + if (isStaff) { + buttonRow.addComponents( + new ButtonBuilder() + .setCustomId('clockInOut') + .setLabel(clockedIn ? 'Clock Out' : 'Clock In') + .setStyle(ButtonStyle.Secondary) + ); + } + } + + if (isStaff && supportbot.Ticket.ClaimTickets.Enabled) { + buttonRow.addComponents( + new ButtonBuilder() + .setCustomId('viewTicketStats') + .setLabel('View Ticket Stats') + .setStyle(ButtonStyle.Secondary) + ); + } + + await interaction.editReply({ embeds: [profileEmbed], components: buttonRow.components.length > 0 ? [buttonRow] : [] }); + + const filter = i => ( + ['editProfile', 'clockInOut', 'viewTicketStats', 'showOpenTickets', 'backToStats', 'backToProfile'].includes(i.customId) + && i.user.id === interactionUserId + ); + + const collector = interaction.channel.createMessageComponentCollector({ filter, time: 60000 }); + + collector.on('collect', async i => { + if (i.customId === 'editProfile') { + const modal = new ModalBuilder() + .setCustomId('editProfileModal') + .setTitle('Edit Profile') + .addComponents( + new ActionRowBuilder().addComponents( + new TextInputBuilder() + .setCustomId('bio') + .setLabel('Bio') + .setStyle(TextInputStyle.Paragraph) + .setPlaceholder('Enter your bio') + .setValue(String(bio)) + .setRequired(false) + ), + new ActionRowBuilder().addComponents( + new TextInputBuilder() + .setCustomId('timezone') + .setLabel('Timezone') + .setStyle(TextInputStyle.Short) + .setPlaceholder('Enter your timezone') + .setValue(String(timezone)) + .setRequired(false) + ) + ); + + await i.showModal(modal); + } + + 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); + } else { + clockedInUsers.delete(viewingUserId); + } + + const updatedButtonRow = new ActionRowBuilder() + .addComponents( + new ButtonBuilder() + .setCustomId('editProfile') + .setLabel('Edit Profile') + .setStyle(ButtonStyle.Secondary) + ); + + 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 }); + } + + 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.') + .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] }); + } + + 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; + + 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 === 'backToProfile') { + await i.update({ embeds: [profileEmbed], components: buttonRow.components.length > 0 ? [buttonRow] : [] }); + } + }); + } +}); + +module.exports.clockedInUsers = clockedInUsers; diff --git a/Commands/ticket.js b/Commands/ticket.js index ccb5c0b..7514a2e 100644 --- a/Commands/ticket.js +++ b/Commands/ticket.js @@ -8,7 +8,7 @@ const { ButtonBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder, - GuildMemberRoleManager, + ButtonStyle } = require("discord.js"); const yaml = require("js-yaml"); @@ -20,6 +20,191 @@ const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8")); const Command = require("../Structures/Command.js"); const TicketNumberID = require("../Structures/TicketID.js"); +async function getClockedInUsers(guild) { + const clockedInUsers = new Set(); + + const profilesDir = './Data/Profiles'; + const profileFiles = fs.readdirSync(profilesDir); + + for (const file of profileFiles) { + const profileData = JSON.parse(fs.readFileSync(`${profilesDir}/${file}`, "utf8")); + if (profileData.clockedIn) { + const userId = file.replace('.json', ''); + const member = await guild.members.fetch(userId); + if (member) clockedInUsers.add(member.id); + } + } + return clockedInUsers; +} + +async function assignTicketToUser(ticketChannel, Staff, Admin, interaction, attempts = 0) { + console.log(`Assigning ticket attempt ${attempts}`); + + try { + const clockedInUsers = await getClockedInUsers(interaction.guild); + console.log(`Clocked in users: ${Array.from(clockedInUsers).join(', ')}`); + + if (clockedInUsers.size === 0 || attempts >= 2) { + console.log("No clocked in users or max attempts reached"); + const claimChannel = await interaction.guild.channels.cache.get(supportbot.Ticket.ClaimTickets.Channel); + if (claimChannel) { + const claimEmbed = new EmbedBuilder() + .setTitle(msgconfig.Ticket.ClaimTickets.ClaimTitle || 'Default Claim Title') + .setDescription(msgconfig.Ticket.ClaimTickets.ClaimMessage || 'Default Claim Message') + .setColor(supportbot.Embed.Colours.General); + + const claimButton = new ButtonBuilder() + .setCustomId(`claimticket-${ticketChannel.id}`) + .setLabel(supportbot.Ticket.ClaimTickets.ButtonTitle || 'Claim Ticket') + .setStyle(ButtonStyle.Primary); + + const row = new ActionRowBuilder().addComponents(claimButton); + + const claimMessage = await claimChannel.send({ + content: `<@&${Staff.id}> <@&${Admin.id}>`, + embeds: [claimEmbed], + components: [row], + }); + + const filter = i => i.customId === `claimticket-${ticketChannel.id}` && i.member.roles.cache.has(Staff.id); + const collector = claimMessage.createMessageComponentCollector({ filter, time: 120000 }); + + collector.on('collect', async i => { + console.log(`Ticket claimed by user ${i.user.id}`); + await ticketChannel.send({ content: `<@${i.user.id}> has claimed the ticket.` }); + }); + + collector.on('end', async collected => { + if (collected.size === 0) { + console.log("No one claimed the ticket, notifying admins"); + await claimMessage.delete(); + + await claimChannel.send({ + content: `No one claimed the ticket. Tagging roles: <@&${Staff.id}> <@&${Admin.id}>`, + embeds: [new EmbedBuilder().setDescription(`Ticket is still unclaimed: <#${ticketChannel.id}>`).setColor(supportbot.Embed.Colours.Warn)], + }); + } + }); + + return null; + } else { + console.error("Claim channel not found"); + return null; + } + } + + const clockedInArray = Array.from(clockedInUsers); + const assignedUserId = clockedInArray[Math.floor(Math.random() * clockedInArray.length)]; + const assignedUser = await interaction.guild.members.fetch(assignedUserId); + console.log(`Assigned user: ${assignedUserId}`); + + const claimEmbed = new EmbedBuilder() + .setTitle(msgconfig.Ticket.ClaimTickets.ClaimTitle || 'Default Claim Title') + .setDescription(msgconfig.Ticket.ClaimTickets.ClaimMessage.replace('%user%', interaction.user.id) || 'Default Claim Message') + .setColor(supportbot.Embed.Colours.General); + + const claimButton = new ButtonBuilder() + .setCustomId(`claimticket-${ticketChannel.id}`) + .setLabel(supportbot.Ticket.ClaimTickets.ButtonTitle || 'Claim Ticket') + .setStyle(ButtonStyle.Primary); + + const row = new ActionRowBuilder().addComponents(claimButton); + + const claimChannel = await interaction.guild.channels.cache.get(supportbot.Ticket.ClaimTickets.Channel); + if (!claimChannel) { + console.error("Claim channel not found"); + return null; + } + + const claimMessage = await claimChannel.send({ + content: `${assignedUser}`, + embeds: [claimEmbed], + components: [row], + }); + + const filter = i => i.customId === `claimticket-${ticketChannel.id}` && i.user.id === assignedUserId; + const collector = claimMessage.createMessageComponentCollector({ filter, time: 120000 }); + + collector.on('collect', async i => { + console.log(`Ticket claimed by user ${i.user.id}`); + await ticketChannel.send({ content: `<@${assignedUserId}>` }); + }); + + collector.on('end', async collected => { + if (collected.size === 0) { + console.log("No one claimed the ticket, attempting reassignment"); + await claimMessage.delete(); + + if (attempts >= 1) { + await claimChannel.send({ + content: `No one claimed the ticket. Tagging roles: <@&${Staff.id}> <@&${Admin.id}>`, + embeds: [new EmbedBuilder().setDescription(`Ticket is still unclaimed: <#${ticketChannel.id}>`).setColor(supportbot.Embed.Colours.Warn)], + }); + + // Add a claim button for any staff member to click + const claimEmbed = new EmbedBuilder() + .setTitle(msgconfig.Ticket.ClaimTickets.ClaimTitle) + .setDescription(msgconfig.Ticket.ClaimTickets.ClaimMessage) + .setColor(supportbot.Embed.Colours.General); + + const claimButton = new ButtonBuilder() + .setCustomId(`claimticket-${ticketChannel.id}`) + .setLabel(supportbot.Ticket.ClaimTickets.ButtonTitle) + .setEmoji(supportbot.Ticket.ClaimTickets.ButtonEmoji) + .setStyle(ButtonStyle.Primary); + + const row = new ActionRowBuilder().addComponents(claimButton); + + const claimMessage = await claimChannel.send({ + content: `<@&${Staff.id}> <@&${Admin.id}>`, + embeds: [claimEmbed], + components: [row], + }); + + const filter = i => i.customId === `claimticket-${ticketChannel.id}` && i.member.roles.cache.has(Staff.id); + const collector = claimMessage.createMessageComponentCollector({ filter, time: 120000 }); + + collector.on('collect', async i => { + console.log(`Ticket ${ticketChannel.id} claimed by user ${i.user.id}`); + await ticketChannel.member.add(i.user.id); + }); + + collector.on('end', async collected => { + if (collected.size === 0) { + console.log(`No one claimed the ticket (${ticketChannel.id}), notifying all staff`); + await claimMessage.delete(); + + await claimChannel.send({ + content: `No one claimed the ticket. Tagging roles: <@&${Staff.id}> <@&${Admin.id}>`, + }); + } + }); + } else { + const ReassigningEmbed = new EmbedBuilder() + .setTitle(msgconfig.Ticket.ClaimTickets.ReassigningTitle) + .setDescription(msgconfig.Ticket.ClaimTickets.ReassigningMessage.replace('%user%', assignedUserId)) + .setColor(supportbot.Embed.Colours.General); + + await claimChannel.send({ embeds: [ReassigningEmbed], components: [] }); + await assignTicketToUser(ticketChannel, Staff, Admin, interaction, attempts + 1); + } + } + }); + + return assignedUserId; + } catch (error) { + console.error("Error in assignTicketToUser function:", error); + if (error instanceof CombinedError) { + console.error("CombinedError details:", error.errors); + } + return null; + } +} + + + + + module.exports = new Command({ name: cmdconfig.OpenTicket.Command, description: cmdconfig.OpenTicket.Description, @@ -34,275 +219,233 @@ module.exports = new Command({ permissions: cmdconfig.OpenTicket.Permission, async run(interaction) { - let department = interaction.customId?.split("-")[1] || null; - let TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8")); - const { getRole, getChannel } = interaction.client; - let User = interaction.guild.members.cache.get(interaction.user.id); + try { + let department = interaction.customId?.split("-")[1] || null; + let TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8")); + const { getRole, getChannel } = interaction.client; + let User = interaction.guild.members.cache.get(interaction.user.id); - if ( - supportbot.Ticket.TicketsPerUser && - TicketData.tickets.filter((t) => t.user === interaction.user.id && t.open).length >= supportbot.Ticket.TicketsPerUser - ) { - return interaction.reply({ - embeds: [ - { - title: "Too Many Tickets!", - description: `You can't have more than ${supportbot.Ticket.TicketsPerUser} open tickets!`, - color: supportbot.Embed.Colours.Warn, - }, - ], - ephemeral: true, - }); - } - - if (User.roles.cache.has(supportbot.Roles.ModRoles.Blacklisted)) { - return interaction.reply({ - content: msgconfig.Ticket.Blacklisted, - ephemeral: true, - }); - } - - if (User.roles.cache.has(supportbot.Roles.ModRoles.Muted)) { - return interaction.reply({ - content: msgconfig.Ticket.Muted, - ephemeral: true, - }); - } - - let ticketNumberID = await TicketNumberID.pad(); - const TicketSubject = interaction.options?.getString("reason") || msgconfig.Ticket.InvalidSubject; - - const TicketExists = new EmbedBuilder() - .setTitle("Ticket Exists!") - .setDescription(msgconfig.Ticket.TicketExists); - - if ( - await interaction.guild.channels.cache.find( - (ticketChannel) => ticketChannel.name === `${supportbot.Ticket.Channel}${ticketNumberID}` - ) - ) { - return await interaction.reply({ - embeds: [TicketExists], - ephemeral: true, - }); - } - - const Staff = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild); - const Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild); - - if (!Staff || !Admin) - return interaction.reply({ - content: "Some roles seem to be missing!\nPlease check for errors when starting the bot.", - ephemeral: true, - }); - - const TicketCategory = interaction.guild.channels.cache.find( - (c) => c.name === supportbot.Ticket.TicketCategory || c.id === supportbot.Ticket.TicketCategory - ); - - const channel = await getChannel(supportbot.Ticket.TicketCategory, interaction.guild); - const ticketChannel = await channel.threads.create({ - name: `${supportbot.Ticket.Channel}${ticketNumberID}`, - type: ChannelType.PrivateThread, - parent: TicketCategory, - autoArchiveDuration: 60, - reason: 'support ticket', - }); - - if (supportbot.Ticket.ClaimTickets.Enabled === false) { - const InvStaff = await ticketChannel.send(`<@&${Staff.id}> <@&${Admin.id}>`) - - setTimeout(() => { - InvStaff.delete(); - }, 2000) - } - - // TICKET CLAIMING SYSTEM [START] - - if (supportbot.Ticket.ClaimTickets.Enabled) { - const InvStaff1 = await ticketChannel.send(`<@&${Admin.id}>`) - - setTimeout(() => { - InvStaff1.delete(); - }, 2000) - - const claimChannel = await getChannel( - supportbot.Ticket.ClaimTickets.Channel, - interaction.guild - ); - - const claimEmbed = new EmbedBuilder() - .setTitle(msgconfig.Ticket.ClaimTickets.ClaimTitle) - .setDescription(msgconfig.Ticket.ClaimTickets.ClaimMessage - .replace('%user%', interaction.user.id) - ) - .setColor(supportbot.Embed.Colours.General); - - const claimButton = new ButtonBuilder() - .setCustomId(`claimticket-${ticketChannel.id}`) - .setLabel(supportbot.Ticket.ClaimTickets.ButtonTitle) - .setStyle(supportbot.Ticket.ClaimTickets.Button); - - const row = new ActionRowBuilder().addComponents(claimButton); - - await claimChannel.send({ - content: `<@&${Staff.id}>`, - embeds: [claimEmbed], - components: [row] - }); - - } - - // TICKET CLAIMING SYSTEM [END] - - if (!department) { - await ticketChannel.members.add(interaction.user.id); - } - - TicketData.tickets.push({ - id: ticketChannel.id, - name: ticketChannel.name, - user: interaction.user.id, - number: ticketNumberID, - reason: TicketSubject, - open: true, - subUsers: [], - createdAt: new Date().toISOString(), - claimedAt: null, // Will be updated when claimed - }); - - fs.writeFileSync( - "./Data/TicketData.json", - JSON.stringify(TicketData, null, 4), - (err) => { - if (err) console.error(err); + if ( + supportbot.Ticket.TicketsPerUser && + TicketData.tickets.filter((t) => t.user === interaction.user.id && t.open).length >= supportbot.Ticket.TicketsPerUser + ) { + return interaction.reply({ + embeds: [ + { + title: "Too Many Tickets!", + description: `You can't have more than ${supportbot.Ticket.TicketsPerUser} open tickets!`, + color: supportbot.Embed.Colours.Warn, + }, + ], + ephemeral: true, + }); } - ); - const CreatedTicket = new EmbedBuilder() - .setDescription( - msgconfig.Ticket.TicketCreatedAlert.replace(/%ticketauthor%/g, interaction.user.id) - .replace(/%ticketid%/g, ticketChannel.id) - .replace(/%ticketusername%/g, interaction.user.username) - ) - .setColor(supportbot.Embed.Colours.General); - await interaction.reply({ embeds: [CreatedTicket], ephemeral: true }); + if (User.roles.cache.has(supportbot.Roles.ModRoles.Blacklisted)) { + return interaction.reply({ + content: msgconfig.Ticket.Blacklisted, + ephemeral: true, + }); + } - if (supportbot.Ticket.AllowMentions) { - await ticketChannel.send(`${interaction.user}`); - } + if (User.roles.cache.has(supportbot.Roles.ModRoles.Muted)) { + return interaction.reply({ + content: msgconfig.Ticket.Muted, + ephemeral: true, + }); + } - const TicketMessage = new EmbedBuilder() - .setAuthor({ - name: msgconfig.Ticket.TicketAuthorTitle.replace(/%ticketauthor%/g, interaction.user.id) - .replace(/%ticketid%/g, ticketChannel.id) - .replace(/%ticketusername%/g, interaction.user.username), - iconURL: interaction.user.displayAvatarURL(), - }) - .setTitle( - msgconfig.Ticket.TicketTitle.replace(/%ticketauthor%/g, interaction.user.id) - .replace(/%ticketid%/g, ticketChannel.id) - .replace(/%ticketusername%/g, interaction.user.username) - ) - .setDescription( - msgconfig.Ticket.TicketMessage.replace(/%ticketauthor%/g, interaction.user.id) - .replace(/%ticketid%/g, ticketChannel.id) - .replace(/%ticketusername%/g, interaction.user.username) - ) - .setColor(supportbot.Embed.Colours.General); + let ticketNumberID = await TicketNumberID.pad(); + const TicketSubject = interaction.options?.getString("reason") || msgconfig.Ticket.InvalidSubject; - const SelectMenus = new StringSelectMenuBuilder() - .setCustomId("ticketcontrolpanel") - .setPlaceholder("Ticket Control Panel") - .addOptions( - new StringSelectMenuOptionBuilder() - .setLabel("Add User") - .setDescription("Add a user to the ticket.") - .setEmoji(supportbot.SelectMenus.Tickets.AddUserEmoji) - .setValue("ticketadduser"), - new StringSelectMenuOptionBuilder() - .setLabel("Remove User") - .setDescription("Remove a user from the ticket.") - .setEmoji(supportbot.SelectMenus.Tickets.RemoveUserEmoji) - .setValue("ticketremoveuser"), - new StringSelectMenuOptionBuilder() - .setLabel("Close") - .setDescription("Close the ticket.") - .setEmoji(supportbot.SelectMenus.Tickets.CloseEmoji) - .setValue("ticketclose"), - new StringSelectMenuOptionBuilder() - .setLabel("Enable Invites") - .setDescription("Enable invites.") - .setEmoji(supportbot.SelectMenus.Tickets.EnableInvitesEmoji) - .setValue("enableinvites"), - new StringSelectMenuOptionBuilder() - .setLabel("Disable Invites") - .setDescription("Disable invites.") - .setEmoji(supportbot.SelectMenus.Tickets.DisableInvitesEmoji) - .setValue("disableinvites"), - ); + const TicketExists = new EmbedBuilder() + .setTitle("Ticket Exists!") + .setDescription(msgconfig.Ticket.TicketExists); - const row2 = new ActionRowBuilder().addComponents(SelectMenus); + if ( + await interaction.guild.channels.cache.find( + (ticketChannel) => ticketChannel.name === `${supportbot.Ticket.Channel}${ticketNumberID}` + ) + ) { + return await interaction.reply({ + embeds: [TicketExists], + ephemeral: true, + }); + } - if (!department) { - try { - let buttons = supportbot.Departments.map((x) => - new ButtonBuilder() - .setCustomId("Department" + supportbot.Departments.indexOf(x)) - .setLabel(x.title) - .setStyle(x.color) - .setEmoji(x.emoji) - ); - - if (supportbot.Ticket.AllowMentions) { - await ticketChannel.send("<@&" + supportbot.Ticket.RoleMention + ">"); - } - - const row = new ActionRowBuilder().addComponents(buttons); - const m = await ticketChannel.send({ - embeds: [TicketMessage], - components: [row2], + const Staff = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild); + const Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild); + if (!Staff || !Admin) + return interaction.reply({ + content: "Some roles seem to be missing!\nPlease check for errors when starting the bot.", + ephemeral: true, }); - try { - const filter = (i) => i.user.id === interaction.user.id; - await m.awaitMessageComponent({ - filter, - max: 1, - componentType: "BUTTON", - time: supportbot.Ticket.Timeout * 60000, - }); - } catch (e) { - if (e.code === "COLLECTOR_ERROR") { - try { - let ticket = TicketData.tickets.findIndex((t) => t.id === ticketChannel.id); - TicketData.tickets[ticket].open = false; - fs.writeFileSync( - "./Data/TicketData.json", - JSON.stringify(TicketData, null, 4), - (err) => { - if (err) console.error(err); - } - ); - interaction.user.send( - "Your ticket has timed out. Please open a new one, and select a department." - ); - } catch (error) { - console.error("An error occurred while handling collector:", error); - } - return await ticketChannel.delete(); - } + const TicketCategory = interaction.guild.channels.cache.find( + (c) => c.name === supportbot.Ticket.TicketCategory || c.id === supportbot.Ticket.TicketCategory + ); + + const channel = await getChannel(supportbot.Ticket.TicketCategory, interaction.guild); + const ticketChannel = await channel.threads.create({ + name: `${supportbot.Ticket.Channel}${ticketNumberID}`, + type: ChannelType.PrivateThread, + parent: TicketCategory, + autoArchiveDuration: 60, + reason: 'support ticket', + }); + + // Assign ticket to a user if claiming tickets is enabled + if (supportbot.Ticket.ClaimTickets.Enabled) { + const assignedUserId = await assignTicketToUser(ticketChannel, Staff, Admin, interaction); + if (!assignedUserId) { + const claimChannel = await interaction.guild.channels.cache.get(supportbot.Ticket.ClaimTickets.Channel); + await claimChannel.send(`<@&${Staff.id}> <@&${Admin.id}>`); } - } catch (error) { - console.error("Error sending message:", error); } - } else { + + // Add user to the ticket channel + if (!department) { + await ticketChannel.members.add(interaction.user.id); + } + + // Save ticket data + TicketData.tickets.push({ + id: ticketChannel.id, + name: ticketChannel.name, + user: interaction.user.id, + number: ticketNumberID, + reason: TicketSubject, + open: true, + subUsers: [], + createdAt: new Date().toISOString(), + claimedAt: null, + }); + + fs.writeFileSync( + "./Data/TicketData.json", + JSON.stringify(TicketData, null, 4), + (err) => { + if (err) console.error(err); + } + ); + + // Notify the user that the ticket was created + const CreatedTicket = new EmbedBuilder() + .setDescription( + msgconfig.Ticket.TicketCreatedAlert.replace(/%ticketauthor%/g, interaction.user.id) + .replace(/%ticketid%/g, ticketChannel.id) + .replace(/%ticketusername%/g, interaction.user.username) + ) + .setColor(supportbot.Embed.Colours.General); + await interaction.reply({ embeds: [CreatedTicket], ephemeral: true }); + + // Create the ticket message + const TicketMessage = new EmbedBuilder() + .setAuthor({ + name: msgconfig.Ticket.TicketAuthorTitle.replace(/%ticketauthor%/g, interaction.user.id) + .replace(/%ticketid%/g, ticketChannel.id) + .replace(/%ticketusername%/g, interaction.user.username), + iconURL: interaction.user.displayAvatarURL(), + }) + .setTitle( + msgconfig.Ticket.TicketTitle.replace(/%ticketauthor%/g, interaction.user.id) + .replace(/%ticketid%/g, ticketChannel.id) + .replace(/%ticketusername%/g, interaction.user.username) + ) + .setDescription( + msgconfig.Ticket.TicketMessage.replace(/%ticketauthor%/g, interaction.user.id) + .replace(/%ticketid%/g, ticketChannel.id) + .replace(/%ticketusername%/g, interaction.user.username) + ) + .setColor(supportbot.Embed.Colours.General); + + // Create the control panel + const SelectMenus = new StringSelectMenuBuilder() + .setCustomId("ticketcontrolpanel") + .setPlaceholder("Ticket Control Panel") + .addOptions( + new StringSelectMenuOptionBuilder() + .setLabel("Add User") + .setDescription("Add a user to the ticket.") + .setEmoji(supportbot.SelectMenus.Tickets.AddUserEmoji) + .setValue("ticketadduser"), + new StringSelectMenuOptionBuilder() + .setLabel("Remove User") + .setDescription("Remove a user from the ticket.") + .setEmoji(supportbot.SelectMenus.Tickets.RemoveUserEmoji) + .setValue("ticketremoveuser"), + new StringSelectMenuOptionBuilder() + .setLabel("Close") + .setDescription("Close the ticket.") + .setEmoji(supportbot.SelectMenus.Tickets.CloseEmoji) + .setValue("ticketclose"), + new StringSelectMenuOptionBuilder() + .setLabel("Enable Invites") + .setDescription("Enable invites.") + .setEmoji(supportbot.SelectMenus.Tickets.EnableInvitesEmoji) + .setValue("enableinvites"), + new StringSelectMenuOptionBuilder() + .setLabel("Disable Invites") + .setDescription("Disable invites.") + .setEmoji(supportbot.SelectMenus.Tickets.DisableInvitesEmoji) + .setValue("disableinvites"), + ); + + const row2 = new ActionRowBuilder().addComponents(SelectMenus); + + // Send the ticket message and control panel await ticketChannel.send({ embeds: [TicketMessage], components: [row2], }); + if (!department) { + try { + let buttons = supportbot.Departments.map((x) => + new ButtonBuilder() + .setCustomId("Department" + supportbot.Departments.indexOf(x)) + .setLabel(x.title) + .setStyle(x.color) + .setEmoji(x.emoji) + ); + + const row = new ActionRowBuilder().addComponents(buttons); + + try { + const filter = (i) => i.user.id === interaction.user.id; + await m.awaitMessageComponent({ + filter, + max: 1, + componentType: "BUTTON", + time: supportbot.Ticket.Timeout * 60000, + }); + } catch (e) { + if (e.code === "COLLECTOR_ERROR") { + try { + let ticket = TicketData.tickets.findIndex((t) => t.id === ticketChannel.id); + TicketData.tickets[ticket].open = false; + fs.writeFileSync( + "./Data/TicketData.json", + JSON.stringify(TicketData, null, 4), + (err) => { + if (err) console.error(err); + } + ); + interaction.user.send( + "Your ticket has timed out. Please open a new one, and select a department." + ); + } catch (error) {return assignedUserId; + console.error("An error occurred while handling collector:", error); + } + return await ticketChannel.delete(); + } + } + } catch (error) { + console.error("Error sending message:", error); + } + } + } catch (error) { + console.error("Error in run method:", error); } - }, }); diff --git a/Commands/ticketstats.js b/Commands/ticketstats.js deleted file mode 100644 index 1afea81..0000000 --- a/Commands/ticketstats.js +++ /dev/null @@ -1,56 +0,0 @@ -const { EmbedBuilder, ApplicationCommandType } = require("discord.js"); -const fs = require("fs"); -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"); - -module.exports = new Command({ - name: cmdconfig.TicketStats.Command, - description: cmdconfig.TicketStats.Description, - type: ApplicationCommandType.ChatInput, - permissions: ["SendMessages"], // Adjust permissions as needed - - async run(interaction) { - - if (supportbot.Ticket.ClaimTickets.Enabled) { - - const TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8")); - const userId = interaction.user.id; - - const ticketsClaimed = TicketData.tickets.filter(t => t.claimedBy === userId); - 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); // Convert to minutes - - const statsEmbed = new EmbedBuilder() - .setTitle(msgconfig.TicketStats.Title) - .setDescription(`> Ticket stats for <@${interaction.user.id}>`) - .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 } - ); - - await interaction.reply({ embeds: [statsEmbed], ephemeral: true }); - } - - } -}); diff --git a/Configs/messages.yml b/Configs/messages.yml index ce83f5a..d7fb80d 100644 --- a/Configs/messages.yml +++ b/Configs/messages.yml @@ -98,6 +98,9 @@ Ticket: NoPermsToClaimTitle: "Error, no permissions!" NoPermsToClaim: "> :x: **Err!** You do not permissions to claim this ticket." + ReassigningTitle: "Reassigning!..." + ReassigningMessage: "> <@%user%> did not claim the ticket. **Reassigning...**" + TicketStats: OpenTickets: "✅ Open Tickets" TotalTickets: "🎫 Total Tickets" diff --git a/Configs/supportbot-ai.yml b/Configs/supportbot-ai.yml index e3ce7d6..0dd7cc3 100644 --- a/Configs/supportbot-ai.yml +++ b/Configs/supportbot-ai.yml @@ -17,16 +17,16 @@ General: # Max tokens you can set is 400 # For more information about tokens, go to https://openai.com/api/pricing/ - OpenAI_Key: "OPEN_AI_API_KEY" + OpenAI_Key: "OPEN AI KEY" # To create an OpenAI API key, go to https://platform.openai.com/api-keys - PastebinAPI_Key: "PASTEBIN_API_KEY" + PastebinAPI_Key: "PASTEBIN KEY" # You can get your Pastebin API Key from https://pastebin.com PastebinAPI_URL: "https://pastebin.com/api/api_post.php" Channels: - AIChannel: "0000000000000000000" + AIChannel: "1250190338954498048" # This is the channel where AI will respond in without needing to mention the bot. Embed: diff --git a/Configs/supportbot.yml b/Configs/supportbot.yml index 71747da..092c0f1 100644 --- a/Configs/supportbot.yml +++ b/Configs/supportbot.yml @@ -11,7 +11,7 @@ # Official Documentation: https://github.com/Emerald-Services/SupportBot/wiki # Third-Party Documentation: COMING SOON -SupportBot_Version: "v8.0" +SupportBot_Version: "v8.0.1" # Do not change the version number # ------------------------- @@ -20,11 +20,11 @@ SupportBot_Version: "v8.0" General: Name: "SupportBot" - Token: "BOT_TOKEN_HERE" + Token: "BOT TOKEN HERE" # To get your Discord Bot Token, Go to https://discord.com/developers/applications Addons: - Enabled: false + Enabled: true # ------------------------- # Activity @@ -155,7 +155,11 @@ Ticket: Channel: "1249723473757077624" # Channel name or Channel ID + ClaimAcceptTime: 120000 + # How long the assigned user has to accept the ticket before it gets re-assigned to someone else. + ButtonTitle: "Claim Ticket" + ButtonEmoji: "🎫" Button: "1" # Available Button Styles diff --git a/Data/Profiles.json b/Data/Profiles.json new file mode 100644 index 0000000..667cd6b --- /dev/null +++ b/Data/Profiles.json @@ -0,0 +1,7 @@ +{ + "419821884431925260": { + "bio": "Hi", + "timezone": "", + "isClockedIn": false + } +} \ No newline at end of file diff --git a/Data/Profiles/294146279456636929.json b/Data/Profiles/294146279456636929.json new file mode 100644 index 0000000..90585c3 --- /dev/null +++ b/Data/Profiles/294146279456636929.json @@ -0,0 +1,5 @@ +{ + "bio": "", + "timezone": "", + "clockedIn": false +} \ No newline at end of file diff --git a/Data/Profiles/419821884431925260.json b/Data/Profiles/419821884431925260.json new file mode 100644 index 0000000..b096324 --- /dev/null +++ b/Data/Profiles/419821884431925260.json @@ -0,0 +1,5 @@ +{ + "clockedIn": true, + "bio": "Hi", + "timezone": "GMT" +} \ No newline at end of file diff --git a/Data/Profiles/682708736359399524.json b/Data/Profiles/682708736359399524.json new file mode 100644 index 0000000..76a6181 --- /dev/null +++ b/Data/Profiles/682708736359399524.json @@ -0,0 +1,5 @@ +{ + "bio": "This is the most W bio", + "timezone": "cst", + "clockedIn": false +} \ No newline at end of file diff --git a/Data/StaffData.json b/Data/StaffData.json new file mode 100644 index 0000000..66e39a4 --- /dev/null +++ b/Data/StaffData.json @@ -0,0 +1,3 @@ +[ + "419821884431925260" +] \ No newline at end of file diff --git a/Data/SuggestionData.json b/Data/SuggestionData.json index 9e26dfe..b550ff7 100644 --- a/Data/SuggestionData.json +++ b/Data/SuggestionData.json @@ -1 +1,16 @@ -{} \ No newline at end of file +{ + "1250864227460186240": { + "suggestion": "hi", + "author": "419821884431925260", + "upvotes": [ + "419821884431925260" + ], + "downvotes": [] + }, + "1250900161958248533": { + "suggestion": "blob", + "author": "419821884431925260", + "upvotes": [], + "downvotes": [] + } +} \ No newline at end of file diff --git a/Data/TicketData.json b/Data/TicketData.json index 987b7f9..9f923fb 100644 --- a/Data/TicketData.json +++ b/Data/TicketData.json @@ -1,5 +1,184 @@ { "tickets": [ - {} + {}, + { + "id": "1253862843053510687", + "name": "ticket-0021", + "user": "419821884431925260", + "number": "0021", + "reason": "> No Reason Provided!", + "open": true, + "subUsers": [], + "createdAt": "2024-06-22T00:03:18.227Z", + "claimedAt": null + }, + { + "id": "1253863588012232727", + "name": "ticket-0022", + "user": "419821884431925260", + "number": "0022", + "reason": "> No Reason Provided!", + "open": true, + "subUsers": [], + "createdAt": "2024-06-22T00:06:16.126Z", + "claimedAt": "2024-06-22T00:09:12.693Z", + "claimedBy": "419821884431925260" + }, + { + "id": "1255499438483574847", + "name": "ticket-0023", + "user": "419821884431925260", + "number": "0023", + "reason": "> No Reason Provided!", + "open": true, + "subUsers": [], + "createdAt": "2024-06-26T12:26:35.729Z", + "claimedAt": null + }, + { + "id": "1255500796355678229", + "name": "ticket-0024", + "user": "419821884431925260", + "number": "0024", + "reason": "> No Reason Provided!", + "open": true, + "subUsers": [], + "createdAt": "2024-06-26T12:31:56.888Z", + "claimedAt": "2024-06-26T12:32:41.240Z", + "claimedBy": "419821884431925260" + }, + { + "id": "1255501873386094603", + "name": "ticket-0025", + "user": "419821884431925260", + "number": "0025", + "reason": "> No Reason Provided!", + "open": false, + "subUsers": [], + "createdAt": "2024-06-26T12:36:13.810Z", + "claimedAt": null + }, + { + "id": "1255508653906198709", + "name": "ticket-0026", + "user": "419821884431925260", + "number": "0026", + "reason": "> No Reason Provided!", + "open": true, + "subUsers": [], + "createdAt": "2024-06-26T13:03:10.321Z", + "claimedAt": null + }, + { + "id": "1256299016023572572", + "name": "ticket-0027", + "user": "419821884431925260", + "number": "0027", + "reason": "> No Reason Provided!", + "open": false, + "subUsers": [], + "createdAt": "2024-06-28T17:23:50.113Z", + "claimedAt": null + }, + { + "id": "1256300956824633426", + "name": "ticket-0028", + "user": "419821884431925260", + "number": "0028", + "reason": "> No Reason Provided!", + "open": false, + "subUsers": [], + "createdAt": "2024-06-28T17:31:31.454Z", + "claimedAt": null + }, + { + "id": "1256302271461986507", + "name": "ticket-0029", + "user": "419821884431925260", + "number": "0029", + "reason": "> No Reason Provided!", + "open": false, + "subUsers": [], + "createdAt": "2024-06-28T17:36:44.673Z", + "claimedAt": null + }, + { + "id": "1256303116505317457", + "name": "ticket-0030", + "user": "419821884431925260", + "number": "0030", + "reason": "> No Reason Provided!", + "open": true, + "subUsers": [], + "createdAt": "2024-06-28T17:40:06.292Z", + "claimedAt": null + }, + { + "id": "1257775687562166304", + "name": "ticket-0031", + "user": "419821884431925260", + "number": "0031", + "reason": "> No Reason Provided!", + "open": false, + "subUsers": [], + "createdAt": "2024-07-02T19:11:33.760Z", + "claimedAt": null + }, + { + "id": "1257776136843427841", + "name": "ticket-0032", + "user": "419821884431925260", + "number": "0032", + "reason": "> No Reason Provided!", + "open": false, + "subUsers": [], + "createdAt": "2024-07-02T19:13:20.641Z", + "claimedAt": null + }, + { + "id": "1257777774483996795", + "name": "ticket-0033", + "user": "419821884431925260", + "number": "0033", + "reason": "> No Reason Provided!", + "open": false, + "subUsers": [], + "createdAt": "2024-07-02T19:19:51.116Z", + "claimedAt": null + }, + { + "id": "1257778630600167535", + "name": "ticket-0034", + "user": "419821884431925260", + "number": "0034", + "reason": "> No Reason Provided!", + "open": false, + "subUsers": [], + "createdAt": "2024-07-02T19:23:15.328Z", + "claimedAt": null + }, + { + "id": "1257780259374366841", + "name": "ticket-0035", + "user": "419821884431925260", + "number": "0035", + "reason": "> No Reason Provided!", + "open": false, + "subUsers": [], + "createdAt": "2024-07-02T19:29:43.607Z", + "claimedAt": null + }, + { + "id": "1257782171389657180", + "name": "ticket-0036", + "user": "419821884431925260", + "number": "0036", + "reason": "> No Reason Provided!", + "open": true, + "subUsers": [], + "createdAt": "2024-07-02T19:37:19.467Z", + "claimedAt": "2024-07-02T19:43:12.873Z", + "claimedBy": "419821884431925260" + } ] } \ No newline at end of file diff --git a/Data/ticket-panel-id.json b/Data/ticket-panel-id.json index b0b4882..805b116 100644 --- a/Data/ticket-panel-id.json +++ b/Data/ticket-panel-id.json @@ -1,4 +1,4 @@ { - "id": 0, - "TicketPanelID": "0000000000000000000" + "id": 37, + "TicketPanelID": "1250863985335472312" } \ No newline at end of file diff --git a/Events/interactionCreate.js b/Events/interactionCreate.js index 9f64240..c636649 100644 --- a/Events/interactionCreate.js +++ b/Events/interactionCreate.js @@ -274,5 +274,52 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { }); } + // Modal submission handler for editing profile + if (interaction.type === Discord.InteractionType.ModalSubmit && interaction.customId === 'editProfileModal') { + const userId = interaction.user.id; + const profilePath = `./Data/Profiles/${userId}.json`; + let profileData; + if (fs.existsSync(profilePath)) { + profileData = JSON.parse(fs.readFileSync(profilePath, "utf8")); + } else { + profileData = { + bio: "", + timezone: "", + clockedIn: false + }; + } + + try { + const newBio = interaction.fields.getTextInputValue('bio'); + const newTimezone = interaction.fields.getTextInputValue('timezone'); + + profileData.bio = newBio; + profileData.timezone = newTimezone; + + fs.writeFileSync(profilePath, JSON.stringify(profileData, null, 2)); + + const updatedProfileEmbed = new Discord.EmbedBuilder() + .setTitle(`${interaction.user.username}'s Profile`) + .setColor(supportbot.Embed.Colours.General) + .setThumbnail(interaction.user.displayAvatarURL()) + .addFields( + { name: 'Bio', value: newBio || 'No bio set.', inline: false }, + { name: 'Timezone', value: newTimezone || 'No timezone set.', inline: true } + ); + + if (profileData.clockedIn !== undefined) { + updatedProfileEmbed.addFields({ + name: 'Clocked In Status', + value: profileData.clockedIn ? '✅ Clocked In' : '❌ Clocked Out', + inline: true + }); + } + + await interaction.reply({ embeds: [updatedProfileEmbed], ephemeral: true }); + } catch (error) { + console.error("Failed to update profile data: ", error); + await interaction.reply({ content: "There was an error while updating your profile. Please try again later.", ephemeral: true }); + } + } }); diff --git a/Events/messageCreate.js b/Events/messageCreate.js index 537efdd..b1999a1 100644 --- a/Events/messageCreate.js +++ b/Events/messageCreate.js @@ -18,6 +18,7 @@ const supportbotai = yaml.load(fs.readFileSync("./Configs/supportbot-ai.yml")); 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")); + // Validate configuration values function validateConfig(config, keys) { keys.forEach(key => { @@ -68,99 +69,150 @@ module.exports = new Event("messageCreate", async (client, message) => { if (message.author.bot || !message.guild) return; if (!CHANNELS.includes(message.channelId) && !message.mentions.users.has(client.user.id)) return; -if (supportbotai.Enabled) { + if (supportbotai.Enabled) { + const sendTypingInterval = setInterval(() => { + message.channel.sendTyping(); + }, 5000); - const sendTypingInterval = setInterval(() => { - message.channel.sendTyping(); - }, 5000); + let conversation = [{ + role: "system", + content: `SupportBot is your friendly neighbourhood support bot. Current date and time: ${new Date().toLocaleString()}. + The bot uses a modular command handler where commands are loaded dynamically from the './Commands' directory. Each command has a name, description, and execute method. + + The main Client class structure: + - Commands are loaded from the './Commands/' directory. + - Configs are loaded from the './Configs/' directory if enabled. + - Events are loaded from the './Events/' directory. + - Addons are loaded from the './Addons/' directory if enabled. + + Example of a command structure: + { + module.exports = new Command({ + name: cmdconfig.Ping.Command, + description: cmdconfig.Ping.Description, + options: [], + permissions: cmdconfig.Ping.Permission, + )} + } - let conversation = [{ - role: "system", - content: "SupportBot is your friendly neighbourhood support bot." - }]; + You can find more information about the commands and configuration files in the 'Configs' and 'Commands' directories. - let prevMessages = await message.channel.messages.fetch({ limit: 10 }); - prevMessages = prevMessages.reverse().filter(msg => !msg.author.bot || msg.author.id === client.user.id); + All commands are loaded into the bot at startup from ./Events/ready.js and ./Client.js. - prevMessages.forEach((msg) => { - const username = msg.author.username.replace(/\s+/g, '_').replace(/[^\w\s]/gi, ''); + If someone asks you to do something, you can use the 'assist' command to have the bot respond, For example: "Can I speak to a support agent?", You would respond by opening a ticket which is the /Commands/ticket.js file or you can tell them to create a ticket in the channel defined as supportbot.Ticket. - conversation.push({ - role: msg.author.id === client.user.id ? "assistant" : "user", - name: username, - content: msg.content, - }); - }); + You can find the source code for the bot at https://github.com/EmeraldServices/SupportBot. - try { - const response = await openai.chat.completions.create({ - model: supportbotai.General.Model, - messages: conversation, - max_tokens: supportbotai.General.Tokens, + You then need to understand how the bot works. Interactions are handled by the ./Events/interactionCreate.js file. + + Commands are filtered based on configuration settings and loaded into the bot at startup. + + Xiled Gaming Network (XGN) is a gaming community that typically offers various services and activities for gamers. + + Xiled Gaming Network (XGN) has 4 divisions, Team Xiled International (TXI), The XiledOnes (TXO), Dealers of Death (DOD), Psychotic Solutions (PS) + Xiled Gaming Network (XGN) can be found at https://officialxgn.gg/handbook/, This is where you can the code of conduct, policies and other important information for members. + You can join XGN with the discord - discord.gg/xgn + You can apply to join the XGN Clan - join.officialxgn.gg + + You can find the founders of XGN under the history section of the handbook. https://officialxgn.gg/handbook/. + ` + }]; + + let prevMessages = await message.channel.messages.fetch({ limit: 10 }); + prevMessages = prevMessages.reverse().filter(msg => !msg.author.bot || msg.author.id === client.user.id); + + prevMessages.forEach((msg) => { + const username = msg.author.username.replace(/\s+/g, '_').replace(/[^\w\s]/gi, ''); + + conversation.push({ + role: msg.author.id === client.user.id ? "assistant" : "user", + name: username, + content: msg.content, + }); }); - clearInterval(sendTypingInterval); + try { + const response = await openai.chat.completions.create({ + model: supportbotai.General.Model, + messages: conversation, + max_tokens: supportbotai.General.Tokens, + }); - if (response.choices && response.choices.length > 0) { - const replyContent = response.choices[0].message.content; + clearInterval(sendTypingInterval); - const SupportBotAI = new Discord.EmbedBuilder() - .setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() }) - .setDescription(replyContent) - .setColor(supportbotai.Embed.Color) - .setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() }) - .setTimestamp(); + if (response.choices && response.choices.length > 0) { + const replyContent = response.choices[0].message.content; - if (replyContent.length > 2000) { - const pasteLink = await createPaste(replyContent); + if (replyContent.toLowerCase().startsWith("generate image:")) { + const prompt = replyContent.substring(15).trim(); + const imageUrl = await generateImage(prompt); - const SupportBotAIPastebin = new Discord.EmbedBuilder() + const SupportBotAIImage = new Discord.EmbedBuilder() + .setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() }) + .setDescription(`Here is the image generated for your prompt: "${prompt}"`) + .setImage(imageUrl) + .setColor(supportbotai.Embed.Color) + .setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() }) + .setTimestamp(); + + message.reply({ embeds: [SupportBotAIImage] }); + } else { + const SupportBotAI = new Discord.EmbedBuilder() + .setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() }) + .setDescription(replyContent) + .setColor(supportbotai.Embed.Color) + .setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() }) + .setTimestamp(); + + if (replyContent.length > 2000) { + const pasteLink = await createPaste(replyContent); + + const SupportBotAIPastebin = new Discord.EmbedBuilder() + .setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() }) + .setDescription(supportbotai.Messages.MessageTooLong.replace("{pastebin}", pasteLink)) + .setColor(supportbotai.Embed.Color) + .setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() }) + .setTimestamp(); + + message.reply({ embeds: [SupportBotAIPastebin] }); + } else { + message.reply({ embeds: [SupportBotAI] }); + } + } + } else { + const SupportBotAIErrorResponse = new Discord.EmbedBuilder() .setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() }) - .setDescription(supportbotai.Messages.MessageTooLong.replace("{pastebin}", pasteLink)) + .setDescription(supportbotai.Messages.ErrorResponse) .setColor(supportbotai.Embed.Color) .setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() }) .setTimestamp(); - message.reply({ embeds: [SupportBotAIPastebin] }); - } else { - message.reply({ embeds: [SupportBotAI] }); + message.reply({ embeds: [SupportBotAIErrorResponse], ephemeral: true }); } - } else { + } catch (error) { + clearInterval(sendTypingInterval); + const SupportBotAIErrorResponse = new Discord.EmbedBuilder() .setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() }) - .setDescription(supportbotai.Messages.ErrorResponse) - .setColor(supportbotai.Embed.Color) + .setDescription(supportbotai.Messages.OpenAIError) + .setColor(supportbot.Embed.Colours.Error) .setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() }) .setTimestamp(); + console.error('OpenAI Error:', error); message.reply({ embeds: [SupportBotAIErrorResponse], ephemeral: true }); } - } catch (error) { - clearInterval(sendTypingInterval); + } - const SupportBotAIErrorResponse = new Discord.EmbedBuilder() + if (!supportbotai.Enabled) { + const AIDisabled = new Discord.EmbedBuilder() .setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() }) - .setDescription(supportbotai.Messages.OpenAIError) + .setDescription(supportbotai.Messages.AIDisabled) .setColor(supportbot.Embed.Colours.Error) .setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() }) .setTimestamp(); - console.error('OpenAI Error:', error); - message.reply({ embeds: [SupportBotAIErrorResponse], ephemeral: true }); + message.reply({ embeds: [AIDisabled], ephemeral: true }); + return; } - -} - -if (supportbotai.Enabled === false) { - const AIDisabled = new Discord.EmbedBuilder() - .setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() }) - .setDescription(supportbotai.Messages.AIDisabled) - .setColor(supportbot.Embed.Colours.Error) - .setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() }) - .setTimestamp(); - - message.reply({ embeds: [AIDisabled], ephemeral: true }) - return; -} - -}); \ No newline at end of file +}); diff --git a/LICENSE.md b/LICENSE.md index 0a0b305..0899fec 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Emerald Services +Copyright (c) 2024 Emerald Services Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Structures/Client.js b/Structures/Client.js index 350d80d..4afb632 100644 --- a/Structures/Client.js +++ b/Structures/Client.js @@ -1,12 +1,8 @@ -// C:\Users\ollie\Documents\GitHub\SupportBot\Structures\Client.js - -// SupportBot | Emerald Services -// Client Structure - const fs = require("fs"); const Discord = require("discord.js"); const { GatewayIntentBits, Partials } = require('discord.js'); const yaml = require("js-yaml"); +const { Command, Event } = require('./Addon.js'); // Adjust the path as needed const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8")); const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8")); @@ -89,18 +85,14 @@ class Client extends Discord.Client { tempCommandFiles = tempCommandFiles.filter(item => item !== "Ping.js"); } - if (cmdconfig.Ping.Enabled === false) { - tempCommandFiles = tempCommandFiles.filter(item => item !== "Ping.js"); - } - if (supportbot.Ticket.ClaimTickets.Enabled === false) { - tempCommandFiles = tempCommandFiles.filter(item => item !== "ticketstats.js"); + tempCommandFiles = tempCommandFiles.filter(item => item !== "ticketstats.js"); } const commandFiles = tempCommandFiles; const commands = commandFiles.map((file) => require(`../Commands/${file}`)); - // Commands + // Load main commands console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Commands ▬▬▬▬▬▬▬"); commands.forEach((cmd) => { @@ -108,13 +100,14 @@ class Client extends Discord.Client { this.commands.set(cmd.name, cmd); }); + // Load addon commands and events console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Commands ▬▬▬▬▬▬▬"); if (supportbot.General.Addons.Enabled) { const addonFiles = fs.readdirSync("./Addons").filter((file) => file.endsWith(".js")); const addons = addonFiles.map((file) => { - let addon = require(`../Addons/${file}`); + const addon = require(`../Addons/${file}`); addon.name = file.split(".")[0]; return addon; }); @@ -132,18 +125,24 @@ class Client extends Discord.Client { `\u001b[32;1m`, "Loaded" ); - addon.commands?.forEach((command) => { - this.commands.set(command.name, command); - }); - addon.events?.forEach((event) => { - this.on(event.event, (...args) => event.run(this, ...args)); - }); + + if (addon instanceof Command) { + this.commands.set(addon.name, addon); + } + + if (addon.events && Array.isArray(addon.events)) { + addon.events.forEach((event) => { + if (event instanceof Event) { + this.on(event.event, (...args) => event.run(this, ...args)); + } + }); + } }); console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬"); } - // Slash Commands + // Register Slash Commands this.once("ready", async () => { await this.guilds.cache.first()?.commands.set(this.commands); From 9a6eece7a1fea8812c0eb0296231c48a36237c10 Mon Sep 17 00:00:00 2001 From: 1tzEmerald Date: Tue, 9 Jul 2024 22:49:49 +0100 Subject: [PATCH 2/3] v8.1 --- Commands/addUser.js | 193 +++++++++++++++++++++ Commands/forceaddUser.js | 119 +++++++++++++ Commands/genImage.js | 79 --------- Commands/profile.js | 6 +- Commands/removeUser.js | 125 ++++++++++++++ Commands/ticket.js | 104 ++++++------ Commands/userinfo.js | 137 --------------- Configs/commands.yml | 28 +-- Configs/messages.yml | 66 ++++++- Configs/supportbot-ai.yml | 4 +- Configs/supportbot.yml | 4 +- Data/Profiles/419821884431925260.json | 2 +- Data/Profiles/709003903353618506.json | 5 + Data/SuggestionData.json | 6 + Data/TicketData.json | 85 ++++++++++ Data/ticket-panel-id.json | 2 +- Events/interactionCreate.js | 236 +++++++++++++++++--------- Events/messageCreate.js | 28 ++- Structures/Client.js | 16 +- Structures/generateImage.js | 33 ++++ 20 files changed, 902 insertions(+), 376 deletions(-) create mode 100644 Commands/addUser.js create mode 100644 Commands/forceaddUser.js delete mode 100644 Commands/genImage.js create mode 100644 Commands/removeUser.js delete mode 100644 Commands/userinfo.js create mode 100644 Data/Profiles/709003903353618506.json create mode 100644 Structures/generateImage.js diff --git a/Commands/addUser.js b/Commands/addUser.js new file mode 100644 index 0000000..179c410 --- /dev/null +++ b/Commands/addUser.js @@ -0,0 +1,193 @@ +const fs = require("fs"); +const { + EmbedBuilder, + ApplicationCommandOptionType, + ApplicationCommandType, + ActionRowBuilder, + ButtonBuilder, + ButtonStyle, +} = require("discord.js"); +const yaml = require("js-yaml"); + +const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8")); +const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8")); +const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8")); + +const Command = require("../Structures/Command.js"); + +module.exports = new Command({ + name: cmdconfig.AddUser.Command, + description: cmdconfig.AddUser.Description, + type: ApplicationCommandType.ChatInput, + options: [ + { + name: "user", + description: "The user to add", + type: ApplicationCommandOptionType.User, + required: true, + }, + ], + permissions: cmdconfig.AddUser.Permission, + + async run(interaction) { + const userToAdd = interaction.options.getUser("user"); + const ticketChannel = interaction.channel; + const ticketDataPath = "./Data/TicketData.json"; + + // Check if the channel is a ticket thread + if (!ticketChannel.isThread()) { + const onlyInTicket = new EmbedBuilder() + .setTitle(msgconfig.AddUser.NotInTicket_Title) + .setDescription(msgconfig.AddUser.NotInTicket_Description) + .setColor(supportbot.Embed.Colours.Error); + + return interaction.reply({ + embeds: [onlyInTicket], + ephemeral: true, + }); + } + + const ticketInviteEmbed = new EmbedBuilder() + .setTitle(msgconfig.AddUser.Invite_Title) + .setDescription(msgconfig.AddUser.Invite_Description.replace("%username%", userToAdd.username)) + .setColor(supportbot.Embed.Colours.General); + + await interaction.reply({ + embeds: [ticketInviteEmbed], + ephemeral: true, + }); + + try { + const dmEmbed = new EmbedBuilder() + .setTitle(msgconfig.AddUser.DM_Title) + .setDescription(msgconfig.AddUser.DM_Description.replace("%channelname%", ticketChannel.name)) + .setColor(supportbot.Embed.Colours.General); + + const acceptButton = new ButtonBuilder() + .setCustomId("accept_invite") + .setLabel(msgconfig.AddUser.Accept_Label) + .setEmoji("✅") + .setStyle(ButtonStyle.Primary); + + const rejectButton = new ButtonBuilder() + .setCustomId("reject_invite") + .setLabel(msgconfig.AddUser.Reject_Label) + .setEmoji("❌") + .setStyle(ButtonStyle.Secondary); + + const row = new ActionRowBuilder().addComponents(acceptButton, rejectButton); + + const dmMessage = await userToAdd.send({ + embeds: [dmEmbed], + components: [row], + }); + + const filter = (i) => i.user.id === userToAdd.id; + const collector = dmMessage.createMessageComponentCollector({ + filter, + time: 60000, + }); + + collector.on("collect", async (i) => { + if (i.customId === "accept_invite") { + try { + await ticketChannel.members.add(userToAdd.id); + + const ticketData = JSON.parse(fs.readFileSync(ticketDataPath, "utf8")); + const ticketIndex = ticketData.tickets.findIndex((t) => t.id === ticketChannel.id); + if (ticketIndex !== -1) { + ticketData.tickets[ticketIndex].subUsers.push(userToAdd.id); + fs.writeFileSync(ticketDataPath, JSON.stringify(ticketData, null, 4)); + } + + const acceptedEmbed = new EmbedBuilder() + .setTitle(msgconfig.AddUser.Accepted_Title) + .setDescription(msgconfig.AddUser.Accepted_Message) + .setColor(supportbot.Embed.Colours.Success); + + await i.update({ + embeds: [acceptedEmbed], + components: [], + }); + + const addedToTicketEmbed = new EmbedBuilder() + .setTitle(msgconfig.AddUser.AddedToTicket_Title) + .setDescription(msgconfig.AddUser.AddedToTicket_Description.replace( + "%channel_link%", + `[${ticketChannel.name}](https://discord.com/channels/${ticketChannel.guild.id}/${ticketChannel.id})` + )) + .setColor(supportbot.Embed.Colours.General); + + const ticketLinkButton = new ButtonBuilder() + .setLabel(msgconfig.AddUser.ViewTicket_Label) + .setURL(`https://discord.com/channels/${ticketChannel.guild.id}/${ticketChannel.id}`) + .setStyle(ButtonStyle.Link); + + await userToAdd.send({ + embeds: [addedToTicketEmbed], + components: [new ActionRowBuilder().addComponents(ticketLinkButton)], + }); + + } catch (err) { + console.error("Error adding user to the ticket:", err); + + const errorEmbed = new EmbedBuilder() + .setTitle(msgconfig.AddUser.Error_Title) + .setDescription(msgconfig.AddUser.Error_Adding) + .setColor(supportbot.Embed.Colours.Error); + + await i.update({ + embeds: [errorEmbed], + components: [], + }); + } + } else { + const declinedEmbed = new EmbedBuilder() + .setTitle(msgconfig.AddUser.Declined_Title) + .setDescription(msgconfig.AddUser.Declined_Message) + .setColor(supportbot.Embed.Colours.Error); + + await i.update({ + embeds: [declinedEmbed], + components: [], + }); + } + }); + + collector.on("end", (collected) => { + if (collected.size === 0) { + userToAdd.send({ + embeds: [ + new EmbedBuilder() + .setTitle(msgconfig.AddUser.NoResponse_Title) + .setDescription(msgconfig.AddUser.NoResponse_Message) + .setColor(supportbot.Embed.Colours.Warning), + ], + }); + } + }); + } catch (error) { + if (error.code === 50007) { + const errorEmbed = new EmbedBuilder() + .setTitle(msgconfig.AddUser.DM_Error_Title) + .setDescription(msgconfig.AddUser.DM_Error_Description.replace("%username%", userToAdd.username)) + .setColor(supportbot.Embed.Colours.Error); + + await ticketChannel.send({ + embeds: [errorEmbed], + }); + } else { + console.error("Error sending DM:", error); + + const unexpectedErrorEmbed = new EmbedBuilder() + .setTitle(msgconfig.AddUser.Unexpected_Error_Title) + .setDescription(msgconfig.AddUser.Unexpected_Error_Description.replace("%error%", error.message)) + .setColor(supportbot.Embed.Colours.Error); + + await ticketChannel.send({ + embeds: [unexpectedErrorEmbed], + }); + } + } + }, +}); diff --git a/Commands/forceaddUser.js b/Commands/forceaddUser.js new file mode 100644 index 0000000..43908a4 --- /dev/null +++ b/Commands/forceaddUser.js @@ -0,0 +1,119 @@ +// File: Commands/ForceAddUser.js +const fs = require("fs"); +const { + EmbedBuilder, + ApplicationCommandOptionType, + ApplicationCommandType, +} = require("discord.js"); +const yaml = require("js-yaml"); + +const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8")); +const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8")); +const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8")); + +const Command = require("../Structures/Command.js"); + +module.exports = new Command({ + name: cmdconfig.ForceAddUser.Command, + description: cmdconfig.ForceAddUser.Description, + type: ApplicationCommandType.ChatInput, + options: [ + { + name: "user", + description: "The user to add", + type: ApplicationCommandOptionType.User, + required: true, + }, + ], + permissions: cmdconfig.ForceAddUser.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); + if (!SupportStaff || !Admin) + + return interaction.reply( + "Some roles seem to be missing!\nPlease check for errors when starting the bot." + ); + + const NoPerms = new 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) + ) + return interaction.reply({ embeds: [NoPerms] }); + + const userToAdd = interaction.options.getUser("user"); + const ticketChannel = interaction.channel; + const ticketDataPath = "./Data/TicketData.json"; + + if (!ticketChannel.isThread()) { + const onlyInTicket = new EmbedBuilder() + .setTitle(msgconfig.ForceAddUser.NotInTicket_Title) + .setDescription(msgconfig.ForceAddUser.NotInTicket_Description) + .setColor(supportbot.Embed.Colours.Error); + + return interaction.reply({ + embeds: [onlyInTicket], + ephemeral: true, + }); + } + + try { + await ticketChannel.members.add(userToAdd.id); + + const ticketData = JSON.parse(fs.readFileSync(ticketDataPath, "utf8")); + const ticketIndex = ticketData.tickets.findIndex((t) => t.id === ticketChannel.id); + if (ticketIndex !== -1) { + ticketData.tickets[ticketIndex].subUsers.push(userToAdd.id); + fs.writeFileSync(ticketDataPath, JSON.stringify(ticketData, null, 4)); + } + + const addedEmbed = new EmbedBuilder() + .setTitle(msgconfig.ForceAddUser.Added_Title) + .setDescription(msgconfig.ForceAddUser.Added_Description.replace( + "%username%", + userToAdd.username + )) + .setColor(supportbot.Embed.Colours.Success); + + await interaction.reply({ + embeds: [addedEmbed], + ephemeral: true, + }); + + const addedToTicketEmbed = new EmbedBuilder() + .setTitle(msgconfig.ForceAddUser.AddedToTicket_Title) + .setDescription(msgconfig.ForceAddUser.AddedToTicket_Description.replace( + "%channel_link%", + `[${ticketChannel.name}](https://discord.com/channels/${ticketChannel.guild.id}/${ticketChannel.id})` + )) + .setColor(supportbot.Embed.Colours.General); + + await userToAdd.send({ + embeds: [addedToTicketEmbed], + }); + + } catch (err) { + console.error("Error adding user to the ticket:", err); + + const errorEmbed = new EmbedBuilder() + .setTitle(msgconfig.ForceAddUser.Error_Title) + .setDescription(msgconfig.ForceAddUser.Error_Adding) + .setColor(supportbot.Embed.Colours.Error); + + await interaction.reply({ + embeds: [errorEmbed], + ephemeral: true, + }); + } + }, +}); diff --git a/Commands/genImage.js b/Commands/genImage.js deleted file mode 100644 index de8e94c..0000000 --- a/Commands/genImage.js +++ /dev/null @@ -1,79 +0,0 @@ -const fs = require("fs"); -const Discord = require("discord.js"); -const yaml = require("js-yaml"); -const generateImage = require('../Structures/generateImage.js'); - -const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8")); -const Command = require("../Structures/Command.js"); - -module.exports = new Command({ - name: "image", - description: "Create an image through SupportBot AI", - type: Discord.ApplicationCommandType.ChatInput, - options: [ - { - name: "prompt", - description: "The prompt to generate an image from", - type: Discord.ApplicationCommandOptionType.String, - required: true, - }, - { - name: "negativeprompt", - description: "The negative prompt to exclude from the image", - type: Discord.ApplicationCommandOptionType.String, - required: false, - }, - { - name: "cfgscale", - description: "The cfg scale for the image generation", - type: Discord.ApplicationCommandOptionType.Number, - required: false, - } - ], - permissions: "SendMessages", - - async run(interaction) { - const prompt = interaction.options.getString("prompt"); - const negativePrompt = interaction.options.getString("negativeprompt") || ""; - const cfgScale = interaction.options.getNumber("cfgscale") || 7.5; // Default value - - try { - const imageUrl = await generateImage(prompt, negativePrompt, cfgScale); - - const ImageEmbed = new Discord.EmbedBuilder() - .setTitle("Generated Image") - .setDescription(`Here is the image generated for your prompt: "${prompt}"`) - .setImage(imageUrl) - .setColor(supportbot.Embed.Colours.General) - .setFooter({ - text: supportbot.Embed.Footer, - iconURL: interaction.user.displayAvatarURL(), - }); - - if (!interaction.replied) { - await interaction.reply({ - ephemeral: true, - embeds: [ImageEmbed], - }); - } else { - await interaction.followUp({ - ephemeral: true, - embeds: [ImageEmbed], - }); - } - } catch (error) { - console.error('Error generating image:', error); - if (!interaction.replied) { - await interaction.reply({ - ephemeral: true, - content: `An error occurred while generating the image: ${error.message}. Please try again later.`, - }); - } else { - await interaction.followUp({ - ephemeral: true, - content: `An error occurred while generating the image: ${error.message}. Please try again later.`, - }); - } - } - }, -}); diff --git a/Commands/profile.js b/Commands/profile.js index 582d042..f54ec86 100644 --- a/Commands/profile.js +++ b/Commands/profile.js @@ -25,10 +25,10 @@ function updateClockedInStatus(profileEmbed, clockedIn) { } module.exports = new Command({ - name: 'profile', - description: 'Show user profile', + name: cmdconfig.Profile.Command, + description: cmdconfig.Profile.Description, type: ApplicationCommandType.ChatInput, - permissions: ["SendMessages"], + permissions: cmdconfig.Profile.Permission, options: [ { name: 'user', diff --git a/Commands/removeUser.js b/Commands/removeUser.js new file mode 100644 index 0000000..c2fe64c --- /dev/null +++ b/Commands/removeUser.js @@ -0,0 +1,125 @@ +// File path: Commands/RemoveUser.js + +const fs = require("fs"); +const { + EmbedBuilder, + ApplicationCommandOptionType, + ApplicationCommandType, + ActionRowBuilder, + ButtonBuilder, + ButtonStyle, +} = require("discord.js"); +const yaml = require("js-yaml"); + +const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8")); +const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8")); +const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8")); + +const Command = require("../Structures/Command.js"); + +module.exports = new Command({ + name: cmdconfig.RemoveUser.Command, + description: cmdconfig.RemoveUser.Description, + type: ApplicationCommandType.ChatInput, + options: [ + { + name: "user", + description: "The user to remove", + type: ApplicationCommandOptionType.User, + required: true, + }, + ], + permissions: cmdconfig.RemoveUser.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); + if (!SupportStaff || !Admin) + + return interaction.reply( + "Some roles seem to be missing!\nPlease check for errors when starting the bot." + ); + + const NoPerms = new 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) + ) + return interaction.reply({ embeds: [NoPerms] }); + + const userToRemove = interaction.options.getUser("user"); + const ticketChannel = interaction.channel; + const ticketDataPath = "./Data/TicketData.json"; + + // Check if the channel is a ticket thread + if (!ticketChannel.isThread()) { + const onlyInTicket = new EmbedBuilder() + .setTitle(msgconfig.RemoveUser.NotInTicket_Title) + .setDescription(msgconfig.RemoveUser.NotInTicket_Description) + .setColor(supportbot.Embed.Colours.Error); + + return interaction.reply({ + embeds: [onlyInTicket], + ephemeral: true, + }); + } + + try { + await ticketChannel.members.remove(userToRemove.id); + + const ticketData = JSON.parse(fs.readFileSync(ticketDataPath, "utf8")); + const ticketIndex = ticketData.tickets.findIndex((t) => t.id === ticketChannel.id); + if (ticketIndex !== -1) { + const subUsers = ticketData.tickets[ticketIndex].subUsers; + const userIndex = subUsers.indexOf(userToRemove.id); + if (userIndex !== -1) { + subUsers.splice(userIndex, 1); + fs.writeFileSync(ticketDataPath, JSON.stringify(ticketData, null, 4)); + } + } + + const removedEmbed = new EmbedBuilder() + .setTitle(msgconfig.RemoveUser.Removed_Title) + .setDescription(msgconfig.RemoveUser.Removed_Message) + .setColor(supportbot.Embed.Colours.Success); + + await interaction.reply({ + embeds: [removedEmbed], + ephemeral: true, + }); + + const removedFromTicketEmbed = new EmbedBuilder() + .setTitle(msgconfig.RemoveUser.RemovedFromTicket_Title) + .setDescription(msgconfig.RemoveUser.RemovedFromTicket_Description.replace( + "%channel_link%", + `[${ticketChannel.name}](https://discord.com/channels/${ticketChannel.guild.id}/${ticketChannel.id})` + )) + .setColor(supportbot.Embed.Colours.General); + + await userToRemove.send({ + embeds: [removedFromTicketEmbed], + }); + + } catch (err) { + console.error("Error removing user from the ticket:", err); + + const errorEmbed = new EmbedBuilder() + .setTitle(msgconfig.RemoveUser.Error_Title) + .setDescription(msgconfig.RemoveUser.Error_Removing) + .setColor(supportbot.Embed.Colours.Error); + + await interaction.reply({ + embeds: [errorEmbed], + ephemeral: true, + }); + } + }, +}); diff --git a/Commands/ticket.js b/Commands/ticket.js index 7514a2e..d56b710 100644 --- a/Commands/ticket.js +++ b/Commands/ticket.js @@ -8,7 +8,7 @@ const { ButtonBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder, - ButtonStyle + ButtonStyle, } = require("discord.js"); const yaml = require("js-yaml"); @@ -22,7 +22,7 @@ const TicketNumberID = require("../Structures/TicketID.js"); async function getClockedInUsers(guild) { const clockedInUsers = new Set(); - + const profilesDir = './Data/Profiles'; const profileFiles = fs.readdirSync(profilesDir); @@ -201,10 +201,6 @@ async function assignTicketToUser(ticketChannel, Staff, Admin, interaction, atte } } - - - - module.exports = new Command({ name: cmdconfig.OpenTicket.Command, description: cmdconfig.OpenTicket.Description, @@ -220,7 +216,11 @@ module.exports = new Command({ async run(interaction) { try { - let department = interaction.customId?.split("-")[1] || null; + let TicketReason = null; + if (supportbot.Ticket.TicketReason) { + TicketReason = interaction.reason || null; // Retrieve the reason from the interaction object + } + let TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8")); const { getRole, getChannel } = interaction.client; let User = interaction.guild.members.cache.get(interaction.user.id); @@ -256,7 +256,7 @@ module.exports = new Command({ } let ticketNumberID = await TicketNumberID.pad(); - const TicketSubject = interaction.options?.getString("reason") || msgconfig.Ticket.InvalidSubject; + const TicketSubject = TicketReason || msgconfig.Ticket.InvalidSubject; const TicketExists = new EmbedBuilder() .setTitle("Ticket Exists!") @@ -304,9 +304,7 @@ module.exports = new Command({ } // Add user to the ticket channel - if (!department) { - await ticketChannel.members.add(interaction.user.id); - } + await ticketChannel.members.add(interaction.user.id); // Save ticket data TicketData.tickets.push({ @@ -335,6 +333,7 @@ module.exports = new Command({ msgconfig.Ticket.TicketCreatedAlert.replace(/%ticketauthor%/g, interaction.user.id) .replace(/%ticketid%/g, ticketChannel.id) .replace(/%ticketusername%/g, interaction.user.username) + .replace(/%ticketreason%/g, TicketSubject) ) .setColor(supportbot.Embed.Colours.General); await interaction.reply({ embeds: [CreatedTicket], ephemeral: true }); @@ -356,24 +355,22 @@ module.exports = new Command({ msgconfig.Ticket.TicketMessage.replace(/%ticketauthor%/g, interaction.user.id) .replace(/%ticketid%/g, ticketChannel.id) .replace(/%ticketusername%/g, interaction.user.username) + .replace(/%ticketreason%/g, TicketSubject) ) .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 } + ); + } + // Create the control panel const SelectMenus = new StringSelectMenuBuilder() .setCustomId("ticketcontrolpanel") .setPlaceholder("Ticket Control Panel") .addOptions( - new StringSelectMenuOptionBuilder() - .setLabel("Add User") - .setDescription("Add a user to the ticket.") - .setEmoji(supportbot.SelectMenus.Tickets.AddUserEmoji) - .setValue("ticketadduser"), - new StringSelectMenuOptionBuilder() - .setLabel("Remove User") - .setDescription("Remove a user from the ticket.") - .setEmoji(supportbot.SelectMenus.Tickets.RemoveUserEmoji) - .setValue("ticketremoveuser"), new StringSelectMenuOptionBuilder() .setLabel("Close") .setDescription("Close the ticket.") @@ -399,11 +396,12 @@ module.exports = new Command({ components: [row2], }); + // Handle department selection if not specified if (!department) { try { - let buttons = supportbot.Departments.map((x) => + const buttons = supportbot.Departments.map((x, index) => new ButtonBuilder() - .setCustomId("Department" + supportbot.Departments.indexOf(x)) + .setCustomId("Department" + index) .setLabel(x.title) .setStyle(x.color) .setEmoji(x.emoji) @@ -411,37 +409,39 @@ module.exports = new Command({ const row = new ActionRowBuilder().addComponents(buttons); - try { - const filter = (i) => i.user.id === interaction.user.id; - await m.awaitMessageComponent({ - filter, - max: 1, - componentType: "BUTTON", - time: supportbot.Ticket.Timeout * 60000, - }); - } catch (e) { - if (e.code === "COLLECTOR_ERROR") { - try { - let ticket = TicketData.tickets.findIndex((t) => t.id === ticketChannel.id); - TicketData.tickets[ticket].open = false; - fs.writeFileSync( - "./Data/TicketData.json", - JSON.stringify(TicketData, null, 4), - (err) => { - if (err) console.error(err); - } - ); - interaction.user.send( - "Your ticket has timed out. Please open a new one, and select a department." - ); - } catch (error) {return assignedUserId; - console.error("An error occurred while handling collector:", error); - } - return await ticketChannel.delete(); + const message = await ticketChannel.send({ + content: "Please select a department:", + components: [row], + }); + + const filter = (i) => i.user.id === interaction.user.id; + await message.awaitMessageComponent({ + filter, + max: 1, + componentType: "BUTTON", + time: supportbot.Ticket.Timeout * 60000, + }); + + } catch (e) { + if (e.code === "COLLECTOR_ERROR") { + try { + const ticket = TicketData.tickets.findIndex((t) => t.id === ticketChannel.id); + TicketData.tickets[ticket].open = false; + fs.writeFileSync( + "./Data/TicketData.json", + JSON.stringify(TicketData, null, 4), + (err) => { + if (err) console.error(err); + } + ); + await interaction.user.send( + "Your ticket has timed out. Please open a new one and select a department." + ); + } catch (error) { + console.error("An error occurred while handling collector:", error); } + await ticketChannel.delete(); } - } catch (error) { - console.error("Error sending message:", error); } } } catch (error) { diff --git a/Commands/userinfo.js b/Commands/userinfo.js deleted file mode 100644 index ef06e7e..0000000 --- a/Commands/userinfo.js +++ /dev/null @@ -1,137 +0,0 @@ -// SupportBot | Emerald Services -// User Info Command - -const fs = require("fs"); -const moment = require("moment"); - -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 Command = require("../Structures/Command.js"); - -module.exports = new Command({ - name: cmdconfig.UserInfo.Command, - description: cmdconfig.UserInfo.Description, - type: Discord.ApplicationCommandType.ChatInput, - options: [ - { - type: Discord.ApplicationCommandOptionType.User, - name: "user", - description: "The user you want to get info about.", - required: true, - }, // The user input - ], - permissions: cmdconfig.UserInfo.Permission, - - async run(interaction) { - let disableCommand = true; - - 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." - ); - - 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) - ) - return interaction.reply({ embeds: [NoPerms] }); - - let user = await interaction.options.getUser("user") || interaction.author; // Grab the mentioned user - let member = await interaction.guild.members.cache.get(user.id) // Check for user and his ID - - const UserNotExist = new Discord.EmbedBuilder() - .setTitle("User Not Found!") - .setDescription( - `${msgconfig.Error.UserNotFound}\n\nTry Again:\`/${cmdconfig.UserInfoCommand} <@!User_ID> or @User\`` - ) - .setColor(supportbot.ErrorColour); - - if (!user) return interaction.reply({ embeds: [UserNotExist] }); - - // Flags a user/bot can have - const flags = { - DISCORD_EMPLOYEE: 'Discord Employee', - DISCORD_PARTNER: 'Discord Partner', - BUGHUNTER_LEVEL_1: 'Bug Hunter (Level 1)', - BUGHUNTER_LEVEL_2: 'Bug Hunter (Level 2)', - HYPESQUAD_EVENTS: 'HypeSquad Events', - HOUSE_BRAVERY: 'House of Bravery', - HOUSE_BRILLIANCE: 'House of Brilliance', - HOUSE_BALANCE: 'House of Balance', - EARLY_SUPPORTER: 'Early Supporter', - TEAM_USER: 'Team User', - SYSTEM: 'System', - VERIFIED_BOT: 'Verified Bot', - VERIFIED_DEVELOPER: 'Verified Bot Developer' - }; - - // Status message with icon - let status = { - online: "🟢Online", - idle: "🟡Idle", - dnd: "🔴Do Not Disturb", - offline: "⚫Offline" - }; - - const userFlags = user.flags.toArray(); - - // Embed containing the userinfo - let userembed = new Discord.EmbedBuilder() - .setTitle(`User Info for User: ${user.tag}`) - .setThumbnail(member.displayAvatarURL({ dynamic: true, size: 512 })) - .setColor(member.roles.highest.hexColor) - .addFields( - { name: "Name:", value: `${user.tag}\n<@${member.id}>`, inline: true }, - { name: "ID:", value: `${member.id}`, inline: true }, - { name: "Is Bot:", value: user.bot ? "Yes" : "No", inline: true }, - { name: "House/Badge:", value: `${userFlags.length ? userFlags.map(flag => flags[flag]).join("\n") : "None"}`, inline: true }, - { name: "Current Status:", value: `${status[member.presence ? member.presence.status : "offline"]}`, inline: true }, - { name: "Avatar URL:", value: `[Click Here](${member.displayAvatarURL()})`, inline: true }, - { name: "\u200b", value: "\u200b", inline: false} - ) - .addFields( - { name: "Creation Date:", value: `${moment(user.createdAt).format("MMMM Do YYYY, h:mm:ss a")}\n> ${moment(user.createdAt).startOf("day").fromNow()}`, inline: true }, - { name: "Joined Date:", value: `${moment(member.joinedAt).format("MMMM Do YYYY, h:mm:ss a")}\n> ${moment(member.joinedAt).startOf("day").fromNow()}`, inline: true }, - { name: "Roles Total:", value: `${member.roles.cache.size -1}`, inline: false }, - { name: "Highest Role:", value: `${member.roles.highest.id === interaction.guild.id ? "None" : member.roles.highest.toString()}`, inline: true }, - { name: "Roles:", value: `${member.roles.cache.map(r => r).join(" ").replace("@everyone", " ") || "None"}`, inline: false }, - ) - .setFooter({ text: supportbot.Footer }) - .setTimestamp(); - - // Send embed [userembed] - await interaction.reply({ embeds: [userembed] }).catch((err) => console.error("An error has occured: ", err)); - - }, - }) - -/** - * @INFO - * UserInfo command by Sypher#3415 | 321332200668790786 - * @INFO - * Solely created for SupportBot | https://github.com/Emerald-Services/SupportBot/ - **/ diff --git a/Configs/commands.yml b/Configs/commands.yml index c8aaa6b..3ab19d3 100644 --- a/Configs/commands.yml +++ b/Configs/commands.yml @@ -19,12 +19,6 @@ Help: Permission: ["SendMessages"] Enabled: true -EnableAI: - Command: "enableai" - Description: "Talk with SupportBot" - Permission: ["SendMessages"] - Enabled: true - Info: Command: "info" Description: "Get information about the **ServerName**." @@ -37,9 +31,9 @@ TicketPanel: Permission: ["Administrator"] Enabled: true -TicketStats: - Command: "ticketstats" - Description: "Shows staff members their ticket statistics" +Profile: + Command: "profile" + Description: "Shows the profile for a discord member." Permission: ["SendMessages"] # Command is automatically disabled when the TicketClaims is disabled. @@ -49,9 +43,21 @@ OpenTicket: Permission: ["SendMessages"] Enabled: true -Add: +AddUser: Command: "add" - Description: "Add a user to the ticket." + Description: "Add a user to a support ticket." + Permission: ["SendMessages"] + Enabled: true + +ForceAddUser: + Command: "forceadd" + Description: "Add a user to a support ticket by bypassing the invite system." + Permission: ["SendMessages"] + Enabled: true + +RemoveUser: + Command: "remove" + Description: "Remove a user from a support ticket." Permission: ["SendMessages"] Enabled: true diff --git a/Configs/messages.yml b/Configs/messages.yml index d7fb80d..09fde0a 100644 --- a/Configs/messages.yml +++ b/Configs/messages.yml @@ -70,12 +70,6 @@ Ticket: # <@%user%> - Tags the user who was added to the ticket. - AddUserToTicket_Title: "➕ Add User!" - AddUserToTicket: "> To add a user to this ticket, Tag them." - - RemovedUserFromTicket_Title: "➖ Removed User!" - RemovedUserFromTicket: "> <@%user%> has been `Removed` from this ticket" - Blacklisted: "> :x: You are not allowed to open a ticket" Muted: "> :x: You are `Muted` and therefore not allowed to open any tickets" InvalidSubject: "> No Reason Provided!" @@ -108,6 +102,66 @@ TicketStats: Title: "Your Ticket Stats" +AddUser: + NotInTicket_Title: "Ticket Invitation" + NotInTicket_Description: "> This command can only be used within a ticket thread." + + Invite_Title: "User Invited" + Invite_Description: "> %username% has been invited to join the ticket." + + DM_Title: "Ticket Invitation" + DM_Description: "> You have been invited to join the ticket %channelname%. Do you accept?" + + Accept_Label: "Accept" + Reject_Label: "Reject" + ViewTicket_Label: "Go to Ticket" + + Accepted_Title: "Invitation Accepted" + Accepted_Message: "> You have been added to the ticket." + AddedToTicket_Title: "Added to Ticket" + AddedToTicket_Description: "> You have been added to the ticket %channel_link%." + + Declined_Title: "Invitation Declined" + Declined_Message: "> You have declined the invitation." + + NoResponse_Title: "No Response" + NoResponse_Message: "> You did not respond in time. Please contact staff if you wish to join the ticket." + + Error_Title: "Error" + Error_Adding: "> There was an error adding you to the ticket. Please try again." + + DM_Error_Title: "Error" + DM_Error_Description: "> Unable to send a DM to %username%. Please ensure your DMs are enabled." + + Unexpected_Error_Title: "Error" + Unexpected_Error_Description: "> An unexpected error occurred: %error%" + +RemoveUser: + NotInTicket_Title: "Error" + NotInTicket_Description: "This command can only be used in ticket threads." + + Removed_Title: "User Removed" + Removed_Message: "The user has been successfully removed from the ticket." + + RemovedFromTicket_Title: "Removed from Ticket" + RemovedFromTicket_Description: "You have been removed from the ticket channel %channel_link%." + + Error_Title: "Error" + Error_Removing: "There was an error removing the user from the ticket." + +ForceAddUser: + NotInTicket_Title: "Not a Ticket" + NotInTicket_Description: "This command can only be used within a ticket thread." + + Added_Title: "User Added" + Added_Description: "Successfully added %username% to the ticket." + + AddedToTicket_Title: "You have been added to a Ticket" + AddedToTicket_Description: "You have been added to the ticket channel [%channel_link%]." + + Error_Title: "Error" + 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!" diff --git a/Configs/supportbot-ai.yml b/Configs/supportbot-ai.yml index 0dd7cc3..02b5494 100644 --- a/Configs/supportbot-ai.yml +++ b/Configs/supportbot-ai.yml @@ -17,11 +17,11 @@ General: # Max tokens you can set is 400 # For more information about tokens, go to https://openai.com/api/pricing/ - OpenAI_Key: "OPEN AI KEY" + OpenAI_Key: "OPENAI_KEY" # To create an OpenAI API key, go to https://platform.openai.com/api-keys - PastebinAPI_Key: "PASTEBIN KEY" + PastebinAPI_Key: "PASTEBIN_KEY" # You can get your Pastebin API Key from https://pastebin.com PastebinAPI_URL: "https://pastebin.com/api/api_post.php" diff --git a/Configs/supportbot.yml b/Configs/supportbot.yml index 092c0f1..f8d52e9 100644 --- a/Configs/supportbot.yml +++ b/Configs/supportbot.yml @@ -11,7 +11,7 @@ # Official Documentation: https://github.com/Emerald-Services/SupportBot/wiki # Third-Party Documentation: COMING SOON -SupportBot_Version: "v8.0.1" +SupportBot_Version: "v8.1" # Do not change the version number # ------------------------- @@ -20,7 +20,7 @@ SupportBot_Version: "v8.0.1" General: Name: "SupportBot" - Token: "BOT TOKEN HERE" + Token: "BOT_TOKEN_HERE" # To get your Discord Bot Token, Go to https://discord.com/developers/applications Addons: diff --git a/Data/Profiles/419821884431925260.json b/Data/Profiles/419821884431925260.json index b096324..bf79b27 100644 --- a/Data/Profiles/419821884431925260.json +++ b/Data/Profiles/419821884431925260.json @@ -1,5 +1,5 @@ { - "clockedIn": true, + "clockedIn": false, "bio": "Hi", "timezone": "GMT" } \ No newline at end of file diff --git a/Data/Profiles/709003903353618506.json b/Data/Profiles/709003903353618506.json new file mode 100644 index 0000000..134d8ba --- /dev/null +++ b/Data/Profiles/709003903353618506.json @@ -0,0 +1,5 @@ +{ + "bio": "", + "timezone": "", + "clockedIn": true +} \ No newline at end of file diff --git a/Data/SuggestionData.json b/Data/SuggestionData.json index b550ff7..c401bda 100644 --- a/Data/SuggestionData.json +++ b/Data/SuggestionData.json @@ -12,5 +12,11 @@ "author": "419821884431925260", "upvotes": [], "downvotes": [] + }, + "1260279162854441042": { + "suggestion": "test", + "author": "419821884431925260", + "upvotes": [], + "downvotes": [] } } \ No newline at end of file diff --git a/Data/TicketData.json b/Data/TicketData.json index 9f923fb..d7b44e2 100644 --- a/Data/TicketData.json +++ b/Data/TicketData.json @@ -179,6 +179,91 @@ "createdAt": "2024-07-02T19:37:19.467Z", "claimedAt": "2024-07-02T19:43:12.873Z", "claimedBy": "419821884431925260" + }, + { + "id": "1259980339816697907", + "name": "ticket-0037", + "user": "419821884431925260", + "number": "0037", + "reason": "> No Reason Provided!", + "open": false, + "subUsers": [], + "createdAt": "2024-07-08T21:12:06.009Z", + "claimedAt": "2024-07-08T21:12:17.454Z", + "claimedBy": "419821884431925260" + }, + { + "id": "1259980831749832807", + "name": "ticket-0038", + "user": "419821884431925260", + "number": "0038", + "reason": "> No Reason Provided!", + "open": false, + "subUsers": [], + "createdAt": "2024-07-08T21:14:01.173Z", + "claimedAt": "2024-07-08T21:14:12.930Z", + "claimedBy": "419821884431925260" + }, + { + "id": "1260004238856163470", + "name": "ticket-0039", + "user": "419821884431925260", + "number": "0039", + "reason": "> No Reason Provided!", + "open": true, + "subUsers": [], + "createdAt": "2024-07-08T22:47:02.254Z", + "claimedAt": "2024-07-08T22:47:11.156Z", + "claimedBy": "419821884431925260" + }, + { + "id": "1260009197207027846", + "name": "ticket-0040", + "user": "419821884431925260", + "number": "0040", + "reason": "test", + "open": true, + "subUsers": [], + "createdAt": "2024-07-08T23:06:44.329Z", + "claimedAt": "2024-07-08T23:06:51.047Z", + "claimedBy": "419821884431925260" + }, + { + "id": "1260257345292865556", + "name": "ticket-0041", + "user": "419821884431925260", + "number": "0041", + "reason": "blob", + "open": true, + "subUsers": [ + "709003903353618506", + "709003903353618506", + "709003903353618506", + "709003903353618506", + "709003903353618506", + "709003903353618506", + "709003903353618506", + "419821884431925260", + "419821884431925260", + "709003903353618506", + "419821884431925260", + "709003903353618506" + ], + "createdAt": "2024-07-09T15:32:47.543Z", + "claimedAt": "2024-07-09T15:33:06.442Z", + "claimedBy": "419821884431925260" + }, + { + "id": "1260263275464233060", + "name": "ticket-0042", + "user": "419821884431925260", + "number": "0042", + "reason": "blob", + "open": true, + "subUsers": [], + "createdAt": "2024-07-09T15:56:21.766Z", + "claimedAt": "2024-07-09T15:58:15.205Z", + "claimedBy": "709003903353618506" } ] } \ No newline at end of file diff --git a/Data/ticket-panel-id.json b/Data/ticket-panel-id.json index 805b116..6b7a3d9 100644 --- a/Data/ticket-panel-id.json +++ b/Data/ticket-panel-id.json @@ -1,4 +1,4 @@ { - "id": 37, + "id": 43, "TicketPanelID": "1250863985335472312" } \ No newline at end of file diff --git a/Events/interactionCreate.js b/Events/interactionCreate.js index c636649..36fe9e6 100644 --- a/Events/interactionCreate.js +++ b/Events/interactionCreate.js @@ -16,20 +16,25 @@ try { } module.exports = new Event("interactionCreate", async (client, interaction) => { - if (interaction.type == Discord.InteractionType.ApplicationCommand) { + console.log("Interaction received:", interaction.type); // Log interaction type + + if (interaction.type === Discord.InteractionType.ApplicationCommand) { const command = client.commands.find( (cmd) => cmd.name.toLowerCase() === interaction.commandName ); + console.log("Command received:", interaction.commandName); // Log command name + if (interaction.user.bot || !interaction.guild) return; const NotValid = new Discord.EmbedBuilder() - .setDescription(`:x: \`Invalid Command\` `) + .setDescription(`:x: \`Invalid Command\``) .setColor(supportbot.Embed.Colours.Error); if (!command) return interaction.reply({ embeds: [NotValid], + ephemeral: true, }); const permission = interaction.member.permissions.has(command.permissions); @@ -43,8 +48,50 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { if (!permission) return interaction.reply({ embeds: [ValidPerms], + ephemeral: true, }); - command.run(interaction); + + try { + if (command.name === cmdconfig.OpenTicket.Command && supportbot.Ticket.TicketReason) { + const modal = new Discord.ModalBuilder() + .setCustomId("ticketReasonModal") + .setTitle("Ticket Reason") + .addComponents( + new Discord.ActionRowBuilder().addComponents( + new Discord.TextInputBuilder() + .setCustomId("reasonInput") + .setLabel("Reason") + .setStyle(Discord.TextInputStyle.Paragraph) + .setRequired(true) + ) + ); + + await interaction.showModal(modal); + } else { + console.log("Running command:", command.name); // Log running command + await command.run(interaction); + } + } catch (error) { + console.error("Error executing command:", error); + await interaction.reply({ + content: "An error occurred while executing the command.", + ephemeral: true, + }); + } + } + + if (interaction.isModalSubmit()) { + if (interaction.customId === "ticketReasonModal") { + const reason = interaction.fields.getTextInputValue("reasonInput"); + interaction.customId = `createticket-${reason}`; + const cmd = client.commands.get(cmdconfig.OpenTicket.Command); + if (cmd) { + interaction.reason = reason; // Attach the reason to the interaction object + await cmd.run(interaction); + } else { + console.error("Command not found."); + } + } } if (interaction.isStringSelectMenu()) { @@ -52,26 +99,11 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { const selectedOption = interaction.values[0]; const { getRole, getChannel } = interaction.client; switch (selectedOption) { - case "ticketadduser": - const AddUserThreadTicket = new Discord.EmbedBuilder() - .setTitle(`${msgconfig.Ticket.AddUserToTicket}`) - .setDescription(`${msgconfig.Ticket.AddUserToTicket}`) - .setColor(supportbot.Embed.Colours.General); - - await interaction.reply({ - embeds: [AddUserThreadTicket], - ephemeral: true, - }); - break; - - case "ticketremoveuser": - await removeUserFromThread(interaction); - break; case "ticketclose": const closeCommand = client.commands.get(cmdconfig.CloseTicket.Command); if (closeCommand) { - closeCommand.run(interaction); + await closeCommand.run(interaction); } else { console.error("[X] Close Command Not Found"); } @@ -89,32 +121,66 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { } if (interaction.isButton()) { - if (interaction.customId === 'createticket') { - try { - const cmd = client.commands.get(cmdconfig.OpenTicket.Command); - if (!cmd) return; - cmd.run(interaction); - } catch (error) { - console.error("Error executing the ticket command:", error); - await interaction.reply({ - content: "An error occurred while creating the ticket.", - ephemeral: true, - }); + if (interaction.customId === "createticket") { + if (supportbot.Ticket.TicketReason) { + const modal = new Discord.ModalBuilder() + .setCustomId("ticketReasonModal") + .setTitle("Ticket Reason") + .addComponents( + new Discord.ActionRowBuilder().addComponents( + new Discord.TextInputBuilder() + .setCustomId("reasonInput") + .setLabel("Reason") + .setStyle(Discord.TextInputStyle.Paragraph) + .setRequired(true) + ) + ); + + await interaction.showModal(modal); + } else { + try { + const cmd = client.commands.get(cmdconfig.OpenTicket.Command); + if (!cmd) return; + await cmd.run(interaction); + } catch (error) { + console.error("Error executing the ticket command:", error); + await interaction.reply({ + content: "An error occurred while creating the ticket.", + ephemeral: true, + }); + } } } if (interaction.customId === "openticket") { - try { - const cmd = client.commands.get(cmdconfig.OpenTicket); - if (!cmd) return; - cmd.run(interaction); - } catch (error) { - console.error(error); + if (supportbot.Ticket.TicketReason) { + const modal = new Discord.ModalBuilder() + .setCustomId("ticketReasonModal") + .setTitle("Ticket Reason") + .addComponents( + new Discord.ActionRowBuilder().addComponents( + new Discord.TextInputBuilder() + .setCustomId("reasonInput") + .setLabel("Reason") + .setStyle(Discord.TextInputStyle.Paragraph) + .setRequired(true) + ) + ); + + await interaction.showModal(modal); + } else { + try { + const cmd = client.commands.get(cmdconfig.OpenTicket.Command); + if (!cmd) return; + await cmd.run(interaction); + } catch (error) { + console.error(error); + } } } - if (interaction.customId.startsWith('claimticket-')) { - const ticketChannelId = interaction.customId.split('-')[1]; + if (interaction.customId.startsWith("claimticket-")) { + const ticketChannelId = interaction.customId.split("-")[1]; const ticketChannel = interaction.guild.channels.cache.get(ticketChannelId); if (!ticketChannel) { return interaction.reply({ @@ -144,7 +210,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { const NoPermsClaim = new Discord.EmbedBuilder() .setTitle(msgconfig.Ticket.ClaimTickets.NoPermsToClaimTitle) .setDescription( - msgconfig.Ticket.ClaimTickets.NoPermsToClaim.replace('%channel%', ticketChannel.name) + msgconfig.Ticket.ClaimTickets.NoPermsToClaim.replace("%channel%", ticketChannel.name) ) .setColor(supportbot.Embed.Colours.Error); @@ -160,7 +226,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { setTimeout(() => { pingclaimedstaff.delete(); - }, 2000) + }, 2000); const TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8")); const ticketIndex = TicketData.tickets.findIndex((t) => t.id === ticketChannelId); @@ -174,13 +240,13 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { const claimedEmbed = new Discord.EmbedBuilder(claimMessage.embeds[0].data) .setTitle(msgconfig.Ticket.ClaimTickets.ClaimedTitle) .setDescription( - msgconfig.Ticket.ClaimTickets.ClaimMessage_Edit.replace('%user%', interaction.user.id).replace('%channel%', ticketChannel.id) + msgconfig.Ticket.ClaimTickets.ClaimMessage_Edit.replace("%user%", interaction.user.id).replace("%channel%", ticketChannel.id) ) .setColor(supportbot.Embed.Colours.Success); const successfullyClaimed = new Discord.EmbedBuilder() .setTitle(msgconfig.Ticket.ClaimTickets.ClaimedTitle) - .setDescription(msgconfig.Ticket.ClaimTickets.Claimed.replace('%channel%', ticketChannel.id)) + .setDescription(msgconfig.Ticket.ClaimTickets.Claimed.replace("%channel%", ticketChannel.id)) .setColor(supportbot.Embed.Colours.Success); await claimMessage.edit({ embeds: [claimedEmbed], components: [] }); @@ -191,36 +257,41 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { } // SUGGESTION INTERACTIONS [START] - - if (interaction.customId === 'upvote' || interaction.customId === 'downvote' || interaction.customId === 'removevote') { + + if ( + interaction.customId === "upvote" || + interaction.customId === "downvote" || + interaction.customId === "removevote" + ) { const suggestion = suggestions[interaction.message.id]; - if (!suggestion) return interaction.reply({ content: 'Suggestion not found.', ephemeral: true }); + if (!suggestion) + return interaction.reply({ content: "Suggestion not found.", ephemeral: true }); const userId = interaction.user.id; const hasUpvoted = suggestion.upvotes.includes(userId); const hasDownvoted = suggestion.downvotes.includes(userId); - if (interaction.customId === 'upvote') { + if (interaction.customId === "upvote") { if (hasUpvoted) { - suggestion.upvotes = suggestion.upvotes.filter(id => id !== userId); + suggestion.upvotes = suggestion.upvotes.filter((id) => id !== userId); } else { suggestion.upvotes.push(userId); if (hasDownvoted) { - suggestion.downvotes = suggestion.downvotes.filter(id => id !== userId); + suggestion.downvotes = suggestion.downvotes.filter((id) => id !== userId); } } - } else if (interaction.customId === 'downvote') { + } else if (interaction.customId === "downvote") { if (hasDownvoted) { - suggestion.downvotes = suggestion.downvotes.filter(id => id !== userId); + suggestion.downvotes = suggestion.downvotes.filter((id) => id !== userId); } else { suggestion.downvotes.push(userId); if (hasUpvoted) { - suggestion.upvotes = suggestion.upvotes.filter(id => id !== userId); + suggestion.upvotes = suggestion.upvotes.filter((id) => id !== userId); } } - } else if (interaction.customId === 'removevote') { - suggestion.upvotes = suggestion.upvotes.filter(id => id !== userId); - suggestion.downvotes = suggestion.downvotes.filter(id => id !== userId); + } else if (interaction.customId === "removevote") { + suggestion.upvotes = suggestion.upvotes.filter((id) => id !== userId); + suggestion.downvotes = suggestion.downvotes.filter((id) => id !== userId); } fs.writeFileSync("./Data/SuggestionData.json", JSON.stringify(suggestions, null, 2)); @@ -228,13 +299,20 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { const upvoteCount = suggestion.upvotes.length; const downvoteCount = suggestion.downvotes.length; - const updatedEmbed = new Discord.EmbedBuilder(interaction.message.embeds[0].data) - .setFields([ - { name: 'Suggestion', value: suggestion.suggestion, inline: true }, - { name: 'From', value: `<@${suggestion.author}>` }, - { name: `${supportbot.Suggestions.UpvoteEmoji} ${supportbot.Suggestions.UpvoteTitle}`, value: `${upvoteCount}`, inline: true }, - { name: `${supportbot.Suggestions.DownvoteEmoji} ${supportbot.Suggestions.DownvoteTitle}`, value: `${downvoteCount}`, inline: true } - ]); + const updatedEmbed = new Discord.EmbedBuilder(interaction.message.embeds[0].data).setFields([ + { name: "Suggestion", value: suggestion.suggestion, inline: true }, + { name: "From", value: `<@${suggestion.author}>` }, + { + name: `${supportbot.Suggestions.UpvoteEmoji} ${supportbot.Suggestions.UpvoteTitle}`, + value: `${upvoteCount}`, + inline: true, + }, + { + name: `${supportbot.Suggestions.DownvoteEmoji} ${supportbot.Suggestions.DownvoteTitle}`, + value: `${downvoteCount}`, + inline: true, + }, + ]); await interaction.update({ embeds: [updatedEmbed] }); } @@ -246,7 +324,9 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { const 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() @@ -256,7 +336,10 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { ) .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] }); } } @@ -264,9 +347,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { await interaction.channel.setInvitable(enable); const message = enable ? msgconfig.Ticket.EnableInvites : msgconfig.Ticket.DisableInvites; - const embed = new Discord.EmbedBuilder() - .setDescription(message) - .setColor(supportbot.Embed.Colours.General); + const embed = new Discord.EmbedBuilder().setDescription(message).setColor(supportbot.Embed.Colours.General); await interaction.reply({ embeds: [embed], @@ -275,7 +356,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { } // Modal submission handler for editing profile - if (interaction.type === Discord.InteractionType.ModalSubmit && interaction.customId === 'editProfileModal') { + if (interaction.type === Discord.InteractionType.ModalSubmit && interaction.customId === "editProfileModal") { const userId = interaction.user.id; const profilePath = `./Data/Profiles/${userId}.json`; @@ -286,13 +367,13 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { profileData = { bio: "", timezone: "", - clockedIn: false + clockedIn: false, }; } try { - const newBio = interaction.fields.getTextInputValue('bio'); - const newTimezone = interaction.fields.getTextInputValue('timezone'); + const newBio = interaction.fields.getTextInputValue("bio"); + const newTimezone = interaction.fields.getTextInputValue("timezone"); profileData.bio = newBio; profileData.timezone = newTimezone; @@ -304,22 +385,25 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { .setColor(supportbot.Embed.Colours.General) .setThumbnail(interaction.user.displayAvatarURL()) .addFields( - { name: 'Bio', value: newBio || 'No bio set.', inline: false }, - { name: 'Timezone', value: newTimezone || 'No timezone set.', inline: true } + { name: "Bio", value: newBio || "No bio set.", inline: false }, + { name: "Timezone", value: newTimezone || "No timezone set.", inline: true } ); if (profileData.clockedIn !== undefined) { updatedProfileEmbed.addFields({ - name: 'Clocked In Status', - value: profileData.clockedIn ? '✅ Clocked In' : '❌ Clocked Out', - inline: true + name: "Clocked In Status", + value: profileData.clockedIn ? "✅ Clocked In" : "❌ Clocked Out", + inline: true, }); } await interaction.reply({ embeds: [updatedProfileEmbed], ephemeral: true }); } catch (error) { console.error("Failed to update profile data: ", error); - await interaction.reply({ content: "There was an error while updating your profile. Please try again later.", ephemeral: true }); + await interaction.reply({ + content: "There was an error while updating your profile. Please try again later.", + ephemeral: true, + }); } } }); diff --git a/Events/messageCreate.js b/Events/messageCreate.js index b1999a1..724537b 100644 --- a/Events/messageCreate.js +++ b/Events/messageCreate.js @@ -14,10 +14,10 @@ function loadConfig(path) { } } -const supportbotai = yaml.load(fs.readFileSync("./Configs/supportbot-ai.yml")); -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 supportbotai = loadConfig("./Configs/supportbot-ai.yml"); +const supportbot = loadConfig("./Configs/supportbot.yml"); +const cmdconfig = loadConfig("./Configs/commands.yml"); +const msgconfig = loadConfig("./Configs/messages.yml"); // Validate configuration values function validateConfig(config, keys) { @@ -28,6 +28,11 @@ function validateConfig(config, keys) { }); } +validateConfig(supportbotai, ['General', 'Channels', 'Messages']); +validateConfig(supportbot, ['Embed']); +validateConfig(cmdconfig, ['Ping']); +validateConfig(msgconfig, ['ErrorResponse', 'OpenAIError']); + const CHANNELS = [supportbotai.Channels.AIChannel]; // Initialize Discord client @@ -64,11 +69,26 @@ async function createPaste(content) { } } +// Function to fetch latest commands and configuration +function getDynamicData() { + const commands = loadConfig("./Configs/commands.yml"); + const configurations = { + supportbotai: loadConfig("./Configs/supportbot-ai.yml"), + supportbot: loadConfig("./Configs/supportbot.yml"), + cmdconfig: loadConfig("./Configs/commands.yml"), + msgconfig: loadConfig("./Configs/messages.yml"), + }; + + return { commands, configurations }; +} + // Event handler for message creation module.exports = new Event("messageCreate", async (client, message) => { if (message.author.bot || !message.guild) return; if (!CHANNELS.includes(message.channelId) && !message.mentions.users.has(client.user.id)) return; + const { commands, configurations } = getDynamicData(); + if (supportbotai.Enabled) { const sendTypingInterval = setInterval(() => { message.channel.sendTyping(); diff --git a/Structures/Client.js b/Structures/Client.js index 4afb632..1b12303 100644 --- a/Structures/Client.js +++ b/Structures/Client.js @@ -85,10 +85,22 @@ class Client extends Discord.Client { tempCommandFiles = tempCommandFiles.filter(item => item !== "Ping.js"); } - if (supportbot.Ticket.ClaimTickets.Enabled === false) { - tempCommandFiles = tempCommandFiles.filter(item => item !== "ticketstats.js"); + if (cmdconfig.AddUser.Enabled === false) { + tempCommandFiles = tempCommandFiles.filter(item => item !== "addUser.js"); } + if (cmdconfig.RemoveUser.Enabled === false) { + tempCommandFiles = tempCommandFiles.filter(item => item !== "removeUser.js"); + } + + if (cmdconfig.ForceAddUser.Enabled === false) { + tempCommandFiles = tempCommandFiles.filter(item => item !== "forceaddUser.js"); + } + + if (cmdconfig.Profile.Enabled === false) { + tempCommandFiles = tempCommandFiles.filter(item => item !== "Profile.js"); + } + const commandFiles = tempCommandFiles; const commands = commandFiles.map((file) => require(`../Commands/${file}`)); diff --git a/Structures/generateImage.js b/Structures/generateImage.js new file mode 100644 index 0000000..d5b201e --- /dev/null +++ b/Structures/generateImage.js @@ -0,0 +1,33 @@ +const fs = require("fs"); +const yaml = require("js-yaml"); +const axios = require("axios"); + +const supportbotai = yaml.load(fs.readFileSync("./Configs/supportbot-ai.yml", "utf8")); + +async function generateImage(prompt, negativePrompt, cfgScale) { + try { + const response = await axios.post('https://api.pepperai.xyz/generate', { + prompt: prompt, + negativePrompt: negativePrompt, + cfgScale: cfgScale + }, { + headers: { + 'Authorization': `Bearer ${supportbotai.General.PepperAI_Key}`, + 'Content-Type': 'application/json' + } + }); + + console.log('PepperAI Response:', response.data); // Log the entire response for debugging + + if (response.data && response.data.imageUrl) { + return response.data.imageUrl; + } else { + throw new Error(`Invalid response structure: ${JSON.stringify(response.data)}`); + } + } catch (error) { + console.error('PepperAI Image Generation Error:', error); + throw error; + } +} + +module.exports = generateImage; From 4f14722e14f28c03e8e36a889d0419e7c90da706 Mon Sep 17 00:00:00 2001 From: 1tzEmerald Date: Tue, 30 Jul 2024 17:45:35 +0100 Subject: [PATCH 3/3] 8.0.1 Update --- Commands/close.js | 3 +- Commands/ticket.js | 10 + Configs/messages.yml | 77 +- Configs/supportbot-ai.yml | 5 +- Configs/supportbot.yml | 31 +- Data/Profiles.json | 8 +- Data/Profiles/294146279456636929.json | 5 - Data/Profiles/419821884431925260.json | 5 - Data/Profiles/682708736359399524.json | 5 - Data/Profiles/709003903353618506.json | 5 - Data/StaffData.json | 4 +- Data/SuggestionData.json | 23 +- Data/TicketData.json | 266 +---- Data/ticket-panel-id.json | 4 +- Events/interactionCreate.js | 64 +- Events/messageCreate.js | 208 ++-- Events/ready.js | 105 +- Structures/Client.js | 12 +- Structures/generateImage.js | 33 - package-lock.json | 1593 +++++++++++++++++++++++++ 20 files changed, 1837 insertions(+), 629 deletions(-) delete mode 100644 Data/Profiles/294146279456636929.json delete mode 100644 Data/Profiles/419821884431925260.json delete mode 100644 Data/Profiles/682708736359399524.json delete mode 100644 Data/Profiles/709003903353618506.json delete mode 100644 Structures/generateImage.js create mode 100644 package-lock.json diff --git a/Commands/close.js b/Commands/close.js index dc497ab..328d190 100644 --- a/Commands/close.js +++ b/Commands/close.js @@ -81,7 +81,8 @@ module.exports = new Command({ const CloseButton = new Discord.ButtonBuilder() .setCustomId("confirmCloseTicket") .setLabel(supportbot.Ticket.Close.Confirmation_Button) - .setStyle(Discord.ButtonStyle.Danger); + .setEmoji(supportbot.Ticket.Close.Confirmation_Emoji) + .setStyle(supportbot.Ticket.Close.Confirmation_Style); const row = new Discord.ActionRowBuilder().addComponents(CloseButton); diff --git a/Commands/ticket.js b/Commands/ticket.js index d56b710..04fe798 100644 --- a/Commands/ticket.js +++ b/Commands/ticket.js @@ -376,6 +376,16 @@ module.exports = new Command({ .setDescription("Close the ticket.") .setEmoji(supportbot.SelectMenus.Tickets.CloseEmoji) .setValue("ticketclose"), + new StringSelectMenuOptionBuilder() + .setLabel("Archive") + .setDescription("Acrhive the ticket.") + .setEmoji(supportbot.SelectMenus.Tickets.ArchiveEmoji) + .setValue("archiveticket"), + new StringSelectMenuOptionBuilder() + .setLabel("Lock Ticket") + .setDescription("Lock the ticket.") + .setEmoji(supportbot.SelectMenus.Tickets.LockEmoji) + .setValue("lockticket"), new StringSelectMenuOptionBuilder() .setLabel("Enable Invites") .setDescription("Enable invites.") diff --git a/Configs/messages.yml b/Configs/messages.yml index 09fde0a..27f6804 100644 --- a/Configs/messages.yml +++ b/Configs/messages.yml @@ -24,7 +24,7 @@ Welcome: Embed: Colour: "#f74343" Title: "Welcome to ServerName" - Message: "> :smile: %joined_user% has just landed!" + Message: ":smile: %joined_user% has just landed!" Thumbnail: "BOT" ImageEnabled: false @@ -34,7 +34,7 @@ Leave: Embed: Colour: "#f74343" Title: "Goodbye!" - Message: "> :slight_frown: %joined_user% has recently dropped out!" + Message: ":slight_frown: %joined_user% has recently dropped out!" Thumbnail: "BOT" ImageEnabled: false @@ -42,8 +42,8 @@ Leave: Suggestions: Sent_Title: "Suggestion Sent!" - Sent: "> :white_check_mark: **You have successfully sent a suggestion.**" - NoSelfVote: "> :x: **You cannot vote on your own suggestion.**" + Sent: ":white_check_mark: **You have successfully sent a suggestion.**" + NoSelfVote: ":x: **You cannot vote on your own suggestion.**" RemoveVoteButton: "Remove Vote" Ticket: @@ -56,44 +56,47 @@ Ticket: TicketAuthorTitle: "%ticketusername%'s Ticket" TicketTitle: "Support Ticket" - TicketMessage: "> Hello <@%ticketauthor%>, Thank you for creating a support ticket\nPlease wait patiently whilst a member of our team reaches out to you." + TicketMessage: "Hello <@%ticketauthor%>, Thank you for creating a support ticket\nPlease wait patiently whilst a member of our team reaches out to you." TicketThumbnail: "https://images.emojiterra.com/google/android-pie/512px/1f39f.png" - TicketCreatedAlert: "> :ticket: Ticked Created: <#%ticketid%>" - TicketExists: "> ❌ Ticket already exists" - TicketClaim: "> :white_check_mark: Ticket claimed by" #The user who claimed the ticket will be added to the end of this message - TicketUnclaim: "> :white_check_mark: Ticket unclaimed by" #The user who claimed the ticket will be added to the end of this message + TicketCreatedAlert: ":ticket: Ticked Created: <#%ticketid%>" + TicketExists: "❌ Ticket already exists" + TicketClaim: ":white_check_mark: Ticket claimed by" #The user who claimed the ticket will be added to the end of this message + TicketUnclaim: ":white_check_mark: Ticket unclaimed by" #The user who claimed the ticket will be added to the end of this message - DisableInvites: "> 🔒 Invites have been disabled for this ticket." - EnableInvites: "> 🔓 Invites have been enabled for this ticket." + TicketArchived: ":white_check_mark: Ticket archived." + TicketUnarchived: ":white_check_mark: Ticket unarchived." + TicketLocked: ":lock: Ticket is locked." + DisableInvites: "🔒 Invites have been disabled for this ticket." + EnableInvites: "🔓 Invites have been enabled for this ticket." # Available Placeholders: # <@%user%> - Tags the user who was added to the ticket. - Blacklisted: "> :x: You are not allowed to open a ticket" - Muted: "> :x: You are `Muted` and therefore not allowed to open any tickets" - InvalidSubject: "> No Reason Provided!" + Blacklisted: ":x: You are not allowed to open a ticket" + Muted: ":x: You are `Muted` and therefore not allowed to open any tickets" + InvalidSubject: "No Reason Provided!" TranscriptSavedMessage: "📝 **Transcript Saved!**" TicketLockMessage: "❌ **Ticket Locked!**" TicketDeleteMessage: "⚠ **Ticket Deleting!**" - ClosingTicket: "> :warning: You have requested to close your support ticket." - ConfirmClose: "> Please confirm closing the ticket by clicking the button below." - CloseTimeout: "> Ticket close confirmation timed out." + ClosingTicket: ":warning: You have requested to close your support ticket." + ConfirmClose: "Please confirm closing the ticket by clicking the button below." + CloseTimeout: "Ticket close confirmation timed out." ClaimTickets: ClaimTitle: "Incoming Support Request!" - ClaimMessage: "> A new ticket has been created by <@!%user%>. Click the button below to claim this ticket." - ClaimMessage_Edit: "> <#%channel%> was claimed by <@!%user%>." + ClaimMessage: "A new ticket has been created by <@!%user%>. Click the button below to claim this ticket." + ClaimMessage_Edit: "<#%channel%> was claimed by <@!%user%>." ClaimedTitle: "Ticket Claimed!" - Claimed: "> You have successfully claimed this ticket. <#%channel%>" + Claimed: "You have successfully claimed this ticket. <#%channel%>" NoPermsToClaimTitle: "Error, no permissions!" - NoPermsToClaim: "> :x: **Err!** You do not permissions to claim this ticket." + NoPermsToClaim: ":x: **Err!** You do not permissions to claim this ticket." ReassigningTitle: "Reassigning!..." - ReassigningMessage: "> <@%user%> did not claim the ticket. **Reassigning...**" + ReassigningMessage: "<@%user%> did not claim the ticket. **Reassigning...**" TicketStats: OpenTickets: "✅ Open Tickets" @@ -104,37 +107,37 @@ TicketStats: AddUser: NotInTicket_Title: "Ticket Invitation" - NotInTicket_Description: "> This command can only be used within a ticket thread." + NotInTicket_Description: "This command can only be used within a ticket thread." Invite_Title: "User Invited" - Invite_Description: "> %username% has been invited to join the ticket." + Invite_Description: "%username% has been invited to join the ticket." DM_Title: "Ticket Invitation" - DM_Description: "> You have been invited to join the ticket %channelname%. Do you accept?" + DM_Description: "You have been invited to join the ticket %channelname%. Do you accept?" Accept_Label: "Accept" Reject_Label: "Reject" ViewTicket_Label: "Go to Ticket" Accepted_Title: "Invitation Accepted" - Accepted_Message: "> You have been added to the ticket." + Accepted_Message: "You have been added to the ticket." AddedToTicket_Title: "Added to Ticket" - AddedToTicket_Description: "> You have been added to the ticket %channel_link%." + AddedToTicket_Description: "You have been added to the ticket %channel_link%." Declined_Title: "Invitation Declined" - Declined_Message: "> You have declined the invitation." + Declined_Message: "You have declined the invitation." NoResponse_Title: "No Response" - NoResponse_Message: "> You did not respond in time. Please contact staff if you wish to join the ticket." + NoResponse_Message: "You did not respond in time. Please contact staff if you wish to join the ticket." Error_Title: "Error" - Error_Adding: "> There was an error adding you to the ticket. Please try again." + Error_Adding: "There was an error adding you to the ticket. Please try again." DM_Error_Title: "Error" - DM_Error_Description: "> Unable to send a DM to %username%. Please ensure your DMs are enabled." + DM_Error_Description: "Unable to send a DM to %username%. Please ensure your DMs are enabled." Unexpected_Error_Title: "Error" - Unexpected_Error_Description: "> An unexpected error occurred: %error%" + Unexpected_Error_Description: "An unexpected error occurred: %error%" RemoveUser: NotInTicket_Title: "Error" @@ -163,11 +166,11 @@ 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!" + 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!" diff --git a/Configs/supportbot-ai.yml b/Configs/supportbot-ai.yml index 02b5494..e68c4ad 100644 --- a/Configs/supportbot-ai.yml +++ b/Configs/supportbot-ai.yml @@ -16,12 +16,11 @@ General: Tokens: 400 # Max tokens you can set is 400 # For more information about tokens, go to https://openai.com/api/pricing/ - - OpenAI_Key: "OPENAI_KEY" + OpenAI_Key: "OPEN_AI_KEY_HERE" # To create an OpenAI API key, go to https://platform.openai.com/api-keys - PastebinAPI_Key: "PASTEBIN_KEY" + PastebinAPI_Key: "PASTEBIN_API_KEY_HERE" # You can get your Pastebin API Key from https://pastebin.com PastebinAPI_URL: "https://pastebin.com/api/api_post.php" diff --git a/Configs/supportbot.yml b/Configs/supportbot.yml index f8d52e9..539fe9c 100644 --- a/Configs/supportbot.yml +++ b/Configs/supportbot.yml @@ -145,11 +145,22 @@ Ticket: StaffOnly: true # Set to false is anyone is able to close a ticket. Confirmation_Button: "Confirm Close" + Confirmation_Emoji: "🗑️" + Confirmation_Style: "2" Title: "Are you sure you want to close this ticket?" + # Available Button Styles + # 1 - Blurple + # 2 - Grey + # 3 - Green + # 4 - Red + Invites: StaffOnly: true + LockedTickets: + Channel: "1267856939275124809" + ClaimTickets: Enabled: true Channel: "1249723473757077624" @@ -188,6 +199,15 @@ Buttons: ClaimEmoji: "🧑‍💻" ClaimStyle: "1" + Unarchive: "Unarchive Ticket" + Unarchive_Emoji: "♻️" + Unarchive_Style: "2" + + Unlock: "Unlock Ticket" + Unlock_Emoji: "🔓" + Unlock_Style: "2" + + # Available Button Styles # 1 - Blurple # 2 - Grey @@ -202,14 +222,13 @@ Buttons: SelectMenus: Tickets: PanelEmoji: "⚙️" + CloseEmoji: "🗑️" ArchiveEmoji: "♻️" - LockTicketEmoji: "🔒" - UnLockTicketEmoji: "🔓" - EnableInvitesEmoji: "🔒" - DisableInvitesEmoji: "🔓" - AddUserEmoji: "➕" - RemoveUserEmoji: "➖" + LockEmoji: "🔐" + + EnableInvitesEmoji: "📤" + DisableInvitesEmoji: "📥" # ------------------------- diff --git a/Data/Profiles.json b/Data/Profiles.json index 667cd6b..9e26dfe 100644 --- a/Data/Profiles.json +++ b/Data/Profiles.json @@ -1,7 +1 @@ -{ - "419821884431925260": { - "bio": "Hi", - "timezone": "", - "isClockedIn": false - } -} \ No newline at end of file +{} \ No newline at end of file diff --git a/Data/Profiles/294146279456636929.json b/Data/Profiles/294146279456636929.json deleted file mode 100644 index 90585c3..0000000 --- a/Data/Profiles/294146279456636929.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "bio": "", - "timezone": "", - "clockedIn": false -} \ No newline at end of file diff --git a/Data/Profiles/419821884431925260.json b/Data/Profiles/419821884431925260.json deleted file mode 100644 index bf79b27..0000000 --- a/Data/Profiles/419821884431925260.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "clockedIn": false, - "bio": "Hi", - "timezone": "GMT" -} \ No newline at end of file diff --git a/Data/Profiles/682708736359399524.json b/Data/Profiles/682708736359399524.json deleted file mode 100644 index 76a6181..0000000 --- a/Data/Profiles/682708736359399524.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "bio": "This is the most W bio", - "timezone": "cst", - "clockedIn": false -} \ No newline at end of file diff --git a/Data/Profiles/709003903353618506.json b/Data/Profiles/709003903353618506.json deleted file mode 100644 index 134d8ba..0000000 --- a/Data/Profiles/709003903353618506.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "bio": "", - "timezone": "", - "clockedIn": true -} \ No newline at end of file diff --git a/Data/StaffData.json b/Data/StaffData.json index 66e39a4..9e26dfe 100644 --- a/Data/StaffData.json +++ b/Data/StaffData.json @@ -1,3 +1 @@ -[ - "419821884431925260" -] \ No newline at end of file +{} \ No newline at end of file diff --git a/Data/SuggestionData.json b/Data/SuggestionData.json index c401bda..9e26dfe 100644 --- a/Data/SuggestionData.json +++ b/Data/SuggestionData.json @@ -1,22 +1 @@ -{ - "1250864227460186240": { - "suggestion": "hi", - "author": "419821884431925260", - "upvotes": [ - "419821884431925260" - ], - "downvotes": [] - }, - "1250900161958248533": { - "suggestion": "blob", - "author": "419821884431925260", - "upvotes": [], - "downvotes": [] - }, - "1260279162854441042": { - "suggestion": "test", - "author": "419821884431925260", - "upvotes": [], - "downvotes": [] - } -} \ No newline at end of file +{} \ No newline at end of file diff --git a/Data/TicketData.json b/Data/TicketData.json index d7b44e2..987b7f9 100644 --- a/Data/TicketData.json +++ b/Data/TicketData.json @@ -1,269 +1,5 @@ { "tickets": [ - {}, - { - "id": "1253862843053510687", - "name": "ticket-0021", - "user": "419821884431925260", - "number": "0021", - "reason": "> No Reason Provided!", - "open": true, - "subUsers": [], - "createdAt": "2024-06-22T00:03:18.227Z", - "claimedAt": null - }, - { - "id": "1253863588012232727", - "name": "ticket-0022", - "user": "419821884431925260", - "number": "0022", - "reason": "> No Reason Provided!", - "open": true, - "subUsers": [], - "createdAt": "2024-06-22T00:06:16.126Z", - "claimedAt": "2024-06-22T00:09:12.693Z", - "claimedBy": "419821884431925260" - }, - { - "id": "1255499438483574847", - "name": "ticket-0023", - "user": "419821884431925260", - "number": "0023", - "reason": "> No Reason Provided!", - "open": true, - "subUsers": [], - "createdAt": "2024-06-26T12:26:35.729Z", - "claimedAt": null - }, - { - "id": "1255500796355678229", - "name": "ticket-0024", - "user": "419821884431925260", - "number": "0024", - "reason": "> No Reason Provided!", - "open": true, - "subUsers": [], - "createdAt": "2024-06-26T12:31:56.888Z", - "claimedAt": "2024-06-26T12:32:41.240Z", - "claimedBy": "419821884431925260" - }, - { - "id": "1255501873386094603", - "name": "ticket-0025", - "user": "419821884431925260", - "number": "0025", - "reason": "> No Reason Provided!", - "open": false, - "subUsers": [], - "createdAt": "2024-06-26T12:36:13.810Z", - "claimedAt": null - }, - { - "id": "1255508653906198709", - "name": "ticket-0026", - "user": "419821884431925260", - "number": "0026", - "reason": "> No Reason Provided!", - "open": true, - "subUsers": [], - "createdAt": "2024-06-26T13:03:10.321Z", - "claimedAt": null - }, - { - "id": "1256299016023572572", - "name": "ticket-0027", - "user": "419821884431925260", - "number": "0027", - "reason": "> No Reason Provided!", - "open": false, - "subUsers": [], - "createdAt": "2024-06-28T17:23:50.113Z", - "claimedAt": null - }, - { - "id": "1256300956824633426", - "name": "ticket-0028", - "user": "419821884431925260", - "number": "0028", - "reason": "> No Reason Provided!", - "open": false, - "subUsers": [], - "createdAt": "2024-06-28T17:31:31.454Z", - "claimedAt": null - }, - { - "id": "1256302271461986507", - "name": "ticket-0029", - "user": "419821884431925260", - "number": "0029", - "reason": "> No Reason Provided!", - "open": false, - "subUsers": [], - "createdAt": "2024-06-28T17:36:44.673Z", - "claimedAt": null - }, - { - "id": "1256303116505317457", - "name": "ticket-0030", - "user": "419821884431925260", - "number": "0030", - "reason": "> No Reason Provided!", - "open": true, - "subUsers": [], - "createdAt": "2024-06-28T17:40:06.292Z", - "claimedAt": null - }, - { - "id": "1257775687562166304", - "name": "ticket-0031", - "user": "419821884431925260", - "number": "0031", - "reason": "> No Reason Provided!", - "open": false, - "subUsers": [], - "createdAt": "2024-07-02T19:11:33.760Z", - "claimedAt": null - }, - { - "id": "1257776136843427841", - "name": "ticket-0032", - "user": "419821884431925260", - "number": "0032", - "reason": "> No Reason Provided!", - "open": false, - "subUsers": [], - "createdAt": "2024-07-02T19:13:20.641Z", - "claimedAt": null - }, - { - "id": "1257777774483996795", - "name": "ticket-0033", - "user": "419821884431925260", - "number": "0033", - "reason": "> No Reason Provided!", - "open": false, - "subUsers": [], - "createdAt": "2024-07-02T19:19:51.116Z", - "claimedAt": null - }, - { - "id": "1257778630600167535", - "name": "ticket-0034", - "user": "419821884431925260", - "number": "0034", - "reason": "> No Reason Provided!", - "open": false, - "subUsers": [], - "createdAt": "2024-07-02T19:23:15.328Z", - "claimedAt": null - }, - { - "id": "1257780259374366841", - "name": "ticket-0035", - "user": "419821884431925260", - "number": "0035", - "reason": "> No Reason Provided!", - "open": false, - "subUsers": [], - "createdAt": "2024-07-02T19:29:43.607Z", - "claimedAt": null - }, - { - "id": "1257782171389657180", - "name": "ticket-0036", - "user": "419821884431925260", - "number": "0036", - "reason": "> No Reason Provided!", - "open": true, - "subUsers": [], - "createdAt": "2024-07-02T19:37:19.467Z", - "claimedAt": "2024-07-02T19:43:12.873Z", - "claimedBy": "419821884431925260" - }, - { - "id": "1259980339816697907", - "name": "ticket-0037", - "user": "419821884431925260", - "number": "0037", - "reason": "> No Reason Provided!", - "open": false, - "subUsers": [], - "createdAt": "2024-07-08T21:12:06.009Z", - "claimedAt": "2024-07-08T21:12:17.454Z", - "claimedBy": "419821884431925260" - }, - { - "id": "1259980831749832807", - "name": "ticket-0038", - "user": "419821884431925260", - "number": "0038", - "reason": "> No Reason Provided!", - "open": false, - "subUsers": [], - "createdAt": "2024-07-08T21:14:01.173Z", - "claimedAt": "2024-07-08T21:14:12.930Z", - "claimedBy": "419821884431925260" - }, - { - "id": "1260004238856163470", - "name": "ticket-0039", - "user": "419821884431925260", - "number": "0039", - "reason": "> No Reason Provided!", - "open": true, - "subUsers": [], - "createdAt": "2024-07-08T22:47:02.254Z", - "claimedAt": "2024-07-08T22:47:11.156Z", - "claimedBy": "419821884431925260" - }, - { - "id": "1260009197207027846", - "name": "ticket-0040", - "user": "419821884431925260", - "number": "0040", - "reason": "test", - "open": true, - "subUsers": [], - "createdAt": "2024-07-08T23:06:44.329Z", - "claimedAt": "2024-07-08T23:06:51.047Z", - "claimedBy": "419821884431925260" - }, - { - "id": "1260257345292865556", - "name": "ticket-0041", - "user": "419821884431925260", - "number": "0041", - "reason": "blob", - "open": true, - "subUsers": [ - "709003903353618506", - "709003903353618506", - "709003903353618506", - "709003903353618506", - "709003903353618506", - "709003903353618506", - "709003903353618506", - "419821884431925260", - "419821884431925260", - "709003903353618506", - "419821884431925260", - "709003903353618506" - ], - "createdAt": "2024-07-09T15:32:47.543Z", - "claimedAt": "2024-07-09T15:33:06.442Z", - "claimedBy": "419821884431925260" - }, - { - "id": "1260263275464233060", - "name": "ticket-0042", - "user": "419821884431925260", - "number": "0042", - "reason": "blob", - "open": true, - "subUsers": [], - "createdAt": "2024-07-09T15:56:21.766Z", - "claimedAt": "2024-07-09T15:58:15.205Z", - "claimedBy": "709003903353618506" - } + {} ] } \ No newline at end of file diff --git a/Data/ticket-panel-id.json b/Data/ticket-panel-id.json index 6b7a3d9..ba9c579 100644 --- a/Data/ticket-panel-id.json +++ b/Data/ticket-panel-id.json @@ -1,4 +1,4 @@ { - "id": 43, - "TicketPanelID": "1250863985335472312" + "id": 0, + "TicketPanelID": "000000000000000000" } \ No newline at end of file diff --git a/Events/interactionCreate.js b/Events/interactionCreate.js index 36fe9e6..f79b9d6 100644 --- a/Events/interactionCreate.js +++ b/Events/interactionCreate.js @@ -16,15 +16,12 @@ try { } module.exports = new Event("interactionCreate", async (client, interaction) => { - console.log("Interaction received:", interaction.type); // Log interaction type if (interaction.type === Discord.InteractionType.ApplicationCommand) { const command = client.commands.find( (cmd) => cmd.name.toLowerCase() === interaction.commandName ); - console.log("Command received:", interaction.commandName); // Log command name - if (interaction.user.bot || !interaction.guild) return; const NotValid = new Discord.EmbedBuilder() @@ -68,7 +65,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { await interaction.showModal(modal); } else { - console.log("Running command:", command.name); // Log running command + console.log("\u001b[32m", "[Executed]", "\u001b[37;1m", `${interaction.user.username} has executed ${command.name}`); await command.run(interaction); } } catch (error) { @@ -109,6 +106,50 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { } break; + case "archiveticket": + const arcembed = new Discord.EmbedBuilder() + .setDescription(msgconfig.Ticket.TicketArchived) + .setColor(supportbot.Embed.Colours.Success); + + 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); + + const unarchiveButton = new Discord.ButtonBuilder() + .setCustomId("unarchiveTicket") + .setLabel(supportbot.Buttons.Tickets.Unarchive) + .setEmoji(supportbot.Buttons.Tickets.Unarchive_Emoji) + .setStyle(supportbot.Buttons.Tickets.Unarchive_Style); + + const ArchivedOptions = new Discord.ActionRowBuilder() + .addComponents(CloseButton, unarchiveButton); + + await interaction.channel.send({ + embeds: [arcembed], + components: [ArchivedOptions], + }); + + await interaction.channel.setArchived(true); + + break; + + case "lockticket": + const lockembed = new Discord.EmbedBuilder() + .setDescription(msgconfig.Ticket.TicketLocked) + .setColor(supportbot.Embed.Colours.Success); + + await interaction.channel.send({ + embeds: [lockembed], + components: [], + ephemeral: true,s + }); + + await interaction.channel.setLocked(true); + + break; + case "enableinvites": await handleInvites(interaction, true); break; @@ -121,6 +162,20 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { } if (interaction.isButton()) { + if (interaction.customId === "unarchiveTicket") { + await interaction.channel.setArchived(false); + + const unrcembed = new Discord.EmbedBuilder() + .setDescription(msgconfig.Ticket.TicketUnarchived) + .setColor(supportbot.Embed.Colours.Success); + + await interaction.update({ + embeds: [unrcembed], + components: [], + }); + + } + if (interaction.customId === "createticket") { if (supportbot.Ticket.TicketReason) { const modal = new Discord.ModalBuilder() @@ -320,6 +375,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { async function handleInvites(interaction, enable) { if (supportbot.Ticket.Invites.StaffOnly) { + const { getRole, getChannel } = interaction.client; const SupportStaff = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild); const Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild); diff --git a/Events/messageCreate.js b/Events/messageCreate.js index 724537b..537efdd 100644 --- a/Events/messageCreate.js +++ b/Events/messageCreate.js @@ -14,11 +14,10 @@ function loadConfig(path) { } } -const supportbotai = loadConfig("./Configs/supportbot-ai.yml"); -const supportbot = loadConfig("./Configs/supportbot.yml"); -const cmdconfig = loadConfig("./Configs/commands.yml"); -const msgconfig = loadConfig("./Configs/messages.yml"); - +const supportbotai = yaml.load(fs.readFileSync("./Configs/supportbot-ai.yml")); +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")); // Validate configuration values function validateConfig(config, keys) { keys.forEach(key => { @@ -28,11 +27,6 @@ function validateConfig(config, keys) { }); } -validateConfig(supportbotai, ['General', 'Channels', 'Messages']); -validateConfig(supportbot, ['Embed']); -validateConfig(cmdconfig, ['Ping']); -validateConfig(msgconfig, ['ErrorResponse', 'OpenAIError']); - const CHANNELS = [supportbotai.Channels.AIChannel]; // Initialize Discord client @@ -69,170 +63,104 @@ async function createPaste(content) { } } -// Function to fetch latest commands and configuration -function getDynamicData() { - const commands = loadConfig("./Configs/commands.yml"); - const configurations = { - supportbotai: loadConfig("./Configs/supportbot-ai.yml"), - supportbot: loadConfig("./Configs/supportbot.yml"), - cmdconfig: loadConfig("./Configs/commands.yml"), - msgconfig: loadConfig("./Configs/messages.yml"), - }; - - return { commands, configurations }; -} - // Event handler for message creation module.exports = new Event("messageCreate", async (client, message) => { if (message.author.bot || !message.guild) return; if (!CHANNELS.includes(message.channelId) && !message.mentions.users.has(client.user.id)) return; - const { commands, configurations } = getDynamicData(); +if (supportbotai.Enabled) { - if (supportbotai.Enabled) { - const sendTypingInterval = setInterval(() => { - message.channel.sendTyping(); - }, 5000); + const sendTypingInterval = setInterval(() => { + message.channel.sendTyping(); + }, 5000); - let conversation = [{ - role: "system", - content: `SupportBot is your friendly neighbourhood support bot. Current date and time: ${new Date().toLocaleString()}. - The bot uses a modular command handler where commands are loaded dynamically from the './Commands' directory. Each command has a name, description, and execute method. - - The main Client class structure: - - Commands are loaded from the './Commands/' directory. - - Configs are loaded from the './Configs/' directory if enabled. - - Events are loaded from the './Events/' directory. - - Addons are loaded from the './Addons/' directory if enabled. - - Example of a command structure: - { - module.exports = new Command({ - name: cmdconfig.Ping.Command, - description: cmdconfig.Ping.Description, - options: [], - permissions: cmdconfig.Ping.Permission, - )} - } + let conversation = [{ + role: "system", + content: "SupportBot is your friendly neighbourhood support bot." + }]; - You can find more information about the commands and configuration files in the 'Configs' and 'Commands' directories. + let prevMessages = await message.channel.messages.fetch({ limit: 10 }); + prevMessages = prevMessages.reverse().filter(msg => !msg.author.bot || msg.author.id === client.user.id); - All commands are loaded into the bot at startup from ./Events/ready.js and ./Client.js. + prevMessages.forEach((msg) => { + const username = msg.author.username.replace(/\s+/g, '_').replace(/[^\w\s]/gi, ''); - If someone asks you to do something, you can use the 'assist' command to have the bot respond, For example: "Can I speak to a support agent?", You would respond by opening a ticket which is the /Commands/ticket.js file or you can tell them to create a ticket in the channel defined as supportbot.Ticket. + conversation.push({ + role: msg.author.id === client.user.id ? "assistant" : "user", + name: username, + content: msg.content, + }); + }); - You can find the source code for the bot at https://github.com/EmeraldServices/SupportBot. - - You then need to understand how the bot works. Interactions are handled by the ./Events/interactionCreate.js file. - - Commands are filtered based on configuration settings and loaded into the bot at startup. - - Xiled Gaming Network (XGN) is a gaming community that typically offers various services and activities for gamers. - - Xiled Gaming Network (XGN) has 4 divisions, Team Xiled International (TXI), The XiledOnes (TXO), Dealers of Death (DOD), Psychotic Solutions (PS) - Xiled Gaming Network (XGN) can be found at https://officialxgn.gg/handbook/, This is where you can the code of conduct, policies and other important information for members. - You can join XGN with the discord - discord.gg/xgn - You can apply to join the XGN Clan - join.officialxgn.gg - - You can find the founders of XGN under the history section of the handbook. https://officialxgn.gg/handbook/. - ` - }]; - - let prevMessages = await message.channel.messages.fetch({ limit: 10 }); - prevMessages = prevMessages.reverse().filter(msg => !msg.author.bot || msg.author.id === client.user.id); - - prevMessages.forEach((msg) => { - const username = msg.author.username.replace(/\s+/g, '_').replace(/[^\w\s]/gi, ''); - - conversation.push({ - role: msg.author.id === client.user.id ? "assistant" : "user", - name: username, - content: msg.content, - }); + try { + const response = await openai.chat.completions.create({ + model: supportbotai.General.Model, + messages: conversation, + max_tokens: supportbotai.General.Tokens, }); - try { - const response = await openai.chat.completions.create({ - model: supportbotai.General.Model, - messages: conversation, - max_tokens: supportbotai.General.Tokens, - }); + clearInterval(sendTypingInterval); - clearInterval(sendTypingInterval); + if (response.choices && response.choices.length > 0) { + const replyContent = response.choices[0].message.content; - if (response.choices && response.choices.length > 0) { - const replyContent = response.choices[0].message.content; + const SupportBotAI = new Discord.EmbedBuilder() + .setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() }) + .setDescription(replyContent) + .setColor(supportbotai.Embed.Color) + .setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() }) + .setTimestamp(); - if (replyContent.toLowerCase().startsWith("generate image:")) { - const prompt = replyContent.substring(15).trim(); - const imageUrl = await generateImage(prompt); + if (replyContent.length > 2000) { + const pasteLink = await createPaste(replyContent); - const SupportBotAIImage = new Discord.EmbedBuilder() - .setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() }) - .setDescription(`Here is the image generated for your prompt: "${prompt}"`) - .setImage(imageUrl) - .setColor(supportbotai.Embed.Color) - .setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() }) - .setTimestamp(); - - message.reply({ embeds: [SupportBotAIImage] }); - } else { - const SupportBotAI = new Discord.EmbedBuilder() - .setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() }) - .setDescription(replyContent) - .setColor(supportbotai.Embed.Color) - .setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() }) - .setTimestamp(); - - if (replyContent.length > 2000) { - const pasteLink = await createPaste(replyContent); - - const SupportBotAIPastebin = new Discord.EmbedBuilder() - .setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() }) - .setDescription(supportbotai.Messages.MessageTooLong.replace("{pastebin}", pasteLink)) - .setColor(supportbotai.Embed.Color) - .setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() }) - .setTimestamp(); - - message.reply({ embeds: [SupportBotAIPastebin] }); - } else { - message.reply({ embeds: [SupportBotAI] }); - } - } - } else { - const SupportBotAIErrorResponse = new Discord.EmbedBuilder() + const SupportBotAIPastebin = new Discord.EmbedBuilder() .setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() }) - .setDescription(supportbotai.Messages.ErrorResponse) + .setDescription(supportbotai.Messages.MessageTooLong.replace("{pastebin}", pasteLink)) .setColor(supportbotai.Embed.Color) .setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() }) .setTimestamp(); - message.reply({ embeds: [SupportBotAIErrorResponse], ephemeral: true }); + message.reply({ embeds: [SupportBotAIPastebin] }); + } else { + message.reply({ embeds: [SupportBotAI] }); } - } catch (error) { - clearInterval(sendTypingInterval); - + } else { const SupportBotAIErrorResponse = new Discord.EmbedBuilder() .setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() }) - .setDescription(supportbotai.Messages.OpenAIError) - .setColor(supportbot.Embed.Colours.Error) + .setDescription(supportbotai.Messages.ErrorResponse) + .setColor(supportbotai.Embed.Color) .setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() }) .setTimestamp(); - console.error('OpenAI Error:', error); message.reply({ embeds: [SupportBotAIErrorResponse], ephemeral: true }); } - } + } catch (error) { + clearInterval(sendTypingInterval); - if (!supportbotai.Enabled) { - const AIDisabled = new Discord.EmbedBuilder() + const SupportBotAIErrorResponse = new Discord.EmbedBuilder() .setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() }) - .setDescription(supportbotai.Messages.AIDisabled) + .setDescription(supportbotai.Messages.OpenAIError) .setColor(supportbot.Embed.Colours.Error) .setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() }) .setTimestamp(); - message.reply({ embeds: [AIDisabled], ephemeral: true }); - return; + console.error('OpenAI Error:', error); + message.reply({ embeds: [SupportBotAIErrorResponse], ephemeral: true }); } -}); + +} + +if (supportbotai.Enabled === false) { + const AIDisabled = new Discord.EmbedBuilder() + .setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() }) + .setDescription(supportbotai.Messages.AIDisabled) + .setColor(supportbot.Embed.Colours.Error) + .setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() }) + .setTimestamp(); + + message.reply({ embeds: [AIDisabled], ephemeral: true }) + return; +} + +}); \ No newline at end of file diff --git a/Events/ready.js b/Events/ready.js index 8d228c6..0289a7b 100644 --- a/Events/ready.js +++ b/Events/ready.js @@ -59,7 +59,7 @@ module.exports = new Event("ready", async (client, interaction) => { }); } - console.log(`\u001b[32m`, `――――――――――――――――――――――――――――――――――――――――――――`); + console.log(`\u001b[33m`, `――――――――――――――――――――――――――――――――――――――――――――`); console.log(` `); console.log(`\u001b[31m`, `┏━━━┓╋╋╋╋╋╋╋╋╋╋╋╋╋┏┓┏━━┓╋╋╋┏┓`); console.log(`\u001b[31m`, `┃┏━┓┃╋╋╋╋╋╋╋╋╋╋╋╋┏┛┗┫┏┓┃╋╋┏┛┗┓`); @@ -70,60 +70,22 @@ module.exports = new Event("ready", async (client, interaction) => { console.log(`\u001b[31m`, `┗╋╋╋╋╋╋╋┃┃╋┃┃`); console.log(`\u001b[31m`, `╋╋╋╋╋╋╋┗┛╋┗┛`); console.log(` `); - console.log(`\u001b[32m`, `――――――――――――――――――――――――――――――――――――――――――――`); + console.log(`\u001b[33m`, `――――――――――――――――――――――――――――――――――――――――――――`); + console.log(` `); + console.log(`\u001b[33m`, `${supportbot.General.Name} | [${supportbot.SupportBot_Version}]`, `\u001b[32m`, `Connected to Discord`,); + console.log("\u001b[32m", "SupportBot created by Emerald Development "); console.log(` `); - console.log( - `\u001b[31m`, - `${supportbot.General.Name} | [${supportbot.SupportBot_Version}]`, - `\u001b[32m`, - `Connected to Discord`, - ); - - console.log(`\u001b[31m`, `SupportBot proudly created by Emerald Development`); - + console.log(`\u001b[33m`, `――――――――――――――――― [Links] ――――――――――――――――――`); + console.log("\u001b[32m", "Discord: https://dsc.gg/emerald-dev"); + console.log("\u001b[32m", "Website: https://emeraldsrv.com"); + console.log("\u001b[32m", "Community: https://community.emeraldsrv.com"); + console.log("\u001b[32m", "Documentation: https://docs.emeraldsrv.com"); console.log(` `); - - console.log( - `\u001b[31m`, - `Discord`, - `\u001b[32m`, - `https://dsc.gg/emerald-dev`, - ); - - console.log( - `\u001b[31m`, - `Website`, - `\u001b[32m`, - `https://emeraldsrv.com`, - ); - - console.log( - `\u001b[31m`, - `Marketplace`, - `\u001b[32m`, - `https://market.emeraldsrv.com`, - ); - - console.log( - `\u001b[31m`, - `Documentation`, - `\u001b[32m`, - `https://emeraldsrv.com/third-party`, - ); - + console.log(`\u001b[33m`, `――――――――――――――――― [Invite] ――――――――――――――――――`); + console.log("\u001b[32m", `Invite URL: https://discord.com/api/oauth2/authorize?client_id=${client.user.id}&permissions=8&scope=bot%20applications.commands`); console.log(` `); - - console.log( - `\u001b[31m`, - `Invite URL:`, - `\u001b[36m`, - `https://discord.com/api/oauth2/authorize?client_id=` + - client.user.id + - `&permissions=8&scope=bot%20applications.commands` - ); - - console.log(` `); - console.log(`\u001b[32m`, `――――――――――――――――――――――――――――――――――――――――――――`); + console.log(`\u001b[33m`, `――――――――――――――――― [Config Check] ――――――――――――――――――`); + console.log("\u001b[32m", "Config initialization..."); const roles = [ supportbot.Roles.StaffMember.Admin, @@ -168,35 +130,18 @@ module.exports = new Event("ready", async (client, interaction) => { // if (!find) missingCat.push(cat); // } - if (missingR.length > 0) { - console.log( - "\u001b[31m", - `[MISSING CONFIGURATION]`, - "\u001b[33m", - `The following Roles could not be found in your server.\n`, `\u001b[31m`, `[${missingR.join( - ", " - )}]`, `\n `, - ); + const missingRoles = await Promise.all(roles.map(role => getRole(role, client.guilds.cache.first()))); + const missingChannels = await Promise.all(channels.map(channel => getChannel(channel, client.guilds.cache.first()))); + + if (missingRoles.some(role => !role)) { + console.log("\u001b[31m", `Missing roles in your server configuration: ${missingRoles.filter(role => !role).join(', ')}`); } - if (missingC.length > 0) { - console.log( - "\u001b[31m", - `[MISSING CONFIGURATION]`, - "\u001b[33m", - `The following Channels could not be found in your server.\n`, `\u001b[31m`, `[${missingC.join( - ", " - )}]`, `\n ` - ); - } - if (missingCat.length > 0) { - console.log( - "\u001b[31m", - `[MISSING CONFIGURATION]`, - "\u001b[33m", - `The following Categories could not be found in your server.\n`, `\u001b[31m`, `[${missingCat.join( - ", " - )}]`, `\n `, - ); + if (missingChannels.some(channel => !channel)) { + console.log("\u001b[31m", `Missing channels in your server configuration: ${missingChannels.filter(channel => !channel).join(', ')}`); } + + console.log("\u001b[32m", "Configs initialized, No problems were detected."); + console.log(` `); + }); \ No newline at end of file diff --git a/Structures/Client.js b/Structures/Client.js index 1b12303..cb27ba5 100644 --- a/Structures/Client.js +++ b/Structures/Client.js @@ -105,7 +105,7 @@ class Client extends Discord.Client { const commands = commandFiles.map((file) => require(`../Commands/${file}`)); // Load main commands - console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Commands ▬▬▬▬▬▬▬"); + console.log(`\u001b[33m`, "――――――――――――――――――――――――――――――――――――――――――――"); commands.forEach((cmd) => { console.log(`\u001b[32m`, `[CMD]`, `\u001b[37;1m`, `${cmd.name}`, `\u001b[32;1m`, "Loaded"); @@ -113,7 +113,7 @@ class Client extends Discord.Client { }); // Load addon commands and events - console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Commands ▬▬▬▬▬▬▬"); + console.log(`\u001b[33m`, "▬▬▬▬▬▬▬ Commands ▬▬▬▬▬▬▬"); if (supportbot.General.Addons.Enabled) { const addonFiles = fs.readdirSync("./Addons").filter((file) => file.endsWith(".js")); @@ -126,7 +126,7 @@ class Client extends Discord.Client { // Addons console.log(" "); - console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬"); + console.log(`\u001b[33m`, "▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬"); addons.forEach((addon) => { console.log( @@ -151,7 +151,7 @@ class Client extends Discord.Client { } }); - console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬"); + console.log(`\u001b[33m`, "▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬"); } // Register Slash Commands @@ -167,7 +167,7 @@ class Client extends Discord.Client { }); console.log(" "); - console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Events ▬▬▬▬▬▬▬"); + console.log(`\u001b[33m`, "▬▬▬▬▬▬▬ Events ▬▬▬▬▬▬▬"); fs.readdirSync("./Events").filter((file) => file.endsWith(".js")).forEach((file) => { const event = require(`../Events/${file}`); @@ -175,7 +175,7 @@ class Client extends Discord.Client { this.on(event.event, (...args) => event.run(this, ...args)); }); - console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Events ▬▬▬▬▬▬▬"); + console.log(`\u001b[33m`, "▬▬▬▬▬▬▬ Events ▬▬▬▬▬▬▬"); if (process.argv[2] !== "test") { await this.login(token); } else { diff --git a/Structures/generateImage.js b/Structures/generateImage.js deleted file mode 100644 index d5b201e..0000000 --- a/Structures/generateImage.js +++ /dev/null @@ -1,33 +0,0 @@ -const fs = require("fs"); -const yaml = require("js-yaml"); -const axios = require("axios"); - -const supportbotai = yaml.load(fs.readFileSync("./Configs/supportbot-ai.yml", "utf8")); - -async function generateImage(prompt, negativePrompt, cfgScale) { - try { - const response = await axios.post('https://api.pepperai.xyz/generate', { - prompt: prompt, - negativePrompt: negativePrompt, - cfgScale: cfgScale - }, { - headers: { - 'Authorization': `Bearer ${supportbotai.General.PepperAI_Key}`, - 'Content-Type': 'application/json' - } - }); - - console.log('PepperAI Response:', response.data); // Log the entire response for debugging - - if (response.data && response.data.imageUrl) { - return response.data.imageUrl; - } else { - throw new Error(`Invalid response structure: ${JSON.stringify(response.data)}`); - } - } catch (error) { - console.error('PepperAI Image Generation Error:', error); - throw error; - } -} - -module.exports = generateImage; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..60c4cb1 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1593 @@ +{ + "name": "supportbot", + "version": "8.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "supportbot", + "version": "8.0.1", + "license": "MIT", + "dependencies": { + "@vitalets/google-translate-api": "^8.0.0", + "axios": "^1.7.2", + "chatgpt": "^5.2.5", + "discord.js": "^14.13.0", + "js-yaml": "^4.1.0", + "moment": "^2.29.3", + "openai": "^4.50.0" + } + }, + "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/@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/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/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-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/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/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" + } + } + } +}