From ac0daf5fe7714470e2e5b60fb91c0ae45f59b9f8 Mon Sep 17 00:00:00 2001 From: 1tzEmerald Date: Thu, 10 Oct 2024 19:14:45 +0100 Subject: [PATCH] 8.2 --- CODE_OF_CONDUCT.md | 128 ----------------- CONTRIBUTING.md | 92 ------------- Commands/addUser.js | 30 +++- Commands/close.js | 66 +++------ Commands/forceaddUser.js | 87 +++++++----- Commands/profile.js | 7 +- Commands/removeUser.js | 31 +++-- Commands/settings.js | 235 +++++++++++++++++++++++++++++++ Commands/ticket.js | 217 +++++++++++++++-------------- Commands/ticketpanel.js | 9 +- Commands/translate.js | 2 +- Configs/commands.yml | 12 ++ Configs/messages.yml | 17 ++- Configs/supportbot-ai.yml | 8 +- Configs/supportbot.yml | 79 ++++++----- Configs/ticket-panel.yml | 3 - Data/SupportBot-AI.json | 4 +- Data/TicketData.json | 2 +- Data/settings.json | 1 + Data/ticket-panel-id.json | 4 +- Events/guildMemberAdd.js | 39 +++--- Events/guildMemberRemove.js | 32 ++--- Events/interactionCreate.js | 268 +++++++++++++++++++++++++++++++++--- Events/ready.js | 6 +- Structures/Addon.js | 8 +- Structures/Client.js | 6 +- package-lock.json | 80 ++++++++++- package.json | 6 +- 28 files changed, 929 insertions(+), 550 deletions(-) delete mode 100644 CODE_OF_CONDUCT.md delete mode 100644 CONTRIBUTING.md create mode 100644 Commands/settings.js create mode 100644 Data/settings.json diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index f44a661..0000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,128 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, religion, or sexual identity -and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the - overall community - -Examples of unacceptable behavior include: - -* The use of sexualized language or imagery, and sexual attention or - advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email - address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -our discord. -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series -of actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or -permanent ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within -the community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.0, available at -https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. - -Community Impact Guidelines were inspired by [Mozilla's code of conduct -enforcement ladder](https://github.com/mozilla/diversity). - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see the FAQ at -https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 9f1ab7b..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,92 +0,0 @@ -# Contributing - -When contributing to this repository, please first discuss the change you wish to make via issue, -email, or any other method with the owners of this repository before making a change. - -Please note we have a code of conduct, please follow it in all your interactions with the project. - -## Pull Request Process - -1. Ensure any install or build dependencies are removed before the end of the layer when doing a - build. -2. Update the README.md with details of changes to the interface, this includes new environment - variables, exposed ports, useful file locations and container parameters. -3. Increase the version numbers in any examples files and the README.md to the new version that this - Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). -4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you - do not have permission to do that, you may request the second reviewer to merge it for you. - -## Code of Conduct - -### Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of experience, -nationality, personal appearance, race, religion, or sexual identity and -orientation. - -### Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or -advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -### Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -### Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -### Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at [INSERT EMAIL ADDRESS]. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -### Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] - -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ diff --git a/Commands/addUser.js b/Commands/addUser.js index 179c410..130f440 100644 --- a/Commands/addUser.js +++ b/Commands/addUser.js @@ -6,6 +6,7 @@ const { ActionRowBuilder, ButtonBuilder, ButtonStyle, + ChannelType, } = require("discord.js"); const yaml = require("js-yaml"); @@ -34,8 +35,10 @@ module.exports = new Command({ const ticketChannel = interaction.channel; const ticketDataPath = "./Data/TicketData.json"; - // Check if the channel is a ticket thread - if (!ticketChannel.isThread()) { + if ( + (supportbot.Ticket.TicketType === "threads" && !ticketChannel.isThread()) || + (supportbot.Ticket.TicketType === "channels" && ticketChannel.type !== ChannelType.GuildText) + ) { const onlyInTicket = new EmbedBuilder() .setTitle(msgconfig.AddUser.NotInTicket_Title) .setDescription(msgconfig.AddUser.NotInTicket_Description) @@ -91,7 +94,16 @@ module.exports = new Command({ collector.on("collect", async (i) => { if (i.customId === "accept_invite") { try { - await ticketChannel.members.add(userToAdd.id); + // Add user to thread or channel based on TicketType + if (supportbot.Ticket.TicketType === "threads") { + await ticketChannel.members.add(userToAdd.id); // For threads + } else if (supportbot.Ticket.TicketType === "channels") { + await ticketChannel.permissionOverwrites.create(userToAdd.id, { + ViewChannel: true, + SendMessages: true, + ReadMessageHistory: true, + }); // For channels + } const ticketData = JSON.parse(fs.readFileSync(ticketDataPath, "utf8")); const ticketIndex = ticketData.tickets.findIndex((t) => t.id === ticketChannel.id); @@ -110,6 +122,18 @@ module.exports = new Command({ components: [], }); + // Send the added user ticket message in the ticket channel + + const addeduserTicket = new EmbedBuilder() + .setTitle(msgconfig.AddUser.Added_Title) + .setDescription(msgconfig.AddUser.Added_Description.replace('%username%', userToAdd.username)) + .setColor(supportbot.Embed.Colours.General); + + await ticketChannel.send({ + embeds: [addeduserTicket], + }); + + const addedToTicketEmbed = new EmbedBuilder() .setTitle(msgconfig.AddUser.AddedToTicket_Title) .setDescription(msgconfig.AddUser.AddedToTicket_Description.replace( diff --git a/Commands/close.js b/Commands/close.js index 328d190..1efa899 100644 --- a/Commands/close.js +++ b/Commands/close.js @@ -2,15 +2,9 @@ const fs = require("fs"); const Discord = require("discord.js"); const yaml = require("js-yaml"); -const supportbot = yaml.load( - fs.readFileSync("./Configs/supportbot.yml", "utf8") -); -const cmdconfig = yaml.load( - fs.readFileSync("./Configs/commands.yml", "utf8") -); -const msgconfig = yaml.load( - fs.readFileSync("./Configs/messages.yml", "utf8") -); +const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8")); +const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8")); +const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8")); const Command = require("../Structures/Command.js"); @@ -35,28 +29,25 @@ module.exports = new Command({ let Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild); if (!SupportStaff || !Admin) - return interaction.reply( - "Some roles seem to be missing!\nPlease check for errors when starting the bot." - ); + return interaction.reply("Some roles seem to be missing!\nPlease check for errors when starting the bot."); const NoPerms = new Discord.EmbedBuilder() .setTitle("Invalid Permissions!") - .setDescription( - `${msgconfig.Error.IncorrectPerms}\n\nRole Required: \`${supportbot.Roles.StaffMember.Staff}\` or \`${supportbot.Roles.StaffMember.Admin}\`` - ) + .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) - ) + if (!interaction.member.roles.cache.has(SupportStaff.id) && !interaction.member.roles.cache.has(Admin.id)) return interaction.reply({ embeds: [NoPerms] }); } - if (interaction.channel.type !== Discord.ChannelType.PrivateThread) { + // Check if it's a thread or channel based on config + if ( + (supportbot.Ticket.TicketType === "threads" && interaction.channel.type !== Discord.ChannelType.PrivateThread) || + (supportbot.Ticket.TicketType === "channels" && interaction.channel.type !== Discord.ChannelType.GuildText) + ) { const NotTicketChannel = new Discord.EmbedBuilder() .setTitle("Invalid Channel!") - .setDescription("This command can only be used in a ticket thread.") + .setDescription(`This command can only be used in a ${supportbot.Ticket.TicketType === "threads" ? "ticket thread" : "ticket channel"}.`) .setColor(supportbot.Embed.Colours.Warn); return interaction.reply({ embeds: [NotTicketChannel], ephemeral: true }); @@ -88,9 +79,7 @@ module.exports = new Command({ const CloseTicketRequest = new Discord.EmbedBuilder() .setTitle(`**${supportbot.Ticket.Close.Title}**`) - .setDescription( - `${msgconfig.Ticket.ConfirmClose}` - ) + .setDescription(`${msgconfig.Ticket.ConfirmClose}`) .setColor(supportbot.Embed.Colours.General); await interaction.followUp({ @@ -117,10 +106,9 @@ async function handleCloseTicket(interaction, reason, ticket, TicketData) { let tickets = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8")); let tUser = interaction.client.users.cache.get(ticket.user); - let transcriptChannel = await getChannel(supportbot.Ticket.Log.TranscriptLog, interaction.guild); - let logChannel = await getChannel(supportbot.Ticket.Log.TicketLog, interaction.guild); + let transcriptChannel = await getChannel(supportbot.Ticket.Log.TicketDataLog, interaction.guild); - if (!transcriptChannel || !logChannel) + if (!transcriptChannel) return interaction.followUp("Some Channels seem to be missing!"); try { @@ -134,22 +122,8 @@ async function handleCloseTicket(interaction, reason, ticket, TicketData) { ); const transcriptEmbed = new Discord.EmbedBuilder() - .setTitle(supportbot.Ticket.Log.TranscriptTitle) - .setColor(supportbot.Embed.Colours.General) - .setFooter({ - text: supportbot.Embed.Footer, - iconURL: interaction.user.displayAvatarURL(), - }) - .addFields( - { name: "Ticket", value: `${interaction.channel.name} (${interaction.channel.id})` }, - { name: "User", value: `${tUser?.username || "N/A"}#${tUser?.discriminator || "N/A"} (${tUser?.id || ticket.user})` }, - { name: "Closed By", value: interaction.user.tag }, - { name: "Reason", value: reason } - ); - - const logEmbed = new Discord.EmbedBuilder() - .setTitle(supportbot.Ticket.Log.TicketLog_Title) - .setColor(supportbot.Embed.Colours.General) + .setTitle(msgconfig.TicketLog.Title) + .setColor(msgconfig.TicketLog.Colour) .setFooter({ text: supportbot.Embed.Footer, iconURL: interaction.user.displayAvatarURL(), @@ -263,12 +237,6 @@ async function handleCloseTicket(interaction, reason, ticket, TicketData) { `; - if (!supportbot.Ticket.Log.DisableTicketLogChannel) { - await logChannel.send({ embeds: [logEmbed] }).catch((err) => { - console.error(err); - }); - } - let file = new Discord.AttachmentBuilder( Buffer.from(html), { name: fileName } diff --git a/Commands/forceaddUser.js b/Commands/forceaddUser.js index 43908a4..fc02f8d 100644 --- a/Commands/forceaddUser.js +++ b/Commands/forceaddUser.js @@ -1,9 +1,9 @@ -// File: Commands/ForceAddUser.js const fs = require("fs"); const { EmbedBuilder, ApplicationCommandOptionType, ApplicationCommandType, + ChannelType, } = require("discord.js"); const yaml = require("js-yaml"); @@ -20,7 +20,7 @@ module.exports = new Command({ options: [ { name: "user", - description: "The user to add", + description: "The user to forcefully add", type: ApplicationCommandOptionType.User, required: true, }, @@ -28,34 +28,14 @@ module.exports = new Command({ 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()) { + if ( + (supportbot.Ticket.TicketType === "threads" && !ticketChannel.isThread()) || + (supportbot.Ticket.TicketType === "channels" && ticketChannel.type !== ChannelType.GuildText) + ) { const onlyInTicket = new EmbedBuilder() .setTitle(msgconfig.ForceAddUser.NotInTicket_Title) .setDescription(msgconfig.ForceAddUser.NotInTicket_Description) @@ -68,7 +48,42 @@ module.exports = new Command({ } try { - await ticketChannel.members.add(userToAdd.id); + + if (supportbot.Ticket.TicketType === "threads") { + try { + // Explicitly ensure the bot is in the thread + const botMember = await ticketChannel.members.fetch(interaction.client.user.id).catch(() => null); + if (!botMember) { + await ticketChannel.join(); + } + + // Delay to ensure API has processed the bot's membership + await new Promise(res => setTimeout(res, 1000)); + + // Attempt to add the user to the thread + await ticketChannel.members.add(userToAdd.id); + + console.log("User successfully added to the thread."); + } catch (err) { + console.error("Error adding user to the thread:", err.message); + const errorEmbed = new EmbedBuilder() + .setTitle(msgconfig.ForceAddUser.Error_Title) + .setDescription(`An error occurred: ${err.message}`) + .setColor(supportbot.Embed.Colours.Error); + + return interaction.reply({ + embeds: [errorEmbed], + ephemeral: true, + }); + } + + } else if (supportbot.Ticket.TicketType === "channels") { + await ticketChannel.permissionOverwrites.create(userToAdd.id, { + ViewChannel: true, + SendMessages: true, + ReadMessageHistory: true, + }); + } const ticketData = JSON.parse(fs.readFileSync(ticketDataPath, "utf8")); const ticketIndex = ticketData.tickets.findIndex((t) => t.id === ticketChannel.id); @@ -79,10 +94,9 @@ module.exports = new Command({ const addedEmbed = new EmbedBuilder() .setTitle(msgconfig.ForceAddUser.Added_Title) - .setDescription(msgconfig.ForceAddUser.Added_Description.replace( - "%username%", - userToAdd.username - )) + .setDescription( + msgconfig.ForceAddUser.Added_Description.replace("%username%", userToAdd.username) + ) .setColor(supportbot.Embed.Colours.Success); await interaction.reply({ @@ -92,16 +106,17 @@ module.exports = new Command({ 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})` - )) + .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); diff --git a/Commands/profile.js b/Commands/profile.js index f54ec86..e732e55 100644 --- a/Commands/profile.js +++ b/Commands/profile.js @@ -10,9 +10,9 @@ const Command = require("../Structures/Command.js"); const clockedInUsers = new Set(); -async function getRole(roleName, guild) { - return guild.roles.cache.find(role => role.name === roleName); -} +// 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 => { @@ -47,6 +47,7 @@ module.exports = new Command({ const interactionUserId = interaction.user.id; const user = interaction.guild.members.cache.get(viewingUserId); + const { getRole, getChannel } = interaction.client; const Staff = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild); const Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild); diff --git a/Commands/removeUser.js b/Commands/removeUser.js index c2fe64c..f5b6512 100644 --- a/Commands/removeUser.js +++ b/Commands/removeUser.js @@ -1,10 +1,9 @@ -// File path: Commands/RemoveUser.js - const fs = require("fs"); const { EmbedBuilder, ApplicationCommandOptionType, ApplicationCommandType, + ChannelType, ActionRowBuilder, ButtonBuilder, ButtonStyle, @@ -36,13 +35,13 @@ module.exports = new Command({ 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) - + if (!SupportStaff || !Admin) { return interaction.reply( "Some roles seem to be missing!\nPlease check for errors when starting the bot." ); + } - const NoPerms = new EmbedBuilder() + const NoPerms = new EmbedBuilder() .setTitle("Invalid Permissions!") .setDescription( `${msgconfig.Error.IncorrectPerms}\n\nRole Required: \`${supportbot.Roles.StaffMember.Staff}\` or \`${supportbot.Roles.StaffMember.Admin}\`` @@ -50,17 +49,21 @@ module.exports = new Command({ .setColor(supportbot.Embed.Colours.Warn); if ( - !interaction.member.roles.cache.has(SupportStaff.id) && - !interaction.member.roles.cache.has(Admin.id) - ) + !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()) { + // Check if the channel is a ticket thread or text channel based on TicketType + if ( + (supportbot.Ticket.TicketType === "threads" && !ticketChannel.isThread()) || + (supportbot.Ticket.TicketType === "channels" && ticketChannel.type !== ChannelType.GuildText) + ) { const onlyInTicket = new EmbedBuilder() .setTitle(msgconfig.RemoveUser.NotInTicket_Title) .setDescription(msgconfig.RemoveUser.NotInTicket_Description) @@ -73,8 +76,14 @@ module.exports = new Command({ } try { - await ticketChannel.members.remove(userToRemove.id); + // Remove user from thread or channel based on TicketType + if (supportbot.Ticket.TicketType === "threads") { + await ticketChannel.members.remove(userToRemove.id); // For threads + } else if (supportbot.Ticket.TicketType === "channels") { + await ticketChannel.permissionOverwrites.delete(userToRemove.id); // For channels + } + // Update the ticket data const ticketData = JSON.parse(fs.readFileSync(ticketDataPath, "utf8")); const ticketIndex = ticketData.tickets.findIndex((t) => t.id === ticketChannel.id); if (ticketIndex !== -1) { diff --git a/Commands/settings.js b/Commands/settings.js new file mode 100644 index 0000000..255ee1c --- /dev/null +++ b/Commands/settings.js @@ -0,0 +1,235 @@ +const { Command } = require('../Structures/Addon.js'); +const Discord = require("discord.js"); +const fs = require("fs"); +const yaml = require("js-yaml"); +const path = require("path"); + +const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8")); +const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8")); +const settingsFilePath = path.resolve(__dirname, '../Data/settings.json'); + +function loadSettings() { + try { + if (fs.existsSync(settingsFilePath)) { + const data = fs.readFileSync(settingsFilePath, 'utf8'); + return JSON.parse(data); + } else { + return {}; + } + } catch (error) { + console.error("Error reading settings file:", error); + return {}; + } +} + +function saveSettings(settings) { + try { + fs.writeFileSync(settingsFilePath, JSON.stringify(settings, null, 2), 'utf8'); + } catch (error) { + console.error("Error writing settings file:", error); + } +} + +module.exports = new Command({ + name: cmdconfig.Settings.Command, + description: cmdconfig.Settings.Description, + permissions: cmdconfig.Settings.Permission, + options: [ + { + name: 'setstatus', + type: Discord.ApplicationCommandOptionType.Subcommand, + description: 'Set the bot status', + options: [ + { + name: 'status', + type: Discord.ApplicationCommandOptionType.String, + description: 'Choose the bot status', + required: true, + choices: [ + { name: 'Online ๐ŸŸข', value: 'online' }, + { name: 'Idle ๐ŸŒ™', value: 'idle' }, + { name: 'Do Not Disturb โ›”', value: 'dnd' }, + { name: 'Invisible ๐Ÿ‘ป', value: 'invisible' } + ], + }, + ], + }, + { + name: 'setactivity', + type: Discord.ApplicationCommandOptionType.Subcommand, + description: 'Set the bot activity', + options: [ + { + name: 'type', + type: Discord.ApplicationCommandOptionType.String, + description: 'Choose the activity type', + required: true, + choices: [ + { name: 'Playing ๐ŸŽฎ', value: 'PLAYING' }, + { name: 'Watching ๐Ÿ‘€', value: 'WATCHING' }, + { name: 'Listening ๐ŸŽง', value: 'LISTENING' }, + { name: 'Competing ๐Ÿ†', value: 'COMPETING' } + ], + }, + { + name: 'message', + type: Discord.ApplicationCommandOptionType.String, + description: 'The activity message', + required: true, + }, + ], + }, + { + name: 'streammode', + type: Discord.ApplicationCommandOptionType.Subcommand, + description: 'Set the bot streaming status', + options: [ + { + name: 'toggle', + type: Discord.ApplicationCommandOptionType.String, + description: 'Turn streaming mode on or off', + required: true, + choices: [ + { name: 'On', value: 'on' }, + { name: 'Off', value: 'off' } + ], + }, + { + name: 'twitchurl', + type: Discord.ApplicationCommandOptionType.String, + description: 'Twitch stream URL (Required if turning on)', + required: false, + }, + ], + }, + { + name: 'setnickname', + type: Discord.ApplicationCommandOptionType.Subcommand, + description: 'Set the bot nickname', + options: [ + { + name: 'nickname', + type: Discord.ApplicationCommandOptionType.String, + description: 'The new nickname for the bot', + required: true, + }, + ], + }, + ], + + async run(interaction) { + const subcommand = interaction.options.getSubcommand(); + const settings = loadSettings(); + + if (subcommand === 'setstatus') { + const status = interaction.options.getString('status'); + + try { + interaction.client.user.setPresence({ + status: status, + activities: interaction.client.user.presence.activities || [], + }); + + settings.status = status; + saveSettings(settings); + + const embed = new Discord.EmbedBuilder() + .setDescription(`Status set to **${status.charAt(0).toUpperCase() + status.slice(1)}**`) + .setColor(supportbot.Embed.Colours.General); + + return interaction.reply({ embeds: [embed], ephemeral: true }); + } catch (error) { + return interaction.reply({ content: `Failed to set status: ${error.message}`, ephemeral: true }); + } + } + + if (subcommand === 'setactivity') { + const activityType = interaction.options.getString('type'); + const activityMessage = interaction.options.getString('message'); + + try { + interaction.client.user.setPresence({ + activities: [{ name: activityMessage, type: Discord.ActivityType[activityType] }], + status: interaction.client.user.presence.status || 'online', + }); + + settings.activity = { type: activityType, message: activityMessage }; + saveSettings(settings); + + const embed = new Discord.EmbedBuilder() + .setDescription(`Activity set to **${activityType.toLowerCase()} ${activityMessage}**`) + .setColor(supportbot.Embed.Colours.General); + + return interaction.reply({ embeds: [embed], ephemeral: true }); + } catch (error) { + return interaction.reply({ content: `Failed to set activity: ${error.message}`, ephemeral: true }); + } + } else if (subcommand === 'streammode') { + const toggle = interaction.options.getString('toggle'); + const twitchUrl = interaction.options.getString('twitchurl'); + + if (toggle === 'on') { + if (!twitchUrl) { + return interaction.reply({ content: 'Please provide a Twitch URL to stream.', ephemeral: true }); + } + + try { + interaction.client.user.setPresence({ + activities: [{ name: 'Streaming', type: Discord.ActivityType.Streaming, url: twitchUrl }], + status: 'online', + }); + + settings.streaming = { active: true, url: twitchUrl }; + saveSettings(settings); + + const embed = new Discord.EmbedBuilder() + .setDescription(`**Streaming mode enabled!**\nStreaming: [Twitch](${twitchUrl})`) + .setColor(supportbot.Embed.Colours.General); + + return interaction.reply({ embeds: [embed], ephemeral: true }); + } catch (error) { + return interaction.reply({ content: `Failed to enable streaming mode: ${error.message}`, ephemeral: true }); + } + } else if (toggle === 'off') { + try { + interaction.client.user.setPresence({ + activities: interaction.client.user.presence.activities.filter(a => a.type !== Discord.ActivityType.Streaming), + status: settings.status || 'online', + }); + + settings.streaming = { active: false }; + saveSettings(settings); + + const embed = new Discord.EmbedBuilder() + .setDescription(`**Streaming mode disabled!**`) + .setColor(supportbot.Embed.Colours.General); + + return interaction.reply({ embeds: [embed], ephemeral: true }); + } catch (error) { + return interaction.reply({ content: `Failed to disable streaming mode: ${error.message}`, ephemeral: true }); + } + } + } else if (subcommand === 'setnickname') { + const nickname = interaction.options.getString('nickname'); + + try { + const guild = interaction.guild; + const botMember = guild.members.cache.get(interaction.client.user.id); + await botMember.setNickname(nickname); + + settings.nickname = nickname; + saveSettings(settings); + + const embed = new Discord.EmbedBuilder() + .setDescription(`Bot nickname set to **${nickname}**`) + .setColor(supportbot.Embed.Colours.General); + + return interaction.reply({ embeds: [embed], ephemeral: true }); + } catch (error) { + return interaction.reply({ content: `Failed to set nickname: ${error.message}`, ephemeral: true }); + } + } else { + return interaction.reply({ content: 'Invalid command usage.', ephemeral: true }); + } + }, +}); \ No newline at end of file diff --git a/Commands/ticket.js b/Commands/ticket.js index 04fe798..f89261d 100644 --- a/Commands/ticket.js +++ b/Commands/ticket.js @@ -38,19 +38,16 @@ async function getClockedInUsers(guild) { } 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') + .setTitle(msgconfig.Ticket.ClaimTickets.ClaimTitle) + .setDescription(msgconfig.Ticket.ClaimTickets.ClaimMessage) .setColor(supportbot.Embed.Colours.General); const claimButton = new ButtonBuilder() @@ -70,17 +67,15 @@ async function assignTicketToUser(ticketChannel, Staff, Admin, interaction, atte 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}>`, + content: `Ticket is still unclaimed. \nCC: [<@&${Staff.id}> <@&${Admin.id}]`, embeds: [new EmbedBuilder().setDescription(`Ticket is still unclaimed: <#${ticketChannel.id}>`).setColor(supportbot.Embed.Colours.Warn)], }); } @@ -96,11 +91,10 @@ async function assignTicketToUser(ticketChannel, Staff, Admin, interaction, atte 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') + .setTitle(msgconfig.Ticket.ClaimTickets.ClaimTitle) + .setDescription(msgconfig.Ticket.ClaimTickets.ClaimMessage.replace('%user%', interaction.user.id)) .setColor(supportbot.Embed.Colours.General); const claimButton = new ButtonBuilder() @@ -126,13 +120,11 @@ async function assignTicketToUser(ticketChannel, Staff, Admin, interaction, atte 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}>` }); + await ticketChannel.send({ content: `<@${assignedUserId}>` }).then(msg => setTimeout(() => msg.delete(), 5000)); }); collector.on('end', async collected => { if (collected.size === 0) { - console.log("No one claimed the ticket, attempting reassignment"); await claimMessage.delete(); if (attempts >= 1) { @@ -141,7 +133,6 @@ async function assignTicketToUser(ticketChannel, Staff, Admin, interaction, atte 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) @@ -156,7 +147,6 @@ async function assignTicketToUser(ticketChannel, Staff, Admin, interaction, atte const row = new ActionRowBuilder().addComponents(claimButton); const claimMessage = await claimChannel.send({ - content: `<@&${Staff.id}> <@&${Admin.id}>`, embeds: [claimEmbed], components: [row], }); @@ -281,31 +271,87 @@ module.exports = new Command({ ephemeral: true, }); - const TicketCategory = interaction.guild.channels.cache.find( - (c) => c.name === supportbot.Ticket.TicketCategory || c.id === supportbot.Ticket.TicketCategory - ); + let ticketChannel; - 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.TicketType === "threads") { + const TicketHome = interaction.guild.channels.cache.find( + (c) => c.name === supportbot.Ticket.TicketHome || c.id === supportbot.Ticket.TicketHome + ); + + const channel = await getChannel(supportbot.Ticket.TicketHome, interaction.guild); + ticketChannel = await channel.threads.create({ + name: `${supportbot.Ticket.Channel}${ticketNumberID}`, + type: ChannelType.PrivateThread, + parent: TicketHome, + autoArchiveDuration: 60, + reason: 'support ticket', + }); + } else if (supportbot.Ticket.TicketType === "channels") { + const category = interaction.guild.channels.cache.find( + (c) => c.name === supportbot.Ticket.TicketChannelsCategory || c.id === supportbot.Ticket.TicketChannelsCategory + ); + + if (!category) { + return interaction.reply({ + content: "The ticket category does not exist!", + ephemeral: true, + }); + } + + ticketChannel = await interaction.guild.channels.create({ + name: `${supportbot.Ticket.Channel}${ticketNumberID}`, + type: ChannelType.GuildText, + parent: category.id, + reason: 'support ticket', + }); + + // Move to secondary category if the primary one is full + if (category.children.size >= 50) { + const secondaryCategory = interaction.guild.channels.cache.find( + (c) => c.name === supportbot.Ticket.TicketChannelsCategory2 || c.id === supportbot.Ticket.TicketChannelsCategory2 + ); + + if (secondaryCategory) { + await ticketChannel.setParent(secondaryCategory.id); + } else { + return interaction.reply({ + content: "The secondary ticket category does not exist!", + ephemeral: true, + }); + } + } + } // 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}>`); } } - // Add user to the ticket channel - await ticketChannel.members.add(interaction.user.id); + if (supportbot.Ticket.TicketType === "threads") { + await ticketChannel.members.add(interaction.user.id); + } else if (supportbot.Ticket.TicketType === "channels") { + + await ticketChannel.permissionOverwrites.create(interaction.user.id, { + ViewChannel: true, + SendMessages: true, + ReadMessageHistory: true, + }); + + await ticketChannel.permissionOverwrites.create(Admin, { + ViewChannel: true, + SendMessages: true, + ReadMessageHistory: true, + }); + + await ticketChannel.permissionOverwrites.create(interaction.guild.roles.everyone, { + ViewChannel: false, + }); + } + // Save ticket data TicketData.tickets.push({ id: ticketChannel.id, @@ -368,92 +414,55 @@ module.exports = new Command({ // Create the control panel const SelectMenus = new StringSelectMenuBuilder() - .setCustomId("ticketcontrolpanel") - .setPlaceholder("Ticket Control Panel") - .addOptions( - new StringSelectMenuOptionBuilder() - .setLabel("Close") - .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"), + .setCustomId("ticketcontrolpanel") + .setPlaceholder("Ticket Control Panel") + .addOptions( + new StringSelectMenuOptionBuilder() + .setLabel("Close") + .setDescription("Close the ticket.") + .setEmoji(supportbot.SelectMenus.Tickets.CloseEmoji) + .setValue("ticketclose"), + new StringSelectMenuOptionBuilder() + .setLabel("Archive") + .setDescription("Archive the ticket.") + .setEmoji(supportbot.SelectMenus.Tickets.ArchiveEmoji) + .setValue("archiveticket"), + ...(supportbot.Ticket.TicketType === "threads" ? [ new StringSelectMenuOptionBuilder() .setLabel("Lock Ticket") .setDescription("Lock the ticket.") .setEmoji(supportbot.SelectMenus.Tickets.LockEmoji) - .setValue("lockticket"), - 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"), - ); + .setValue("lockticket") + ] : []), + new StringSelectMenuOptionBuilder() + .setLabel("Rename Ticket") + .setDescription("Rename the ticket.") + .setEmoji(supportbot.SelectMenus.Tickets.RenameEmoji) + .setValue("renameticket") + ); + + if (supportbot.Ticket.TicketType === "threads") { + SelectMenus.addOptions( + 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], }); - // Handle department selection if not specified - if (!department) { - try { - const buttons = supportbot.Departments.map((x, index) => - new ButtonBuilder() - .setCustomId("Department" + index) - .setLabel(x.title) - .setStyle(x.color) - .setEmoji(x.emoji) - ); - - const row = new ActionRowBuilder().addComponents(buttons); - - 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 in run method:", error); } diff --git a/Commands/ticketpanel.js b/Commands/ticketpanel.js index ff0f0b7..e78f48a 100644 --- a/Commands/ticketpanel.js +++ b/Commands/ticketpanel.js @@ -35,8 +35,13 @@ module.exports = new Command({ }); } - const channelName = panelconfig.Channel; - const channel = interaction.guild.channels.cache.find(ch => ch.name === channelName); + const { getChannel } = interaction.client; + + const channel = await getChannel( + supportbot.Ticket.TicketHome, + interaction.guild + ); + if (!channel) { console.log(`[TICKET PANEL] ${channelName} channel not found. Please check your config file.`); return interaction.reply({ diff --git a/Commands/translate.js b/Commands/translate.js index 084abe4..fe4e4cd 100644 --- a/Commands/translate.js +++ b/Commands/translate.js @@ -71,7 +71,7 @@ module.exports = new Command({ let url = "https://www.science.co.il/language/Codes.php"; // URL to all available language codes let text = await interaction.options.getString("text"); // Grab the text to translate by the user let translatelog = await getChannel( - supportbot.Translate.Log, + supportbot.Translate.TranslateLog, interaction.guild ); // Grab the set logging channel for translations diff --git a/Configs/commands.yml b/Configs/commands.yml index 3ab19d3..cd72e55 100644 --- a/Configs/commands.yml +++ b/Configs/commands.yml @@ -13,6 +13,8 @@ # # Command Configuration +# If you don't want to mess with this configuration, You can manage commands via Discord Settings the Intregations. + Help: Command: "help" Description: "Get a list of all the commands." @@ -97,6 +99,16 @@ Ping: Permission: ["SendMessages"] Enabled: true +# --------------------------------------- +# Bot Administrator Commands +# --------------------------------------- + +Settings: + Command: "settings" + Description: "Update bot settings." + Permission: ["Administrator"] + Enabled: true + # --------------------------------------- # Command Description Configuration # --------------------------------------- diff --git a/Configs/messages.yml b/Configs/messages.yml index 27f6804..5da7eb2 100644 --- a/Configs/messages.yml +++ b/Configs/messages.yml @@ -17,7 +17,7 @@ Info: Title: "About Us" Description: "SupportBot is an open-sourced support system for discord. It's completely free to use with high amounts of customization to match your own brand." Button: "Visit Site" - URL: "https://emeraldsrv.com" + URL: "https://docs.emeraldsrv.com" Colour: "#f74343" Welcome: @@ -77,14 +77,11 @@ 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." + RenamedSuccess: "๐Ÿท๏ธ Ticket renamed successfully!" + ClaimTickets: ClaimTitle: "Incoming Support Request!" ClaimMessage: "A new ticket has been created by <@!%user%>. Click the button below to claim this ticket." @@ -105,6 +102,10 @@ TicketStats: Title: "Your Ticket Stats" +TicketLog: + Title: "Ticket Log" + Colour: "#0cc559" + AddUser: NotInTicket_Title: "Ticket Invitation" NotInTicket_Description: "This command can only be used within a ticket thread." @@ -123,6 +124,10 @@ AddUser: 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%." + + # Added Message for inside the ticket, this informs everyone inside the ticket that the user has been added. + Added_Title: "User Added" + Added_Description: "Successfully added **%username%** to the ticket." Declined_Title: "Invitation Declined" Declined_Message: "You have declined the invitation." diff --git a/Configs/supportbot-ai.yml b/Configs/supportbot-ai.yml index e68c4ad..43a298c 100644 --- a/Configs/supportbot-ai.yml +++ b/Configs/supportbot-ai.yml @@ -10,22 +10,22 @@ Enabled: true General: Name: "SupportBot AI" # What would you like to call your AI - Model: "gpt-4o" + Model: "gpt-4o-mini" # You can see a list of OpenAI Models here: https://platform.openai.com/docs/models Tokens: 400 # Max tokens you can set is 400 # For more information about tokens, go to https://openai.com/api/pricing/ - OpenAI_Key: "OPEN_AI_KEY_HERE" + OpenAI_Key: "OPEN_AI_API_KEY" # To create an OpenAI API key, go to https://platform.openai.com/api-keys - PastebinAPI_Key: "PASTEBIN_API_KEY_HERE" + PastebinAPI_Key: "PASTEBIN_API_KEY" # You can get your Pastebin API Key from https://pastebin.com PastebinAPI_URL: "https://pastebin.com/api/api_post.php" Channels: - AIChannel: "1250190338954498048" + AIChannel: "AI_BOT_CHANNEL" # 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 539fe9c..d781aa2 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.1" +SupportBot_Version: "v8.2" # Do not change the version number # ------------------------- @@ -37,6 +37,8 @@ Activity: StreamingURL: "https://www.twitch.tv/YOUR_TWITCH_NAME" # Set The Type to "Streaming" for a streaming status! + # You can update the activity via Discord using: /settings + # ------------------------- # Embeds # ------------------------- @@ -57,15 +59,15 @@ Embed: Roles: AutoRole: Enabled: true - Role: "Member" + Role: "ROLE_ID" StaffMember: - Staff: "Staff" - Admin: "Admin" + Staff: "STAFF_ROLE_ID" + Admin: "ADMIN_ROLE_ID" ModRoles: - Muted: "Muted" - Blacklisted: "Blacklisted" + Muted: "MUTED_ROLE_ID" + Blacklisted: "BLACKLISTED_ROLE_ID" # ---------------------------- # System Messages @@ -73,18 +75,18 @@ Roles: Welcome: Enabled: true - Channel: "๐Ÿšจโ”ƒjoin-leave-logs" + Channel: "WELCOME_CHANNEL_ID" Leave: Enabled: true - Channel: "๐Ÿšจโ”ƒjoin-leave-logs" + Channel: "LEAVE_CHANNEL_ID" # ---------------------------- # Suggestions # ---------------------------- Suggestions: - Channel: "๐Ÿ“—โ”ƒsuggestions" + Channel: "SUGGEST_CHANNEL_ID" UpvoteEmoji: "โœ…" DownvoteEmoji: "โŒ" @@ -119,8 +121,23 @@ Suggestions: # ------------------------- Ticket: - TicketCategory: "๐Ÿ“—โ”ƒtickets" #Where tickets normally go with the /ticket command - # This is the channel name for the ticket threads to be created, NOT A DISCORD CATEGORY! + TicketType: "channels" # Choose how you want tickets to be created. + # Options: + # channels - Tickets will be created as channels in the + # threads - Tickets will be created as threads through the "TicketHome" + + # For TicketType "Threads" + TicketHome: "TICKETHOME_CHANNEL_ID" + # What is TicketHome? + # - Channel where the Ticket creation panel is sent. + # - Channel where ticket threads are made. (only if ticket threads is enabled above.) + + # For TicketType "Channels" + TicketChannelsCategory: "TICKET_CATEGORY_ID" # if Type is set to channels + TicketChannelsCategory2: "BACKUP_TICKET_CATEGORY_ID" # This will be used if the first category has reached the max channel limit. + TicketArchiveCategory: "ARCHIVE_TICKET_CATEGORY_ID" + + # --- Channel: "ticket-" # The prefix for ticket channel names TicketsPerUser: 100 @@ -134,19 +151,17 @@ Ticket: # Set to true to allow all staff to access the ticket Log: - TicketLog: "๐Ÿšจโ”ƒticket-data" - TicketLog_Title: "Ticket Closed" - DisableTicketLogChannel: false - - TranscriptLog: "๐Ÿšจโ”ƒticket-data" - TranscriptTitle: "Ticket Transcript" + TicketDataLog: "TICKET_LOG_ID" + TickeDataTitle: "Ticket Data" Close: 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 @@ -158,16 +173,18 @@ Ticket: Invites: StaffOnly: true - LockedTickets: - Channel: "1267856939275124809" - ClaimTickets: Enabled: true - Channel: "1249723473757077624" + Channel: "CLAIM_TICKETS_CHANNEL_ID" # 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. + # 120000 = 2 Minutes + # 180000 = 3 Minutes + # 240000 = 4 Minutes + # 300000 = 5 Minutes + # Time in Mliseconds ButtonTitle: "Claim Ticket" ButtonEmoji: "๐ŸŽซ" @@ -179,18 +196,6 @@ Ticket: # 3 - Green # 4 - Red - -# ------------------------- -# Departments - NOT IN THIS UPDATE -# ------------------------- - -Departments: - - title: "Create Ticket" - role: "Staff" - color: "2" - emoji: "๐ŸŽซ" - category: "TICKETS" - # ------------------------- # Buttons # ------------------------- @@ -207,14 +212,12 @@ Buttons: Unlock_Emoji: "๐Ÿ”“" Unlock_Style: "2" - # Available Button Styles # 1 - Blurple # 2 - Grey # 3 - Green # 4 - Red - # ------------------------- # Select Menus # ------------------------- @@ -222,18 +225,18 @@ Buttons: SelectMenus: Tickets: PanelEmoji: "โš™๏ธ" - + CloseEmoji: "๐Ÿ—‘๏ธ" ArchiveEmoji: "โ™ป๏ธ" LockEmoji: "๐Ÿ”" + RenameEmoji: "๐Ÿท๏ธ" EnableInvitesEmoji: "๐Ÿ“ค" DisableInvitesEmoji: "๐Ÿ“ฅ" - # ------------------------- # Misc # ------------------------- Translate: - Log: "๐Ÿšจโ”ƒlogs" + TranslateLog: "TRANSLATE_LOG_ID" diff --git a/Configs/ticket-panel.yml b/Configs/ticket-panel.yml index e4ed791..28072eb 100644 --- a/Configs/ticket-panel.yml +++ b/Configs/ticket-panel.yml @@ -20,9 +20,6 @@ Panel: true # Ticket Panel Message Configuration #2b2d31 # ---------------------------------------- -Channel: "๐Ÿ“—โ”ƒtickets" -# The channel used to create the ticket panel, Use command /ticketpanel to generate it. - PanelTitle: "SupportBot" PanelColour: "#0cc559" diff --git a/Data/SupportBot-AI.json b/Data/SupportBot-AI.json index 544b7b4..9e26dfe 100644 --- a/Data/SupportBot-AI.json +++ b/Data/SupportBot-AI.json @@ -1,3 +1 @@ -{ - -} \ No newline at end of file +{} \ No newline at end of file diff --git a/Data/TicketData.json b/Data/TicketData.json index 987b7f9..182fdc3 100644 --- a/Data/TicketData.json +++ b/Data/TicketData.json @@ -1,5 +1,5 @@ { "tickets": [ - {} + ] } \ No newline at end of file diff --git a/Data/settings.json b/Data/settings.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/Data/settings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/Data/ticket-panel-id.json b/Data/ticket-panel-id.json index ba9c579..3af9513 100644 --- a/Data/ticket-panel-id.json +++ b/Data/ticket-panel-id.json @@ -1,4 +1,4 @@ { "id": 0, - "TicketPanelID": "000000000000000000" -} \ No newline at end of file + "TicketPanelID": "0000000000000000000" +} diff --git a/Events/guildMemberAdd.js b/Events/guildMemberAdd.js index ce1be9c..f0b97fd 100644 --- a/Events/guildMemberAdd.js +++ b/Events/guildMemberAdd.js @@ -19,7 +19,8 @@ const msgconfig = yaml.load( module.exports = new Event("guildMemberAdd", async (client, member, interaction, guild) => { if (supportbot.Welcome.Enabled) { - const WelcomeChannel = member.guild.channels.cache.find(channel => channel.name === supportbot.Welcome.Channel) + const WelcomeChannel = member.guild.channels.cache.get(supportbot.Welcome.Channel) || + member.guild.channels.cache.find(channel => channel.name === supportbot.Welcome.Channel); const WelcomeEmbed = new Discord.EmbedBuilder() .setColor(msgconfig.Welcome.Embed.Colour) @@ -27,29 +28,29 @@ module.exports = new Event("guildMemberAdd", async (client, member, interaction, .setDescription(msgconfig.Welcome.Embed.Message.replace(/%joined_user%/g, member.user)) .setTimestamp(); - if (msgconfig.Welcome.Embed.Thumbnail === "BOT") { - WelcomeEmbed.setThumbnail(client.user.displayAvatarURL()) - } + if (msgconfig.Welcome.Embed.Thumbnail === "BOT") { + WelcomeEmbed.setThumbnail(client.user.displayAvatarURL()) + } else if (msgconfig.Welcome.Embed.Thumbnail === "USER") { + WelcomeEmbed.setThumbnail(member.user.displayAvatarURL()) + } - if (msgconfig.Welcome.Embed.Thumbnail === "USER") { - WelcomeEmbed.setThumbnail(member.user.displayAvatarURL()) - } + if (msgconfig.Welcome.Embed.ImageEnabled) { + WelcomeEmbed.setImage(msgconfig.Welcome.Embed.ImageURL) + } - if (msgconfig.Welcome.Embed.ImageEnabled) { - WelcomeEmbed.setImage(msgconfig.Welcome.Embed.ImageURL) - } + if (WelcomeChannel) { + WelcomeChannel.send({ embeds: [WelcomeEmbed] }); + } - WelcomeChannel.send({ - embeds: [WelcomeEmbed] - }) - - if (supportbot.Roles.AutoRole.Enabled) { - var role = member.guild.roles.cache.find(role => role.name === supportbot.Roles.AutoRole.Role); + if (supportbot.Roles.AutoRole.Enabled) { + const role = member.guild.roles.cache.get(supportbot.Roles.AutoRole.Role) || + member.guild.roles.cache.find(role => role.name === supportbot.Roles.AutoRole.Role); + if (role) { member.roles.add(role); - } - - console.log(`\u001b[32m`, `[+]`, `\u001b[33m`, `${member.user.username} joined the server!`) + } + } + console.log(`\u001b[32m`, `[+]`, `\u001b[33m`, `${member.user.username} joined the server!`); } }); \ No newline at end of file diff --git a/Events/guildMemberRemove.js b/Events/guildMemberRemove.js index 1ede06b..3004af7 100644 --- a/Events/guildMemberRemove.js +++ b/Events/guildMemberRemove.js @@ -19,32 +19,32 @@ const msgconfig = yaml.load( module.exports = new Event("guildMemberRemove", async (client, member, interaction, guild) => { if (supportbot.Leave.Enabled) { - const LeaveChannel = member.guild.channels.cache.find(channel => channel.name === supportbot.Leave.Channel) + const LeaveChannel = member.guild.channels.cache.get(supportbot.Leave.Channel) || + member.guild.channels.cache.find(channel => channel.name === supportbot.Leave.Channel); const LeaveEmbed = new Discord.EmbedBuilder() .setColor(msgconfig.Leave.Embed.Colour) .setTitle(msgconfig.Leave.Embed.Title) - .setDescription(msgconfig.Leave.Embed.Message.replace(/%joined_user%/g, member.user)) + .setDescription(msgconfig.Leave.Embed.Message.replace(/%left_user%/g, member.user)) .setTimestamp(); - if (msgconfig.Leave.Embed.Thumbnail === "BOT") { - LeaveEmbed.setThumbnail(client.user.displayAvatarURL()) - } + if (msgconfig.Leave.Embed.Thumbnail === "BOT") { + LeaveEmbed.setThumbnail(client.user.displayAvatarURL()); + } else if (msgconfig.Leave.Embed.Thumbnail === "USER") { + LeaveEmbed.setThumbnail(member.user.displayAvatarURL()); + } - if (msgconfig.Leave.Embed.Thumbnail === "USER") { - LeaveEmbed.setThumbnail(member.user.displayAvatarURL()) - } - - if (msgconfig.Leave.Embed.ImageEnabled) { - LeaveEmbed.setImage(msgconfig.Leave.Embed.ImageURL) - } + if (msgconfig.Leave.Embed.ImageEnabled) { + LeaveEmbed.setImage(msgconfig.Leave.Embed.ImageURL); + } + if (LeaveChannel) { LeaveChannel.send({ embeds: [LeaveEmbed] - }) + }); + } - console.log(`\u001b[31m`, `[-]`, `\u001b[33m`, `${member.user.username} left the server!`) + console.log(`\u001b[31m`, `[-]`, `\u001b[33m`, `${member.user.username} left the server!`); } -}); - +}); \ No newline at end of file diff --git a/Events/interactionCreate.js b/Events/interactionCreate.js index f79b9d6..9d4bcf6 100644 --- a/Events/interactionCreate.js +++ b/Events/interactionCreate.js @@ -91,6 +91,46 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { } } + if (interaction.customId === "renameTicketModal") { + const newTicketName = interaction.fields.getTextInputValue("renameTicket"); + + // Fixing the ticketChannelId extraction and lookup + const ticketChannelId = interaction.channel.id; + + let ticketChannel; + if (supportbot.Ticket.TicketType === "threads") { + ticketChannel = interaction.guild.channels.cache.get(ticketChannelId); + } else if (supportbot.Ticket.TicketType === "channels") { + ticketChannel = interaction.guild.channels.cache.get(ticketChannelId); + } + + if (!ticketChannel) { + return interaction.reply({ + content: "Ticket channel not found.", + ephemeral: true, + }); + } + + try { + await ticketChannel.setName(newTicketName); + + const renamedsuccess = new Discord.EmbedBuilder() + .setDescription(msgconfig.Ticket.RenamedSuccess) + .setColor(supportbot.Embed.Colours.Success); + + return interaction.reply({ + embeds: [renamedsuccess], + ephemeral: true, + }); + } catch (error) { + console.error("Error renaming the ticket:", error); + return interaction.reply({ + content: "There was an error renaming the ticket.", + ephemeral: true, + }); + } +} + if (interaction.isStringSelectMenu()) { if (interaction.customId === "ticketcontrolpanel") { const selectedOption = interaction.values[0]; @@ -131,25 +171,112 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { components: [ArchivedOptions], }); - await interaction.channel.setArchived(true); + if (supportbot.Ticket.TicketType === "threads") { + await interaction.channel.setArchived(true); + } + + if (supportbot.Ticket.TicketType === "channels") { + const { getRole, getChannel } = interaction.client; + await interaction.channel.setParent(supportbot.Ticket.TicketArchiveCategory); - break; + const ticketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8")); + const ticketInfo = ticketData.tickets.find(t => t.id === interaction.channel.id); + + if (ticketInfo) { + const user = interaction.guild.members.cache.get(ticketInfo.user) || ticketInfo.user; + const claimedBy = interaction.guild.members.cache.get(ticketInfo.claimedBy) || ticketInfo.claimedBy; + const Admin = await interaction.guild.roles.fetch(supportbot.Roles.StaffMember.Admin); + const Staff = await interaction.guild.roles.fetch(supportbot.Roles.StaffMember.Staff); + + if (user && user.id) { + await interaction.channel.permissionOverwrites.edit(user.id, { + ViewChannel: false, + SendMessages: false, + }); + } + + if (supportbot.ClaimTickets === true) { + if (claimedBy && claimedBy.id) { + await interaction.channel.permissionOverwrites.edit(claimedBy.id, { + ViewChannel: true, + SendMessages: true, + ReadMessageHistory: true, + }); + } + } - 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); + if (supportbot.ClaimTickets === false) { + if (Staff && Staff.id) { + await interaction.channel.permissionOverwrites.edit(claimedBy.id, { + ViewChannel: true, + SendMessages: true, + ReadMessageHistory: true, + }); + } + } + + if (Admin && Admin.id) { + await interaction.channel.permissionOverwrites.edit(Admin.id, { + ViewChannel: true, + SendMessages: true, + ReadMessageHistory: true, + }); + } + + await interaction.channel.permissionOverwrites.edit(interaction.guild.roles.everyone.id, { + ViewChannel: false, + SendMessages: false, + ReadMessageHistory: false, + }); + } - break; + } + break; + + case "lockticket": + const lockembed = new Discord.EmbedBuilder() + .setDescription(msgconfig.Ticket.TicketLocked) + .setColor(supportbot.Embed.Colours.Success); + + const unlockButton = new Discord.ButtonBuilder() + .setCustomId("unlockTicket") + .setLabel(supportbot.Buttons.Tickets.Unlock) + .setEmoji(supportbot.Buttons.Tickets.Unlock_Emoji) + .setStyle(supportbot.Buttons.Tickets.Unlock_Style); + + const LockedOptions = new Discord.ActionRowBuilder() + .addComponents(unlockButton); + + await interaction.channel.send({ + embeds: [lockembed], + components: [LockedOptions], + ephemeral: false, + }); + + + if (supportbot.Ticket.TicketType === "threads") { + await interaction.channel.setLocked(true); + } + + break; + + case "unlockTicket": + const unlockEmbed = new Discord.EmbedBuilder() + .setDescription(msgconfig.Ticket.TicketUnlocked) + .setColor(supportbot.Embed.Colours.Success); + + await interaction.channel.send({ + embeds: [unlockEmbed], + ephemeral: false, + }); + + if (supportbot.Ticket.TicketType === "threads") { + await interaction.channel.setLocked(false); + } + + break; + case "enableinvites": await handleInvites(interaction, true); break; @@ -157,23 +284,101 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { case "disableinvites": await handleInvites(interaction, false); break; + + case "renameticket": + const modal = new Discord.ModalBuilder() + .setCustomId("renameTicketModal") + .setTitle("Rename Channel") + .addComponents( + new Discord.ActionRowBuilder() + .addComponents( + new Discord.TextInputBuilder() + .setCustomId("renameTicket") + .setLabel("New Channel Name") + .setStyle(Discord.TextInputStyle.Short) + .setRequired(true) + ) + ); + await interaction.showModal(modal); + break; + } } } if (interaction.isButton()) { if (interaction.customId === "unarchiveTicket") { - await interaction.channel.setArchived(false); + if (supportbot.Ticket.TicketType === "threads") { + await interaction.channel.setArchived(false); + } + + if (supportbot.Ticket.TicketType === "channels") { + const { getRole, getChannel } = interaction.client; + await interaction.channel.setParent(supportbot.Ticket.TicketChannelsCategory); + + const ticketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8")); + const ticketInfo = ticketData.tickets.find(t => t.id === interaction.channel.id); + + if (ticketInfo) { + const user = interaction.guild.members.cache.get(ticketInfo.user) || ticketInfo.user; + const claimedBy = interaction.guild.members.cache.get(ticketInfo.claimedBy) || ticketInfo.claimedBy; + const Admin = await interaction.guild.roles.fetch(supportbot.Roles.StaffMember.Admin); + const Staff = await interaction.guild.roles.fetch(supportbot.Roles.StaffMember.Staff); + + if (user && user.id) { + await interaction.channel.permissionOverwrites.edit(user.id, { + ViewChannel: true, + SendMessages: true, + ReadMessageHistory: true, + }); + } + + if (supportbot.ClaimTickets === true) { + if (claimedBy && claimedBy.id) { + await interaction.channel.permissionOverwrites.edit(claimedBy.id, { + ViewChannel: true, + SendMessages: true, + ReadMessageHistory: true, + }); + } + } + + if (supportbot.ClaimTickets === false) { + if (Staff && Staff.id) { + await interaction.channel.permissionOverwrites.edit(claimedBy.id, { + ViewChannel: true, + SendMessages: true, + ReadMessageHistory: true, + }); + } + } + + if (Admin && Admin.id) { + await interaction.channel.permissionOverwrites.edit(Admin.id, { + ViewChannel: true, + SendMessages: true, + ReadMessageHistory: true, + }); + } + + await interaction.channel.permissionOverwrites.edit(interaction.guild.roles.everyone.id, { + ViewChannel: false, + SendMessages: false, + ReadMessageHistory: false, + }); + } + } + const unrcembed = new Discord.EmbedBuilder() - .setDescription(msgconfig.Ticket.TicketUnarchived) - .setColor(supportbot.Embed.Colours.Success); - + .setDescription(msgconfig.Ticket.TicketUnarchived) + .setColor(supportbot.Embed.Colours.Success); + await interaction.update({ embeds: [unrcembed], components: [], }); - + } if (interaction.customId === "createticket") { @@ -276,7 +481,27 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { }); } - await ticketChannel.members.add(interaction.user.id); + if (supportbot.Ticket.TicketType === "threads") { + await ticketChannel.members.add(interaction.user.id); + + } else if (supportbot.Ticket.TicketType === "channels") { + await ticketChannel.permissionOverwrites.create(interaction.user.id, { + ViewChannel: true, + SendMessages: true, + ReadMessageHistory: true, + }); + + await ticketChannel.permissionOverwrites.create(Admin, { + ViewChannel: true, + SendMessages: true, + ReadMessageHistory: true, + }); + + await ticketChannel.permissionOverwrites.create(interaction.guild.roles.everyone, { + ViewChannel: false, + }); + } + const pingclaimedstaff = await ticketChannel.send(`<@${interaction.user.id}>`); setTimeout(() => { @@ -371,6 +596,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => { await interaction.update({ embeds: [updatedEmbed] }); } + } async function handleInvites(interaction, enable) { diff --git a/Events/ready.js b/Events/ready.js index 0289a7b..51fe00e 100644 --- a/Events/ready.js +++ b/Events/ready.js @@ -26,7 +26,7 @@ const msgconfig = yaml.load( const Event = require("../Structures/Event.js"); -let chan1 = client.channels.cache.get(panelconfig.Channel); +let chan1 = client.channels.cache.get(supportbot.Ticket.TicketHome); module.exports = new Event("ready", async (client, interaction) => { const { getRole, getChannel, getCategory } = client; @@ -94,15 +94,13 @@ module.exports = new Event("ready", async (client, interaction) => { supportbot.Roles.ModRoles.Muted ]; - supportbot.Departments.forEach((department) => roles.push(department.role)); - if (supportbot.Roles.AutoRole.Role) roles.push(supportbot.Roles.AutoRole.Role); const channels = [ supportbot.Suggestions.Channel, supportbot.Ticket.Log.TicketLog, supportbot.Ticket.Log.TranscriptLog, - panelconfig.Channel, + supportbot.Ticket.TicketHome, supportbot.Welcome.Channel, supportbot.Leave.Channel, supportbot.Translate.Log diff --git a/Structures/Addon.js b/Structures/Addon.js index a64b4ae..5705d49 100644 --- a/Structures/Addon.js +++ b/Structures/Addon.js @@ -1,6 +1,6 @@ + // SupportBot | Emerald Services // Addon Structure - class Command { constructor(options) { this.name = options.name; @@ -10,7 +10,13 @@ class Command { this.run = options.run; this.defaultPermission = true; } + + // Method to dynamically update options + setOptions(options) { + this.options = options; + } } + class Event { constructor(event, run) { this.event = event; diff --git a/Structures/Client.js b/Structures/Client.js index cb27ba5..3786833 100644 --- a/Structures/Client.js +++ b/Structures/Client.js @@ -98,7 +98,11 @@ class Client extends Discord.Client { } if (cmdconfig.Profile.Enabled === false) { - tempCommandFiles = tempCommandFiles.filter(item => item !== "Profile.js"); + tempCommandFiles = tempCommandFiles.filter(item => item !== "profile.js"); + } + + if (cmdconfig.Settings.Enabled === false) { + tempCommandFiles = tempCommandFiles.filter(item => item !== "settings.js"); } const commandFiles = tempCommandFiles; diff --git a/package-lock.json b/package-lock.json index 60c4cb1..142e265 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,13 +9,17 @@ "version": "8.0.1", "license": "MIT", "dependencies": { + "@paypal/checkout-server-sdk": "^1.0.3", "@vitalets/google-translate-api": "^8.0.0", "axios": "^1.7.2", "chatgpt": "^5.2.5", "discord.js": "^14.13.0", + "js": "^0.1.0", "js-yaml": "^4.1.0", "moment": "^2.29.3", - "openai": "^4.50.0" + "ms": "^2.1.3", + "openai": "^4.50.0", + "paypal-rest-sdk": "^1.8.1" } }, "node_modules/@babel/code-frame": { @@ -171,6 +175,25 @@ "url": "https://github.com/discordjs/discord.js?sponsor" } }, + "node_modules/@paypal/checkout-server-sdk": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@paypal/checkout-server-sdk/-/checkout-server-sdk-1.0.3.tgz", + "integrity": "sha512-UEdq8akEdMz0Vs4qoQFU2gMp8PpyE/HKyMwiZuK4vIWUKl8jfd1fWKjQN5cDFm9NkFUIp5U7h++rdMOVj9WMNA==", + "dependencies": { + "@paypal/paypalhttp": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@paypal/paypalhttp": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@paypal/paypalhttp/-/paypalhttp-1.0.1.tgz", + "integrity": "sha512-DC7AHxTT7drF6dUi3YaFdPVuT15sIkpD5H2eHmdtFgxM4UanS1o1ZDfMhR9mpxd8o+X6pz2r+EZVRRq+n1cssQ==", + "engines": { + "node": ">=4" + } + }, "node_modules/@sapphire/async-queue": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.3.tgz", @@ -380,6 +403,14 @@ } ] }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "engines": { + "node": "*" + } + }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -510,6 +541,17 @@ "node": ">= 0.8" } }, + "node_modules/commander": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-1.1.1.tgz", + "integrity": "sha512-71Rod2AhcH3JhkBikVpNd0pA+fWsmAaVoti6OR38T76chA7vE3pSerS0Jor4wDw+tOueD2zLVvFOw5H0Rcj7rA==", + "dependencies": { + "keypress": "0.1.x" + }, + "engines": { + "node": ">= 0.6.x" + } + }, "node_modules/conf": { "version": "11.0.2", "resolved": "https://registry.npmjs.org/conf/-/conf-11.0.2.tgz", @@ -914,6 +956,17 @@ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" }, + "node_modules/js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/js/-/js-0.1.0.tgz", + "integrity": "sha512-ZBbGYOpact8QAH9RprFWL4RAESYwbDodxiuDjOnzwzzk9pBzKycoifGuUrHHcDixE/eLMKPHRaXenTgu1qXBqA==", + "dependencies": { + "commander": "~1.1.1" + }, + "bin": { + "js": "bin/js" + } + }, "node_modules/js-tiktoken": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.12.tgz", @@ -958,6 +1011,11 @@ "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.1.tgz", "integrity": "sha512-XQmWYj2Sm4kn4WeTYvmpKEbyPsL7nBsb647c7pMe6l02/yx2+Jfc4dT6UZkEXnIUb5LhD55r2HPsJ1milQ4rDg==" }, + "node_modules/keypress": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz", + "integrity": "sha512-x0yf9PL/nx9Nw9oLL8ZVErFAk85/lslwEP7Vz7s5SI1ODXZIgit3C5qyWjw4DxOuO/3Hb4866SQh28a1V1d+WA==" + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -1257,6 +1315,26 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, + "node_modules/paypal-rest-sdk": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/paypal-rest-sdk/-/paypal-rest-sdk-1.8.1.tgz", + "integrity": "sha512-Trj2GuPn10GqpICAxQh5wjxuDT7rq7DMOkvyatz05wI5xPGmqXN7UC0WfDSF9WSBs4YdcWZP0g+nY+sOdaFggw==", + "dependencies": { + "buffer-crc32": "^0.2.3", + "semver": "^5.0.3" + }, + "engines": { + "node": ">= v0.6.0" + } + }, + "node_modules/paypal-rest-sdk/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, "node_modules/picocolors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", diff --git a/package.json b/package.json index 4aed27f..f1f08a6 100644 --- a/package.json +++ b/package.json @@ -18,12 +18,16 @@ }, "homepage": "https://github.com/Emerald-Services/SupportBot#readme", "dependencies": { + "@paypal/checkout-server-sdk": "^1.0.3", "@vitalets/google-translate-api": "^8.0.0", "axios": "^1.7.2", "chatgpt": "^5.2.5", "discord.js": "^14.13.0", + "js": "^0.1.0", "js-yaml": "^4.1.0", "moment": "^2.29.3", - "openai": "^4.50.0" + "ms": "^2.1.3", + "openai": "^4.50.0", + "paypal-rest-sdk": "^1.8.1" } }