Compare commits

..
16 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
ItsNyoty 48500fe8a4 Update README.md 2024-07-03 13:42:46 +02:00
38 changed files with 3376 additions and 1251 deletions

No files matched your search

-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 yaml = require("js-yaml");
const supportbot = yaml.load(
fs.readFileSync("./Configs/supportbot.yml", "utf8")
);
const cmdconfig = yaml.load(
fs.readFileSync("./Configs/commands.yml", "utf8")
);
const msgconfig = yaml.load(
fs.readFileSync("./Configs/messages.yml", "utf8")
);
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
const Command = require("../Structures/Command.js");
@@ -35,28 +29,25 @@ module.exports = new Command({
let Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild);
if (!SupportStaff || !Admin)
return interaction.reply(
"Some roles seem to be missing!\nPlease check for errors when starting the bot."
);
return interaction.reply("Some roles seem to be missing!\nPlease check for errors when starting the bot.");
const NoPerms = new Discord.EmbedBuilder()
.setTitle("Invalid Permissions!")
.setDescription(
`${msgconfig.Error.IncorrectPerms}\n\nRole Required: \`${supportbot.Roles.StaffMember.Staff}\` or \`${supportbot.Roles.StaffMember.Admin}\``
)
.setDescription(`${msgconfig.Error.IncorrectPerms}\n\nRole Required: \`${supportbot.Roles.StaffMember.Staff}\` or \`${supportbot.Roles.StaffMember.Admin}\``)
.setColor(supportbot.Embed.Colours.Warn);
if (
!interaction.member.roles.cache.has(SupportStaff.id) &&
!interaction.member.roles.cache.has(Admin.id)
)
if (!interaction.member.roles.cache.has(SupportStaff.id) && !interaction.member.roles.cache.has(Admin.id))
return interaction.reply({ embeds: [NoPerms] });
}
if (interaction.channel.type !== Discord.ChannelType.PrivateThread) {
// Check if it's a thread or channel based on config
if (
(supportbot.Ticket.TicketType === "threads" && interaction.channel.type !== Discord.ChannelType.PrivateThread) ||
(supportbot.Ticket.TicketType === "channels" && interaction.channel.type !== Discord.ChannelType.GuildText)
) {
const NotTicketChannel = new Discord.EmbedBuilder()
.setTitle("Invalid Channel!")
.setDescription("This command can only be used in a ticket thread.")
.setDescription(`This command can only be used in a ${supportbot.Ticket.TicketType === "threads" ? "ticket thread" : "ticket channel"}.`)
.setColor(supportbot.Embed.Colours.Warn);
return interaction.reply({ embeds: [NotTicketChannel], ephemeral: true });
@@ -81,15 +72,14 @@ module.exports = new Command({
const CloseButton = new Discord.ButtonBuilder()
.setCustomId("confirmCloseTicket")
.setLabel(supportbot.Ticket.Close.Confirmation_Button)
.setStyle(Discord.ButtonStyle.Danger);
.setEmoji(supportbot.Ticket.Close.Confirmation_Emoji)
.setStyle(supportbot.Ticket.Close.Confirmation_Style);
const row = new Discord.ActionRowBuilder().addComponents(CloseButton);
const CloseTicketRequest = new Discord.EmbedBuilder()
.setTitle(`**${supportbot.Ticket.Close.Title}**`)
.setDescription(
`${msgconfig.Ticket.ConfirmClose}`
)
.setDescription(`${msgconfig.Ticket.ConfirmClose}`)
.setColor(supportbot.Embed.Colours.General);
await interaction.followUp({
@@ -116,10 +106,9 @@ async function handleCloseTicket(interaction, reason, ticket, TicketData) {
let tickets = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
let tUser = interaction.client.users.cache.get(ticket.user);
let transcriptChannel = await getChannel(supportbot.Ticket.Log.TranscriptLog, interaction.guild);
let logChannel = await getChannel(supportbot.Ticket.Log.TicketLog, interaction.guild);
let transcriptChannel = await getChannel(supportbot.Ticket.Log.TicketDataLog, interaction.guild);
if (!transcriptChannel || !logChannel)
if (!transcriptChannel)
return interaction.followUp("Some Channels seem to be missing!");
try {
@@ -133,22 +122,8 @@ async function handleCloseTicket(interaction, reason, ticket, TicketData) {
);
const transcriptEmbed = new Discord.EmbedBuilder()
.setTitle(supportbot.Ticket.Log.TranscriptTitle)
.setColor(supportbot.Embed.Colours.General)
.setFooter({
text: supportbot.Embed.Footer,
iconURL: interaction.user.displayAvatarURL(),
})
.addFields(
{ name: "Ticket", value: `${interaction.channel.name} (${interaction.channel.id})` },
{ name: "User", value: `${tUser?.username || "N/A"}#${tUser?.discriminator || "N/A"} (${tUser?.id || ticket.user})` },
{ name: "Closed By", value: interaction.user.tag },
{ name: "Reason", value: reason }
);
const logEmbed = new Discord.EmbedBuilder()
.setTitle(supportbot.Ticket.Log.TicketLog_Title)
.setColor(supportbot.Embed.Colours.General)
.setTitle(msgconfig.TicketLog.Title)
.setColor(msgconfig.TicketLog.Colour)
.setFooter({
text: supportbot.Embed.Footer,
iconURL: interaction.user.displayAvatarURL(),
@@ -262,12 +237,6 @@ async function handleCloseTicket(interaction, reason, ticket, TicketData) {
</html>
`;
if (!supportbot.Ticket.Log.DisableTicketLogChannel) {
await logChannel.send({ embeds: [logEmbed] }).catch((err) => {
console.error(err);
});
}
let file = new Discord.AttachmentBuilder(
Buffer.from(html),
{ name: fileName }
+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,
});
}
},
});
-79
View File
@@ -1,79 +0,0 @@
const fs = require("fs");
const Discord = require("discord.js");
const yaml = require("js-yaml");
const generateImage = require('../Structures/generateImage.js');
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
const Command = require("../Structures/Command.js");
module.exports = new Command({
name: "image",
description: "Create an image through SupportBot AI",
type: Discord.ApplicationCommandType.ChatInput,
options: [
{
name: "prompt",
description: "The prompt to generate an image from",
type: Discord.ApplicationCommandOptionType.String,
required: true,
},
{
name: "negativeprompt",
description: "The negative prompt to exclude from the image",
type: Discord.ApplicationCommandOptionType.String,
required: false,
},
{
name: "cfgscale",
description: "The cfg scale for the image generation",
type: Discord.ApplicationCommandOptionType.Number,
required: false,
}
],
permissions: "SendMessages",
async run(interaction) {
const prompt = interaction.options.getString("prompt");
const negativePrompt = interaction.options.getString("negativeprompt") || "";
const cfgScale = interaction.options.getNumber("cfgscale") || 7.5; // Default value
try {
const imageUrl = await generateImage(prompt, negativePrompt, cfgScale);
const ImageEmbed = new Discord.EmbedBuilder()
.setTitle("Generated Image")
.setDescription(`Here is the image generated for your prompt: "${prompt}"`)
.setImage(imageUrl)
.setColor(supportbot.Embed.Colours.General)
.setFooter({
text: supportbot.Embed.Footer,
iconURL: interaction.user.displayAvatarURL(),
});
if (!interaction.replied) {
await interaction.reply({
ephemeral: true,
embeds: [ImageEmbed],
});
} else {
await interaction.followUp({
ephemeral: true,
embeds: [ImageEmbed],
});
}
} catch (error) {
console.error('Error generating image:', error);
if (!interaction.replied) {
await interaction.reply({
ephemeral: true,
content: `An error occurred while generating the image: ${error.message}. Please try again later.`,
});
} else {
await interaction.followUp({
ephemeral: true,
content: `An error occurred while generating the image: ${error.message}. Please try again later.`,
});
}
}
},
});
+7 -6
View File
@@ -10,9 +10,9 @@ const Command = require("../Structures/Command.js");
const clockedInUsers = new Set();
async function getRole(roleName, guild) {
return guild.roles.cache.find(role => role.name === roleName);
}
// async function getRole(roleName, guild) {
// return guild.roles.cache.find(role => role.name === roleName);
// }
function updateClockedInStatus(profileEmbed, clockedIn) {
profileEmbed.data.fields = profileEmbed.data.fields.map(field => {
@@ -25,10 +25,10 @@ function updateClockedInStatus(profileEmbed, clockedIn) {
}
module.exports = new Command({
name: 'profile',
description: 'Show user profile',
name: cmdconfig.Profile.Command,
description: cmdconfig.Profile.Description,
type: ApplicationCommandType.ChatInput,
permissions: ["SendMessages"],
permissions: cmdconfig.Profile.Permission,
options: [
{
name: 'user',
@@ -47,6 +47,7 @@ module.exports = new Command({
const interactionUserId = interaction.user.id;
const user = interaction.guild.members.cache.get(viewingUserId);
const { getRole, getChannel } = interaction.client;
const Staff = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild);
const Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild);
+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 });
}
},
});
+133 -114
View File
@@ -8,7 +8,7 @@ const {
ButtonBuilder,
StringSelectMenuBuilder,
StringSelectMenuOptionBuilder,
ButtonStyle
ButtonStyle,
} = require("discord.js");
const yaml = require("js-yaml");
@@ -22,7 +22,7 @@ const TicketNumberID = require("../Structures/TicketID.js");
async function getClockedInUsers(guild) {
const clockedInUsers = new Set();
const profilesDir = './Data/Profiles';
const profileFiles = fs.readdirSync(profilesDir);
@@ -38,19 +38,16 @@ async function getClockedInUsers(guild) {
}
async function assignTicketToUser(ticketChannel, Staff, Admin, interaction, attempts = 0) {
console.log(`Assigning ticket attempt ${attempts}`);
try {
const clockedInUsers = await getClockedInUsers(interaction.guild);
console.log(`Clocked in users: ${Array.from(clockedInUsers).join(', ')}`);
if (clockedInUsers.size === 0 || attempts >= 2) {
console.log("No clocked in users or max attempts reached");
const claimChannel = await interaction.guild.channels.cache.get(supportbot.Ticket.ClaimTickets.Channel);
if (claimChannel) {
const claimEmbed = new EmbedBuilder()
.setTitle(msgconfig.Ticket.ClaimTickets.ClaimTitle || 'Default Claim Title')
.setDescription(msgconfig.Ticket.ClaimTickets.ClaimMessage || 'Default Claim Message')
.setTitle(msgconfig.Ticket.ClaimTickets.ClaimTitle)
.setDescription(msgconfig.Ticket.ClaimTickets.ClaimMessage)
.setColor(supportbot.Embed.Colours.General);
const claimButton = new ButtonBuilder()
@@ -70,17 +67,15 @@ async function assignTicketToUser(ticketChannel, Staff, Admin, interaction, atte
const collector = claimMessage.createMessageComponentCollector({ filter, time: 120000 });
collector.on('collect', async i => {
console.log(`Ticket claimed by user ${i.user.id}`);
await ticketChannel.send({ content: `<@${i.user.id}> has claimed the ticket.` });
});
collector.on('end', async collected => {
if (collected.size === 0) {
console.log("No one claimed the ticket, notifying admins");
await claimMessage.delete();
await claimChannel.send({
content: `No one claimed the ticket. Tagging roles: <@&${Staff.id}> <@&${Admin.id}>`,
content: `Ticket is still unclaimed. \nCC: [<@&${Staff.id}> <@&${Admin.id}]`,
embeds: [new EmbedBuilder().setDescription(`Ticket is still unclaimed: <#${ticketChannel.id}>`).setColor(supportbot.Embed.Colours.Warn)],
});
}
@@ -96,11 +91,10 @@ async function assignTicketToUser(ticketChannel, Staff, Admin, interaction, atte
const clockedInArray = Array.from(clockedInUsers);
const assignedUserId = clockedInArray[Math.floor(Math.random() * clockedInArray.length)];
const assignedUser = await interaction.guild.members.fetch(assignedUserId);
console.log(`Assigned user: ${assignedUserId}`);
const claimEmbed = new EmbedBuilder()
.setTitle(msgconfig.Ticket.ClaimTickets.ClaimTitle || 'Default Claim Title')
.setDescription(msgconfig.Ticket.ClaimTickets.ClaimMessage.replace('%user%', interaction.user.id) || 'Default Claim Message')
.setTitle(msgconfig.Ticket.ClaimTickets.ClaimTitle)
.setDescription(msgconfig.Ticket.ClaimTickets.ClaimMessage.replace('%user%', interaction.user.id))
.setColor(supportbot.Embed.Colours.General);
const claimButton = new ButtonBuilder()
@@ -126,13 +120,11 @@ async function assignTicketToUser(ticketChannel, Staff, Admin, interaction, atte
const collector = claimMessage.createMessageComponentCollector({ filter, time: 120000 });
collector.on('collect', async i => {
console.log(`Ticket claimed by user ${i.user.id}`);
await ticketChannel.send({ content: `<@${assignedUserId}>` });
await ticketChannel.send({ content: `<@${assignedUserId}>` }).then(msg => setTimeout(() => msg.delete(), 5000));
});
collector.on('end', async collected => {
if (collected.size === 0) {
console.log("No one claimed the ticket, attempting reassignment");
await claimMessage.delete();
if (attempts >= 1) {
@@ -141,7 +133,6 @@ async function assignTicketToUser(ticketChannel, Staff, Admin, interaction, atte
embeds: [new EmbedBuilder().setDescription(`Ticket is still unclaimed: <#${ticketChannel.id}>`).setColor(supportbot.Embed.Colours.Warn)],
});
// Add a claim button for any staff member to click
const claimEmbed = new EmbedBuilder()
.setTitle(msgconfig.Ticket.ClaimTickets.ClaimTitle)
.setDescription(msgconfig.Ticket.ClaimTickets.ClaimMessage)
@@ -156,7 +147,6 @@ async function assignTicketToUser(ticketChannel, Staff, Admin, interaction, atte
const row = new ActionRowBuilder().addComponents(claimButton);
const claimMessage = await claimChannel.send({
content: `<@&${Staff.id}> <@&${Admin.id}>`,
embeds: [claimEmbed],
components: [row],
});
@@ -201,10 +191,6 @@ async function assignTicketToUser(ticketChannel, Staff, Admin, interaction, atte
}
}
module.exports = new Command({
name: cmdconfig.OpenTicket.Command,
description: cmdconfig.OpenTicket.Description,
@@ -220,7 +206,11 @@ module.exports = new Command({
async run(interaction) {
try {
let department = interaction.customId?.split("-")[1] || null;
let TicketReason = null;
if (supportbot.Ticket.TicketReason) {
TicketReason = interaction.reason || null; // Retrieve the reason from the interaction object
}
let TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
const { getRole, getChannel } = interaction.client;
let User = interaction.guild.members.cache.get(interaction.user.id);
@@ -256,7 +246,7 @@ module.exports = new Command({
}
let ticketNumberID = await TicketNumberID.pad();
const TicketSubject = interaction.options?.getString("reason") || msgconfig.Ticket.InvalidSubject;
const TicketSubject = TicketReason || msgconfig.Ticket.InvalidSubject;
const TicketExists = new EmbedBuilder()
.setTitle("Ticket Exists!")
@@ -281,33 +271,87 @@ module.exports = new Command({
ephemeral: true,
});
const TicketCategory = interaction.guild.channels.cache.find(
(c) => c.name === supportbot.Ticket.TicketCategory || c.id === supportbot.Ticket.TicketCategory
);
let ticketChannel;
const channel = await getChannel(supportbot.Ticket.TicketCategory, interaction.guild);
const ticketChannel = await channel.threads.create({
name: `${supportbot.Ticket.Channel}${ticketNumberID}`,
type: ChannelType.PrivateThread,
parent: TicketCategory,
autoArchiveDuration: 60,
reason: 'support ticket',
});
if (supportbot.Ticket.TicketType === "threads") {
const TicketHome = interaction.guild.channels.cache.find(
(c) => c.name === supportbot.Ticket.TicketHome || c.id === supportbot.Ticket.TicketHome
);
const channel = await getChannel(supportbot.Ticket.TicketHome, interaction.guild);
ticketChannel = await channel.threads.create({
name: `${supportbot.Ticket.Channel}${ticketNumberID}`,
type: ChannelType.PrivateThread,
parent: TicketHome,
autoArchiveDuration: 60,
reason: 'support ticket',
});
} else if (supportbot.Ticket.TicketType === "channels") {
const category = interaction.guild.channels.cache.find(
(c) => c.name === supportbot.Ticket.TicketChannelsCategory || c.id === supportbot.Ticket.TicketChannelsCategory
);
if (!category) {
return interaction.reply({
content: "The ticket category does not exist!",
ephemeral: true,
});
}
ticketChannel = await interaction.guild.channels.create({
name: `${supportbot.Ticket.Channel}${ticketNumberID}`,
type: ChannelType.GuildText,
parent: category.id,
reason: 'support ticket',
});
// Move to secondary category if the primary one is full
if (category.children.size >= 50) {
const secondaryCategory = interaction.guild.channels.cache.find(
(c) => c.name === supportbot.Ticket.TicketChannelsCategory2 || c.id === supportbot.Ticket.TicketChannelsCategory2
);
if (secondaryCategory) {
await ticketChannel.setParent(secondaryCategory.id);
} else {
return interaction.reply({
content: "The secondary ticket category does not exist!",
ephemeral: true,
});
}
}
}
// Assign ticket to a user if claiming tickets is enabled
if (supportbot.Ticket.ClaimTickets.Enabled) {
const assignedUserId = await assignTicketToUser(ticketChannel, Staff, Admin, interaction);
if (!assignedUserId) {
const claimChannel = await interaction.guild.channels.cache.get(supportbot.Ticket.ClaimTickets.Channel);
await claimChannel.send(`<@&${Staff.id}> <@&${Admin.id}>`);
}
}
// Add user to the ticket channel
if (!department) {
await ticketChannel.members.add(interaction.user.id);
}
if (supportbot.Ticket.TicketType === "threads") {
await ticketChannel.members.add(interaction.user.id);
} else if (supportbot.Ticket.TicketType === "channels") {
await ticketChannel.permissionOverwrites.create(interaction.user.id, {
ViewChannel: true,
SendMessages: true,
ReadMessageHistory: true,
});
await ticketChannel.permissionOverwrites.create(Admin, {
ViewChannel: true,
SendMessages: true,
ReadMessageHistory: true,
});
await ticketChannel.permissionOverwrites.create(interaction.guild.roles.everyone, {
ViewChannel: false,
});
}
// Save ticket data
TicketData.tickets.push({
id: ticketChannel.id,
@@ -335,6 +379,7 @@ module.exports = new Command({
msgconfig.Ticket.TicketCreatedAlert.replace(/%ticketauthor%/g, interaction.user.id)
.replace(/%ticketid%/g, ticketChannel.id)
.replace(/%ticketusername%/g, interaction.user.username)
.replace(/%ticketreason%/g, TicketSubject)
)
.setColor(supportbot.Embed.Colours.General);
await interaction.reply({ embeds: [CreatedTicket], ephemeral: true });
@@ -356,94 +401,68 @@ module.exports = new Command({
msgconfig.Ticket.TicketMessage.replace(/%ticketauthor%/g, interaction.user.id)
.replace(/%ticketid%/g, ticketChannel.id)
.replace(/%ticketusername%/g, interaction.user.username)
.replace(/%ticketreason%/g, TicketSubject)
)
.setColor(supportbot.Embed.Colours.General);
// Add reason field if ticket reason is enabled
if (supportbot.Ticket.TicketReason && TicketReason) {
TicketMessage.addFields(
{ name: "Reason", value: TicketReason, inline: false }
);
}
// Create the control panel
const SelectMenus = new StringSelectMenuBuilder()
.setCustomId("ticketcontrolpanel")
.setPlaceholder("Ticket Control Panel")
.addOptions(
.setCustomId("ticketcontrolpanel")
.setPlaceholder("Ticket Control Panel")
.addOptions(
new StringSelectMenuOptionBuilder()
.setLabel("Close")
.setDescription("Close the ticket.")
.setEmoji(supportbot.SelectMenus.Tickets.CloseEmoji)
.setValue("ticketclose"),
new StringSelectMenuOptionBuilder()
.setLabel("Archive")
.setDescription("Archive the ticket.")
.setEmoji(supportbot.SelectMenus.Tickets.ArchiveEmoji)
.setValue("archiveticket"),
...(supportbot.Ticket.TicketType === "threads" ? [
new StringSelectMenuOptionBuilder()
.setLabel("Add User")
.setDescription("Add a user to the ticket.")
.setEmoji(supportbot.SelectMenus.Tickets.AddUserEmoji)
.setValue("ticketadduser"),
new StringSelectMenuOptionBuilder()
.setLabel("Remove User")
.setDescription("Remove a user from the ticket.")
.setEmoji(supportbot.SelectMenus.Tickets.RemoveUserEmoji)
.setValue("ticketremoveuser"),
new StringSelectMenuOptionBuilder()
.setLabel("Close")
.setDescription("Close the ticket.")
.setEmoji(supportbot.SelectMenus.Tickets.CloseEmoji)
.setValue("ticketclose"),
new StringSelectMenuOptionBuilder()
.setLabel("Enable Invites")
.setDescription("Enable invites.")
.setEmoji(supportbot.SelectMenus.Tickets.EnableInvitesEmoji)
.setValue("enableinvites"),
new StringSelectMenuOptionBuilder()
.setLabel("Disable Invites")
.setDescription("Disable invites.")
.setEmoji(supportbot.SelectMenus.Tickets.DisableInvitesEmoji)
.setValue("disableinvites"),
);
.setLabel("Lock Ticket")
.setDescription("Lock the ticket.")
.setEmoji(supportbot.SelectMenus.Tickets.LockEmoji)
.setValue("lockticket")
] : []),
new StringSelectMenuOptionBuilder()
.setLabel("Rename Ticket")
.setDescription("Rename the ticket.")
.setEmoji(supportbot.SelectMenus.Tickets.RenameEmoji)
.setValue("renameticket")
);
if (supportbot.Ticket.TicketType === "threads") {
SelectMenus.addOptions(
new StringSelectMenuOptionBuilder()
.setLabel("Enable Invites")
.setDescription("Enable invites.")
.setEmoji(supportbot.SelectMenus.Tickets.EnableInvitesEmoji)
.setValue("enableinvites"),
new StringSelectMenuOptionBuilder()
.setLabel("Disable Invites")
.setDescription("Disable invites.")
.setEmoji(supportbot.SelectMenus.Tickets.DisableInvitesEmoji)
.setValue("disableinvites"),
);
}
const row2 = new ActionRowBuilder().addComponents(SelectMenus);
// Send the ticket message and control panel
await ticketChannel.send({
embeds: [TicketMessage],
components: [row2],
});
if (!department) {
try {
let buttons = supportbot.Departments.map((x) =>
new ButtonBuilder()
.setCustomId("Department" + supportbot.Departments.indexOf(x))
.setLabel(x.title)
.setStyle(x.color)
.setEmoji(x.emoji)
);
const row = new ActionRowBuilder().addComponents(buttons);
try {
const filter = (i) => i.user.id === interaction.user.id;
await m.awaitMessageComponent({
filter,
max: 1,
componentType: "BUTTON",
time: supportbot.Ticket.Timeout * 60000,
});
} catch (e) {
if (e.code === "COLLECTOR_ERROR") {
try {
let ticket = TicketData.tickets.findIndex((t) => t.id === ticketChannel.id);
TicketData.tickets[ticket].open = false;
fs.writeFileSync(
"./Data/TicketData.json",
JSON.stringify(TicketData, null, 4),
(err) => {
if (err) console.error(err);
}
);
interaction.user.send(
"Your ticket has timed out. Please open a new one, and select a department."
);
} catch (error) {return assignedUserId;
console.error("An error occurred while handling collector:", error);
}
return await ticketChannel.delete();
}
}
} catch (error) {
console.error("Error sending message:", error);
}
}
} catch (error) {
console.error("Error in run method:", error);
}
+7 -2
View File
@@ -35,8 +35,13 @@ module.exports = new Command({
});
}
const channelName = panelconfig.Channel;
const channel = interaction.guild.channels.cache.find(ch => ch.name === channelName);
const { getChannel } = interaction.client;
const channel = await getChannel(
supportbot.Ticket.TicketHome,
interaction.guild
);
if (!channel) {
console.log(`[TICKET PANEL] ${channelName} channel not found. Please check your config file.`);
return interaction.reply({
+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 text = await interaction.options.getString("text"); // Grab the text to translate by the user
let translatelog = await getChannel(
supportbot.Translate.Log,
supportbot.Translate.TranslateLog,
interaction.guild
); // Grab the set logging channel for translations
-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
# If you don't want to mess with this configuration, You can manage commands via Discord Settings the Intregations.
Help:
Command: "help"
Description: "Get a list of all the commands."
Permission: ["SendMessages"]
Enabled: true
EnableAI:
Command: "enableai"
Description: "Talk with SupportBot"
Permission: ["SendMessages"]
Enabled: true
Info:
Command: "info"
Description: "Get information about the **ServerName**."
@@ -37,9 +33,9 @@ TicketPanel:
Permission: ["Administrator"]
Enabled: true
TicketStats:
Command: "ticketstats"
Description: "Shows staff members their ticket statistics"
Profile:
Command: "profile"
Description: "Shows the profile for a discord member."
Permission: ["SendMessages"]
# Command is automatically disabled when the TicketClaims is disabled.
@@ -49,9 +45,21 @@ OpenTicket:
Permission: ["SendMessages"]
Enabled: true
Add:
AddUser:
Command: "add"
Description: "Add a user to the ticket."
Description: "Add a user to a support ticket."
Permission: ["SendMessages"]
Enabled: true
ForceAddUser:
Command: "forceadd"
Description: "Add a user to a support ticket by bypassing the invite system."
Permission: ["SendMessages"]
Enabled: true
RemoveUser:
Command: "remove"
Description: "Remove a user from a support ticket."
Permission: ["SendMessages"]
Enabled: true
@@ -91,6 +99,16 @@ Ping:
Permission: ["SendMessages"]
Enabled: true
# ---------------------------------------
# Bot Administrator Commands
# ---------------------------------------
Settings:
Command: "settings"
Description: "Update bot settings."
Permission: ["Administrator"]
Enabled: true
# ---------------------------------------
# Command Description Configuration
# ---------------------------------------
+99 -37
View File
@@ -17,14 +17,14 @@ Info:
Title: "About Us"
Description: "SupportBot is an open-sourced support system for discord. It's completely free to use with high amounts of customization to match your own brand."
Button: "Visit Site"
URL: "https://emeraldsrv.com"
URL: "https://docs.emeraldsrv.com"
Colour: "#f74343"
Welcome:
Embed:
Colour: "#f74343"
Title: "Welcome to ServerName"
Message: "> :smile: %joined_user% has just landed!"
Message: ":smile: %joined_user% has just landed!"
Thumbnail: "BOT"
ImageEnabled: false
@@ -34,7 +34,7 @@ Leave:
Embed:
Colour: "#f74343"
Title: "Goodbye!"
Message: "> :slight_frown: %joined_user% has recently dropped out!"
Message: ":slight_frown: %joined_user% has recently dropped out!"
Thumbnail: "BOT"
ImageEnabled: false
@@ -42,8 +42,8 @@ Leave:
Suggestions:
Sent_Title: "Suggestion Sent!"
Sent: "> :white_check_mark: **You have successfully sent a suggestion.**"
NoSelfVote: "> :x: **You cannot vote on your own suggestion.**"
Sent: ":white_check_mark: **You have successfully sent a suggestion.**"
NoSelfVote: ":x: **You cannot vote on your own suggestion.**"
RemoveVoteButton: "Remove Vote"
Ticket:
@@ -56,50 +56,44 @@ Ticket:
TicketAuthorTitle: "%ticketusername%'s Ticket"
TicketTitle: "Support Ticket"
TicketMessage: "> Hello <@%ticketauthor%>, Thank you for creating a support ticket\nPlease wait patiently whilst a member of our team reaches out to you."
TicketMessage: "Hello <@%ticketauthor%>, Thank you for creating a support ticket\nPlease wait patiently whilst a member of our team reaches out to you."
TicketThumbnail: "https://images.emojiterra.com/google/android-pie/512px/1f39f.png"
TicketCreatedAlert: "> :ticket: Ticked Created: <#%ticketid%>"
TicketExists: "> ❌ Ticket already exists"
TicketClaim: "> :white_check_mark: Ticket claimed by" #The user who claimed the ticket will be added to the end of this message
TicketUnclaim: "> :white_check_mark: Ticket unclaimed by" #The user who claimed the ticket will be added to the end of this message
TicketCreatedAlert: ":ticket: Ticked Created: <#%ticketid%>"
TicketExists: "❌ Ticket already exists"
TicketClaim: ":white_check_mark: Ticket claimed by" #The user who claimed the ticket will be added to the end of this message
TicketUnclaim: ":white_check_mark: Ticket unclaimed by" #The user who claimed the ticket will be added to the end of this message
DisableInvites: "> 🔒 Invites have been disabled for this ticket."
EnableInvites: "> 🔓 Invites have been enabled for this ticket."
TicketArchived: ":white_check_mark: Ticket archived."
TicketUnarchived: ":white_check_mark: Ticket unarchived."
TicketLocked: ":lock: Ticket is locked."
DisableInvites: "🔒 Invites have been disabled for this ticket."
EnableInvites: "🔓 Invites have been enabled for this ticket."
# Available Placeholders:
# <@%user%> - Tags the user who was added to the ticket.
AddUserToTicket_Title: "➕ Add User!"
AddUserToTicket: "> To add a user to this ticket, Tag them."
Blacklisted: ":x: You are not allowed to open a ticket"
Muted: ":x: You are `Muted` and therefore not allowed to open any tickets"
InvalidSubject: "No Reason Provided!"
RemovedUserFromTicket_Title: "➖ Removed User!"
RemovedUserFromTicket: "> <@%user%> has been `Removed` from this ticket"
ConfirmClose: "Please confirm closing the ticket by clicking the button below."
CloseTimeout: "Ticket close confirmation timed out."
Blacklisted: "> :x: You are not allowed to open a ticket"
Muted: "> :x: You are `Muted` and therefore not allowed to open any tickets"
InvalidSubject: "> No Reason Provided!"
TranscriptSavedMessage: "📝 **Transcript Saved!**"
TicketLockMessage: "❌ **Ticket Locked!**"
TicketDeleteMessage: "⚠ **Ticket Deleting!**"
ClosingTicket: "> :warning: You have requested to close your support ticket."
ConfirmClose: "> Please confirm closing the ticket by clicking the button below."
CloseTimeout: "> Ticket close confirmation timed out."
RenamedSuccess: "🏷️ Ticket renamed successfully!"
ClaimTickets:
ClaimTitle: "Incoming Support Request!"
ClaimMessage: "> A new ticket has been created by <@!%user%>. Click the button below to claim this ticket."
ClaimMessage_Edit: "> <#%channel%> was claimed by <@!%user%>."
ClaimMessage: "A new ticket has been created by <@!%user%>. Click the button below to claim this ticket."
ClaimMessage_Edit: "<#%channel%> was claimed by <@!%user%>."
ClaimedTitle: "Ticket Claimed!"
Claimed: "> You have successfully claimed this ticket. <#%channel%>"
Claimed: "You have successfully claimed this ticket. <#%channel%>"
NoPermsToClaimTitle: "Error, no permissions!"
NoPermsToClaim: "> :x: **Err!** You do not permissions to claim this ticket."
NoPermsToClaim: ":x: **Err!** You do not permissions to claim this ticket."
ReassigningTitle: "Reassigning!..."
ReassigningMessage: "> <@%user%> did not claim the ticket. **Reassigning...**"
ReassigningMessage: "<@%user%> did not claim the ticket. **Reassigning...**"
TicketStats:
OpenTickets: "✅ Open Tickets"
@@ -108,12 +102,80 @@ TicketStats:
Title: "Your Ticket Stats"
TicketLog:
Title: "Ticket Log"
Colour: "#0cc559"
AddUser:
NotInTicket_Title: "Ticket Invitation"
NotInTicket_Description: "This command can only be used within a ticket thread."
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:
IncorrectPerms: "> :x: **Err!** You do not have the correct permissions to use this."
InvalidChannel: "> :x: **Err!** Channel not found. Please create it!"
UserNotFound: "> :x: **Err!** This user doesn't exist, Are they in this server?"
NoValidTicket: "> :warning: You cannot execute this command here."
TicketExists: "> :warning: This ticket cannot be opened, It already exists!"
IncorrectPerms: ":x: **Err!** You do not have the correct permissions to use this."
InvalidChannel: ":x: **Err!** Channel not found. Please create it!"
UserNotFound: ":x: **Err!** This user doesn't exist, Are they in this server?"
NoValidTicket: ":warning: You cannot execute this command here."
TicketExists: ":warning: This ticket cannot be opened, It already exists!"
+4 -5
View File
@@ -10,23 +10,22 @@ Enabled: true
General:
Name: "SupportBot AI"
# What would you like to call your AI
Model: "gpt-4o"
Model: "gpt-4o-mini"
# You can see a list of OpenAI Models here: https://platform.openai.com/docs/models
Tokens: 400
# Max tokens you can set is 400
# For more information about tokens, go to https://openai.com/api/pricing/
OpenAI_Key: "OPEN AI KEY"
OpenAI_Key: "OPEN_AI_API_KEY"
# To create an OpenAI API key, go to https://platform.openai.com/api-keys
PastebinAPI_Key: "PASTEBIN KEY"
PastebinAPI_Key: "PASTEBIN_API_KEY"
# You can get your Pastebin API Key from https://pastebin.com
PastebinAPI_URL: "https://pastebin.com/api/api_post.php"
Channels:
AIChannel: "1250190338954498048"
AIChannel: "AI_BOT_CHANNEL"
# This is the channel where AI will respond in without needing to mention the bot.
Embed:
+61 -39
View File
@@ -11,7 +11,7 @@
# Official Documentation: https://github.com/Emerald-Services/SupportBot/wiki
# Third-Party Documentation: COMING SOON
SupportBot_Version: "v8.0.1"
SupportBot_Version: "v8.2"
# Do not change the version number
# -------------------------
@@ -20,7 +20,7 @@ SupportBot_Version: "v8.0.1"
General:
Name: "SupportBot"
Token: "BOT TOKEN HERE"
Token: "BOT_TOKEN_HERE"
# To get your Discord Bot Token, Go to https://discord.com/developers/applications
Addons:
@@ -37,6 +37,8 @@ Activity:
StreamingURL: "https://www.twitch.tv/YOUR_TWITCH_NAME"
# Set The Type to "Streaming" for a streaming status!
# You can update the activity via Discord using: /settings
# -------------------------
# Embeds
# -------------------------
@@ -57,15 +59,15 @@ Embed:
Roles:
AutoRole:
Enabled: true
Role: "Member"
Role: "ROLE_ID"
StaffMember:
Staff: "Staff"
Admin: "Admin"
Staff: "STAFF_ROLE_ID"
Admin: "ADMIN_ROLE_ID"
ModRoles:
Muted: "Muted"
Blacklisted: "Blacklisted"
Muted: "MUTED_ROLE_ID"
Blacklisted: "BLACKLISTED_ROLE_ID"
# ----------------------------
# System Messages
@@ -73,18 +75,18 @@ Roles:
Welcome:
Enabled: true
Channel: "🚨┃join-leave-logs"
Channel: "WELCOME_CHANNEL_ID"
Leave:
Enabled: true
Channel: "🚨┃join-leave-logs"
Channel: "LEAVE_CHANNEL_ID"
# ----------------------------
# Suggestions
# ----------------------------
Suggestions:
Channel: "📗┃suggestions"
Channel: "SUGGEST_CHANNEL_ID"
UpvoteEmoji: "✅"
DownvoteEmoji: "❌"
@@ -119,8 +121,23 @@ Suggestions:
# -------------------------
Ticket:
TicketCategory: "📗┃tickets" #Where tickets normally go with the /ticket command
# This is the channel name for the ticket threads to be created, NOT A DISCORD CATEGORY!
TicketType: "channels" # Choose how you want tickets to be created.
# Options:
# channels - Tickets will be created as channels in the
# threads - Tickets will be created as threads through the "TicketHome"
# For TicketType "Threads"
TicketHome: "TICKETHOME_CHANNEL_ID"
# What is TicketHome?
# - Channel where the Ticket creation panel is sent.
# - Channel where ticket threads are made. (only if ticket threads is enabled above.)
# For TicketType "Channels"
TicketChannelsCategory: "TICKET_CATEGORY_ID" # if Type is set to channels
TicketChannelsCategory2: "BACKUP_TICKET_CATEGORY_ID" # This will be used if the first category has reached the max channel limit.
TicketArchiveCategory: "ARCHIVE_TICKET_CATEGORY_ID"
# ---
Channel: "ticket-" # The prefix for ticket channel names
TicketsPerUser: 100
@@ -134,29 +151,40 @@ Ticket:
# Set to true to allow all staff to access the ticket
Log:
TicketLog: "🚨┃ticket-data"
TicketLog_Title: "Ticket Closed"
DisableTicketLogChannel: false
TranscriptLog: "🚨┃ticket-data"
TranscriptTitle: "Ticket Transcript"
TicketDataLog: "TICKET_LOG_ID"
TickeDataTitle: "Ticket Data"
Close:
StaffOnly: true
# Set to false is anyone is able to close a ticket.
Confirmation_Button: "Confirm Close"
Confirmation_Emoji: "🗑️"
Confirmation_Style: "2"
Title: "Are you sure you want to close this ticket?"
# Available Button Styles
# 1 - Blurple
# 2 - Grey
# 3 - Green
# 4 - Red
Invites:
StaffOnly: true
ClaimTickets:
Enabled: true
Channel: "1249723473757077624"
Channel: "CLAIM_TICKETS_CHANNEL_ID"
# Channel name or Channel ID
ClaimAcceptTime: 120000
# How long the assigned user has to accept the ticket before it gets re-assigned to someone else.
# 120000 = 2 Minutes
# 180000 = 3 Minutes
# 240000 = 4 Minutes
# 300000 = 5 Minutes
# Time in Mliseconds
ButtonTitle: "Claim Ticket"
ButtonEmoji: "🎫"
@@ -168,18 +196,6 @@ Ticket:
# 3 - Green
# 4 - Red
# -------------------------
# Departments - NOT IN THIS UPDATE
# -------------------------
Departments:
- title: "Create Ticket"
role: "Staff"
color: "2"
emoji: "🎫"
category: "TICKETS"
# -------------------------
# Buttons
# -------------------------
@@ -188,13 +204,20 @@ Buttons:
ClaimEmoji: "🧑‍💻"
ClaimStyle: "1"
Unarchive: "Unarchive Ticket"
Unarchive_Emoji: "♻️"
Unarchive_Style: "2"
Unlock: "Unlock Ticket"
Unlock_Emoji: "🔓"
Unlock_Style: "2"
# Available Button Styles
# 1 - Blurple
# 2 - Grey
# 3 - Green
# 4 - Red
# -------------------------
# Select Menus
# -------------------------
@@ -202,19 +225,18 @@ Buttons:
SelectMenus:
Tickets:
PanelEmoji: "⚙️"
CloseEmoji: "🗑️"
ArchiveEmoji: "♻️"
LockTicketEmoji: "🔒"
UnLockTicketEmoji: "🔓"
EnableInvitesEmoji: "🔒"
DisableInvitesEmoji: "🔓"
AddUserEmoji: "➕"
RemoveUserEmoji: "➖"
LockEmoji: "🔐"
RenameEmoji: "🏷️"
EnableInvitesEmoji: "📤"
DisableInvitesEmoji: "📥"
# -------------------------
# Misc
# -------------------------
Translate:
Log: "🚨┃logs"
TranslateLog: "TRANSLATE_LOG_ID"
-3
View File
@@ -20,9 +20,6 @@ Panel: true
# Ticket Panel Message Configuration #2b2d31
# ----------------------------------------
Channel: "📗┃tickets"
# The channel used to create the ticket panel, Use command /ticketpanel to generate it.
PanelTitle: "SupportBot"
PanelColour: "#0cc559"
+1 -7
View File
@@ -1,7 +1 @@
{
"419821884431925260": {
"bio": "Hi",
"timezone": "",
"isClockedIn": false
}
}
{}
-5
View File
@@ -1,5 +0,0 @@
{
"clockedIn": true,
"bio": "Hi",
"timezone": "GMT"
}
-5
View File
@@ -1,5 +0,0 @@
{
"bio": "This is the most W bio",
"timezone": "cst",
"clockedIn": false
}
+1 -3
View File
@@ -1,3 +1 @@
[
"419821884431925260"
]
{}
+1 -16
View File
@@ -1,16 +1 @@
{
"1250864227460186240": {
"suggestion": "hi",
"author": "419821884431925260",
"upvotes": [
"419821884431925260"
],
"downvotes": []
},
"1250900161958248533": {
"suggestion": "blob",
"author": "419821884431925260",
"upvotes": [],
"downvotes": []
}
}
{}
+1 -3
View File
@@ -1,3 +1 @@
{
}
{}
+1 -180
View File
@@ -1,184 +1,5 @@
{
"tickets": [
{},
{
"id": "1253862843053510687",
"name": "ticket-0021",
"user": "419821884431925260",
"number": "0021",
"reason": "> No Reason Provided!",
"open": true,
"subUsers": [],
"createdAt": "2024-06-22T00:03:18.227Z",
"claimedAt": null
},
{
"id": "1253863588012232727",
"name": "ticket-0022",
"user": "419821884431925260",
"number": "0022",
"reason": "> No Reason Provided!",
"open": true,
"subUsers": [],
"createdAt": "2024-06-22T00:06:16.126Z",
"claimedAt": "2024-06-22T00:09:12.693Z",
"claimedBy": "419821884431925260"
},
{
"id": "1255499438483574847",
"name": "ticket-0023",
"user": "419821884431925260",
"number": "0023",
"reason": "> No Reason Provided!",
"open": true,
"subUsers": [],
"createdAt": "2024-06-26T12:26:35.729Z",
"claimedAt": null
},
{
"id": "1255500796355678229",
"name": "ticket-0024",
"user": "419821884431925260",
"number": "0024",
"reason": "> No Reason Provided!",
"open": true,
"subUsers": [],
"createdAt": "2024-06-26T12:31:56.888Z",
"claimedAt": "2024-06-26T12:32:41.240Z",
"claimedBy": "419821884431925260"
},
{
"id": "1255501873386094603",
"name": "ticket-0025",
"user": "419821884431925260",
"number": "0025",
"reason": "> No Reason Provided!",
"open": false,
"subUsers": [],
"createdAt": "2024-06-26T12:36:13.810Z",
"claimedAt": null
},
{
"id": "1255508653906198709",
"name": "ticket-0026",
"user": "419821884431925260",
"number": "0026",
"reason": "> No Reason Provided!",
"open": true,
"subUsers": [],
"createdAt": "2024-06-26T13:03:10.321Z",
"claimedAt": null
},
{
"id": "1256299016023572572",
"name": "ticket-0027",
"user": "419821884431925260",
"number": "0027",
"reason": "> No Reason Provided!",
"open": false,
"subUsers": [],
"createdAt": "2024-06-28T17:23:50.113Z",
"claimedAt": null
},
{
"id": "1256300956824633426",
"name": "ticket-0028",
"user": "419821884431925260",
"number": "0028",
"reason": "> No Reason Provided!",
"open": false,
"subUsers": [],
"createdAt": "2024-06-28T17:31:31.454Z",
"claimedAt": null
},
{
"id": "1256302271461986507",
"name": "ticket-0029",
"user": "419821884431925260",
"number": "0029",
"reason": "> No Reason Provided!",
"open": false,
"subUsers": [],
"createdAt": "2024-06-28T17:36:44.673Z",
"claimedAt": null
},
{
"id": "1256303116505317457",
"name": "ticket-0030",
"user": "419821884431925260",
"number": "0030",
"reason": "> No Reason Provided!",
"open": true,
"subUsers": [],
"createdAt": "2024-06-28T17:40:06.292Z",
"claimedAt": null
},
{
"id": "1257775687562166304",
"name": "ticket-0031",
"user": "419821884431925260",
"number": "0031",
"reason": "> No Reason Provided!",
"open": false,
"subUsers": [],
"createdAt": "2024-07-02T19:11:33.760Z",
"claimedAt": null
},
{
"id": "1257776136843427841",
"name": "ticket-0032",
"user": "419821884431925260",
"number": "0032",
"reason": "> No Reason Provided!",
"open": false,
"subUsers": [],
"createdAt": "2024-07-02T19:13:20.641Z",
"claimedAt": null
},
{
"id": "1257777774483996795",
"name": "ticket-0033",
"user": "419821884431925260",
"number": "0033",
"reason": "> No Reason Provided!",
"open": false,
"subUsers": [],
"createdAt": "2024-07-02T19:19:51.116Z",
"claimedAt": null
},
{
"id": "1257778630600167535",
"name": "ticket-0034",
"user": "419821884431925260",
"number": "0034",
"reason": "> No Reason Provided!",
"open": false,
"subUsers": [],
"createdAt": "2024-07-02T19:23:15.328Z",
"claimedAt": null
},
{
"id": "1257780259374366841",
"name": "ticket-0035",
"user": "419821884431925260",
"number": "0035",
"reason": "> No Reason Provided!",
"open": false,
"subUsers": [],
"createdAt": "2024-07-02T19:29:43.607Z",
"claimedAt": null
},
{
"id": "1257782171389657180",
"name": "ticket-0036",
"user": "419821884431925260",
"number": "0036",
"reason": "> No Reason Provided!",
"open": true,
"subUsers": [],
"createdAt": "2024-07-02T19:37:19.467Z",
"claimedAt": "2024-07-02T19:43:12.873Z",
"claimedBy": "419821884431925260"
}
]
}
@@ -2,4 +2,4 @@
"bio": "",
"timezone": "",
"clockedIn": false
}
}
+1
View File
@@ -0,0 +1 @@
{}
+3 -3
View File
@@ -1,4 +1,4 @@
{
"id": 37,
"TicketPanelID": "1250863985335472312"
}
"id": 0,
"TicketPanelID": "0000000000000000000"
}
+20 -19
View File
@@ -19,7 +19,8 @@ const msgconfig = yaml.load(
module.exports = new Event("guildMemberAdd", async (client, member, interaction, guild) => {
if (supportbot.Welcome.Enabled) {
const WelcomeChannel = member.guild.channels.cache.find(channel => channel.name === supportbot.Welcome.Channel)
const WelcomeChannel = member.guild.channels.cache.get(supportbot.Welcome.Channel) ||
member.guild.channels.cache.find(channel => channel.name === supportbot.Welcome.Channel);
const WelcomeEmbed = new Discord.EmbedBuilder()
.setColor(msgconfig.Welcome.Embed.Colour)
@@ -27,29 +28,29 @@ module.exports = new Event("guildMemberAdd", async (client, member, interaction,
.setDescription(msgconfig.Welcome.Embed.Message.replace(/%joined_user%/g, member.user))
.setTimestamp();
if (msgconfig.Welcome.Embed.Thumbnail === "BOT") {
WelcomeEmbed.setThumbnail(client.user.displayAvatarURL())
}
if (msgconfig.Welcome.Embed.Thumbnail === "BOT") {
WelcomeEmbed.setThumbnail(client.user.displayAvatarURL())
} else if (msgconfig.Welcome.Embed.Thumbnail === "USER") {
WelcomeEmbed.setThumbnail(member.user.displayAvatarURL())
}
if (msgconfig.Welcome.Embed.Thumbnail === "USER") {
WelcomeEmbed.setThumbnail(member.user.displayAvatarURL())
}
if (msgconfig.Welcome.Embed.ImageEnabled) {
WelcomeEmbed.setImage(msgconfig.Welcome.Embed.ImageURL)
}
if (msgconfig.Welcome.Embed.ImageEnabled) {
WelcomeEmbed.setImage(msgconfig.Welcome.Embed.ImageURL)
}
if (WelcomeChannel) {
WelcomeChannel.send({ embeds: [WelcomeEmbed] });
}
WelcomeChannel.send({
embeds: [WelcomeEmbed]
})
if (supportbot.Roles.AutoRole.Enabled) {
var role = member.guild.roles.cache.find(role => role.name === supportbot.Roles.AutoRole.Role);
if (supportbot.Roles.AutoRole.Enabled) {
const role = member.guild.roles.cache.get(supportbot.Roles.AutoRole.Role) ||
member.guild.roles.cache.find(role => role.name === supportbot.Roles.AutoRole.Role);
if (role) {
member.roles.add(role);
}
console.log(`\u001b[32m`, `[+]`, `\u001b[33m`, `${member.user.username} joined the server!`)
}
}
console.log(`\u001b[32m`, `[+]`, `\u001b[33m`, `${member.user.username} joined the server!`);
}
});
+16 -16
View File
@@ -19,32 +19,32 @@ const msgconfig = yaml.load(
module.exports = new Event("guildMemberRemove", async (client, member, interaction, guild) => {
if (supportbot.Leave.Enabled) {
const LeaveChannel = member.guild.channels.cache.find(channel => channel.name === supportbot.Leave.Channel)
const LeaveChannel = member.guild.channels.cache.get(supportbot.Leave.Channel) ||
member.guild.channels.cache.find(channel => channel.name === supportbot.Leave.Channel);
const LeaveEmbed = new Discord.EmbedBuilder()
.setColor(msgconfig.Leave.Embed.Colour)
.setTitle(msgconfig.Leave.Embed.Title)
.setDescription(msgconfig.Leave.Embed.Message.replace(/%joined_user%/g, member.user))
.setDescription(msgconfig.Leave.Embed.Message.replace(/%left_user%/g, member.user))
.setTimestamp();
if (msgconfig.Leave.Embed.Thumbnail === "BOT") {
LeaveEmbed.setThumbnail(client.user.displayAvatarURL())
}
if (msgconfig.Leave.Embed.Thumbnail === "BOT") {
LeaveEmbed.setThumbnail(client.user.displayAvatarURL());
} else if (msgconfig.Leave.Embed.Thumbnail === "USER") {
LeaveEmbed.setThumbnail(member.user.displayAvatarURL());
}
if (msgconfig.Leave.Embed.Thumbnail === "USER") {
LeaveEmbed.setThumbnail(member.user.displayAvatarURL())
}
if (msgconfig.Leave.Embed.ImageEnabled) {
LeaveEmbed.setImage(msgconfig.Leave.Embed.ImageURL)
}
if (msgconfig.Leave.Embed.ImageEnabled) {
LeaveEmbed.setImage(msgconfig.Leave.Embed.ImageURL);
}
if (LeaveChannel) {
LeaveChannel.send({
embeds: [LeaveEmbed]
})
});
}
console.log(`\u001b[31m`, `[-]`, `\u001b[33m`, `${member.user.username} left the server!`)
console.log(`\u001b[31m`, `[-]`, `\u001b[33m`, `${member.user.username} left the server!`);
}
});
});
+443 -77
View File
@@ -16,7 +16,8 @@ try {
}
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(
(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;
const NotValid = new Discord.EmbedBuilder()
.setDescription(`:x: \`Invalid Command\` `)
.setDescription(`:x: \`Invalid Command\``)
.setColor(supportbot.Embed.Colours.Error);
if (!command)
return interaction.reply({
embeds: [NotValid],
ephemeral: true,
});
const permission = interaction.member.permissions.has(command.permissions);
@@ -43,40 +45,238 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
if (!permission)
return interaction.reply({
embeds: [ValidPerms],
ephemeral: true,
});
command.run(interaction);
try {
if (command.name === cmdconfig.OpenTicket.Command && supportbot.Ticket.TicketReason) {
const modal = new Discord.ModalBuilder()
.setCustomId("ticketReasonModal")
.setTitle("Ticket Reason")
.addComponents(
new Discord.ActionRowBuilder().addComponents(
new Discord.TextInputBuilder()
.setCustomId("reasonInput")
.setLabel("Reason")
.setStyle(Discord.TextInputStyle.Paragraph)
.setRequired(true)
)
);
await interaction.showModal(modal);
} else {
console.log("\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.customId === "ticketcontrolpanel") {
const selectedOption = interaction.values[0];
const { getRole, getChannel } = interaction.client;
switch (selectedOption) {
case "ticketadduser":
const AddUserThreadTicket = new Discord.EmbedBuilder()
.setTitle(`${msgconfig.Ticket.AddUserToTicket}`)
.setDescription(`${msgconfig.Ticket.AddUserToTicket}`)
.setColor(supportbot.Embed.Colours.General);
await interaction.reply({
embeds: [AddUserThreadTicket],
ephemeral: true,
});
break;
case "ticketremoveuser":
await removeUserFromThread(interaction);
break;
case "ticketclose":
const closeCommand = client.commands.get(cmdconfig.CloseTicket.Command);
if (closeCommand) {
closeCommand.run(interaction);
await closeCommand.run(interaction);
} else {
console.error("[X] Close Command Not Found");
}
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":
await handleInvites(interaction, true);
break;
@@ -84,37 +284,163 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
case "disableinvites":
await handleInvites(interaction, false);
break;
case "renameticket":
const modal = new Discord.ModalBuilder()
.setCustomId("renameTicketModal")
.setTitle("Rename Channel")
.addComponents(
new Discord.ActionRowBuilder()
.addComponents(
new Discord.TextInputBuilder()
.setCustomId("renameTicket")
.setLabel("New Channel Name")
.setStyle(Discord.TextInputStyle.Short)
.setRequired(true)
)
);
await interaction.showModal(modal);
break;
}
}
}
if (interaction.isButton()) {
if (interaction.customId === 'createticket') {
try {
const cmd = client.commands.get(cmdconfig.OpenTicket.Command);
if (!cmd) return;
cmd.run(interaction);
} catch (error) {
console.error("Error executing the ticket command:", error);
await interaction.reply({
content: "An error occurred while creating the ticket.",
ephemeral: true,
});
if (interaction.customId === "unarchiveTicket") {
if (supportbot.Ticket.TicketType === "threads") {
await interaction.channel.setArchived(false);
}
if (supportbot.Ticket.TicketType === "channels") {
const { getRole, getChannel } = interaction.client;
await interaction.channel.setParent(supportbot.Ticket.TicketChannelsCategory);
const ticketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
const ticketInfo = ticketData.tickets.find(t => t.id === interaction.channel.id);
if (ticketInfo) {
const user = interaction.guild.members.cache.get(ticketInfo.user) || ticketInfo.user;
const claimedBy = interaction.guild.members.cache.get(ticketInfo.claimedBy) || ticketInfo.claimedBy;
const Admin = await interaction.guild.roles.fetch(supportbot.Roles.StaffMember.Admin);
const Staff = await interaction.guild.roles.fetch(supportbot.Roles.StaffMember.Staff);
if (user && user.id) {
await interaction.channel.permissionOverwrites.edit(user.id, {
ViewChannel: true,
SendMessages: true,
ReadMessageHistory: true,
});
}
if (supportbot.ClaimTickets === true) {
if (claimedBy && claimedBy.id) {
await interaction.channel.permissionOverwrites.edit(claimedBy.id, {
ViewChannel: true,
SendMessages: true,
ReadMessageHistory: true,
});
}
}
if (supportbot.ClaimTickets === false) {
if (Staff && Staff.id) {
await interaction.channel.permissionOverwrites.edit(claimedBy.id, {
ViewChannel: true,
SendMessages: true,
ReadMessageHistory: true,
});
}
}
if (Admin && Admin.id) {
await interaction.channel.permissionOverwrites.edit(Admin.id, {
ViewChannel: true,
SendMessages: true,
ReadMessageHistory: true,
});
}
await interaction.channel.permissionOverwrites.edit(interaction.guild.roles.everyone.id, {
ViewChannel: false,
SendMessages: false,
ReadMessageHistory: false,
});
}
}
const unrcembed = new Discord.EmbedBuilder()
.setDescription(msgconfig.Ticket.TicketUnarchived)
.setColor(supportbot.Embed.Colours.Success);
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") {
try {
const cmd = client.commands.get(cmdconfig.OpenTicket);
if (!cmd) return;
cmd.run(interaction);
} catch (error) {
console.error(error);
if (supportbot.Ticket.TicketReason) {
const modal = new Discord.ModalBuilder()
.setCustomId("ticketReasonModal")
.setTitle("Ticket Reason")
.addComponents(
new Discord.ActionRowBuilder().addComponents(
new Discord.TextInputBuilder()
.setCustomId("reasonInput")
.setLabel("Reason")
.setStyle(Discord.TextInputStyle.Paragraph)
.setRequired(true)
)
);
await interaction.showModal(modal);
} else {
try {
const cmd = client.commands.get(cmdconfig.OpenTicket.Command);
if (!cmd) return;
await cmd.run(interaction);
} catch (error) {
console.error(error);
}
}
}
if (interaction.customId.startsWith('claimticket-')) {
const ticketChannelId = interaction.customId.split('-')[1];
if (interaction.customId.startsWith("claimticket-")) {
const ticketChannelId = interaction.customId.split("-")[1];
const ticketChannel = interaction.guild.channels.cache.get(ticketChannelId);
if (!ticketChannel) {
return interaction.reply({
@@ -144,7 +470,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
const NoPermsClaim = new Discord.EmbedBuilder()
.setTitle(msgconfig.Ticket.ClaimTickets.NoPermsToClaimTitle)
.setDescription(
msgconfig.Ticket.ClaimTickets.NoPermsToClaim.replace('%channel%', ticketChannel.name)
msgconfig.Ticket.ClaimTickets.NoPermsToClaim.replace("%channel%", ticketChannel.name)
)
.setColor(supportbot.Embed.Colours.Error);
@@ -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}>`);
setTimeout(() => {
pingclaimedstaff.delete();
}, 2000)
}, 2000);
const TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
const ticketIndex = TicketData.tickets.findIndex((t) => t.id === ticketChannelId);
@@ -174,13 +520,13 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
const claimedEmbed = new Discord.EmbedBuilder(claimMessage.embeds[0].data)
.setTitle(msgconfig.Ticket.ClaimTickets.ClaimedTitle)
.setDescription(
msgconfig.Ticket.ClaimTickets.ClaimMessage_Edit.replace('%user%', interaction.user.id).replace('%channel%', ticketChannel.id)
msgconfig.Ticket.ClaimTickets.ClaimMessage_Edit.replace("%user%", interaction.user.id).replace("%channel%", ticketChannel.id)
)
.setColor(supportbot.Embed.Colours.Success);
const successfullyClaimed = new Discord.EmbedBuilder()
.setTitle(msgconfig.Ticket.ClaimTickets.ClaimedTitle)
.setDescription(msgconfig.Ticket.ClaimTickets.Claimed.replace('%channel%', ticketChannel.id))
.setDescription(msgconfig.Ticket.ClaimTickets.Claimed.replace("%channel%", ticketChannel.id))
.setColor(supportbot.Embed.Colours.Success);
await claimMessage.edit({ embeds: [claimedEmbed], components: [] });
@@ -191,36 +537,41 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
}
// SUGGESTION INTERACTIONS [START]
if (interaction.customId === 'upvote' || interaction.customId === 'downvote' || interaction.customId === 'removevote') {
if (
interaction.customId === "upvote" ||
interaction.customId === "downvote" ||
interaction.customId === "removevote"
) {
const suggestion = suggestions[interaction.message.id];
if (!suggestion) return interaction.reply({ content: 'Suggestion not found.', ephemeral: true });
if (!suggestion)
return interaction.reply({ content: "Suggestion not found.", ephemeral: true });
const userId = interaction.user.id;
const hasUpvoted = suggestion.upvotes.includes(userId);
const hasDownvoted = suggestion.downvotes.includes(userId);
if (interaction.customId === 'upvote') {
if (interaction.customId === "upvote") {
if (hasUpvoted) {
suggestion.upvotes = suggestion.upvotes.filter(id => id !== userId);
suggestion.upvotes = suggestion.upvotes.filter((id) => id !== userId);
} else {
suggestion.upvotes.push(userId);
if (hasDownvoted) {
suggestion.downvotes = suggestion.downvotes.filter(id => id !== userId);
suggestion.downvotes = suggestion.downvotes.filter((id) => id !== userId);
}
}
} else if (interaction.customId === 'downvote') {
} else if (interaction.customId === "downvote") {
if (hasDownvoted) {
suggestion.downvotes = suggestion.downvotes.filter(id => id !== userId);
suggestion.downvotes = suggestion.downvotes.filter((id) => id !== userId);
} else {
suggestion.downvotes.push(userId);
if (hasUpvoted) {
suggestion.upvotes = suggestion.upvotes.filter(id => id !== userId);
suggestion.upvotes = suggestion.upvotes.filter((id) => id !== userId);
}
}
} else if (interaction.customId === 'removevote') {
suggestion.upvotes = suggestion.upvotes.filter(id => id !== userId);
suggestion.downvotes = suggestion.downvotes.filter(id => id !== userId);
} else if (interaction.customId === "removevote") {
suggestion.upvotes = suggestion.upvotes.filter((id) => id !== userId);
suggestion.downvotes = suggestion.downvotes.filter((id) => id !== userId);
}
fs.writeFileSync("./Data/SuggestionData.json", JSON.stringify(suggestions, null, 2));
@@ -228,25 +579,36 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
const upvoteCount = suggestion.upvotes.length;
const downvoteCount = suggestion.downvotes.length;
const updatedEmbed = new Discord.EmbedBuilder(interaction.message.embeds[0].data)
.setFields([
{ name: 'Suggestion', value: suggestion.suggestion, inline: true },
{ name: 'From', value: `<@${suggestion.author}>` },
{ name: `${supportbot.Suggestions.UpvoteEmoji} ${supportbot.Suggestions.UpvoteTitle}`, value: `${upvoteCount}`, inline: true },
{ name: `${supportbot.Suggestions.DownvoteEmoji} ${supportbot.Suggestions.DownvoteTitle}`, value: `${downvoteCount}`, inline: true }
]);
const updatedEmbed = new Discord.EmbedBuilder(interaction.message.embeds[0].data).setFields([
{ name: "Suggestion", value: suggestion.suggestion, inline: true },
{ name: "From", value: `<@${suggestion.author}>` },
{
name: `${supportbot.Suggestions.UpvoteEmoji} ${supportbot.Suggestions.UpvoteTitle}`,
value: `${upvoteCount}`,
inline: true,
},
{
name: `${supportbot.Suggestions.DownvoteEmoji} ${supportbot.Suggestions.DownvoteTitle}`,
value: `${downvoteCount}`,
inline: true,
},
]);
await interaction.update({ embeds: [updatedEmbed] });
}
}
async function handleInvites(interaction, enable) {
if (supportbot.Ticket.Invites.StaffOnly) {
const { getRole, getChannel } = interaction.client;
const SupportStaff = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild);
const Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild);
if (!SupportStaff || !Admin) {
return interaction.reply("Some roles seem to be missing!\nPlease check for errors when starting the bot.");
return interaction.reply(
"Some roles seem to be missing!\nPlease check for errors when starting the bot."
);
}
const NoPerms = new Discord.EmbedBuilder()
@@ -256,7 +618,10 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
)
.setColor(supportbot.Embed.Colours.Warn);
if (!interaction.member.roles.cache.has(SupportStaff.id) && !interaction.member.roles.cache.has(Admin.id)) {
if (
!interaction.member.roles.cache.has(SupportStaff.id) &&
!interaction.member.roles.cache.has(Admin.id)
) {
return interaction.reply({ embeds: [NoPerms] });
}
}
@@ -264,9 +629,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
await interaction.channel.setInvitable(enable);
const message = enable ? msgconfig.Ticket.EnableInvites : msgconfig.Ticket.DisableInvites;
const embed = new Discord.EmbedBuilder()
.setDescription(message)
.setColor(supportbot.Embed.Colours.General);
const embed = new Discord.EmbedBuilder().setDescription(message).setColor(supportbot.Embed.Colours.General);
await interaction.reply({
embeds: [embed],
@@ -275,7 +638,7 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
}
// Modal submission handler for editing profile
if (interaction.type === Discord.InteractionType.ModalSubmit && interaction.customId === 'editProfileModal') {
if (interaction.type === Discord.InteractionType.ModalSubmit && interaction.customId === "editProfileModal") {
const userId = interaction.user.id;
const profilePath = `./Data/Profiles/${userId}.json`;
@@ -286,13 +649,13 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
profileData = {
bio: "",
timezone: "",
clockedIn: false
clockedIn: false,
};
}
try {
const newBio = interaction.fields.getTextInputValue('bio');
const newTimezone = interaction.fields.getTextInputValue('timezone');
const newBio = interaction.fields.getTextInputValue("bio");
const newTimezone = interaction.fields.getTextInputValue("timezone");
profileData.bio = newBio;
profileData.timezone = newTimezone;
@@ -304,22 +667,25 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
.setColor(supportbot.Embed.Colours.General)
.setThumbnail(interaction.user.displayAvatarURL())
.addFields(
{ name: 'Bio', value: newBio || 'No bio set.', inline: false },
{ name: 'Timezone', value: newTimezone || 'No timezone set.', inline: true }
{ name: "Bio", value: newBio || "No bio set.", inline: false },
{ name: "Timezone", value: newTimezone || "No timezone set.", inline: true }
);
if (profileData.clockedIn !== undefined) {
updatedProfileEmbed.addFields({
name: 'Clocked In Status',
value: profileData.clockedIn ? '✅ Clocked In' : '❌ Clocked Out',
inline: true
name: "Clocked In Status",
value: profileData.clockedIn ? "✅ Clocked In" : "❌ Clocked Out",
inline: true,
});
}
await interaction.reply({ embeds: [updatedProfileEmbed], ephemeral: true });
} catch (error) {
console.error("Failed to update profile data: ", error);
await interaction.reply({ content: "There was an error while updating your profile. Please try again later.", ephemeral: true });
await interaction.reply({
content: "There was an error while updating your profile. Please try again later.",
ephemeral: true,
});
}
}
});
+64 -116
View File
@@ -18,7 +18,6 @@ const supportbotai = yaml.load(fs.readFileSync("./Configs/supportbot-ai.yml"));
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
// Validate configuration values
function validateConfig(config, keys) {
keys.forEach(key => {
@@ -69,150 +68,99 @@ module.exports = new Event("messageCreate", async (client, message) => {
if (message.author.bot || !message.guild) return;
if (!CHANNELS.includes(message.channelId) && !message.mentions.users.has(client.user.id)) return;
if (supportbotai.Enabled) {
const sendTypingInterval = setInterval(() => {
message.channel.sendTyping();
}, 5000);
if (supportbotai.Enabled) {
let conversation = [{
role: "system",
content: `SupportBot is your friendly neighbourhood support bot. Current date and time: ${new Date().toLocaleString()}.
The bot uses a modular command handler where commands are loaded dynamically from the './Commands' directory. Each command has a name, description, and execute method.
The main Client class structure:
- Commands are loaded from the './Commands/' directory.
- Configs are loaded from the './Configs/' directory if enabled.
- Events are loaded from the './Events/' directory.
- Addons are loaded from the './Addons/' directory if enabled.
Example of a command structure:
{
module.exports = new Command({
name: cmdconfig.Ping.Command,
description: cmdconfig.Ping.Description,
options: [],
permissions: cmdconfig.Ping.Permission,
)}
}
const sendTypingInterval = setInterval(() => {
message.channel.sendTyping();
}, 5000);
You can find more information about the commands and configuration files in the 'Configs' and 'Commands' directories.
let conversation = [{
role: "system",
content: "SupportBot is your friendly neighbourhood support bot."
}];
All commands are loaded into the bot at startup from ./Events/ready.js and ./Client.js.
let prevMessages = await message.channel.messages.fetch({ limit: 10 });
prevMessages = prevMessages.reverse().filter(msg => !msg.author.bot || msg.author.id === client.user.id);
If someone asks you to do something, you can use the 'assist' command to have the bot respond, For example: "Can I speak to a support agent?", You would respond by opening a ticket which is the /Commands/ticket.js file or you can tell them to create a ticket in the channel defined as supportbot.Ticket.
prevMessages.forEach((msg) => {
const username = msg.author.username.replace(/\s+/g, '_').replace(/[^\w\s]/gi, '');
You can find the source code for the bot at https://github.com/EmeraldServices/SupportBot.
conversation.push({
role: msg.author.id === client.user.id ? "assistant" : "user",
name: username,
content: msg.content,
});
});
You then need to understand how the bot works. Interactions are handled by the ./Events/interactionCreate.js file.
Commands are filtered based on configuration settings and loaded into the bot at startup.
Xiled Gaming Network (XGN) is a gaming community that typically offers various services and activities for gamers.
Xiled Gaming Network (XGN) has 4 divisions, Team Xiled International (TXI), The XiledOnes (TXO), Dealers of Death (DOD), Psychotic Solutions (PS)
Xiled Gaming Network (XGN) can be found at https://officialxgn.gg/handbook/, This is where you can the code of conduct, policies and other important information for members.
You can join XGN with the discord - discord.gg/xgn
You can apply to join the XGN Clan - join.officialxgn.gg
You can find the founders of XGN under the history section of the handbook. https://officialxgn.gg/handbook/.
`
}];
let prevMessages = await message.channel.messages.fetch({ limit: 10 });
prevMessages = prevMessages.reverse().filter(msg => !msg.author.bot || msg.author.id === client.user.id);
prevMessages.forEach((msg) => {
const username = msg.author.username.replace(/\s+/g, '_').replace(/[^\w\s]/gi, '');
conversation.push({
role: msg.author.id === client.user.id ? "assistant" : "user",
name: username,
content: msg.content,
});
try {
const response = await openai.chat.completions.create({
model: supportbotai.General.Model,
messages: conversation,
max_tokens: supportbotai.General.Tokens,
});
try {
const response = await openai.chat.completions.create({
model: supportbotai.General.Model,
messages: conversation,
max_tokens: supportbotai.General.Tokens,
});
clearInterval(sendTypingInterval);
clearInterval(sendTypingInterval);
if (response.choices && response.choices.length > 0) {
const replyContent = response.choices[0].message.content;
if (response.choices && response.choices.length > 0) {
const replyContent = response.choices[0].message.content;
const SupportBotAI = new Discord.EmbedBuilder()
.setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() })
.setDescription(replyContent)
.setColor(supportbotai.Embed.Color)
.setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() })
.setTimestamp();
if (replyContent.toLowerCase().startsWith("generate image:")) {
const prompt = replyContent.substring(15).trim();
const imageUrl = await generateImage(prompt);
if (replyContent.length > 2000) {
const pasteLink = await createPaste(replyContent);
const SupportBotAIImage = new Discord.EmbedBuilder()
.setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() })
.setDescription(`Here is the image generated for your prompt: "${prompt}"`)
.setImage(imageUrl)
.setColor(supportbotai.Embed.Color)
.setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() })
.setTimestamp();
message.reply({ embeds: [SupportBotAIImage] });
} else {
const SupportBotAI = new Discord.EmbedBuilder()
.setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() })
.setDescription(replyContent)
.setColor(supportbotai.Embed.Color)
.setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() })
.setTimestamp();
if (replyContent.length > 2000) {
const pasteLink = await createPaste(replyContent);
const SupportBotAIPastebin = new Discord.EmbedBuilder()
.setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() })
.setDescription(supportbotai.Messages.MessageTooLong.replace("{pastebin}", pasteLink))
.setColor(supportbotai.Embed.Color)
.setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() })
.setTimestamp();
message.reply({ embeds: [SupportBotAIPastebin] });
} else {
message.reply({ embeds: [SupportBotAI] });
}
}
} else {
const SupportBotAIErrorResponse = new Discord.EmbedBuilder()
const SupportBotAIPastebin = new Discord.EmbedBuilder()
.setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() })
.setDescription(supportbotai.Messages.ErrorResponse)
.setDescription(supportbotai.Messages.MessageTooLong.replace("{pastebin}", pasteLink))
.setColor(supportbotai.Embed.Color)
.setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() })
.setTimestamp();
message.reply({ embeds: [SupportBotAIErrorResponse], ephemeral: true });
message.reply({ embeds: [SupportBotAIPastebin] });
} else {
message.reply({ embeds: [SupportBotAI] });
}
} catch (error) {
clearInterval(sendTypingInterval);
} else {
const SupportBotAIErrorResponse = new Discord.EmbedBuilder()
.setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() })
.setDescription(supportbotai.Messages.OpenAIError)
.setColor(supportbot.Embed.Colours.Error)
.setDescription(supportbotai.Messages.ErrorResponse)
.setColor(supportbotai.Embed.Color)
.setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() })
.setTimestamp();
console.error('OpenAI Error:', error);
message.reply({ embeds: [SupportBotAIErrorResponse], ephemeral: true });
}
}
} catch (error) {
clearInterval(sendTypingInterval);
if (!supportbotai.Enabled) {
const AIDisabled = new Discord.EmbedBuilder()
const SupportBotAIErrorResponse = new Discord.EmbedBuilder()
.setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() })
.setDescription(supportbotai.Messages.AIDisabled)
.setDescription(supportbotai.Messages.OpenAIError)
.setColor(supportbot.Embed.Colours.Error)
.setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() })
.setTimestamp();
message.reply({ embeds: [AIDisabled], ephemeral: true });
return;
console.error('OpenAI Error:', error);
message.reply({ embeds: [SupportBotAIErrorResponse], ephemeral: true });
}
});
}
if (supportbotai.Enabled === false) {
const AIDisabled = new Discord.EmbedBuilder()
.setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() })
.setDescription(supportbotai.Messages.AIDisabled)
.setColor(supportbot.Embed.Colours.Error)
.setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() })
.setTimestamp();
message.reply({ embeds: [AIDisabled], ephemeral: true })
return;
}
});
+27 -84
View File
@@ -26,7 +26,7 @@ const msgconfig = yaml.load(
const Event = require("../Structures/Event.js");
let chan1 = client.channels.cache.get(panelconfig.Channel);
let chan1 = client.channels.cache.get(supportbot.Ticket.TicketHome);
module.exports = new Event("ready", async (client, interaction) => {
const { getRole, getChannel, getCategory } = client;
@@ -59,7 +59,7 @@ module.exports = new Event("ready", async (client, interaction) => {
});
}
console.log(`\u001b[32m`, `――――――――――――――――――――――――――――――――――――――――――――`);
console.log(`\u001b[33m`, `――――――――――――――――――――――――――――――――――――――――――――`);
console.log(` `);
console.log(`\u001b[31m`, `┏━━━┓╋╋╋╋╋╋╋╋╋╋╋╋╋┏┓┏━━┓╋╋╋┏┓`);
console.log(`\u001b[31m`, `┃┏━┓┃╋╋╋╋╋╋╋╋╋╋╋╋┏┛┗┫┏┓┃╋╋┏┛┗┓`);
@@ -70,60 +70,22 @@ module.exports = new Event("ready", async (client, interaction) => {
console.log(`\u001b[31m`, `┗╋╋╋╋╋╋╋┃┃╋┃┃`);
console.log(`\u001b[31m`, `╋╋╋╋╋╋╋┗┛╋┗┛`);
console.log(` `);
console.log(`\u001b[32m`, `――――――――――――――――――――――――――――――――――――――――――――`);
console.log(`\u001b[33m`, `――――――――――――――――――――――――――――――――――――――――――――`);
console.log(` `);
console.log(`\u001b[33m`, `${supportbot.General.Name} | [${supportbot.SupportBot_Version}]`, `\u001b[32m`, `Connected to Discord`,);
console.log("\u001b[32m", "SupportBot created by Emerald Development ");
console.log(` `);
console.log(
`\u001b[31m`,
`${supportbot.General.Name} | [${supportbot.SupportBot_Version}]`,
`\u001b[32m`,
`Connected to Discord`,
);
console.log(`\u001b[31m`, `SupportBot proudly created by Emerald Development`);
console.log(`\u001b[33m`, `――――――――――――――――― [Links] ――――――――――――――――――`);
console.log("\u001b[32m", "Discord: https://dsc.gg/emerald-dev");
console.log("\u001b[32m", "Website: https://emeraldsrv.com");
console.log("\u001b[32m", "Community: https://community.emeraldsrv.com");
console.log("\u001b[32m", "Documentation: https://docs.emeraldsrv.com");
console.log(` `);
console.log(
`\u001b[31m`,
`Discord`,
`\u001b[32m`,
`https://dsc.gg/emerald-dev`,
);
console.log(
`\u001b[31m`,
`Website`,
`\u001b[32m`,
`https://emeraldsrv.com`,
);
console.log(
`\u001b[31m`,
`Marketplace`,
`\u001b[32m`,
`https://market.emeraldsrv.com`,
);
console.log(
`\u001b[31m`,
`Documentation`,
`\u001b[32m`,
`https://emeraldsrv.com/third-party`,
);
console.log(`\u001b[33m`, `――――――――――――――――― [Invite] ――――――――――――――――――`);
console.log("\u001b[32m", `Invite URL: https://discord.com/api/oauth2/authorize?client_id=${client.user.id}&permissions=8&scope=bot%20applications.commands`);
console.log(` `);
console.log(
`\u001b[31m`,
`Invite URL:`,
`\u001b[36m`,
`https://discord.com/api/oauth2/authorize?client_id=` +
client.user.id +
`&permissions=8&scope=bot%20applications.commands`
);
console.log(` `);
console.log(`\u001b[32m`, `――――――――――――――――――――――――――――――――――――――――――――`);
console.log(`\u001b[33m`, `――――――――――――――――― [Config Check] ――――――――――――――――――`);
console.log("\u001b[32m", "Config initialization...");
const roles = [
supportbot.Roles.StaffMember.Admin,
@@ -132,15 +94,13 @@ module.exports = new Event("ready", async (client, interaction) => {
supportbot.Roles.ModRoles.Muted
];
supportbot.Departments.forEach((department) => roles.push(department.role));
if (supportbot.Roles.AutoRole.Role) roles.push(supportbot.Roles.AutoRole.Role);
const channels = [
supportbot.Suggestions.Channel,
supportbot.Ticket.Log.TicketLog,
supportbot.Ticket.Log.TranscriptLog,
panelconfig.Channel,
supportbot.Ticket.TicketHome,
supportbot.Welcome.Channel,
supportbot.Leave.Channel,
supportbot.Translate.Log
@@ -168,35 +128,18 @@ module.exports = new Event("ready", async (client, interaction) => {
// if (!find) missingCat.push(cat);
// }
if (missingR.length > 0) {
console.log(
"\u001b[31m",
`[MISSING CONFIGURATION]`,
"\u001b[33m",
`The following Roles could not be found in your server.\n`, `\u001b[31m`, `[${missingR.join(
", "
)}]`, `\n `,
);
const missingRoles = await Promise.all(roles.map(role => getRole(role, client.guilds.cache.first())));
const missingChannels = await Promise.all(channels.map(channel => getChannel(channel, client.guilds.cache.first())));
if (missingRoles.some(role => !role)) {
console.log("\u001b[31m", `Missing roles in your server configuration: ${missingRoles.filter(role => !role).join(', ')}`);
}
if (missingC.length > 0) {
console.log(
"\u001b[31m",
`[MISSING CONFIGURATION]`,
"\u001b[33m",
`The following Channels could not be found in your server.\n`, `\u001b[31m`, `[${missingC.join(
", "
)}]`, `\n `
);
}
if (missingCat.length > 0) {
console.log(
"\u001b[31m",
`[MISSING CONFIGURATION]`,
"\u001b[33m",
`The following Categories could not be found in your server.\n`, `\u001b[31m`, `[${missingCat.join(
", "
)}]`, `\n `,
);
if (missingChannels.some(channel => !channel)) {
console.log("\u001b[31m", `Missing channels in your server configuration: ${missingChannels.filter(channel => !channel).join(', ')}`);
}
console.log("\u001b[32m", "Configs initialized, No problems were detected.");
console.log(` `);
});
+10 -2
View File
@@ -13,11 +13,11 @@
</a>
<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 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 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">
</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
// Addon Structure
class Command {
constructor(options) {
this.name = options.name;
@@ -10,7 +10,13 @@ class Command {
this.run = options.run;
this.defaultPermission = true;
}
// Method to dynamically update options
setOptions(options) {
this.options = options;
}
}
class Event {
constructor(event, run) {
this.event = event;
+24 -8
View File
@@ -85,15 +85,31 @@ class Client extends Discord.Client {
tempCommandFiles = tempCommandFiles.filter(item => item !== "Ping.js");
}
if (supportbot.Ticket.ClaimTickets.Enabled === false) {
tempCommandFiles = tempCommandFiles.filter(item => item !== "ticketstats.js");
if (cmdconfig.AddUser.Enabled === false) {
tempCommandFiles = tempCommandFiles.filter(item => item !== "addUser.js");
}
if (cmdconfig.RemoveUser.Enabled === false) {
tempCommandFiles = tempCommandFiles.filter(item => item !== "removeUser.js");
}
if (cmdconfig.ForceAddUser.Enabled === false) {
tempCommandFiles = tempCommandFiles.filter(item => item !== "forceaddUser.js");
}
if (cmdconfig.Profile.Enabled === false) {
tempCommandFiles = tempCommandFiles.filter(item => item !== "profile.js");
}
if (cmdconfig.Settings.Enabled === false) {
tempCommandFiles = tempCommandFiles.filter(item => item !== "settings.js");
}
const commandFiles = tempCommandFiles;
const commands = commandFiles.map((file) => require(`../Commands/${file}`));
// Load main commands
console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Commands ▬▬▬▬▬▬▬");
console.log(`\u001b[33m`, "――――――――――――――――――――――――――――――――――――――――――――");
commands.forEach((cmd) => {
console.log(`\u001b[32m`, `[CMD]`, `\u001b[37;1m`, `${cmd.name}`, `\u001b[32;1m`, "Loaded");
@@ -101,7 +117,7 @@ class Client extends Discord.Client {
});
// Load addon commands and events
console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Commands ▬▬▬▬▬▬▬");
console.log(`\u001b[33m`, "▬▬▬▬▬▬▬ Commands ▬▬▬▬▬▬▬");
if (supportbot.General.Addons.Enabled) {
const addonFiles = fs.readdirSync("./Addons").filter((file) => file.endsWith(".js"));
@@ -114,7 +130,7 @@ class Client extends Discord.Client {
// Addons
console.log(" ");
console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬");
console.log(`\u001b[33m`, "▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬");
addons.forEach((addon) => {
console.log(
@@ -139,7 +155,7 @@ class Client extends Discord.Client {
}
});
console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬");
console.log(`\u001b[33m`, "▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬");
}
// Register Slash Commands
@@ -155,7 +171,7 @@ class Client extends Discord.Client {
});
console.log(" ");
console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Events ▬▬▬▬▬▬▬");
console.log(`\u001b[33m`, "▬▬▬▬▬▬▬ Events ▬▬▬▬▬▬▬");
fs.readdirSync("./Events").filter((file) => file.endsWith(".js")).forEach((file) => {
const event = require(`../Events/${file}`);
@@ -163,7 +179,7 @@ class Client extends Discord.Client {
this.on(event.event, (...args) => event.run(this, ...args));
});
console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Events ▬▬▬▬▬▬▬");
console.log(`\u001b[33m`, "▬▬▬▬▬▬▬ Events ▬▬▬▬▬▬▬");
if (process.argv[2] !== "test") {
await this.login(token);
} else {
+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",
"dependencies": {
"@paypal/checkout-server-sdk": "^1.0.3",
"@vitalets/google-translate-api": "^8.0.0",
"axios": "^1.7.2",
"chatgpt": "^5.2.5",
"discord.js": "^14.13.0",
"js": "^0.1.0",
"js-yaml": "^4.1.0",
"moment": "^2.29.3",
"openai": "^4.50.0"
"ms": "^2.1.3",
"openai": "^4.50.0",
}
}