Compare commits

...
22 Commits
Author SHA1 Message Date
1tzEmerald 8d8d9e4bee 8.0.1 Bug Fix 2024-06-13 18:01:41 +01:00
1tzEmerald dcbddf9f23 8.0.1 - Bug Fix 2024-06-13 17:36:44 +01:00
1tzEmerald 7449c0a61a License files 2024-06-12 12:55:45 +01:00
1tzEmerald c95b8ccb32 SupportBot 8.0 2024-06-12 12:54:31 +01:00
1tzEmerald 2c5bfe8e33 Merge pull request #135 from ItsNyoty/patch-1
Update messages.yml
2022-09-05 13:42:52 +01:00
ItsNyoty a3bbd97333 Update messages.yml
TicketExists wasn't defined before
2022-08-29 14:58:01 +02:00
1tzEmerald 4ea488951d Delete oldconfig.yml 2022-08-12 01:38:07 +01:00
1tzEmerald 481424d5a6 Update LICENSE.md 2022-08-12 01:37:49 +01:00
1tzEmerald fe82e96cc1 Update LICENSE.md 2022-08-12 01:37:31 +01:00
1tzEmerald 976258c959 Delete .gitignore 2022-08-12 01:37:14 +01:00
1tzEmerald 83cf0689e2 Delete .DS_Store 2022-08-12 01:37:00 +01:00
ItzEmerald c911cb4423 v8.0 New configuration files 2022-08-12 01:35:36 +01:00
ItzEmerald 4eadef8b65 8.0 Dev 1.2
Reworked configuration files & completed upgrade to V14
2022-08-11 19:19:29 +01:00
ItzEmerald 4e081358ff v8.0-Dev-1.1 2022-07-23 07:50:12 +01:00
ItzEmerald 209ec0b435 v8.0 - Dev-1 2022-07-22 13:19:05 +01:00
Xiled Emerald 6d363abb54 Merge pull request #129 from SypherRed/dev
Better looking readme
2022-07-01 22:19:03 +01:00
SypherRed 6f8b84aeda Update README.md 2022-07-01 23:16:16 +02:00
SypherRed 5b8c5da33a Update README.md 2022-07-01 23:14:01 +02:00
Xiled Emerald b6ef0f95cb Merge pull request #126 from SypherRed/release
Muted Role check and issue fix
2022-06-30 15:49:14 +01:00
SypherRed cce1480a42 remove empty declaration
AboutMe was removed in a previous version.
2022-06-29 13:12:56 +02:00
SypherRed 26e9b56961 Issue fix with Muted Role check
Fixed the issue to recognize a Muted Role and made changes to ready.js accordingly
2022-06-27 19:03:34 +02:00
SypherRed 979271cfb5 beta push of muted check
push of a check if author has a muted role on ticket opening, if so, decline the interaction

Also either check for missing role or not
2022-06-23 20:58:03 +02:00
41 changed files with 2395 additions and 2473 deletions

No files matched your search

