Compare commits

..
17 Commits
Author SHA1 Message Date
1tzEmerald 753f432c95 Merge pull request #179 from Emerald-Services/1tzemerald-patch-4
Update package.json
2024-10-11 15:04:37 +01:00
1tzEmerald c824bc51b2 Update package.json 2024-10-11 15:03:47 +01:00
1tzEmerald 39ee540b55 Merge pull request #177 from Emerald-Services/dev
8.2
2024-10-11 15:00:05 +01:00
1tzEmerald 903b214051 Merge branch 'release' into dev 2024-10-11 14:59:50 +01:00
1tzEmerald ac0daf5fe7 8.2 2024-10-10 19:14:45 +01:00
ItsNyoty 80e38a07e9 Merge pull request #175 from Emerald-Services/ItsNyoty-patch-1
Update README.md
2024-09-19 13:06:05 +02:00
ItsNyoty d2598e09f7 Update README.md 2024-09-19 13:05:26 +02:00
ItsNyoty 28aab47150 Update README.md 2024-09-19 13:04:44 +02:00
1tzEmerald 81bda67ae4 Merge pull request #172 from Emerald-Services/1tzemerald-patch-3
Update supportbot.yml
2024-07-30 17:58:41 +01:00
1tzEmerald 25bbd1ff85 Update supportbot.yml 2024-07-30 17:58:01 +01:00
1tzEmerald 164d695a5f Merge pull request #171 from Emerald-Services/1tzemerald-patch-2
Create 0000000000000000.json
2024-07-30 17:57:33 +01:00
1tzEmerald 57e4a3e77c Create 0000000000000000.json 2024-07-30 17:57:12 +01:00
1tzEmerald a6ac532b2b Merge pull request #169 from Emerald-Services/dev
8.0.1 Dev -> Release Branch
2024-07-30 17:47:13 +01:00
1tzEmerald 4f14722e14 8.0.1 Update 2024-07-30 17:45:35 +01:00
1tzEmerald 9a6eece7a1 v8.1 2024-07-09 22:49:49 +01:00
1tzEmerald fd65d0a9a7 8.1-BETA 2024-07-03 13:46:57 +01:00
ItsNyoty 48500fe8a4 Update README.md 2024-07-03 13:42:46 +02:00
37 changed files with 4000 additions and 1047 deletions

No files matched your search