Vendored
BIN
View File
Binary file not shown.
-2
View File
@@ -1,2 +0,0 @@
node_modules/
.DS_Store
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
-45
View File
@@ -1,45 +0,0 @@
// SupportBot | Emerald Services
// Example (default) Addon
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"));
// Your Addon Config file! Saved in /Addons/Configs/YourAddonConfig.yml
// const config = yaml.load(fs.readFileSync('./Addons/Configs/YourAddonConfig.yml', 'utf8'));
const { Command, Event } = require("../Structures/Addon");
module.exports.commands = [
/*
// EXAMPLE COMMAND
new Command({
name: "hello",
description: "hello",
options: [],
permissions: ["SEND_MESSAGES"],
async run(interaction) {
const Embed = new Discord.MessageEmbed()
.setDescription("Hello World!")
.setColor(supportbot.InfoColour);
interaction.reply({
embeds: [Embed],
});
},
}),
*/
];
module.exports.events = [
/*
// EXAMPLE EVENT
new Event("ready", async (client) => {
console.log("Bot started!");
}),
*/
];
+128
View File
@@ -0,0 +1,128 @@
# 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
@@ -0,0 +1,92 @@
# 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/
-92
View File
@@ -1,92 +0,0 @@
// SupportBot | Emerald Services
// Ticket Add Command
const fs = require("fs");
const Discord = require("discord.js");
const yaml = require("js-yaml");
const supportbot = yaml.load(
fs.readFileSync("./Configs/supportbot.yml", "utf8")
);
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
const Command = require("../Structures/Command.js");
module.exports = new Command({
name: cmdconfig.TicketAdd,
description: cmdconfig.TicketAddDesc,
options: [
{
name: "user",
description: "User to add",
type: "USER",
required: true,
},
],
permissions: ["SEND_MESSAGES"],
async run(interaction) {
const { getRole } = interaction.client;
let SupportStaff = await getRole(supportbot.Staff, interaction.guild);
let Admin = await getRole(supportbot.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.MessageEmbed()
.setTitle("Invalid Permissions!")
.setDescription(
`${supportbot.IncorrectPerms}\n\nRole Required: \`${supportbot.Staff}\` or \`${supportbot.Admin}\``
)
.setColor(supportbot.WarningColour);
if (
!interaction.member.roles.cache.has(SupportStaff.id) &&
!interaction.member.roles.cache.has(Admin.id)
)
return interaction.reply({ ephemeral: true, embeds: [NoPerms] });
let TicketData = await JSON.parse(
fs.readFileSync("./Data/TicketData.json", "utf8")
);
let ticket = await TicketData.tickets.findIndex(
(t) => t.id === interaction.channel.id
);
if (ticket === -1) {
const Exists = new Discord.MessageEmbed()
.setTitle("No Ticket Found!")
.setDescription(supportbot.NoValidTicket)
.setColor(supportbot.WarningColour);
return interaction.reply({ ephemeral: true, embeds: [Exists] });
}
let uMember = interaction.options.getUser("user");
const UserNotExist = new Discord.MessageEmbed()
.setTitle("User Not Found!")
.setDescription(
`${supportbot.UserNotFound}\n\nTry Again:\`/${cmdconfig.TicketAdd} <user#0000>\``
)
.setColor(supportbot.ErrorColour);
if (!uMember) return interaction.reply({ embeds: [UserNotExist] });
await interaction.channel.permissionOverwrites.edit(uMember.id, {
VIEW_CHANNEL: true,
});
const Complete = new Discord.MessageEmbed()
.setTitle("User Added!")
.setDescription(supportbot.AddedUser.replace(/%user%/g, uMember.id))
.setTimestamp()
.setColor(supportbot.EmbedColour);
interaction.reply({ embeds: [Complete] });
TicketData.tickets[ticket].subUsers.push(uMember.id);
fs.writeFileSync(
"./Data/TicketData.json",
JSON.stringify(TicketData, null, 4),
(err) => {
if (err) console.error(err);
}
);
},
});
+278 -142
View File
@@ -1,44 +1,50 @@
// SupportBot | Emerald Services
// Close Ticket Command
const fs = require("fs");
const Discord = require("discord.js");
const yaml = require("js-yaml");
const supportbot = yaml.load(
fs.readFileSync("./Configs/supportbot.yml", "utf8")
);
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
const 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.CloseTicket,
description: cmdconfig.CloseTicketDesc,
name: cmdconfig.CloseTicket.Command,
description: cmdconfig.CloseTicket.Description,
type: Discord.ApplicationCommandType.ChatInput,
options: [
{
name: "reason",
description: "Ticket Close Reason",
type: "STRING",
type: Discord.ApplicationCommandOptionType.String,
},
],
permissions: ["SEND_MESSAGES"],
permissions: cmdconfig.CloseTicket.Permission,
async run(interaction) {
const { getRole, getChannel } = interaction.client;
if (supportbot.StaffOnly) {
let SupportStaff = await getRole(supportbot.Staff, interaction.guild);
let Admin = await getRole(supportbot.Admin, interaction.guild);
if (supportbot.Ticket.Close.StaffOnly) {
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.MessageEmbed()
const NoPerms = new Discord.EmbedBuilder()
.setTitle("Invalid Permissions!")
.setDescription(
`${supportbot.IncorrectPerms}\n\nRole Required: \`${supportbot.Staff}\` or \`${supportbot.Admin}\``
`${msgconfig.Error.IncorrectPerms}\n\nRole Required: \`${supportbot.Roles.StaffMember.Staff}\` or \`${supportbot.Roles.StaffMember.Admin}\``
)
.setColor(supportbot.WarningColour);
.setColor(supportbot.Embed.Colours.Warn);
if (
!interaction.member.roles.cache.has(SupportStaff.id) &&
@@ -46,136 +52,240 @@ module.exports = new Command({
)
return interaction.reply({ embeds: [NoPerms] });
}
if (interaction.channel.type !== Discord.ChannelType.PrivateThread) {
const NotTicketChannel = new Discord.EmbedBuilder()
.setTitle("Invalid Channel!")
.setDescription("This command can only be used in a ticket thread.")
.setColor(supportbot.Embed.Colours.Warn);
return interaction.reply({ embeds: [NotTicketChannel], ephemeral: true });
}
await interaction.deferReply();
let tickets = await JSON.parse(
fs.readFileSync("./Data/TicketData.json", "utf8")
);
let TicketData = await tickets.tickets.findIndex(
(t) => t.id === interaction.channel.id
);
let tickets = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
let TicketData = tickets.tickets.findIndex((t) => t.id === interaction.channel.id);
let ticket = tickets.tickets[TicketData];
if (TicketData === -1) {
const Exists = new Discord.MessageEmbed()
const Exists = new Discord.EmbedBuilder()
.setTitle("No Ticket Found!")
.setDescription(supportbot.NoValidTicket)
.setColor(supportbot.WarningColour);
.setDescription(msgconfig.Error.NoValidTicket)
.setColor(supportbot.Embed.Colours.Warn);
return interaction.followUp({ embeds: [Exists] });
}
let tUser = interaction.client.users.cache.get(ticket.user);
let transcriptChannel = await getChannel(
supportbot.TranscriptLog,
interaction.guild
);
let logChannel = await getChannel(supportbot.TicketLog, interaction.guild);
let reason =
(await interaction.options?.getString("reason")) || "No Reason Provided.";
if (!transcriptChannel || !logChannel)
return interaction.followUp("Some Channels seem to be missing!");
if (supportbot.CloseConfirmation) {
const CloseTicketRequest = new Discord.MessageEmbed()
.setTitle(`**${supportbot.ClosingTicket}**`)
.setDescription(
`Please confirm by repeating the following word.. \`${supportbot.ClosingConfirmation_Word}\` `
)
.setColor(supportbot.EmbedColour);
await interaction.followUp({ embeds: [CloseTicketRequest] });
let filter = (m) =>
m.content.toLowerCase() === supportbot.ClosingConfirmation_Word &&
m.author.id === interaction.user.id;
await interaction.channel.awaitMessages({
filter,
max: 1,
time: 20000,
errors: ["time"],
let reason = interaction.options?.getString("reason") || "No Reason Provided.";
const CloseButton = new Discord.ButtonBuilder()
.setCustomId("confirmCloseTicket")
.setLabel(supportbot.Ticket.Close.Confirmation_Button)
.setStyle(Discord.ButtonStyle.Danger);
const row = new Discord.ActionRowBuilder().addComponents(CloseButton);
const CloseTicketRequest = new Discord.EmbedBuilder()
.setTitle(`**${supportbot.Ticket.Close.Title}**`)
.setDescription(
`${msgconfig.Ticket.ConfirmClose}`
)
.setColor(supportbot.Embed.Colours.General);
await interaction.followUp({
embeds: [CloseTicketRequest],
components: [row],
});
const filter = (btnInteraction) => btnInteraction.customId === "confirmCloseTicket" && btnInteraction.user.id === interaction.user.id;
interaction.channel.awaitMessageComponent({ filter, time: 20000 })
.then(async (btnInteraction) => {
await handleCloseTicket(interaction, reason, ticket, TicketData);
await btnInteraction.reply({ content: "Ticket successfully closed.", ephemeral: true });
})
.catch((err) => {
interaction.followUp(`${msgconfig.Ticket.CloseTimeout}`);
console.error(err);
});
},
});
async function handleCloseTicket(interaction, reason, ticket, TicketData) {
const { getChannel } = interaction.client;
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);
if (!transcriptChannel || !logChannel)
return interaction.followUp("Some Channels seem to be missing!");
try {
tickets.tickets[TicketData].open = false;
fs.writeFileSync(
"./Data/TicketData.json",
JSON.stringify(tickets, null, 4),
(err) => {
if (err) console.error(err);
}
);
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)
.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 }
);
let msgs = await interaction.channel.messages.fetch({ limit: 100 });
let html = `
<html>
<head>
<link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
<style>
body {
background-color: #1a1a1a;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
color: white;
margin: 0;
padding: 0;
height: 100%;
display: flex;
flex-direction: column;
}
.container {
backdrop-filter: blur(10px);
background-color: rgba(0, 128, 0, 0.3);
border-radius: 15px;
padding: 20px;
margin: 20px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
flex-grow: 1;
display: flex;
flex-direction: column;
max-width: 1000px;
width: 100%;
margin: auto;
}
.message {
display: flex;
align-items: center;
margin-bottom: 10px;
padding: 10px;
background-color: rgba(255, 255, 255, 0.1);
border-radius: 10px;
}
.avatar {
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 10px;
}
.content {
flex: 1;
}
.username {
font-weight: bold;
}
.timestamp {
font-size: 0.8em;
color: #bbb;
}
.download-button {
background-color: #4CAF50;
color: white;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin-top: 20px;
border-radius: 5px;
}
</style>
</head>
<body>
<div class="container">
<h1 class="text-2xl font-bold mb-4">Ticket Transcript</h1>
<p><strong>Server Name:</strong> ${interaction.guild.name}</p>
<p><strong>Ticket:</strong> ${interaction.channel.name}</p>
<p><strong>Messages:</strong> ${msgs.size} Messages</p>
<div class="mt-4">
`;
msgs = msgs.sort((a, b) => a.createdTimestamp - b.createdTimestamp);
msgs.forEach((msg) => {
if (msg.content) {
html += `
<div class="message">
<img class="avatar" src="${msg.author.displayAvatarURL()}" alt="Avatar">
<div class="content">
<p class="username">${msg.author.tag}</p>
<p class="timestamp">${msg.createdAt.toLocaleString()}</p>
<p>${convertMarkdownToHTML(msg.content)}</p>
</div>
</div>
`;
}
});
const fileName = `${interaction.channel.name}.html`;
html += `
</div>
<a href="${fileName}" download class="download-button">Download Transcript</a>
</div>
</body>
</html>
`;
if (!supportbot.Ticket.Log.DisableTicketLogChannel) {
await logChannel.send({ embeds: [logEmbed] }).catch((err) => {
console.error(err);
});
}
try {
tickets.tickets[TicketData].open = false;
fs.writeFileSync(
"./Data/TicketData.json",
JSON.stringify(tickets, null, 4),
(err) => {
if (err) console.error(err);
}
);
await interaction.followUp(`**${supportbot.ClosingTicket}**`);
const transcriptEmbed = new Discord.MessageEmbed()
.setTitle(supportbot.TranscriptTitle)
.setColor(supportbot.EmbedColour)
.setFooter({
text: supportbot.EmbedFooter,
iconURL: interaction.user.displayAvatarURL(),
})
.addField(
"Ticket",
`${interaction.channel.name} (${interaction.channel.id})`
)
.addField(
"User",
`${tUser?.username || "N/A"}#${tUser?.discriminator || "N/A"} (${
tUser?.id || ticket.user
})`
)
.addField("Closed By", interaction.user.tag)
.addField("Reason", reason);
const logEmbed = new Discord.MessageEmbed()
.setTitle(supportbot.TicketLog_Title)
.setColor(supportbot.EmbedColour)
.setFooter({
text: supportbot.EmbedFooter,
iconURL: interaction.user.displayAvatarURL(),
})
.addField(
"Ticket",
`${interaction.channel.name} (${interaction.channel.id})`
)
.addField(
"User",
`${tUser?.username || "N/A"}#${tUser?.discriminator || "N/A"} (${
tUser?.id || ticket.user
})`
)
.addField("Closed By", interaction.user.tag)
.addField("Reason", reason);
let msgs = await interaction.channel.messages.fetch({ limit: 100 });
let html = "";
msgs = msgs.sort((a, b) => a.createdTimestamp - b.createdTimestamp);
html += `<style>* {background-color: #2c2f33;color: #fff;font-family: Arial, Helvetica, sans-serif;}</style>`;
html += `<strong>Server Name:</strong> ${interaction.guild.name}<br>`;
html += `<strong>Ticket:</strong> ${interaction.channel.name}<br>`;
html += `<strong>Message:</strong> ${msgs.size} Messages<br><br><br>`;
let file = new Discord.AttachmentBuilder(
Buffer.from(html),
{ name: fileName }
);
await msgs.forEach((msg) => {
if (msg.content) {
html += `<strong>User:</strong> ${msg.author.tag}<br>`;
html += `<strong>Message:</strong> ${msg.content}<br>`;
html += `-----<br><br>`;
}
await transcriptChannel
.send({ embeds: [transcriptEmbed], files: [file] })
.catch((err) => {
console.error(err);
});
if (!(supportbot.DisableTicketLogChannel)) {
await logChannel.send({ embeds: [logEmbed] }).catch((err) => {
console.error(err);
});
}
let file = new Discord.MessageAttachment(
Buffer.from(html),
`${interaction.channel.name}.html`
);
await transcriptChannel
.send({ embeds: [transcriptEmbed], files: [file] })
.catch(async (err) => {
console.error(err);
});
if (supportbot.DMTranscripts) {
await tUser
if (supportbot.Ticket.DMTranscripts) {
await tUser
?.send({ embeds: [transcriptEmbed], files: [file] })
.catch(async (err) => {
.catch((err) => {
console.error(err);
});
if (ticket.subUsers) {
ticket.subUsers.forEach(async (subUser) => {
await interaction.client.users.cache
@@ -183,17 +293,43 @@ module.exports = new Command({
?.send({ embeds: [transcriptEmbed], files: [file] });
});
}
}
await interaction.channel.delete().catch(async (error) => {
console.error(error);
if (error.code !== Discord.Constants.APIErrors.UNKNOWN_CHANNEL) {
console.error("Failed to delete the interaction:", error);
}
});
} catch (error) {
console.error(error);
}
},
});
await interaction.channel.delete().catch((error) => {
console.error(error);
if (error.code !== Discord.Constants.APIErrors.UNKNOWN_CHANNEL) {
console.error("Failed to delete the interaction:", error);
}
});
} catch (error) {
console.error(error);
}
}
function convertMarkdownToHTML(text) {
// Convert **bold** to <strong>bold</strong>
text = text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
// Convert *italic* to <em>italic</em>
text = text.replace(/\*(.*?)\*/g, '<em>$1</em>');
// Convert __underline__ to <u>underline</u>
text = text.replace(/__(.*?)__/g, '<u>$1</u>');
// Convert ~~strikethrough~~ to <s>strikethrough</s>
text = text.replace(/~~(.*?)~~/g, '<s>$1</s>');
// Convert inline code `code` to <code>code</code>
text = text.replace(/`(.*?)`/g, '<code>$1</code>');
// Convert blockquotes > quote to <blockquote>quote</blockquote>
text = text.replace(/^> (.*?$)/gm, '<blockquote>$1</blockquote>');
// Convert multiline code blocks ```code``` to <pre><code>code</code></pre>
text = text.replace(/```(.*?)```/gs, '<pre><code>$1</code></pre>');
// Convert line breaks to <br>
text = text.replace(/\n/g, '<br>');
return text;
}
+103 -43
View File
@@ -1,101 +1,161 @@
// SupportBot | Emerald Services
// Embed Command
// File path: ./commands/embed.js
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 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.EmbedCommand,
description: cmdconfig.EmbedCommandDesc,
name: cmdconfig.Embed.Command,
description: cmdconfig.Embed.Description,
type: Discord.ApplicationCommandType.ChatInput,
options: [
{
name: "title",
description: "Embed Title",
type: "STRING",
type: Discord.ApplicationCommandOptionType.String,
required: true,
},
{
name: "message",
description: "Embed Message",
type: "STRING",
type: Discord.ApplicationCommandOptionType.String,
required: true,
},
{
name: "color",
description: "Embed HEX Color",
type: "STRING",
type: Discord.ApplicationCommandOptionType.String,
required: false,
},
{
name: "fieldtitle",
description: "Add an additional Embed Field",
type: "STRING",
required: false,
},
{
name: "fieldcontent",
description: "Add an additional Embed Field",
type: "STRING",
name: "fields",
description: "Embed Fields (title|content;title|content;...)",
type: Discord.ApplicationCommandOptionType.String,
required: false,
},
{
name: "thumbnail",
description: "Embed Thumbnail URL",
type: "STRING",
type: Discord.ApplicationCommandOptionType.String,
required: false,
},
{
name: "image",
description: "Embed Image URL",
type: "STRING",
type: Discord.ApplicationCommandOptionType.String,
required: false,
},
{
name: "footer",
description: "Embed Footer Text",
type: Discord.ApplicationCommandOptionType.String,
required: false,
},
{
name: "footericon",
description: "Embed Footer Icon URL",
type: Discord.ApplicationCommandOptionType.String,
required: false,
},
{
name: "author",
description: "Embed Author Name",
type: Discord.ApplicationCommandOptionType.String,
required: false,
},
{
name: "authoricon",
description: "Embed Author Icon URL",
type: Discord.ApplicationCommandOptionType.String,
required: false,
},
{
name: "authorurl",
description: "Embed Author URL",
type: Discord.ApplicationCommandOptionType.String,
required: false,
},
{
name: "timestamp",
description: "Include Timestamp",
type: Discord.ApplicationCommandOptionType.Boolean,
required: false,
},
{
name: "inline",
description: "Make Fields Inline",
type: Discord.ApplicationCommandOptionType.Boolean,
required: false,
}
],
permissions: ["SEND_MESSAGES"],
permissions: cmdconfig.Embed.Permission,
async run(interaction) {
const { getRole } = interaction.client;
let SupportStaff = await getRole(supportbot.Staff, interaction.guild);
let Admin = await getRole(supportbot.Admin, interaction.guild);
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.MessageEmbed()
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(
`${supportbot.IncorrectPerms}\n\nRole Required: \`${supportbot.Staff}\` or \`${supportbot.Admin}\``
)
.setColor(supportbot.WarningColour);
.setDescription(`${msgconfig.Error.IncorrectPerms}\n\nRole Required: \`${supportbot.Roles.StaffMember.Staff}\` or \`${supportbot.Roles.StaffMember.Admin}\``)
.setColor(supportbot.Embed.Colours.Warn);
if (
interaction.member.roles.cache.has(SupportStaff.id) ||
interaction.member.roles.cache.has(Admin.id)
) {
const EmbedTitle = interaction.options.getString("title");
const EmbedSubject = interaction.options.getString("message");
const EmbedColour = interaction.options.getString("color");
const EmbedTitle = interaction.options.getString("title").replace(/\\n/g, '\n');
const EmbedSubject = interaction.options.getString("message").replace(/\\n/g, '\n');
const GeneralColour = interaction.options.getString("color") || supportbot.Embed.Colours.Default;
const EmbedThumbnail = interaction.options.getString("thumbnail");
const EmbedImage = interaction.options.getString("image");
const EmbedFieldContent = interaction.options.getString("fieldcontent");
const EmbedFieldTitle = interaction.options.getString("fieldtitle");
const EmbedFields = interaction.options.getString("fields");
const EmbedFooter = interaction.options.getString("footer")?.replace(/\\n/g, '\n');
const EmbedFooterIcon = interaction.options.getString("footericon");
const EmbedAuthor = interaction.options.getString("author")?.replace(/\\n/g, '\n');
const EmbedAuthorIcon = interaction.options.getString("authoricon");
const EmbedAuthorURL = interaction.options.getString("authorurl");
const EmbedTimestamp = interaction.options.getBoolean("timestamp");
const InlineFields = interaction.options.getBoolean("inline");
const EmbedMsg = new Discord.MessageEmbed()
const EmbedMsg = new Discord.EmbedBuilder()
.setTitle(EmbedTitle)
.setDescription(EmbedSubject)
.setColor(EmbedColour)
.setColor(GeneralColour)
.setThumbnail(EmbedThumbnail)
.setImage(EmbedImage)
if (EmbedFieldTitle && EmbedFieldContent) { EmbedMsg.addField(EmbedFieldTitle, EmbedFieldContent) };
.setImage(EmbedImage);
if (EmbedFields) {
const fieldsArray = EmbedFields.split(';');
fieldsArray.forEach(field => {
const [title, value] = field.split('|');
if (title && value) {
EmbedMsg.addFields({ name: title.replace(/\\n/g, '\n'), value: value.replace(/\\n/g, '\n'), inline: InlineFields || false });
}
});
}
if (EmbedFooter) {
EmbedMsg.setFooter({ text: EmbedFooter, iconURL: EmbedFooterIcon });
}
if (EmbedAuthor) {
EmbedMsg.setAuthor({ name: EmbedAuthor, iconURL: EmbedAuthorIcon, url: EmbedAuthorURL });
}
if (EmbedTimestamp) {
EmbedMsg.setTimestamp();
}
interaction.reply({
embeds: [EmbedMsg],
+40 -56
View File
@@ -2,88 +2,72 @@
// Help Command
const fs = require("fs");
const Discord = require("discord.js");
const yaml = require("js-yaml");
const supportbot = yaml.load(
fs.readFileSync("./Configs/supportbot.yml", "utf8")
);
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
const cmdconfig = yaml.load(
fs.readFileSync("./Configs/commands.yml", "utf8")
);
const Command = require("../Structures/Command.js");
module.exports = new Command({
name: cmdconfig.HelpCommand,
description: cmdconfig.HelpCommandDesc,
name: cmdconfig.Help.Command,
description: cmdconfig.Help.Description,
type: Discord.ApplicationCommandType.ChatInput,
options: [],
permissions: ["SEND_MESSAGES"],
permissions: cmdconfig.Help.Permission,
async run(interaction) {
const { getRole } = interaction.client;
let SupportStaff = await getRole(supportbot.Staff, interaction.guild);
let Admin = await getRole(supportbot.Admin, interaction.guild);
if (!SupportStaff || !Admin)
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."
);
let botCommands = "";
botCommands += `**/${cmdconfig.HelpCommand}** ${cmdconfig.HelpCommandDesc}\n`;
botCommands += `**/${cmdconfig.InfoCommand}** ${cmdconfig.InfoCommandDesc}\n`;
botCommands += `**/${cmdconfig.PingCommand}** ${cmdconfig.PingCommandDesc}\n`;
botCommands += `**/${cmdconfig.UserInfoCommand}** ${cmdconfig.UserInfoCommandDesc}\n`;
if (supportbot.DisableSuggestions === false) {
botCommands += `**/${cmdconfig.SuggestCommand}** ${cmdconfig.SuggestCommandDesc}\n`;
}
let ticketCommands = "";
ticketCommands += `**/${cmdconfig.OpenTicket}** ${cmdconfig.OpenTicketDesc}\n`;
ticketCommands += `**/${cmdconfig.CloseTicket}** ${cmdconfig.CloseTicketDesc}\n`;
// Fetch all commands from the client's commands collection
let botCommands = "";
interaction.client.commands.forEach((command) => {
if (command.permissions) {
botCommands += `\`${command.name}\` `;
}
});
let staffCommands = "";
staffCommands += `**/${cmdconfig.TicketAdd}** ${cmdconfig.TicketAddDesc}\n`;
staffCommands += `**/${cmdconfig.TicketRemove}** ${cmdconfig.TicketRemoveDesc}\n`;
staffCommands += `**/${cmdconfig.UserInfoCommand}** ${cmdconfig.UserInfoCommandDesc}\n`;
staffCommands += `**/${cmdconfig.TranslateCommand}** ${cmdconfig.TranslateCommandDesc}\n`;
const HelpEmbed1 = new Discord.MessageEmbed()
.setTitle(supportbot.Name + " Commands")
// Create embed
const HelpEmbed1 = new Discord.EmbedBuilder()
.setTitle(supportbot.General.Name + " Commands")
.setThumbnail(interaction.user.displayAvatarURL())
.addFields(
{
name: "🖥️ General Commands\n",
value: `${botCommands}\n`,
inline: false,
},
{
name: "🎫 Support Commands\n",
value: `${ticketCommands}\n`,
inline: false,
}
)
.setColor(supportbot.EmbedColour)
.setFooter({
text: supportbot.EmbedFooter,
iconURL: interaction.user.displayAvatarURL(),
.setColor(supportbot.Embed.Colours.General)
.addFields({
name: "🖥️ Commands\n",
value: botCommands || "No commands available.",
inline: false,
});
if (
interaction.member.roles.cache.has(SupportStaff.id) ||
interaction.member.roles.cache.has(Admin.id)
) {
// Conditionally add Addons section
if (supportbot.General.Addons.Enabled) {
HelpEmbed1.addFields({
name: "🔐 Staff Commands\n",
value: `${staffCommands}\n`,
name: "🛠️ Addons\n",
value: "No addons available.",
inline: false,
});
}
interaction.reply({
ephemeral: true,
embeds: [HelpEmbed1],
});
HelpEmbed1.setFooter({
text: supportbot.Embed.Footer,
iconURL: interaction.user.displayAvatarURL(),
});
interaction.reply({
ephemeral: true,
embeds: [HelpEmbed1],
});
},
});
+30 -19
View File
@@ -5,36 +5,47 @@ 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 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.InfoCommand,
description: cmdconfig.InfoCommandDesc,
name: cmdconfig.Info.Command,
description: cmdconfig.Info.Description,
options: [],
permissions: ["SEND_MESSAGES"],
permissions: cmdconfig.Info.Permission,
async run(interaction) {
const InfoButton = new Discord.MessageButton()
.setLabel(supportbot.InfoButtonText)
.setURL(supportbot.InfoURL)
.setStyle("LINK");
let disableCommand = true;
const inforow = new Discord.MessageActionRow().addComponents(InfoButton);
const InfoButton = new Discord.ButtonBuilder()
.setLabel(msgconfig.Info.Button)
.setURL(msgconfig.Info.URL)
.setStyle("Link");
const InfoEmbed = new Discord.MessageEmbed()
.setURL(supportbot.InfoURL)
.setTitle(supportbot.InfoTitle)
.setDescription(supportbot.AboutMe)
.setColor(supportbot.InfoColour);
const inforow = new Discord.ActionRowBuilder().addComponents(InfoButton);
const InfoEmbed = new Discord.EmbedBuilder()
.setURL(msgconfig.Info.URL)
.setTitle(msgconfig.Info.Title)
.setDescription(msgconfig.Info.Description)
.setColor(msgconfig.Info.Colour)
interaction.reply({
embeds: [InfoEmbed],
components: [inforow],
});
interaction.reply({
embeds: [InfoEmbed],
components: [inforow],
});
},
});
});
+7 -5
View File
@@ -13,17 +13,19 @@ const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
const Command = require("../Structures/Command.js");
module.exports = new Command({
name: cmdconfig.PingCommand,
description: cmdconfig.PingCommandDesc,
name: cmdconfig.Ping.Command,
description: cmdconfig.Ping.Description,
options: [],
permissions: ["SEND_MESSAGES"],
permissions: cmdconfig.Ping.Permission,
async run(interaction) {
const PingEmbed = new Discord.MessageEmbed()
let disableCommand = true;
const PingEmbed = new Discord.EmbedBuilder()
.setDescription(
`:ping_pong: **Ping:** \`${interaction.client.ws.ping} ms\``
)
.setColor(supportbot.EmbedColour);
.setColor(supportbot.Embed.Colours.General);
interaction.reply({
embeds: [PingEmbed],
-94
View File
@@ -1,94 +0,0 @@
// SupportBot | Emerald Services
// Ticket Remove Command
const fs = require("fs");
const Discord = require("discord.js");
const yaml = require("js-yaml");
const supportbot = yaml.load(
fs.readFileSync("./Configs/supportbot.yml", "utf8")
);
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
const Command = require("../Structures/Command.js");
module.exports = new Command({
name: cmdconfig.TicketRemove,
description: cmdconfig.TicketRemoveDesc,
options: [
{
name: "user",
description: "User to remove",
type: "USER",
required: true,
},
],
permissions: ["SEND_MESSAGES"],
async run(interaction) {
const { getRole } = interaction.client;
let Staff = await getRole(supportbot.Staff, interaction.guild);
let Admin = await getRole(supportbot.Admin, interaction.guild);
if (!Staff || !Admin)
return interaction.reply(
"Some roles seem to be missing!\nPlease check for errors when starting the bot."
);
const NoPerms = new Discord.MessageEmbed()
.setTitle("Invalid Permissions!")
.setDescription(
`${supportbot.IncorrectPerms}\n\nRole Required: \`${supportbot.Staff}\` or \`${supportbot.Admin}\``
)
.setColor(supportbot.WarningColour);
if (
!interaction.member.roles.cache.has(Staff.id) &&
!interaction.member.roles.cache.has(Admin.id)
)
return interaction.reply({ embeds: [NoPerms] });
let TicketData = await JSON.parse(
fs.readFileSync("./Data/TicketData.json", "utf8")
);
let ticket = await TicketData.tickets.findIndex(
(t) => t.id === interaction.channel.id
);
if (ticket === -1) {
const Exists = new Discord.MessageEmbed()
.setTitle("No Ticket Found!")
.setDescription(supportbot.NoValidTicket)
.setColor(supportbot.WarningColour);
return interaction.reply({ ephemeral: true, embeds: [Exists] });
}
let uMember = interaction.options.getUser("user");
const UserNotExist = new Discord.MessageEmbed()
.setTitle("User Not Found!")
.setDescription(
`${supportbot.UserNotFound}\n\nTry Again:\`/${cmdconfig.TicketRemove} <user#0000>\``
)
.setColor(supportbot.ErrorColour);
if (!uMember) return interaction.reply({ ephemeral: true, embeds: [UserNotExist] });
await interaction.channel.permissionOverwrites.edit(uMember.id, {
VIEW_CHANNEL: false,
});
const Complete = new Discord.MessageEmbed()
.setTitle("User Removed!")
.setDescription(supportbot.RemovedUser.replace(/%user%/g, uMember.id))
.setTimestamp()
.setColor(supportbot.EmbedColour);
interaction.reply({ embeds: [Complete] });
TicketData.tickets[ticket].subUsers = TicketData.tickets[
ticket
].subUsers.filter((u) => u !== uMember.id);
fs.writeFileSync(
"./Data/TicketData.json",
JSON.stringify(TicketData, null, 4),
(err) => {
if (err) console.error(err);
}
);
},
});
+73 -42
View File
@@ -1,84 +1,115 @@
// SupportBot | Emerald Services
// Suggest Command
const fs = require("fs");
const Discord = require("discord.js");
const yaml = require("js-yaml");
const supportbot = yaml.load(
fs.readFileSync("./Configs/supportbot.yml", "utf8")
);
// Load configurations
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");
// Ensure suggestions file exists and is correctly loaded
let suggestions;
try {
suggestions = require("../Data/SuggestionData.json");
} catch (error) {
suggestions = {};
}
module.exports = new Command({
name: cmdconfig.SuggestCommand,
description: cmdconfig.SuggestCommandDesc,
name: cmdconfig.Suggestion.Command,
description: cmdconfig.Suggestion.Description,
type: Discord.ApplicationCommandType.ChatInput,
options: [
{
name: "suggestion",
description: "Create a Suggestion",
type: "STRING",
type: Discord.ApplicationCommandOptionType.String,
required: true,
},
],
permissions: ["SEND_MESSAGES"],
permissions: cmdconfig.Suggestion.Permission,
async run(interaction) {
const { getChannel } = interaction.client;
const suggestChannel = await getChannel(
supportbot.SuggestionChannel,
supportbot.Suggestions.Channel,
interaction.guild
);
const NoChannel = new Discord.MessageEmbed()
const NoChannel = new Discord.EmbedBuilder()
.setTitle("Missing Channel!")
.setDescription(supportbot.InvalidChannel)
.setColor(supportbot.ErrorColour);
.setDescription(msgconfig.Error.InvalidChannel)
.setColor(supportbot.Embed.Colours.Error);
if (!suggestChannel) return interaction.reply({ embeds: [NoChannel] });
let suggestion = interaction.options.getString("suggestion");
const SuggestEmbed = new Discord.MessageEmbed()
.addField("Suggestion", suggestion, true)
.addField("From", `<@${interaction.user.id}>`)
const SuggestEmbed = new Discord.EmbedBuilder()
.addFields(
{ name: "Suggestion", value: suggestion, inline: true },
{ name: "From", value: `<@${interaction.user.id}>` },
{ name: `${supportbot.Suggestions.UpvoteEmoji} ${supportbot.Suggestions.UpvoteTitle}`, value: "0", inline: true },
{ name: `${supportbot.Suggestions.DownvoteEmoji} ${supportbot.Suggestions.DownvoteTitle}`, value: "0", inline: true }
)
.setThumbnail(interaction.user.displayAvatarURL())
.setFooter({
text: supportbot.EmbedFooter,
text: supportbot.Embed.Footer,
iconURL: interaction.user.displayAvatarURL(),
})
.setColor(supportbot.EmbedColour);
.setColor(supportbot.Embed.Colours.General);
const suggestionMsg = await suggestChannel.send({ embeds: [SuggestEmbed] });
if (supportbot.SuggestionUpvote && supportbot.SuggestionDownvote) {
await suggestionMsg.react(supportbot.SuggestionUpvote);
await suggestionMsg.react(supportbot.SuggestionDownvote);
const UpvoteButton = new Discord.ButtonBuilder()
.setCustomId("upvote")
.setEmoji(supportbot.Suggestions.UpvoteEmoji)
.setStyle(supportbot.Suggestions.Buttons.Upvote);
if (supportbot.SuggestionThreads) {
await suggestionMsg.startThread({
name: `Suggestion-releated Thread`,
autoArchiveDuration: 60,
type: 'GUILD_PUBLIC_THREAD',
reason: 'Suggestion-releated thread',
})
}
const DownvoteButton = new Discord.ButtonBuilder()
.setCustomId("downvote")
.setEmoji(supportbot.Suggestions.DownvoteEmoji)
.setStyle(supportbot.Suggestions.Buttons.Downvote);
const RemoveVoteButton = new Discord.ButtonBuilder()
.setCustomId("removevote")
.setLabel(supportbot.Suggestions.Buttons.RemoveVote_Title)
.setStyle(supportbot.Suggestions.Buttons.RemoveVote); // Red button
const row = new Discord.ActionRowBuilder().addComponents(UpvoteButton, DownvoteButton, RemoveVoteButton);
const suggestionMsg = await suggestChannel.send({ embeds: [SuggestEmbed], components: [row] });
suggestions[suggestionMsg.id] = {
suggestion: suggestion,
author: interaction.user.id,
upvotes: [],
downvotes: []
};
fs.writeFileSync("./Data/SuggestionData.json", JSON.stringify(suggestions, null, 2));
if (supportbot.Suggestions.Threads.Enabled) {
await suggestionMsg.startThread({
name: supportbot.Suggestions.Threads.Title,
autoArchiveDuration: 60,
type: Discord.ChannelType.PublicThread,
reason: supportbot.Suggestions.Threads.Reason,
});
}
const Submitted = new Discord.MessageEmbed()
.setTitle("Suggestion Submitted!")
.setDescription(
`:white_check_mark: You have successfully submitted a suggestion.`
const Submitted = new Discord.EmbedBuilder()
.setTitle(`${msgconfig.Suggestions.Sent_Title}`)
.setDescription(`${msgconfig.Suggestions.Sent}`)
.addFields(
{ name: "Sent to:", value: `<#${suggestChannel.id}>` },
)
.addField("Sent to:", `<#${suggestChannel.id}>`)
.setColor(supportbot.SuccessColour);
.setColor(supportbot.Embed.Colours.Success);
return interaction.reply({
await interaction.reply({
ephemeral: true,
embeds: [Submitted]
})
});
},
});
+173 -194
View File
@@ -1,91 +1,84 @@
// SupportBot | Emerald Services
// Ticket Command
const fs = require("fs");
const {
MessageEmbed,
Permissions,
MessageButton,
MessageActionRow,
EmbedBuilder,
ApplicationCommandOptionType,
ApplicationCommandType,
ChannelType,
ActionRowBuilder,
ButtonBuilder,
StringSelectMenuBuilder,
StringSelectMenuOptionBuilder,
GuildMemberRoleManager,
} = require("discord.js");
const yaml = require("js-yaml");
const supportbot = yaml.load(
fs.readFileSync("./Configs/supportbot.yml", "utf8")
);
const panelconfig = yaml.load(fs.readFileSync("./Configs/ticket-panel.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");
const TicketNumberID = require("../Structures/TicketID.js");
module.exports = new Command({
name: cmdconfig.OpenTicket,
description: cmdconfig.OpenTicketDesc,
name: cmdconfig.OpenTicket.Command,
description: cmdconfig.OpenTicket.Description,
type: ApplicationCommandType.ChatInput,
options: [
{
name: "reason",
description: "Ticket Reason",
type: "STRING",
type: ApplicationCommandOptionType.String,
},
],
permissions: ["SEND_MESSAGES"],
permissions: cmdconfig.OpenTicket.Permission,
async run(interaction) {
let disableCommand = true;
if (interaction.isCommand() && disableCommand)
return interaction.reply({
content: "This command is disabled",
ephemeral: true,
});
let department = interaction.customId?.split("-")[1] || null;
let TicketData = await JSON.parse(
fs.readFileSync("./Data/TicketData.json", "utf8")
);
const { getRole, getCategory } = interaction.client;
let TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
const { getRole, getChannel } = interaction.client;
let User = interaction.guild.members.cache.get(interaction.user.id);
if (
supportbot.MaxAllowedTickets &&
TicketData.tickets.filter((t) => t.user === interaction.user.id && t.open)
.length >= supportbot.MaxAllowedTickets
supportbot.Ticket.TicketsPerUser &&
TicketData.tickets.filter((t) => t.user === interaction.user.id && t.open).length >= supportbot.Ticket.TicketsPerUser
) {
return interaction.reply({
embeds: [
{
title: "Too Many Tickets!",
description: `You can't have more than ${supportbot.MaxAllowedTickets} open tickets!`,
color: supportbot.WarningColour,
description: `You can't have more than ${supportbot.Ticket.TicketsPerUser} open tickets!`,
color: supportbot.Embed.Colours.Warn,
},
],
ephemeral: true,
});
}
if (
User.roles.cache.has(
getRole(supportbot.TicketBlackListRole, interaction.guild).id
)
) {
if (User.roles.cache.has(supportbot.Roles.ModRoles.Blacklisted)) {
return interaction.reply({
content: supportbot.TicketBlackListMessage,
content: msgconfig.Ticket.Blacklisted,
ephemeral: true,
});
}
if (User.roles.cache.has(supportbot.Roles.ModRoles.Muted)) {
return interaction.reply({
content: msgconfig.Ticket.Muted,
ephemeral: true,
});
}
// Ticket ID
let ticketNumberID = await TicketNumberID.pad();
const TicketSubject = interaction.options?.getString("reason") || msgconfig.Ticket.InvalidSubject;
// Ticket Subject
const TicketSubject =
interaction.options?.getString("reason") || supportbot.NoTicketSubject;
const TicketExists = new MessageEmbed()
const TicketExists = new EmbedBuilder()
.setTitle("Ticket Exists!")
.setDescription(supportbot.TicketExists);
.setDescription(msgconfig.Ticket.TicketExists);
if (
await interaction.guild.channels.cache.find(
(ticketChannel) =>
ticketChannel.name === `${supportbot.TicketPrefix}${ticketNumberID}`
(ticketChannel) => ticketChannel.name === `${supportbot.Ticket.Channel}${ticketNumberID}`
)
) {
return await interaction.reply({
@@ -93,50 +86,80 @@ module.exports = new Command({
ephemeral: true,
});
}
const Staff = await getRole(supportbot.Staff, interaction.guild);
const Admin = await getRole(supportbot.Admin, interaction.guild);
const Staff = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild);
const Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild);
if (!Staff || !Admin)
return interaction.reply({
content:
"Some roles seem to be missing!\nPlease check for errors when starting the bot.",
content: "Some roles seem to be missing!\nPlease check for errors when starting the bot.",
ephemeral: true,
});
let TicketCategory = await getCategory(
supportbot.Departments[department].category,
interaction.guild
const TicketCategory = interaction.guild.channels.cache.find(
(c) => c.name === supportbot.Ticket.TicketCategory || c.id === supportbot.Ticket.TicketCategory
);
const ticketChannel = await interaction.guild.channels.create(
`${supportbot.TicketPrefix}${ticketNumberID}`,
{
type: "GUILD_TEXT",
parent: TicketCategory.id,
permissionOverwrites: [
{
id: Admin.id,
allow: [Permissions.FLAGS.VIEW_CHANNEL],
},
{
id: interaction.user.id,
allow: [Permissions.FLAGS.VIEW_CHANNEL],
},
{
id: interaction.guild.id,
deny: [Permissions.FLAGS.VIEW_CHANNEL],
},
],
}
);
if (supportbot.AllowAllStaff) {
await ticketChannel.permissionOverwrites.edit(Staff.id, {
VIEW_CHANNEL: true,
});
const channel = await getChannel(supportbot.Ticket.TicketCategory, interaction.guild);
const ticketChannel = await channel.threads.create({
name: `${supportbot.Ticket.Channel}${ticketNumberID}`,
type: ChannelType.PrivateThread,
parent: TicketCategory,
autoArchiveDuration: 60,
reason: 'support ticket',
});
if (supportbot.Ticket.ClaimTickets.Enabled === false) {
const InvStaff = await ticketChannel.send(`<@&${Staff.id}> <@&${Admin.id}>`)
setTimeout(() => {
InvStaff.delete();
}, 2000)
}
// TICKET CLAIMING SYSTEM [START]
if (supportbot.Ticket.ClaimTickets.Enabled) {
const InvStaff1 = await ticketChannel.send(`<@&${Admin.id}>`)
setTimeout(() => {
InvStaff1.delete();
}, 2000)
const claimChannel = await getChannel(
supportbot.Ticket.ClaimTickets.Channel,
interaction.guild
);
const claimEmbed = new EmbedBuilder()
.setTitle(msgconfig.Ticket.ClaimTickets.ClaimTitle)
.setDescription(msgconfig.Ticket.ClaimTickets.ClaimMessage
.replace('%user%', interaction.user.id)
)
.setColor(supportbot.Embed.Colours.General);
const claimButton = new ButtonBuilder()
.setCustomId(`claimticket-${ticketChannel.id}`)
.setLabel(supportbot.Ticket.ClaimTickets.ButtonTitle)
.setStyle(supportbot.Ticket.ClaimTickets.Button);
const row = new ActionRowBuilder().addComponents(claimButton);
await claimChannel.send({
content: `<@&${Staff.id}>`,
embeds: [claimEmbed],
components: [row]
});
}
// TICKET CLAIMING SYSTEM [END]
if (!department) {
await ticketChannel.permissionOverwrites.edit(interaction.user.id, {
SEND_MESSAGES: false,
});
await ticketChannel.members.add(interaction.user.id);
}
await TicketData.tickets.push({
TicketData.tickets.push({
id: ticketChannel.id,
name: ticketChannel.name,
user: interaction.user.id,
@@ -144,7 +167,10 @@ module.exports = new Command({
reason: TicketSubject,
open: true,
subUsers: [],
createdAt: new Date().toISOString(),
claimedAt: null, // Will be updated when claimed
});
fs.writeFileSync(
"./Data/TicketData.json",
JSON.stringify(TicketData, null, 4),
@@ -153,101 +179,103 @@ module.exports = new Command({
}
);
const CreatedTicket = new MessageEmbed()
const CreatedTicket = new EmbedBuilder()
.setDescription(
supportbot.TicketCreatedAlert.replace(
/%ticketauthor%/g,
interaction.user.id
)
msgconfig.Ticket.TicketCreatedAlert.replace(/%ticketauthor%/g, interaction.user.id)
.replace(/%ticketid%/g, ticketChannel.id)
.replace(/%ticketusername%/g, interaction.user.username)
)
.setColor(supportbot.EmbedColour);
.setColor(supportbot.Embed.Colours.General);
await interaction.reply({ embeds: [CreatedTicket], ephemeral: true });
if (supportbot.AllowTicketMentions) {
if (supportbot.Ticket.AllowMentions) {
await ticketChannel.send(`${interaction.user}`);
}
const TicketMessage = new MessageEmbed()
const TicketMessage = new EmbedBuilder()
.setAuthor({
name: msgconfig.Ticket.TicketAuthorTitle.replace(/%ticketauthor%/g, interaction.user.id)
.replace(/%ticketid%/g, ticketChannel.id)
.replace(/%ticketusername%/g, interaction.user.username),
iconURL: interaction.user.displayAvatarURL(),
})
.setTitle(
supportbot.Ticket_Title.replace(/%ticketauthor%/g, interaction.user.id)
msgconfig.Ticket.TicketTitle.replace(/%ticketauthor%/g, interaction.user.id)
.replace(/%ticketid%/g, ticketChannel.id)
.replace(/%ticketusername%/g, interaction.user.username)
)
.setDescription(
supportbot.TicketMessage.replace(/%ticketauthor%/g, interaction.user.id)
msgconfig.Ticket.TicketMessage.replace(/%ticketauthor%/g, interaction.user.id)
.replace(/%ticketid%/g, ticketChannel.id)
.replace(/%ticketusername%/g, interaction.user.username)
)
.setColor(supportbot.EmbedColour);
.setColor(supportbot.Embed.Colours.General);
if (supportbot.TicketSubject === "embed") {
if (TicketSubject !== "No Reason Provided.") {
TicketMessage.addFields({ name: "Reason", value: TicketSubject });
}
}
const SelectMenus = new StringSelectMenuBuilder()
.setCustomId("ticketcontrolpanel")
.setPlaceholder("Ticket Control Panel")
.addOptions(
new StringSelectMenuOptionBuilder()
.setLabel("Add User")
.setDescription("Add a user to the ticket.")
.setEmoji(supportbot.SelectMenus.Tickets.AddUserEmoji)
.setValue("ticketadduser"),
new StringSelectMenuOptionBuilder()
.setLabel("Remove User")
.setDescription("Remove a user from the ticket.")
.setEmoji(supportbot.SelectMenus.Tickets.RemoveUserEmoji)
.setValue("ticketremoveuser"),
new StringSelectMenuOptionBuilder()
.setLabel("Close")
.setDescription("Close the ticket.")
.setEmoji(supportbot.SelectMenus.Tickets.CloseEmoji)
.setValue("ticketclose"),
new StringSelectMenuOptionBuilder()
.setLabel("Enable Invites")
.setDescription("Enable invites.")
.setEmoji(supportbot.SelectMenus.Tickets.EnableInvitesEmoji)
.setValue("enableinvites"),
new StringSelectMenuOptionBuilder()
.setLabel("Disable Invites")
.setDescription("Disable invites.")
.setEmoji(supportbot.SelectMenus.Tickets.DisableInvitesEmoji)
.setValue("disableinvites"),
);
if (supportbot.TicketSubject === "description") {
if (TicketSubject !== "No Reason Provided.") {
await ticketChannel.setTopic(
`Reason: ${TicketSubject} - User ID: ${interaction.user.id} - Ticket: ${ticketChannel.name}`
);
}
}
const row2 = new ActionRowBuilder().addComponents(SelectMenus);
if (!department) {
TicketMessage.addFields({
name: "Departments",
value: ` **${supportbot.Departments.map((X) => X.title).join("\n")}**`,
});
}
const CloseButton = new MessageButton()
.setCustomId("ticketclose")
.setLabel("Close")
.setStyle(supportbot.TicketCloseColour)
.setEmoji(supportbot.TicketCloseEmoji);
const LockButton = new MessageButton()
.setCustomId("ticketlock")
.setLabel("Lock")
.setStyle(supportbot.TicketLockColour)
.setEmoji(supportbot.TicketLockEmoji);
const row2 = new MessageActionRow().addComponents(CloseButton, LockButton);
if (!department) {
try {
let buttons = await supportbot.Departments.map((x) =>
new MessageButton()
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 MessageActionRow().addComponents(buttons);
if (supportbot.Ticket.AllowMentions) {
await ticketChannel.send("<@&" + supportbot.Ticket.RoleMention + ">");
}
const row = new ActionRowBuilder().addComponents(buttons);
const m = await ticketChannel.send({
embeds: [TicketMessage],
components: [row],
components: [row2],
});
let collector;
try {
const filter = (i) => i.user.id === interaction.user.id;
collector = await m.awaitMessageComponent({
await m.awaitMessageComponent({
filter,
max: 1,
componentType: "BUTTON",
time: supportbot.Timeout * 60000,
time: supportbot.Ticket.Timeout * 60000,
});
} catch (e) {
if (e.code === "INTERACTION_COLLECTOR_ERROR") {
if (e.code === "COLLECTOR_ERROR") {
try {
TicketData = await JSON.parse(
fs.readFileSync("./Data/TicketData.json", "utf8")
);
let ticket = await TicketData.tickets.findIndex(
(t) => t.id === ticketChannel.id
);
let ticket = TicketData.tickets.findIndex((t) => t.id === ticketChannel.id);
TicketData.tickets[ticket].open = false;
fs.writeFileSync(
"./Data/TicketData.json",
@@ -259,71 +287,22 @@ module.exports = new Command({
interaction.user.send(
"Your ticket has timed out. Please open a new one, and select a department."
);
} catch (e) {}
} catch (error) {
console.error("An error occurred while handling collector:", error);
}
return await ticketChannel.delete();
}
}
let num = collector.customId.split("Department")[1];
let role = getRole(supportbot.Departments[num].role, interaction.guild);
let title = supportbot.Departments[num].title;
await ticketChannel.permissionOverwrites.edit(role.id, {
VIEW_CHANNEL: true,
});
await ticketChannel.permissionOverwrites.edit(interaction.user.id, {
SEND_MESSAGES: true,
});
await collector.reply({
embeds: [
{
description: `> Thank for reaching out to the **${title} Department**. Please provide us information regarding your query.`,
color: supportbot.EmbedColour,
},
],
});
if (supportbot.AllowTicketMentions) {
await ticketChannel.send("@here");
}
await collector.message.edit({
embeds: [TicketMessage],
components: [row2],
});
} catch (error) {}
} catch (error) {
console.error("Error sending message:", error);
}
} else {
await ticketChannel.send({
embeds: [TicketMessage],
components: [row2],
});
if (department) {
let role = await getRole(
supportbot.Departments[department].role,
interaction.guild
);
let title = supportbot.Departments[department].title;
await ticketChannel.permissionOverwrites.edit(role.id, {
VIEW_CHANNEL: true,
});
let TicketCat = await getCategory(
supportbot.Departments[department].category,
interaction.guild
);
await ticketChannel.setParent(TicketCat.id, { lockPermissions: false });
if (supportbot.AllowThanksForReachingOutMessage) {
await ticketChannel.send({
embeds: [
{
description: `> Thank for reaching out to the **${title} Department**. Please provide us information regarding your query.`,
color: supportbot.EmbedColour,
},
],
});
}
}
if (supportbot.AllowTicketMentions) {
await ticketChannel.send(supportbot.TicketRoleMention);
}
}
},
});
+115
View File
@@ -0,0 +1,115 @@
// ticketpanel.js
const fs = require("fs");
const {
EmbedBuilder,
ApplicationCommandType,
ActionRowBuilder,
ButtonBuilder,
} = require("discord.js");
const yaml = require("js-yaml");
const panelconfig = yaml.load(fs.readFileSync("./Configs/ticket-panel.yml", "utf8"));
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
const Command = require("../Structures/Command.js");
module.exports = new Command({
name: cmdconfig.TicketPanel.Command,
description: cmdconfig.TicketPanel.Description,
type: ApplicationCommandType.ChatInput,
options: [],
permissions: cmdconfig.TicketPanel.Permission,
async run(interaction) {
if (!panelconfig.Panel) {
console.log(
"\u001b[31m",
`[TICKET PANEL]`,
"\u001b[33m",
"Ticket Panel is not setup, You can set this in", `\u001b[31m`, '/Configs/ticket-panel.yml', `\n `,
);
return interaction.reply({
content: "Ticket Panel is not set up. Please configure it in `/Configs/ticket-panel.yml`.",
ephemeral: true,
});
}
const channelName = panelconfig.Channel;
const channel = interaction.guild.channels.cache.find(ch => ch.name === channelName);
if (!channel) {
console.log(`[TICKET PANEL] ${channelName} channel not found. Please check your config file.`);
return interaction.reply({
content: `${channelName} channel not found. Please check your config file.`,
ephemeral: true,
});
}
const panelid = JSON.parse(fs.readFileSync("./Data/ticket-panel-id.json", "utf8"));
try {
await channel.messages.fetch(panelid.TicketPanelID);
return interaction.reply({
content: "Ticket panel message already exists.",
ephemeral: true,
});
} catch (error) {
let embed = new EmbedBuilder()
.setTitle(panelconfig.PanelTitle)
.setColor(panelconfig.PanelColour)
.setFooter({
text: supportbot.Embed.Footer,
iconURL: interaction.user.displayAvatarURL(),
});
if (panelconfig.TicketPanel_Description) {
embed.setDescription(panelconfig.PanelMessage);
}
if (panelconfig.TicketPanel_Thumbnail) {
embed.setThumbnail(panelconfig.PanelThumbnail);
}
if (panelconfig.TicketPanel_Image) {
embed.setImage(panelconfig.PanelImage);
}
const createTicketButton = new ButtonBuilder()
.setCustomId("createticket")
.setLabel(panelconfig.Button.Text)
.setEmoji(panelconfig.Button.Emoji)
.setStyle(panelconfig.Button.Color);
let row = new ActionRowBuilder().addComponents(createTicketButton);
try {
const message = await channel.send({
embeds: [embed],
components: [row],
});
let data = {
id: panelid.id,
TicketPanelID: message.id,
};
fs.writeFileSync(
"./Data/ticket-panel-id.json",
JSON.stringify(data),
"utf8"
);
return interaction.reply({
content: "Ticket panel message has been sent!",
ephemeral: true,
});
} catch (e) {
console.log("Error sending message:", e);
return interaction.reply({
content: "An error occurred while sending the ticket panel message.",
ephemeral: true,
});
}
}
},
});
+56
View File
@@ -0,0 +1,56 @@
const { EmbedBuilder, ApplicationCommandType } = require("discord.js");
const fs = require("fs");
const yaml = require("js-yaml");
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
const Command = require("../Structures/Command.js");
module.exports = new Command({
name: cmdconfig.TicketStats.Command,
description: cmdconfig.TicketStats.Description,
type: ApplicationCommandType.ChatInput,
permissions: ["SendMessages"], // Adjust permissions as needed
async run(interaction) {
if (supportbot.Ticket.ClaimTickets.Enabled) {
const TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
const userId = interaction.user.id;
const ticketsClaimed = TicketData.tickets.filter(t => t.claimedBy === userId);
const ticketsOpen = ticketsClaimed.filter(t => t.open);
const totalTickets = ticketsClaimed.length;
let totalResponseTime = 0;
let responseCount = 0;
ticketsClaimed.forEach(ticket => {
if (ticket.claimedAt && ticket.createdAt) {
const responseTime = new Date(ticket.claimedAt) - new Date(ticket.createdAt);
totalResponseTime += responseTime;
responseCount++;
}
});
const averageResponseTime = responseCount ? totalResponseTime / responseCount : 0;
const averageResponseTimeMinutes = Math.round(averageResponseTime / 60000); // Convert to minutes
const statsEmbed = new EmbedBuilder()
.setTitle(msgconfig.TicketStats.Title)
.setDescription(`> Ticket stats for <@${interaction.user.id}>`)
.setColor(supportbot.Embed.Colours.General)
.addFields(
{ name: msgconfig.TicketStats.OpenTickets, value: `${ticketsOpen.length}`, inline: true },
{ name: msgconfig.TicketStats.TotalTickets, value: `${totalTickets}`, inline: true },
{ name: msgconfig.TicketStats.ResponseTime, value: `${averageResponseTimeMinutes} minutes`, inline: false }
);
await interaction.reply({ embeds: [statsEmbed], ephemeral: true });
}
}
});
+29 -20
View File
@@ -5,51 +5,60 @@ 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 Command = require("../Structures/Command.js");
const translate = require("@vitalets/google-translate-api");
module.exports = new Command({
name: cmdconfig.TranslateCommand, // Name of command
description: cmdconfig.TranslateCommandDesc, // Description of command
name: cmdconfig.Translate.Command,
description: cmdconfig.Translate.Description,
type: Discord.ApplicationCommandType.ChatInput,
options: [
{
type: "STRING",
type: Discord.ApplicationCommandOptionType.String,
name: "language",
description: "A 2 letter language code",
required: true,
},
{
type: "STRING",
type: Discord.ApplicationCommandOptionType.String,
name: "text",
description: "The text to translate",
required: true,
}, // The input text for the translation
],
permissions: ["SEND_MESSAGES"], // The permission the user/role at least requires
permissions: cmdconfig.Translate.Permission, // The permission the user/role at least requires
async run(interaction) {
let disableCommand = true;
const { getRole } = interaction.client;
let SupportStaff = await getRole(supportbot.Staff, interaction.guild);
let Admin = await getRole(supportbot.Admin, interaction.guild);
if (!SupportStaff || !Admin)
return interaction.reply(
"Some roles seem to be missing!\nPlease check the error logs."
);
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.MessageEmbed()
const NoPerms = new Discord.EmbedBuilder()
.setTitle("Invalid Permissions!")
.setDescription(
`${supportbot.IncorrectPerms}\n\nRole Required: \`${supportbot.Staff}\` or \`${supportbot.Admin}\``
`${msgconfig.Error.IncorrectPerms}\n\nRole Required: \`${supportbot.Roles.StaffMember.Staff}\` or \`${supportbot.Roles.StaffMember.Admin}\``
)
.setColor(supportbot.WarningColour);
.setColor(supportbot.Embed.Colours.Warn);
if (
!interaction.member.roles.cache.has(SupportStaff.id) &&
@@ -62,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.TranslateLogChannel,
supportbot.Translate.Log,
interaction.guild
); // Grab the set logging channel for translations
@@ -72,8 +81,8 @@ module.exports = new Command({
if (err && err.code == 400) {
await interaction.reply({
embeds: [
new Discord.MessageEmbed()
.setColor(supportbot.WarningColour)
new Discord.EmbedBuilder()
.setColor(supportbot.Embed.Colours.Warn)
.setTitle("Valid Language Codes")
.setURL(url)
.setDescription(
@@ -87,9 +96,9 @@ module.exports = new Command({
if (!result) return;
// Embed containing language code, original text and translated text
let transembed = new Discord.MessageEmbed()
let transembed = new Discord.EmbedBuilder()
.setAuthor({ name: interaction.user.tag, iconURL: interaction.user.displayAvatarURL({ dynamic: true }) })
.setColor(supportbot.SuccessColour)
.setColor(supportbot.Embed.Colours.Success)
.setDescription(`**Translation to ${lang}**`)
.addFields(
{ name: "Original text", value: text },
+32 -22
View File
@@ -6,43 +6,53 @@ 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")
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.UserInfoCommand, // Name of command
description: cmdconfig.UserInfoCommandDesc, // Description of command
name: cmdconfig.UserInfo.Command,
description: cmdconfig.UserInfo.Description,
type: Discord.ApplicationCommandType.ChatInput,
options: [
{
type: "USER",
type: Discord.ApplicationCommandOptionType.User,
name: "user",
description: "The user you want to get info about.",
required: true,
}, // The user input
],
permissions: ["SEND_MESSAGES"], // The permission the user/role at least requires
permissions: cmdconfig.UserInfo.Permission,
async run(interaction) {
const { getRole } = interaction.client;
let SupportStaff = await getRole(supportbot.Staff, interaction.guild);
let Admin = await getRole(supportbot.Admin, interaction.guild);
if (!SupportStaff || !Admin)
return interaction.reply(
"Some roles seem to be missing!\nPlease check the error logs."
);
let disableCommand = true;
const NoPerms = new Discord.MessageEmbed()
.setTitle("Invalid Permissions!")
.setDescription(
`${supportbot.IncorrectPerms}\n\nRole Required: \`${supportbot.Staff}\` or \`${supportbot.Admin}\``
)
.setColor(supportbot.WarningColour);
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) &&
@@ -53,10 +63,10 @@ module.exports = new Command({
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.MessageEmbed()
const UserNotExist = new Discord.EmbedBuilder()
.setTitle("User Not Found!")
.setDescription(
`${supportbot.UserNotFound}\n\nTry Again:\`/${cmdconfig.UserInfoCommand} <@!User_ID> or @User\``
`${msgconfig.Error.UserNotFound}\n\nTry Again:\`/${cmdconfig.UserInfoCommand} <@!User_ID> or @User\``
)
.setColor(supportbot.ErrorColour);
@@ -90,7 +100,7 @@ module.exports = new Command({
const userFlags = user.flags.toArray();
// Embed containing the userinfo
let userembed = new Discord.MessageEmbed()
let userembed = new Discord.EmbedBuilder()
.setTitle(`User Info for User: ${user.tag}`)
.setThumbnail(member.displayAvatarURL({ dynamic: true, size: 512 }))
.setColor(member.roles.highest.hexColor)
@@ -110,7 +120,7 @@ module.exports = new Command({
{ 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.EmbedFooter })
.setFooter({ text: supportbot.Footer })
.setTimestamp();
// Send embed [userembed]
+81 -17
View File
@@ -7,25 +7,89 @@
# SupportBot created by Emerald Services
# Installed with MIT License
#
# Discord Support: https://emeraldsrv.dev/discord
# Community Resources: https://emeraldsrv.dev/resources
# Discord Support: https://dsc.gg/emerald-dev
# Official Documentation: https://emeraldsrv.dev/wiki
# Third-Party Documentation: https://emeraldsrv.dev/third-party
#
# Command Configuration
# -------------------------------
# Command Name Configuration
# -------------------------------
Help:
Command: "help"
Description: "Get a list of all the commands."
Permission: ["SendMessages"]
Enabled: true
Blacklist: "blacklist"
TicketAdd: "add"
TicketRemove: "remove"
OpenTicket: "ticket"
CloseTicket: "close"
InfoCommand: "info"
PingCommand: "ping"
HelpCommand: "help"
EmbedCommand: "embed"
SuggestCommand: "suggest"
TranslateCommand: "translate"
UserInfoCommand: "userinfo"
EnableAI:
Command: "enableai"
Description: "Talk with SupportBot"
Permission: ["SendMessages"]
Enabled: true
Info:
Command: "info"
Description: "Get information about the **ServerName**."
Permission: ["SendMessages"]
Enabled: true
TicketPanel:
Command: "ticketpanel"
Description: "Create the ticket creation panel"
Permission: ["Administrator"]
Enabled: true
TicketStats:
Command: "ticketstats"
Description: "Shows staff members their ticket statistics"
Permission: ["SendMessages"]
# Command is automatically disabled when the TicketClaims is disabled.
OpenTicket:
Command: "ticket"
Description: "Create a support ticket."
Permission: ["SendMessages"]
Enabled: true
Add:
Command: "add"
Description: "Add a user to the ticket."
Permission: ["SendMessages"]
Enabled: true
CloseTicket:
Command: "close"
Description: "Close a support ticket"
Permission: ["SendMessages"]
Enabled: true
Suggestion:
Command: "suggest"
Description: "Create a suggestion."
Permission: ["SendMessages"]
Enabled: true
Embed:
Command: "embed"
Description: "Build a discord embed."
Permission: ["SendMessages"]
Enabled: true
Translate:
Command: "translate"
Description: "Translate something to english"
Permission: ["SendMessages"]
Enabled: false
UserInfo:
Command: "userinfo"
Description: "Get info about a user."
Permission: ["SendMessages"]
Enabled: true
Ping:
Command: "ping"
Description: "Pong."
Permission: ["SendMessages"]
Enabled: true
# ---------------------------------------
# Command Description Configuration
+118
View File
@@ -0,0 +1,118 @@
# ___ _ ___ _
# / __> _ _ ___ ___ ___ _ _ _| |_ | . > ___ _| |_
# \__ \| | || . \| . \/ . \| '_> | | | . \/ . \ | |
# <___/`___|| _/| _/\___/|_| |_| |___/\___/ |_|
# |_| |_|
#
# SupportBot created by Emerald Services
# Installed with MIT License
#
# Discord Support: https://dsc.gg/emerald-dev
# Official Documentation: https://emeraldsrv.com/wiki
# Third-Party Documentation: https://emeraldsrv.com/third-party
#
# Messages Configuration
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"
Colour: "#f74343"
Welcome:
Embed:
Colour: "#f74343"
Title: "Welcome to ServerName"
Message: "> :smile: %joined_user% has just landed!"
Thumbnail: "BOT"
ImageEnabled: false
ImageURL: "https://animagelink.png/"
Leave:
Embed:
Colour: "#f74343"
Title: "Goodbye!"
Message: "> :slight_frown: %joined_user% has recently dropped out!"
Thumbnail: "BOT"
ImageEnabled: false
ImageURL: "https://animagelink.png/"
Suggestions:
Sent_Title: "Suggestion Sent!"
Sent: "> :white_check_mark: **You have successfully sent a suggestion.**"
NoSelfVote: "> :x: **You cannot vote on your own suggestion.**"
RemoveVoteButton: "Remove Vote"
Ticket:
# Available Placeholders:
# %ticketusername% - User who created the ticket.
# <@%ticketauthor%> - Tags the user who created the ticket.
# <#%ticketid%> - Mentions the ticket channel.
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."
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
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."
RemovedUserFromTicket_Title: "➖ Removed User!"
RemovedUserFromTicket: "> <@%user%> has been `Removed` from this ticket"
Blacklisted: "> :x: You are not allowed to open a ticket"
Muted: "> :x: You are `Muted` and therefore not allowed to open any tickets"
InvalidSubject: "> No Reason Provided!"
TranscriptSavedMessage: "📝 **Transcript Saved!**"
TicketLockMessage: "❌ **Ticket Locked!**"
TicketDeleteMessage: "⚠ **Ticket Deleting!**"
ClosingTicket: "> :warning: You have requested to close your support ticket."
ConfirmClose: "> Please confirm closing the ticket by clicking the button below."
CloseTimeout: "> Ticket close confirmation timed out."
ClaimTickets:
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%>."
ClaimedTitle: "Ticket Claimed!"
Claimed: "> You have successfully claimed this ticket. <#%channel%>"
NoPermsToClaimTitle: "Error, no permissions!"
NoPermsToClaim: "> :x: **Err!** You do not permissions to claim this ticket."
TicketStats:
OpenTickets: "✅ Open Tickets"
TotalTickets: "🎫 Total Tickets"
ResponseTime: "⏱️ Average Response Time"
Title: "Your Ticket Stats"
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!"
+40
View File
@@ -0,0 +1,40 @@
# -------------------------
# SupportBot AI
#
# In order to use the SupportBot AI, You need to setup an OpenAI Account.
# this can be done at https://platform.openai.com/
# -------------------------
Enabled: true
General:
Name: "SupportBot AI"
# What would you like to call your AI
Model: "gpt-4o"
# 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_API_KEY"
# To create an OpenAI API key, go to https://platform.openai.com/api-keys
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: "0000000000000000000"
# This is the channel where AI will respond in without needing to mention the bot.
Embed:
Color: 0x0cc559 # 0x<hexcode>
Footer: "SupportBot | Developed by Emerald Development"
Messages:
MessageTooLong: "The response was too long, so I've posted it here: {pastebin}"
ErrorResponse: "I didn't receive a proper response from OpenAI. Please try again later."
OpenAIError: "I'm having some trouble talking to SupportBot AI's Model, OpenAI. Please try again later."
AIDisabled: "I am disabled, You have enable me in `supportbot-ai.yml`"
+160 -203
View File
@@ -4,256 +4,213 @@
# <___/`___|| _/| _/\___/|_| |_| |___/\___/ |_|
# |_| |_|
#
# SupportBot created by Emerald Services
# SupportBot created by Emerald Development
# Installed with MIT License
#
# Discord Support: https://emeraldsrv.dev/discord
# Community Resources: https://emeraldsrv.dev/resources
# Discord Support: https://dsc.gg/emerald-dev
# Official Documentation: https://github.com/Emerald-Services/SupportBot/wiki
# Third-Party Documentation: COMING SOON
SupportBot_Version: "v7.7"
SupportBot_Version: "v8.0"
# Do not change the version number
# -------------------------
# General Configuration
# General
# -------------------------
Name: "SupportBot"
Token: "BOT_TOKEN HTTPS://DISCORD.COM/DEVELOPERS"
General:
Name: "SupportBot"
Token: "BOT_TOKEN_HERE"
# To get your Discord Bot Token, Go to https://discord.com/developers/applications
Addons:
Enabled: false
# -------------------------
# Activity Configuration
# Activity
# -------------------------
BotActivity: "Here to help!"
StreamingURL: "https://www.twitch.tv/YOUR_TWITCH_NAME"
# Set The ActivityType to "STREAMING" for a streaming status!
ActivityType: "PLAYING"
# Activity status can either be PLAYING, WATCHING, LISTENING or STREAMING.
Activity:
Status: "SupportBot v8"
Type: "Competing"
# The Type can either be Playing, Watching, Listening or Competing
StreamingURL: "https://www.twitch.tv/YOUR_TWITCH_NAME"
# Set The Type to "Streaming" for a streaming status!
# -------------------------
# Embed Configuration
# Embeds
# -------------------------
EmbedColour: "#f74343"
EmbedFooter: "SupportBot | Developed by Emerald Services"
Embed:
Colours:
General: 0x0cc559 # 0x<hexcode>
Success: 0x04b545 # 0x<hexcode>
Error: 0xd13030 # 0x<hexcode>
Warn: 0xfca117 # 0x<hexcode>
ErrorColour: "#d12519"
SuccessColour: "#04b545"
WarningColour: "#fca117"
Footer: "SupportBot | Developed by Emerald Development"
# -------------------------
# Role Configuration
# Roles
# -------------------------
AutoRole: true
JoinRole: "Member"
Roles:
AutoRole:
Enabled: true
Role: "Member"
Staff: "Staff"
Admin: "Admin"
TicketBlackListRole: "Blacklisted"
StaffMember:
Staff: "Staff"
Admin: "Admin"
ModRoles:
Muted: "Muted"
Blacklisted: "Blacklisted"
# ----------------------------
# Join/Leave Configuration
# System Messages
# ----------------------------
Welcome: true
# set to false to disable joining logs
Welcome:
Enabled: true
Channel: "🚨┃join-leave-logs"
WelcomeChannel: "join-leave-logs"
WelcomeColour: "#f74343"
WelcomeTitle: "User Joined!"
WelcomeMessage: "%joined_user% has joined the server!"
# Available Placeholders:
# %joined_user% - Mentions the user who joined
EmbedWelcomeImage: true
EmbedWelcomeImageURL: "https://media.discordapp.net/attachments/958247876344025108/986777740738986094/unknown.png"
EmbedWelcomeThumbnail: "BOT"
# "USER" will display the user's profile picture.
# "BOT" will display the bot's profile picture.
# false to disable
Leave: true
# set to false to disable leaving logs
LeaveChannel: "join-leave-logs"
LeaveColour: "#f74343"
LeaveTitle: "User Left!"
LeaveMessage: "%joined_user% has left the server!"
# Available Placeholders:
# %joined_user% - Mentions the user who joined
EmbedLeaveImage: true
EmbedLeaveImageURL: "https://cdn.discordapp.com/attachments/958247876344025108/986778170223120384/unknown.png"
EmbedLeaveThumbnail: false
# "USER" will display the user's profile picture.
# "BOT" will display the bot's profile picture.
# false to disable
# -------------------------
# Information Configuration
# -------------------------
InfoTitle: "Information"
InfoButtonText: "Visit Site"
InfoURL: "https://emeraldsrv.dev"
InfoColour: "#f74343"
Leave:
Enabled: true
Channel: "🚨┃join-leave-logs"
# ----------------------------
# Suggestions Configuration
# Suggestions
# ----------------------------
SuggestionChannel: "suggestions"
SuggestionUpvote: "✅"
SuggestionDownvote: "❌"
Suggestions:
Channel: "📗┃suggestions"
UpvoteEmoji: "✅"
DownvoteEmoji: "❌"
SuggestionThreads: true
# set to false to disable threads being created from suggestions.
UpvoteTitle: "Yes"
DownvoteTitle: "No"
DisableSuggestions: false
# Set to true to disable the suggestions feature.
OwnSuggestion: true
# If false this will not allow the user to vote for a suggestion they created
# If true this will allow the user to vote for a suggestion they created
ShowUsers: true
Threads:
Enabled: true
Title: "Suggestion-releated Thread"
Reason: "A thread releted to this suggestion"
Buttons:
Upvote: "2"
Downvote: "2"
RemoveVote: "4"
RemoveVote_Title: "Remove Vote"
# Available Button Styles
# 1 - Blurple
# 2 - Grey
# 3 - Green
# 4 - Red
# -------------------------
# Translate Configuration
# Tickets
# -------------------------
TranslateLogChannel: "translations-log"
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!
Channel: "ticket-" # The prefix for ticket channel names
TicketsPerUser: 100
DMTranscripts: true
TicketReason: true
# If ticket reason is set to false, It will not prompt to create a reason
TicketSubject: "description"
# "description" will display the ticket reason within the channel description
# "embed" will display the ticket reason within the ticket embed
Timeout: 5
# Set to true to allow all staff to access the ticket
Log:
TicketLog: "🚨┃ticket-data"
TicketLog_Title: "Ticket Closed"
DisableTicketLogChannel: false
TranscriptLog: "🚨┃ticket-data"
TranscriptTitle: "Ticket Transcript"
Close:
StaffOnly: true
# Set to false is anyone is able to close a ticket.
Confirmation_Button: "Confirm Close"
Title: "Are you sure you want to close this ticket?"
Invites:
StaffOnly: true
ClaimTickets:
Enabled: true
Channel: "1249723473757077624"
# Channel name or Channel ID
ButtonTitle: "Claim Ticket"
Button: "1"
# Available Button Styles
# 1 - Blurple
# 2 - Grey
# 3 - Green
# 4 - Red
# -------------------------
# Tickets Configuration
# Departments - NOT IN THIS UPDATE
# -------------------------
TicketPrefix: "ticket-" # The prefix for ticket channel names
TicketLog: "ticket-data"
TranscriptLog: "ticket-data"
DMTranscripts: true
TranscriptTitle: "Ticket Transcript"
TicketLog_Title: "Ticket Closed"
TicketBlackListMessage: "You are not allowed to open a ticket"
TicketRoleMention: "@here"
# To ping a role: <@ROLE_ID>
MaxAllowedTickets: 2
TicketSubject: "description"
# "description" will display the ticket reason within the channel description
# "embed" will display the ticket reason within the ticket embed
AllowTicketMentions: true
# Set to true to allow the ticket to mention the author and the support team.
# Set false to dis-allow mentions within tickets.
AllowThanksForReachingOutMessage: true
# Set to true to allow the "Thanks for reaching out Message" to be sent when the ticket is created
DisableTicketLogChannel: false
# Set to true to disable the requirement of the "TicketLog"
# Set to true to also disable the ticket logs to be sent in the "TicketLogs" channel
# --- Creation Config ---
# --- Start ---
# Available Placeholders:
# %ticketusername% - User who created the ticket.
# <@%ticketauthor%> - Tags the user who created the ticket.
# <#%ticketid%> - Mentions the ticket channel.
Ticket_Title: "Server Support | %ticketusername%"
TicketMessage: "Hello <@%ticketauthor%>, Thank you for creating a support ticket\nPlease wait patiently whilst a member of our team reaches out to you."
Ticket_Thumbnail: "https://images.emojiterra.com/google/android-pie/512px/1f39f.png"
NoTicketSubject: "No Reason Provided!"
TicketCreatedAlert: ":ticket: Your support ticket has been created: <#%ticketid%>"
AddedUser: "👍 <@%user%> has been added to this ticket"
RemovedUser: "👍 <@%user%> has been removed from this ticket"
# -------------------------
# Tickets Categories
# -------------------------
Timeout: 5
# Set to true to allow all staff to access the ticket
AllowAllStaff: false
# Ticket roles are the roles that will be able to gain access to tickets depending on what department has been chosen.
Departments:
- title: "Support"
- title: "Create Ticket"
role: "Staff"
color: "SECONDARY"
emoji: "1️⃣"
category: "Tickets"
color: "2"
emoji: "🎫"
category: "TICKETS"
- title: "Billing Support"
role: "Staff"
color: "SECONDARY"
emoji: "2️⃣"
category: "Tickets"
# -------------------------
# Buttons
# -------------------------
Buttons:
Tickets:
ClaimEmoji: "🧑‍💻"
ClaimStyle: "1"
- title: "Admin Team"
role: "Admin"
color: "SECONDARY"
emoji: "3️⃣"
category: "Tickets"
# Available Button Styles
# 1 - Blurple
# 2 - Grey
# 3 - Green
# 4 - Red
# - title: "Other"
# role: "Staff"
# color: "SECONDARY"
# emoji: "3️⃣"
# category: "Tickets"
# Discord Button Colour Selection
# PRIMARY - Blurple
# SECONDARY - Grey
# DANGER - Red
# SUCCESS - Green
# -------------------------
# Select Menus
# -------------------------
# --- Buttons Config ---
# --- Start ---
SelectMenus:
Tickets:
PanelEmoji: "⚙️"
CloseEmoji: "🗑️"
ArchiveEmoji: "♻️"
LockTicketEmoji: "🔒"
UnLockTicketEmoji: "🔓"
EnableInvitesEmoji: "🔒"
DisableInvitesEmoji: "🔓"
AddUserEmoji: "➕"
RemoveUserEmoji: "➖"
LockTicketCategory: "Archive"
TicketCloseEmoji: "❌"
TicketDeleteEmoji: "⛔"
TicketLockEmoji: "🔒"
# -------------------------
# Misc
# -------------------------
TicketCloseColour: "DANGER"
TicketDeleteColour: "DANGER"
TicketLockColour: "PRIMARY"
# Discord Button Colour Selection
# PRIMARY - Blue
# SECONDARY - Grey
# DANGER - Red
# SUCCESS - Green
TranscriptSavedMessage: "📝 **Transcript Saved!**"
TicketLockMessage: "❌ **Ticket Locked!**"
TicketDeleteMessage: "⚠ **Ticket Deleting!**" # Tickets deleting after 5 seconds by default.
# --- Closing Config ---
# --- Start ---
StaffOnly: true
ClosingTicket: ":warning: You have requested to close your support ticket."
ClosingConfirmation_Word: "confirm"
CloseConfirmation: true
# ---------------------------
# Error / Warning Messages
# ---------------------------
IncorrectPerms: ":x: **Err!** You do not have the correct permissions to use this command."
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!"
Translate:
Log: "🚨┃logs"
+20 -8
View File
@@ -14,21 +14,33 @@
# Ticket Panel Configuration
# -------------------------------
TicketPanel: true
Panel: true
# ----------------------------------------
# Ticket Panel Message Configuration
# Ticket Panel Message Configuration #2b2d31
# ----------------------------------------
Channel: "ticket-creation"
Channel: "📗┃tickets"
# The channel used to create the ticket panel, Use command /ticketpanel to generate it.
PanelTitle: "Need Help? Create a Support Ticket."
PanelColour: "#f74343"
PanelTitle: "SupportBot"
PanelColour: "#0cc559"
Button:
Text: "Create Ticket"
Emoji: "🎫"
Color: "2"
# Available Button Styles
# 1 - Blurple
# 2 - Grey
# 3 - Red
# 4 - Green
TicketPanel_Description: true
TicketPanel_Thumbnail: true
TicketPanel_Thumbnail: false
TicketPanel_Image: true
PanelMessage: "Click the button to open a new support ticket."
PanelMessage: "Click on the button to file a `Ticket` and our team will try their best to assist you in the best way we can!"
PanelThumbnail: "https://i.imgur.com/ieeZejl.png"
PanelImage: "https://i.imgur.com/5NkcmDq.png"
PanelImage: "https://i.imgur.com/dejQvkg.png"
BIN
View File
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
{}
+3
View File
@@ -0,0 +1,3 @@
{
}
+5 -1
View File
@@ -1 +1,5 @@
{"tickets": [{}]}
{
"tickets": [
{}
]
}
+1 -1
View File
@@ -1,4 +1,4 @@
{
"id": 0,
"TicketPanelID": ""
"TicketPanelID": "0000000000000000000"
}
+22 -13
View File
@@ -3,39 +3,48 @@ const Discord = require('discord.js');
const fs = require("fs");
const Event = require("../Structures/Event.js");
const yaml = require("js-yaml");
const supportbot = yaml.load(
fs.readFileSync("./Configs/supportbot.yml", "utf8")
fs.readFileSync("./Configs/supportbot.yml", "utf8")
);
const cmdconfig = yaml.load(
fs.readFileSync("./Configs/commands.yml", "utf8")
);
const msgconfig = yaml.load(
fs.readFileSync("./Configs/messages.yml", "utf8")
);
module.exports = new Event("guildMemberAdd", async (client, member, interaction, guild) => {
if (supportbot.Welcome) {
const WelcomeChannel = member.guild.channels.cache.find(channel => channel.name === supportbot.WelcomeChannel)
if (supportbot.Welcome.Enabled) {
const WelcomeChannel = member.guild.channels.cache.find(channel => channel.name === supportbot.Welcome.Channel)
const WelcomeEmbed = new Discord.MessageEmbed()
.setColor(supportbot.WelcomeColour)
.setTitle(supportbot.WelcomeTitle)
.setDescription(supportbot.WelcomeMessage.replace(/%joined_user%/g, member.user))
const WelcomeEmbed = new Discord.EmbedBuilder()
.setColor(msgconfig.Welcome.Embed.Colour)
.setTitle(msgconfig.Welcome.Embed.Title)
.setDescription(msgconfig.Welcome.Embed.Message.replace(/%joined_user%/g, member.user))
.setTimestamp();
if (supportbot.EmbedWelcomeThumbnail === "BOT") {
if (msgconfig.Welcome.Embed.Thumbnail === "BOT") {
WelcomeEmbed.setThumbnail(client.user.displayAvatarURL())
}
if (supportbot.EmbedWelcomeThumbnail === "USER") {
if (msgconfig.Welcome.Embed.Thumbnail === "USER") {
WelcomeEmbed.setThumbnail(member.user.displayAvatarURL())
}
if (supportbot.EmbedWelcomeImage) {
WelcomeEmbed.setImage(supportbot.EmbedWelcomeImageURL)
if (msgconfig.Welcome.Embed.ImageEnabled) {
WelcomeEmbed.setImage(msgconfig.Welcome.Embed.ImageURL)
}
WelcomeChannel.send({
embeds: [WelcomeEmbed]
})
if (supportbot.AutoRole) {
var role = member.guild.roles.cache.find(role => role.name === supportbot.JoinRole);
if (supportbot.Roles.AutoRole.Enabled) {
var role = member.guild.roles.cache.find(role => role.name === supportbot.Roles.AutoRole.Role);
member.roles.add(role);
}
+22 -13
View File
@@ -3,38 +3,47 @@ const Discord = require('discord.js');
const fs = require("fs");
const Event = require("../Structures/Event.js");
const yaml = require("js-yaml");
const supportbot = yaml.load(
fs.readFileSync("./Configs/supportbot.yml", "utf8")
fs.readFileSync("./Configs/supportbot.yml", "utf8")
);
const cmdconfig = yaml.load(
fs.readFileSync("./Configs/commands.yml", "utf8")
);
const msgconfig = yaml.load(
fs.readFileSync("./Configs/messages.yml", "utf8")
);
module.exports = new Event("guildMemberRemove", async (client, member, interaction, guild) => {
if (supportbot.Leave) {
const LeaveChannel = member.guild.channels.cache.find(channel => channel.name === supportbot.LeaveChannel)
if (supportbot.Leave.Enabled) {
const LeaveChannel = member.guild.channels.cache.find(channel => channel.name === supportbot.Leave.Channel)
const LeaveEmbed = new Discord.MessageEmbed()
.setColor(supportbot.LeaveColour)
.setTitle(supportbot.LeaveTitle)
.setDescription(supportbot.LeaveMessage.replace(/%joined_user%/g, member.user))
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))
.setTimestamp();
if (supportbot.EmbedLeaveThumbnail === "BOT") {
if (msgconfig.Leave.Embed.Thumbnail === "BOT") {
LeaveEmbed.setThumbnail(client.user.displayAvatarURL())
}
if (supportbot.EmbedLeaveThumbnail === "USER") {
if (msgconfig.Leave.Embed.Thumbnail === "USER") {
LeaveEmbed.setThumbnail(member.user.displayAvatarURL())
}
if (supportbot.EmbedLeaveImage) {
LeaveEmbed.setImage(supportbot.EmbedLeaveImageURL)
if (msgconfig.Leave.Embed.ImageEnabled) {
LeaveEmbed.setImage(msgconfig.Leave.Embed.ImageURL)
}
LeaveChannel.send({
embeds: [LeaveEmbed]
})
console.log(`\u001b[31m`, `[-]`, `\u001b[33m`, `${member.user.username} joined the server!`)
console.log(`\u001b[31m`, `[-]`, `\u001b[33m`, `${member.user.username} left the server!`)
}
});
+226 -79
View File
@@ -1,28 +1,31 @@
// SupportBot | Emerald Services
// Interaction Create Event
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 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 Event = require("../Structures/Event.js");
let suggestions;
try {
suggestions = require("../Data/SuggestionData.json");
} catch (error) {
suggestions = {};
}
module.exports = new Event("interactionCreate", async (client, interaction) => {
if (interaction.isCommand()) {
if (interaction.type == Discord.InteractionType.ApplicationCommand) {
const command = client.commands.find(
(cmd) => cmd.name.toLowerCase() === interaction.commandName
);
if (interaction.user.bot || !interaction.guild) return;
const NotValid = new Discord.MessageEmbed()
const NotValid = new Discord.EmbedBuilder()
.setDescription(`:x: \`Invalid Command\` `)
.setColor(supportbot.ErrorColour);
.setColor(supportbot.Embed.Colours.Error);
if (!command)
return interaction.reply({
@@ -31,11 +34,11 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
const permission = interaction.member.permissions.has(command.permissions);
const ValidPerms = new Discord.MessageEmbed()
const ValidPerms = new Discord.EmbedBuilder()
.setDescription(
":x: `Invalid Permissions` Do you have the correct permissions to execute this command?"
"> :x: `Invalid Permissions` Do you have the correct permissions to execute this command?"
)
.setColor(supportbot.ErrorColour);
.setColor(supportbot.Embed.Colours.Error);
if (!permission)
return interaction.reply({
@@ -43,11 +46,64 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
});
command.run(interaction);
}
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);
} else {
console.error("[X] Close Command Not Found");
}
break;
case "enableinvites":
await handleInvites(interaction, true);
break;
case "disableinvites":
await handleInvites(interaction, false);
break;
}
}
}
if (interaction.isButton()) {
if (
interaction.customId === "openticket" ||
interaction.customId.includes("department-")
) {
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 === "openticket") {
try {
const cmd = client.commands.get(cmdconfig.OpenTicket);
if (!cmd) return;
@@ -57,75 +113,166 @@ module.exports = new Event("interactionCreate", async (client, interaction) => {
}
}
if (interaction.customId === "ticketclose") {
try {
const cmd = client.commands.get(cmdconfig.CloseTicket);
if (!cmd) return;
cmd.run(interaction);
} catch (error) {
console.error(error);
if (interaction.customId.startsWith('claimticket-')) {
const ticketChannelId = interaction.customId.split('-')[1];
const ticketChannel = interaction.guild.channels.cache.get(ticketChannelId);
if (!ticketChannel) {
return interaction.reply({
content: "Ticket channel not found.",
ephemeral: true,
});
}
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);
const RolePerms = 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 (!Staff || !Admin) {
return interaction.reply({
embeds: [RolePerms],
ephemeral: true,
});
}
const NoPermsClaim = new Discord.EmbedBuilder()
.setTitle(msgconfig.Ticket.ClaimTickets.NoPermsToClaimTitle)
.setDescription(
msgconfig.Ticket.ClaimTickets.NoPermsToClaim.replace('%channel%', ticketChannel.name)
)
.setColor(supportbot.Embed.Colours.Error);
if (!interaction.member.roles.cache.has(Staff.id) && !interaction.member.roles.cache.has(Admin.id)) {
return interaction.reply({
embeds: [NoPermsClaim],
ephemeral: true,
});
}
await ticketChannel.members.add(interaction.user.id);
const pingclaimedstaff = await ticketChannel.send(`<@${interaction.user.id}>`);
setTimeout(() => {
pingclaimedstaff.delete();
}, 2000)
const TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
const ticketIndex = TicketData.tickets.findIndex((t) => t.id === ticketChannelId);
if (ticketIndex !== -1) {
TicketData.tickets[ticketIndex].claimedBy = interaction.user.id;
TicketData.tickets[ticketIndex].claimedAt = new Date().toISOString();
fs.writeFileSync("./Data/TicketData.json", JSON.stringify(TicketData, null, 4));
}
const claimMessage = await interaction.message.fetch();
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)
)
.setColor(supportbot.Embed.Colours.Success);
const successfullyClaimed = new Discord.EmbedBuilder()
.setTitle(msgconfig.Ticket.ClaimTickets.ClaimedTitle)
.setDescription(msgconfig.Ticket.ClaimTickets.Claimed.replace('%channel%', ticketChannel.id))
.setColor(supportbot.Embed.Colours.Success);
await claimMessage.edit({ embeds: [claimedEmbed], components: [] });
await interaction.reply({
embeds: [successfullyClaimed],
ephemeral: true,
});
}
if (interaction.customId === "ticketlock") {
if (interaction.channel.name.startsWith(supportbot.TicketPrefix)) {
interaction.message.fetch();
let Admin =
interaction.guild.roles.cache.find(
(AdminUser) => AdminUser.name === supportbot.Admin
) ||
interaction.guild.roles.cache.find(
(AdminUser) => AdminUser.id === supportbot.Admin
);
if (!interaction.member.roles.cache.has(Admin.id)) {
return await interaction.reply({
content: "You don't have permission to do that.",
ephemeral: true,
});
}
let all = interaction.channel.permissionOverwrites.cache;
let parent =
interaction.guild.channels.cache.find(
(c) => c.name === supportbot.LockTicketCategory
) ||
interaction.guild.channels.cache.find(
(c) => c.id === supportbot.LockTicketCategory
);
if (parent) {
await interaction.channel.setParent(parent.id);
}
// SUGGESTION INTERACTIONS [START]
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 });
all.forEach((perm) => {
interaction.channel.permissionOverwrites.delete(perm.id);
});
await interaction.channel.permissionOverwrites.create(Admin.id, {
VIEW_CHANNEL: true,
});
const userId = interaction.user.id;
const hasUpvoted = suggestion.upvotes.includes(userId);
const hasDownvoted = suggestion.downvotes.includes(userId);
await interaction.channel.permissionOverwrites.create(
interaction.guild.id,
{
VIEW_CHANNEL: false,
if (interaction.customId === 'upvote') {
if (hasUpvoted) {
suggestion.upvotes = suggestion.upvotes.filter(id => id !== userId);
} else {
suggestion.upvotes.push(userId);
if (hasDownvoted) {
suggestion.downvotes = suggestion.downvotes.filter(id => id !== userId);
}
);
const ticketDeleteButton = new Discord.MessageButton()
.setCustomId("ticketclose")
.setLabel("Close")
.setStyle(supportbot.TicketDeleteColour)
.setEmoji(supportbot.TicketDeleteEmoji);
const row = new Discord.MessageActionRow().addComponents(
ticketDeleteButton
);
const ArchiveEmbed = new Discord.MessageEmbed()
.setTitle("Archived")
.setDescription(`Archived ${interaction.channel.name}`)
.setColor(supportbot.SuccessColour);
interaction.reply({ embeds: [ArchiveEmbed] });
interaction.message.edit({ components: [row] });
}
} else if (interaction.customId === 'downvote') {
if (hasDownvoted) {
suggestion.downvotes = suggestion.downvotes.filter(id => id !== userId);
} else {
suggestion.downvotes.push(userId);
if (hasUpvoted) {
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);
}
fs.writeFileSync("./Data/SuggestionData.json", JSON.stringify(suggestions, null, 2));
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 }
]);
await interaction.update({ embeds: [updatedEmbed] });
}
}
async function handleInvites(interaction, enable) {
if (supportbot.Ticket.Invites.StaffOnly) {
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.");
}
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] });
}
}
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);
await interaction.reply({
embeds: [embed],
ephemeral: true,
});
}
});
+166
View File
@@ -0,0 +1,166 @@
const fs = require("fs");
const Discord = require("discord.js");
const yaml = require("js-yaml");
const axios = require("axios");
const { OpenAI } = require('openai');
// Load configuration files with validation
function loadConfig(path) {
try {
return yaml.load(fs.readFileSync(path, "utf8"));
} catch (error) {
console.error(`Error loading config file at ${path}:`, error);
throw error;
}
}
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 => {
if (!config[key]) {
throw new Error(`Missing configuration key: ${key}`);
}
});
}
const CHANNELS = [supportbotai.Channels.AIChannel];
// Initialize Discord client
const client = new Discord.Client({
intents: ['Guilds', 'GuildMembers', 'GuildMessages']
});
// Load custom Event structure
const Event = require("../Structures/Event.js");
// Initialize OpenAI client
const openai = new OpenAI({
apiKey: supportbotai.General.OpenAI_Key,
});
const PASTEBIN_API_KEY = supportbotai.General.PastebinAPI_Key;
const PASTEBIN_API_URL = supportbotai.General.PastebinAPI_URL;
// Function to create a paste on Pastebin
async function createPaste(content) {
try {
const response = await axios.post(PASTEBIN_API_URL, new URLSearchParams({
api_dev_key: PASTEBIN_API_KEY,
api_option: 'paste',
api_paste_code: content,
api_paste_private: 1, // 0 = public, 1 = unlisted, 2 = private
api_paste_expire_date: '1D', // Expire in 1 day
api_paste_format: 'text'
}));
return response.data;
} catch (error) {
console.error('Pastebin Error:', error);
throw error;
}
}
// Event handler for message creation
module.exports = new Event("messageCreate", async (client, message) => {
if (message.author.bot || !message.guild) return;
if (!CHANNELS.includes(message.channelId) && !message.mentions.users.has(client.user.id)) return;
if (supportbotai.Enabled) {
const sendTypingInterval = setInterval(() => {
message.channel.sendTyping();
}, 5000);
let conversation = [{
role: "system",
content: "SupportBot is your friendly neighbourhood support bot."
}];
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,
});
clearInterval(sendTypingInterval);
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.length > 2000) {
const pasteLink = await createPaste(replyContent);
const SupportBotAIPastebin = new Discord.EmbedBuilder()
.setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() })
.setDescription(supportbotai.Messages.MessageTooLong.replace("{pastebin}", pasteLink))
.setColor(supportbotai.Embed.Color)
.setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() })
.setTimestamp();
message.reply({ embeds: [SupportBotAIPastebin] });
} else {
message.reply({ embeds: [SupportBotAI] });
}
} else {
const SupportBotAIErrorResponse = new Discord.EmbedBuilder()
.setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() })
.setDescription(supportbotai.Messages.ErrorResponse)
.setColor(supportbotai.Embed.Color)
.setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() })
.setTimestamp();
message.reply({ embeds: [SupportBotAIErrorResponse], ephemeral: true });
}
} catch (error) {
clearInterval(sendTypingInterval);
const SupportBotAIErrorResponse = new Discord.EmbedBuilder()
.setAuthor({ name: supportbotai.General.Name, iconURL: client.user.displayAvatarURL() })
.setDescription(supportbotai.Messages.OpenAIError)
.setColor(supportbot.Embed.Colours.Error)
.setFooter({ text: supportbotai.Embed.Footer, iconURL: message.author.displayAvatarURL() })
.setTimestamp();
console.error('OpenAI Error:', error);
message.reply({ embeds: [SupportBotAIErrorResponse], ephemeral: true });
}
}
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;
}
});
+131 -146
View File
@@ -4,7 +4,11 @@
const fs = require("fs");
const Discord = require("discord.js");
const { Client, GatewayIntentBits, ActivityType } = require('discord.js');
const client = new Discord.Client({intents: 32767})
const yaml = require("js-yaml");
const supportbot = yaml.load(
fs.readFileSync("./Configs/supportbot.yml", "utf8")
);
@@ -12,103 +16,149 @@ const panelconfig = yaml.load(
fs.readFileSync("./Configs/ticket-panel.yml", "utf8")
);
const cmdconfig = yaml.load(
fs.readFileSync("./Configs/commands.yml", "utf8")
);
const msgconfig = yaml.load(
fs.readFileSync("./Configs/messages.yml", "utf8")
);
const Event = require("../Structures/Event.js");
let chan1 = client.channels.cache.get(panelconfig.Channel);
module.exports = new Event("ready", async (client, interaction) => {
const { getRole, getChannel, getCategory } = client;
client.user.setActivity(supportbot.BotActivity, {
type: supportbot.ActivityType,
url: supportbot.StreamingURL,
});
console.log(`\u001b[107m`, `――――――――――――――――――――――――――――――――――――――――――――`);
console.log(`\u001b[91m`, `┏━━━┓╋╋╋╋╋╋╋╋╋╋╋╋╋┏┓┏━━┓╋╋╋┏┓`);
console.log(`\u001b[91m`, `┃┏━┓┃╋╋╋╋╋╋╋╋╋╋╋╋┏┛┗┫┏┓┃╋╋┏┛┗┓`);
console.log(`\u001b[91m`, `┃┗━━┳┓┏┳━━┳━━┳━━┳┻┓┏┫┗┛┗┳━┻┓┏┛`);
console.log(`\u001b[91m`, `┗━━┓┃┃┃┃┏┓┃┏┓┃┏┓┃┏┫┃┃┏━┓┃┏┓┃┃`);
console.log(`\u001b[91m`, `┃┗━┛┃┗┛┃┗┛┃┗┛┃┗┛┃┃┃┗┫┗━┛┃┗┛┃┗┓`);
console.log(`\u001b[91m`, `┗━━━┻━━┫┏━┫┏━┻━━┻┛┗━┻━━━┻━━┻━┛`);
console.log(`\u001b[91m`, `┗╋╋╋╋╋╋╋┃┃╋┃┃`);
console.log(`\u001b[91m`, `╋╋╋╋╋╋╋┗┛╋┗┛`);
if (supportbot.Activity.Type === "Playing", "playing") {
client.user.setPresence({
activities: [{ name: supportbot.Activity.Status, type: Discord.ActivityType.Playing }],
status: supportbot.Activity.Type,
});
}
if (supportbot.Activity.Type === "Watching", "watching") {
client.user.setPresence({
activities: [{ name: supportbot.Activity.Status, type: Discord.ActivityType.Watching }],
status: supportbot.Activity.Type,
});
}
if (supportbot.Activity.Type === "Listening", "listening") {
client.user.setPresence({
activities: [{ name: supportbot.Activity.Status, type: Discord.ActivityType.Listening }],
status: supportbot.Activity.Type,
});
}
if (supportbot.Activity.Type === "Competing", "competing") {
client.user.setPresence({
activities: [{ name: supportbot.Activity.Status, type: Discord.ActivityType.Competing }],
status: supportbot.Activity.Type,
});
}
console.log(`\u001b[32m`, `――――――――――――――――――――――――――――――――――――――――――――`);
console.log(` `);
console.log(`\u001b[31m`, `┏━━━┓╋╋╋╋╋╋╋╋╋╋╋╋╋┏┓┏━━┓╋╋╋┏┓`);
console.log(`\u001b[31m`, `┃┏━┓┃╋╋╋╋╋╋╋╋╋╋╋╋┏┛┗┫┏┓┃╋╋┏┛┗┓`);
console.log(`\u001b[31m`, `┃┗━━┳┓┏┳━━┳━━┳━━┳┻┓┏┫┗┛┗┳━┻┓┏┛`);
console.log(`\u001b[31m`, `┗━━┓┃┃┃┃┏┓┃┏┓┃┏┓┃┏┫┃┃┏━┓┃┏┓┃┃`);
console.log(`\u001b[31m`, `┃┗━┛┃┗┛┃┗┛┃┗┛┃┗┛┃┃┃┗┫┗━┛┃┗┛┃┗┓`);
console.log(`\u001b[31m`, `┗━━━┻━━┫┏━┫┏━┻━━┻┛┗━┻━━━┻━━┻━┛`);
console.log(`\u001b[31m`, `┗╋╋╋╋╋╋╋┃┃╋┃┃`);
console.log(`\u001b[31m`, `╋╋╋╋╋╋╋┗┛╋┗┛`);
console.log(` `);
console.log(`\u001b[32m`, `――――――――――――――――――――――――――――――――――――――――――――`);
console.log(` `);
console.log(
`\u001b[33m`,
`${supportbot.Name} | v${supportbot.SupportBot_Version}`,
`\u001b[36m`,
`Connected to Discord`
`\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(` `);
console.log(
`\u001b[33m`,
`Invite to your server:`,
`\u001b[31m`,
`Discord`,
`\u001b[32m`,
`https://dsc.gg/emerald-dev`,
);
console.log(
`\u001b[31m`,
`Website`,
`\u001b[32m`,
`https://emeraldsrv.com`,
);
console.log(
`\u001b[31m`,
`Marketplace`,
`\u001b[32m`,
`https://market.emeraldsrv.com`,
);
console.log(
`\u001b[31m`,
`Documentation`,
`\u001b[32m`,
`https://emeraldsrv.com/third-party`,
);
console.log(` `);
console.log(
`\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(
`\u001b[33m`,
`Resources:`,
`\u001b[36m`,
`https://emeraldsrv.dev/resources`
);
console.log(
`\u001b[33m`,
`Documentation:`,
`\u001b[36m`,
`https://emeraldsrv.dev/wiki/supportbot`
);
console.log(
`\u001b[33m`,
`Discord:`,
`\u001b[36m`,
`https://emeraldsrv.dev/discord`
);
console.log(
`\u001b[33m`,
`Hosting:`,
`\u001b[36m`,
`https://emeraldsrv.dev/hosting`
);
console.log(` `);
console.log(`\u001b[37m`, `SupportBot proudly created by Emerald Services`);
console.log(` `);
console.log(`\u001b[31m`, `――――――――――――――――――――――――――――――――――――――――――――`);
console.log(`\u001b[32m`, `――――――――――――――――――――――――――――――――――――――――――――`);
const roles = [
supportbot.Admin,
supportbot.Staff,
supportbot.TicketBlackListRole,
supportbot.Roles.StaffMember.Admin,
supportbot.Roles.StaffMember.Staff,
supportbot.Roles.ModRoles.Blacklisted,
supportbot.Roles.ModRoles.Muted
];
supportbot.Departments.forEach((department) => roles.push(department.role));
if (supportbot.AutoRole) roles.push(supportbot.JoinRole);
const channels = [
supportbot.SuggestionChannel,
supportbot.TicketLog,
supportbot.TranscriptLog,
panelconfig.Channel,
supportbot.WelcomeChannel,
supportbot.LeaveChannel,
supportbot.TranslateLogChannel
];
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.Welcome.Channel,
supportbot.Leave.Channel,
supportbot.Translate.Log
];
//const categories = [supportbot.TicketCategory];
if (!channels) {
console.log("\u001b[31m", `[MISSING CHANNEL]`, `\u001b[37;1m`, `${channels}`, "\u001b[31m", `channel not found. Please check your config file.`);
return;
}
// else {
// console.log(`\u001b[32m`, `[CHANNEL LOCATED]`, `\u001b[37;1m`, `${channels}`, `\u001b[32;1m`, `channel has been found.`);
// }
const missingC = [];
const missingR = [];
const missingCat = [];
for (let c of channels) {
if ((c === supportbot.SuggestionChannel && supportbot.DisableSuggestionsChannel) || c === supportbot.TicketLog && supportbot.DisableTicketLogChannel) {
continue;
}
const find = await getChannel(c, client.guilds.cache.first());
if (!find) missingC.push(c);
}
for (let r of roles) {
const find = await getRole(r, client.guilds.cache.first());
if (!find) missingR.push(r);
@@ -120,98 +170,33 @@ module.exports = new Event("ready", async (client, interaction) => {
if (missingR.length > 0) {
console.log(
"\u001b[31m",
`[MISSING CONFIGURATION]`,
"\u001b[33m",
`[WARN] The following Roles could not be found! Please check your configs!\n${missingR.join(
`The following Roles could not be found in your server.\n`, `\u001b[31m`, `[${missingR.join(
", "
)}`
)}]`, `\n `,
);
}
if (missingC.length > 0) {
console.log(
"\u001b[31m",
`[MISSING CONFIGURATION]`,
"\u001b[33m",
`[WARN] The following Channels could not be found! Please check your configs!\n${missingC.join(
`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",
`[WARN] The following Categories could not be found! Please check your configs!\n${missingCat.join(
`The following Categories could not be found in your server.\n`, `\u001b[31m`, `[${missingCat.join(
", "
)}`
)}]`, `\n `,
);
}
if (panelconfig.TicketPanel) {
let chan1 = await client.getChannel(
panelconfig.Channel,
client.guilds.cache.first()
);
if (!chan1) {
console.log(
"\u001b[33m",
"[WARN] Ticket reaction panel is not setup, You can do so via the configuration file!"
);
return false;
}
const panelid = JSON.parse(
fs.readFileSync("./Data/ticket-panel-id.json", "utf8")
);
chan1.messages.fetch(panelid.TicketPanelID).catch(async () => {
let embed = new Discord.MessageEmbed()
.setTitle(panelconfig.PanelTitle)
.setColor(panelconfig.PanelColour)
.setFooter({
text: supportbot.EmbedFooter,
iconURL: interaction.user.displayAvatarURL(),
});
if (panelconfig.TicketPanel_Description) {
embed.setDescription(panelconfig.PanelMessage);
}
if (panelconfig.TicketPanel_Thumbnail) {
embed.setThumbnail(panelconfig.PanelThumbnail);
}
if (panelconfig.TicketPanel_Image) {
embed.setImage(panelconfig.PanelImage);
}
let button;
button = supportbot.Departments.map((x) =>
new Discord.MessageButton()
.setCustomId("department-" + supportbot.Departments.indexOf(x))
.setLabel(x.title)
.setStyle(x.color)
.setEmoji(x.emoji)
);
let row = new Discord.MessageActionRow().addComponents(button);
await chan1
.send({
embeds: [embed],
components: [row],
})
.then((r) => {
let data = {
id: panelid.id,
TicketPanelID: r.id,
};
fs.writeFileSync(
"./Data/ticket-panel-id.json",
JSON.stringify(data),
"utf8"
);
})
.catch((e) => {
console.log("Raw: " + e);
});
});
}
});
+84 -8
View File
@@ -1,12 +1,88 @@
![imgur](https://i.imgur.com/TQj40Mt.png)
<h1 align="center">
<br>
<a href="https://github.com/Emerald-Services/SupportBot"><img src="https://i.imgur.com/cuadAYv.png"></a>
<br>
SupportBot
<br>
</h1>
## What is SupportBot?
<div align="center">
<a href="https://opensource.org/licenses/MIT">
<img src="https://img.shields.io/badge/license-MIT-green?style=for-the-badge" alt="shield.png">
</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">
</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">
</a>
<a href="https://github.com/Emerald-Services/SupportBot/releases">
<img src="https://img.shields.io/github/downloads/Emerald-Services/SupportBot/total?style=for-the-badge" alt="shield.png">
</a>
## Supported Versions
| SupportBot | Node.JS | Discord.JS | Supported |
|------------|---------|------------|------------|
| 8.x | 20.x | 14.x. | ✅ Yes
| 7.7.3 | 16.x | 13.x. | ✅ Yes
| 7.x | 16.x | 13.x. | ❌ No
| 6.x | 14.x | 13.x. | ❌ No
| 5.x | 12.x | 13.x. | ❌ No
| 4.x | 11.x | 13.x. | ❌ No
| Older | 10.x | 13.x. | ❌ No
</div>
<p align="center">
<a href="https://emeraldsrv.com/">Website</a>
•
<a href="https://github.com/Emerald-Services/SupportBot/wiki">Wiki</a>
<br>
<a href="#about">About</a>
•
<a href="#support">Support</a>
•
<a href="#license">License</a>
•
<a href="https://github.com/Emerald-Services/SupportBot/wiki/Third-Party-Developer">Third-Party</a>
•
<a href="#contributors">Contributors</a>
</p>
## About
SupportBot is an Open-Source discord bot built with [discord.js](https://github.com/discordjs/discord.js). It's primarily focused to help servers organise and manage their support system in a unique way with full customisation to match your brand.
## Support
<a href="https://emeraldsrv.com/discord">
<img alt="Discord" src="https://img.shields.io/discord/597779408295821323?logo=discord&logoColor=white&style=for-the-badge">
</a>
## License
Released under the [MIT](https://opensource.org/licenses/MIT) license.
## Third-Party
- [Official addons repository](https://github.com/Emerald-Services/Addons/)
- [Syphers Addons](https://github.com/SypherRed/SB_Addons_Unofficial)
- [BuiltByBit](https://builtbybit.com/supportbot)
- [Polymart](https://polymart.org/resource/supportbot-1-discord-ticket-bot.518)
## Contributors
<a href="https://github.com/Emerald-Services/SupportBot/graphs/contributors">
<img src="https://contrib.rocks/image?repo=Emerald-Services/SupportBot">
</a>
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.
## Quick Links
**|** [Discord](https://emeraldsrv.dev/discord) **|**
[Hosting](https://emeraldsrv.dev/hosting) **|**
[Documentation](https://emeraldsrv.dev/wiki/supportbot) **|**
[Download via MC-Market](https://www.mc-market.org/resources/8940/) **|**
+20
View File
@@ -0,0 +1,20 @@
# Security Policy
## Supported Versions
Use this section to tell people about which versions of your project are
currently being supported with security updates.
| Version | Supported |
| ------- | ------------------ |
| 7.7.2 | :white_check_mark: |
| 7.7.1 | :white_check_mark: |
| < 4.0 | :x: |
## Reporting a Vulnerability
Use this section to tell people how to report a vulnerability.
Tell them where to go, how often they can expect to get an update on a
reported vulnerability, what to expect if the vulnerability is accepted or
declined, etc.
+108 -55
View File
@@ -1,101 +1,156 @@
// C:\Users\ollie\Documents\GitHub\SupportBot\Structures\Client.js
// SupportBot | Emerald Services
// Client Structure
const fs = require("fs");
const Discord = require("discord.js");
const intents = new Discord.Intents(32767);
const { GatewayIntentBits, Partials } = require('discord.js');
const yaml = require("js-yaml");
const supportbot = yaml.load(
fs.readFileSync("./Configs/supportbot.yml", "utf8")
);
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
class Client extends Discord.Client {
constructor() {
super({ intents });
super({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMembers,
GatewayIntentBits.GuildMessages
],
partials: [Partials.Channel]
});
this.commands = new Discord.Collection();
}
async getChannel(channel, guild) {
if (!channel) return null; // Add check for undefined channel
return guild.channels.cache.find(
(c) =>
(c.type === "GUILD_TEXT" || c.type === "GUILD_NEWS") &&
(c.id === channel || c.name.toLowerCase() === channel.toLowerCase())
(c.type === Discord.ChannelType.GuildText || c.type === Discord.ChannelType.GuildNews) &&
(c.id === channel || (c.name && c.name.toLowerCase() === channel.toLowerCase()))
);
}
async getRole(role, guild) {
if (!role) return null; // Add check for undefined role
return guild.roles.cache.find(
(r) => r.id === role || r.name.toLowerCase() === role.toLowerCase()
(r) => r.id === role || (r.name && r.name.toLowerCase() === role.toLowerCase())
);
}
async getCategory(category, guild) {
if (!category) return null; // Add check for undefined category
return guild.channels.cache.find(
(c) =>
c.type === "GUILD_CATEGORY" &&
(c.id === category || c.name.toLowerCase() === category.toLowerCase())
c.type === Discord.ChannelType.GuildCategory &&
(c.id === category || (c.name && c.name.toLowerCase() === category.toLowerCase()))
);
}
async start(token) {
var tempCommandFiles = fs.readdirSync("./Commands").filter((file) => file.endsWith(".js"));
let tempCommandFiles = fs.readdirSync("./Commands").filter((file) => file.endsWith(".js"));
if (supportbot.DisableSuggestions) {
if (cmdconfig.Suggestion.Enabled === false) {
tempCommandFiles = tempCommandFiles.filter(item => item !== "suggest.js");
}
if (cmdconfig.Help.Enabled === false) {
tempCommandFiles = tempCommandFiles.filter(item => item !== "help.js");
}
if (cmdconfig.Info.Enabled === false) {
tempCommandFiles = tempCommandFiles.filter(item => item !== "info.js");
}
if (cmdconfig.OpenTicket.Enabled === false) {
tempCommandFiles = tempCommandFiles.filter(item => item !== "ticket.js");
}
if (cmdconfig.CloseTicket.Enabled === false) {
tempCommandFiles = tempCommandFiles.filter(item => item !== "close.js");
}
if (cmdconfig.Embed.Enabled === false) {
tempCommandFiles = tempCommandFiles.filter(item => item !== "embed.js");
}
if (cmdconfig.Translate.Enabled === false) {
tempCommandFiles = tempCommandFiles.filter(item => item !== "translate.js");
}
if (cmdconfig.UserInfo.Enabled === false) {
tempCommandFiles = tempCommandFiles.filter(item => item !== "userinfo.js");
}
if (cmdconfig.Ping.Enabled === false) {
tempCommandFiles = tempCommandFiles.filter(item => item !== "Ping.js");
}
if (cmdconfig.Ping.Enabled === false) {
tempCommandFiles = tempCommandFiles.filter(item => item !== "Ping.js");
}
if (supportbot.Ticket.ClaimTickets.Enabled === false) {
tempCommandFiles = tempCommandFiles.filter(item => item !== "ticketstats.js");
}
const commandFiles = tempCommandFiles;
const addonFiles = fs
.readdirSync("./Addons")
.filter((file) => file.endsWith(".js"));
const addons = addonFiles.map((file) => {
let addon = require(`../Addons/${file}`);
addon.name = file.split(".")[0];
return addon;
});
const commands = commandFiles.map((file) => require(`../Commands/${file}`));
// Commands
console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Commands ▬▬▬▬▬▬▬");
commands.forEach((cmd) => {
console.log(`\u001b[31;1m`, `${cmd.name}`, `\u001b[32;1m`, "Loaded");
console.log(`\u001b[32m`, `[CMD]`, `\u001b[37;1m`, `${cmd.name}`, `\u001b[32;1m`, "Loaded");
this.commands.set(cmd.name, cmd);
});
console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Commands ▬▬▬▬▬▬▬");
// Addons
console.log(" ");
console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬");
addons.forEach((addon) => {
console.log(
`\u001b[33m`,
`[ADDON]`,
`\u001b[31;1m`,
`${addon.name}`,
`\u001b[32;1m`,
"Loaded"
);
addon.commands?.forEach((command) => {
this.commands.set(command.name, command);
});
addon.events?.forEach((event) => {
this.on(event.event, (...args) => event.run(this, ...args));
});
});
if (supportbot.General.Addons.Enabled) {
const addonFiles = fs.readdirSync("./Addons").filter((file) => file.endsWith(".js"));
console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬");
const addons = addonFiles.map((file) => {
let addon = require(`../Addons/${file}`);
addon.name = file.split(".")[0];
return addon;
});
// Addons
console.log(" ");
console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬");
addons.forEach((addon) => {
console.log(
`\u001b[32m`,
`[ADDON]`,
`\u001b[37;1m`,
`${addon.name}`,
`\u001b[32;1m`,
"Loaded"
);
addon.commands?.forEach((command) => {
this.commands.set(command.name, command);
});
addon.events?.forEach((event) => {
this.on(event.event, (...args) => event.run(this, ...args));
});
});
console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬");
}
// Slash Commands
this.once("ready", async () => {
await this.guilds.cache.first()?.commands.set(this.commands);
console.log(
`\u001b[32;1m`,
"\u001b[32m",
`[SUCCESS]`,
`\u001b[37;1m`,
`Slash Commands Registered for ${this.guilds.cache.first().name}`
);
});
@@ -103,13 +158,11 @@ class Client extends Discord.Client {
console.log(" ");
console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Events ▬▬▬▬▬▬▬");
fs.readdirSync("./Events")
.filter((file) => file.endsWith(".js"))
.forEach((file) => {
const event = require(`../Events/${file}`);
console.log(`\u001b[31;1m`, `${event.event}`, `\u001b[32;1m`, "Loaded");
this.on(event.event, (...args) => event.run(this, ...args));
});
fs.readdirSync("./Events").filter((file) => file.endsWith(".js")).forEach((file) => {
const event = require(`../Events/${file}`);
console.log(`\u001b[32m`, `[EVENT]`, `\u001b[37;1m`, `${event.event}`, `\u001b[32;1m`, "Loaded");
this.on(event.event, (...args) => event.run(this, ...args));
});
console.log(`\u001b[34;1m`, "▬▬▬▬▬▬▬ Events ▬▬▬▬▬▬▬");
if (process.argv[2] !== "test") {
+23 -2
View File
@@ -18,6 +18,27 @@ const supportbot = yaml.load(
);
const Client = require("./Structures/Client.js");
const client = new Client({});
const client = new Client({
intents: ['Guilds', 'GuildMembers', 'GuildMessages']
});
client.start(supportbot.General.Token);
client.start(supportbot.Token);
-1146
View File
File diff suppressed because it is too large. Load diff
+8 -5
View File
@@ -1,11 +1,11 @@
{
"name": "supportbot",
"version": "7.7b",
"version": "8.0.1",
"description": "SupportBot is an open-sourced support system for discord. It's completely free to use with high amounts of customization to match your own brand.",
"main": "index.js",
"scripts": {
"start": "npm i && node index.js",
"test": "node index.js test"
"setup": "npm i && node index.js",
"start": "node index.js"
},
"repository": {
"type": "git",
@@ -19,8 +19,11 @@
"homepage": "https://github.com/Emerald-Services/SupportBot#readme",
"dependencies": {
"@vitalets/google-translate-api": "^8.0.0",
"discord.js": "^13.6.0",
"axios": "^1.7.2",
"chatgpt": "^5.2.5",
"discord.js": "^14.13.0",
"js-yaml": "^4.1.0",
"moment": "^2.29.3"
"moment": "^2.29.3",
"openai": "^4.50.0"
}
}