+26
View File
@@ -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],
});
},
});
-128
View File
@@ -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.
-92
View File
@@ -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/
+217
View File
@@ -0,0 +1,217 @@
const fs = require("fs");
const {
EmbedBuilder,
ApplicationCommandOptionType,
ApplicationCommandType,
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
ChannelType,
} = 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";
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)
.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 {
// 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);
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: [],
});
// 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(
"%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],
});
}
}
},
});
+19 -50
View File
@@ -2,15 +2,9 @@ const fs = require("fs");
const Discord = require("discord.js"); const Discord = require("discord.js");
const yaml = require("js-yaml"); const yaml = require("js-yaml");
const supportbot = yaml.load( const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
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 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 Command = require("../Structures/Command.js");
@@ -35,28 +29,25 @@ module.exports = new Command({
let Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild); let Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild);
if (!SupportStaff || !Admin) if (!SupportStaff || !Admin)
return interaction.reply( return interaction.reply("Some roles seem to be missing!\nPlease check for errors when starting the bot.");
"Some roles seem to be missing!\nPlease check for errors when starting the bot."
);
const NoPerms = new Discord.EmbedBuilder() const NoPerms = new Discord.EmbedBuilder()
.setTitle("Invalid Permissions!") .setTitle("Invalid Permissions!")
.setDescription( .setDescription(`${msgconfig.Error.IncorrectPerms}\n\nRole Required: \`${supportbot.Roles.StaffMember.Staff}\` or \`${supportbot.Roles.StaffMember.Admin}\``)
`${msgconfig.Error.IncorrectPerms}\n\nRole Required: \`${supportbot.Roles.StaffMember.Staff}\` or \`${supportbot.Roles.StaffMember.Admin}\``
)
.setColor(supportbot.Embed.Colours.Warn); .setColor(supportbot.Embed.Colours.Warn);
if ( 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] }); 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() const NotTicketChannel = new Discord.EmbedBuilder()
.setTitle("Invalid Channel!") .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); .setColor(supportbot.Embed.Colours.Warn);
return interaction.reply({ embeds: [NotTicketChannel], ephemeral: true }); return interaction.reply({ embeds: [NotTicketChannel], ephemeral: true });
@@ -81,15 +72,14 @@ module.exports = new Command({
const CloseButton = new Discord.ButtonBuilder() const CloseButton = new Discord.ButtonBuilder()
.setCustomId("confirmCloseTicket") .setCustomId("confirmCloseTicket")
.setLabel(supportbot.Ticket.Close.Confirmation_Button) .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); const row = new Discord.ActionRowBuilder().addComponents(CloseButton);
const CloseTicketRequest = new Discord.EmbedBuilder() const CloseTicketRequest = new Discord.EmbedBuilder()
.setTitle(`**${supportbot.Ticket.Close.Title}**`) .setTitle(`**${supportbot.Ticket.Close.Title}**`)
.setDescription( .setDescription(`${msgconfig.Ticket.ConfirmClose}`)
`${msgconfig.Ticket.ConfirmClose}`
)
.setColor(supportbot.Embed.Colours.General); .setColor(supportbot.Embed.Colours.General);
await interaction.followUp({ await interaction.followUp({
@@ -116,10 +106,9 @@ async function handleCloseTicket(interaction, reason, ticket, TicketData) {
let tickets = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8")); let tickets = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
let tUser = interaction.client.users.cache.get(ticket.user); let tUser = interaction.client.users.cache.get(ticket.user);
let transcriptChannel = await getChannel(supportbot.Ticket.Log.TranscriptLog, interaction.guild); let transcriptChannel = await getChannel(supportbot.Ticket.Log.TicketDataLog, interaction.guild);
let logChannel = await getChannel(supportbot.Ticket.Log.TicketLog, interaction.guild);
if (!transcriptChannel || !logChannel) if (!transcriptChannel)
return interaction.followUp("Some Channels seem to be missing!"); return interaction.followUp("Some Channels seem to be missing!");
try { try {
@@ -133,22 +122,8 @@ async function handleCloseTicket(interaction, reason, ticket, TicketData) {
); );
const transcriptEmbed = new Discord.EmbedBuilder() const transcriptEmbed = new Discord.EmbedBuilder()
.setTitle(supportbot.Ticket.Log.TranscriptTitle) .setTitle(msgconfig.TicketLog.Title)
.setColor(supportbot.Embed.Colours.General) .setColor(msgconfig.TicketLog.Colour)
.setFooter({
text: supportbot.Embed.Footer,
iconURL: interaction.user.displayAvatarURL(),
})
.addFields(
{ name: "Ticket", value: `${interaction.channel.name} (${interaction.channel.id})` },
{ name: "User", value: `${tUser?.username || "N/A"}#${tUser?.discriminator || "N/A"} (${tUser?.id || ticket.user})` },
{ name: "Closed By", value: interaction.user.tag },
{ name: "Reason", value: reason }
);
const logEmbed = new Discord.EmbedBuilder()
.setTitle(supportbot.Ticket.Log.TicketLog_Title)
.setColor(supportbot.Embed.Colours.General)
.setFooter({ .setFooter({
text: supportbot.Embed.Footer, text: supportbot.Embed.Footer,
iconURL: interaction.user.displayAvatarURL(), iconURL: interaction.user.displayAvatarURL(),
@@ -262,12 +237,6 @@ async function handleCloseTicket(interaction, reason, ticket, TicketData) {
</html> </html>
`; `;
if (!supportbot.Ticket.Log.DisableTicketLogChannel) {
await logChannel.send({ embeds: [logEmbed] }).catch((err) => {
console.error(err);
});
}
let file = new Discord.AttachmentBuilder( let file = new Discord.AttachmentBuilder(
Buffer.from(html), Buffer.from(html),
{ name: fileName } { name: fileName }
+134
View File
@@ -0,0 +1,134 @@
const fs = require("fs");
const {
EmbedBuilder,
ApplicationCommandOptionType,
ApplicationCommandType,
ChannelType,
} = 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 forcefully add",
type: ApplicationCommandOptionType.User,
required: true,
},
],
permissions: cmdconfig.ForceAddUser.Permission,
async run(interaction) {
const userToAdd = interaction.options.getUser("user");
const ticketChannel = interaction.channel;
const ticketDataPath = "./Data/TicketData.json";
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)
.setColor(supportbot.Embed.Colours.Error);
return interaction.reply({
embeds: [onlyInTicket],
ephemeral: true,
});
}
try {
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);
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,
});
}
},
});
+10 -21
View File
@@ -1,17 +1,9 @@
// SupportBot | Emerald Services
// Help Command
const fs = require("fs"); const fs = require("fs");
const Discord = require("discord.js"); const Discord = require("discord.js");
const yaml = require("js-yaml"); const yaml = require("js-yaml");
const supportbot = yaml.load( const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
fs.readFileSync("./Configs/supportbot.yml", "utf8") const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
);
const cmdconfig = yaml.load(
fs.readFileSync("./Configs/commands.yml", "utf8")
);
const Command = require("../Structures/Command.js"); 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 botCommands = "";
let addonCommands = "";
interaction.client.commands.forEach((command) => { interaction.client.commands.forEach((command) => {
if (command.permissions) { 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, inline: false,
}); });
// Conditionally add Addons section
if (supportbot.General.Addons.Enabled) {
HelpEmbed1.addFields({
name: "🛠️ Addons\n",
value: "No addons available.",
inline: false,
});
}
HelpEmbed1.setFooter({ HelpEmbed1.setFooter({
text: supportbot.Embed.Footer, text: supportbot.Embed.Footer,
iconURL: interaction.user.displayAvatarURL(), iconURL: interaction.user.displayAvatarURL(),
+324
View File
@@ -0,0 +1,324 @@
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: cmdconfig.Profile.Command,
description: cmdconfig.Profile.Description,
type: ApplicationCommandType.ChatInput,
permissions: cmdconfig.Profile.Permission,
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 { getRole, getChannel } = interaction.client;
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;
+134
View File
@@ -0,0 +1,134 @@
const fs = require("fs");
const {
EmbedBuilder,
ApplicationCommandOptionType,
ApplicationCommandType,
ChannelType,
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 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)
.setColor(supportbot.Embed.Colours.Error);
return interaction.reply({
embeds: [onlyInTicket],
ephemeral: true,
});
}
try {
// 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) {
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,
});
}
},
});
+235
View File
@@ -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 });
}
},
});
+402 -240
View File
@@ -8,7 +8,7 @@ const {
ButtonBuilder, ButtonBuilder,
StringSelectMenuBuilder, StringSelectMenuBuilder,
StringSelectMenuOptionBuilder, StringSelectMenuOptionBuilder,
GuildMemberRoleManager, ButtonStyle,
} = require("discord.js"); } = require("discord.js");
const yaml = require("js-yaml"); const yaml = require("js-yaml");
@@ -20,6 +20,177 @@ const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
const Command = require("../Structures/Command.js"); const Command = require("../Structures/Command.js");
const TicketNumberID = require("../Structures/TicketID.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) {
try {
const clockedInUsers = await getClockedInUsers(interaction.guild);
if (clockedInUsers.size === 0 || attempts >= 2) {
const claimChannel = await interaction.guild.channels.cache.get(supportbot.Ticket.ClaimTickets.Channel);
if (claimChannel) {
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 || '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 => {
await ticketChannel.send({ content: `<@${i.user.id}> has claimed the ticket.` });
});
collector.on('end', async collected => {
if (collected.size === 0) {
await claimMessage.delete();
await claimChannel.send({
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)],
});
}
});
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);
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 || '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 => {
await ticketChannel.send({ content: `<@${assignedUserId}>` }).then(msg => setTimeout(() => msg.delete(), 5000));
});
collector.on('end', async collected => {
if (collected.size === 0) {
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)],
});
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({
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({ module.exports = new Command({
name: cmdconfig.OpenTicket.Command, name: cmdconfig.OpenTicket.Command,
description: cmdconfig.OpenTicket.Description, description: cmdconfig.OpenTicket.Description,
@@ -34,275 +205,266 @@ module.exports = new Command({
permissions: cmdconfig.OpenTicket.Permission, permissions: cmdconfig.OpenTicket.Permission,
async run(interaction) { async run(interaction) {
let department = interaction.customId?.split("-")[1] || null; try {
let TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8")); let TicketReason = null;
const { getRole, getChannel } = interaction.client; if (supportbot.Ticket.TicketReason) {
let User = interaction.guild.members.cache.get(interaction.user.id); TicketReason = interaction.reason || null; // Retrieve the reason from the interaction object
}
if ( let TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
supportbot.Ticket.TicketsPerUser && const { getRole, getChannel } = interaction.client;
TicketData.tickets.filter((t) => t.user === interaction.user.id && t.open).length >= supportbot.Ticket.TicketsPerUser let User = interaction.guild.members.cache.get(interaction.user.id);
) {
return interaction.reply({ if (
embeds: [ supportbot.Ticket.TicketsPerUser &&
{ TicketData.tickets.filter((t) => t.user === interaction.user.id && t.open).length >= supportbot.Ticket.TicketsPerUser
title: "Too Many Tickets!", ) {
description: `You can't have more than ${supportbot.Ticket.TicketsPerUser} open tickets!`, return interaction.reply({
color: supportbot.Embed.Colours.Warn, embeds: [
}, {
], title: "Too Many Tickets!",
ephemeral: true, 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 = TicketReason || 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,
});
let ticketChannel;
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);
}
}
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,
});
} }
if (User.roles.cache.has(supportbot.Roles.ModRoles.Blacklisted)) { // Save ticket data
return interaction.reply({ TicketData.tickets.push({
content: msgconfig.Ticket.Blacklisted, id: ticketChannel.id,
ephemeral: true, name: ticketChannel.name,
}); user: interaction.user.id,
} number: ticketNumberID,
reason: TicketSubject,
if (User.roles.cache.has(supportbot.Roles.ModRoles.Muted)) { open: true,
return interaction.reply({ subUsers: [],
content: msgconfig.Ticket.Muted, createdAt: new Date().toISOString(),
ephemeral: true, claimedAt: null,
});
}
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( fs.writeFileSync(
(c) => c.name === supportbot.Ticket.TicketCategory || c.id === supportbot.Ticket.TicketCategory "./Data/TicketData.json",
); JSON.stringify(TicketData, null, 4),
(err) => {
const channel = await getChannel(supportbot.Ticket.TicketCategory, interaction.guild); if (err) console.error(err);
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() // Notify the user that the ticket was created
.setTitle(msgconfig.Ticket.ClaimTickets.ClaimTitle) const CreatedTicket = new EmbedBuilder()
.setDescription(msgconfig.Ticket.ClaimTickets.ClaimMessage .setDescription(
.replace('%user%', interaction.user.id) msgconfig.Ticket.TicketCreatedAlert.replace(/%ticketauthor%/g, interaction.user.id)
) .replace(/%ticketid%/g, ticketChannel.id)
.setColor(supportbot.Embed.Colours.General); .replace(/%ticketusername%/g, interaction.user.username)
.replace(/%ticketreason%/g, TicketSubject)
)
.setColor(supportbot.Embed.Colours.General);
await interaction.reply({ embeds: [CreatedTicket], ephemeral: true });
const claimButton = new ButtonBuilder() // Create the ticket message
.setCustomId(`claimticket-${ticketChannel.id}`) const TicketMessage = new EmbedBuilder()
.setLabel(supportbot.Ticket.ClaimTickets.ButtonTitle) .setAuthor({
.setStyle(supportbot.Ticket.ClaimTickets.Button); 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)
.replace(/%ticketreason%/g, TicketSubject)
)
.setColor(supportbot.Embed.Colours.General);
const row = new ActionRowBuilder().addComponents(claimButton); // Add reason field if ticket reason is enabled
if (supportbot.Ticket.TicketReason && TicketReason) {
await claimChannel.send({ TicketMessage.addFields(
content: `<@&${Staff.id}>`, { name: "Reason", value: TicketReason, inline: false }
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);
} }
);
const CreatedTicket = new EmbedBuilder() // Create the control panel
.setDescription( const SelectMenus = new StringSelectMenuBuilder()
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 (supportbot.Ticket.AllowMentions) {
await ticketChannel.send(`${interaction.user}`);
}
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);
const SelectMenus = new StringSelectMenuBuilder()
.setCustomId("ticketcontrolpanel") .setCustomId("ticketcontrolpanel")
.setPlaceholder("Ticket Control Panel") .setPlaceholder("Ticket Control Panel")
.addOptions( .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() new StringSelectMenuOptionBuilder()
.setLabel("Close") .setLabel("Close")
.setDescription("Close the ticket.") .setDescription("Close the ticket.")
.setEmoji(supportbot.SelectMenus.Tickets.CloseEmoji) .setEmoji(supportbot.SelectMenus.Tickets.CloseEmoji)
.setValue("ticketclose"), .setValue("ticketclose"),
new StringSelectMenuOptionBuilder() new StringSelectMenuOptionBuilder()
.setLabel("Enable Invites") .setLabel("Archive")
.setDescription("Enable invites.") .setDescription("Archive the ticket.")
.setEmoji(supportbot.SelectMenus.Tickets.EnableInvitesEmoji) .setEmoji(supportbot.SelectMenus.Tickets.ArchiveEmoji)
.setValue("enableinvites"), .setValue("archiveticket"),
...(supportbot.Ticket.TicketType === "threads" ? [
new StringSelectMenuOptionBuilder()
.setLabel("Lock Ticket")
.setDescription("Lock the ticket.")
.setEmoji(supportbot.SelectMenus.Tickets.LockEmoji)
.setValue("lockticket")
] : []),
new StringSelectMenuOptionBuilder() new StringSelectMenuOptionBuilder()
.setLabel("Disable Invites") .setLabel("Rename Ticket")
.setDescription("Disable invites.") .setDescription("Rename the ticket.")
.setEmoji(supportbot.SelectMenus.Tickets.DisableInvitesEmoji) .setEmoji(supportbot.SelectMenus.Tickets.RenameEmoji)
.setValue("disableinvites"), .setValue("renameticket")
); );
const row2 = new ActionRowBuilder().addComponents(SelectMenus); if (supportbot.Ticket.TicketType === "threads") {
SelectMenus.addOptions(
if (!department) { new StringSelectMenuOptionBuilder()
try { .setLabel("Enable Invites")
let buttons = supportbot.Departments.map((x) => .setDescription("Enable invites.")
new ButtonBuilder() .setEmoji(supportbot.SelectMenus.Tickets.EnableInvitesEmoji)
.setCustomId("Department" + supportbot.Departments.indexOf(x)) .setValue("enableinvites"),
.setLabel(x.title) new StringSelectMenuOptionBuilder()
.setStyle(x.color) .setLabel("Disable Invites")
.setEmoji(x.emoji) .setDescription("Disable invites.")
); .setEmoji(supportbot.SelectMenus.Tickets.DisableInvitesEmoji)
.setValue("disableinvites"),
if (supportbot.Ticket.AllowMentions) { );
await ticketChannel.send("<@&" + supportbot.Ticket.RoleMention + ">");
} }
const row = new ActionRowBuilder().addComponents(buttons); const row2 = new ActionRowBuilder().addComponents(SelectMenus);
const m = await ticketChannel.send({
embeds: [TicketMessage],
components: [row2],
});
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();
}
}
} catch (error) {
console.error("Error sending message:", error);
}
} else {
await ticketChannel.send({ await ticketChannel.send({
embeds: [TicketMessage], embeds: [TicketMessage],
components: [row2], components: [row2],
}); });
} catch (error) {
console.error("Error in run method:", error);
} }
}, },
}); });
+7 -2
View File
@@ -35,8 +35,13 @@ module.exports = new Command({
}); });
} }
const channelName = panelconfig.Channel; const { getChannel } = interaction.client;
const channel = interaction.guild.channels.cache.find(ch => ch.name === channelName);
const channel = await getChannel(
supportbot.Ticket.TicketHome,
interaction.guild
);
if (!channel) { if (!channel) {
console.log(`[TICKET PANEL] ${channelName} channel not found. Please check your config file.`); console.log(`[TICKET PANEL] ${channelName} channel not found. Please check your config file.`);
return interaction.reply({ return interaction.reply({
-56
View File
@@ -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 });
}
}
});
+1 -1
View File
@@ -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 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 text = await interaction.options.getString("text"); // Grab the text to translate by the user
let translatelog = await getChannel( let translatelog = await getChannel(
supportbot.Translate.Log, supportbot.Translate.TranslateLog,
interaction.guild interaction.guild
); // Grab the set logging channel for translations ); // Grab the set logging channel for translations
-137
View File
@@ -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/
**/
+29 -11
View File
@@ -13,18 +13,14 @@
# #
# Command Configuration # Command Configuration
# If you don't want to mess with this configuration, You can manage commands via Discord Settings the Intregations.
Help: Help:
Command: "help" Command: "help"
Description: "Get a list of all the commands." Description: "Get a list of all the commands."
Permission: ["SendMessages"] Permission: ["SendMessages"]
Enabled: true Enabled: true
EnableAI:
Command: "enableai"
Description: "Talk with SupportBot"
Permission: ["SendMessages"]
Enabled: true
Info: Info:
Command: "info" Command: "info"
Description: "Get information about the **ServerName**." Description: "Get information about the **ServerName**."
@@ -37,9 +33,9 @@ TicketPanel:
Permission: ["Administrator"] Permission: ["Administrator"]
Enabled: true Enabled: true
TicketStats: Profile:
Command: "ticketstats" Command: "profile"
Description: "Shows staff members their ticket statistics" Description: "Shows the profile for a discord member."
Permission: ["SendMessages"] Permission: ["SendMessages"]
# Command is automatically disabled when the TicketClaims is disabled. # Command is automatically disabled when the TicketClaims is disabled.
@@ -49,9 +45,21 @@ OpenTicket:
Permission: ["SendMessages"] Permission: ["SendMessages"]
Enabled: true Enabled: true
Add: AddUser:
Command: "add" 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"] Permission: ["SendMessages"]
Enabled: true Enabled: true
@@ -91,6 +99,16 @@ Ping:
Permission: ["SendMessages"] Permission: ["SendMessages"]
Enabled: true Enabled: true
# ---------------------------------------
# Bot Administrator Commands
# ---------------------------------------
Settings:
Command: "settings"
Description: "Update bot settings."
Permission: ["Administrator"]
Enabled: true
# --------------------------------------- # ---------------------------------------
# Command Description Configuration # Command Description Configuration
# --------------------------------------- # ---------------------------------------
+101 -36
View File
@@ -17,14 +17,14 @@ Info:
Title: "About Us" 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." 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" Button: "Visit Site"
URL: "https://emeraldsrv.com" URL: "https://docs.emeraldsrv.com"
Colour: "#f74343" Colour: "#f74343"
Welcome: Welcome:
Embed: Embed:
Colour: "#f74343" Colour: "#f74343"
Title: "Welcome to ServerName" Title: "Welcome to ServerName"
Message: "> :smile: %joined_user% has just landed!" Message: ":smile: %joined_user% has just landed!"
Thumbnail: "BOT" Thumbnail: "BOT"
ImageEnabled: false ImageEnabled: false
@@ -34,7 +34,7 @@ Leave:
Embed: Embed:
Colour: "#f74343" Colour: "#f74343"
Title: "Goodbye!" Title: "Goodbye!"
Message: "> :slight_frown: %joined_user% has recently dropped out!" Message: ":slight_frown: %joined_user% has recently dropped out!"
Thumbnail: "BOT" Thumbnail: "BOT"
ImageEnabled: false ImageEnabled: false
@@ -42,8 +42,8 @@ Leave:
Suggestions: Suggestions:
Sent_Title: "Suggestion Sent!" Sent_Title: "Suggestion Sent!"
Sent: "> :white_check_mark: **You have successfully sent a suggestion.**" Sent: ":white_check_mark: **You have successfully sent a suggestion.**"
NoSelfVote: "> :x: **You cannot vote on your own suggestion.**" NoSelfVote: ":x: **You cannot vote on your own suggestion.**"
RemoveVoteButton: "Remove Vote" RemoveVoteButton: "Remove Vote"
Ticket: Ticket:
@@ -56,47 +56,44 @@ Ticket:
TicketAuthorTitle: "%ticketusername%'s Ticket" TicketAuthorTitle: "%ticketusername%'s Ticket"
TicketTitle: "Support 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" TicketThumbnail: "https://images.emojiterra.com/google/android-pie/512px/1f39f.png"
TicketCreatedAlert: "> :ticket: Ticked Created: <#%ticketid%>" TicketCreatedAlert: ":ticket: Ticked Created: <#%ticketid%>"
TicketExists: "> ❌ Ticket already exists" 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 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 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." TicketArchived: ":white_check_mark: Ticket archived."
EnableInvites: "> 🔓 Invites have been enabled for this ticket." 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: # Available Placeholders:
# <@%user%> - Tags the user who was added to the ticket. # <@%user%> - Tags the user who was added to the ticket.
AddUserToTicket_Title: "➕ Add User!" Blacklisted: ":x: You are not allowed to open a ticket"
AddUserToTicket: "> To add a user to this ticket, Tag them." Muted: ":x: You are `Muted` and therefore not allowed to open any tickets"
InvalidSubject: "No Reason Provided!"
RemovedUserFromTicket_Title: "➖ Removed User!" ConfirmClose: "Please confirm closing the ticket by clicking the button below."
RemovedUserFromTicket: "> <@%user%> has been `Removed` from this ticket" CloseTimeout: "Ticket close confirmation timed out."
Blacklisted: "> :x: You are not allowed to open a ticket" RenamedSuccess: "🏷️ Ticket renamed successfully!"
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."
ClaimTickets: ClaimTickets:
ClaimTitle: "Incoming Support Request!" ClaimTitle: "Incoming Support Request!"
ClaimMessage: "> A new ticket has been created by <@!%user%>. Click the button below to claim this ticket." 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_Edit: "<#%channel%> was claimed by <@!%user%>."
ClaimedTitle: "Ticket Claimed!" ClaimedTitle: "Ticket Claimed!"
Claimed: "> You have successfully claimed this ticket. <#%channel%>" Claimed: "You have successfully claimed this ticket. <#%channel%>"
NoPermsToClaimTitle: "Error, no permissions!" 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...**"
TicketStats: TicketStats:
OpenTickets: "✅ Open Tickets" OpenTickets: "✅ Open Tickets"
@@ -105,12 +102,80 @@ TicketStats:
Title: "Your Ticket Stats" 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."
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%."
# 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."
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: Error:
IncorrectPerms: "> :x: **Err!** You do not have the correct permissions to use this." IncorrectPerms: ":x: **Err!** You do not have the correct permissions to use this."
InvalidChannel: "> :x: **Err!** Channel not found. Please create it!" InvalidChannel: ":x: **Err!** Channel not found. Please create it!"
UserNotFound: "> :x: **Err!** This user doesn't exist, Are they in this server?" UserNotFound: ":x: **Err!** This user doesn't exist, Are they in this server?"
NoValidTicket: "> :warning: You cannot execute this command here." NoValidTicket: ":warning: You cannot execute this command here."
TicketExists: "> :warning: This ticket cannot be opened, It already exists!" TicketExists: ":warning: This ticket cannot be opened, It already exists!"
+2 -3
View File
@@ -10,13 +10,12 @@ Enabled: true
General: General:
Name: "SupportBot AI" Name: "SupportBot AI"
# What would you like to call your 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 # You can see a list of OpenAI Models here: https://platform.openai.com/docs/models
Tokens: 400 Tokens: 400
# Max tokens you can set is 400 # Max tokens you can set is 400
# For more information about tokens, go to https://openai.com/api/pricing/ # For more information about tokens, go to https://openai.com/api/pricing/
OpenAI_Key: "OPEN_AI_API_KEY" OpenAI_Key: "OPEN_AI_API_KEY"
# To create an OpenAI API key, go to https://platform.openai.com/api-keys # To create an OpenAI API key, go to https://platform.openai.com/api-keys
@@ -26,7 +25,7 @@ General:
PastebinAPI_URL: "https://pastebin.com/api/api_post.php" PastebinAPI_URL: "https://pastebin.com/api/api_post.php"
Channels: Channels:
AIChannel: "0000000000000000000" AIChannel: "AI_BOT_CHANNEL"
# This is the channel where AI will respond in without needing to mention the bot. # This is the channel where AI will respond in without needing to mention the bot.
Embed: Embed:
+72 -46
View File
@@ -11,7 +11,7 @@
# Official Documentation: https://github.com/Emerald-Services/SupportBot/wiki # Official Documentation: https://github.com/Emerald-Services/SupportBot/wiki
# Third-Party Documentation: COMING SOON # Third-Party Documentation: COMING SOON
SupportBot_Version: "v8.0" SupportBot_Version: "v8.2"
# Do not change the version number # Do not change the version number
# ------------------------- # -------------------------
@@ -24,7 +24,7 @@ General:
# To get your Discord Bot Token, Go to https://discord.com/developers/applications # To get your Discord Bot Token, Go to https://discord.com/developers/applications
Addons: Addons:
Enabled: false Enabled: true
# ------------------------- # -------------------------
# Activity # Activity
@@ -37,6 +37,8 @@ Activity:
StreamingURL: "https://www.twitch.tv/YOUR_TWITCH_NAME" StreamingURL: "https://www.twitch.tv/YOUR_TWITCH_NAME"
# Set The Type to "Streaming" for a streaming status! # Set The Type to "Streaming" for a streaming status!
# You can update the activity via Discord using: /settings
# ------------------------- # -------------------------
# Embeds # Embeds
# ------------------------- # -------------------------
@@ -57,15 +59,15 @@ Embed:
Roles: Roles:
AutoRole: AutoRole:
Enabled: true Enabled: true
Role: "Member" Role: "ROLE_ID"
StaffMember: StaffMember:
Staff: "Staff" Staff: "STAFF_ROLE_ID"
Admin: "Admin" Admin: "ADMIN_ROLE_ID"
ModRoles: ModRoles:
Muted: "Muted" Muted: "MUTED_ROLE_ID"
Blacklisted: "Blacklisted" Blacklisted: "BLACKLISTED_ROLE_ID"
# ---------------------------- # ----------------------------
# System Messages # System Messages
@@ -73,18 +75,18 @@ Roles:
Welcome: Welcome:
Enabled: true Enabled: true
Channel: "🚨┃join-leave-logs" Channel: "WELCOME_CHANNEL_ID"
Leave: Leave:
Enabled: true Enabled: true
Channel: "🚨┃join-leave-logs" Channel: "LEAVE_CHANNEL_ID"
# ---------------------------- # ----------------------------
# Suggestions # Suggestions
# ---------------------------- # ----------------------------
Suggestions: Suggestions:
Channel: "📗┃suggestions" Channel: "SUGGEST_CHANNEL_ID"
UpvoteEmoji: "✅" UpvoteEmoji: "✅"
DownvoteEmoji: "❌" DownvoteEmoji: "❌"
@@ -119,8 +121,23 @@ Suggestions:
# ------------------------- # -------------------------
Ticket: Ticket:
TicketCategory: "📗┃tickets" #Where tickets normally go with the /ticket command TicketType: "channels" # Choose how you want tickets to be created.
# This is the channel name for the ticket threads to be created, NOT A DISCORD CATEGORY! # 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 Channel: "ticket-" # The prefix for ticket channel names
TicketsPerUser: 100 TicketsPerUser: 100
@@ -134,47 +151,50 @@ Ticket:
# Set to true to allow all staff to access the ticket # Set to true to allow all staff to access the ticket
Log: Log:
TicketLog: "🚨┃ticket-data" TicketDataLog: "TICKET_LOG_ID"
TicketLog_Title: "Ticket Closed" TickeDataTitle: "Ticket Data"
DisableTicketLogChannel: false
TranscriptLog: "🚨┃ticket-data"
TranscriptTitle: "Ticket Transcript"
Close: Close:
StaffOnly: true StaffOnly: true
# Set to false is anyone is able to close a ticket. # Set to false is anyone is able to close a ticket.
Confirmation_Button: "Confirm Close" Confirmation_Button: "Confirm Close"
Confirmation_Emoji: "🗑️"
Confirmation_Style: "2"
Title: "Are you sure you want to close this ticket?" Title: "Are you sure you want to close this ticket?"
Invites:
StaffOnly: true
ClaimTickets:
Enabled: true
Channel: "1249723473757077624"
# Channel name or Channel ID
ButtonTitle: "Claim Ticket"
Button: "1"
# Available Button Styles # Available Button Styles
# 1 - Blurple # 1 - Blurple
# 2 - Grey # 2 - Grey
# 3 - Green # 3 - Green
# 4 - Red # 4 - Red
Invites:
StaffOnly: true
# ------------------------- ClaimTickets:
# Departments - NOT IN THIS UPDATE Enabled: true
# ------------------------- 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
Departments: ButtonTitle: "Claim Ticket"
- title: "Create Ticket" ButtonEmoji: "🎫"
role: "Staff" Button: "1"
color: "2"
emoji: "🎫" # Available Button Styles
category: "TICKETS" # 1 - Blurple
# 2 - Grey
# 3 - Green
# 4 - Red
# ------------------------- # -------------------------
# Buttons # Buttons
@@ -184,13 +204,20 @@ Buttons:
ClaimEmoji: "🧑‍💻" ClaimEmoji: "🧑‍💻"
ClaimStyle: "1" ClaimStyle: "1"
Unarchive: "Unarchive Ticket"
Unarchive_Emoji: "♻️"
Unarchive_Style: "2"
Unlock: "Unlock Ticket"
Unlock_Emoji: "🔓"
Unlock_Style: "2"
# Available Button Styles # Available Button Styles
# 1 - Blurple # 1 - Blurple
# 2 - Grey # 2 - Grey
# 3 - Green # 3 - Green
# 4 - Red # 4 - Red
# ------------------------- # -------------------------
# Select Menus # Select Menus
# ------------------------- # -------------------------
@@ -198,19 +225,18 @@ Buttons:
SelectMenus: SelectMenus:
Tickets: Tickets:
PanelEmoji: "⚙️" PanelEmoji: "⚙️"
CloseEmoji: "🗑️" CloseEmoji: "🗑️"
ArchiveEmoji: "♻️" ArchiveEmoji: "♻️"
LockTicketEmoji: "🔒" LockEmoji: "🔐"
UnLockTicketEmoji: "🔓" RenameEmoji: "🏷️"
EnableInvitesEmoji: "🔒"
DisableInvitesEmoji: "🔓"
AddUserEmoji: "➕"
RemoveUserEmoji: "➖"
EnableInvitesEmoji: "📤"
DisableInvitesEmoji: "📥"
# ------------------------- # -------------------------
# Misc # Misc
# ------------------------- # -------------------------
Translate: Translate:
Log: "🚨┃logs" TranslateLog: "TRANSLATE_LOG_ID"
-3
View File
@@ -20,9 +20,6 @@ Panel: true
# Ticket Panel Message Configuration #2b2d31 # Ticket Panel Message Configuration #2b2d31
# ---------------------------------------- # ----------------------------------------
Channel: "📗┃tickets"
# The channel used to create the ticket panel, Use command /ticketpanel to generate it.
PanelTitle: "SupportBot" PanelTitle: "SupportBot"
PanelColour: "#0cc559" PanelColour: "#0cc559"
+1
View File
@@ -0,0 +1 @@
{}
+1
View File
@@ -0,0 +1 @@
{}
+1 -3
View File
@@ -1,3 +1 @@
{ {}
}
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"tickets": [ "tickets": [
{}
] ]
} }
+5
View File
@@ -0,0 +1,5 @@
{
"bio": "",
"timezone": "",
"clockedIn": false
}
+1
View File
@@ -0,0 +1 @@
{}
+1 -1
View File
@@ -1,4 +1,4 @@
{ {
"id": 0, "id": 0,
"TicketPanelID": "0000000000000000000" "TicketPanelID": "0000000000000000000"
} }
+20 -19
View File
@@ -19,7 +19,8 @@ const msgconfig = yaml.load(
module.exports = new Event("guildMemberAdd", async (client, member, interaction, guild) => { module.exports = new Event("guildMemberAdd", async (client, member, interaction, guild) => {
if (supportbot.Welcome.Enabled) { 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() const WelcomeEmbed = new Discord.EmbedBuilder()
.setColor(msgconfig.Welcome.Embed.Colour) .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)) .setDescription(msgconfig.Welcome.Embed.Message.replace(/%joined_user%/g, member.user))
.setTimestamp(); .setTimestamp();
if (msgconfig.Welcome.Embed.Thumbnail === "BOT") { if (msgconfig.Welcome.Embed.Thumbnail === "BOT") {
WelcomeEmbed.setThumbnail(client.user.displayAvatarURL()) WelcomeEmbed.setThumbnail(client.user.displayAvatarURL())
} } else if (msgconfig.Welcome.Embed.Thumbnail === "USER") {
WelcomeEmbed.setThumbnail(member.user.displayAvatarURL())
}
if (msgconfig.Welcome.Embed.Thumbnail === "USER") { if (msgconfig.Welcome.Embed.ImageEnabled) {
WelcomeEmbed.setThumbnail(member.user.displayAvatarURL()) WelcomeEmbed.setImage(msgconfig.Welcome.Embed.ImageURL)
} }
if (msgconfig.Welcome.Embed.ImageEnabled) { if (WelcomeChannel) {
WelcomeEmbed.setImage(msgconfig.Welcome.Embed.ImageURL) WelcomeChannel.send({ embeds: [WelcomeEmbed] });
} }
WelcomeChannel.send({ if (supportbot.Roles.AutoRole.Enabled) {
embeds: [WelcomeEmbed] 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) {
if (supportbot.Roles.AutoRole.Enabled) {
var role = member.guild.roles.cache.find(role => role.name === supportbot.Roles.AutoRole.Role);
member.roles.add(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!`);
} }
}); });
+16 -16
View File
@@ -19,32 +19,32 @@ const msgconfig = yaml.load(
module.exports = new Event("guildMemberRemove", async (client, member, interaction, guild) => { module.exports = new Event("guildMemberRemove", async (client, member, interaction, guild) => {
if (supportbot.Leave.Enabled) { 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() const LeaveEmbed = new Discord.EmbedBuilder()
.setColor(msgconfig.Leave.Embed.Colour) .setColor(msgconfig.Leave.Embed.Colour)
.setTitle(msgconfig.Leave.Embed.Title) .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(); .setTimestamp();
if (msgconfig.Leave.Embed.Thumbnail === "BOT") { if (msgconfig.Leave.Embed.Thumbnail === "BOT") {
LeaveEmbed.setThumbnail(client.user.displayAvatarURL()) LeaveEmbed.setThumbnail(client.user.displayAvatarURL());
} } else if (msgconfig.Leave.Embed.Thumbnail === "USER") {
LeaveEmbed.setThumbnail(member.user.displayAvatarURL());
}
if (msgconfig.Leave.Embed.Thumbnail === "USER") { if (msgconfig.Leave.Embed.ImageEnabled) {
LeaveEmbed.setThumbnail(member.user.displayAvatarURL()) LeaveEmbed.setImage(msgconfig.Leave.Embed.ImageURL);
} }
if (msgconfig.Leave.Embed.ImageEnabled) {
LeaveEmbed.setImage(msgconfig.Leave.Embed.ImageURL)
}
if (LeaveChannel) {
LeaveChannel.send({ LeaveChannel.send({
embeds: [LeaveEmbed] 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!`);
} }
}); });
+480 -67
View File
@@ -16,7 +16,8 @@ try {
} }
module.exports = new Event("interactionCreate", async (client, interaction) => { module.exports = new Event("interactionCreate", async (client, interaction) => {
if (interaction.type == Discord.InteractionType.ApplicationCommand) {
if (interaction.type === Discord.InteractionType.ApplicationCommand) {
const command = client.commands.find( const command = client.commands.find(
(cmd) => cmd.name.toLowerCase() === interaction.commandName (cmd) => cmd.name.toLowerCase() === interaction.commandName
); );
@@ -24,12 +25,13 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
if (interaction.user.bot || !interaction.guild) return; if (interaction.user.bot || !interaction.guild) return;
const NotValid = new Discord.EmbedBuilder() const NotValid = new Discord.EmbedBuilder()
.setDescription(`:x: \`Invalid Command\` `) .setDescription(`:x: \`Invalid Command\``)
.setColor(supportbot.Embed.Colours.Error); .setColor(supportbot.Embed.Colours.Error);
if (!command) if (!command)
return interaction.reply({ return interaction.reply({
embeds: [NotValid], embeds: [NotValid],
ephemeral: true,
}); });
const permission = interaction.member.permissions.has(command.permissions); const permission = interaction.member.permissions.has(command.permissions);
@@ -43,40 +45,238 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
if (!permission) if (!permission)
return interaction.reply({ return interaction.reply({
embeds: [ValidPerms], 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("\u001b[32m", "[Executed]", "\u001b[37;1m", `${interaction.user.username} has executed ${command.name}`);
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.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.isStringSelectMenu()) {
if (interaction.customId === "ticketcontrolpanel") { if (interaction.customId === "ticketcontrolpanel") {
const selectedOption = interaction.values[0]; const selectedOption = interaction.values[0];
const { getRole, getChannel } = interaction.client; const { getRole, getChannel } = interaction.client;
switch (selectedOption) { 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": case "ticketclose":
const closeCommand = client.commands.get(cmdconfig.CloseTicket.Command); const closeCommand = client.commands.get(cmdconfig.CloseTicket.Command);
if (closeCommand) { if (closeCommand) {
closeCommand.run(interaction); await closeCommand.run(interaction);
} else { } else {
console.error("[X] Close Command Not Found"); console.error("[X] Close Command Not Found");
} }
break; 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],
});
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);
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,
});
}
}
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;
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": case "enableinvites":
await handleInvites(interaction, true); await handleInvites(interaction, true);
break; break;
@@ -84,37 +284,163 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
case "disableinvites": case "disableinvites":
await handleInvites(interaction, false); await handleInvites(interaction, false);
break; 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.isButton()) {
if (interaction.customId === 'createticket') { if (interaction.customId === "unarchiveTicket") {
try {
const cmd = client.commands.get(cmdconfig.OpenTicket.Command); if (supportbot.Ticket.TicketType === "threads") {
if (!cmd) return; await interaction.channel.setArchived(false);
cmd.run(interaction); }
} catch (error) {
console.error("Error executing the ticket command:", error); if (supportbot.Ticket.TicketType === "channels") {
await interaction.reply({ const { getRole, getChannel } = interaction.client;
content: "An error occurred while creating the ticket.", await interaction.channel.setParent(supportbot.Ticket.TicketChannelsCategory);
ephemeral: true,
}); 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);
await interaction.update({
embeds: [unrcembed],
components: [],
});
}
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") { if (interaction.customId === "openticket") {
try { if (supportbot.Ticket.TicketReason) {
const cmd = client.commands.get(cmdconfig.OpenTicket); const modal = new Discord.ModalBuilder()
if (!cmd) return; .setCustomId("ticketReasonModal")
cmd.run(interaction); .setTitle("Ticket Reason")
} catch (error) { .addComponents(
console.error(error); 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-')) { if (interaction.customId.startsWith("claimticket-")) {
const ticketChannelId = interaction.customId.split('-')[1]; const ticketChannelId = interaction.customId.split("-")[1];
const ticketChannel = interaction.guild.channels.cache.get(ticketChannelId); const ticketChannel = interaction.guild.channels.cache.get(ticketChannelId);
if (!ticketChannel) { if (!ticketChannel) {
return interaction.reply({ return interaction.reply({
@@ -144,7 +470,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
const NoPermsClaim = new Discord.EmbedBuilder() const NoPermsClaim = new Discord.EmbedBuilder()
.setTitle(msgconfig.Ticket.ClaimTickets.NoPermsToClaimTitle) .setTitle(msgconfig.Ticket.ClaimTickets.NoPermsToClaimTitle)
.setDescription( .setDescription(
msgconfig.Ticket.ClaimTickets.NoPermsToClaim.replace('%channel%', ticketChannel.name) msgconfig.Ticket.ClaimTickets.NoPermsToClaim.replace("%channel%", ticketChannel.name)
) )
.setColor(supportbot.Embed.Colours.Error); .setColor(supportbot.Embed.Colours.Error);
@@ -155,12 +481,32 @@ 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}>`); const pingclaimedstaff = await ticketChannel.send(`<@${interaction.user.id}>`);
setTimeout(() => { setTimeout(() => {
pingclaimedstaff.delete(); pingclaimedstaff.delete();
}, 2000) }, 2000);
const TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8")); const TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
const ticketIndex = TicketData.tickets.findIndex((t) => t.id === ticketChannelId); const ticketIndex = TicketData.tickets.findIndex((t) => t.id === ticketChannelId);
@@ -174,13 +520,13 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
const claimedEmbed = new Discord.EmbedBuilder(claimMessage.embeds[0].data) const claimedEmbed = new Discord.EmbedBuilder(claimMessage.embeds[0].data)
.setTitle(msgconfig.Ticket.ClaimTickets.ClaimedTitle) .setTitle(msgconfig.Ticket.ClaimTickets.ClaimedTitle)
.setDescription( .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); .setColor(supportbot.Embed.Colours.Success);
const successfullyClaimed = new Discord.EmbedBuilder() const successfullyClaimed = new Discord.EmbedBuilder()
.setTitle(msgconfig.Ticket.ClaimTickets.ClaimedTitle) .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); .setColor(supportbot.Embed.Colours.Success);
await claimMessage.edit({ embeds: [claimedEmbed], components: [] }); await claimMessage.edit({ embeds: [claimedEmbed], components: [] });
@@ -191,36 +537,41 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
} }
// SUGGESTION INTERACTIONS [START] // 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]; 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 userId = interaction.user.id;
const hasUpvoted = suggestion.upvotes.includes(userId); const hasUpvoted = suggestion.upvotes.includes(userId);
const hasDownvoted = suggestion.downvotes.includes(userId); const hasDownvoted = suggestion.downvotes.includes(userId);
if (interaction.customId === 'upvote') { if (interaction.customId === "upvote") {
if (hasUpvoted) { if (hasUpvoted) {
suggestion.upvotes = suggestion.upvotes.filter(id => id !== userId); suggestion.upvotes = suggestion.upvotes.filter((id) => id !== userId);
} else { } else {
suggestion.upvotes.push(userId); suggestion.upvotes.push(userId);
if (hasDownvoted) { 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) { if (hasDownvoted) {
suggestion.downvotes = suggestion.downvotes.filter(id => id !== userId); suggestion.downvotes = suggestion.downvotes.filter((id) => id !== userId);
} else { } else {
suggestion.downvotes.push(userId); suggestion.downvotes.push(userId);
if (hasUpvoted) { if (hasUpvoted) {
suggestion.upvotes = suggestion.upvotes.filter(id => id !== userId); suggestion.upvotes = suggestion.upvotes.filter((id) => id !== userId);
} }
} }
} else if (interaction.customId === 'removevote') { } else if (interaction.customId === "removevote") {
suggestion.upvotes = suggestion.upvotes.filter(id => id !== userId); suggestion.upvotes = suggestion.upvotes.filter((id) => id !== userId);
suggestion.downvotes = suggestion.downvotes.filter(id => id !== userId); suggestion.downvotes = suggestion.downvotes.filter((id) => id !== userId);
} }
fs.writeFileSync("./Data/SuggestionData.json", JSON.stringify(suggestions, null, 2)); fs.writeFileSync("./Data/SuggestionData.json", JSON.stringify(suggestions, null, 2));
@@ -228,25 +579,36 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
const upvoteCount = suggestion.upvotes.length; const upvoteCount = suggestion.upvotes.length;
const downvoteCount = suggestion.downvotes.length; const downvoteCount = suggestion.downvotes.length;
const updatedEmbed = new Discord.EmbedBuilder(interaction.message.embeds[0].data) const updatedEmbed = new Discord.EmbedBuilder(interaction.message.embeds[0].data).setFields([
.setFields([ { name: "Suggestion", value: suggestion.suggestion, inline: true },
{ name: 'Suggestion', value: suggestion.suggestion, inline: true }, { name: "From", value: `<@${suggestion.author}>` },
{ name: 'From', value: `<@${suggestion.author}>` }, {
{ name: `${supportbot.Suggestions.UpvoteEmoji} ${supportbot.Suggestions.UpvoteTitle}`, value: `${upvoteCount}`, inline: true }, name: `${supportbot.Suggestions.UpvoteEmoji} ${supportbot.Suggestions.UpvoteTitle}`,
{ name: `${supportbot.Suggestions.DownvoteEmoji} ${supportbot.Suggestions.DownvoteTitle}`, value: `${downvoteCount}`, inline: true } value: `${upvoteCount}`,
]); inline: true,
},
{
name: `${supportbot.Suggestions.DownvoteEmoji} ${supportbot.Suggestions.DownvoteTitle}`,
value: `${downvoteCount}`,
inline: true,
},
]);
await interaction.update({ embeds: [updatedEmbed] }); await interaction.update({ embeds: [updatedEmbed] });
} }
} }
async function handleInvites(interaction, enable) { async function handleInvites(interaction, enable) {
if (supportbot.Ticket.Invites.StaffOnly) { if (supportbot.Ticket.Invites.StaffOnly) {
const { getRole, getChannel } = interaction.client;
const SupportStaff = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild); const SupportStaff = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild);
const Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild); const 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."); return interaction.reply(
"Some roles seem to be missing!\nPlease check for errors when starting the bot."
);
} }
const NoPerms = new Discord.EmbedBuilder() const NoPerms = new Discord.EmbedBuilder()
@@ -256,7 +618,10 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
) )
.setColor(supportbot.Embed.Colours.Warn); .setColor(supportbot.Embed.Colours.Warn);
if (!interaction.member.roles.cache.has(SupportStaff.id) && !interaction.member.roles.cache.has(Admin.id)) { if (
!interaction.member.roles.cache.has(SupportStaff.id) &&
!interaction.member.roles.cache.has(Admin.id)
) {
return interaction.reply({ embeds: [NoPerms] }); return interaction.reply({ embeds: [NoPerms] });
} }
} }
@@ -264,9 +629,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
await interaction.channel.setInvitable(enable); await interaction.channel.setInvitable(enable);
const message = enable ? msgconfig.Ticket.EnableInvites : msgconfig.Ticket.DisableInvites; const message = enable ? msgconfig.Ticket.EnableInvites : msgconfig.Ticket.DisableInvites;
const embed = new Discord.EmbedBuilder() const embed = new Discord.EmbedBuilder().setDescription(message).setColor(supportbot.Embed.Colours.General);
.setDescription(message)
.setColor(supportbot.Embed.Colours.General);
await interaction.reply({ await interaction.reply({
embeds: [embed], embeds: [embed],
@@ -274,5 +637,55 @@ 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,
});
}
}
}); });
+27 -84
View File
@@ -26,7 +26,7 @@ const msgconfig = yaml.load(
const Event = require("../Structures/Event.js"); 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) => { module.exports = new Event("ready", async (client, interaction) => {
const { getRole, getChannel, getCategory } = client; const { getRole, getChannel, getCategory } = client;
@@ -59,7 +59,7 @@ module.exports = new Event("ready", async (client, interaction) => {
}); });
} }
console.log(`\u001b[32m`, `――――――――――――――――――――――――――――――――――――――――――――`); console.log(`\u001b[33m`, `――――――――――――――――――――――――――――――――――――――――――――`);
console.log(` `); console.log(` `);
console.log(`\u001b[31m`, `┏━━━┓╋╋╋╋╋╋╋╋╋╋╋╋╋┏┓┏━━┓╋╋╋┏┓`); console.log(`\u001b[31m`, `┏━━━┓╋╋╋╋╋╋╋╋╋╋╋╋╋┏┓┏━━┓╋╋╋┏┓`);
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(`\u001b[31m`, `╋╋╋╋╋╋╋┗┛╋┗┛`); console.log(`\u001b[31m`, `╋╋╋╋╋╋╋┗┛╋┗┛`);
console.log(` `); 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(` `);
console.log( console.log(`\u001b[33m`, `――――――――――――――――― [Links] ――――――――――――――――――`);
`\u001b[31m`, console.log("\u001b[32m", "Discord: https://dsc.gg/emerald-dev");
`${supportbot.General.Name} | [${supportbot.SupportBot_Version}]`, console.log("\u001b[32m", "Website: https://emeraldsrv.com");
`\u001b[32m`, console.log("\u001b[32m", "Community: https://community.emeraldsrv.com");
`Connected to Discord`, console.log("\u001b[32m", "Documentation: https://docs.emeraldsrv.com");
);
console.log(`\u001b[31m`, `SupportBot proudly created by Emerald Development`);
console.log(` `); console.log(` `);
console.log(`\u001b[33m`, `――――――――――――――――― [Invite] ――――――――――――――――――`);
console.log( console.log("\u001b[32m", `Invite URL: https://discord.com/api/oauth2/authorize?client_id=${client.user.id}&permissions=8&scope=bot%20applications.commands`);
`\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(` `); console.log(` `);
console.log(`\u001b[33m`, `――――――――――――――――― [Config Check] ――――――――――――――――――`);
console.log( console.log("\u001b[32m", "Config initialization...");
`\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`, `――――――――――――――――――――――――――――――――――――――――――――`);
const roles = [ const roles = [
supportbot.Roles.StaffMember.Admin, supportbot.Roles.StaffMember.Admin,
@@ -132,15 +94,13 @@ module.exports = new Event("ready", async (client, interaction) => {
supportbot.Roles.ModRoles.Muted supportbot.Roles.ModRoles.Muted
]; ];
supportbot.Departments.forEach((department) => roles.push(department.role));
if (supportbot.Roles.AutoRole.Role) roles.push(supportbot.Roles.AutoRole.Role); if (supportbot.Roles.AutoRole.Role) roles.push(supportbot.Roles.AutoRole.Role);
const channels = [ const channels = [
supportbot.Suggestions.Channel, supportbot.Suggestions.Channel,
supportbot.Ticket.Log.TicketLog, supportbot.Ticket.Log.TicketLog,
supportbot.Ticket.Log.TranscriptLog, supportbot.Ticket.Log.TranscriptLog,
panelconfig.Channel, supportbot.Ticket.TicketHome,
supportbot.Welcome.Channel, supportbot.Welcome.Channel,
supportbot.Leave.Channel, supportbot.Leave.Channel,
supportbot.Translate.Log supportbot.Translate.Log
@@ -168,35 +128,18 @@ module.exports = new Event("ready", async (client, interaction) => {
// if (!find) missingCat.push(cat); // if (!find) missingCat.push(cat);
// } // }
if (missingR.length > 0) { const missingRoles = await Promise.all(roles.map(role => getRole(role, client.guilds.cache.first())));
console.log( const missingChannels = await Promise.all(channels.map(channel => getChannel(channel, client.guilds.cache.first())));
"\u001b[31m",
`[MISSING CONFIGURATION]`, if (missingRoles.some(role => !role)) {
"\u001b[33m", console.log("\u001b[31m", `Missing roles in your server configuration: ${missingRoles.filter(role => !role).join(', ')}`);
`The following Roles could not be found in your server.\n`, `\u001b[31m`, `[${missingR.join(
", "
)}]`, `\n `,
);
} }
if (missingC.length > 0) { if (missingChannels.some(channel => !channel)) {
console.log( console.log("\u001b[31m", `Missing channels in your server configuration: ${missingChannels.filter(channel => !channel).join(', ')}`);
"\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 `,
);
} }
console.log("\u001b[32m", "Configs initialized, No problems were detected.");
console.log(` `);
}); });
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License 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 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
+10 -2
View File
@@ -13,11 +13,11 @@
</a> </a>
<a href="https://github.com/discordjs"> <a href="https://github.com/discordjs">
<img src="https://img.shields.io/badge/discord.js-v13.6.x-blue.svg?style=for-the-badge&logo=javascript.svg" alt="shield.png"> <img src="https://img.shields.io/badge/DISCORD.JS%20-%20v14-blue?style=for-the-badge" alt="shield.png">
</a> </a>
<a href="https://github.com/nodejs/node"> <a href="https://github.com/nodejs/node">
<img src="https://img.shields.io/badge/node.js-v16.14.x-green.svg?style=for-the-badge&logo=node" alt="shield.png"> <img src="https://img.shields.io/badge/NODE.JS%20-%20v20.x-green?style=for-the-badge" alt="shield.png">
</a> </a>
<a href="https://github.com/Emerald-Services/SupportBot/releases"> <a href="https://github.com/Emerald-Services/SupportBot/releases">
@@ -84,5 +84,13 @@ Released under the [MIT](https://opensource.org/licenses/MIT) license.
<img src="https://contrib.rocks/image?repo=Emerald-Services/SupportBot"> <img src="https://contrib.rocks/image?repo=Emerald-Services/SupportBot">
</a> </a>
## Hosting
You can use our [Pterodactyl egg](https://github.com/Emerald-Services/Pterodactyl-Egg) to host it on your own panel. We currently don't offer hosting for you, altough we might add this in the future.
If you don't know how to install pterodactyl, you could always host it on replit:
[![Run on Repl.it](https://repl.it/badge/github/ItsNyoty/IKI-bot)]([https://repl.it/github/ItsNyoty/iki](https://repl.it/github/Emerald-Services/SupportBot
)
+7 -1
View File
@@ -1,6 +1,6 @@
// SupportBot | Emerald Services // SupportBot | Emerald Services
// Addon Structure // Addon Structure
class Command { class Command {
constructor(options) { constructor(options) {
this.name = options.name; this.name = options.name;
@@ -10,7 +10,13 @@ class Command {
this.run = options.run; this.run = options.run;
this.defaultPermission = true; this.defaultPermission = true;
} }
// Method to dynamically update options
setOptions(options) {
this.options = options;
}
} }
class Event { class Event {
constructor(event, run) { constructor(event, run) {
this.event = event; this.event = event;
+40 -25
View File
@@ -1,12 +1,8 @@
// C:\Users\ollie\Documents\GitHub\SupportBot\Structures\Client.js
// SupportBot | Emerald Services
// Client Structure
const fs = require("fs"); const fs = require("fs");
const Discord = require("discord.js"); const Discord = require("discord.js");
const { GatewayIntentBits, Partials } = require('discord.js'); const { GatewayIntentBits, Partials } = require('discord.js');
const yaml = require("js-yaml"); 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 supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8")); const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
@@ -89,39 +85,52 @@ class Client extends Discord.Client {
tempCommandFiles = tempCommandFiles.filter(item => item !== "Ping.js"); tempCommandFiles = tempCommandFiles.filter(item => item !== "Ping.js");
} }
if (cmdconfig.Ping.Enabled === false) { if (cmdconfig.AddUser.Enabled === false) {
tempCommandFiles = tempCommandFiles.filter(item => item !== "Ping.js"); tempCommandFiles = tempCommandFiles.filter(item => item !== "addUser.js");
} }
if (supportbot.Ticket.ClaimTickets.Enabled === false) { if (cmdconfig.RemoveUser.Enabled === false) {
tempCommandFiles = tempCommandFiles.filter(item => item !== "ticketstats.js"); 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");
}
if (cmdconfig.Settings.Enabled === false) {
tempCommandFiles = tempCommandFiles.filter(item => item !== "settings.js");
}
const commandFiles = tempCommandFiles; const commandFiles = tempCommandFiles;
const commands = commandFiles.map((file) => require(`../Commands/${file}`)); const commands = commandFiles.map((file) => require(`../Commands/${file}`));
// Commands // Load main commands
console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Commands ▬▬▬▬▬▬▬"); console.log(`\u001b[33m`, "――――――――――――――――――――――――――――――――――――――――――――");
commands.forEach((cmd) => { commands.forEach((cmd) => {
console.log(`\u001b[32m`, `[CMD]`, `\u001b[37;1m`, `${cmd.name}`, `\u001b[32;1m`, "Loaded"); console.log(`\u001b[32m`, `[CMD]`, `\u001b[37;1m`, `${cmd.name}`, `\u001b[32;1m`, "Loaded");
this.commands.set(cmd.name, cmd); this.commands.set(cmd.name, cmd);
}); });
console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Commands ▬▬▬▬▬▬▬"); // Load addon commands and events
console.log(`\u001b[33m`, "▬▬▬▬▬▬▬ Commands ▬▬▬▬▬▬▬");
if (supportbot.General.Addons.Enabled) { if (supportbot.General.Addons.Enabled) {
const addonFiles = fs.readdirSync("./Addons").filter((file) => file.endsWith(".js")); const addonFiles = fs.readdirSync("./Addons").filter((file) => file.endsWith(".js"));
const addons = addonFiles.map((file) => { const addons = addonFiles.map((file) => {
let addon = require(`../Addons/${file}`); const addon = require(`../Addons/${file}`);
addon.name = file.split(".")[0]; addon.name = file.split(".")[0];
return addon; return addon;
}); });
// Addons // Addons
console.log(" "); console.log(" ");
console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬"); console.log(`\u001b[33m`, "▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬");
addons.forEach((addon) => { addons.forEach((addon) => {
console.log( console.log(
@@ -132,18 +141,24 @@ class Client extends Discord.Client {
`\u001b[32;1m`, `\u001b[32;1m`,
"Loaded" "Loaded"
); );
addon.commands?.forEach((command) => {
this.commands.set(command.name, command); if (addon instanceof Command) {
}); this.commands.set(addon.name, addon);
addon.events?.forEach((event) => { }
this.on(event.event, (...args) => event.run(this, ...args));
}); 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 ▬▬▬▬▬▬▬"); console.log(`\u001b[33m`, "▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬");
} }
// Slash Commands // Register Slash Commands
this.once("ready", async () => { this.once("ready", async () => {
await this.guilds.cache.first()?.commands.set(this.commands); await this.guilds.cache.first()?.commands.set(this.commands);
@@ -156,7 +171,7 @@ class Client extends Discord.Client {
}); });
console.log(" "); console.log(" ");
console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Events ▬▬▬▬▬▬▬"); console.log(`\u001b[33m`, "▬▬▬▬▬▬▬ Events ▬▬▬▬▬▬▬");
fs.readdirSync("./Events").filter((file) => file.endsWith(".js")).forEach((file) => { fs.readdirSync("./Events").filter((file) => file.endsWith(".js")).forEach((file) => {
const event = require(`../Events/${file}`); const event = require(`../Events/${file}`);
@@ -164,7 +179,7 @@ class Client extends Discord.Client {
this.on(event.event, (...args) => event.run(this, ...args)); 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") { if (process.argv[2] !== "test") {
await this.login(token); await this.login(token);
} else { } else {
+1671
View File
File diff suppressed because it is too large. Load diff
+4 -1
View File
@@ -18,12 +18,15 @@
}, },
"homepage": "https://github.com/Emerald-Services/SupportBot#readme", "homepage": "https://github.com/Emerald-Services/SupportBot#readme",
"dependencies": { "dependencies": {
"@paypal/checkout-server-sdk": "^1.0.3",
"@vitalets/google-translate-api": "^8.0.0", "@vitalets/google-translate-api": "^8.0.0",
"axios": "^1.7.2", "axios": "^1.7.2",
"chatgpt": "^5.2.5", "chatgpt": "^5.2.5",
"discord.js": "^14.13.0", "discord.js": "^14.13.0",
"js": "^0.1.0",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"moment": "^2.29.3", "moment": "^2.29.3",
"openai": "^4.50.0" "ms": "^2.1.3",
"openai": "^4.50.0",
} }
} }