Compare commits
230
Commits
7.1-Pre-3
...
v8.4-Beta-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7383bd2c48 | ||
|
|
a05d65099b | ||
|
|
c6db929d12 | ||
|
|
5c6a9f7306 | ||
|
|
0b8b092cb9 | ||
|
|
5a1c4ad99c | ||
|
|
d9cb72f22e | ||
|
|
fece90d2bd | ||
|
|
f34158caa6 | ||
|
|
13590b08a8 | ||
|
|
72b8c2247e | ||
|
|
925504a928 | ||
|
|
236d67707f | ||
|
|
753f432c95 | ||
|
|
c824bc51b2 | ||
|
|
39ee540b55 | ||
|
|
903b214051 | ||
|
|
ac0daf5fe7 | ||
|
|
80e38a07e9 | ||
|
|
d2598e09f7 | ||
|
|
28aab47150 | ||
|
|
81bda67ae4 | ||
|
|
25bbd1ff85 | ||
|
|
164d695a5f | ||
|
|
57e4a3e77c | ||
|
|
a6ac532b2b | ||
|
|
4f14722e14 | ||
|
|
9a6eece7a1 | ||
|
|
fd65d0a9a7 | ||
|
|
48500fe8a4 | ||
|
|
8d8d9e4bee | ||
|
|
dcbddf9f23 | ||
|
|
7449c0a61a | ||
|
|
c95b8ccb32 | ||
|
|
2c5bfe8e33 | ||
|
|
a3bbd97333 | ||
|
|
4ea488951d | ||
|
|
481424d5a6 | ||
|
|
fe82e96cc1 | ||
|
|
976258c959 | ||
|
|
83cf0689e2 | ||
|
|
c911cb4423 | ||
|
|
4eadef8b65 | ||
|
|
4e081358ff | ||
|
|
209ec0b435 | ||
|
|
6d363abb54 | ||
|
|
6f8b84aeda | ||
|
|
5b8c5da33a | ||
|
|
b6ef0f95cb | ||
|
|
cce1480a42 | ||
|
|
44fbe9f2d6 | ||
|
|
29db6c7d67 | ||
|
|
26e9b56961 | ||
|
|
979271cfb5 | ||
|
|
69f0b00ed3 | ||
|
|
c1a1839457 | ||
|
|
dd4b53800e | ||
|
|
1fc94528fc | ||
|
|
bb1a817980 | ||
|
|
53305acbc7 | ||
|
|
d0f1bae0bd | ||
|
|
c0cf3b1bb7 | ||
|
|
ce67b668d5 | ||
|
|
3273cfd0e1 | ||
|
|
fd992a6514 | ||
|
|
550d681fe1 | ||
|
|
7d29226953 | ||
|
|
38da72ca91 | ||
|
|
eab11361cb | ||
|
|
b281bba36f | ||
|
|
5e38573aed | ||
|
|
da7b6d487c | ||
|
|
6bf4e5f4bb | ||
|
|
213f532a3c | ||
|
|
45bd88e4d2 | ||
|
|
4eb8658459 | ||
|
|
b37118fee2 | ||
|
|
026caba68a | ||
|
|
2003f8801a | ||
|
|
473bacf13a | ||
|
|
8b3f2bc61a | ||
|
|
3aa373c33c | ||
|
|
9b6263e583 | ||
|
|
3e066d08ce | ||
|
|
aa48e28162 | ||
|
|
dc660ed94a | ||
|
|
e02ecf9553 | ||
|
|
23acc0926f | ||
|
|
3fc1cd18d5 | ||
|
|
ebe29b403e | ||
|
|
dcba3312dd | ||
|
|
7fddeeec37 | ||
|
|
b5341b023f | ||
|
|
bbb7443ef2 | ||
|
|
22df347bb9 | ||
|
|
9aed6b8f35 | ||
|
|
9e7ec9f1bd | ||
|
|
373f43eac5 | ||
|
|
6c91b2791a | ||
|
|
bde62a431f | ||
|
|
615a074540 | ||
|
|
09b335ac46 | ||
|
|
2701880c77 | ||
|
|
24c8f2fb38 | ||
|
|
c3ec158ac3 | ||
|
|
b3690222d0 | ||
|
|
4d7583785b | ||
|
|
17ad1a15db | ||
|
|
4c89de9e76 | ||
|
|
5f25af41f9 | ||
|
|
3a3ee357fc | ||
|
|
08a6d27903 | ||
|
|
33f7675aac | ||
|
|
1178a0a611 | ||
|
|
96ded77bec | ||
|
|
e83ca8cb2b | ||
|
|
e507176d39 | ||
|
|
4e554b8bee | ||
|
|
bc649caa99 | ||
|
|
f7a4c7b616 | ||
|
|
2c7be5882c | ||
|
|
6107d268f7 | ||
|
|
7760699155 | ||
|
|
acf349c72e | ||
|
|
8096e5b882 | ||
|
|
873cfc7dc2 | ||
|
|
038174bc7e | ||
|
|
14881096bb | ||
|
|
ed1905d8f4 | ||
|
|
72c0a2efa3 | ||
|
|
e599d48713 | ||
|
|
7f44634d4f | ||
|
|
44170caac6 | ||
|
|
adedfadd92 | ||
|
|
6b01d0c8fe | ||
|
|
d4d96f59ab | ||
|
|
a1bf7ed6bd | ||
|
|
306e20efc9 | ||
|
|
f593f6e0a8 | ||
|
|
74646a4318 | ||
|
|
8bfcba8832 | ||
|
|
4ba5621a4d | ||
|
|
1f928324c2 | ||
|
|
3771872cd2 | ||
|
|
f359a60343 | ||
|
|
f53afd7bc9 | ||
|
|
e7dcc2ab29 | ||
|
|
0118b9e591 | ||
|
|
ffe42f9777 | ||
|
|
5a467599e9 | ||
|
|
c56e78484a | ||
|
|
1ee0d4a09e | ||
|
|
ffdce5820b | ||
|
|
9fed504f37 | ||
|
|
6c5799be0d | ||
|
|
b6747a0340 | ||
|
|
cd6df02e30 | ||
|
|
edbef6a36f | ||
|
|
b6a0016f5f | ||
|
|
e740c17837 | ||
|
|
7eb949f109 | ||
|
|
02394e310d | ||
|
|
ef83832819 | ||
|
|
a82101ab30 | ||
|
|
007c78930e | ||
|
|
300c83512c | ||
|
|
b674872c56 | ||
|
|
12a7680275 | ||
|
|
c9cf7c942b | ||
|
|
8e0326c8f8 | ||
|
|
13972119a8 | ||
|
|
acf165a119 | ||
|
|
bdff7c6069 | ||
|
|
e2e3376c87 | ||
|
|
3c613088e5 | ||
|
|
7e9ece50a0 | ||
|
|
af81b6536a | ||
|
|
4cd3ecf81f | ||
|
|
31f6b9e08e | ||
|
|
02de920cb3 | ||
|
|
84c0905297 | ||
|
|
250583f859 | ||
|
|
ee7d42196d | ||
|
|
a397de2b9f | ||
|
|
0610df9400 | ||
|
|
ed583deb31 | ||
|
|
e039048fc6 | ||
|
|
f2ea9650c4 | ||
|
|
1e89057006 | ||
|
|
019bb4842f | ||
|
|
4cb45beded | ||
|
|
8a2059f087 | ||
|
|
a6b105a2e1 | ||
|
|
6c14a6a06c | ||
|
|
cddd5d0026 | ||
|
|
78dea91e1d | ||
|
|
3df591ec52 | ||
|
|
1c4032caa6 | ||
|
|
465c422171 | ||
|
|
284e8dc6f5 | ||
|
|
3fc905c82e | ||
|
|
55a42b88ae | ||
|
|
bd26162e4a | ||
|
|
62e7e33406 | ||
|
|
2f0ddcec3f | ||
|
|
7cdfa68e0e | ||
|
|
f8939a964d | ||
|
|
7830e5dda5 | ||
|
|
6aa2f22294 | ||
|
|
50b14e3d44 | ||
|
|
e09c2192c2 | ||
|
|
b725d2cc62 | ||
|
|
abf3f617a4 | ||
|
|
1eda7e66fe | ||
|
|
b5000531c4 | ||
|
|
d4f95e752b | ||
|
|
e31c611f70 | ||
|
|
085481bee6 | ||
|
|
cf610fbe49 | ||
|
|
b681eb3cd2 | ||
|
|
f8ffdf0e81 | ||
|
|
ba38bce1da | ||
|
|
3a39b05fe4 | ||
|
|
27d2982b28 | ||
|
|
33e4d97be5 | ||
|
|
654064d7c5 | ||
|
|
aeb0da9e0d | ||
|
|
cb1a2fe067 | ||
|
|
7ad5016966 | ||
|
|
155b768f73 |
No files matched your search
@@ -1,32 +0,0 @@
|
||||
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||
|
||||
name: Node.js CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [10.x, 12.x, 14.x, 15.x]
|
||||
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: npm ci
|
||||
- run: npm install discord.js --save
|
||||
- run: npm install js-yaml
|
||||
- run: npm run build --if-present
|
||||
- run: node index.js
|
||||
@@ -1,34 +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('./Data/supportbot.yml', 'utf8'));
|
||||
const cmdconfig = yaml.load(fs.readFileSync('./Data/commands.yml', 'utf8'));
|
||||
|
||||
// Your Addon Config file! Saved in /Addons/Settings/YourAddonConig.yml
|
||||
// const exampleaddon = yaml.load(fs.readFileSync('./Addons/Settings/YourAddonConig.yml', 'utf8'));
|
||||
|
||||
const Command = require("../Structures/Command.js");
|
||||
|
||||
module.exports = new Command({
|
||||
name: "hello",
|
||||
description: "hello",
|
||||
type: "BOTH",
|
||||
slashCommandOptions: [],
|
||||
permission: "SEND_MESSAGES",
|
||||
|
||||
async run(message, args, client) {
|
||||
|
||||
const Embed = new Discord.MessageEmbed()
|
||||
.setDescription("Hello World!")
|
||||
.setColor(supportbot.InfoColour)
|
||||
|
||||
message.channel.send({
|
||||
embeds: [Embed]
|
||||
})
|
||||
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,62 @@
|
||||
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.Help.Command,
|
||||
description: cmdconfig.Help.Description,
|
||||
type: Discord.ApplicationCommandType.ChatInput,
|
||||
options: [],
|
||||
permissions: cmdconfig.Help.Permission,
|
||||
|
||||
async run(interaction) {
|
||||
const { getRole } = interaction.client;
|
||||
let SupportStaff = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild);
|
||||
let Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild);
|
||||
if (!SupportStaff || !Admin) {
|
||||
return interaction.reply(
|
||||
"Some roles seem to be missing!\nPlease check for errors when starting the bot."
|
||||
);
|
||||
}
|
||||
|
||||
// Separate regular commands and addon commands
|
||||
let botCommands = "";
|
||||
let addonCommands = "";
|
||||
|
||||
interaction.client.commands.forEach((command) => {
|
||||
if (command.permissions) {
|
||||
if (command.addon) {
|
||||
addonCommands += `\`${command.name}\` `;
|
||||
} else {
|
||||
botCommands += `\`${command.name}\` `;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Create embed
|
||||
const HelpEmbed1 = new Discord.EmbedBuilder()
|
||||
.setTitle(supportbot.General.Name + " Commands")
|
||||
.setThumbnail(interaction.user.displayAvatarURL())
|
||||
.setColor(supportbot.Embed.Colours.General)
|
||||
.addFields({
|
||||
name: "🖥️ Commands\n",
|
||||
value: botCommands || "No commands available.",
|
||||
inline: false,
|
||||
});
|
||||
|
||||
HelpEmbed1.setFooter({
|
||||
text: supportbot.Embed.Footer,
|
||||
iconURL: interaction.user.displayAvatarURL(),
|
||||
});
|
||||
|
||||
interaction.reply({
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
embeds: [HelpEmbed1],
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
// SupportBot | Emerald Services
|
||||
// Info 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 msgconfig = yaml.load(
|
||||
fs.readFileSync("./Configs/messages.yml", "utf8")
|
||||
);
|
||||
|
||||
const Command = require("../../Structures/Command.js");
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.Info.Command,
|
||||
description: cmdconfig.Info.Description,
|
||||
options: [],
|
||||
permissions: cmdconfig.Info.Permission,
|
||||
|
||||
async run(interaction) {
|
||||
let disableCommand = true;
|
||||
|
||||
const InfoButton = new Discord.ButtonBuilder()
|
||||
.setLabel(msgconfig.Info.Button)
|
||||
.setURL(msgconfig.Info.URL)
|
||||
.setStyle("Link");
|
||||
|
||||
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],
|
||||
});
|
||||
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
// SupportBot | Emerald Services
|
||||
// Ping 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.Ping.Command,
|
||||
description: cmdconfig.Ping.Description,
|
||||
options: [],
|
||||
permissions: cmdconfig.Ping.Permission,
|
||||
|
||||
async run(interaction) {
|
||||
let disableCommand = true;
|
||||
|
||||
const PingEmbed = new Discord.EmbedBuilder()
|
||||
.setDescription(
|
||||
`:ping_pong: **Ping:** \`${interaction.client.ws.ping} ms\``
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
interaction.reply({
|
||||
embeds: [PingEmbed],
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,296 @@
|
||||
const {
|
||||
EmbedBuilder,
|
||||
ModalBuilder,
|
||||
TextInputBuilder,
|
||||
TextInputStyle,
|
||||
ApplicationCommandType,
|
||||
ActionRowBuilder,
|
||||
ButtonBuilder,
|
||||
ButtonStyle,
|
||||
ApplicationCommandOptionType
|
||||
} = require("discord.js");
|
||||
const fs = require("fs");
|
||||
const yaml = require("js-yaml");
|
||||
const db = require("../../Structures/Database.js");
|
||||
const Command = require("../../Structures/Command.js");
|
||||
|
||||
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 clockedInUsers = new Set();
|
||||
|
||||
function updateClockedInStatus(embed, clockedIn) {
|
||||
embed.data.fields = embed.data.fields.map(f =>
|
||||
f.name === "Clocked In Status"
|
||||
? { ...f, value: clockedIn ? "✅ Clocked In" : "❌ Clocked Out" }
|
||||
: f
|
||||
);
|
||||
return embed;
|
||||
}
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.Profile.Command,
|
||||
description: cmdconfig.Profile.Description,
|
||||
type: ApplicationCommandType.ChatInput,
|
||||
permissions: cmdconfig.Profile.Permission,
|
||||
options: [
|
||||
{
|
||||
name: "user",
|
||||
description: "The user to view the profile of",
|
||||
type: ApplicationCommandOptionType.User,
|
||||
required: false
|
||||
}
|
||||
],
|
||||
|
||||
async run(interaction) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
|
||||
const viewingUser = interaction.options.getUser("user") || interaction.user;
|
||||
const viewingUserId = viewingUser.id;
|
||||
const interactionUserId = interaction.user.id;
|
||||
|
||||
const member = await interaction.guild.members.fetch(viewingUserId);
|
||||
const { getRole } = interaction.client;
|
||||
const Staff = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild);
|
||||
const Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild);
|
||||
|
||||
if (!Staff || !Admin) {
|
||||
return interaction.editReply({
|
||||
content: "Some roles seem to be missing!\nPlease check for errors when starting the bot.",
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
|
||||
const isStaff = member.roles.cache.has(Staff.id) || member.roles.cache.has(Admin.id);
|
||||
const isOwnProfile = interactionUserId === viewingUserId;
|
||||
|
||||
const profileData = db.getProfile(viewingUserId);
|
||||
let { bio, timezone, clockedIn } = profileData;
|
||||
clockedIn = Boolean(clockedIn);
|
||||
|
||||
let profileEmbed = new EmbedBuilder()
|
||||
.setTitle(`${viewingUser.username}'s Profile`)
|
||||
.setColor(supportbot.Embed.Colours.General)
|
||||
.setThumbnail(viewingUser.displayAvatarURL())
|
||||
.addFields(
|
||||
{ name: "Bio", value: bio || "No bio set.", inline: false },
|
||||
{ name: "Timezone", value: timezone || "No timezone set.", inline: true }
|
||||
);
|
||||
|
||||
if (isStaff) {
|
||||
profileEmbed.addFields({
|
||||
name: "Clocked In Status",
|
||||
value: clockedIn ? "✅ Clocked In" : "❌ Clocked Out",
|
||||
inline: true
|
||||
});
|
||||
}
|
||||
|
||||
const buttonRow = new ActionRowBuilder();
|
||||
if (isOwnProfile) {
|
||||
buttonRow.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId("editProfile")
|
||||
.setLabel("Edit Profile")
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
|
||||
if (isStaff) {
|
||||
buttonRow.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId("clockInOut")
|
||||
.setLabel(clockedIn ? "Clock Out" : "Clock In")
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (isStaff && supportbot.Ticket.ClaimTickets.Enabled) {
|
||||
buttonRow.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId("viewTicketStats")
|
||||
.setLabel("View Ticket Stats")
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
}
|
||||
|
||||
await interaction.editReply({
|
||||
embeds: [profileEmbed],
|
||||
components: buttonRow.components.length ? [buttonRow] : []
|
||||
});
|
||||
|
||||
const filter = i =>
|
||||
["editProfile", "clockInOut", "viewTicketStats", "showOpenTickets", "backToStats", "backToProfile"]
|
||||
.includes(i.customId) &&
|
||||
i.user.id === interactionUserId;
|
||||
|
||||
const collector = interaction.channel.createMessageComponentCollector({ filter, time: 60000 });
|
||||
|
||||
collector.on("collect", async i => {
|
||||
if (i.customId === "editProfile") {
|
||||
const modal = new ModalBuilder()
|
||||
.setCustomId("editProfileModal")
|
||||
.setTitle("Edit Profile")
|
||||
.addComponents(
|
||||
new ActionRowBuilder().addComponents(
|
||||
new TextInputBuilder()
|
||||
.setCustomId("bio")
|
||||
.setLabel("Bio")
|
||||
.setStyle(TextInputStyle.Paragraph)
|
||||
.setPlaceholder("Enter your bio")
|
||||
.setValue(String(bio || ""))
|
||||
.setRequired(false)
|
||||
),
|
||||
new ActionRowBuilder().addComponents(
|
||||
new TextInputBuilder()
|
||||
.setCustomId("timezone")
|
||||
.setLabel("Timezone")
|
||||
.setStyle(TextInputStyle.Short)
|
||||
.setPlaceholder("Enter your timezone")
|
||||
.setValue(String(timezone || ""))
|
||||
.setRequired(false)
|
||||
)
|
||||
);
|
||||
|
||||
return i.showModal(modal);
|
||||
}
|
||||
|
||||
if (i.customId === "clockInOut") {
|
||||
const newStatus = !clockedIn;
|
||||
db.setClockedIn(viewingUserId, newStatus);
|
||||
clockedIn = newStatus;
|
||||
profileEmbed = updateClockedInStatus(profileEmbed, newStatus);
|
||||
|
||||
if (newStatus) clockedInUsers.add(viewingUserId);
|
||||
else clockedInUsers.delete(viewingUserId);
|
||||
|
||||
const updatedRow = new ActionRowBuilder().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId("editProfile")
|
||||
.setLabel("Edit Profile")
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
if (isStaff) {
|
||||
updatedRow.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId("clockInOut")
|
||||
.setLabel(newStatus ? "Clock Out" : "Clock In")
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
if (supportbot.Ticket.ClaimTickets.Enabled) {
|
||||
updatedRow.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId("viewTicketStats")
|
||||
.setLabel("View Ticket Stats")
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await i.update({ embeds: [profileEmbed], components: [updatedRow] });
|
||||
|
||||
const clockEmbed = new EmbedBuilder()
|
||||
.setTitle(newStatus ? "Clocked In!" : "Clocked Out!")
|
||||
.setDescription(newStatus ? "You have successfully clocked in." : "You have successfully clocked out.")
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
return interaction.followUp({ embeds: [clockEmbed], ephemeral: true });
|
||||
}
|
||||
|
||||
if (i.customId === "viewTicketStats") {
|
||||
const TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
const ticketsClaimed = TicketData.tickets.filter(t => t.claimedBy === viewingUserId);
|
||||
const ticketsOpen = ticketsClaimed.filter(t => t.open);
|
||||
const totalTickets = ticketsClaimed.length;
|
||||
|
||||
let totalResponseTime = 0;
|
||||
let responseCount = 0;
|
||||
ticketsClaimed.forEach(t => {
|
||||
if (t.claimedAt && t.createdAt) {
|
||||
totalResponseTime += new Date(t.claimedAt) - new Date(t.createdAt);
|
||||
responseCount++;
|
||||
}
|
||||
});
|
||||
|
||||
const avgMinutes = responseCount ? Math.round(totalResponseTime / responseCount / 60000) : 0;
|
||||
|
||||
const statsEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.TicketStats.Title)
|
||||
.setDescription(`> Ticket stats for <@${viewingUserId}>`)
|
||||
.setColor(supportbot.Embed.Colours.General)
|
||||
.addFields(
|
||||
{ name: msgconfig.TicketStats.OpenTickets, value: `${ticketsOpen.length}`, inline: true },
|
||||
{ name: msgconfig.TicketStats.TotalTickets, value: `${totalTickets}`, inline: true },
|
||||
{ name: msgconfig.TicketStats.ResponseTime, value: `${avgMinutes} minutes`, inline: false }
|
||||
);
|
||||
|
||||
const statsRow = new ActionRowBuilder().addComponents(
|
||||
new ButtonBuilder().setCustomId("showOpenTickets").setLabel("Show Open Tickets").setStyle(ButtonStyle.Secondary),
|
||||
new ButtonBuilder().setCustomId("backToProfile").setLabel("Back to Profile").setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
|
||||
return i.update({ embeds: [statsEmbed], components: [statsRow] });
|
||||
}
|
||||
|
||||
if (i.customId === "showOpenTickets") {
|
||||
const TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
const ticketsOpen = TicketData.tickets.filter(t => t.claimedBy === viewingUserId && t.open);
|
||||
|
||||
const openEmbed = new EmbedBuilder()
|
||||
.setTitle("Active Tickets")
|
||||
.setDescription(ticketsOpen.map(t => `<#${t.id}>`).join("\n") || "No active tickets.")
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
const backRow = new ActionRowBuilder().addComponents(
|
||||
new ButtonBuilder().setCustomId("backToStats").setLabel("Back to Stats").setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
|
||||
return i.update({ embeds: [openEmbed], components: [backRow] });
|
||||
}
|
||||
|
||||
if (i.customId === "backToStats") {
|
||||
|
||||
const TicketData = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
const ticketsClaimed = TicketData.tickets.filter(t => t.claimedBy === viewingUserId);
|
||||
const ticketsOpen = ticketsClaimed.filter(t => t.open);
|
||||
const totalTickets = ticketsClaimed.length;
|
||||
|
||||
let totalResponseTime = 0;
|
||||
let responseCount = 0;
|
||||
ticketsClaimed.forEach(t => {
|
||||
if (t.claimedAt && t.createdAt) {
|
||||
totalResponseTime += new Date(t.claimedAt) - new Date(t.createdAt);
|
||||
responseCount++;
|
||||
}
|
||||
});
|
||||
const avgMinutes = responseCount ? Math.round(totalResponseTime / responseCount / 60000) : 0;
|
||||
|
||||
const statsEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.TicketStats.Title)
|
||||
.setDescription(`> Ticket stats for <@${viewingUserId}>`)
|
||||
.setColor(supportbot.Embed.Colours.General)
|
||||
.addFields(
|
||||
{ name: msgconfig.TicketStats.OpenTickets, value: `${ticketsOpen.length}`, inline: true },
|
||||
{ name: msgconfig.TicketStats.TotalTickets, value: `${totalTickets}`, inline: true },
|
||||
{ name: msgconfig.TicketStats.ResponseTime, value: `${avgMinutes} minutes`, inline: false }
|
||||
);
|
||||
|
||||
const statsRow = new ActionRowBuilder().addComponents(
|
||||
new ButtonBuilder().setCustomId("showOpenTickets").setLabel("Show Open Tickets").setStyle(ButtonStyle.Secondary),
|
||||
new ButtonBuilder().setCustomId("backToProfile").setLabel("Back to Profile").setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
|
||||
return i.update({ embeds: [statsEmbed], components: [statsRow] });
|
||||
}
|
||||
|
||||
if (i.customId === "backToProfile") {
|
||||
return i.update({
|
||||
embeds: [profileEmbed],
|
||||
components: buttonRow.components.length ? [buttonRow] : []
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports.clockedInUsers = clockedInUsers;
|
||||
@@ -0,0 +1,117 @@
|
||||
// SupportBot | Emerald Services
|
||||
// Translate 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 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.Translate.Command,
|
||||
description: cmdconfig.Translate.Description,
|
||||
type: Discord.ApplicationCommandType.ChatInput,
|
||||
options: [
|
||||
{
|
||||
type: Discord.ApplicationCommandOptionType.String,
|
||||
name: "language",
|
||||
description: "A 2 letter language code",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: Discord.ApplicationCommandOptionType.String,
|
||||
name: "text",
|
||||
description: "The text to translate",
|
||||
required: true,
|
||||
}, // The input text for the translation
|
||||
],
|
||||
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.Roles.StaffMember.Staff, interaction.guild);
|
||||
let Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild);
|
||||
if (!SupportStaff || !Admin)
|
||||
|
||||
return interaction.reply(
|
||||
"Some roles seem to be missing!\nPlease check for errors when starting the bot."
|
||||
);
|
||||
|
||||
const NoPerms = new Discord.EmbedBuilder()
|
||||
.setTitle("Invalid Permissions!")
|
||||
.setDescription(
|
||||
`${msgconfig.Error.IncorrectPerms}\n\nRole Required: \`${supportbot.Roles.StaffMember.Staff}\` or \`${supportbot.Roles.StaffMember.Admin}\``
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.Warn);
|
||||
|
||||
if (
|
||||
!interaction.member.roles.cache.has(SupportStaff.id) &&
|
||||
!interaction.member.roles.cache.has(Admin.id)
|
||||
)
|
||||
return interaction.reply({ embeds: [NoPerms] });
|
||||
|
||||
const { getChannel } = interaction.client;
|
||||
let lang = interaction.options.getString("language"); // Grab choice of language code by user
|
||||
let url = "https://www.science.co.il/language/Codes.php"; // URL to all available language codes
|
||||
let text = await interaction.options.getString("text"); // Grab the text to translate by the user
|
||||
let translatelog = await getChannel(
|
||||
supportbot.Translate.TranslateLog,
|
||||
interaction.guild
|
||||
); // Grab the set logging channel for translations
|
||||
|
||||
|
||||
// Return message if user has provided an invalid language code
|
||||
const result = await translate(text, { to: lang }).catch(async (err) => {
|
||||
if (err && err.code == 400) {
|
||||
await interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setColor(supportbot.Embed.Colours.Warn)
|
||||
.setTitle("Valid Language Codes")
|
||||
.setURL(url)
|
||||
.setDescription(
|
||||
"Click the title to see which 2 letter language codes are valid."
|
||||
),
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
});
|
||||
if (!result) return;
|
||||
|
||||
// Embed containing language code, original text and translated text
|
||||
let transembed = new Discord.EmbedBuilder()
|
||||
.setAuthor({ name: interaction.user.tag, iconURL: interaction.user.displayAvatarURL({ dynamic: true }) })
|
||||
.setColor(supportbot.Embed.Colours.Success)
|
||||
.setDescription(`**Translation to ${lang}**`)
|
||||
.addFields(
|
||||
{ name: "Original text", value: text },
|
||||
{ name: "Translated text", value: result.text },
|
||||
)
|
||||
.setFooter({ text: `Author: ${interaction.user.id}` })
|
||||
.setTimestamp();
|
||||
|
||||
// Send embed [transembed] with translated message to channel
|
||||
await interaction.reply({ embeds: [transembed] });
|
||||
|
||||
// Send embed [transembed] to logging channel
|
||||
await translatelog.send({ embeds: [transembed] });
|
||||
},
|
||||
|
||||
})
|
||||
@@ -0,0 +1,147 @@
|
||||
const fs = require("fs");
|
||||
const Discord = require("discord.js");
|
||||
const yaml = require("js-yaml");
|
||||
const path = require("path");
|
||||
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
|
||||
|
||||
const Command = require("../../Structures/Command.js");
|
||||
const DB = require("../../Structures/Database.js");
|
||||
|
||||
module.exports = new Command({
|
||||
name: "suggest",
|
||||
description: cmdconfig.Suggestion?.Description || "Submit a new suggestion",
|
||||
type: Discord.ApplicationCommandType.ChatInput,
|
||||
options: [
|
||||
{
|
||||
name: "text",
|
||||
description: "Your suggestion text",
|
||||
type: Discord.ApplicationCommandOptionType.String,
|
||||
required: true
|
||||
}
|
||||
],
|
||||
|
||||
async run(interaction) {
|
||||
const suggestion = interaction.options.getString("text");
|
||||
const mode = supportbot.Suggestions.Mode || "Channel";
|
||||
|
||||
const embed = 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())
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
const row = new Discord.ActionRowBuilder().addComponents(
|
||||
new Discord.ButtonBuilder()
|
||||
.setCustomId("upvote")
|
||||
.setEmoji(supportbot.Suggestions.UpvoteEmoji)
|
||||
.setStyle(supportbot.Suggestions.Buttons.Upvote),
|
||||
new Discord.ButtonBuilder()
|
||||
.setCustomId("downvote")
|
||||
.setEmoji(supportbot.Suggestions.DownvoteEmoji)
|
||||
.setStyle(supportbot.Suggestions.Buttons.Downvote),
|
||||
new Discord.ButtonBuilder()
|
||||
.setCustomId("removevote")
|
||||
.setLabel(supportbot.Suggestions.Buttons.RemoveVote_Title)
|
||||
.setStyle(supportbot.Suggestions.Buttons.RemoveVote)
|
||||
);
|
||||
|
||||
if (mode === "Forum") {
|
||||
const forum = await interaction.guild.channels
|
||||
.fetch(supportbot.Suggestions.ForumChannel)
|
||||
.catch(() => null);
|
||||
|
||||
if (!forum || forum.type !== Discord.ChannelType.GuildForum) {
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle("⚠ Missing Forum Channel")
|
||||
.setDescription(msgconfig.Error.MissingChannel)
|
||||
.setColor(supportbot.Embed.Colours.Error)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
const appliedTags = supportbot.Suggestions.Tags?.Created
|
||||
? [supportbot.Suggestions.Tags.Created]
|
||||
: [];
|
||||
|
||||
const thread = await forum.threads.create({
|
||||
name: suggestion.slice(0, 100),
|
||||
message: {
|
||||
content: "\u200B",
|
||||
embeds: [embed],
|
||||
components: [row]
|
||||
},
|
||||
appliedTags,
|
||||
autoArchiveDuration: Discord.ThreadAutoArchiveDuration.OneDay,
|
||||
reason: "New Suggestion"
|
||||
});
|
||||
|
||||
embed.setFooter({
|
||||
text: `${supportbot.Embed.Footer} | Suggestion ID: ${thread.id}`,
|
||||
iconURL: interaction.user.displayAvatarURL()
|
||||
});
|
||||
const starter = await thread.fetchStarterMessage();
|
||||
await starter.edit({ embeds: [embed], components: [row] });
|
||||
|
||||
DB.add(thread.id, interaction.user.id, suggestion);
|
||||
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle(msgconfig.Suggestions.Sent_Title)
|
||||
.setDescription(`${msgconfig.Suggestions.Sent}`)
|
||||
.addFields({ name: 'Posted in:', value: `<#${forum.id}>` })
|
||||
.setColor(supportbot.Embed.Colours.Success)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
// ---------- Channel Mode ----------
|
||||
const channel = await interaction.guild.channels
|
||||
.fetch(supportbot.Suggestions.Channel)
|
||||
.catch(() => null);
|
||||
|
||||
if (!channel) {
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle("⚠ Missing Suggestion Channel")
|
||||
.setDescription(msgconfig.Error.MissingChannel)
|
||||
.setColor(supportbot.Embed.Colours.Error)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
const message = await channel.send({ embeds: [embed], components: [row] });
|
||||
|
||||
embed.setFooter({
|
||||
text: `${supportbot.Embed.Footer} | Suggestion ID: ${message.id}`,
|
||||
iconURL: interaction.user.displayAvatarURL()
|
||||
});
|
||||
await message.edit({ embeds: [embed], components: [row] });
|
||||
|
||||
DB.add(message.id, interaction.user.id, suggestion);
|
||||
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle(msgconfig.Suggestions.Sent_Title)
|
||||
.setDescription(`${msgconfig.Suggestions.Sent}`)
|
||||
.addFields({ name: 'Posted in:', value: `<#${channel.id}>` })
|
||||
.setColor(supportbot.Embed.Colours.Success)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,183 @@
|
||||
const Discord = require("discord.js");
|
||||
const fs = require("fs");
|
||||
const yaml = require("js-yaml");
|
||||
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
const SuggestionDB = require("../../Structures/Database.js");
|
||||
const Command = require("../../Structures/Command.js");
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.SuggestionAdmin.Command,
|
||||
description: cmdconfig.SuggestionAdmin.Description,
|
||||
type: Discord.ApplicationCommandType.ChatInput,
|
||||
permissions: cmdconfig.SuggestionAdmin.Permission,
|
||||
options: [
|
||||
{
|
||||
name: "status",
|
||||
description: "New status",
|
||||
type: Discord.ApplicationCommandOptionType.String,
|
||||
required: true,
|
||||
choices: [
|
||||
{ name: "Solved", value: "Solved" },
|
||||
{ name: "Denied", value: "Denied" },
|
||||
{ name: "Considering", value: "Considering" }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "id",
|
||||
description: "Message ID (required in Channel mode)",
|
||||
type: Discord.ApplicationCommandOptionType.String,
|
||||
required: false
|
||||
}
|
||||
],
|
||||
|
||||
async run(interaction) {
|
||||
const status = interaction.options.getString("status");
|
||||
const idOption = interaction.options.getString("id");
|
||||
const mode = supportbot.Suggestions.Mode || "Channel";
|
||||
|
||||
const { getRole } = interaction.client;
|
||||
const staffRole = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild);
|
||||
const adminRole = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild);
|
||||
if (
|
||||
!interaction.member.roles.cache.has(staffRole?.id) &&
|
||||
!interaction.member.roles.cache.has(adminRole?.id)
|
||||
) {
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle("Invalid Permissions!")
|
||||
.setDescription(
|
||||
`${msgconfig.Error.IncorrectPerms}\nRequired: ${supportbot.Roles.StaffMember.Staff} or ${supportbot.Roles.StaffMember.Admin}`
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.Warn)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
if (mode === "Forum") {
|
||||
const thread = interaction.channel;
|
||||
if (!thread.isThread() || thread.parentId !== supportbot.Suggestions.ForumChannel) {
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle("Not a Suggestion Thread")
|
||||
.setDescription(msgconfig.Error.MissingChannel)
|
||||
.setColor(supportbot.Embed.Colours.Error)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
const tagMap = {
|
||||
Solved: supportbot.Suggestions.Tags.Solved,
|
||||
Denied: supportbot.Suggestions.Tags.Denied,
|
||||
Considering: supportbot.Suggestions.Tags.Considering
|
||||
};
|
||||
const tagId = tagMap[status];
|
||||
if (!tagId) {
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle("Tag Missing")
|
||||
.setDescription("No forum tag configured for this status.")
|
||||
.setColor(supportbot.Embed.Colours.Error)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
await thread.setAppliedTags([tagId]).catch(() => null);
|
||||
SuggestionDB.setStatus(thread.id, status);
|
||||
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle("Suggestion Updated")
|
||||
.setDescription(`Status set to **${status}**.`)
|
||||
.setColor(supportbot.Embed.Colours.Success)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
if (!idOption) {
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle("Message ID Required")
|
||||
.setDescription("Provide the suggestion’s message ID when in Channel mode.")
|
||||
.setColor(supportbot.Embed.Colours.Error)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
const channel = await interaction.guild.channels
|
||||
.fetch(supportbot.Suggestions.Channel)
|
||||
.catch(() => null);
|
||||
if (!channel) {
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle("Missing Suggestion Channel")
|
||||
.setDescription(msgconfig.Error.MissingChannel)
|
||||
.setColor(supportbot.Embed.Colours.Error)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
const targetMsg = await channel.messages.fetch(idOption).catch(() => null);
|
||||
if (!targetMsg) {
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle("Message Not Found")
|
||||
.setDescription("Could not fetch a suggestion with that ID.")
|
||||
.setColor(supportbot.Embed.Colours.Error)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
const oldEmbed = targetMsg.embeds[0];
|
||||
if (!oldEmbed) {
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle("No Embed Found")
|
||||
.setDescription("The target message has no suggestion embed.")
|
||||
.setColor(supportbot.Embed.Colours.Error)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
const colorMap = {
|
||||
Solved: supportbot.Embed.Colours.Success,
|
||||
Denied: supportbot.Embed.Colours.Error,
|
||||
Considering: supportbot.Embed.Colours.Warn
|
||||
};
|
||||
|
||||
const newEmbed = Discord.EmbedBuilder.from(oldEmbed)
|
||||
.setColor(colorMap[status])
|
||||
.addFields({ name: "Status", value: status, inline: true });
|
||||
|
||||
await targetMsg.edit({ embeds: [newEmbed], components: targetMsg.components });
|
||||
SuggestionDB.setStatus(idOption, status);
|
||||
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
new Discord.EmbedBuilder()
|
||||
.setTitle("Suggestion Updated")
|
||||
.setDescription(`Suggestion **${idOption}** marked as **${status}**.`)
|
||||
.setColor(supportbot.Embed.Colours.Success)
|
||||
],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1,75 +0,0 @@
|
||||
// SupportBot | Emerald Services
|
||||
// Info Command
|
||||
|
||||
const fs = require("fs");
|
||||
|
||||
const Discord = require("discord.js");
|
||||
const yaml = require('js-yaml');
|
||||
const supportbot = yaml.load(fs.readFileSync('./Data/supportbot.yml', 'utf8'));
|
||||
const cmdconfig = yaml.load(fs.readFileSync('./Data/commands.yml', 'utf8'));
|
||||
|
||||
const Command = require("../Structures/Command.js");
|
||||
|
||||
async function asyncForEach(array, callback) {
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
await callback(array[index], index, array);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.TicketAdd,
|
||||
description: cmdconfig.TicketAddDesc,
|
||||
type: "BOTH",
|
||||
slashCommandOptions: [],
|
||||
permission: "SEND_MESSAGES",
|
||||
|
||||
async run(message, args, client) {
|
||||
|
||||
let SupportStaff = message.guild.roles.cache.find(SupportTeam => SupportTeam.name === supportbot.Staff) || message.guild.roles.cache.find(SupportTeam => SupportTeam.id === supportbot.Staff)
|
||||
let Admins = message.guild.roles.cache.find(AdminUser => AdminUser.name === supportbot.Admin) || message.guild.roles.cache.find(AdminUser => AdminUser.id === supportbot.Admin)
|
||||
|
||||
const NoPerms = new Discord.MessageEmbed()
|
||||
.setTitle("Invalid Permissions!")
|
||||
.setDescription(`${supportbot.IncorrectPerms}\n\nRole Required: \`${supportbot.Staff}\` or \`${supportbot.Admin}\``)
|
||||
.setColor(supportbot.WarningColour)
|
||||
|
||||
if (message.member.roles.cache.has(SupportStaff.id) || message.member.roles.cache.has(Admins.id)) {
|
||||
if (!message.channel.name.startsWith( `${supportbot.TicketChannel}-` )) {
|
||||
const Exists = new Discord.MessageEmbed()
|
||||
.setTitle("No Ticket Found!")
|
||||
.setDescription(`${supportbot.NoValidTicket}`)
|
||||
.setColor(supportbot.WarningColour);
|
||||
message.channel.send({ embeds: [Exists] });
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let uMember = message.mentions.users.first();
|
||||
const UserNotExist = new Discord.MessageEmbed()
|
||||
.setTitle("User Not Found!")
|
||||
.setDescription(`${supportbot.UserNotFound}\n\nTry Again:\`${supportbot.Prefix}${cmdconfig.TicketAdd} <user#0000>\``)
|
||||
.setColor(supportbot.ErrorColour)
|
||||
|
||||
if (!uMember) return message.channel.send({ embeds: [UserNotExist] });
|
||||
|
||||
message.channel.permissionOverwrites.edit(uMember, {
|
||||
VIEW_CHANNEL: true,
|
||||
READ_MESSAGE_HISTORY: true,
|
||||
SEND_MESSAGES: true,
|
||||
});
|
||||
|
||||
const Complete = new Discord.MessageEmbed()
|
||||
.setTitle("User Added!")
|
||||
.setDescription(supportbot.AddedUser.replace(/%user%/g, uMember.id))
|
||||
.setTimestamp()
|
||||
.setColor(supportbot.EmbedColour)
|
||||
message.channel.send({ embeds: [Complete] });
|
||||
} else {
|
||||
|
||||
return message.channel.send({ embeds: [NoPerms] });
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
@@ -1,75 +0,0 @@
|
||||
// SupportBot | Emerald Services
|
||||
// Info Command
|
||||
|
||||
const fs = require("fs");
|
||||
|
||||
const Discord = require("discord.js");
|
||||
const yaml = require('js-yaml');
|
||||
const supportbot = yaml.load(fs.readFileSync('./Data/supportbot.yml', 'utf8'));
|
||||
const cmdconfig = yaml.load(fs.readFileSync('./Data/commands.yml', 'utf8'));
|
||||
|
||||
const Command = require("../Structures/Command.js");
|
||||
|
||||
async function asyncForEach(array, callback) {
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
await callback(array[index], index, array);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.TicketRemove,
|
||||
description: cmdconfig.TicketRemoveDesc,
|
||||
type: "BOTH",
|
||||
slashCommandOptions: [],
|
||||
permission: "SEND_MESSAGES",
|
||||
|
||||
async run(message, args, client) {
|
||||
|
||||
let SupportStaff = message.guild.roles.cache.find(SupportTeam => SupportTeam.name === supportbot.Staff) || message.guild.roles.cache.find(SupportTeam => SupportTeam.id === supportbot.Staff)
|
||||
let Admins = message.guild.roles.cache.find(AdminUser => AdminUser.name === supportbot.Admin) || message.guild.roles.cache.find(AdminUser => AdminUser.id === supportbot.Admin)
|
||||
|
||||
const NoPerms = new Discord.MessageEmbed()
|
||||
.setTitle("Invalid Permissions!")
|
||||
.setDescription(`${supportbot.IncorrectPerms}\n\nRole Required: \`${supportbot.Staff}\` or \`${supportbot.Admin}\``)
|
||||
.setColor(supportbot.WarningColour)
|
||||
|
||||
if (message.member.roles.cache.has(SupportStaff.id) || message.member.roles.cache.has(Admins.id)) {
|
||||
if (!message.channel.name.startsWith( `${supportbot.TicketChannel}-` )) {
|
||||
const Exists = new Discord.MessageEmbed()
|
||||
.setTitle("No Ticket Found!")
|
||||
.setDescription(`${supportbot.NoValidTicket}`)
|
||||
.setColor(supportbot.WarningColour);
|
||||
message.channel.send({ embeds: [Exists] });
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let uMember = message.mentions.users.first();
|
||||
const UserNotExist = new Discord.MessageEmbed()
|
||||
.setTitle("User Not Found!")
|
||||
.setDescription(`${supportbot.UserNotFound}\n\nTry Again:\`${supportbot.Prefix}${cmdconfig.TicketRemove} <user#0000>\``)
|
||||
.setColor(supportbot.ErrorColour)
|
||||
|
||||
if (!uMember) return message.channel.send({ embeds: [UserNotExist] });
|
||||
|
||||
message.channel.permissionOverwrites.edit(uMember, {
|
||||
VIEW_CHANNEL: false,
|
||||
READ_MESSAGE_HISTORY: false,
|
||||
SEND_MESSAGES: false,
|
||||
});
|
||||
|
||||
const Complete = new Discord.MessageEmbed()
|
||||
.setTitle("User Removed!")
|
||||
.setDescription(supportbot.RemovedUser.replace(/%user%/g, uMember.id))
|
||||
.setTimestamp()
|
||||
.setColor(supportbot.EmbedColour)
|
||||
message.channel.send({ embeds: [Complete] });
|
||||
} else {
|
||||
|
||||
return message.channel.send({ embeds: [NoPerms] });
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,119 @@
|
||||
const fs = require("fs");
|
||||
const {
|
||||
EmbedBuilder,
|
||||
ApplicationCommandOptionType,
|
||||
ApplicationCommandType,
|
||||
ChannelType,
|
||||
MessageFlags,
|
||||
} = require("discord.js");
|
||||
const yaml = require("js-yaml");
|
||||
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
|
||||
const Command = require("../../Structures/Command.js");
|
||||
const db = require("../../Structures/Database.js");
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.ForceAddUser.Command,
|
||||
description: cmdconfig.ForceAddUser.Description,
|
||||
type: ApplicationCommandType.ChatInput,
|
||||
options: [
|
||||
{
|
||||
name: "user",
|
||||
description: "The user to forcefully add",
|
||||
type: ApplicationCommandOptionType.User,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
permissions: cmdconfig.ForceAddUser.Permission,
|
||||
|
||||
async run(interaction) {
|
||||
const userToAdd = interaction.options.getUser("user");
|
||||
const ticketChannel = interaction.channel;
|
||||
|
||||
if (
|
||||
(supportbot.Ticket.TicketType === "threads" && !ticketChannel.isThread()) ||
|
||||
(supportbot.Ticket.TicketType === "channels" && ticketChannel.type !== ChannelType.GuildText)
|
||||
) {
|
||||
const onlyInTicket = new EmbedBuilder()
|
||||
.setTitle(msgconfig.ForceAddUser.NotInTicket_Title)
|
||||
.setDescription(msgconfig.ForceAddUser.NotInTicket_Description)
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
return interaction.reply({
|
||||
embeds: [onlyInTicket],
|
||||
flags: MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
const ticketRow = db.getTicket(ticketChannel.id);
|
||||
if (!ticketRow) {
|
||||
const notFound = new EmbedBuilder()
|
||||
.setTitle(msgconfig.ForceAddUser.NotInTicket_Title)
|
||||
.setDescription(msgconfig.ForceAddUser.NotInTicket_Description)
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
return interaction.reply({
|
||||
embeds: [notFound],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
if (supportbot.Ticket.TicketType === "threads") {
|
||||
const botMember = await ticketChannel.members.fetch(interaction.client.user.id).catch(() => null);
|
||||
if (!botMember) await ticketChannel.join();
|
||||
await new Promise(res => setTimeout(res, 1000));
|
||||
await ticketChannel.members.add(userToAdd.id);
|
||||
} else if (supportbot.Ticket.TicketType === "channels") {
|
||||
await ticketChannel.permissionOverwrites.create(userToAdd.id, {
|
||||
ViewChannel: true,
|
||||
SendMessages: true,
|
||||
ReadMessageHistory: true,
|
||||
});
|
||||
}
|
||||
|
||||
db.addUserToTicket(ticketChannel.id, userToAdd.id);
|
||||
|
||||
const addedEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.ForceAddUser.Added_Title)
|
||||
.setDescription(
|
||||
msgconfig.ForceAddUser.Added_Description.replace("%username%", userToAdd.username)
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
|
||||
await interaction.reply({
|
||||
embeds: [addedEmbed],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
|
||||
const addedToTicketEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.ForceAddUser.AddedToTicket_Title)
|
||||
.setDescription(
|
||||
msgconfig.ForceAddUser.AddedToTicket_Description.replace(
|
||||
"%channel_link%",
|
||||
`[${ticketChannel.name}](https://discord.com/channels/${ticketChannel.guild.id}/${ticketChannel.id})`
|
||||
)
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
await userToAdd.send({
|
||||
embeds: [addedToTicketEmbed],
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Error adding user to the ticket:", err);
|
||||
|
||||
const errorEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.ForceAddUser.Error_Title)
|
||||
.setDescription(msgconfig.ForceAddUser.Error_Adding)
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
await interaction.reply({
|
||||
embeds: [errorEmbed],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,441 @@
|
||||
const fs = require("fs");
|
||||
const Discord = require("discord.js");
|
||||
const yaml = require("js-yaml");
|
||||
|
||||
const db = require("../../Structures/Database.js");
|
||||
|
||||
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.CloseTicket.Command,
|
||||
description: cmdconfig.CloseTicket.Description,
|
||||
type: Discord.ApplicationCommandType.ChatInput,
|
||||
options: [
|
||||
{
|
||||
name: "reason",
|
||||
description: "Ticket Close Reason",
|
||||
type: Discord.ApplicationCommandOptionType.String,
|
||||
}
|
||||
],
|
||||
permissions: cmdconfig.CloseTicket.Permission,
|
||||
|
||||
async run(interaction) {
|
||||
const { getRole, getChannel } = interaction.client;
|
||||
|
||||
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! Please 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] });
|
||||
}
|
||||
}
|
||||
|
||||
const isThread = interaction.channel.type === Discord.ChannelType.PrivateThread;
|
||||
if (
|
||||
(supportbot.Ticket.TicketType === "threads" && !isThread) ||
|
||||
(supportbot.Ticket.TicketType === "channels" && interaction.channel.type !== Discord.ChannelType.GuildText)
|
||||
) {
|
||||
const NotTicketChannel = new Discord.EmbedBuilder()
|
||||
.setTitle("Invalid Channel!")
|
||||
.setDescription(`This command can only be used in a ${supportbot.Ticket.TicketType === "threads" ? "ticket thread" : "ticket channel"}.`)
|
||||
.setColor(supportbot.Embed.Colours.Warn);
|
||||
|
||||
return interaction.reply({ embeds: [NotTicketChannel], ephemeral: true });
|
||||
}
|
||||
|
||||
await interaction.deferReply();
|
||||
|
||||
const ticket = db.getTicket(interaction.channel.id);
|
||||
|
||||
if (!ticket) {
|
||||
const Exists = new Discord.EmbedBuilder()
|
||||
.setTitle("No Ticket Found!")
|
||||
.setDescription(msgconfig.Error.NoValidTicket)
|
||||
.setColor(supportbot.Embed.Colours.Warn);
|
||||
return interaction.followUp({ embeds: [Exists] });
|
||||
}
|
||||
|
||||
let reason = interaction.options?.getString("reason") || "No Reason Provided.";
|
||||
const ticketUserId = ticket.user;
|
||||
|
||||
if (supportbot.Ticket.ReviewSystem.Enabled) {
|
||||
const rating = await collectReviewRating(interaction, ticketUserId);
|
||||
const comment = await collectReviewComment(interaction, ticketUserId);
|
||||
|
||||
const reviewChannel = await getChannel(supportbot.Ticket.ReviewSystem.Channel, interaction.guild);
|
||||
const reviewer = supportbot.Ticket.ClaimTickets ? `<@${ticket.claimedBy}>` : null;
|
||||
|
||||
const reviewEmbed = new Discord.EmbedBuilder()
|
||||
.setTitle(msgconfig.ReviewSystem.ReviewEmbed.Title)
|
||||
.addFields(
|
||||
{
|
||||
name: msgconfig.ReviewSystem.ReviewEmbed.RatingTitle,
|
||||
value: `${msgconfig.ReviewSystem.ReviewEmbed.ReviewEmoji.repeat(rating)}`,
|
||||
inline: false
|
||||
},
|
||||
{
|
||||
name: msgconfig.ReviewSystem.ReviewEmbed.CommentTitle,
|
||||
value: `\`\`\`${comment}\`\`\``,
|
||||
inline: false
|
||||
}
|
||||
)
|
||||
.setColor(msgconfig.ReviewSystem.ReviewEmbed.Color);
|
||||
|
||||
if (supportbot.Ticket.ClaimTickets.Enabled) {
|
||||
reviewEmbed.setDescription(
|
||||
`**${msgconfig.ReviewSystem.ReviewEmbed.ReviewedStaffTitle}** ${reviewer}\n**${msgconfig.ReviewSystem.ReviewEmbed.ReviewedByTitle}** <@${ticketUserId}>` || `N/A`
|
||||
);
|
||||
} else {
|
||||
reviewEmbed.setDescription(
|
||||
`**${msgconfig.ReviewSystem.ReviewEmbed.ReviewedByTitle}** <@${ticketUserId}>`
|
||||
);
|
||||
}
|
||||
|
||||
if (reviewChannel) {
|
||||
await reviewChannel.send({ embeds: [reviewEmbed] });
|
||||
}
|
||||
|
||||
if (!interaction.replied && !interaction.deferred) {
|
||||
await interaction.followUp({ embeds: [reviewEmbed] });
|
||||
}
|
||||
|
||||
await handleCloseTicket(interaction, reason, ticket);
|
||||
} else {
|
||||
await handleCloseTicket(interaction, reason, ticket);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
async function collectReviewRating(interaction, ticketUserId) {
|
||||
const reviewPrompt = new Discord.EmbedBuilder()
|
||||
.setTitle(msgconfig.ReviewSystem.Rate.Title)
|
||||
.setDescription(`${msgconfig.ReviewSystem.Rate.Description}`)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
const stars = new Discord.ActionRowBuilder().addComponents(
|
||||
new Discord.StringSelectMenuBuilder()
|
||||
.setCustomId("starRating")
|
||||
.setPlaceholder("Select a rating")
|
||||
.addOptions([
|
||||
{ label: msgconfig.ReviewSystem.Stars.One, value: "1" },
|
||||
{ label: msgconfig.ReviewSystem.Stars.Two, value: "2" },
|
||||
{ label: msgconfig.ReviewSystem.Stars.Three, value: "3" },
|
||||
{ label: msgconfig.ReviewSystem.Stars.Four, value: "4" },
|
||||
{ label: msgconfig.ReviewSystem.Stars.Five, value: "5" },
|
||||
])
|
||||
);
|
||||
|
||||
await interaction.followUp({
|
||||
content: `<@${ticketUserId}>`,
|
||||
embeds: [reviewPrompt],
|
||||
components: [stars],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
|
||||
const starRating = await interaction.channel.awaitMessageComponent({
|
||||
componentType: Discord.ComponentType.StringSelect,
|
||||
filter: (i) => i.customId === "starRating" && i.user.id === ticketUserId,
|
||||
});
|
||||
await starRating.deferUpdate();
|
||||
return starRating.values[0];
|
||||
}
|
||||
|
||||
async function collectReviewComment(interaction, ticketUserId) {
|
||||
const commentPrompt = new Discord.EmbedBuilder()
|
||||
.setTitle(msgconfig.ReviewSystem.Comment.Title)
|
||||
.setDescription(`${msgconfig.ReviewSystem.Comment.Description}`)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
await interaction.followUp({
|
||||
content: `<@${ticketUserId}>`,
|
||||
embeds: [commentPrompt],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
|
||||
const filter = (response) => response.author.id === ticketUserId;
|
||||
|
||||
const commentCollection = await interaction.channel.awaitMessages({ filter, max: 1 });
|
||||
const comment = commentCollection.first()?.content;
|
||||
return comment && comment.toLowerCase() !== "no" ? comment : "No Comment Provided";
|
||||
}
|
||||
async function handleCloseTicket(interaction, reason, ticket) {
|
||||
const { getChannel } = interaction.client;
|
||||
|
||||
let tUser = interaction.client.users.cache.get(ticket.user_id);
|
||||
let transcriptChannel = await getChannel(supportbot.Ticket.Log.TicketDataLog, interaction.guild);
|
||||
|
||||
if (!transcriptChannel) {
|
||||
console.log("Transcript channel missing or inaccessible");
|
||||
return interaction.followUp("Error: Transcript log channel is missing or bot lacks permission.");
|
||||
}
|
||||
|
||||
try {
|
||||
let allMessages = [];
|
||||
let lastId = null;
|
||||
|
||||
while (true) {
|
||||
const options = { limit: 100 };
|
||||
if (lastId) {
|
||||
options.before = lastId;
|
||||
}
|
||||
|
||||
const messages = await interaction.channel.messages.fetch(options);
|
||||
if (messages.size === 0) break;
|
||||
|
||||
allMessages = [...allMessages, ...messages.values()];
|
||||
lastId = messages.last().id;
|
||||
|
||||
if (messages.size < 100) break;
|
||||
}
|
||||
|
||||
allMessages.sort((a, b) => a.createdTimestamp - b.createdTimestamp);
|
||||
|
||||
const transcriptData = allMessages.map(msg => ({
|
||||
content: msg.content || "No content",
|
||||
username: msg.author.username,
|
||||
userId: msg.author.id,
|
||||
avatar: msg.author.displayAvatarURL(),
|
||||
timestamp: msg.createdAt.toISOString(),
|
||||
attachments: Array.from(msg.attachments.values()).map(att => ({
|
||||
url: att.url,
|
||||
name: att.name
|
||||
})),
|
||||
embeds: msg.embeds.map(embed => ({
|
||||
title: embed.title,
|
||||
description: embed.description,
|
||||
fields: embed.fields
|
||||
}))
|
||||
}));
|
||||
|
||||
db.updateTicketStatus(interaction.channel.id, "closed");
|
||||
|
||||
const transcriptEmbed = new Discord.EmbedBuilder()
|
||||
.setTitle(msgconfig.TicketLog.Title)
|
||||
.setColor(msgconfig.TicketLog.Colour)
|
||||
.setFooter({ text: supportbot.Embed.Footer, iconURL: interaction.user.displayAvatarURL() })
|
||||
.setDescription(
|
||||
`> **Ticket:** ${interaction.channel.name} (\`${interaction.channel.id}\`)\n` +
|
||||
`> **User:** ${tUser?.tag || "Unknown User"} (\`${tUser?.id || ticket.user_id}\`)` +
|
||||
`> **Closed by:** <@${interaction.user.id}>\n` +
|
||||
`> **Message Count:** ${transcriptData.length}`
|
||||
)
|
||||
.addFields({ name: "Reason", value: `\`\`\`${reason}\`\`\``, inline: true });
|
||||
|
||||
const html = createTranscriptHTML(
|
||||
{
|
||||
id: interaction.channel.id,
|
||||
messages: transcriptData,
|
||||
name: interaction.channel.name
|
||||
},
|
||||
reason
|
||||
);
|
||||
|
||||
const transcriptDir = "./Data/Transcripts";
|
||||
|
||||
if (!fs.existsSync(transcriptDir)) {
|
||||
fs.mkdirSync(transcriptDir, { recursive: true });
|
||||
}
|
||||
|
||||
const fileName = `${interaction.channel.id}-transcript.html`;
|
||||
fs.writeFileSync(`./Data/Transcripts/${fileName}`, html);
|
||||
|
||||
await transcriptChannel.send({
|
||||
embeds: [transcriptEmbed],
|
||||
files: [{
|
||||
attachment: `./Data/Transcripts/${fileName}`,
|
||||
name: `SPOILER_${fileName}`,
|
||||
spoiler: true
|
||||
}]
|
||||
});
|
||||
|
||||
if (interaction.channel.type === Discord.ChannelType.GuildText ||
|
||||
interaction.channel.type === Discord.ChannelType.PrivateThread) {
|
||||
await interaction.channel.delete();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error handling ticket close:", err);
|
||||
interaction.followUp("An error occurred while closing the ticket.");
|
||||
}
|
||||
}
|
||||
|
||||
function parseMarkdown(content) {
|
||||
content = content.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
|
||||
|
||||
content = content.replace(/_(.*?)_/g, '<em>$1</em>');
|
||||
|
||||
content = content.replace(/`(.*?)`/g, '<code>$1</code>');
|
||||
|
||||
content = content.replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2" target="_blank">$1</a>');
|
||||
|
||||
content = content.replace(/```(.*?)```/gs, '<pre><code>$1</code></pre>');
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
function createTranscriptHTML(ticket, reason) {
|
||||
return `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
background: url('file-EWLgzFEYsq16ANVg6ENJrP.png') no-repeat center center fixed;
|
||||
background-size: cover;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
color: #ffffff;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
backdrop-filter: blur(10px);
|
||||
background: rgba(0,0,0,0.4);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.ticket-info p {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.message {
|
||||
backdrop-filter: blur(15px);
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 15px;
|
||||
padding: 15px;
|
||||
margin-bottom: 15px;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.message:hover {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
.message-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
margin-right: 15px;
|
||||
border: 2px solid rgba(255,255,255,0.3);
|
||||
}
|
||||
|
||||
.username {
|
||||
font-weight: bold;
|
||||
color: #00ffe4;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
margin-left: auto;
|
||||
color: #aaa;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.content {
|
||||
word-break: break-word;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.embed {
|
||||
border-left: 4px solid #00ffe4;
|
||||
padding-left: 10px;
|
||||
margin: 10px 0;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.attachment {
|
||||
display: inline-block;
|
||||
background-color: rgba(0, 255, 228, 0.1);
|
||||
border: 1px solid #00ffe4;
|
||||
border-radius: 5px;
|
||||
padding: 5px 10px;
|
||||
margin: 5px 0;
|
||||
color: #00ffe4;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Ticket Transcript</h1>
|
||||
<div class="ticket-info mb-6">
|
||||
<p><strong>Channel:</strong> ${ticket.name}</p>
|
||||
<p><strong>Ticket ID:</strong> ${ticket.id}</p>
|
||||
<p><strong>Message Count:</strong> ${ticket.messages.length}</p>
|
||||
<p><strong>Close Reason:</strong> ${reason}</p>
|
||||
</div>
|
||||
|
||||
<div class="messages">
|
||||
${ticket.messages.map(msg => `
|
||||
<div class="message">
|
||||
<div class="message-header">
|
||||
<img src="${msg.avatar}" alt="Avatar" class="avatar">
|
||||
<span class="username">${msg.username}</span>
|
||||
<span class="timestamp">${new Date(msg.timestamp).toLocaleString()}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
${parseMarkdown(msg.content)}
|
||||
|
||||
${msg.embeds.map(embed => `
|
||||
<div class="embed">
|
||||
${embed.title ? `<div class="font-bold">${embed.title}</div>` : ''}
|
||||
${embed.description ? `<div>${embed.description}</div>` : ''}
|
||||
${embed.fields.map(field => `
|
||||
<div class="mt-2">
|
||||
<strong>${field.name}:</strong>
|
||||
<div>${field.value}</div>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
`).join('')}
|
||||
|
||||
${msg.attachments.map(att => `
|
||||
<a href="${att.url}" class="attachment" target="_blank">
|
||||
📎 ${att.name}
|
||||
</a>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
const fs = require("fs");
|
||||
const {
|
||||
EmbedBuilder,
|
||||
ApplicationCommandOptionType,
|
||||
ApplicationCommandType,
|
||||
ChannelType,
|
||||
MessageFlags,
|
||||
} = require("discord.js");
|
||||
const yaml = require("js-yaml");
|
||||
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
|
||||
const Command = require("../../Structures/Command.js");
|
||||
const db = require("../../Structures/Database.js");
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.ForceAddUser.Command,
|
||||
description: cmdconfig.ForceAddUser.Description,
|
||||
type: ApplicationCommandType.ChatInput,
|
||||
options: [
|
||||
{
|
||||
name: "user",
|
||||
description: "The user to forcefully add",
|
||||
type: ApplicationCommandOptionType.User,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
permissions: cmdconfig.ForceAddUser.Permission,
|
||||
|
||||
async run(interaction) {
|
||||
const userToAdd = interaction.options.getUser("user");
|
||||
const ticketChannel = interaction.channel;
|
||||
|
||||
if (
|
||||
(supportbot.Ticket.TicketType === "threads" && !ticketChannel.isThread()) ||
|
||||
(supportbot.Ticket.TicketType === "channels" && ticketChannel.type !== ChannelType.GuildText)
|
||||
) {
|
||||
const onlyInTicket = new EmbedBuilder()
|
||||
.setTitle(msgconfig.ForceAddUser.NotInTicket_Title)
|
||||
.setDescription(msgconfig.ForceAddUser.NotInTicket_Description)
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
return interaction.reply({
|
||||
embeds: [onlyInTicket],
|
||||
flags: MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
const ticketRow = db.getTicket(ticketChannel.id);
|
||||
if (!ticketRow) {
|
||||
const notFound = new EmbedBuilder()
|
||||
.setTitle(msgconfig.ForceAddUser.NotInTicket_Title)
|
||||
.setDescription(msgconfig.ForceAddUser.NotInTicket_Description)
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
return interaction.reply({
|
||||
embeds: [notFound],
|
||||
flags: MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
if (supportbot.Ticket.TicketType === "threads") {
|
||||
const botMember = await ticketChannel.members.fetch(interaction.client.user.id).catch(() => null);
|
||||
if (!botMember) await ticketChannel.join();
|
||||
await new Promise(res => setTimeout(res, 1000));
|
||||
await ticketChannel.members.add(userToAdd.id);
|
||||
} else if (supportbot.Ticket.TicketType === "channels") {
|
||||
await ticketChannel.permissionOverwrites.create(userToAdd.id, {
|
||||
ViewChannel: true,
|
||||
SendMessages: true,
|
||||
ReadMessageHistory: true,
|
||||
});
|
||||
}
|
||||
|
||||
db.addUserToTicket(ticketChannel.id, userToAdd.id);
|
||||
|
||||
const addedEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.ForceAddUser.Added_Title)
|
||||
.setDescription(
|
||||
msgconfig.ForceAddUser.Added_Description.replace("%username%", userToAdd.username)
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
|
||||
await interaction.reply({
|
||||
embeds: [addedEmbed],
|
||||
flags: MessageFlags.Ephemeral,
|
||||
});
|
||||
|
||||
const addedToTicketEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.ForceAddUser.AddedToTicket_Title)
|
||||
.setDescription(
|
||||
msgconfig.ForceAddUser.AddedToTicket_Description.replace(
|
||||
"%channel_link%",
|
||||
`[${ticketChannel.name}](https://discord.com/channels/${ticketChannel.guild.id}/${ticketChannel.id})`
|
||||
)
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
await userToAdd.send({
|
||||
embeds: [addedToTicketEmbed],
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Error adding user to the ticket:", err);
|
||||
|
||||
const errorEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.ForceAddUser.Error_Title)
|
||||
.setDescription(msgconfig.ForceAddUser.Error_Adding)
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
await interaction.reply({
|
||||
embeds: [errorEmbed],
|
||||
flags: MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,108 @@
|
||||
// SupportBot | Emerald Services
|
||||
// Remove user from a ticket
|
||||
|
||||
const {
|
||||
EmbedBuilder,
|
||||
ApplicationCommandOptionType,
|
||||
ApplicationCommandType,
|
||||
ChannelType,
|
||||
MessageFlags,
|
||||
} = require("discord.js");
|
||||
const yaml = require("js-yaml");
|
||||
const fs = require("fs");
|
||||
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
|
||||
const Command = require("../../Structures/Command.js");
|
||||
const db = require("../../Structures/Database.js");
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.RemoveUser.Command,
|
||||
description: cmdconfig.RemoveUser.Description,
|
||||
type: ApplicationCommandType.ChatInput,
|
||||
options: [
|
||||
{
|
||||
name: "user",
|
||||
description: "The user to remove",
|
||||
type: ApplicationCommandOptionType.User,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
permissions: cmdconfig.RemoveUser.Permission,
|
||||
|
||||
async run(interaction) {
|
||||
const { getRole } = interaction.client;
|
||||
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("Missing staff/admin roles in config.");
|
||||
}
|
||||
if (
|
||||
!interaction.member.roles.cache.has(SupportStaff.id) &&
|
||||
!interaction.member.roles.cache.has(Admin.id)
|
||||
) {
|
||||
const NoPerms = new EmbedBuilder()
|
||||
.setTitle("Invalid Permissions!")
|
||||
.setDescription(
|
||||
`${msgconfig.Error.IncorrectPerms}\n\nRole Required: \`${supportbot.Roles.StaffMember.Staff}\` or \`${supportbot.Roles.StaffMember.Admin}\``
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.Warn);
|
||||
return interaction.reply({ embeds: [NoPerms], flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
|
||||
const userToRemove = interaction.options.getUser("user");
|
||||
const ticketChannel = interaction.channel;
|
||||
|
||||
if (
|
||||
(supportbot.Ticket.TicketType === "threads" && !ticketChannel.isThread()) ||
|
||||
(supportbot.Ticket.TicketType === "channels" && ticketChannel.type !== ChannelType.GuildText)
|
||||
) {
|
||||
const onlyInTicket = new EmbedBuilder()
|
||||
.setTitle(msgconfig.RemoveUser.NotInTicket_Title)
|
||||
.setDescription(msgconfig.RemoveUser.NotInTicket_Description)
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
return interaction.reply({ embeds: [onlyInTicket], flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
|
||||
try {
|
||||
if (supportbot.Ticket.TicketType === "threads") {
|
||||
await ticketChannel.members.remove(userToRemove.id);
|
||||
} else {
|
||||
await ticketChannel.permissionOverwrites.delete(userToRemove.id);
|
||||
}
|
||||
|
||||
db.removeUserFromTicket(ticketChannel.id, userToRemove.id);
|
||||
|
||||
const removedEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.RemoveUser.Removed_Title)
|
||||
.setDescription(msgconfig.RemoveUser.Removed_Message)
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
|
||||
await interaction.reply({ embeds: [removedEmbed], flags: MessageFlags.Ephemeral });
|
||||
|
||||
const removedFromTicketEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.RemoveUser.RemovedFromTicket_Title)
|
||||
.setDescription(
|
||||
msgconfig.RemoveUser.RemovedFromTicket_Description.replace(
|
||||
"%channel_link%",
|
||||
`[${ticketChannel.name}](https://discord.com/channels/${ticketChannel.guild.id}/${ticketChannel.id})`
|
||||
)
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
await userToRemove.send({ embeds: [removedFromTicketEmbed] }).catch(() => {});
|
||||
} catch (err) {
|
||||
console.error("Error removing user from the ticket:", err);
|
||||
|
||||
const errorEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.RemoveUser.Error_Title)
|
||||
.setDescription(msgconfig.RemoveUser.Error_Removing)
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
await interaction.reply({ embeds: [errorEmbed], flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,551 @@
|
||||
const fs = require("fs");
|
||||
const {
|
||||
EmbedBuilder,
|
||||
ApplicationCommandOptionType,
|
||||
ApplicationCommandType,
|
||||
ChannelType,
|
||||
ActionRowBuilder,
|
||||
ButtonBuilder,
|
||||
StringSelectMenuBuilder,
|
||||
StringSelectMenuOptionBuilder,
|
||||
ButtonStyle,
|
||||
MessageCollector,
|
||||
MessageFlags,
|
||||
} = require("discord.js");
|
||||
const yaml = require("js-yaml");
|
||||
|
||||
const db = require("../../Structures/Database.js");
|
||||
|
||||
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");
|
||||
|
||||
async function isBlacklisted(userId) {
|
||||
let blacklistData;
|
||||
try {
|
||||
blacklistData = JSON.parse(fs.readFileSync("./Data/BlacklistedUsers.json", "utf8"));
|
||||
} catch (error) {
|
||||
console.error("Error reading BlacklistedUsers.json:", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!blacklistData || !Array.isArray(blacklistData.blacklistedUsers)) {
|
||||
console.error("blacklistedUsers is not defined or is not an array");
|
||||
return false;
|
||||
}
|
||||
|
||||
return blacklistData.blacklistedUsers.includes(userId);
|
||||
}
|
||||
|
||||
async function getClockedInUsers(guild) {
|
||||
const clockedInUsers = new Set();
|
||||
const rows = guild.members.cache.map(m => m.id);
|
||||
for (const id of rows) {
|
||||
const profile = db.getProfile(id);
|
||||
if (profile && profile.clockedIn) {
|
||||
clockedInUsers.add(id);
|
||||
}
|
||||
}
|
||||
return clockedInUsers;
|
||||
}
|
||||
|
||||
async function askTicketQuestions(ticketChannel, interaction, questions) {
|
||||
const answers = [];
|
||||
let questionIndex = 0;
|
||||
|
||||
const sendNextQuestion = async () => {
|
||||
if (questionIndex < questions.length) {
|
||||
const questionsEmbed = new EmbedBuilder()
|
||||
.setDescription(`**${questionIndex + 1}.** ${questions[questionIndex]}`)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
const questionMessage = await ticketChannel.send({ embeds: [questionsEmbed] });
|
||||
|
||||
const collector = new MessageCollector(ticketChannel, {
|
||||
filter: (m) => m.author.id === interaction.user.id,
|
||||
max: 1,
|
||||
time: 60000,
|
||||
});
|
||||
|
||||
collector.on("collect", async (message) => {
|
||||
answers.push({ question: questions[questionIndex], answer: message.content });
|
||||
questionIndex++;
|
||||
await message.delete();
|
||||
await questionMessage.delete();
|
||||
sendNextQuestion();
|
||||
});
|
||||
} else {
|
||||
const summaryEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.Ticket.TicketQuestions.Details.Title)
|
||||
.setDescription(
|
||||
msgconfig.Ticket.TicketQuestions.Details.Description.replace("%user%", interaction.user.id)
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.General)
|
||||
.addFields(
|
||||
answers.map((a) => ({
|
||||
name: `Q: ${a.question}`,
|
||||
value: `A: ${a.answer}`,
|
||||
inline: false,
|
||||
}))
|
||||
)
|
||||
.setTimestamp();
|
||||
|
||||
await ticketChannel.send({ embeds: [summaryEmbed] });
|
||||
}
|
||||
};
|
||||
|
||||
sendNextQuestion();
|
||||
}
|
||||
|
||||
async function assignTicketToUser(ticketChannel, Staff, Admin, interaction, attempts = 0) {
|
||||
try {
|
||||
const clockedInUsers = await getClockedInUsers(interaction.guild);
|
||||
|
||||
if (clockedInUsers.size === 0 || attempts >= 2) {
|
||||
const claimChannel = await interaction.guild.channels.cache.get(supportbot.Ticket.ClaimTickets.Channel);
|
||||
if (claimChannel) {
|
||||
const claimEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.Ticket.ClaimTickets.ClaimTitle)
|
||||
.setDescription(msgconfig.Ticket.ClaimTickets.ClaimMessage)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
const claimButton = new ButtonBuilder()
|
||||
.setCustomId(`claimticket-${ticketChannel.id}`)
|
||||
.setLabel(supportbot.Ticket.ClaimTickets.ButtonTitle || "Claim Ticket")
|
||||
.setStyle(ButtonStyle.Primary);
|
||||
|
||||
const row = new ActionRowBuilder().addComponents(claimButton);
|
||||
|
||||
const claimMessage = await claimChannel.send({
|
||||
content: `<@&${Staff.id}> <@&${Admin.id}>`,
|
||||
embeds: [claimEmbed],
|
||||
components: [row],
|
||||
});
|
||||
|
||||
const filter = (i) =>
|
||||
i.customId === `claimticket-${ticketChannel.id}` &&
|
||||
i.member.roles.cache.has(Staff.id);
|
||||
const collector = claimMessage.createMessageComponentCollector({ filter, time: 120000 });
|
||||
|
||||
collector.on("collect", async (i) => {
|
||||
await ticketChannel.send({ content: `<@${i.user.id}> has claimed the ticket.` });
|
||||
});
|
||||
|
||||
collector.on("end", async (collected) => {
|
||||
if (collected.size === 0) {
|
||||
await claimMessage.delete();
|
||||
await claimChannel.send({
|
||||
content: `Ticket is still unclaimed. \nCC: [<@&${Staff.id}> <@&${Admin.id}>]`,
|
||||
embeds: [
|
||||
new EmbedBuilder()
|
||||
.setDescription(`Ticket is still unclaimed: <#${ticketChannel.id}>`)
|
||||
.setColor(supportbot.Embed.Colours.Warn),
|
||||
],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return null;
|
||||
} else {
|
||||
console.error("Claim channel not found");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const clockedInArray = Array.from(clockedInUsers);
|
||||
const assignedUserId =
|
||||
clockedInArray[Math.floor(Math.random() * clockedInArray.length)];
|
||||
const assignedUser = await interaction.guild.members.fetch(assignedUserId);
|
||||
|
||||
const claimEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.Ticket.ClaimTickets.ClaimTitle)
|
||||
.setDescription(
|
||||
msgconfig.Ticket.ClaimTickets.ClaimMessage.replace("%user%", interaction.user.id)
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
const claimButton = new ButtonBuilder()
|
||||
.setCustomId(`claimticket-${ticketChannel.id}`)
|
||||
.setLabel(supportbot.Ticket.ClaimTickets.ButtonTitle || "Claim Ticket")
|
||||
.setStyle(ButtonStyle.Primary);
|
||||
|
||||
const row = new ActionRowBuilder().addComponents(claimButton);
|
||||
|
||||
const claimChannel = await interaction.guild.channels.cache.get(
|
||||
supportbot.Ticket.ClaimTickets.Channel
|
||||
);
|
||||
if (!claimChannel) {
|
||||
console.error("Claim channel not found");
|
||||
return null;
|
||||
}
|
||||
|
||||
const claimMessage = await claimChannel.send({
|
||||
content: `${assignedUser}`,
|
||||
embeds: [claimEmbed],
|
||||
components: [row],
|
||||
});
|
||||
|
||||
const filter = (i) =>
|
||||
i.customId === `claimticket-${ticketChannel.id}` && i.user.id === assignedUserId;
|
||||
const collector = claimMessage.createMessageComponentCollector({ filter, time: 120000 });
|
||||
|
||||
collector.on("collect", async (i) => {
|
||||
await ticketChannel
|
||||
.send({ content: `<@${assignedUserId}>` })
|
||||
.then((msg) => setTimeout(() => msg.delete(), 5000));
|
||||
});
|
||||
|
||||
collector.on("end", async (collected) => {
|
||||
if (collected.size === 0) {
|
||||
await claimMessage.delete();
|
||||
|
||||
if (attempts >= 1) {
|
||||
await claimChannel.send({
|
||||
content: `No one claimed the ticket. Tagging roles: <@&${Staff.id}> <@&${Admin.id}>`,
|
||||
embeds: [
|
||||
new EmbedBuilder()
|
||||
.setDescription(`Ticket is still unclaimed: <#${ticketChannel.id}>`)
|
||||
.setColor(supportbot.Embed.Colours.Warn),
|
||||
],
|
||||
});
|
||||
|
||||
const claimEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.Ticket.ClaimTickets.ClaimTitle)
|
||||
.setDescription(msgconfig.Ticket.ClaimTickets.ClaimMessage)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
const claimButton = new ButtonBuilder()
|
||||
.setCustomId(`claimticket-${ticketChannel.id}`)
|
||||
.setLabel(supportbot.Ticket.ClaimTickets.ButtonTitle)
|
||||
.setEmoji(supportbot.Ticket.ClaimTickets.ButtonEmoji)
|
||||
.setStyle(ButtonStyle.Primary);
|
||||
|
||||
const row = new ActionRowBuilder().addComponents(claimButton);
|
||||
|
||||
const claimMessage = await claimChannel.send({
|
||||
embeds: [claimEmbed],
|
||||
components: [row],
|
||||
});
|
||||
|
||||
const filter = (i) =>
|
||||
i.customId === `claimticket-${ticketChannel.id}` &&
|
||||
i.member.roles.cache.has(Staff.id);
|
||||
const collector = claimMessage.createMessageComponentCollector({
|
||||
filter,
|
||||
time: 120000,
|
||||
});
|
||||
|
||||
collector.on("collect", async (i) => {
|
||||
console.log(`Ticket ${ticketChannel.id} claimed by user ${i.user.id}`);
|
||||
await ticketChannel.member.add(i.user.id);
|
||||
});
|
||||
|
||||
collector.on("end", async (collected) => {
|
||||
if (collected.size === 0) {
|
||||
console.log(
|
||||
`No one claimed the ticket (${ticketChannel.id}), notifying all staff`
|
||||
);
|
||||
await claimMessage.delete();
|
||||
|
||||
await claimChannel.send({
|
||||
content: `No one claimed the ticket. Tagging roles: <@&${Staff.id}> <@&${Admin.id}>`,
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const ReassigningEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.Ticket.ClaimTickets.ReassigningTitle)
|
||||
.setDescription(
|
||||
msgconfig.Ticket.ClaimTickets.ReassigningMessage.replace("%user%", assignedUserId)
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
await claimChannel.send({ embeds: [ReassigningEmbed], components: [] });
|
||||
await assignTicketToUser(ticketChannel, Staff, Admin, interaction, attempts + 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return assignedUserId;
|
||||
} catch (error) {
|
||||
console.error("Error in assignTicketToUser function:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.OpenTicket.Command,
|
||||
description: cmdconfig.OpenTicket.Description,
|
||||
type: ApplicationCommandType.ChatInput,
|
||||
options: [
|
||||
{
|
||||
name: "reason",
|
||||
description: "Ticket Reason",
|
||||
type: ApplicationCommandOptionType.String,
|
||||
},
|
||||
],
|
||||
permissions: cmdconfig.OpenTicket.Permission,
|
||||
|
||||
async run(interaction) {
|
||||
|
||||
try {
|
||||
|
||||
if (await isBlacklisted(interaction.user.id)) {
|
||||
return interaction.reply({
|
||||
content: msgconfig.Ticket.Blacklisted,
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
let TicketReason = null;
|
||||
if (supportbot.Ticket.TicketReason) {
|
||||
TicketReason = interaction.reason || null;
|
||||
}
|
||||
|
||||
let TicketData = { tickets: db.getAllTickets() };
|
||||
|
||||
const { getRole, getChannel } = interaction.client;
|
||||
let User = interaction.guild.members.cache.get(interaction.user.id);
|
||||
|
||||
if (
|
||||
supportbot.Ticket.TicketsPerUser &&
|
||||
TicketData.tickets.filter((t) => t.user === interaction.user.id && t.open).length >= supportbot.Ticket.TicketsPerUser
|
||||
) {
|
||||
return interaction.reply({
|
||||
embeds: [
|
||||
{
|
||||
title: "Too Many Tickets!",
|
||||
description: `You can't have more than ${supportbot.Ticket.TicketsPerUser} open tickets!`,
|
||||
color: supportbot.Embed.Colours.Warn,
|
||||
},
|
||||
],
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
let ticketNumberID = await TicketNumberID.pad();
|
||||
const TicketSubject = TicketReason || msgconfig.Ticket.InvalidSubject;
|
||||
|
||||
const TicketExists = new EmbedBuilder()
|
||||
.setTitle("Ticket Exists!")
|
||||
.setDescription(msgconfig.Ticket.TicketExists);
|
||||
|
||||
if (
|
||||
await interaction.guild.channels.cache.find(
|
||||
(ticketChannel) => ticketChannel.name === `${supportbot.Ticket.Channel}${ticketNumberID}`
|
||||
)
|
||||
) {
|
||||
return await interaction.reply({
|
||||
embeds: [TicketExists],
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
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.",
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
|
||||
let ticketChannel;
|
||||
|
||||
if (supportbot.Ticket.TicketType === "threads") {
|
||||
const TicketHome = interaction.guild.channels.cache.find(
|
||||
(c) => c.name === supportbot.Ticket.TicketHome || c.id === supportbot.Ticket.TicketHome
|
||||
);
|
||||
|
||||
const channel = await getChannel(supportbot.Ticket.TicketHome, interaction.guild);
|
||||
ticketChannel = await channel.threads.create({
|
||||
name: `${supportbot.Ticket.Channel}${ticketNumberID}`,
|
||||
type: ChannelType.PrivateThread,
|
||||
parent: TicketHome,
|
||||
autoArchiveDuration: 60,
|
||||
reason: 'support ticket',
|
||||
});
|
||||
} else if (supportbot.Ticket.TicketType === "channels") {
|
||||
const category = interaction.guild.channels.cache.find(
|
||||
(c) => c.name === supportbot.Ticket.TicketChannelsCategory || c.id === supportbot.Ticket.TicketChannelsCategory
|
||||
);
|
||||
|
||||
if (!category) {
|
||||
return interaction.reply({
|
||||
content: "The ticket category does not exist!",
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
ticketChannel = await interaction.guild.channels.create({
|
||||
name: `${supportbot.Ticket.Channel}${ticketNumberID}`,
|
||||
type: ChannelType.GuildText,
|
||||
parent: category.id,
|
||||
reason: 'support ticket',
|
||||
});
|
||||
|
||||
if (category.children.size >= 50) {
|
||||
const secondaryCategory = interaction.guild.channels.cache.find(
|
||||
(c) => c.name === supportbot.Ticket.TicketChannelsCategory2 || c.id === supportbot.Ticket.TicketChannelsCategory2
|
||||
);
|
||||
|
||||
if (secondaryCategory) {
|
||||
await ticketChannel.setParent(secondaryCategory.id);
|
||||
} else {
|
||||
return interaction.reply({
|
||||
content: "The secondary ticket category does not exist!",
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (supportbot.Ticket.ClaimTickets.Enabled) {
|
||||
const assignedUserId = await assignTicketToUser(ticketChannel, Staff, Admin, interaction);
|
||||
if (!assignedUserId) {
|
||||
const claimChannel = await interaction.guild.channels.cache.get(supportbot.Ticket.ClaimTickets.Channel);
|
||||
}
|
||||
}
|
||||
|
||||
if (supportbot.Ticket.TicketType === "threads") {
|
||||
await ticketChannel.members.add(interaction.user.id);
|
||||
|
||||
} else if (supportbot.Ticket.TicketType === "channels") {
|
||||
|
||||
await ticketChannel.permissionOverwrites.create(interaction.user.id, {
|
||||
ViewChannel: true,
|
||||
SendMessages: true,
|
||||
ReadMessageHistory: true,
|
||||
});
|
||||
|
||||
await ticketChannel.permissionOverwrites.create(Admin, {
|
||||
ViewChannel: true,
|
||||
SendMessages: true,
|
||||
ReadMessageHistory: true,
|
||||
});
|
||||
|
||||
await ticketChannel.permissionOverwrites.create(interaction.guild.roles.everyone, {
|
||||
ViewChannel: false,
|
||||
});
|
||||
}
|
||||
|
||||
db.addTicket({
|
||||
id: ticketChannel.id,
|
||||
name: ticketChannel.name,
|
||||
user: interaction.user.id,
|
||||
number: ticketNumberID,
|
||||
reason: TicketSubject,
|
||||
open: true,
|
||||
subUsers: [],
|
||||
createdAt: new Date().toISOString(),
|
||||
claimedAt: null,
|
||||
});
|
||||
TicketData.tickets = db.getAllTickets();
|
||||
|
||||
const CreatedTicket = new EmbedBuilder()
|
||||
.setDescription(
|
||||
msgconfig.Ticket.TicketCreatedAlert.replace(/%ticketauthor%/g, interaction.user.id)
|
||||
.replace(/%ticketid%/g, ticketChannel.id)
|
||||
.replace(/%ticketusername%/g, interaction.user.username)
|
||||
.replace(/%ticketreason%/g, TicketSubject)
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
await interaction.reply({ embeds: [CreatedTicket], flags: MessageFlags.Ephemeral });
|
||||
|
||||
const TicketMessage = new EmbedBuilder()
|
||||
.setAuthor({
|
||||
name: msgconfig.Ticket.TicketAuthorTitle.replace(/%ticketauthor%/g, interaction.user.id)
|
||||
.replace(/%ticketid%/g, ticketChannel.id)
|
||||
.replace(/%ticketusername%/g, interaction.user.username),
|
||||
iconURL: interaction.user.displayAvatarURL(),
|
||||
})
|
||||
.setTitle(
|
||||
msgconfig.Ticket.TicketTitle.replace(/%ticketauthor%/g, interaction.user.id)
|
||||
.replace(/%ticketid%/g, ticketChannel.id)
|
||||
.replace(/%ticketusername%/g, interaction.user.username)
|
||||
)
|
||||
.setDescription(
|
||||
msgconfig.Ticket.TicketMessage.replace(/%ticketauthor%/g, interaction.user.id)
|
||||
.replace(/%ticketid%/g, ticketChannel.id)
|
||||
.replace(/%ticketusername%/g, interaction.user.username)
|
||||
.replace(/%ticketreason%/g, TicketSubject)
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
if (supportbot.Ticket.TicketReason && TicketReason) {
|
||||
TicketMessage.addFields(
|
||||
{ name: "Reason", value: TicketReason, inline: false }
|
||||
);
|
||||
}
|
||||
|
||||
const SelectMenus = new StringSelectMenuBuilder()
|
||||
.setCustomId("ticketcontrolpanel")
|
||||
.setPlaceholder("Ticket Control Panel")
|
||||
.addOptions(
|
||||
new StringSelectMenuOptionBuilder()
|
||||
.setLabel("Close")
|
||||
.setDescription("Close the ticket.")
|
||||
.setEmoji(supportbot.SelectMenus.Tickets.CloseEmoji)
|
||||
.setValue("ticketclose"),
|
||||
new StringSelectMenuOptionBuilder()
|
||||
.setLabel("Voice Channel")
|
||||
.setDescription("Create a support voice channel.")
|
||||
.setEmoji(supportbot.SelectMenus.Tickets.VCEmoji)
|
||||
.setValue("supportvc"),
|
||||
new StringSelectMenuOptionBuilder()
|
||||
.setLabel("Archive")
|
||||
.setDescription("Archive the ticket.")
|
||||
.setEmoji(supportbot.SelectMenus.Tickets.ArchiveEmoji)
|
||||
.setValue("archiveticket"),
|
||||
...(supportbot.Ticket.TicketType === "threads" ? [
|
||||
new StringSelectMenuOptionBuilder()
|
||||
.setLabel("Lock Ticket")
|
||||
.setDescription("Lock the ticket.")
|
||||
.setEmoji(supportbot.SelectMenus.Tickets.LockEmoji)
|
||||
.setValue("lockticket")
|
||||
] : []),
|
||||
new StringSelectMenuOptionBuilder()
|
||||
.setLabel("Rename Ticket")
|
||||
.setDescription("Rename the ticket.")
|
||||
.setEmoji(supportbot.SelectMenus.Tickets.RenameEmoji)
|
||||
.setValue("renameticket")
|
||||
);
|
||||
|
||||
if (supportbot.Ticket.TicketType === "threads") {
|
||||
SelectMenus.addOptions(
|
||||
new StringSelectMenuOptionBuilder()
|
||||
.setLabel("Enable Invites")
|
||||
.setDescription("Enable invites.")
|
||||
.setEmoji(supportbot.SelectMenus.Tickets.EnableInvitesEmoji)
|
||||
.setValue("enableinvites"),
|
||||
new StringSelectMenuOptionBuilder()
|
||||
.setLabel("Disable Invites")
|
||||
.setDescription("Disable invites.")
|
||||
.setEmoji(supportbot.SelectMenus.Tickets.DisableInvitesEmoji)
|
||||
.setValue("disableinvites"),
|
||||
);
|
||||
}
|
||||
|
||||
const row2 = new ActionRowBuilder().addComponents(SelectMenus);
|
||||
|
||||
await ticketChannel.send({
|
||||
embeds: [TicketMessage],
|
||||
components: [row2],
|
||||
});
|
||||
|
||||
if (supportbot.Ticket.Questions.Enabled) {
|
||||
const questions = supportbot.Ticket.Questions.List || [];
|
||||
|
||||
if (questions.length > 0) {
|
||||
await askTicketQuestions(ticketChannel, interaction, questions);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error in run method:", error);
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,113 @@
|
||||
const fs = require("fs");
|
||||
const {
|
||||
EmbedBuilder,
|
||||
ApplicationCommandType,
|
||||
ActionRowBuilder,
|
||||
ButtonBuilder,
|
||||
MessageFlags,
|
||||
} = 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");
|
||||
const db = require("../../Structures/Database.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`.",
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
const { getChannel } = interaction.client;
|
||||
|
||||
const channel = await getChannel(
|
||||
supportbot.Ticket.TicketHome,
|
||||
interaction.guild
|
||||
);
|
||||
|
||||
if (!channel) {
|
||||
console.log(`[TICKET PANEL] ${channelName} channel not found. Please check your config file.`);
|
||||
return interaction.reply({
|
||||
content: `${channelName} channel not found. Please check your config file.`,
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
const panelRow = db.getTicketPanel();
|
||||
if (panelRow) {
|
||||
try {
|
||||
await channel.messages.fetch(panelRow.message_id);
|
||||
return interaction.reply({
|
||||
content: "Ticket panel message already exists.",
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
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],
|
||||
});
|
||||
|
||||
db.saveTicketPanel(message.id, channel.id);
|
||||
|
||||
return interaction.reply({
|
||||
content: "Ticket panel message has been sent!",
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("Error sending message:", e);
|
||||
return interaction.reply({
|
||||
content: "An error occurred while sending the ticket panel message.",
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,468 @@
|
||||
const {
|
||||
ApplicationCommandType,
|
||||
ApplicationCommandOptionType,
|
||||
ActionRowBuilder,
|
||||
ButtonBuilder,
|
||||
EmbedBuilder,
|
||||
StringSelectMenuBuilder,
|
||||
ModalBuilder,
|
||||
TextInputBuilder,
|
||||
TextInputStyle,
|
||||
ButtonStyle,
|
||||
ComponentType,
|
||||
MessageFlags,
|
||||
} = require('discord.js');
|
||||
const Command = require('../../Structures/Command.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'));
|
||||
|
||||
function parseColor(color) {
|
||||
if (!color) return 0x5865F2;
|
||||
if (typeof color === 'number') return color;
|
||||
if (typeof color !== 'string') return 0x5865F2;
|
||||
color = color.replace('#', '');
|
||||
if (/^[0-9A-F]{6}$/i.test(color)) {
|
||||
return parseInt(color, 16);
|
||||
}
|
||||
return 0x5865F2;
|
||||
}
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.Embed.Command,
|
||||
description: cmdconfig.Embed.Description,
|
||||
type: ApplicationCommandType.ChatInput,
|
||||
options: [
|
||||
{
|
||||
name: 'channel',
|
||||
description: 'Select channel to send the message',
|
||||
type: ApplicationCommandOptionType.Channel,
|
||||
required: true
|
||||
}
|
||||
],
|
||||
permissions: cmdconfig.Embed.Permission,
|
||||
|
||||
async run(interaction) {
|
||||
const { getRole } = interaction.client;
|
||||
let SupportStaff = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild);
|
||||
let Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild);
|
||||
|
||||
if (!SupportStaff || !Admin) {
|
||||
return interaction.reply('Some roles seem to be missing!\nPlease check for errors when starting the bot.');
|
||||
}
|
||||
|
||||
if (!interaction.member.roles.cache.has(SupportStaff.id) &&
|
||||
!interaction.member.roles.cache.has(Admin.id)) {
|
||||
const NoPerms = new EmbedBuilder()
|
||||
.setTitle('Invalid Permissions!')
|
||||
.setDescription(`${msgconfig.Error.IncorrectPerms}\n\nRole Required: \`${supportbot.Roles.StaffMember.Staff}\` or \`${supportbot.Roles.StaffMember.Admin}\``)
|
||||
.setColor(0xFF0000);
|
||||
return interaction.reply({ embeds: [NoPerms], flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
|
||||
const channel = interaction.options.getChannel('channel');
|
||||
|
||||
const messageData = {
|
||||
type: 'embed',
|
||||
title: 'New Embed',
|
||||
description: 'Click the buttons below to edit this embed',
|
||||
color: 0x5865F2,
|
||||
fields: [],
|
||||
footer: null,
|
||||
thumbnail: null,
|
||||
image: null,
|
||||
timestamp: false
|
||||
};
|
||||
|
||||
function createEmbed() {
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(messageData.title)
|
||||
.setDescription(messageData.description)
|
||||
.setColor(messageData.color);
|
||||
|
||||
if (messageData.fields.length > 0) {
|
||||
embed.addFields(messageData.fields);
|
||||
}
|
||||
if (messageData.footer) {
|
||||
embed.setFooter({ text: messageData.footer });
|
||||
}
|
||||
if (messageData.thumbnail) {
|
||||
embed.setThumbnail(messageData.thumbnail);
|
||||
}
|
||||
if (messageData.image) {
|
||||
embed.setImage(messageData.image);
|
||||
}
|
||||
if (messageData.timestamp) {
|
||||
embed.setTimestamp();
|
||||
}
|
||||
|
||||
return embed;
|
||||
}
|
||||
|
||||
const typeRow = new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new StringSelectMenuBuilder()
|
||||
.setCustomId('message_type')
|
||||
.setPlaceholder('Select message type')
|
||||
.addOptions([
|
||||
{
|
||||
label: 'Embed Message',
|
||||
description: 'Send as an embedded message',
|
||||
value: 'embed'
|
||||
},
|
||||
{
|
||||
label: 'Text Message',
|
||||
description: 'Send as a plain text message',
|
||||
value: 'text'
|
||||
}
|
||||
])
|
||||
);
|
||||
|
||||
function createButtons(messageType) {
|
||||
const buttons = [];
|
||||
|
||||
if (messageType === 'embed') {
|
||||
const row1 = new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('edit_title')
|
||||
.setLabel('Edit Title')
|
||||
.setStyle(ButtonStyle.Secondary),
|
||||
new ButtonBuilder()
|
||||
.setCustomId('edit_description')
|
||||
.setLabel('Edit Description')
|
||||
.setStyle(ButtonStyle.Secondary),
|
||||
new ButtonBuilder()
|
||||
.setCustomId('edit_color')
|
||||
.setLabel('Edit Color')
|
||||
.setStyle(ButtonStyle.Secondary),
|
||||
new ButtonBuilder()
|
||||
.setCustomId('add_field')
|
||||
.setLabel('Add Field')
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
|
||||
const row2 = new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('edit_footer')
|
||||
.setLabel('Edit Footer')
|
||||
.setStyle(ButtonStyle.Secondary),
|
||||
new ButtonBuilder()
|
||||
.setCustomId('edit_image')
|
||||
.setLabel('Add Image')
|
||||
.setStyle(ButtonStyle.Secondary),
|
||||
new ButtonBuilder()
|
||||
.setCustomId('edit_thumbnail')
|
||||
.setLabel('Add Thumbnail')
|
||||
.setStyle(ButtonStyle.Secondary),
|
||||
new ButtonBuilder()
|
||||
.setCustomId('toggle_timestamp')
|
||||
.setLabel('Toggle Timestamp')
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
|
||||
buttons.push(row1, row2);
|
||||
} else {
|
||||
const textRow = new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('edit_description')
|
||||
.setLabel('Edit Text')
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
|
||||
buttons.push(textRow);
|
||||
}
|
||||
|
||||
// Send button always shows
|
||||
const sendRow = new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('send_message')
|
||||
.setLabel('Send')
|
||||
.setStyle(ButtonStyle.Success)
|
||||
);
|
||||
|
||||
buttons.push(sendRow);
|
||||
return buttons;
|
||||
}
|
||||
|
||||
async function updatePreview(i) {
|
||||
if (messageData.type === 'embed') {
|
||||
await i.update({
|
||||
content: 'Customize your message:',
|
||||
embeds: [createEmbed()],
|
||||
components: [typeRow, ...createButtons('embed')]
|
||||
});
|
||||
} else {
|
||||
await i.update({
|
||||
content: messageData.description,
|
||||
embeds: [],
|
||||
components: [typeRow, ...createButtons('text')]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const message = await interaction.reply({
|
||||
content: messageData.type === 'text' ? messageData.description : 'Customize your message:',
|
||||
components: [typeRow, ...createButtons(messageData.type)],
|
||||
embeds: messageData.type === 'embed' ? [createEmbed()] : [],
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
|
||||
const collector = message.createMessageComponentCollector({
|
||||
time: 900000
|
||||
});
|
||||
|
||||
collector.on('collect', async (i) => {
|
||||
if (i.user.id !== interaction.user.id) {
|
||||
return i.reply({
|
||||
content: 'You cannot edit this message.',
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
switch (i.customId) {
|
||||
case 'message_type':
|
||||
messageData.type = i.values[0];
|
||||
await updatePreview(i);
|
||||
break;
|
||||
|
||||
case 'edit_description':
|
||||
const descModal = new ModalBuilder()
|
||||
.setCustomId('desc_modal')
|
||||
.setTitle(messageData.type === 'embed' ? 'Edit Description' : 'Edit Text');
|
||||
|
||||
descModal.addComponents(
|
||||
new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new TextInputBuilder()
|
||||
.setCustomId('desc_input')
|
||||
.setLabel(messageData.type === 'embed' ? 'Description' : 'Message Text')
|
||||
.setStyle(TextInputStyle.Paragraph)
|
||||
.setValue(messageData.description)
|
||||
.setMaxLength(4000)
|
||||
)
|
||||
);
|
||||
|
||||
await i.showModal(descModal);
|
||||
break;
|
||||
|
||||
case 'edit_title':
|
||||
const titleModal = new ModalBuilder()
|
||||
.setCustomId('title_modal')
|
||||
.setTitle('Edit Title');
|
||||
|
||||
titleModal.addComponents(
|
||||
new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new TextInputBuilder()
|
||||
.setCustomId('title_input')
|
||||
.setLabel('Title')
|
||||
.setStyle(TextInputStyle.Short)
|
||||
.setValue(messageData.title)
|
||||
.setMaxLength(256)
|
||||
)
|
||||
);
|
||||
|
||||
await i.showModal(titleModal);
|
||||
break;
|
||||
|
||||
case 'edit_color':
|
||||
const colorModal = new ModalBuilder()
|
||||
.setCustomId('color_modal')
|
||||
.setTitle('Edit Color');
|
||||
|
||||
colorModal.addComponents(
|
||||
new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new TextInputBuilder()
|
||||
.setCustomId('color_input')
|
||||
.setLabel('Color (HEX)')
|
||||
.setStyle(TextInputStyle.Short)
|
||||
.setPlaceholder('#FF0000')
|
||||
.setMinLength(6)
|
||||
.setMaxLength(7)
|
||||
.setValue(messageData.color.toString(16).padStart(6, '0'))
|
||||
)
|
||||
);
|
||||
|
||||
await i.showModal(colorModal);
|
||||
break;
|
||||
|
||||
case 'add_field':
|
||||
const fieldModal = new ModalBuilder()
|
||||
.setCustomId('field_modal')
|
||||
.setTitle('Add Field');
|
||||
|
||||
fieldModal.addComponents(
|
||||
new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new TextInputBuilder()
|
||||
.setCustomId('field_name')
|
||||
.setLabel('Field Name')
|
||||
.setStyle(TextInputStyle.Short)
|
||||
.setMaxLength(256)
|
||||
),
|
||||
new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new TextInputBuilder()
|
||||
.setCustomId('field_value')
|
||||
.setLabel('Field Value')
|
||||
.setStyle(TextInputStyle.Paragraph)
|
||||
.setMaxLength(1024)
|
||||
),
|
||||
new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new TextInputBuilder()
|
||||
.setCustomId('field_inline')
|
||||
.setLabel('Inline? (true/false)')
|
||||
.setStyle(TextInputStyle.Short)
|
||||
.setValue('false')
|
||||
.setPlaceholder('true or false')
|
||||
)
|
||||
);
|
||||
|
||||
await i.showModal(fieldModal);
|
||||
break;
|
||||
|
||||
case 'edit_footer':
|
||||
const footerModal = new ModalBuilder()
|
||||
.setCustomId('footer_modal')
|
||||
.setTitle('Edit Footer');
|
||||
|
||||
footerModal.addComponents(
|
||||
new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new TextInputBuilder()
|
||||
.setCustomId('footer_input')
|
||||
.setLabel('Footer Text')
|
||||
.setStyle(TextInputStyle.Short)
|
||||
.setValue(messageData.footer || '')
|
||||
.setMaxLength(2048)
|
||||
)
|
||||
);
|
||||
|
||||
await i.showModal(footerModal);
|
||||
break;
|
||||
|
||||
case 'edit_image':
|
||||
const imageModal = new ModalBuilder()
|
||||
.setCustomId('image_modal')
|
||||
.setTitle('Add Image');
|
||||
|
||||
imageModal.addComponents(
|
||||
new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new TextInputBuilder()
|
||||
.setCustomId('image_input')
|
||||
.setLabel('Image URL')
|
||||
.setStyle(TextInputStyle.Short)
|
||||
.setValue(messageData.image || '')
|
||||
.setPlaceholder('https://example.com/image.png')
|
||||
)
|
||||
);
|
||||
|
||||
await i.showModal(imageModal);
|
||||
break;
|
||||
|
||||
case 'edit_thumbnail':
|
||||
const thumbnailModal = new ModalBuilder()
|
||||
.setCustomId('thumbnail_modal')
|
||||
.setTitle('Add Thumbnail');
|
||||
|
||||
thumbnailModal.addComponents(
|
||||
new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new TextInputBuilder()
|
||||
.setCustomId('thumbnail_input')
|
||||
.setLabel('Thumbnail URL')
|
||||
.setStyle(TextInputStyle.Short)
|
||||
.setValue(messageData.thumbnail || '')
|
||||
.setPlaceholder('https://example.com/thumbnail.png')
|
||||
)
|
||||
);
|
||||
|
||||
await i.showModal(thumbnailModal);
|
||||
break;
|
||||
|
||||
case 'toggle_timestamp':
|
||||
messageData.timestamp = !messageData.timestamp;
|
||||
await updatePreview(i);
|
||||
break;
|
||||
|
||||
case 'send_message':
|
||||
try {
|
||||
if (messageData.type === 'embed') {
|
||||
await channel.send({ embeds: [createEmbed()] });
|
||||
} else {
|
||||
await channel.send(messageData.description);
|
||||
}
|
||||
await i.update({
|
||||
content: 'Message sent successfully!',
|
||||
components: [],
|
||||
embeds: []
|
||||
});
|
||||
collector.stop();
|
||||
} catch (error) {
|
||||
await i.reply({
|
||||
content: 'Failed to send message. Please check channel permissions.',
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// Handle modal submissions
|
||||
interaction.client.on('interactionCreate', async (modal) => {
|
||||
if (!modal.isModalSubmit()) return;
|
||||
if (modal.user.id !== interaction.user.id) return;
|
||||
|
||||
switch (modal.customId) {
|
||||
case 'title_modal':
|
||||
messageData.title = modal.fields.getTextInputValue('title_input');
|
||||
break;
|
||||
case 'desc_modal':
|
||||
messageData.description = modal.fields.getTextInputValue('desc_input');
|
||||
break;
|
||||
case 'color_modal':
|
||||
messageData.color = parseColor(modal.fields.getTextInputValue('color_input'));
|
||||
break;
|
||||
case 'field_modal':
|
||||
const inlineValue = modal.fields.getTextInputValue('field_inline').toLowerCase();
|
||||
messageData.fields.push({
|
||||
name: modal.fields.getTextInputValue('field_name'),
|
||||
value: modal.fields.getTextInputValue('field_value'),
|
||||
inline: inlineValue === 'true'
|
||||
});
|
||||
break;
|
||||
case 'footer_modal':
|
||||
messageData.footer = modal.fields.getTextInputValue('footer_input');
|
||||
break;
|
||||
case 'image_modal':
|
||||
messageData.image = modal.fields.getTextInputValue('image_input');
|
||||
break;
|
||||
case 'thumbnail_modal':
|
||||
messageData.thumbnail = modal.fields.getTextInputValue('thumbnail_input');
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
await updatePreview(modal);
|
||||
});
|
||||
|
||||
collector.on('end', () => {
|
||||
interaction.editReply({
|
||||
content: 'Message editor timed out.',
|
||||
components: [],
|
||||
embeds: []
|
||||
}).catch(() => {});
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,261 @@
|
||||
const fs = require("fs");
|
||||
const { ApplicationCommandOptionType, ApplicationCommandType, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, ComponentType, MessageFlags } = 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");
|
||||
|
||||
// Function to chunk array into smaller arrays
|
||||
function chunkArray(arr, size) {
|
||||
const chunks = [];
|
||||
for (let i = 0; i < arr.length; i += size) {
|
||||
chunks.push(arr.slice(i, i + size));
|
||||
}
|
||||
return chunks;
|
||||
}
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.Mod.Command,
|
||||
description: cmdconfig.Mod.Command,
|
||||
type: ApplicationCommandType.ChatInput,
|
||||
options: [
|
||||
{
|
||||
name: cmdconfig.Mod.TicketBlacklist.Command,
|
||||
description: cmdconfig.Mod.TicketBlacklist.Description,
|
||||
type: ApplicationCommandOptionType.SubcommandGroup,
|
||||
options: [
|
||||
{
|
||||
name: cmdconfig.Mod.TicketBlacklist.Add.Command,
|
||||
description: msgconfig.Mod.TicketBlacklist.Add.Description || "Add a user to the ticket blacklist",
|
||||
type: ApplicationCommandOptionType.Subcommand,
|
||||
options: [
|
||||
{
|
||||
name: 'user',
|
||||
description: msgconfig.Mod.TicketBlacklist.Add.UserDescription || "User to blacklist",
|
||||
type: ApplicationCommandOptionType.User,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'reason',
|
||||
description: msgconfig.Mod.TicketBlacklist.Add.ReasonDescription || "Reason for blacklisting",
|
||||
type: ApplicationCommandOptionType.String,
|
||||
required: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: cmdconfig.Mod.TicketBlacklist.Remove.Command,
|
||||
description: msgconfig.Mod.TicketBlacklist.Remove.Description || "Remove a user from the ticket blacklist",
|
||||
type: ApplicationCommandOptionType.Subcommand,
|
||||
options: [
|
||||
{
|
||||
name: 'user',
|
||||
description: msgconfig.Mod.TicketBlacklist.Remove.UserDescription || "User to remove from blacklist",
|
||||
type: ApplicationCommandOptionType.User,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'reason',
|
||||
description: msgconfig.Mod.TicketBlacklist.Remove.ReasonDescription || "Reason for removing from blacklist",
|
||||
type: ApplicationCommandOptionType.String,
|
||||
required: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "view",
|
||||
description: msgconfig.Mod.TicketBlacklist.View.Description || "View all blacklisted users",
|
||||
type: ApplicationCommandOptionType.Subcommand,
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
permissions: cmdconfig.Mod.Permission,
|
||||
|
||||
async run(interaction) {
|
||||
const { getRole } = interaction.client;
|
||||
|
||||
let SupportStaff = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild);
|
||||
let Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild);
|
||||
let Moderator = await getRole(supportbot.Roles.StaffMember.Moderator, interaction.guild);
|
||||
|
||||
if (!SupportStaff || !Admin || !Moderator) {
|
||||
const missingRolesEmbed = new EmbedBuilder()
|
||||
.setDescription(msgconfig.Error.InvalidChannel || "Required roles are missing!")
|
||||
.setColor(supportbot.Embed.Colours.Warn);
|
||||
return interaction.reply({ embeds: [missingRolesEmbed] });
|
||||
}
|
||||
|
||||
const NoPerms = new EmbedBuilder()
|
||||
.setDescription(msgconfig.Error.IncorrectPerms || "You do not have the correct permissions!")
|
||||
.setColor(supportbot.Embed.Colours.Warn);
|
||||
|
||||
if (!interaction.member.roles.cache.has(Admin.id) && !interaction.member.roles.cache.has(Moderator.id) && (!supportbot.Mod.AllowSupportStaff || !interaction.member.roles.cache.has(SupportStaff.id))) {
|
||||
return interaction.reply({ embeds: [NoPerms] });
|
||||
}
|
||||
|
||||
const subcommand = interaction.options.getSubcommand(false);
|
||||
const user = interaction.options.getUser('user');
|
||||
const reason = interaction.options.getString('reason') || "No reason provided";
|
||||
|
||||
try {
|
||||
let blacklistedUsers;
|
||||
try {
|
||||
blacklistedUsers = JSON.parse(fs.readFileSync("./Data/BlacklistedUsers.json", "utf8")).blacklistedUsers;
|
||||
if (!Array.isArray(blacklistedUsers)) {
|
||||
blacklistedUsers = [];
|
||||
}
|
||||
} catch (error) {
|
||||
blacklistedUsers = [];
|
||||
}
|
||||
|
||||
if (subcommand === cmdconfig.Mod.TicketBlacklist.Add.Command) {
|
||||
if (blacklistedUsers.includes(user.id)) {
|
||||
const alreadyBlacklistedEmbed = new EmbedBuilder()
|
||||
.setDescription(msgconfig.Mod.TicketBlacklist.Add.AlreadyBlacklisted.replace("{userTag}", user.tag) || ":x: User is already blacklisted.")
|
||||
.setColor(supportbot.Embed.Colours.Warn);
|
||||
return interaction.reply({ embeds: [alreadyBlacklistedEmbed], flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
|
||||
blacklistedUsers.push(user.id);
|
||||
fs.writeFileSync("./Data/BlacklistedUsers.json", JSON.stringify({ "blacklistedUsers": blacklistedUsers }, null, 4));
|
||||
|
||||
const successEmbed = new EmbedBuilder()
|
||||
.setDescription(msgconfig.Mod.TicketBlacklist.Add.Success.replace("{userTag}", user.tag) || ":white_check_mark: User has been blacklisted.")
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
|
||||
const blacklistChannel = interaction.guild.channels.cache.get(supportbot.Ticket.Log.TicketBlacklistLog);
|
||||
if (blacklistChannel) {
|
||||
const blacklistLogEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.TicketBlacklistLog.Title || "Ticket Blacklist Log")
|
||||
.setColor(msgconfig.TicketBlacklistLog.Colour || supportbot.Embed.Colours.Success)
|
||||
.setFooter({ text: supportbot.Embed.Footer, iconURL: interaction.user.displayAvatarURL() })
|
||||
.setDescription(`> **User:** ${user.tag} (\`${user.id}\`)\n> **Actioned by:** <@${interaction.user.id}>`)
|
||||
.addFields({ name: "Action", value: "\`\`\`Removed from blacklist\`\`\`", inline: false })
|
||||
.addFields({ name: "Reason", value: `\`\`\`${reason}\`\`\``, inline: false });
|
||||
|
||||
blacklistChannel.send({ embeds: [blacklistLogEmbed] });
|
||||
}
|
||||
|
||||
return interaction.reply({
|
||||
embeds: [successEmbed],
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
|
||||
} else if (subcommand === cmdconfig.Mod.TicketBlacklist.Remove.Command) {
|
||||
if (!blacklistedUsers.includes(user.id)) {
|
||||
const notBlacklistedEmbed = new EmbedBuilder()
|
||||
.setDescription(msgconfig.Mod.TicketBlacklist.Remove.NotBlacklisted.replace("{userTag}", user.tag) || ":x: User is not blacklisted.")
|
||||
.setColor(supportbot.Embed.Colours.Warn);
|
||||
return interaction.reply({ embeds: [notBlacklistedEmbed], flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
|
||||
blacklistedUsers = blacklistedUsers.filter(id => id !== user.id);
|
||||
fs.writeFileSync("./Data/BlacklistedUsers.json", JSON.stringify({ "blacklistedUsers": blacklistedUsers }, null, 4));
|
||||
|
||||
const removedEmbed = new EmbedBuilder()
|
||||
.setDescription(msgconfig.Mod.TicketBlacklist.Remove.Success.replace("{userTag}", user.tag) || ":white_check_mark: User has been removed from the blacklist.")
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
|
||||
const blacklistChannel = interaction.guild.channels.cache.get(supportbot.Ticket.Log.TicketBlacklistLog);
|
||||
if (blacklistChannel) {
|
||||
const blacklistLogEmbed = new EmbedBuilder()
|
||||
.setTitle(msgconfig.TicketBlacklistLog.Title || "Ticket Blacklist Log")
|
||||
.setColor(msgconfig.TicketBlacklistLog.Colour || supportbot.Embed.Colours.Success)
|
||||
.setFooter({ text: supportbot.Embed.Footer, iconURL: interaction.user.displayAvatarURL() })
|
||||
.setDescription(`> **User:** ${user.tag} (\`${user.id}\`)\n> **Actioned by:** <@${interaction.user.id}>`)
|
||||
.addFields({ name: "Action", value: "\`\`\`Removed from blacklist\`\`\`", inline: false })
|
||||
.addFields({ name: "Reason", value: `\`\`\`${reason}\`\`\``, inline: false });
|
||||
|
||||
blacklistChannel.send({ embeds: [blacklistLogEmbed] });
|
||||
}
|
||||
|
||||
return interaction.reply({ embeds: [removedEmbed], flags: MessageFlags.Ephemeral });
|
||||
|
||||
} else if (subcommand === "view") {
|
||||
if (blacklistedUsers.length === 0) {
|
||||
const noBlacklistedUsersEmbed = new EmbedBuilder()
|
||||
.setDescription(msgconfig.Mod.TicketBlacklist.View.NoBlacklistedUsers || "No users are blacklisted.")
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
return interaction.reply({ embeds: [noBlacklistedUsersEmbed], flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
|
||||
const chunkedUsers = chunkArray(blacklistedUsers, 5);
|
||||
let currentPage = 0;
|
||||
|
||||
const createEmbed = (page) => {
|
||||
return new EmbedBuilder()
|
||||
.setTitle(msgconfig.Mod.TicketBlacklist.View.EmbedTitle || "Blacklisted Users")
|
||||
.setDescription(chunkedUsers[page].map(id => `<@${id}>`).join("\n"))
|
||||
.setColor(supportbot.Embed.Colours.Success)
|
||||
.setFooter({ text: `Page ${page + 1} of ${chunkedUsers.length}` });
|
||||
};
|
||||
|
||||
const buttons = new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('previous')
|
||||
.setLabel('Previous')
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
.setDisabled(currentPage === 0),
|
||||
new ButtonBuilder()
|
||||
.setCustomId('next')
|
||||
.setLabel('Next')
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
.setDisabled(currentPage === chunkedUsers.length - 1)
|
||||
);
|
||||
|
||||
const response = await interaction.reply({
|
||||
embeds: [createEmbed(currentPage)],
|
||||
components: chunkedUsers.length > 1 ? [buttons] : [],
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
|
||||
if (chunkedUsers.length <= 1) return;
|
||||
|
||||
const collector = response.createMessageComponentCollector({
|
||||
componentType: ComponentType.Button,
|
||||
time: 60000
|
||||
});
|
||||
|
||||
collector.on('collect', async (i) => {
|
||||
if (i.user.id !== interaction.user.id) {
|
||||
return i.reply({
|
||||
content: 'You cannot use these buttons.',
|
||||
flags: MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
if (i.customId === 'previous' && currentPage > 0) {
|
||||
currentPage--;
|
||||
} else if (i.customId === 'next' && currentPage < chunkedUsers.length - 1) {
|
||||
currentPage++;
|
||||
}
|
||||
|
||||
buttons.components[0].setDisabled(currentPage === 0);
|
||||
buttons.components[1].setDisabled(currentPage === chunkedUsers.length - 1);
|
||||
|
||||
await i.update({
|
||||
embeds: [createEmbed(currentPage)],
|
||||
components: [buttons]
|
||||
});
|
||||
});
|
||||
|
||||
collector.on('end', () => {
|
||||
buttons.components.forEach(button => button.setDisabled(true));
|
||||
interaction.editReply({
|
||||
components: [buttons]
|
||||
}).catch(() => {});
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error in mod command:", error);
|
||||
const errorEmbed = new EmbedBuilder()
|
||||
.setDescription(msgconfig.Error.ActionFailed || "An error occurred while processing your request.")
|
||||
.setColor(supportbot.Embed.Colours.Warn);
|
||||
interaction.reply({ embeds: [errorEmbed], flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,237 @@
|
||||
const { Command } = require('../../Structures/Addon.js');
|
||||
const Discord = require("discord.js");
|
||||
const fs = require("fs");
|
||||
const yaml = require("js-yaml");
|
||||
const db = require("../../Structures/Database.js");
|
||||
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const cmdconfig = yaml.load(fs.readFileSync("./Configs/commands.yml", "utf8"));
|
||||
|
||||
async function loadSettings() {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.get("SELECT data FROM settings WHERE id = 1", (err, row) => {
|
||||
if (err) return reject(err);
|
||||
if (!row) return resolve({});
|
||||
try {
|
||||
resolve(JSON.parse(row.data));
|
||||
} catch (e) {
|
||||
resolve({});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function saveSettings(settings) {
|
||||
const data = JSON.stringify(settings);
|
||||
return new Promise((resolve, reject) => {
|
||||
db.run(
|
||||
"INSERT INTO settings (id, data) VALUES (1, ?) ON CONFLICT(id) DO UPDATE SET data = excluded.data",
|
||||
[data],
|
||||
(err) => (err ? reject(err) : resolve())
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.Settings.Command,
|
||||
description: cmdconfig.Settings.Description,
|
||||
permissions: cmdconfig.Settings.Permission,
|
||||
options: [
|
||||
{
|
||||
name: 'setstatus',
|
||||
type: Discord.ApplicationCommandOptionType.Subcommand,
|
||||
description: 'Set the bot status',
|
||||
options: [
|
||||
{
|
||||
name: 'status',
|
||||
type: Discord.ApplicationCommandOptionType.String,
|
||||
description: 'Choose the bot status',
|
||||
required: true,
|
||||
choices: [
|
||||
{ name: 'Online 🟢', value: 'online' },
|
||||
{ name: 'Idle 🌙', value: 'idle' },
|
||||
{ name: 'Do Not Disturb ⛔', value: 'dnd' },
|
||||
{ name: 'Invisible 👻', value: 'invisible' }
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'setactivity',
|
||||
type: Discord.ApplicationCommandOptionType.Subcommand,
|
||||
description: 'Set the bot activity',
|
||||
options: [
|
||||
{
|
||||
name: 'type',
|
||||
type: Discord.ApplicationCommandOptionType.String,
|
||||
description: 'Choose the activity type',
|
||||
required: true,
|
||||
choices: [
|
||||
{ name: 'Playing 🎮', value: 'PLAYING' },
|
||||
{ name: 'Watching 👀', value: 'WATCHING' },
|
||||
{ name: 'Listening 🎧', value: 'LISTENING' },
|
||||
{ name: 'Competing 🏆', value: 'COMPETING' }
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'message',
|
||||
type: Discord.ApplicationCommandOptionType.String,
|
||||
description: 'The activity message',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'streammode',
|
||||
type: Discord.ApplicationCommandOptionType.Subcommand,
|
||||
description: 'Set the bot streaming status',
|
||||
options: [
|
||||
{
|
||||
name: 'toggle',
|
||||
type: Discord.ApplicationCommandOptionType.String,
|
||||
description: 'Turn streaming mode on or off',
|
||||
required: true,
|
||||
choices: [
|
||||
{ name: 'On', value: 'on' },
|
||||
{ name: 'Off', value: 'off' }
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'twitchurl',
|
||||
type: Discord.ApplicationCommandOptionType.String,
|
||||
description: 'Twitch stream URL (Required if turning on)',
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'setnickname',
|
||||
type: Discord.ApplicationCommandOptionType.Subcommand,
|
||||
description: 'Set the bot nickname',
|
||||
options: [
|
||||
{
|
||||
name: 'nickname',
|
||||
type: Discord.ApplicationCommandOptionType.String,
|
||||
description: 'The new nickname for the bot',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
async run(interaction) {
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
const settings = await loadSettings();
|
||||
|
||||
if (subcommand === 'setstatus') {
|
||||
const status = interaction.options.getString('status');
|
||||
|
||||
try {
|
||||
interaction.client.user.setPresence({
|
||||
status: status,
|
||||
activities: interaction.client.user.presence.activities || [],
|
||||
});
|
||||
|
||||
settings.status = status;
|
||||
await saveSettings(settings);
|
||||
|
||||
const embed = new Discord.EmbedBuilder()
|
||||
.setDescription(`Status set to **${status.charAt(0).toUpperCase() + status.slice(1)}**`)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
return interaction.reply({ embeds: [embed], flags: Discord.MessageFlags.Ephemeral });
|
||||
} catch (error) {
|
||||
return interaction.reply({ content: `Failed to set status: ${error.message}`, flags: Discord.MessageFlags.Ephemeral });
|
||||
}
|
||||
}
|
||||
|
||||
if (subcommand === 'setactivity') {
|
||||
const activityType = interaction.options.getString('type');
|
||||
const activityMessage = interaction.options.getString('message');
|
||||
|
||||
try {
|
||||
interaction.client.user.setPresence({
|
||||
activities: [{ name: activityMessage, type: Discord.ActivityType[activityType] }],
|
||||
status: interaction.client.user.presence.status || 'online',
|
||||
});
|
||||
|
||||
settings.activity = { type: activityType, message: activityMessage };
|
||||
await saveSettings(settings);
|
||||
|
||||
const embed = new Discord.EmbedBuilder()
|
||||
.setDescription(`Activity set to **${activityType.toLowerCase()} ${activityMessage}**`)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
return interaction.reply({ embeds: [embed], flags: Discord.MessageFlags.Ephemeral });
|
||||
} catch (error) {
|
||||
return interaction.reply({ content: `Failed to set activity: ${error.message}`, flags: Discord.MessageFlags.Ephemeral });
|
||||
}
|
||||
} else if (subcommand === 'streammode') {
|
||||
const toggle = interaction.options.getString('toggle');
|
||||
const twitchUrl = interaction.options.getString('twitchurl');
|
||||
|
||||
if (toggle === 'on') {
|
||||
if (!twitchUrl) {
|
||||
return interaction.reply({ content: 'Please provide a Twitch URL to stream.', flags: Discord.MessageFlags.Ephemeral });
|
||||
}
|
||||
|
||||
try {
|
||||
interaction.client.user.setPresence({
|
||||
activities: [{ name: 'Streaming', type: Discord.ActivityType.Streaming, url: twitchUrl }],
|
||||
status: 'online',
|
||||
});
|
||||
|
||||
settings.streaming = { active: true, url: twitchUrl };
|
||||
await saveSettings(settings);
|
||||
|
||||
const embed = new Discord.EmbedBuilder()
|
||||
.setDescription(`**Streaming mode enabled!**\nStreaming: [Twitch](${twitchUrl})`)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
return interaction.reply({ embeds: [embed], flags: Discord.MessageFlags.Ephemeral });
|
||||
} catch (error) {
|
||||
return interaction.reply({ content: `Failed to enable streaming mode: ${error.message}`, flags: Discord.MessageFlags.Ephemeral });
|
||||
}
|
||||
} else if (toggle === 'off') {
|
||||
try {
|
||||
interaction.client.user.setPresence({
|
||||
activities: interaction.client.user.presence.activities.filter(a => a.type !== Discord.ActivityType.Streaming),
|
||||
status: settings.status || 'online',
|
||||
});
|
||||
|
||||
settings.streaming = { active: false };
|
||||
await saveSettings(settings);
|
||||
|
||||
const embed = new Discord.EmbedBuilder()
|
||||
.setDescription(`**Streaming mode disabled!**`)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
return interaction.reply({ embeds: [embed], flags: Discord.MessageFlags.Ephemeral });
|
||||
} catch (error) {
|
||||
return interaction.reply({ content: `Failed to disable streaming mode: ${error.message}`, flags: Discord.MessageFlags.Ephemeral });
|
||||
}
|
||||
}
|
||||
} else if (subcommand === 'setnickname') {
|
||||
const nickname = interaction.options.getString('nickname');
|
||||
|
||||
try {
|
||||
const guild = interaction.guild;
|
||||
const botMember = guild.members.cache.get(interaction.client.user.id);
|
||||
await botMember.setNickname(nickname);
|
||||
|
||||
settings.nickname = nickname;
|
||||
await saveSettings(settings);
|
||||
|
||||
const embed = new Discord.EmbedBuilder()
|
||||
.setDescription(`Bot nickname set to **${nickname}**`)
|
||||
.setColor(supportbot.Embed.Colours.General);
|
||||
|
||||
return interaction.reply({ embeds: [embed], flags: Discord.MessageFlags.Ephemeral });
|
||||
} catch (error) {
|
||||
return interaction.reply({ content: `Failed to set nickname: ${error.message}`, flags: Discord.MessageFlags.Ephemeral });
|
||||
}
|
||||
} else {
|
||||
return interaction.reply({ content: 'Invalid command usage.', flags: Discord.MessageFlags.Ephemeral });
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -1,174 +0,0 @@
|
||||
// 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('./Data/supportbot.yml', 'utf8'));
|
||||
const cmdconfig = yaml.load(fs.readFileSync('./Data/commands.yml', 'utf8'));
|
||||
|
||||
const Command = require("../Structures/Command.js");
|
||||
const TicketNumberID = require('../Structures/TicketID.js');
|
||||
|
||||
async function asyncForEach(array, callback) {
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
await callback(array[index], index, array);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.CloseTicket,
|
||||
description: cmdconfig.CloseTicketDesc,
|
||||
type: "BOTH",
|
||||
slashCommandOptions: [],
|
||||
permission: "SEND_MESSAGES",
|
||||
|
||||
async run(message, args, client) {
|
||||
|
||||
if (!message.channel.name.startsWith( `${supportbot.TicketChannel}-` )) {
|
||||
const Exists = new Discord.MessageEmbed()
|
||||
.setTitle("No Ticket Found!")
|
||||
.setDescription(`${supportbot.NoValidTicket}`)
|
||||
.setColor(supportbot.WarningColour);
|
||||
message.channel.send({ embed: Exists });
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
message.channel.send(CloseTicketRequest).then(m => {
|
||||
message.channel.awaitMessages( response => response.content.toLowerCase() === supportbot.ClosingConfirmation_Word, {
|
||||
max: 1,
|
||||
time: 20000,
|
||||
errors: [ 'time' ],
|
||||
|
||||
|
||||
}).then( collected => {
|
||||
let logChannel = message.guild.channels.cache.find(channel => channel.name === supportbot.TranscriptLog) || message.guild.channels.cache.find(channel => channel.id === supportbot.TranscriptLog)
|
||||
let user = message.author;
|
||||
let reason = args.join(" ") || "No Reason Provided.";
|
||||
|
||||
let name = message.channel.name;
|
||||
let ticketChannel = message.channel;
|
||||
|
||||
message.channel.send(`**${supportbot.ClosingTicket}**`)
|
||||
|
||||
.then(() => {
|
||||
const logEmbed = new Discord.MessageEmbed()
|
||||
.setTitle(supportbot.TranscriptTitle)
|
||||
.setColor(supportbot.EmbedColour)
|
||||
.setFooter(supportbot.EmbedFooter)
|
||||
.addField("Closed By", message.author.tag)
|
||||
.addField("Reason", reason);
|
||||
|
||||
message.channel.messages.fetch({ limit: 100 }).then( msgs => {
|
||||
let html = '';
|
||||
|
||||
msgs = msgs.sort( ( a, b ) => a.createdTimestamp - b.createdTimestamp );
|
||||
html += `<strong>Server Name:</strong> ${message.guild.name}<br>`;
|
||||
html += `<strong>Ticket:</strong> ${ticketChannel.name}<br>`;
|
||||
html += `<strong>Message:</strong> ${msgs.size} Messages<br><br><br>`;
|
||||
|
||||
msgs.forEach( msg => {
|
||||
if ( msg.content ) {
|
||||
html += `<strong>User:</strong> ${msg.author.tag}<br>`;
|
||||
html += `<strong>Message:</strong> ${msg.content}<br>`;
|
||||
html += `-----<br><br>`;
|
||||
}
|
||||
});
|
||||
|
||||
logChannel.send({embeds: [logEmbed]}).catch(err => {
|
||||
message.reply(err)
|
||||
})
|
||||
|
||||
|
||||
let file = new Discord.MessageAttachment( Buffer.from( html ), `${name}.html` )
|
||||
logChannel.send({embeds: [logEmbed], files: [file]}).catch(err => {
|
||||
message.reply(err)
|
||||
})
|
||||
|
||||
message.channel.delete().catch(error => {
|
||||
if (error.code !== Discord.Constants.APIErrors.UNKNOWN_CHANNEL) {
|
||||
console.error('Failed to delete the message:', error);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
}).catch( () => {
|
||||
interaction.m.edit({content: 'The request to close the ticket has timed out.'}).then(m2 => setTimeout(() => channel.delete(3000)) );
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
if (!supportbot.CloseConfirmation) {
|
||||
|
||||
let logChannel = message.guild.channels.cache.find(channel => channel.name === supportbot.TranscriptLog) || message.guild.channels.cache.find(channel => channel.id === supportbot.TranscriptLog)
|
||||
let user = message.author;
|
||||
let reason = args.join(" ") || "No Reason Provided.";
|
||||
|
||||
let name = message.channel.name;
|
||||
let ticketChannel = message.channel;
|
||||
|
||||
message.channel.send(`**${supportbot.ClosingTicket}**`)
|
||||
|
||||
.then(() => {
|
||||
const logEmbed = new Discord.MessageEmbed()
|
||||
.setTitle(supportbot.TranscriptTitle)
|
||||
.setColor(supportbot.EmbedColour)
|
||||
.setFooter(supportbot.EmbedFooter)
|
||||
.addField("Closed By", message.author.tag)
|
||||
.addField("Reason", reason);
|
||||
|
||||
message.channel.messages.fetch({ limit: 100 }).then( msgs => {
|
||||
let html = '';
|
||||
|
||||
msgs = msgs.sort( ( a, b ) => a.createdTimestamp - b.createdTimestamp );
|
||||
html += `<strong>Server Name:</strong> ${message.guild.name}<br>`;
|
||||
html += `<strong>Ticket:</strong> ${ticketChannel.name}<br>`;
|
||||
html += `<strong>Message:</strong> ${msgs.size} Messages<br><br><br>`;
|
||||
|
||||
msgs.forEach( msg => {
|
||||
if ( msg.content ) {
|
||||
html += `<strong>User:</strong> ${msg.author.tag}<br>`;
|
||||
html += `<strong>Message:</strong> ${msg.content}<br>`;
|
||||
html += `-----<br><br>`;
|
||||
}
|
||||
});
|
||||
|
||||
logChannel.send({embeds: [logEmbed]}).catch(err => {
|
||||
message.reply(err)
|
||||
})
|
||||
|
||||
let file = new Discord.MessageAttachment( Buffer.from( html ), `${name}.html` )
|
||||
logChannel.send({embeds: [logEmbed], files: [file]}).catch(err => {
|
||||
message.reply(err)
|
||||
})
|
||||
|
||||
message.channel.delete().catch(error => {
|
||||
if (error.code !== Discord.Constants.APIErrors.UNKNOWN_CHANNEL) {
|
||||
console.error('Failed to delete the message:', error);
|
||||
}
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
}).catch( () => {
|
||||
interaction.m.edit({content: 'The request to close the ticket has timed out.'}).then(m2 => setTimeout(() => channel.delete(3000)) );
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
@@ -1,86 +0,0 @@
|
||||
// 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('./Data/supportbot.yml', 'utf8'));
|
||||
const cmdconfig = yaml.load(fs.readFileSync('./Data/commands.yml', 'utf8'));
|
||||
|
||||
const Command = require("../Structures/Command.js");
|
||||
const TicketNumberID = require('../Structures/TicketID.js');
|
||||
|
||||
async function asyncForEach(array, callback) {
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
await callback(array[index], index, array);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.HelpCommand,
|
||||
description: cmdconfig.HelpCommandDesc,
|
||||
type: "BOTH",
|
||||
slashCommandOptions: [],
|
||||
permission: "SEND_MESSAGES",
|
||||
|
||||
async run(message, args, client) {
|
||||
|
||||
let SupportStaff = message.guild.roles.cache.find(adminRole => adminRole.name === supportbot.Staff) || message.guild.roles.cache.find(staffRole => staffRole.id === supportbot.Staff);
|
||||
let AdminStaff = message.guild.roles.cache.find(adminRole => adminRole.name === supportbot.Admin) || message.guild.roles.cache.find(adminRole => adminRole.id === supportbot.Admin);
|
||||
|
||||
if (supportbot.HelpMenu) {
|
||||
let botCommands = "";
|
||||
botCommands += `**${supportbot.Prefix}${cmdconfig.HelpCommand}** ${cmdconfig.HelpCommandDesc}\n`;
|
||||
botCommands += `**${supportbot.Prefix}${cmdconfig.InfoCommand}** ${cmdconfig.InfoCommandDesc}\n`;
|
||||
botCommands += `**${supportbot.Prefix}${cmdconfig.PingCommand}** ${cmdconfig.PingCommandDesc}\n`
|
||||
|
||||
let ticketCommands = "";
|
||||
ticketCommands += `**${supportbot.Prefix}${cmdconfig.OpenTicket}** ${cmdconfig.OpenTicketDesc}\n`;
|
||||
ticketCommands += `**${supportbot.Prefix}${cmdconfig.CloseTicket}** ${cmdconfig.CloseTicketDesc}\n`
|
||||
|
||||
let staffCommands = "";
|
||||
staffCommands += `**${supportbot.Prefix}${cmdconfig.TicketAdd}** ${cmdconfig.TicketAddDesc}\n`
|
||||
staffCommands += `**${supportbot.Prefix}${cmdconfig.TicketRemove}** ${cmdconfig.TicketRemoveDesc}\n`
|
||||
|
||||
const HelpEmbed1 = new Discord.MessageEmbed()
|
||||
.setTitle(supportbot.Name)
|
||||
.setThumbnail(message.author.displayAvatarURL())
|
||||
|
||||
.addFields(
|
||||
{ name: '🖥️ General Commands\n', value: `${botCommands}\n`, inline: false },
|
||||
{ name: '🎫 Support Commands\n', value: `${ticketCommands}\n`, inline: false },
|
||||
)
|
||||
|
||||
.setColor(supportbot.EmbedColour)
|
||||
.setFooter(supportbot.EmbedFooter, message.author.displayAvatarURL());
|
||||
|
||||
if (message.member.roles.cache.has(SupportStaff.id)) {
|
||||
HelpEmbed1.addFields(
|
||||
{ name: '🔐 Staff Commands\n', value: `${staffCommands}\n`, inline: false },
|
||||
)
|
||||
} else {
|
||||
if ((message.member.roles.cache.has(AdminStaff.id))) {
|
||||
HelpEmbed1.addFields(
|
||||
{ name: '🔐 Staff Commands\n', value: `${staffCommands}\n`, inline: false },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (supportbot.SendHelpPage === "dm") {
|
||||
message.author.send({
|
||||
embeds: [HelpEmbed1]
|
||||
});
|
||||
}
|
||||
|
||||
if (supportbot.SendHelpPage === "channel") {
|
||||
message.channel.send({
|
||||
embeds: [HelpEmbed1]
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
@@ -1,42 +0,0 @@
|
||||
// SupportBot | Emerald Services
|
||||
// Info Command
|
||||
|
||||
const fs = require("fs");
|
||||
|
||||
const Discord = require("discord.js");
|
||||
const yaml = require('js-yaml');
|
||||
const supportbot = yaml.load(fs.readFileSync('./Data/supportbot.yml', 'utf8'));
|
||||
const cmdconfig = yaml.load(fs.readFileSync('./Data/commands.yml', 'utf8'));
|
||||
|
||||
const Command = require("../Structures/Command.js");
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.InfoCommand,
|
||||
description: cmdconfig.InfoCommandDesc,
|
||||
type: "BOTH",
|
||||
slashCommandOptions: [],
|
||||
permission: "SEND_MESSAGES",
|
||||
|
||||
async run(message, args, client) {
|
||||
|
||||
const InfoButton = new Discord.MessageButton()
|
||||
.setLabel(supportbot.InfoButtonText)
|
||||
.setURL(supportbot.InfoURL)
|
||||
.setStyle('LINK')
|
||||
|
||||
const inforow = new Discord.MessageActionRow()
|
||||
.addComponents(InfoButton)
|
||||
|
||||
const InfoEmbed = new Discord.MessageEmbed()
|
||||
.setURL(supportbot.InfoURL)
|
||||
.setTitle(supportbot.InfoTitle)
|
||||
.setDescription(supportbot.AboutMe)
|
||||
.setColor(supportbot.InfoColour)
|
||||
|
||||
|
||||
message.channel.send({
|
||||
embeds: [InfoEmbed], components: [inforow]
|
||||
})
|
||||
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,157 @@
|
||||
# ___ _ ___ _
|
||||
# / __> _ _ ___ ___ ___ _ _ _| |_ | . > ___ _| |_
|
||||
# \__ \| | || . \| . \/ . \| '_> | | | . \/ . \ | |
|
||||
# <___/`___|| _/| _/\___/|_| |_| |___/\___/ |_|
|
||||
# |_| |_|
|
||||
#
|
||||
# SupportBot created by Emerald Services
|
||||
# Installed with MIT License
|
||||
#
|
||||
# Discord Support: https://discord.gg/emeraldsrv
|
||||
# Official Documentation: https://docs.emeraldsrv.com
|
||||
# Addons Marketplace: https://community.emeraldsrv.com
|
||||
#
|
||||
# Commands Configuration (commands.yml)
|
||||
|
||||
Help:
|
||||
Command: "help"
|
||||
Description: "Get a list of all the commands."
|
||||
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
|
||||
|
||||
Profile:
|
||||
Command: "profile"
|
||||
Description: "Shows the profile for a discord member."
|
||||
Permission: ["SendMessages"]
|
||||
# Command is automatically disabled when the TicketClaims is disabled.
|
||||
|
||||
OpenTicket:
|
||||
Command: "ticket"
|
||||
Description: "Create a support ticket."
|
||||
Permission: ["SendMessages"]
|
||||
Enabled: true
|
||||
|
||||
AddUser:
|
||||
Command: "add"
|
||||
Description: "Add a user to a support ticket."
|
||||
Permission: ["SendMessages"]
|
||||
Enabled: true
|
||||
|
||||
ForceAddUser:
|
||||
Command: "forceadd"
|
||||
Description: "Add a user to a support ticket by bypassing the invite system."
|
||||
Permission: ["SendMessages"]
|
||||
Enabled: true
|
||||
|
||||
RemoveUser:
|
||||
Command: "remove"
|
||||
Description: "Remove a user from a support ticket."
|
||||
Permission: ["SendMessages"]
|
||||
Enabled: true
|
||||
|
||||
CloseTicket:
|
||||
Command: "close"
|
||||
Description: "Close a support ticket"
|
||||
Permission: ["SendMessages"]
|
||||
Enabled: true
|
||||
|
||||
Suggestion:
|
||||
Command: "suggest"
|
||||
Description: "Create a suggestion."
|
||||
Permission: ["SendMessages"]
|
||||
Enabled: true
|
||||
|
||||
SuggestionAdmin:
|
||||
Command: "suggestadmin"
|
||||
Description: "Update a suggestion’s status."
|
||||
Permission: ['SendMessages']
|
||||
# Disable "Suggestion" to disable this command.
|
||||
|
||||
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
|
||||
|
||||
# ---------------------------------------
|
||||
# Bot Moderator Commands
|
||||
# ---------------------------------------
|
||||
|
||||
Mod:
|
||||
Command: "mod"
|
||||
Description: "Execute a moderation action."
|
||||
Permission: ["BanMembers", "KickMembers"]
|
||||
Enabled: true
|
||||
|
||||
TicketBlacklist:
|
||||
Command: "ticket-blacklist"
|
||||
Description: "Blacklist a user from creating Tickets."
|
||||
|
||||
Add:
|
||||
Command: "add"
|
||||
Description: "Add a user to the blacklist."
|
||||
|
||||
Remove:
|
||||
Command: "remove"
|
||||
Description: "Remove a user from the blacklist."
|
||||
|
||||
View:
|
||||
Command: "view"
|
||||
Description: "View all blacklisted users."
|
||||
|
||||
# ---------------------------------------
|
||||
# Bot Administrator Commands
|
||||
# ---------------------------------------
|
||||
|
||||
Settings:
|
||||
Command: "settings"
|
||||
Description: "Update bot settings."
|
||||
Permission: ["Administrator"]
|
||||
Enabled: true
|
||||
|
||||
# ---------------------------------------
|
||||
# Command Description Configuration
|
||||
# ---------------------------------------
|
||||
|
||||
BlacklistDesc: "Blacklist a user from creating Tickets."
|
||||
TicketAddDesc: "Add a user to a support ticket."
|
||||
TicketRemoveDesc: "Remove a user from a support ticket."
|
||||
TranslateCommandDesc: "Translates the language provided, along with the abbreviation (2 char) long language code"
|
||||
OpenTicketDesc: "Open a new support ticket."
|
||||
CloseTicketDesc: "Close your support ticket."
|
||||
InfoCommandDesc: "Get information about the brand."
|
||||
PingCommandDesc: "Pong!"
|
||||
HelpCommandDesc: "Get a list of all the commands."
|
||||
EmbedCommandDesc: "Send a message as an embed."
|
||||
SuggestCommandDesc: "Suggest a feature for the server."
|
||||
UserInfoCommandDesc: "Get info about a user."
|
||||
@@ -0,0 +1,323 @@
|
||||
# ___ _ ___ _
|
||||
# / __> _ _ ___ ___ ___ _ _ _| |_ | . > ___ _| |_
|
||||
# \__ \| | || . \| . \/ . \| '_> | | | . \/ . \ | |
|
||||
# <___/`___|| _/| _/\___/|_| |_| |___/\___/ |_|
|
||||
# |_| |_|
|
||||
#
|
||||
# SupportBot created by Emerald Services
|
||||
# Installed with MIT License
|
||||
#
|
||||
# Discord Support: https://discord.gg/emeraldsrv
|
||||
# Official Documentation: https://docs.emeraldsrv.com
|
||||
# Addons Marketplace: https://community.emeraldsrv.com
|
||||
#
|
||||
# Messages Configuration (messages.yml)
|
||||
|
||||
# ----------------------------
|
||||
# Info Command
|
||||
# ----------------------------
|
||||
|
||||
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://docs.emeraldsrv.com"
|
||||
Colour: "#45b463"
|
||||
|
||||
# ----------------------------
|
||||
# Welcome/Leave Messages
|
||||
# ----------------------------
|
||||
|
||||
Welcome:
|
||||
Enabled: true
|
||||
LayoutStyle: "modern"
|
||||
# Available Layout Styles
|
||||
# "modern" - For the new Discord v2 components
|
||||
# "classic" - Use the classic Discord look.
|
||||
|
||||
Embed:
|
||||
Colour: "#45b463"
|
||||
Title: "Welcome to ServerName"
|
||||
Message: ":smile: %joined_user% has just landed!\n\n🔨 **Server Rules:** <#1202667199853232129>"
|
||||
Icon:
|
||||
Mode: "USER" # USER | BOT | CUSTOM
|
||||
CustomURL: "https://example.com/custom.png"
|
||||
Image:
|
||||
Enabled: false
|
||||
URL: "https://example.com/banner.png"
|
||||
Buttons:
|
||||
Enabled: true
|
||||
List:
|
||||
- label: "BuiltByBit"
|
||||
style: "Link"
|
||||
url: "https://your-website.com/how-to-play-java"
|
||||
emoji: "⌨️"
|
||||
- label: "GitHub"
|
||||
style: "Link"
|
||||
url: "https://your-website.com/how-to-play-bedrock"
|
||||
emoji: "👾"
|
||||
- label: "Tebex"
|
||||
style: "Link"
|
||||
url: "https://your-store.tebex.io/"
|
||||
emoji: "🛒"
|
||||
|
||||
Modern:
|
||||
Colour: "#45b463"
|
||||
Title: "**Welcome to ServerName**"
|
||||
Body: ":smile: %joined_user% has just landed!\n\n🔨 **Server Rules:** <#1202667199853232129>"
|
||||
Icon:
|
||||
Mode: "BOT" # USER | BOT | CUSTOM
|
||||
CustomURL: "https://example.com/custom-icon.png"
|
||||
Image:
|
||||
Enabled: false
|
||||
URL: "https://example.com/banner.png"
|
||||
Buttons:
|
||||
- label: "BuiltByBit"
|
||||
style: "Link"
|
||||
url: "https://your-website.com/how-to-play-java"
|
||||
emoji: "⌨️"
|
||||
- label: "GitHub"
|
||||
style: "Link"
|
||||
url: "https://your-website.com/how-to-play-bedrock"
|
||||
emoji: "👾"
|
||||
- label: "Tebex"
|
||||
style: "Link"
|
||||
url: "https://your-store.tebex.io/"
|
||||
emoji: "🛒"
|
||||
|
||||
Leave:
|
||||
Embed:
|
||||
Colour: "#f74343"
|
||||
Title: "Goodbye!"
|
||||
Message: ":slight_frown: %left_user% has recently dropped out!"
|
||||
Thumbnail: "BOT"
|
||||
|
||||
ImageEnabled: false
|
||||
ImageURL: "https://animagelink.png/"
|
||||
|
||||
# ----------------------------
|
||||
# Suggestion Messages
|
||||
# ----------------------------
|
||||
|
||||
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 Messages
|
||||
# ----------------------------
|
||||
|
||||
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
|
||||
|
||||
TicketArchived: ":white_check_mark: Ticket archived."
|
||||
TicketUnarchived: ":white_check_mark: Ticket unarchived."
|
||||
TicketLocked: ":lock: Ticket is locked."
|
||||
DisableInvites: "🔒 Invites have been disabled for this ticket."
|
||||
EnableInvites: "🔓 Invites have been enabled for this ticket."
|
||||
|
||||
# Available Placeholders:
|
||||
|
||||
# <@%user%> - Tags the user who was added to the ticket.
|
||||
|
||||
Blacklisted: ":x: You are not allowed to open a ticket"
|
||||
Muted: ":x: You are `Muted` and therefore not allowed to open any tickets"
|
||||
InvalidSubject: "No Reason Provided!"
|
||||
|
||||
ConfirmClose: "Please confirm closing the ticket by clicking the button below."
|
||||
CloseTimeout: "Ticket close confirmation timed out."
|
||||
|
||||
RenamedSuccess: "🏷️ Ticket renamed successfully!"
|
||||
|
||||
ClaimTickets:
|
||||
ClaimTitle: "Incoming Support Request!"
|
||||
ClaimMessage: "A new ticket has been created by <@!%user%>. Click the button below to claim this ticket."
|
||||
ClaimMessage_Edit: "<#%channel%> was claimed by <@!%user%>."
|
||||
|
||||
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."
|
||||
|
||||
ReassigningTitle: "Reassigning!..."
|
||||
ReassigningMessage: "<@%user%> did not claim the ticket. **Reassigning...**"
|
||||
|
||||
TicketQuestions:
|
||||
Details:
|
||||
Title: "Ticket Details"
|
||||
Description: "Questions by <@%user%>" # Use <@%user%> to display the username.
|
||||
|
||||
# ----------------------------
|
||||
# Review Messages
|
||||
# ----------------------------
|
||||
|
||||
ReviewSystem:
|
||||
Rate:
|
||||
Title: "Review us!"
|
||||
Description: "Please rate our support for this ticket (1-5 stars)."
|
||||
|
||||
Stars:
|
||||
One: "⭐ 1 Star"
|
||||
Two: "⭐⭐ 2 Stars"
|
||||
Three: "⭐⭐⭐ 3 Stars"
|
||||
Four: "⭐⭐⭐⭐ 4 Stars"
|
||||
Five: "⭐⭐⭐⭐⭐ 5 Stars"
|
||||
|
||||
Comment:
|
||||
Title: "Write a comment."
|
||||
Description: "Write a comment about your experience."
|
||||
|
||||
ReviewEmbed:
|
||||
Title: "Ticket Review"
|
||||
CommentTitle: "Comment"
|
||||
RatingTitle: "Rating"
|
||||
ReviewedStaffTitle: "Reviewed Staff:"
|
||||
ReviewedByTitle: "Reviewed By:"
|
||||
ReviewEmoji: "⭐"
|
||||
Color: "#f7a943"
|
||||
|
||||
# ----------------------------
|
||||
# Profile Messages
|
||||
# ----------------------------
|
||||
|
||||
TicketStats:
|
||||
OpenTickets: "✅ Open Tickets"
|
||||
TotalTickets: "🎫 Total Tickets"
|
||||
ResponseTime: "⏱️ Average Response Time"
|
||||
|
||||
Title: "Your Ticket Stats"
|
||||
|
||||
# ----------------------------
|
||||
# Mod Command Messages
|
||||
# ----------------------------
|
||||
|
||||
Mod:
|
||||
|
||||
TicketBlacklist:
|
||||
Add:
|
||||
Description: "Add a user to the ticket blacklist"
|
||||
UserDescription: "User to blacklist"
|
||||
AlreadyBlacklisted: ":x: {userTag} is already blacklisted."
|
||||
Success: ":white_check_mark: {userTag} has been added to the blacklist. They can no longer use the ticket system."
|
||||
|
||||
Remove:
|
||||
Description: "Remove a user from the ticket blacklist"
|
||||
UserDescription: "User to remove from blacklist"
|
||||
NotBlacklisted: ":x: {userTag} is not blacklisted."
|
||||
Success: ":white_check_mark: {userTag} has been removed from the blacklist. They can now use the ticket system again."
|
||||
|
||||
View:
|
||||
Description: "View the list of all blacklisted users"
|
||||
NoBlacklistedUsers: ":information_source: There are currently no users on the blacklist."
|
||||
|
||||
|
||||
# ----------------------------
|
||||
# Logging Messages
|
||||
# ----------------------------
|
||||
|
||||
TicketLog:
|
||||
Title: "Ticket Log"
|
||||
Colour: "#0cc559"
|
||||
|
||||
TicketBlacklistLog:
|
||||
Title: "Ticket Blacklist Log"
|
||||
Colour: "#0cc559"
|
||||
|
||||
# ----------------------------
|
||||
# Misc Messages
|
||||
# ----------------------------
|
||||
|
||||
AddUser:
|
||||
NotInTicket_Title: "Ticket Invitation"
|
||||
NotInTicket_Description: "This command can only be used within a ticket thread."
|
||||
|
||||
Invite_Title: "User Invited"
|
||||
Invite_Description: "%username% has been invited to join the ticket."
|
||||
|
||||
DM_Title: "Ticket Invitation"
|
||||
DM_Description: "You have been invited to join the ticket %channelname%. Do you accept?"
|
||||
|
||||
Accept_Label: "Accept"
|
||||
Reject_Label: "Reject"
|
||||
ViewTicket_Label: "Go to Ticket"
|
||||
|
||||
Accepted_Title: "Invitation Accepted"
|
||||
Accepted_Message: "You have been added to the ticket."
|
||||
AddedToTicket_Title: "Added to Ticket"
|
||||
AddedToTicket_Description: "You have been added to the ticket %channel_link%."
|
||||
|
||||
# Added Message for inside the ticket, this informs everyone inside the ticket that the user has been added.
|
||||
Added_Title: "User Added"
|
||||
Added_Description: "Successfully added **%username%** to the ticket."
|
||||
|
||||
Declined_Title: "Invitation Declined"
|
||||
Declined_Message: "You have declined the invitation."
|
||||
|
||||
NoResponse_Title: "No Response"
|
||||
NoResponse_Message: "You did not respond in time. Please contact staff if you wish to join the ticket."
|
||||
|
||||
Error_Title: "Error"
|
||||
Error_Adding: "There was an error adding you to the ticket. Please try again."
|
||||
|
||||
DM_Error_Title: "Error"
|
||||
DM_Error_Description: "Unable to send a DM to %username%. Please ensure your DMs are enabled."
|
||||
|
||||
Unexpected_Error_Title: "Error"
|
||||
Unexpected_Error_Description: "An unexpected error occurred: %error%"
|
||||
|
||||
RemoveUser:
|
||||
NotInTicket_Title: "Error"
|
||||
NotInTicket_Description: "This command can only be used in ticket threads."
|
||||
|
||||
Removed_Title: "User Removed"
|
||||
Removed_Message: "The user has been successfully removed from the ticket."
|
||||
|
||||
RemovedFromTicket_Title: "Removed from Ticket"
|
||||
RemovedFromTicket_Description: "You have been removed from the ticket channel %channel_link%."
|
||||
|
||||
Error_Title: "Error"
|
||||
Error_Removing: "There was an error removing the user from the ticket."
|
||||
|
||||
ForceAddUser:
|
||||
NotInTicket_Title: "Not a Ticket"
|
||||
NotInTicket_Description: "This command can only be used within a ticket thread."
|
||||
|
||||
Added_Title: "User Added"
|
||||
Added_Description: "Successfully added %username% to the ticket."
|
||||
|
||||
AddedToTicket_Title: "You have been added to a Ticket"
|
||||
AddedToTicket_Description: "You have been added to the ticket channel [%channel_link%]."
|
||||
|
||||
Error_Title: "Error"
|
||||
Error_Adding: "There was an error adding the user to the ticket."
|
||||
|
||||
Error:
|
||||
IncorrectPermissions: ":x: **Error!** You do not have the correct permissions to use this command."
|
||||
MissingChannel: ":x: **Error!** Required channel not found. Please ensure it is set up correctly!"
|
||||
UserNotFound: ":x: **Error!** Specified user not found. Please check if they are in this server."
|
||||
CommandNotAllowedHere: ":warning: This command cannot be used in this channel."
|
||||
EntryAlreadyExists: ":warning: Cannot complete the action, as the entry already exists."
|
||||
EntryDoesNotExist: ":warning: Cannot complete the action, as the entry does not exist."
|
||||
ActionFailed: ":x: **Error!** An error occurred while processing your request. Please try again later."
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# -------------------------
|
||||
# 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-mini"
|
||||
# You can see a list of OpenAI Models here: https://platform.openai.com/docs/models
|
||||
|
||||
Tokens: 400
|
||||
# Max tokens you can set is 400
|
||||
# For more information about tokens, go to https://openai.com/api/pricing/
|
||||
OpenAI_Key: "OPEN_AI_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: "AI_BOT_CHANNEL"
|
||||
# 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`"
|
||||
@@ -0,0 +1,298 @@
|
||||
# ___ _ ___ _
|
||||
# / __> _ _ ___ ___ ___ _ _ _| |_ | . > ___ _| |_
|
||||
# \__ \| | || . \| . \/ . \| '_> | | | . \/ . \ | |
|
||||
# <___/`___|| _/| _/\___/|_| |_| |___/\___/ |_|
|
||||
# |_| |_|
|
||||
#
|
||||
# SupportBot created by Emerald Services
|
||||
# Installed with MIT License
|
||||
#
|
||||
# Discord Support: https://discord.gg/emeraldsrv
|
||||
# Official Documentation: https://docs.emeraldsrv.dev
|
||||
# Addons Marketplace: https://community.emeraldsrv.dev
|
||||
#
|
||||
# SupportBot Configuration (supportbot.yml)
|
||||
|
||||
SupportBot_Version: "v8.4-DEV-2"
|
||||
|
||||
# -------------------------
|
||||
# General
|
||||
# -------------------------
|
||||
|
||||
General:
|
||||
Name: "SupportBot"
|
||||
Token: ""
|
||||
|
||||
Addons:
|
||||
Enabled: true
|
||||
|
||||
# -------------------------
|
||||
# Activity
|
||||
# -------------------------
|
||||
|
||||
Activity:
|
||||
Status: "SupportBot v8"
|
||||
Type: "Competing"
|
||||
StreamingURL: "https://www.twitch.tv/YOUR_TWITCH_NAME"
|
||||
|
||||
# Available Types:
|
||||
# Playing
|
||||
# Watching
|
||||
# Listening
|
||||
# Competing
|
||||
# Streaming (URL required)
|
||||
|
||||
# You can also update the activity via Discord using: /settings
|
||||
|
||||
# -------------------------
|
||||
# Embeds
|
||||
# -------------------------
|
||||
|
||||
Embed:
|
||||
Colours:
|
||||
General: 0x0cc559
|
||||
Success: 0x04b545
|
||||
Error: 0xd13030
|
||||
Warn: 0xfca117
|
||||
|
||||
Footer: "SupportBot | Developed by Emerald Development"
|
||||
|
||||
# Use 0x<hexcode> to change the colour of the embeds.
|
||||
|
||||
# -------------------------
|
||||
# Roles
|
||||
# -------------------------
|
||||
|
||||
Roles:
|
||||
AutoRole:
|
||||
Enabled: true
|
||||
Role: "1202669457751801917"
|
||||
|
||||
StaffMember:
|
||||
Staff: "996420644373811371"
|
||||
Admin: "996419960442208326"
|
||||
Moderator: "996420644373811371"
|
||||
|
||||
Mod:
|
||||
AllowSupportStaff: false
|
||||
|
||||
# Allow Support Staff to use the /mod command. (More in detail customisation in commands.yml)
|
||||
|
||||
# ----------------------------
|
||||
# System Messages
|
||||
# ----------------------------
|
||||
|
||||
Welcome:
|
||||
Enabled: true
|
||||
Channel: "1295788797065166869"
|
||||
|
||||
Leave:
|
||||
Enabled: true
|
||||
Channel: "1295788797065166869"
|
||||
|
||||
# ----------------------------
|
||||
# Suggestions
|
||||
# ----------------------------
|
||||
|
||||
Suggestions:
|
||||
Mode: "Channel" # Channel or Forum
|
||||
ForumChannel: "1402655496477081671"
|
||||
Channel: "1295788900094316706"
|
||||
|
||||
UpvoteEmoji: "✅"
|
||||
DownvoteEmoji: "❌"
|
||||
|
||||
UpvoteTitle: "Yes"
|
||||
DownvoteTitle: "No"
|
||||
|
||||
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
|
||||
|
||||
Tags:
|
||||
Created: "1418937323118137435"
|
||||
Considering: "1418937237156139130"
|
||||
Solved: "1418937168029679697"
|
||||
Denied: "1418937185469464647"
|
||||
|
||||
# -------------------------
|
||||
# Tickets
|
||||
# -------------------------
|
||||
|
||||
VoiceTickets:
|
||||
Name: "%username%'s VC"
|
||||
Category: "1419062819688550460"
|
||||
|
||||
Ticket:
|
||||
TicketType: "threads"
|
||||
|
||||
# Available TicketTypes:
|
||||
# channels - Tickets will be created as channels in the TicketChannelsCategory
|
||||
# threads - Tickets will be created as threads through the "TicketHome" channel.
|
||||
|
||||
TicketHome: "1295784943204700171"
|
||||
|
||||
# What is TicketHome?
|
||||
# - Channel where the Ticket creation panel is sent.
|
||||
# - Channel where ticket threads are made. (only if ticket threads is enabled above.)
|
||||
|
||||
# For TicketType: channels
|
||||
TicketChannelsCategory: "1418988408193155112"
|
||||
TicketChannelsCategory2: "1418988456167870615" # This will be used if the first category has reached the max channel limit.
|
||||
TicketArchiveCategory: "1418988434600759348"
|
||||
# ---
|
||||
|
||||
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"
|
||||
|
||||
# Available TicketSubjects:
|
||||
# "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
|
||||
|
||||
Close:
|
||||
StaffOnly: true
|
||||
# Set to false is anyone is able to close a ticket.
|
||||
|
||||
Confirmation_Button: "Confirm Close"
|
||||
Confirmation_Emoji: "🗑️"
|
||||
Confirmation_Style: "2"
|
||||
|
||||
Title: "Are you sure you want to close this ticket?"
|
||||
|
||||
# Available Button Styles:
|
||||
# 1 - Blurple
|
||||
# 2 - Grey
|
||||
# 3 - Green
|
||||
# 4 - Red
|
||||
|
||||
Invites:
|
||||
StaffOnly: true
|
||||
|
||||
ClaimTickets:
|
||||
Enabled: true
|
||||
Channel: "1295789389619793961"
|
||||
# Channel where the ticket assignments are sent for staff to claim.
|
||||
|
||||
ClaimAcceptTime: 120000
|
||||
# How long the assigned user has to accept the ticket before it gets re-assigned to someone else.
|
||||
# 120000 = 2 Minutes
|
||||
# 180000 = 3 Minutes
|
||||
# 240000 = 4 Minutes
|
||||
# 300000 = 5 Minutes
|
||||
# Time in Mliseconds
|
||||
|
||||
ButtonTitle: "Claim Ticket"
|
||||
ButtonEmoji: "🎫"
|
||||
Button: "1"
|
||||
|
||||
# Available Button Styles
|
||||
# 1 - Blurple
|
||||
# 2 - Grey
|
||||
# 3 - Green
|
||||
# 4 - Red
|
||||
|
||||
ReviewSystem:
|
||||
Enabled: false
|
||||
Channel: "1304480728884445225"
|
||||
UseModal: true
|
||||
|
||||
Questions:
|
||||
Enabled: true # Set to false to disable ticket questions
|
||||
List:
|
||||
- "Please provide your account email?"
|
||||
- "What is the nature of your issue?"
|
||||
- "Have you tried resolving the issue yourself?"
|
||||
|
||||
Log:
|
||||
TicketDataLog: "1339271338761650290"
|
||||
TickeDataTitle: "Ticket Data"
|
||||
TicketBlacklistLog: "1339271338761650290"
|
||||
|
||||
# -------------------------
|
||||
# Buttons
|
||||
# -------------------------
|
||||
Buttons:
|
||||
General:
|
||||
Delete: "🗑️"
|
||||
DeleteStyle: "4"
|
||||
|
||||
Voice:
|
||||
TicketDeleteText: "Delete Voice Channel"
|
||||
|
||||
Tickets:
|
||||
ClaimEmoji: "🧑💻"
|
||||
ClaimStyle: "1"
|
||||
|
||||
Unarchive: "Unarchive Ticket"
|
||||
Unarchive_Emoji: "♻️"
|
||||
Unarchive_Style: "2"
|
||||
|
||||
Unlock: "Unlock Ticket"
|
||||
Unlock_Emoji: "🔓"
|
||||
Unlock_Style: "2"
|
||||
|
||||
# Available Button Styles
|
||||
# 1 - Blurple
|
||||
# 2 - Grey
|
||||
# 3 - Green
|
||||
# 4 - Red
|
||||
|
||||
# -------------------------
|
||||
# Select Menus
|
||||
# -------------------------
|
||||
|
||||
SelectMenus:
|
||||
Tickets:
|
||||
PanelEmoji: "⚙️"
|
||||
|
||||
CloseEmoji: "🗑️"
|
||||
ArchiveEmoji: "♻️"
|
||||
LockEmoji: "🔐"
|
||||
RenameEmoji: "🏷️"
|
||||
VCEmoji: "🔊"
|
||||
|
||||
EnableInvitesEmoji: "📤"
|
||||
DisableInvitesEmoji: "📥"
|
||||
|
||||
# -------------------------
|
||||
# Logging
|
||||
# -------------------------
|
||||
|
||||
MessageDelete:
|
||||
Channel: "1202671982932402246"
|
||||
Colour: "#d92525"
|
||||
|
||||
MessageUpdate:
|
||||
Channel: "1202671982932402246"
|
||||
Colour: "#f2a024"
|
||||
|
||||
Translate:
|
||||
TranslateLog: "1202671982932402246"
|
||||
@@ -0,0 +1,48 @@
|
||||
# ___ _ ___ _
|
||||
# / __> _ _ ___ ___ ___ _ _ _| |_ | . > ___ _| |_
|
||||
# \__ \| | || . \| . \/ . \| '_> | | | . \/ . \ | |
|
||||
# <___/`___|| _/| _/\___/|_| |_| |___/\___/ |_|
|
||||
# |_| |_|
|
||||
#
|
||||
# SupportBot created by Emerald Services
|
||||
# Installed with MIT License
|
||||
#
|
||||
# Discord Support: https://discord.gg/emeraldsrv
|
||||
# Official Documentation: https://docs.emeraldsrv.com
|
||||
# Addons Marketplace: https://community.emeraldsrv.com
|
||||
#
|
||||
# Ticket Creation Panel Configuration (ticket-panel.yml)
|
||||
|
||||
# -------------------------------
|
||||
# Ticket Panel Configuration
|
||||
# -------------------------------
|
||||
|
||||
Panel: true
|
||||
|
||||
LayoutStyle: modern
|
||||
|
||||
# ----------------------------------------
|
||||
# Ticket Panel Message Configuration #2b2d31
|
||||
# ----------------------------------------
|
||||
|
||||
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: false
|
||||
TicketPanel_Image: true
|
||||
|
||||
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/dejQvkg.png"
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"blacklistedUsers": []
|
||||
}
|
||||
File renamed without changes.
@@ -1,37 +0,0 @@
|
||||
# ___ _ ___ _
|
||||
# / __> _ _ ___ ___ ___ _ _ _| |_ | . > ___ _| |_
|
||||
# \__ \| | || . \| . \/ . \| '_> | | | . \/ . \ | |
|
||||
# <___/`___|| _/| _/\___/|_| |_| |___/\___/ |_|
|
||||
# |_| |_|
|
||||
#
|
||||
# SupportBot created by Emerald Services
|
||||
# Installed with MIT License
|
||||
#
|
||||
# Discord Support: https:#emeraldsrv.dev/discord
|
||||
# Community Resources: https:#emeraldsrv.dev/resources
|
||||
|
||||
# -------------------------------
|
||||
# Commamnd Name Configuration
|
||||
# -------------------------------
|
||||
|
||||
TicketAdd: "ticketadd"
|
||||
TicketRemove: "ticketremove"
|
||||
OpenTicket: "ticket"
|
||||
CloseTicket: "close"
|
||||
InfoCommand: "info"
|
||||
PingCommand: "ping"
|
||||
HelpCommand: "help"
|
||||
|
||||
# ---------------------------------------
|
||||
# Commamnd Description Configuration
|
||||
# ---------------------------------------
|
||||
|
||||
TicketAddDesc: "Add a user to a support ticket."
|
||||
TicketRemoveDesc: "Remove a user from a support ticket."
|
||||
OpenTicketDesc: "Open a new support ticket."
|
||||
CloseTicketDesc: "Close your support ticket."
|
||||
InfoCommandDesc: "Get information about the brand."
|
||||
PingCommandDesc: "Pong!"
|
||||
HelpCommandDesc: "Get a list of all the commands."
|
||||
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
# ___ _ ___ _
|
||||
# / __> _ _ ___ ___ ___ _ _ _| |_ | . > ___ _| |_
|
||||
# \__ \| | || . \| . \/ . \| '_> | | | . \/ . \ | |
|
||||
# <___/`___|| _/| _/\___/|_| |_| |___/\___/ |_|
|
||||
# |_| |_|
|
||||
#
|
||||
# SupportBot created by Emerald Services
|
||||
# Installed with MIT License
|
||||
#
|
||||
# Discord Support: https:#emeraldsrv.dev/discord
|
||||
# Community Resources: https:#emeraldsrv.dev/resources
|
||||
|
||||
SupportBot_Version: "7.2 pre-3"
|
||||
|
||||
# -------------------------
|
||||
# General Configuration
|
||||
# -------------------------
|
||||
|
||||
Name: "SupportBot"
|
||||
Prefix: "!"
|
||||
Token: "BOT_TOKEN"
|
||||
|
||||
# -------------------------
|
||||
# Activity Configuration
|
||||
# -------------------------
|
||||
|
||||
BotActivity: "Need Help? -ticket"
|
||||
|
||||
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.
|
||||
|
||||
# -------------------------
|
||||
# Embed Configuration
|
||||
# -------------------------
|
||||
|
||||
EmbedColour: "#f74343"
|
||||
EmbedFooter: "SupportBot | An Advanced Ticket System"
|
||||
|
||||
ErrorColour: "#d12519"
|
||||
SuccessColour: "#04b545"
|
||||
WarningColour: "#fca117"
|
||||
|
||||
# -------------------------
|
||||
# Information Configuration
|
||||
# -------------------------
|
||||
|
||||
InfoTitle: "Information"
|
||||
AboutMe: "Hi, I'm SupportBot an advanced discord ticket bot with 100% customisation! [Download](https://emeraldsrv.dev/download)"
|
||||
|
||||
InfoButtonText: "Visit Site"
|
||||
InfoURL: "https://emeraldsrv.dev"
|
||||
InfoColour: "#f74343"
|
||||
|
||||
# -------------------------
|
||||
# Help Configuration
|
||||
# -------------------------
|
||||
|
||||
HelpMenu: true # Do not change!
|
||||
SendHelpPage: "channel" # Set to channel or dm
|
||||
|
||||
# -------------------------
|
||||
# Role Configuration
|
||||
# -------------------------
|
||||
|
||||
AutoRole: true
|
||||
AutoRole_Role: "Member"
|
||||
LockdownRole: "@everyone"
|
||||
|
||||
Staff: "Support Team"
|
||||
Admin: "Project Developer"
|
||||
TicketBlackListRole: "Blacklisted"
|
||||
|
||||
# -------------------------
|
||||
# Tickets Configuration
|
||||
# -------------------------
|
||||
|
||||
TicketCategory: "▬▬ TICKETS ▬▬"
|
||||
TicketChannel: "ticket"
|
||||
|
||||
TicketLog: "ticket-log"
|
||||
TranscriptLog: "ticket-data"
|
||||
|
||||
TranscriptTitle: "Ticket Transcript"
|
||||
TicketLog_Title: "Ticket Log"
|
||||
TicketBlackListMessage: "You are not allowed to open a ticket"
|
||||
|
||||
TicketSubject: "description"
|
||||
# "description" will displays the ticket reason within the channel description
|
||||
# "embed" will displays 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 to false to dis-allow mentions within tickets.
|
||||
|
||||
# --- 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"
|
||||
|
||||
|
||||
# --- Buttons Config ---
|
||||
# --- Start ---
|
||||
|
||||
# Discord Button Colour Selection
|
||||
# PRIMARY - Blurple
|
||||
# DANGER - Red
|
||||
# SUCCESS - Green
|
||||
# Secondary - Grey
|
||||
|
||||
TicketLockReaction: "🔒"
|
||||
TicketDeleteReaction: "⛔"
|
||||
LockTicketCategory: "Archive"
|
||||
|
||||
TicketCloseEmoji: "❌"
|
||||
TicketDeleteEmoji: "⛔"
|
||||
TicketLockEmoji: "🔒"
|
||||
|
||||
TicketCloseColour: "DANGER"
|
||||
TicketDeleteColour: "DANGER"
|
||||
TicketLockColour: "PRIMARY"
|
||||
|
||||
TicketDept1Colour: "PRIMARY"
|
||||
TicketDept2Colour: "PRIMARY"
|
||||
TicketDept3Colour: "PRIMARY"
|
||||
|
||||
TicketDept1Emoji: "1️⃣"
|
||||
TicketDept2Emoji: "2️⃣"
|
||||
TicketDept3Emoji: "3️⃣"
|
||||
|
||||
TranscriptSavedMessage: "📝 **Transcript Saved!**"
|
||||
TicketLockMessage: "❌ **Ticket Locked!**"
|
||||
TicketDeleteMessage: "⚠ **Ticket Deleting!**" # Tickets deleting after 5 seconds by default.
|
||||
TicketDeleteTime: 5000 # Time must be set in miliseconds.
|
||||
|
||||
# --- Departments Config ---
|
||||
# --- Start ---
|
||||
|
||||
TicketDepartments: true
|
||||
|
||||
AllowStaff: true
|
||||
# If set to false users with the "Staff" role will not be able to view tickets
|
||||
# without a valid department role set below.
|
||||
|
||||
# Set to false to allow "Staff" to see tickets whether its a department for them or not.
|
||||
|
||||
DepartmentTitle_1: "General Support"
|
||||
DepartmentTitle_2: "Billing Support"
|
||||
DepartmentTitle_3: "Admin Team"
|
||||
# Ticket department title, This is the title for a ticket department to help users know what is available.
|
||||
|
||||
DepartmentRole_1: "Support Team"
|
||||
DepartmentRole_2: "Support Team"
|
||||
DepartmentRole_3: "Project Developer"
|
||||
# Ticket roles are the roles that will be able to gain access to tickets depending on what department has been choosen.
|
||||
|
||||
# --- Closing Config ---
|
||||
# --- Start ---
|
||||
|
||||
ClosingTicket: ":warning: You have requested to close your support ticket."
|
||||
ClosingConfirmation_Word: "confirm"
|
||||
CloseConfirmation: false
|
||||
|
||||
# ---------------------------
|
||||
# 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!"
|
||||
@@ -1 +0,0 @@
|
||||
TicketPanelID: '896035054235574362'
|
||||
@@ -1,42 +0,0 @@
|
||||
# ___ _ ___ _
|
||||
# / __> _ _ ___ ___ ___ _ _ _| |_ | . > ___ _| |_
|
||||
# \__ \| | || . \| . \/ . \| '_> | | | . \/ . \ | |
|
||||
# <___/`___|| _/| _/\___/|_| |_| |___/\___/ |_|
|
||||
# |_| |_|
|
||||
#
|
||||
# SupportBot created by Emerald Services
|
||||
# Installed with MIT License
|
||||
#
|
||||
# Discord Support: https:#emeraldsrv.dev/discord
|
||||
# Community Resources: https:#emeraldsrv.dev/resources
|
||||
|
||||
# -------------------------------
|
||||
# Ticket Panel Configuration
|
||||
# -------------------------------
|
||||
|
||||
TicketPanel: true
|
||||
|
||||
# ----------------------------------------
|
||||
# Ticket Panel Message Configuration
|
||||
# ----------------------------------------
|
||||
|
||||
Channel: "create-ticket"
|
||||
ButtonEmoji: "📩"
|
||||
ButtonLabel: "Open Ticket"
|
||||
ButtonColour: "SUCCESS"
|
||||
# Colour Options:
|
||||
# -> PRIMARY - Blurple
|
||||
# -> SECONDARY - Grey
|
||||
# -> SUCCESS - Green
|
||||
# -> DANGER - Red
|
||||
|
||||
PanelTitle: "Need Help? Create a Support Ticket."
|
||||
|
||||
TicketPanel_Description: true
|
||||
TicketPanel_Thumbnail: false
|
||||
TicketPanel_Image: false
|
||||
|
||||
PanelMessage: "Click the button to open a new support ticket."
|
||||
PanelThumbnail: "https://i.imgur.com/ieeZejl.png"
|
||||
PanelImage: "https://i.imgur.com/5NkcmDq.png"
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
const Event = require("../Structures/Event.js");
|
||||
module.exports = new Event("guildCreate", async (client) => {
|
||||
console.log(
|
||||
`\u001b[31m`,
|
||||
`${client.user.username} is not in the correct server set in your config. Please leave all other servers and restart the bot.`
|
||||
);
|
||||
console.log(`\u001b[31m`, `${client.user.username} will now exit.`);
|
||||
return process.exit(1);
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
const Event = require("../Structures/Event.js");
|
||||
|
||||
module.exports = new Event("guildDelete", async (client) => {
|
||||
console.log(
|
||||
`\u001b[31m`,
|
||||
`${client.user.username} is not in the correct server set in your config. Please join the server and restart the bot.`
|
||||
);
|
||||
console.log(`\u001b[31m`, `${client.user.username} will now exit.`);
|
||||
return process.exit(1);
|
||||
});
|
||||
@@ -0,0 +1,151 @@
|
||||
const {
|
||||
EmbedBuilder,
|
||||
ActionRowBuilder,
|
||||
ButtonBuilder,
|
||||
ButtonStyle,
|
||||
MessageFlags,
|
||||
ContainerBuilder,
|
||||
TextDisplayBuilder,
|
||||
SectionBuilder,
|
||||
ThumbnailBuilder,
|
||||
AttachmentBuilder
|
||||
} = require("discord.js");
|
||||
const fs = require("fs");
|
||||
const yaml = require("js-yaml");
|
||||
const Event = require("../Structures/Event.js");
|
||||
|
||||
const supportbot = yaml.load(
|
||||
fs.readFileSync("./Configs/supportbot.yml", "utf8")
|
||||
);
|
||||
const msgconfig = yaml.load(
|
||||
fs.readFileSync("./Configs/messages.yml", "utf8")
|
||||
);
|
||||
|
||||
function getWelcomeThumbnail(client, member, iconCfg) {
|
||||
switch ((iconCfg?.Mode || "").toUpperCase()) {
|
||||
case "USER":
|
||||
return member.user.displayAvatarURL();
|
||||
case "BOT":
|
||||
return client.user.displayAvatarURL();
|
||||
case "CUSTOM":
|
||||
return iconCfg.CustomURL;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function buildModernComponents(client, member) {
|
||||
const container = new ContainerBuilder().setAccentColor(
|
||||
parseInt(msgconfig.Welcome.Modern.Colour.replace("#", ""), 16)
|
||||
);
|
||||
|
||||
const section = new SectionBuilder().addTextDisplayComponents(
|
||||
new TextDisplayBuilder().setContent(
|
||||
msgconfig.Welcome.Modern.Title.replace(/%joined_user%/g, member.user)
|
||||
),
|
||||
new TextDisplayBuilder().setContent(
|
||||
msgconfig.Welcome.Modern.Body.replace(/%joined_user%/g, member.user)
|
||||
)
|
||||
);
|
||||
|
||||
const thumbUrl = getWelcomeThumbnail(client, member, msgconfig.Welcome.Modern.Icon);
|
||||
if (thumbUrl) {
|
||||
section.setThumbnailAccessory(
|
||||
new ThumbnailBuilder()
|
||||
.setURL(thumbUrl)
|
||||
);
|
||||
}
|
||||
|
||||
container.addSectionComponents(section);
|
||||
|
||||
if (msgconfig.Welcome.Modern.Image?.Enabled && msgconfig.Welcome.Modern.Image.URL) {
|
||||
container.addTextDisplayComponents(
|
||||
new TextDisplayBuilder().setContent(
|
||||
``
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (Array.isArray(msgconfig.Welcome.Modern.Buttons)) {
|
||||
const row = new ActionRowBuilder();
|
||||
msgconfig.Welcome.Modern.Buttons.slice(0, 5).forEach(b => {
|
||||
const btn = new ButtonBuilder()
|
||||
.setLabel(b.label)
|
||||
.setStyle(ButtonStyle[b.style])
|
||||
.setEmoji(b.emoji || null);
|
||||
if (b.style === "Link") btn.setURL(b.url);
|
||||
else if (b.customId) btn.setCustomId(b.customId);
|
||||
row.addComponents(btn);
|
||||
});
|
||||
|
||||
container.addActionRowComponents(row);
|
||||
}
|
||||
|
||||
return [container];
|
||||
}
|
||||
|
||||
module.exports = new Event("guildMemberAdd", async (client, member) => {
|
||||
if (!supportbot.Welcome.Enabled) return;
|
||||
|
||||
const WelcomeChannel =
|
||||
member.guild.channels.cache.get(supportbot.Welcome.Channel) ||
|
||||
member.guild.channels.cache.find(c => c.name === supportbot.Welcome.Channel);
|
||||
if (!WelcomeChannel) return;
|
||||
|
||||
const style = (msgconfig.Welcome.LayoutStyle || "classic").toLowerCase();
|
||||
|
||||
if (style === "classic") {
|
||||
const WelcomeEmbed = new EmbedBuilder()
|
||||
.setColor(msgconfig.Welcome.Embed.Colour)
|
||||
.setTitle(msgconfig.Welcome.Embed.Title || null)
|
||||
.setDescription(
|
||||
msgconfig.Welcome.Embed.Message.replace(/%joined_user%/g, member.user)
|
||||
)
|
||||
.setTimestamp();
|
||||
|
||||
const thumb = getWelcomeThumbnail(client, member, msgconfig.Welcome.Embed.Icon);
|
||||
if (thumb) WelcomeEmbed.setThumbnail(thumb);
|
||||
|
||||
if (msgconfig.Welcome.Embed.Image?.Enabled && msgconfig.Welcome.Embed.Image.URL) {
|
||||
WelcomeEmbed.setImage(msgconfig.Welcome.Embed.Image.URL);
|
||||
}
|
||||
|
||||
const components = [];
|
||||
if (
|
||||
msgconfig.Welcome.Embed.Buttons?.Enabled &&
|
||||
Array.isArray(msgconfig.Welcome.Embed.Buttons.List)
|
||||
) {
|
||||
const row = new ActionRowBuilder();
|
||||
msgconfig.Welcome.Embed.Buttons.List.slice(0, 5).forEach(b => {
|
||||
const btn = new ButtonBuilder()
|
||||
.setLabel(b.label)
|
||||
.setStyle(ButtonStyle[b.style])
|
||||
.setEmoji(b.emoji || null);
|
||||
if (b.style === "Link") btn.setURL(b.url);
|
||||
else if (b.customId) btn.setCustomId(b.customId);
|
||||
row.addComponents(btn);
|
||||
});
|
||||
components.push(row);
|
||||
}
|
||||
|
||||
await WelcomeChannel.send({ embeds: [WelcomeEmbed], components });
|
||||
} else if (style === "modern") {
|
||||
const components = buildModernComponents(client, member);
|
||||
if (components.length > 0) {
|
||||
await WelcomeChannel.send({
|
||||
components,
|
||||
flags: MessageFlags.IsComponentsV2
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (supportbot.Roles.AutoRole.Enabled) {
|
||||
const role =
|
||||
member.guild.roles.cache.get(supportbot.Roles.AutoRole.Role) ||
|
||||
member.guild.roles.cache.find(r => r.name === supportbot.Roles.AutoRole.Role);
|
||||
if (role) await member.roles.add(role);
|
||||
}
|
||||
|
||||
console.log("\u001b[32m", "[+]", "\u001b[33m", `${member.user.username} joined the server!`);
|
||||
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
const { EmbedBuilder } = require("discord.js");
|
||||
const fs = require("fs");
|
||||
const yaml = require("js-yaml");
|
||||
const Event = require("../Structures/Event.js");
|
||||
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
|
||||
|
||||
module.exports = new Event("guildMemberRemove", async (client, member) => {
|
||||
|
||||
if (!supportbot.Leave || !supportbot.Leave.Enabled) return;
|
||||
|
||||
const leaveChannel =
|
||||
member.guild.channels.cache.get(supportbot.Leave.Channel) ||
|
||||
member.guild.channels.cache.find(c => c.name === supportbot.Leave.Channel);
|
||||
|
||||
if (!leaveChannel) return;
|
||||
|
||||
const leaveEmbed = new EmbedBuilder()
|
||||
.setColor(msgconfig.Leave.Embed.Colour)
|
||||
.setTitle(msgconfig.Leave.Embed.Title)
|
||||
.setDescription(
|
||||
msgconfig.Leave.Embed.Message.replace(/%left_user%/g, member.user.toString())
|
||||
)
|
||||
.setTimestamp();
|
||||
|
||||
if (msgconfig.Leave.Embed.Thumbnail === "BOT") {
|
||||
leaveEmbed.setThumbnail(client.user.displayAvatarURL());
|
||||
} else if (msgconfig.Leave.Embed.Thumbnail === "USER") {
|
||||
leaveEmbed.setThumbnail(member.user.displayAvatarURL());
|
||||
}
|
||||
|
||||
if (msgconfig.Leave.Embed.ImageEnabled) {
|
||||
leaveEmbed.setImage(msgconfig.Leave.Embed.ImageURL);
|
||||
}
|
||||
|
||||
await leaveChannel.send({ embeds: [leaveEmbed] });
|
||||
|
||||
console.log(`[-] ${member.user.tag} left the server`);
|
||||
});
|
||||
@@ -0,0 +1,733 @@
|
||||
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 Event = require("../Structures/Event.js");
|
||||
const db = require("../Structures/Database.js");
|
||||
|
||||
module.exports = new Event("interactionCreate", async (client, interaction) => {
|
||||
|
||||
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.EmbedBuilder()
|
||||
.setDescription(`:x: \`Invalid Command\``)
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
if (!command)
|
||||
return interaction.reply({
|
||||
embeds: [NotValid],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
|
||||
const permission = interaction.member.permissions.has(command.permissions);
|
||||
|
||||
const ValidPerms = new Discord.EmbedBuilder()
|
||||
.setDescription(
|
||||
"> :x: `Invalid Permissions` Do you have the correct permissions to execute this command?"
|
||||
)
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
|
||||
if (!permission)
|
||||
return interaction.reply({
|
||||
embeds: [ValidPerms],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
|
||||
try {
|
||||
if (command.name === cmdconfig.OpenTicket.Command && supportbot.Ticket.TicketReason) {
|
||||
const modal = new Discord.ModalBuilder()
|
||||
.setCustomId("ticketReasonModal")
|
||||
.setTitle("Ticket Reason")
|
||||
.addComponents(
|
||||
new Discord.ActionRowBuilder().addComponents(
|
||||
new Discord.TextInputBuilder()
|
||||
.setCustomId("reasonInput")
|
||||
.setLabel("Reason")
|
||||
.setStyle(Discord.TextInputStyle.Paragraph)
|
||||
.setRequired(true)
|
||||
)
|
||||
);
|
||||
|
||||
await interaction.showModal(modal);
|
||||
} else {
|
||||
console.log("\u001b[32m", "[Executed]", "\u001b[37;1m", `${interaction.user.username} has executed ${command.name}`);
|
||||
await command.run(interaction);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error executing command:", error);
|
||||
await interaction.reply({
|
||||
content: "An error occurred while executing the command.",
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (interaction.isModalSubmit()) {
|
||||
if (interaction.customId === "ticketReasonModal") {
|
||||
const reason = interaction.fields.getTextInputValue("reasonInput");
|
||||
interaction.customId = `createticket-${reason}`;
|
||||
const cmd = client.commands.get(cmdconfig.OpenTicket.Command);
|
||||
if (cmd) {
|
||||
interaction.reason = reason;
|
||||
await cmd.run(interaction);
|
||||
} else {
|
||||
console.error("Command not found.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (interaction.customId === "renameTicketModal") {
|
||||
const newTicketName = interaction.fields.getTextInputValue("renameTicket");
|
||||
const ticketChannelId = interaction.channel.id;
|
||||
let ticketChannel;
|
||||
if (supportbot.Ticket.TicketType === "threads") {
|
||||
ticketChannel = interaction.guild.channels.cache.get(ticketChannelId);
|
||||
} else if (supportbot.Ticket.TicketType === "channels") {
|
||||
ticketChannel = interaction.guild.channels.cache.get(ticketChannelId);
|
||||
}
|
||||
if (!ticketChannel) {
|
||||
return interaction.reply({
|
||||
content: "Ticket channel not found.",
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
try {
|
||||
await ticketChannel.setName(newTicketName);
|
||||
const renamedsuccess = new Discord.EmbedBuilder()
|
||||
.setDescription(msgconfig.Ticket.RenamedSuccess)
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
return interaction.reply({
|
||||
embeds: [renamedsuccess],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error renaming the ticket:", error);
|
||||
return interaction.reply({
|
||||
content: "There was an error renaming the ticket.",
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (interaction.isStringSelectMenu()) {
|
||||
if (interaction.customId === "ticketcontrolpanel") {
|
||||
const selectedOption = interaction.values[0];
|
||||
const { getRole, getChannel } = interaction.client;
|
||||
switch (selectedOption) {
|
||||
|
||||
case "ticketclose":
|
||||
const closeCommand = client.commands.get(cmdconfig.CloseTicket.Command);
|
||||
if (closeCommand) {
|
||||
await closeCommand.run(interaction);
|
||||
} else {
|
||||
console.error("[X] Close Command Not Found");
|
||||
}
|
||||
break;
|
||||
|
||||
case "supportvc":
|
||||
const ticketInfo = db.getTicket(interaction.channel.id);
|
||||
if (!ticketInfo) {
|
||||
return interaction.reply({
|
||||
content: "⚠️ This channel isn’t a valid ticket.",
|
||||
flags: Discord.MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
const user = interaction.guild.members.cache.get(ticketInfo.user_id) || ticketInfo.user_id;
|
||||
const username = user?.user?.username || "Unknown User";
|
||||
|
||||
const VCcategory = interaction.guild.channels.cache.find(
|
||||
(c) => c.name === supportbot.VoiceTickets.Category || c.id === supportbot.VoiceTickets.Category
|
||||
);
|
||||
|
||||
if (!VCcategory) {
|
||||
return interaction.reply({
|
||||
content: "⚠️ The Support VC category does not exist!",
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
const SupportVoice = await interaction.guild.channels.create({
|
||||
name: supportbot.VoiceTickets.Name.replace(/%username%/g, username),
|
||||
type: Discord.ChannelType.GuildVoice,
|
||||
parent: VCcategory,
|
||||
permissionOverwrites: [
|
||||
{
|
||||
id: interaction.guild.id,
|
||||
deny: [Discord.PermissionsBitField.Flags.Connect]
|
||||
},
|
||||
{
|
||||
id: user.id || user, // works if user is a Member or just ID
|
||||
allow: [
|
||||
Discord.PermissionsBitField.Flags.Connect,
|
||||
Discord.PermissionsBitField.Flags.Speak,
|
||||
Discord.PermissionsBitField.Flags.ViewChannel,
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
db.updateTicketVoice(ticketInfo.ticket_id, SupportVoice.id);
|
||||
|
||||
const vcmadeembed = new Discord.EmbedBuilder()
|
||||
.setDescription(`> **Support VC Created:** <#${SupportVoice.id}>`)
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
|
||||
const VCDeleteButton = new Discord.ButtonBuilder()
|
||||
.setCustomId("deleteSupportVC")
|
||||
.setLabel(supportbot.Buttons.Voice.TicketDeleteText)
|
||||
.setEmoji(supportbot.Buttons.General.Delete)
|
||||
.setStyle(supportbot.Buttons.General.DeleteStyle);
|
||||
|
||||
const VCDeleteRow = new Discord.ActionRowBuilder().addComponents(VCDeleteButton);
|
||||
|
||||
await interaction.reply({
|
||||
embeds: [vcmadeembed],
|
||||
components: [VCDeleteRow],
|
||||
});
|
||||
break;
|
||||
|
||||
case "archiveticket":
|
||||
const arcembed = new Discord.EmbedBuilder()
|
||||
.setDescription(msgconfig.Ticket.TicketArchived)
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
const CloseButton = new Discord.ButtonBuilder()
|
||||
.setCustomId("confirmCloseTicket")
|
||||
.setLabel(supportbot.Ticket.Close.Confirmation_Button)
|
||||
.setEmoji(supportbot.Ticket.Close.Confirmation_Emoji)
|
||||
.setStyle(supportbot.Ticket.Close.Confirmation_Style);
|
||||
const unarchiveButton = new Discord.ButtonBuilder()
|
||||
.setCustomId("unarchiveTicket")
|
||||
.setLabel(supportbot.Buttons.Tickets.Unarchive)
|
||||
.setEmoji(supportbot.Buttons.Tickets.Unarchive_Emoji)
|
||||
.setStyle(supportbot.Buttons.Tickets.Unarchive_Style);
|
||||
const ArchivedOptions = new Discord.ActionRowBuilder()
|
||||
.addComponents(CloseButton, unarchiveButton);
|
||||
await interaction.channel.send({
|
||||
embeds: [arcembed],
|
||||
components: [ArchivedOptions],
|
||||
});
|
||||
if (supportbot.Ticket.TicketType === "threads") {
|
||||
await interaction.channel.setArchived(true);
|
||||
}
|
||||
if (supportbot.Ticket.TicketType === "channels") {
|
||||
const { getRole, getChannel } = interaction.client;
|
||||
await interaction.channel.setParent(supportbot.Ticket.TicketArchiveCategory);
|
||||
const tickets = db.getAllTickets();
|
||||
const ticketInfo = tickets.find(t => t.ticket_id === interaction.channel.id);
|
||||
if (ticketInfo) {
|
||||
const user = interaction.guild.members.cache.get(ticketInfo.user_id) || ticketInfo.user_id;
|
||||
const claimedBy = interaction.guild.members.cache.get(ticketInfo.claimed_by) || ticketInfo.claimed_by;
|
||||
const Admin = await interaction.guild.roles.fetch(supportbot.Roles.StaffMember.Admin);
|
||||
const Staff = await interaction.guild.roles.fetch(supportbot.Roles.StaffMember.Staff);
|
||||
if (user && user.id) {
|
||||
await interaction.channel.permissionOverwrites.edit(user.id, {
|
||||
ViewChannel: false,
|
||||
SendMessages: false,
|
||||
});
|
||||
}
|
||||
if (supportbot.ClaimTickets === true) {
|
||||
if (claimedBy && claimedBy.id) {
|
||||
await interaction.channel.permissionOverwrites.edit(claimedBy.id, {
|
||||
ViewChannel: true,
|
||||
SendMessages: true,
|
||||
ReadMessageHistory: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (supportbot.ClaimTickets === false) {
|
||||
if (Staff && Staff.id) {
|
||||
await interaction.channel.permissionOverwrites.edit(claimedBy.id, {
|
||||
ViewChannel: true,
|
||||
SendMessages: true,
|
||||
ReadMessageHistory: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (Admin && Admin.id) {
|
||||
await interaction.channel.permissionOverwrites.edit(Admin.id, {
|
||||
ViewChannel: true,
|
||||
SendMessages: true,
|
||||
ReadMessageHistory: true,
|
||||
});
|
||||
}
|
||||
await interaction.channel.permissionOverwrites.edit(interaction.guild.roles.everyone.id, {
|
||||
ViewChannel: false,
|
||||
SendMessages: false,
|
||||
ReadMessageHistory: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "lockticket":
|
||||
const lockembed = new Discord.EmbedBuilder()
|
||||
.setDescription(msgconfig.Ticket.TicketLocked)
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
const unlockButton = new Discord.ButtonBuilder()
|
||||
.setCustomId("unlockTicket")
|
||||
.setLabel(supportbot.Buttons.Tickets.Unlock)
|
||||
.setEmoji(supportbot.Buttons.Tickets.Unlock_Emoji)
|
||||
.setStyle(supportbot.Buttons.Tickets.Unlock_Style);
|
||||
const LockedOptions = new Discord.ActionRowBuilder()
|
||||
.addComponents(unlockButton);
|
||||
await interaction.channel.send({
|
||||
embeds: [lockembed],
|
||||
components: [LockedOptions],
|
||||
ephemeral: false,
|
||||
});
|
||||
if (supportbot.Ticket.TicketType === "threads") {
|
||||
await interaction.channel.setLocked(true);
|
||||
}
|
||||
break;
|
||||
|
||||
case "unlockTicket":
|
||||
const unlockEmbed = new Discord.EmbedBuilder()
|
||||
.setDescription(msgconfig.Ticket.TicketUnlocked)
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
await interaction.channel.send({
|
||||
embeds: [unlockEmbed],
|
||||
ephemeral: false,
|
||||
});
|
||||
if (supportbot.Ticket.TicketType === "threads") {
|
||||
await interaction.channel.setLocked(false);
|
||||
}
|
||||
break;
|
||||
|
||||
case "enableinvites":
|
||||
await handleInvites(interaction, true);
|
||||
break;
|
||||
|
||||
case "disableinvites":
|
||||
await handleInvites(interaction, false);
|
||||
break;
|
||||
|
||||
case "renameticket":
|
||||
const modal = new Discord.ModalBuilder()
|
||||
.setCustomId("renameTicketModal")
|
||||
.setTitle("Rename Channel")
|
||||
.addComponents(
|
||||
new Discord.ActionRowBuilder()
|
||||
.addComponents(
|
||||
new Discord.TextInputBuilder()
|
||||
.setCustomId("renameTicket")
|
||||
.setLabel("New Channel Name")
|
||||
.setStyle(Discord.TextInputStyle.Short)
|
||||
.setRequired(true)
|
||||
)
|
||||
);
|
||||
await interaction.showModal(modal);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (interaction.customId === 'deleteSupportVC') {
|
||||
const ticketData = db.getAllTickets();
|
||||
const ticketInfo = ticketData.find(t => t.ticket_id === interaction.channel.id);
|
||||
if (ticketInfo && ticketInfo.voiceChannelId) {
|
||||
try {
|
||||
const voiceChannel = await interaction.guild.channels.fetch(ticketInfo.voiceChannelId).catch(() => null);
|
||||
if (voiceChannel) {
|
||||
await voiceChannel.delete();
|
||||
const successEmbed = new Discord.EmbedBuilder()
|
||||
.setDescription('✅ Support voice channel has been successfully deleted!')
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
await interaction.reply({
|
||||
embeds: [successEmbed],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
} else {
|
||||
const errorEmbed = new Discord.EmbedBuilder()
|
||||
.setDescription('❌ The associated voice channel could not be found.')
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
await interaction.reply({
|
||||
embeds: [errorEmbed],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error deleting the Voice Channel:', error);
|
||||
const errorEmbed = new Discord.EmbedBuilder()
|
||||
.setDescription('❌ **There was an error trying to delete this voice channel. Please try again later.**')
|
||||
.setColor(supportbot.Embed.Colours.Error);
|
||||
await interaction.reply({
|
||||
embeds: [errorEmbed],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const errorEmbed = new Discord.EmbedBuilder()
|
||||
.setDescription('⚠️ This button is not linked to a valid voice channel.')
|
||||
.setColor(supportbot.Embed.Colours.Warn);
|
||||
await interaction.reply({
|
||||
embeds: [errorEmbed],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (interaction.isButton()) {
|
||||
if (interaction.customId === "unarchiveTicket") {
|
||||
if (supportbot.Ticket.TicketType === "threads") {
|
||||
await interaction.channel.setArchived(false);
|
||||
}
|
||||
if (supportbot.Ticket.TicketType === "channels") {
|
||||
const { getRole, getChannel } = interaction.client;
|
||||
await interaction.channel.setParent(supportbot.Ticket.TicketChannelsCategory);
|
||||
const ticketData = db.getAllTickets();
|
||||
const ticketInfo = ticketData.find(t => t.ticket_id === interaction.channel.id);
|
||||
if (ticketInfo) {
|
||||
const user = interaction.guild.members.cache.get(ticketInfo.user_id) || ticketInfo.user_id;
|
||||
const claimedBy = interaction.guild.members.cache.get(ticketInfo.claimed_by) || ticketInfo.claimed_by;
|
||||
const Admin = await interaction.guild.roles.fetch(supportbot.Roles.StaffMember.Admin);
|
||||
const Staff = await interaction.guild.roles.fetch(supportbot.Roles.StaffMember.Staff);
|
||||
if (user && user.id) {
|
||||
await interaction.channel.permissionOverwrites.edit(user.id, {
|
||||
ViewChannel: true,
|
||||
SendMessages: true,
|
||||
ReadMessageHistory: true,
|
||||
});
|
||||
}
|
||||
if (supportbot.ClaimTickets === true) {
|
||||
if (claimedBy && claimedBy.id) {
|
||||
await interaction.channel.permissionOverwrites.edit(claimedBy.id, {
|
||||
ViewChannel: true,
|
||||
SendMessages: true,
|
||||
ReadMessageHistory: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (supportbot.ClaimTickets === false) {
|
||||
if (Staff && Staff.id) {
|
||||
await interaction.channel.permissionOverwrites.edit(claimedBy.id, {
|
||||
ViewChannel: true,
|
||||
SendMessages: true,
|
||||
ReadMessageHistory: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (Admin && Admin.id) {
|
||||
await interaction.channel.permissionOverwrites.edit(Admin.id, {
|
||||
ViewChannel: true,
|
||||
SendMessages: true,
|
||||
ReadMessageHistory: true,
|
||||
});
|
||||
}
|
||||
await interaction.channel.permissionOverwrites.edit(interaction.guild.roles.everyone.id, {
|
||||
ViewChannel: false,
|
||||
SendMessages: false,
|
||||
ReadMessageHistory: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
const unrcembed = new Discord.EmbedBuilder()
|
||||
.setDescription(msgconfig.Ticket.TicketUnarchived)
|
||||
.setColor(supportbot.Embed.Colours.Success);
|
||||
await interaction.update({
|
||||
embeds: [unrcembed],
|
||||
components: [],
|
||||
});
|
||||
}
|
||||
|
||||
if (interaction.customId === "createticket") {
|
||||
if (supportbot.Ticket.TicketReason) {
|
||||
const modal = new Discord.ModalBuilder()
|
||||
.setCustomId("ticketReasonModal")
|
||||
.setTitle("Ticket Reason")
|
||||
.addComponents(
|
||||
new Discord.ActionRowBuilder().addComponents(
|
||||
new Discord.TextInputBuilder()
|
||||
.setCustomId("reasonInput")
|
||||
.setLabel("Reason")
|
||||
.setStyle(Discord.TextInputStyle.Paragraph)
|
||||
.setRequired(true)
|
||||
)
|
||||
);
|
||||
await interaction.showModal(modal);
|
||||
} else {
|
||||
try {
|
||||
const cmd = client.commands.get(cmdconfig.OpenTicket.Command);
|
||||
if (!cmd) return;
|
||||
await cmd.run(interaction);
|
||||
} catch (error) {
|
||||
console.error("Error executing the ticket command:", error);
|
||||
await interaction.reply({
|
||||
content: "An error occurred while creating the ticket.",
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (interaction.customId === "openticket") {
|
||||
if (supportbot.Ticket.TicketReason) {
|
||||
const modal = new Discord.ModalBuilder()
|
||||
.setCustomId("ticketReasonModal")
|
||||
.setTitle("Ticket Reason")
|
||||
.addComponents(
|
||||
new Discord.ActionRowBuilder().addComponents(
|
||||
new Discord.TextInputBuilder()
|
||||
.setCustomId("reasonInput")
|
||||
.setLabel("Reason")
|
||||
.setStyle(Discord.TextInputStyle.Paragraph)
|
||||
.setRequired(true)
|
||||
)
|
||||
);
|
||||
await interaction.showModal(modal);
|
||||
} else {
|
||||
try {
|
||||
const cmd = client.commands.get(cmdconfig.OpenTicket.Command);
|
||||
if (!cmd) return;
|
||||
await cmd.run(interaction);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (interaction.customId.startsWith("claimticket-")) {
|
||||
const ticketChannelId = interaction.customId.split("-")[1];
|
||||
const ticketChannel = interaction.guild.channels.cache.get(ticketChannelId);
|
||||
if (!ticketChannel) {
|
||||
return interaction.reply({
|
||||
content: "Ticket channel not found.",
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
const { getRole } = 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 });
|
||||
}
|
||||
|
||||
if (supportbot.Ticket.TicketType === "threads") {
|
||||
await ticketChannel.members.add(interaction.user.id);
|
||||
} else {
|
||||
await ticketChannel.permissionOverwrites.create(interaction.user.id, {
|
||||
ViewChannel: true,
|
||||
SendMessages: true,
|
||||
ReadMessageHistory: true,
|
||||
});
|
||||
await ticketChannel.permissionOverwrites.create(Admin, {
|
||||
ViewChannel: true,
|
||||
SendMessages: true,
|
||||
ReadMessageHistory: true,
|
||||
});
|
||||
await ticketChannel.permissionOverwrites.create(interaction.guild.roles.everyone, {
|
||||
ViewChannel: false,
|
||||
});
|
||||
}
|
||||
|
||||
const pingclaimedstaff = await ticketChannel.send(`<@${interaction.user.id}>`);
|
||||
setTimeout(() => pingclaimedstaff.delete(), 2000);
|
||||
|
||||
const ticketInfo = db.getTicket(ticketChannelId);
|
||||
if (ticketInfo) {
|
||||
db.updateTicketStatus(ticketChannelId, ticketInfo.status);
|
||||
ticketInfo.claimed_by = interaction.user.id;
|
||||
}
|
||||
|
||||
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 });
|
||||
}
|
||||
|
||||
// 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 });
|
||||
|
||||
const userId = interaction.user.id;
|
||||
const hasUpvoted = suggestion.upvotes.includes(userId);
|
||||
const hasDownvoted = suggestion.downvotes.includes(userId);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
} 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 { getRole, getChannel } = interaction.client;
|
||||
const SupportStaff = await getRole(supportbot.Roles.StaffMember.Staff, interaction.guild);
|
||||
const Admin = await getRole(supportbot.Roles.StaffMember.Admin, interaction.guild);
|
||||
|
||||
if (!SupportStaff || !Admin) {
|
||||
return interaction.reply(
|
||||
"Some roles seem to be missing!\nPlease check for errors when starting the bot."
|
||||
);
|
||||
}
|
||||
|
||||
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],
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
|
||||
if (interaction.type === Discord.InteractionType.ModalSubmit && interaction.customId === "editProfileModal") {
|
||||
const userId = interaction.user.id;
|
||||
const profilePath = `./Data/Profiles/${userId}.json`;
|
||||
|
||||
let profileData;
|
||||
if (fs.existsSync(profilePath)) {
|
||||
profileData = JSON.parse(fs.readFileSync(profilePath, "utf8"));
|
||||
} else {
|
||||
profileData = {
|
||||
bio: "",
|
||||
timezone: "",
|
||||
clockedIn: false,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const newBio = interaction.fields.getTextInputValue("bio");
|
||||
const newTimezone = interaction.fields.getTextInputValue("timezone");
|
||||
|
||||
profileData.bio = newBio;
|
||||
profileData.timezone = newTimezone;
|
||||
|
||||
fs.writeFileSync(profilePath, JSON.stringify(profileData, null, 2));
|
||||
|
||||
const updatedProfileEmbed = new Discord.EmbedBuilder()
|
||||
.setTitle(`${interaction.user.username}'s Profile`)
|
||||
.setColor(supportbot.Embed.Colours.General)
|
||||
.setThumbnail(interaction.user.displayAvatarURL())
|
||||
.addFields(
|
||||
{ name: "Bio", value: newBio || "No bio set.", inline: false },
|
||||
{ name: "Timezone", value: newTimezone || "No timezone set.", inline: true }
|
||||
);
|
||||
|
||||
if (profileData.clockedIn !== undefined) {
|
||||
updatedProfileEmbed.addFields({
|
||||
name: "Clocked In Status",
|
||||
value: profileData.clockedIn ? "✅ Clocked In" : "❌ Clocked Out",
|
||||
inline: true,
|
||||
});
|
||||
}
|
||||
|
||||
await interaction.reply({ embeds: [updatedProfileEmbed], ephemeral: true });
|
||||
} catch (error) {
|
||||
console.error("Failed to update profile data: ", error);
|
||||
await interaction.reply({
|
||||
content: "There was an error while updating your profile. Please try again later.",
|
||||
flags: Discord.MessageFlags.Ephemeral,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
const { EmbedBuilder } = require('discord.js');
|
||||
const fs = require("fs");
|
||||
const yaml = require("js-yaml");
|
||||
const Event = require("../Structures/Event.js");
|
||||
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
|
||||
|
||||
module.exports = new Event("messageDelete", async (client, message) => {
|
||||
if (!message.guild) return;
|
||||
|
||||
const author = message.author || (message.partial ? await message.fetch().then(m => m.author).catch(() => null) : null);
|
||||
if (!author || author.bot) return;
|
||||
|
||||
const content = message.content || (message.attachments.size > 0 ? "Contains attachment(s)" : "*No content*");
|
||||
|
||||
const deleteLogChannel = message.guild.channels.cache.get(supportbot.MessageDelete.Channel) ||
|
||||
message.guild.channels.cache.find(c => c.name === supportbot.MessageDelete.Channel);
|
||||
|
||||
const deletedMessageEmbed = new EmbedBuilder()
|
||||
.setColor(supportbot.MessageDelete.Colour)
|
||||
.setTitle("Message Deleted")
|
||||
.setDescription(`> **Channel:** <#${message.channel.id}>\n> **Message ID:** ${message.id}\n> **Message author:** ${author.tag} (${author.id})\n> **Message Created:** <t:${Math.floor(message.createdTimestamp / 1000)}:F>`)
|
||||
.addFields({ name: "Deleted Content", value: content, inline: false })
|
||||
.setThumbnail(author.displayAvatarURL())
|
||||
.setTimestamp();
|
||||
|
||||
if (message.attachments.size > 0) {
|
||||
const attachment = message.attachments.first();
|
||||
deletedMessageEmbed.addFields({ name: "Attachment", value: `[${attachment.name}](${attachment.proxyURL})` });
|
||||
}
|
||||
|
||||
if (deleteLogChannel) {
|
||||
deleteLogChannel.send({ embeds: [deletedMessageEmbed] });
|
||||
}
|
||||
});
|
||||
@@ -1,40 +0,0 @@
|
||||
// SupportBot | Emerald Services
|
||||
// Ready Event
|
||||
|
||||
const fs = require("fs");
|
||||
|
||||
const Discord = require("discord.js");
|
||||
const yaml = require('js-yaml');
|
||||
const supportbot = yaml.load(fs.readFileSync('./Data/supportbot.yml', 'utf8'));
|
||||
const cmdconfig = yaml.load(fs.readFileSync('./Data/commands.yml', 'utf8'));
|
||||
const panelconfig = yaml.load(fs.readFileSync('./Data/ticket-panel.yml', 'utf8'));
|
||||
|
||||
const Event = require("../Structures/Event.js");
|
||||
|
||||
module.exports = new Event("messageReactionAdd", async (client) => {
|
||||
|
||||
let message = {
|
||||
guild: reaction.message.channel.guild,
|
||||
author: user,
|
||||
content: "N/A",
|
||||
channel: reaction.message.channel
|
||||
}
|
||||
|
||||
if (panelconfig.TicketPanel) {
|
||||
if (reaction.message.id === panelconfig.TicketPanelID) {
|
||||
if (reaction.emoji.name == panelconfig.ButtonEmoji || reaction.emoji.id == panelconfig.ButtonEmoji) {
|
||||
await reaction.users.remove(user.id)
|
||||
|
||||
try {
|
||||
const cmd = client.commands.get(cmdconfig.OpenTicket);
|
||||
if(!cmd) return;
|
||||
|
||||
cmd.run(message, message.content);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,57 @@
|
||||
const { EmbedBuilder } = require('discord.js');
|
||||
const fs = require("fs");
|
||||
const yaml = require("js-yaml");
|
||||
const Event = require("../Structures/Event.js");
|
||||
|
||||
// Load the configuration files
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const msgconfig = yaml.load(fs.readFileSync("./Configs/messages.yml", "utf8"));
|
||||
|
||||
module.exports = new Event("messageUpdate", async (client, oldMessage, newMessage) => {
|
||||
|
||||
// Check if the message is from a guild and not from a bot
|
||||
if (!newMessage.guild || newMessage.author.bot) return;
|
||||
|
||||
// Fetch old message if it's not cached (important for message content)
|
||||
if (!oldMessage.content && oldMessage.partial) {
|
||||
try {
|
||||
oldMessage = await oldMessage.fetch();
|
||||
} catch (err) {
|
||||
console.error('Error fetching old message:', err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback if content is missing
|
||||
const oldMessageContent = oldMessage.content || (oldMessage.attachments.size > 0 ? 'Contains attachment(s)' : '*No content*');
|
||||
const newMessageContent = newMessage.content || (newMessage.attachments.size > 0 ? 'Contains attachment(s)' : '*No content*');
|
||||
|
||||
// Get the update log channel either by ID or by name
|
||||
const updateLogChannel = newMessage.guild.channels.cache.get(supportbot.MessageUpdate.Channel) ||
|
||||
newMessage.guild.channels.cache.find(channel => channel.name === supportbot.MessageUpdate.Channel);
|
||||
|
||||
// Create the embed for the updated message
|
||||
const updatedMessageEmbed = new EmbedBuilder()
|
||||
.setColor(supportbot.MessageUpdate.Colour)
|
||||
.setTitle("Message Updated")
|
||||
.setDescription(`> **Channel:** <#${newMessage.channel.id}>\n> **Message ID:** [${newMessage.id}](https://discord.com/channels/${newMessage.guild.id}/${newMessage.channel.id}/${newMessage.id})\n> **Message author:** ${newMessage.author.tag} (${newMessage.author.id})\n> **Message Created:** <t:${Math.floor(oldMessage.createdTimestamp / 1000)}:F>`)
|
||||
.addFields(
|
||||
{ name: 'Old Message', value: oldMessageContent, inline: false },
|
||||
{ name: 'New Message', value: newMessageContent, inline: false }
|
||||
)
|
||||
.setThumbnail(newMessage.author.displayAvatarURL())
|
||||
.setTimestamp();
|
||||
|
||||
// If the new message contains an attachment, add it to the embed
|
||||
if (newMessage.attachments.size > 0) {
|
||||
const attachment = newMessage.attachments.first();
|
||||
updatedMessageEmbed.addFields({ name: 'Attachment', value: `[${attachment.name}](${attachment.proxyURL})` });
|
||||
}
|
||||
|
||||
// Send the embed to the update log channel if the channel is found
|
||||
if (updateLogChannel) {
|
||||
updateLogChannel.send({
|
||||
embeds: [updatedMessageEmbed]
|
||||
});
|
||||
}
|
||||
});
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
const fs = require("fs");
|
||||
const Discord = require("discord.js");
|
||||
const yaml = require("js-yaml");
|
||||
const Event = require("../Structures/Event.js");
|
||||
const gradient = require("gradient-string");
|
||||
const figlet = require("figlet");
|
||||
|
||||
// Load configs
|
||||
const supportbot = yaml.load(fs.readFileSync("./Configs/supportbot.yml", "utf8"));
|
||||
const panel = 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 ACTIVITY_TYPES = {
|
||||
PLAYING: Discord.ActivityType.Playing,
|
||||
WATCHING: Discord.ActivityType.Watching,
|
||||
LISTENING: Discord.ActivityType.Listening,
|
||||
COMPETING: Discord.ActivityType.Competing
|
||||
};
|
||||
|
||||
const setActivity = (client, type, status) => {
|
||||
const activityType = ACTIVITY_TYPES[type?.toUpperCase()];
|
||||
if (activityType) {
|
||||
client.user.setPresence({
|
||||
activities: [{ name: status || "Online", type: activityType }],
|
||||
status: type?.toLowerCase() || "online"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
const logStartup = async (client) => {
|
||||
console.clear();
|
||||
|
||||
// Animated loading
|
||||
const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
||||
for (let i = 0; i < frames.length * 2; i++) {
|
||||
process.stdout.write(`\r${frames[i % frames.length]} Initializing SupportBot...`);
|
||||
await sleep(100);
|
||||
}
|
||||
console.clear();
|
||||
|
||||
// Main title
|
||||
console.log("\n" + gradient.pastel.multiline(figlet.textSync("SupportBot", { font: "ANSI Shadow", horizontalLayout: "fitted" })));
|
||||
|
||||
// Extract bot details safely
|
||||
const botName = supportbot?.General?.Name || "SupportBot";
|
||||
const botVersion = supportbot.SupportBot_Version;
|
||||
|
||||
console.log("\n╔═══════════════════════════════════════════════════════════════╗");
|
||||
console.log("║ System Information ║");
|
||||
console.log("╠═══════════════════════════════════════════════════════════════╣");
|
||||
console.log(`║ Bot Name : ${botName.padEnd(45)} ║`);
|
||||
console.log(`║ Version : ${botVersion.padEnd(45)} ║`);
|
||||
console.log(`║ Node.js : ${process.version.padEnd(45)} ║`);
|
||||
console.log(`║ Discord.js : v${Discord.version.padEnd(44)} ║`);
|
||||
console.log("╚═══════════════════════════════════════════════════════════════╝\n");
|
||||
|
||||
// Links section
|
||||
console.log(gradient.summer("┏━━━━━━━━━━━━━━━━━━━ Important Links ━━━━━━━━━━━━━━━━━━━┓"));
|
||||
console.log(gradient.summer("┃ ┃"));
|
||||
console.log(gradient.summer("┃ ") + "📚 Docs : " + gradient.cristal("https://emerald-services.gitbook.io/ ┃"));
|
||||
console.log(gradient.summer("┃ ") + "🤝 Discord : " + gradient.cristal("https://dsc.gg/emerald-dev ┃"));
|
||||
console.log(gradient.summer("┃ ┃"));
|
||||
console.log(gradient.summer("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n"));
|
||||
|
||||
// Bot Invite
|
||||
const inviteUrl = `https://discord.com/api/oauth2/authorize?client_id=${client.user.id}&permissions=8&scope=bot%20applications.commands`;
|
||||
console.log(gradient.teen("╔════════════════════════ Bot Invite ════════════════════════╗"));
|
||||
console.log(gradient.teen("║ ║"));
|
||||
console.log(gradient.teen("║ ") + gradient.pastel(inviteUrl.padEnd(56)) + gradient.teen("║"));
|
||||
console.log(gradient.teen("║ ║"));
|
||||
console.log(gradient.teen("╚════════════════════════════════════════════════════════════╝\n"));
|
||||
};
|
||||
|
||||
const checkConfigs = async (client) => {
|
||||
const guild = client.guilds.cache.first();
|
||||
if (!guild) return;
|
||||
|
||||
console.log(gradient.morning("┏━━━━━━━━━━━━━━━━━━━━ Config Check ━━━━━━━━━━━━━━━━━━━━━┓"));
|
||||
|
||||
const roles = [
|
||||
supportbot?.Roles?.StaffMember?.Admin,
|
||||
supportbot?.Roles?.StaffMember?.Staff,
|
||||
supportbot?.Roles?.AutoRole?.Role
|
||||
].filter(Boolean);
|
||||
|
||||
const channels = [
|
||||
supportbot?.Suggestions?.Channel,
|
||||
supportbot?.Ticket?.Log?.TicketLog,
|
||||
supportbot?.Ticket?.Log?.TranscriptLog,
|
||||
supportbot?.Ticket?.TicketHome,
|
||||
supportbot?.Welcome?.Channel,
|
||||
supportbot?.Leave?.Channel,
|
||||
supportbot?.Translate?.Log
|
||||
].filter(Boolean);
|
||||
|
||||
const [missingRoles, missingChannels] = await Promise.all([
|
||||
Promise.all(roles.map((role) => client.getRole(role, guild))),
|
||||
Promise.all(channels.map((channel) => client.getChannel(channel, guild)))
|
||||
]);
|
||||
|
||||
const missing = {
|
||||
roles: roles.filter((_, i) => !missingRoles[i]),
|
||||
channels: channels.filter((_, i) => !missingChannels[i])
|
||||
};
|
||||
|
||||
if (missing.roles.length || missing.channels.length) {
|
||||
console.log(gradient.morning("┃ ⚠️ - Configuration Issues Found: ┃"));
|
||||
if (missing.roles.length) {
|
||||
console.log(gradient.morning("┃ • Missing Roles: ") + gradient.fruit(missing.roles.join(", ")));
|
||||
}
|
||||
if (missing.channels.length) {
|
||||
console.log(gradient.morning("┃ • Missing Channels: ") + gradient.fruit(missing.channels.join(", ")));
|
||||
}
|
||||
} else {
|
||||
console.log(gradient.morning("┃ ✅ - All configurations validated successfully ┃"));
|
||||
}
|
||||
|
||||
console.log(gradient.morning("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n"));
|
||||
};
|
||||
|
||||
module.exports = new Event("clientReady", async (client) => {
|
||||
setActivity(client, supportbot?.Activity?.Type, supportbot?.Activity?.Status);
|
||||
await logStartup(client);
|
||||
await checkConfigs(client);
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Tanner Nielsen
|
||||
Copyright (c) 2025 Emerald Services
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -0,0 +1,96 @@
|
||||
<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>
|
||||
|
||||
<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%20-%20v14-blue?style=for-the-badge" alt="shield.png">
|
||||
</a>
|
||||
|
||||
<a href="https://github.com/nodejs/node">
|
||||
<img src="https://img.shields.io/badge/NODE.JS%20-%20v20.x-green?style=for-the-badge" 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>
|
||||
|
||||
## Hosting
|
||||
|
||||
You can use our [Pterodactyl egg](https://github.com/Emerald-Services/Pterodactyl-Egg) to host it on your own panel. We currently don't offer hosting for you, altough we might add this in the future.
|
||||
If you don't know how to install pterodactyl, you could always host it on replit:
|
||||
|
||||
[]([https://repl.it/github/ItsNyoty/iki](https://repl.it/github/Emerald-Services/SupportBot
|
||||
)
|
||||
|
||||
|
||||
|
||||
+20
@@ -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.
|
||||
@@ -0,0 +1,27 @@
|
||||
|
||||
// SupportBot | Emerald Services
|
||||
// Addon Structure
|
||||
class Command {
|
||||
constructor(options) {
|
||||
this.name = options.name;
|
||||
this.description = options.description;
|
||||
this.permissions = options.permissions;
|
||||
this.options = options.options || [];
|
||||
this.run = options.run;
|
||||
this.defaultPermission = true;
|
||||
}
|
||||
|
||||
// Method to dynamically update options
|
||||
setOptions(options) {
|
||||
this.options = options;
|
||||
}
|
||||
}
|
||||
|
||||
class Event {
|
||||
constructor(event, run) {
|
||||
this.event = event;
|
||||
this.run = run;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { Command, Event };
|
||||
@@ -1,24 +0,0 @@
|
||||
// SupportBot | Emerald Services
|
||||
// Addon Structure
|
||||
|
||||
const fs = require("fs");
|
||||
|
||||
const Discord = require("discord.js");
|
||||
|
||||
const yaml = require('js-yaml');
|
||||
const supportbot = yaml.load(fs.readFileSync('./Data/supportbot.yml', 'utf8'));
|
||||
|
||||
function RunFunction(message, args, client) {}
|
||||
|
||||
class Addon {
|
||||
constructor(options) {
|
||||
this.name = options.name;
|
||||
this.description = options.description;
|
||||
this.permission = options.permission;
|
||||
this.type = ["BOTH", "SLASH", "TEXT"].includes(options.type) ? options.type : "TEXT";
|
||||
this.slashCommandOptions = options.slashCommandOptions || [];
|
||||
this.run = options.run;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Addon;
|
||||
+207
-96
@@ -1,103 +1,214 @@
|
||||
// SupportBot | Emerald Services
|
||||
// Client Structure
|
||||
|
||||
const fs = require("fs");
|
||||
|
||||
const path = require("path");
|
||||
const Discord = require("discord.js");
|
||||
const Command = require("./Command.js");
|
||||
const Event = require("./Event.js");
|
||||
const intents = new Discord.Intents(32767);
|
||||
const { GatewayIntentBits, Partials } = require('discord.js');
|
||||
const yaml = require("js-yaml");
|
||||
const { Command, Event } = require('./Addon.js');
|
||||
|
||||
const yaml = require('js-yaml');
|
||||
const supportbot = yaml.load(fs.readFileSync('./Data/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 });
|
||||
|
||||
this.commands = new Discord.Collection();
|
||||
|
||||
this.prefix = supportbot.Prefix;
|
||||
}
|
||||
|
||||
start(token) {
|
||||
const commandFiles = fs.readdirSync("./Commands")
|
||||
.filter(file => file.endsWith(".js"));
|
||||
|
||||
const commands = commandFiles.map(file => require(`../Commands/${file}`));
|
||||
|
||||
console.log(`\u001b[34;1m`, '▬▬▬▬▬▬▬ Commands ▬▬▬▬▬▬▬')
|
||||
|
||||
commands.forEach(cmd => {
|
||||
console.log(`\u001b[31;1m`, `${cmd.name}`, `\u001b[32;1m`, 'Loaded');
|
||||
this.commands.set(cmd.name, cmd);
|
||||
});
|
||||
|
||||
console.log(`\u001b[34;1m`, '▬▬▬▬▬▬▬ Commands ▬▬▬▬▬▬▬')
|
||||
|
||||
// Addons
|
||||
|
||||
const addonFiles = fs.readdirSync("./Addons")
|
||||
.filter(file => file.endsWith(".js"));
|
||||
|
||||
const addons = addonFiles.map(file => require(`../Addons/${file}`));
|
||||
|
||||
console.log(' ')
|
||||
console.log(`\u001b[34;1m`, '▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬')
|
||||
|
||||
addons.forEach(addon => {
|
||||
console.log(`\u001b[31;1m`, `${addon.name}`, `\u001b[32;1m`, 'Loaded');
|
||||
this.commands.set(addon.name, addon);
|
||||
});
|
||||
|
||||
console.log(`\u001b[34;1m`, '▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬')
|
||||
console.log(' ')
|
||||
|
||||
// Slash Commands
|
||||
|
||||
const slashCommands = commands
|
||||
.filter(cmd => ["BOTH", "SLASH"].includes(cmd.type))
|
||||
.map(cmd => ({
|
||||
name: cmd.name.toLowerCase(),
|
||||
description: cmd.description,
|
||||
permissions: [],
|
||||
options: cmd.slashCommandOptions,
|
||||
defaultPermission: true
|
||||
}));
|
||||
|
||||
this.removeAllListeners();
|
||||
|
||||
this.on("ready", async () => {
|
||||
const cmds = await this.application.commands.set(slashCommands);
|
||||
|
||||
console.log(' ')
|
||||
console.log(`\u001b[34;1m`, '▬▬▬▬▬▬▬ Slash Command ▬▬▬▬▬▬▬'),
|
||||
console.log(' ')
|
||||
|
||||
cmds.forEach(cmd =>
|
||||
console.log(`Slash Command ${cmd.name} registered`),
|
||||
)
|
||||
|
||||
})
|
||||
|
||||
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, event.run.bind(null, this));
|
||||
});
|
||||
|
||||
console.log(`\u001b[34;1m`, '▬▬▬▬▬▬▬ Events ▬▬▬▬▬▬▬')
|
||||
console.log(' ')
|
||||
|
||||
this.login(token);
|
||||
}
|
||||
function walk(dir, ext = ".js", fileList = []) {
|
||||
const files = fs.readdirSync(dir);
|
||||
for (const file of files) {
|
||||
const full = `${dir}/${file}`;
|
||||
const stat = fs.statSync(full);
|
||||
if (stat.isDirectory()) {
|
||||
walk(full, ext, fileList);
|
||||
} else if (file.endsWith(ext)) {
|
||||
fileList.push(full);
|
||||
}
|
||||
}
|
||||
return fileList;
|
||||
}
|
||||
|
||||
module.exports = Client;
|
||||
class Client extends Discord.Client {
|
||||
constructor() {
|
||||
super({
|
||||
intents: [
|
||||
GatewayIntentBits.Guilds,
|
||||
GatewayIntentBits.GuildMembers,
|
||||
GatewayIntentBits.GuildMessages,
|
||||
GatewayIntentBits.MessageContent
|
||||
],
|
||||
partials: [Partials.Message, Partials.Channel]
|
||||
});
|
||||
|
||||
this.commands = new Discord.Collection();
|
||||
}
|
||||
|
||||
async getChannel(channel, guild) {
|
||||
if (!channel) return null;
|
||||
return guild.channels.cache.find(
|
||||
(c) =>
|
||||
(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;
|
||||
return guild.roles.cache.find(
|
||||
(r) => r.id === role || (r.name && r.name.toLowerCase() === role.toLowerCase())
|
||||
);
|
||||
}
|
||||
|
||||
async getCategory(category, guild) {
|
||||
if (!category) return null;
|
||||
return guild.channels.cache.find(
|
||||
(c) =>
|
||||
c.type === Discord.ChannelType.GuildCategory &&
|
||||
(c.id === category || (c.name && c.name.toLowerCase() === category.toLowerCase()))
|
||||
);
|
||||
}
|
||||
|
||||
async start(token) {
|
||||
let tempCommandFiles = walk("./Commands");
|
||||
|
||||
if (cmdconfig.Suggestion.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "suggest.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.Suggestion.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "suggestadmin.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.Help.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "help.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.Info.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "info.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.OpenTicket.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "ticket.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.CloseTicket.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "close.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.Embed.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "embed.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.Translate.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "translate.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.UserInfo.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "userinfo.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.Ping.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "Ping.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.AddUser.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "addUser.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.RemoveUser.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "removeUser.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.ForceAddUser.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "forceaddUser.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.Profile.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "profile.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.Settings.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "settings.js");
|
||||
}
|
||||
|
||||
if (cmdconfig.Mod.Enabled === false) {
|
||||
tempCommandFiles = tempCommandFiles.filter(item => path.basename(item) !== "mod.js");
|
||||
}
|
||||
|
||||
const commandFiles = tempCommandFiles;
|
||||
const commands = commandFiles.map((file) => require(`../${file}`));
|
||||
|
||||
console.log(`\u001b[33m`, "――――――――――――――――――――――――――――――――――――――――――――");
|
||||
|
||||
commands.forEach((cmd) => {
|
||||
console.log(`\u001b[32m`, `[CMD]`, `\u001b[37;1m`, `${cmd.name}`, `\u001b[32;1m`, "Loaded");
|
||||
this.commands.set(cmd.name, cmd);
|
||||
});
|
||||
|
||||
console.log(`\u001b[33m`, "▬▬▬▬▬▬▬ Commands ▬▬▬▬▬▬▬");
|
||||
|
||||
if (supportbot.General.Addons.Enabled) {
|
||||
const addonFiles = fs.readdirSync("./Addons").filter((file) => file.endsWith(".js"));
|
||||
|
||||
const addons = addonFiles.map((file) => {
|
||||
const addon = require(`../Addons/${file}`);
|
||||
addon.name = file.split(".")[0];
|
||||
return addon;
|
||||
});
|
||||
|
||||
console.log(" ");
|
||||
console.log(`\u001b[33m`, "▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬");
|
||||
|
||||
addons.forEach((addon) => {
|
||||
console.log(
|
||||
`\u001b[32m`,
|
||||
`[ADDON]`,
|
||||
`\u001b[37;1m`,
|
||||
`${addon.name}`,
|
||||
`\u001b[32;1m`,
|
||||
"Loaded"
|
||||
);
|
||||
|
||||
if (addon instanceof Command) {
|
||||
this.commands.set(addon.name, addon);
|
||||
}
|
||||
|
||||
if (addon.events && Array.isArray(addon.events)) {
|
||||
addon.events.forEach((event) => {
|
||||
if (event instanceof Event) {
|
||||
this.on(event.event, (...args) => event.run(this, ...args));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`\u001b[33m`, "▬▬▬▬▬▬▬ Addons ▬▬▬▬▬▬▬");
|
||||
}
|
||||
|
||||
this.once("clientReady", async () => {
|
||||
await this.guilds.cache.first()?.commands.set(this.commands);
|
||||
console.log(
|
||||
"\u001b[32m",
|
||||
`[SUCCESS]`,
|
||||
`\u001b[37;1m`,
|
||||
`Slash Commands Registered for ${this.guilds.cache.first().name}`
|
||||
);
|
||||
});
|
||||
|
||||
console.log(" ");
|
||||
console.log(`\u001b[33m`, "▬▬▬▬▬▬▬ Events ▬▬▬▬▬▬▬");
|
||||
|
||||
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[33m`, "▬▬▬▬▬▬▬ Events ▬▬▬▬▬▬▬");
|
||||
if (process.argv[2] !== "test") {
|
||||
await this.login(token);
|
||||
} else {
|
||||
console.log(
|
||||
`\u001b[31;1m`,
|
||||
"RUNNING IN TEST MODE, IF NOT INTENDED, PLEASE USE npm start"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Client;
|
||||
+9
-19
@@ -1,24 +1,14 @@
|
||||
// SupportBot | Emerald Services
|
||||
// Command Structure
|
||||
|
||||
const fs = require("fs");
|
||||
|
||||
const Discord = require("discord.js");
|
||||
|
||||
const yaml = require('js-yaml');
|
||||
const supportbot = yaml.load(fs.readFileSync('./Data/supportbot.yml', 'utf8'));
|
||||
|
||||
function RunFunction(message, args, client) {}
|
||||
|
||||
class Command {
|
||||
constructor(options) {
|
||||
this.name = options.name;
|
||||
this.description = options.description;
|
||||
this.permission = options.permission;
|
||||
this.type = ["BOTH", "SLASH", "TEXT"].includes(options.type) ? options.type : "TEXT";
|
||||
this.slashCommandOptions = options.slashCommandOptions || [];
|
||||
this.run = options.run;
|
||||
}
|
||||
constructor(options) {
|
||||
this.name = options.name;
|
||||
this.description = options.description;
|
||||
this.permissions = options.permissions || [];
|
||||
this.options = options.options || [];
|
||||
this.run = options.run;
|
||||
this.defaultPermission = true;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Command;
|
||||
module.exports = Command;
|
||||
@@ -0,0 +1,221 @@
|
||||
// SupportBot | Emerald Services
|
||||
// Database Structure
|
||||
|
||||
const Database = require("better-sqlite3");
|
||||
const path = require("path");
|
||||
|
||||
const db = new Database(path.join(__dirname, "../Data/supportbot.db"));
|
||||
|
||||
db.prepare(`
|
||||
CREATE TABLE IF NOT EXISTS suggestions (
|
||||
thread_id TEXT PRIMARY KEY,
|
||||
author_id TEXT,
|
||||
text TEXT,
|
||||
status TEXT DEFAULT 'Created'
|
||||
)
|
||||
`).run();
|
||||
|
||||
db.prepare(`
|
||||
CREATE TABLE IF NOT EXISTS profiles (
|
||||
user_id TEXT PRIMARY KEY,
|
||||
bio TEXT,
|
||||
timezone TEXT,
|
||||
clockedIn INTEGER DEFAULT 0
|
||||
)
|
||||
`).run();
|
||||
|
||||
db.prepare(`
|
||||
CREATE TABLE IF NOT EXISTS tickets (
|
||||
ticket_id TEXT PRIMARY KEY,
|
||||
user_id TEXT,
|
||||
status TEXT DEFAULT 'open',
|
||||
created_at INTEGER,
|
||||
updated_at INTEGER,
|
||||
subject TEXT,
|
||||
description TEXT
|
||||
)
|
||||
`).run();
|
||||
|
||||
try {
|
||||
db.prepare("ALTER TABLE tickets ADD COLUMN voiceChannelId TEXT").run();
|
||||
} catch (e) {
|
||||
if (!String(e).includes("duplicate column name")) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
db.prepare(`
|
||||
CREATE TABLE IF NOT EXISTS ticket_panel (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
message_id TEXT,
|
||||
channel_id TEXT,
|
||||
created_at INTEGER
|
||||
)
|
||||
`).run();
|
||||
|
||||
db.prepare(`
|
||||
CREATE TABLE IF NOT EXISTS ticket_users (
|
||||
ticket_id TEXT,
|
||||
user_id TEXT,
|
||||
PRIMARY KEY(ticket_id, user_id)
|
||||
)
|
||||
`).run();
|
||||
|
||||
db.prepare(`
|
||||
CREATE TABLE IF NOT EXISTS settings (
|
||||
id INTEGER PRIMARY KEY,
|
||||
data TEXT
|
||||
)
|
||||
`).run();
|
||||
|
||||
module.exports = {
|
||||
addSuggestion(threadId, authorId, text) {
|
||||
db.prepare(`
|
||||
INSERT INTO suggestions (thread_id, author_id, text)
|
||||
VALUES (@threadId, @authorId, @text)
|
||||
ON CONFLICT(thread_id) DO NOTHING
|
||||
`).run({ threadId, authorId, text });
|
||||
},
|
||||
|
||||
setSuggestionStatus(threadId, status) {
|
||||
db.prepare(`
|
||||
UPDATE suggestions SET status=@status WHERE thread_id=@threadId
|
||||
`).run({ threadId, status });
|
||||
},
|
||||
|
||||
getSuggestion(threadId) {
|
||||
return db.prepare(`
|
||||
SELECT * FROM suggestions WHERE thread_id=@threadId
|
||||
`).get({ threadId });
|
||||
},
|
||||
|
||||
// ----- Profiles -----
|
||||
getProfile(userId) {
|
||||
const row = db.prepare(`SELECT * FROM profiles WHERE user_id=?`).get(userId);
|
||||
if (!row) {
|
||||
db.prepare(`
|
||||
INSERT INTO profiles (user_id, bio, timezone, clockedIn)
|
||||
VALUES (?, '', '', 0)
|
||||
`).run(userId);
|
||||
return { user_id: userId, bio: "", timezone: "", clockedIn: 0 };
|
||||
}
|
||||
return row;
|
||||
},
|
||||
|
||||
updateProfile(userId, data) {
|
||||
const { bio = "", timezone = "", clockedIn = 0 } = data;
|
||||
db.prepare(`
|
||||
INSERT INTO profiles (user_id, bio, timezone, clockedIn)
|
||||
VALUES (@userId, @bio, @timezone, @clockedIn)
|
||||
ON CONFLICT(user_id) DO UPDATE SET
|
||||
bio = excluded.bio,
|
||||
timezone = excluded.timezone,
|
||||
clockedIn = excluded.clockedIn
|
||||
`).run({ userId, bio, timezone, clockedIn });
|
||||
},
|
||||
|
||||
setClockedIn(userId, value) {
|
||||
db.prepare(`UPDATE profiles SET clockedIn=? WHERE user_id=?`)
|
||||
.run(value ? 1 : 0, userId);
|
||||
},
|
||||
|
||||
addTicket(ticket) {
|
||||
const now = Date.now();
|
||||
db.prepare(`
|
||||
INSERT INTO tickets (ticket_id, user_id, subject, description, created_at, updated_at)
|
||||
VALUES (@ticket_id, @user_id, @subject, @description, @created_at, @updated_at)
|
||||
ON CONFLICT(ticket_id) DO NOTHING
|
||||
`).run({
|
||||
ticket_id: ticket.id,
|
||||
user_id: ticket.user,
|
||||
subject: ticket.reason || "",
|
||||
description: ticket.description || "",
|
||||
created_at: now,
|
||||
updated_at: now
|
||||
});
|
||||
},
|
||||
|
||||
updateTicketVoice(ticketId, voiceChannelId) {
|
||||
db.prepare(`
|
||||
UPDATE tickets SET voiceChannelId=@voiceChannelId, updated_at=@updatedAt WHERE ticket_id=@ticketId
|
||||
`).run({ ticketId, voiceChannelId, updatedAt: Date.now() });
|
||||
},
|
||||
|
||||
getTicketVoice(ticketId) {
|
||||
return db.prepare(`SELECT voiceChannelId FROM tickets WHERE ticket_id=?`).get(ticketId);
|
||||
},
|
||||
|
||||
deleteTicketVoice(ticketId) {
|
||||
db.prepare(`
|
||||
UPDATE tickets SET voiceChannelId=NULL, updated_at=@updatedAt WHERE ticket_id=@ticketId
|
||||
`).run({ ticketId, updatedAt: Date.now() });
|
||||
},
|
||||
|
||||
updateTicketStatus(ticketId, status) {
|
||||
db.prepare(`
|
||||
UPDATE tickets SET status=@status, updated_at=@updatedAt WHERE ticket_id=@ticketId
|
||||
`).run({ status, updatedAt: Date.now(), ticketId });
|
||||
},
|
||||
|
||||
getTicket(ticketId) {
|
||||
return db.prepare(`
|
||||
SELECT * FROM tickets WHERE ticket_id=@ticketId
|
||||
`).get({ ticketId });
|
||||
},
|
||||
|
||||
getAllTickets() {
|
||||
return db.prepare(`SELECT * FROM tickets`).all();
|
||||
},
|
||||
|
||||
saveTicketPanel(messageId, channelId) {
|
||||
db.prepare(`
|
||||
INSERT INTO ticket_panel (message_id, channel_id, created_at)
|
||||
VALUES (@messageId, @channelId, @createdAt)
|
||||
`).run({ messageId, channelId, createdAt: Date.now() });
|
||||
},
|
||||
|
||||
getTicketPanel() {
|
||||
return db.prepare(`
|
||||
SELECT * FROM ticket_panel ORDER BY id DESC LIMIT 1
|
||||
`).get();
|
||||
},
|
||||
|
||||
addUserToTicket(ticketId, userId) {
|
||||
db.prepare(`
|
||||
INSERT INTO ticket_users (ticket_id, user_id)
|
||||
VALUES (?, ?)
|
||||
ON CONFLICT(ticket_id, user_id) DO NOTHING
|
||||
`).run(ticketId, userId);
|
||||
},
|
||||
|
||||
removeUserFromTicket(ticketId, userId) {
|
||||
db.prepare(`
|
||||
DELETE FROM ticket_users WHERE ticket_id=? AND user_id=?
|
||||
`).run(ticketId, userId);
|
||||
},
|
||||
|
||||
getTicketUsers(ticketId) {
|
||||
return db.prepare(`
|
||||
SELECT user_id FROM ticket_users WHERE ticket_id=?
|
||||
`).all(ticketId).map(r => r.user_id);
|
||||
},
|
||||
|
||||
getSettings() {
|
||||
const row = db.prepare(`SELECT data FROM settings WHERE id=1`).get();
|
||||
if (!row) return {};
|
||||
try {
|
||||
return JSON.parse(row.data);
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
|
||||
saveSettings(obj) {
|
||||
const data = JSON.stringify(obj);
|
||||
db.prepare(`
|
||||
INSERT INTO settings (id, data)
|
||||
VALUES (1, @data)
|
||||
ON CONFLICT(id) DO UPDATE SET data = excluded.data
|
||||
`).run({ data });
|
||||
}
|
||||
};
|
||||
+5
-15
@@ -1,21 +1,11 @@
|
||||
// SupportBot | Emerald Services
|
||||
// Event Structure
|
||||
|
||||
const fs = require("fs");
|
||||
|
||||
const Discord = require("discord.js");
|
||||
|
||||
const yaml = require('js-yaml');
|
||||
const supportbot = yaml.load(fs.readFileSync('./Data/supportbot.yml', 'utf8'));
|
||||
|
||||
function RunFunction(client, ...eventArgs) {}
|
||||
|
||||
class Event {
|
||||
|
||||
constructor(event, runFunction) {
|
||||
this.event = event;
|
||||
this.run = runFunction;
|
||||
}
|
||||
constructor(event, run) {
|
||||
this.event = event;
|
||||
this.run = run;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Event;
|
||||
module.exports = Event;
|
||||
+23
-69
@@ -1,76 +1,30 @@
|
||||
// SupportBot | Emerald Services
|
||||
// Ticket ID Structure
|
||||
|
||||
const fs = require("fs");
|
||||
|
||||
const Discord = require("discord.js");
|
||||
const Command = require("./Command.js");
|
||||
const Client = require("./Client.js");
|
||||
const Event = require("./Event.js");
|
||||
|
||||
const yaml = require('js-yaml');
|
||||
const supportbot = yaml.load(fs.readFileSync('./Data/supportbot.yml', 'utf8'));
|
||||
const db = require("./Database")
|
||||
|
||||
class TicketNumberID {
|
||||
|
||||
static pad(guildId) {
|
||||
let n = this.get(guildId) + '';
|
||||
let z = '0';
|
||||
let width = 4;
|
||||
setTimeout(() => {
|
||||
this.set(guildId, this.get(guildId) + 1);
|
||||
}, 50);
|
||||
return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
|
||||
static async pad() {
|
||||
let num = await this.get()
|
||||
let n = num + ""
|
||||
await this.set(num + 1)
|
||||
return n.length >= 4 ? n : new Array(4 - n.length + 1).join("0") + n
|
||||
}
|
||||
|
||||
static async get() {
|
||||
const row = db.getTicketPanel()
|
||||
if (!row) return await this.set(0)
|
||||
return row.id
|
||||
}
|
||||
|
||||
static async set(value) {
|
||||
db.saveTicketPanel(`panel-${value}`, "none")
|
||||
return value
|
||||
}
|
||||
|
||||
static async reset() {
|
||||
return await this.set(0)
|
||||
}
|
||||
}
|
||||
|
||||
static get(guildId) {
|
||||
|
||||
let ticketnumberID = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
|
||||
if (!ticketnumberID[guildId]) {
|
||||
ticketnumberID[guildId] = {
|
||||
id: 0
|
||||
}
|
||||
|
||||
fs.writeFileSync("./Data/TicketData.json", JSON.stringify(ticketnumberID, null, 4), err => {
|
||||
if (err) console.log(err);
|
||||
});
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
} else {
|
||||
return ticketnumberID[guildId].id;
|
||||
}
|
||||
}
|
||||
|
||||
static set(guildId, value) {
|
||||
let ticketnumberID = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
|
||||
ticketnumberID[guildId] = {
|
||||
id: value
|
||||
}
|
||||
|
||||
fs.writeFileSync("./Data/TicketData.json", JSON.stringify(ticketnumberID, null, 4), err => {
|
||||
if (err) console.log(err);
|
||||
});
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static reset(guildId) {
|
||||
let ticketnumberID = JSON.parse(fs.readFileSync("./Data/TicketData.json", "utf8"));
|
||||
|
||||
ticketnumberID[guildId] = {
|
||||
id: 0
|
||||
}
|
||||
|
||||
fs.writeFileSync("./Data/TicketData.json", JSON.stringify(ticketnumberID, null, 4), err => {
|
||||
if (err) console.log(err);
|
||||
});
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TicketNumberID;
|
||||
module.exports = TicketNumberID
|
||||
@@ -1,30 +0,0 @@
|
||||
// SupportBot | Emerald Services
|
||||
// Ping Command
|
||||
|
||||
const fs = require("fs");
|
||||
|
||||
const Discord = require("discord.js");
|
||||
const yaml = require('js-yaml');
|
||||
const supportbot = yaml.load(fs.readFileSync('./Data/supportbot.yml', 'utf8'));
|
||||
const cmdconfig = yaml.load(fs.readFileSync('./Data/commands.yml', 'utf8'));
|
||||
|
||||
const Command = require("../Structures/Command.js");
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.PingCommand,
|
||||
description: cmdconfig.PingCommandDesc,
|
||||
type: "BOTH",
|
||||
slashCommandOptions: [],
|
||||
permission: "SEND_MESSAGES",
|
||||
|
||||
async run(message, args, client) {
|
||||
|
||||
const PingEmbed = new Discord.MessageEmbed()
|
||||
.setDescription(`:ping_pong: **Ping:** \`${client.ws.ping} ms\``)
|
||||
.setColor(supportbot.EmbedColour)
|
||||
|
||||
message.channel.send({
|
||||
embeds: [PingEmbed]
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1,245 +0,0 @@
|
||||
// SupportBot | Emerald Services
|
||||
// Ticket Command
|
||||
|
||||
const fs = require("fs");
|
||||
|
||||
const Discord = require("discord.js");
|
||||
const yaml = require('js-yaml');
|
||||
const supportbot = yaml.load(fs.readFileSync('./Data/supportbot.yml', 'utf8'));
|
||||
const cmdconfig = yaml.load(fs.readFileSync('./Data/commands.yml', 'utf8'));
|
||||
|
||||
const Command = require("../Structures/Command.js");
|
||||
const TicketNumberID = require('../Structures/TicketID.js');
|
||||
|
||||
async function asyncForEach(array, callback) {
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
await callback(array[index], index, array);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new Command({
|
||||
name: cmdconfig.OpenTicket,
|
||||
description: cmdconfig.OpenTicketDesc,
|
||||
type: "BOTH",
|
||||
slashCommandOptions: [],
|
||||
permission: "SEND_MESSAGES",
|
||||
|
||||
async run(message, args, client) {
|
||||
|
||||
let reactionUser = message.guild.members.cache.get(message.author.id)
|
||||
|
||||
if (reactionUser.roles.cache.find(role => role.name === supportbot.TicketBlackListRole) || reactionUser.roles.cache.find(role => role.id === supportbot.TicketBlackListRole)) {
|
||||
return message.channel.send({content:`<@${message.author.id}> ${supportbot.TicketBlackListMessage}`}).then(msg => {
|
||||
setTimeout(() => msg.delete(), 3500)
|
||||
})
|
||||
}
|
||||
|
||||
// Ticket ID
|
||||
let ticketNumberID = TicketNumberID.pad(message.guild.id);
|
||||
|
||||
// Ticket Subject
|
||||
const TicketSubject = args || supportbot.NoTicketSubject;
|
||||
|
||||
const TicketExists = new Discord.MessageEmbed()
|
||||
.setTitle("Ticket Exists!")
|
||||
.setDescription(`${supportbot.TicketExists}`)
|
||||
.setColor(supportbot.WarningColour)
|
||||
|
||||
if (message.guild.channels.cache.find(ticketChannel => ticketChannel.name === `${supportbot.TicketChannel}-${ticketNumberID}`)) {
|
||||
return message.channel.send({ embeds: [TicketExists] });
|
||||
}
|
||||
const Staff = message.guild.roles.cache.find(SupportTeam => SupportTeam.name === supportbot.Staff) || message.guild.roles.cache.find(SupportTeam => SupportTeam.id === supportbot.Staff)
|
||||
const Admins = message.guild.roles.cache.find(AdminUser => AdminUser.name === supportbot.Admin) || message.guild.roles.cache.find(AdminUser => AdminUser.id === supportbot.Admin)
|
||||
const DeptRole1 = message.guild.roles.cache.find(DepartmentRole => DepartmentRole.name === `${supportbot.DepartmentRole_1}`) || message.guild.roles.cache.find(DepartmentRole => DepartmentRole.id === `${supportbot.DepartmentRole_1}`)
|
||||
const DeptRole2 = message.guild.roles.cache.find(DepartmentRole => DepartmentRole.name === `${supportbot.DepartmentRole_2}`) || message.guild.roles.cache.find(DepartmentRole => DepartmentRole.id === `${supportbot.DepartmentRole_2}`)
|
||||
const DeptRole3 = message.guild.roles.cache.find(DepartmentRole => DepartmentRole.name === `${supportbot.DepartmentRole_3}`) || message.guild.roles.cache.find(DepartmentRole => DepartmentRole.id === `${supportbot.DepartmentRole_3}`)
|
||||
const Author = message.author
|
||||
const Everyone = message.guild.id
|
||||
const ticketChannel = await message.guild.channels.create(`${supportbot.TicketChannel}-${ticketNumberID}`, {
|
||||
type: "GUILD_TEXT",
|
||||
})
|
||||
|
||||
if (message.channel.name === supportbot.ReactionChannel || message.channel.id === supportbot.ReactionChannel) {
|
||||
const CreatedTicket = new Discord.MessageEmbed()
|
||||
.setDescription(supportbot.TicketCreatedAlert.replace(/%ticketauthor%/g, message.author.id).replace(/%ticketid%/g, ticketChannel.id).replace(/%ticketusername%/g, message.author.username))
|
||||
.setColor(supportbot.EmbedColour)
|
||||
message.channel.send({embeds: [CreatedTicket]}).then(msg => {
|
||||
setTimeout(() => msg.delete(), 5000)
|
||||
})
|
||||
} else {
|
||||
const CreatedTicket = new Discord.MessageEmbed()
|
||||
.setDescription(supportbot.TicketCreatedAlert.replace(/%ticketauthor%/g, message.author.id).replace(/%ticketid%/g, ticketChannel.id).replace(/%ticketusername%/g, message.author.username))
|
||||
.setColor(supportbot.EmbedColour)
|
||||
message.channel.send({embeds: [CreatedTicket]}).then(msg => setTimeout(() => msg.delete(), 5000));
|
||||
}
|
||||
let AllowArr = [Staff, Staff, Admins, Author]
|
||||
await asyncForEach(AllowArr, (index) => {
|
||||
ticketChannel.permissionOverwrites.create(index, {VIEW_CHANNEL: true})
|
||||
})
|
||||
|
||||
let DenyArr = [Everyone]
|
||||
await asyncForEach(DenyArr, (index) => {
|
||||
ticketChannel.permissionOverwrites.create(index, {VIEW_CHANNEL: false})
|
||||
})
|
||||
|
||||
let TicketCategory = message.guild.channels.cache.find(category => category.name === supportbot.TicketCategory) || message.guild.channels.cache.find(category => category.id === supportbot.TicketCategory)
|
||||
if(TicketCategory) {
|
||||
ticketChannel.setParent(TicketCategory.id)
|
||||
}
|
||||
|
||||
if (supportbot.AllowTicketMentions) {
|
||||
ticketChannel.send({content:`${message.author}`})
|
||||
}
|
||||
|
||||
const TicketMessage = new Discord.MessageEmbed()
|
||||
.setTitle(supportbot.Ticket_Title.replace(/%ticketauthor%/g, message.author.id).replace(/%ticketid%/g, ticketChannel.id).replace(/%ticketusername%/g, message.author.username))
|
||||
.setDescription(supportbot.TicketMessage.replace(/%ticketauthor%/g, message.author.id).replace(/%ticketid%/g, ticketChannel.id).replace(/%ticketusername%/g, message.author.username))
|
||||
.setColor(supportbot.EmbedColour)
|
||||
|
||||
if (supportbot.TicketSubject === "embed") {
|
||||
|
||||
if (TicketSubject != 'No Reason Provided.') {
|
||||
TicketMessage.addFields({ name: 'Reason', value: TicketSubject })
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (supportbot.TicketSubject === "description") {
|
||||
|
||||
if (TicketSubject != 'No Reason Provided.') {
|
||||
ticketChannel.setTopic(`Reason: ${TicketSubject} - User ID: ${message.author.id} - Ticket: ${ticketChannel.name}`)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (supportbot.TicketDepartments) {
|
||||
TicketMessage.addFields({ name: 'Departments', value: ` **${supportbot.DepartmentTitle_1}**\n **${supportbot.DepartmentTitle_2}**\n **${supportbot.DepartmentTitle_3}**` })
|
||||
}
|
||||
const CloseButton = new Discord.MessageButton()
|
||||
.setCustomId('ticketclose')
|
||||
.setLabel('Close')
|
||||
.setStyle(supportbot.TicketCloseColour)
|
||||
.setEmoji(supportbot.TicketCloseEmoji)
|
||||
|
||||
const LockButton = new Discord.MessageButton()
|
||||
.setCustomId('ticketlock')
|
||||
.setLabel('Lock')
|
||||
.setStyle(supportbot.TicketLockColour)
|
||||
.setEmoji(supportbot.TicketLockEmoji)
|
||||
|
||||
const row2 = new Discord.MessageActionRow()
|
||||
.addComponents(CloseButton, LockButton)
|
||||
|
||||
if(supportbot.TicketDepartments) {
|
||||
|
||||
let DenyArr = [DeptRole1, DeptRole2, DeptRole3]
|
||||
await asyncForEach(DenyArr, (index) => {
|
||||
ticketChannel.permissionOverwrites.create(index, {VIEW_CHANNEL: false})
|
||||
})
|
||||
const Department1Button = new Discord.MessageButton()
|
||||
.setCustomId('Department1')
|
||||
.setLabel(`${supportbot.DepartmentTitle_1}`)
|
||||
.setStyle(supportbot.TicketDept1Colour)
|
||||
.setEmoji(supportbot.TicketDept1Emoji)
|
||||
|
||||
const Department2Button = new Discord.MessageButton()
|
||||
.setCustomId('Department2')
|
||||
.setLabel(`${supportbot.DepartmentTitle_2}`)
|
||||
.setStyle(supportbot.TicketDept2Colour)
|
||||
.setEmoji(supportbot.TicketDept2Emoji)
|
||||
|
||||
const Department3Button = new Discord.MessageButton()
|
||||
.setCustomId('Department3')
|
||||
.setLabel(`${supportbot.DepartmentTitle_3}`)
|
||||
.setStyle(supportbot.TicketDept3Colour)
|
||||
.setEmoji(supportbot.TicketDept3Emoji)
|
||||
|
||||
const row = new Discord.MessageActionRow()
|
||||
.addComponents(Department1Button, Department2Button, Department3Button)
|
||||
|
||||
const m = await ticketChannel.send({embeds: [TicketMessage], components: [row]})
|
||||
|
||||
const filter = i => {
|
||||
i.deferUpdate();
|
||||
return i.user.id === message.author.id;
|
||||
};
|
||||
const collector = m.createMessageComponentCollector({ filter, max: 1, componentType: 'BUTTON', time: 60000 });
|
||||
|
||||
collector.on('collect',async (i) => {
|
||||
|
||||
if(i.customId === 'Department1') {
|
||||
|
||||
ticketChannel.permissionOverwrites.edit(DeptRole1, {
|
||||
VIEW_CHANNEL: true,
|
||||
READ_MESSAGE_HISTORY: true,
|
||||
SEND_MESSAGES: true,
|
||||
})
|
||||
|
||||
if(!supportbot.AllowStaff) {
|
||||
ticketChannel.permissionOverwrites.edit(Staff, { VIEW_CHANNEL: false, READ_MESSAGE_HISTORY: false, SEND_MESSAGES: false })
|
||||
}
|
||||
|
||||
const Department1 = new Discord.MessageEmbed()
|
||||
.setDescription(`> Thank for reaching out to the **${supportbot.DepartmentTitle_1} Department**. Please provide us information regarding your query.`)
|
||||
.setColor(supportbot.EmbedColour)
|
||||
ticketChannel.send({ embeds: [Department1] });
|
||||
|
||||
if (supportbot.AllowTicketMentions) {
|
||||
ticketChannel.send({content: `@here`})
|
||||
}
|
||||
}
|
||||
i.message.edit({embeds: [TicketMessage], components: [row2]})
|
||||
|
||||
if(i.customId === 'Department2') {
|
||||
|
||||
ticketChannel.permissionOverwrites.edit(DeptRole2, {
|
||||
VIEW_CHANNEL: true,
|
||||
READ_MESSAGE_HISTORY: true,
|
||||
SEND_MESSAGES: true,
|
||||
})
|
||||
|
||||
if(!supportbot.AllowStaff) {
|
||||
ticketChannel.permissionOverwrites.edit(Staff, { VIEW_CHANNEL: false, READ_MESSAGE_HISTORY: false, SEND_MESSAGES: false })
|
||||
}
|
||||
|
||||
|
||||
const Department2 = new Discord.MessageEmbed()
|
||||
.setDescription(`> Thank for reaching out to the **${supportbot.DepartmentTitle_2} Department**. Please provide us information regarding your query.`)
|
||||
.setColor(supportbot.EmbedColour)
|
||||
ticketChannel.send({ embeds: [Department2] });
|
||||
|
||||
if (supportbot.AllowTicketMentions) {
|
||||
ticketChannel.send({content: `@here`})
|
||||
}
|
||||
|
||||
i.message.edit({embeds: [TicketMessage], components: [row2]})
|
||||
}
|
||||
if(i.customId === 'Department3') {
|
||||
|
||||
ticketChannel.permissionOverwrites.edit(DeptRole3, {
|
||||
VIEW_CHANNEL: true,
|
||||
READ_MESSAGE_HISTORY: true,
|
||||
SEND_MESSAGES: true,
|
||||
})
|
||||
|
||||
if(!supportbot.AllowStaff) {
|
||||
ticketChannel.permissionOverwrites.edit(Staff, { VIEW_CHANNEL: false, READ_MESSAGE_HISTORY: false, SEND_MESSAGES: false })
|
||||
}
|
||||
|
||||
const Department3 = new Discord.MessageEmbed()
|
||||
.setDescription(`> Thank for reaching out to the **${supportbot.DepartmentTitle_3} Department**. Please provide us information regarding your query.`)
|
||||
.setColor(supportbot.EmbedColour)
|
||||
ticketChannel.send({ embeds: [Department3] });
|
||||
|
||||
if (supportbot.AllowTicketMentions) {
|
||||
ticketChannel.send({content: `@here`})
|
||||
}
|
||||
i.message.edit({embeds: [TicketMessage], components: [row2]})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
ticketChannel.send({embeds: [TicketMessage], components: [row2]})
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
@@ -1,166 +0,0 @@
|
||||
// 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('./Data/supportbot.yml', 'utf8'));
|
||||
const cmdconfig = yaml.load(fs.readFileSync('./Data/commands.yml', 'utf8'));
|
||||
|
||||
const Event = require("../Structures/Event.js");
|
||||
|
||||
module.exports = new Event("interactionCreate", (client, interaction) => {
|
||||
|
||||
if (interaction.isCommand()) return;
|
||||
|
||||
const command = client.commands.find(cmd => cmd.name.toLowerCase() == interaction.commandName);
|
||||
|
||||
if (interaction.user.bot || !interaction.isCommand() || !interaction.guild)
|
||||
return;
|
||||
|
||||
const args = [
|
||||
interaction.commandName,
|
||||
...client.commands
|
||||
.find(cmd => cmd.name.toLowerCase() == interaction.commandName)
|
||||
.slashCommandOptions.map(v => `${interaction.options.get(v.name).value}`)
|
||||
];
|
||||
|
||||
const NotValid = new Discord.MessageEmbed()
|
||||
.setDescription(`:x: \`Invalid Command\` `)
|
||||
.setColor(supportbot.ErrorColour)
|
||||
|
||||
if (!command) return interaction.reply({
|
||||
embeds: [NotValid]
|
||||
});
|
||||
|
||||
const permission = interaction.member.permissions.has(command.permission);
|
||||
|
||||
const ValidPerms = new Discord.MessageEmbed()
|
||||
.setDescription(":x: \`Invalid Permissions\` Do you have the correct permissions to execute this command?")
|
||||
.setColor(supportbot.ErrorColour)
|
||||
|
||||
if (!permission)
|
||||
return interaction.reply({
|
||||
embeds: [ValidPerms]
|
||||
});
|
||||
|
||||
command.run(interaction, args, client);
|
||||
|
||||
if(interaction.isButton()) {
|
||||
if(interaction.customId === 'openticket') {
|
||||
interaction.message.fetch()
|
||||
interaction.deferUpdate()
|
||||
let message = {
|
||||
guild: client.guilds.cache.get(interaction.message.guildId),
|
||||
author: client.users.cache.get(interaction.user.id),
|
||||
content: "N/A",
|
||||
channel: client.channels.cache.get(interaction.message.channelId)
|
||||
}
|
||||
try {
|
||||
const cmd = client.commands.get(cmdconfig.OpenTicket);
|
||||
if(!cmd) return;
|
||||
cmd.run(message, message.content)
|
||||
}
|
||||
catch (error){
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
if(interaction.customId === 'ticketclose') {
|
||||
if(interaction.channel.name.startsWith(`${supportbot.TicketChannel}-`)) {
|
||||
interaction.message.fetch()
|
||||
interaction.deferUpdate()
|
||||
const DeleteTicketEmbed = new Discord.MessageEmbed()
|
||||
.setDescription(supportbot.TicketDeleteMessage)
|
||||
.setColor(supportbot.EmbedColour)
|
||||
interaction.channel.send({embeds: [DeleteTicketEmbed]})
|
||||
|
||||
let logChannel = interaction.guild.channels.cache.find(channel => channel.name === supportbot.TranscriptLog) || interaction.guild.channels.cache.find(channel => channel.id === supportbot.TranscriptLog)
|
||||
|
||||
let user = interaction.user
|
||||
let name = interaction.channel.name;
|
||||
let ticketChannel = interaction.channel
|
||||
let reason = "No Reason Provided."
|
||||
|
||||
interaction.channel.send({content: `**${supportbot.ClosingTicket}**`})
|
||||
.then(() => {
|
||||
const logEmbed = new Discord.MessageEmbed()
|
||||
.setTitle(supportbot.TranscriptTitle)
|
||||
.setColor(supportbot.EmbedColour)
|
||||
.setFooter(supportbot.EmbedFooter)
|
||||
.addField("Closed By", user.username)
|
||||
.addField("Reason", reason)
|
||||
|
||||
interaction.channel.messages.fetch({limit: 100}).then(msgs => {
|
||||
let html = ''
|
||||
|
||||
msgs = msgs.sort( ( a, b ) => a.createdTimestamp - b.createdTimestamp );
|
||||
html += `<strong>Server Name:</strong> ${interaction.guild.name}<br>`
|
||||
html += `<strong>Ticket:</strong> ${ticketChannel.name}<br>`
|
||||
html += `<strong>Message:</strong> ${msgs.size} Messages<br><br><br>`
|
||||
|
||||
|
||||
msgs.forEach(msg => {
|
||||
if(msg.content) {
|
||||
html += `<strong>User:</strong> ${msg.author.tag}<br>`;
|
||||
html += `<strong>Message:</strong> ${msg.content}<br>`;
|
||||
html += `-----<br><br>`;
|
||||
}
|
||||
})
|
||||
|
||||
const TranscriptSavedEmbed = new Discord.MessageEmbed()
|
||||
.setDescription(supportbot.TranscriptSavedMessage)
|
||||
.setColor(supportbot.SuccessColour)
|
||||
interaction.channel.send({embeds: [TranscriptSavedEmbed]})
|
||||
|
||||
let file = new Discord.MessageAttachment( Buffer.from( html ), `${name}.html` )
|
||||
logChannel.send({embeds: [logEmbed], files: [file]})
|
||||
|
||||
setTimeout(() => interaction.channel.delete(), supportbot.TicketDeleteTime)
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
if(interaction.customId === 'ticketlock') {
|
||||
if(interaction.channel.name.startsWith(`${supportbot.TicketChannel}`)) {
|
||||
interaction.message.fetch()
|
||||
interaction.deferUpdate()
|
||||
let Admin = interaction.guild.roles.cache.find(AdminUser => AdminUser.name === supportbot.Admin) || interaction.guild.roles.cache.find(AdminUser => AdminUser.id === supportbot.Admin)
|
||||
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) {
|
||||
interaction.channel.setParent(parent.id)
|
||||
}
|
||||
|
||||
all.forEach(perm => {
|
||||
interaction.channel.permissionOverwrites.delete(perm.id)
|
||||
|
||||
})
|
||||
interaction.channel.permissionOverwrites.create(Admin.id, {VIEW_CHANNEL: true})
|
||||
|
||||
interaction.channel.permissionOverwrites.create(interaction.guild.id, {VIEW_CHANNEL: false})
|
||||
|
||||
|
||||
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.channel.send({embeds: [ArchiveEmbed]})
|
||||
interaction.message.edit({components: [row]})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
@@ -1,50 +0,0 @@
|
||||
// SupportBot | Emerald Services
|
||||
// Message Create Event
|
||||
|
||||
const fs = require("fs");
|
||||
|
||||
const Discord = require("discord.js");
|
||||
const yaml = require('js-yaml');
|
||||
const supportbot = yaml.load(fs.readFileSync('./Data/supportbot.yml', 'utf8'));
|
||||
|
||||
const Event = require("../Structures/Event.js");
|
||||
|
||||
module.exports = new Event("messageCreate", (client, message) => {
|
||||
if (message.author.bot) return;
|
||||
|
||||
if (!message.content.startsWith(client.prefix)) return;
|
||||
|
||||
const args = message.content.substring(client.prefix.length).split(/ +/);
|
||||
|
||||
const command = client.commands.find(cmd => cmd.name == args[0]);
|
||||
|
||||
const NotValid = new Discord.MessageEmbed()
|
||||
.setDescription(`:x: \`${args[0]}\` is an Invalid Command `)
|
||||
.setColor(supportbot.ErrorColour)
|
||||
|
||||
if (!command) return message.reply({
|
||||
embeds: [NotValid]
|
||||
});
|
||||
|
||||
const OnlySlashCmd = new Discord.MessageEmbed()
|
||||
.setDescription(`:warning: That command is only useable as a \`Slash Command\` `)
|
||||
.setColor(supportbot.WarnColour)
|
||||
|
||||
if (!["BOTH", "TEXT"].includes(command.type))
|
||||
return message.reply({
|
||||
embeds: [OnlySlashCmd]
|
||||
})
|
||||
|
||||
const permission = message.member.permissions.has(command.permission, true);
|
||||
|
||||
const ValidPerms = new Discord.MessageEmbed()
|
||||
.setDescription(":x: Do you have the \`${command.permission}\` permissions to execute this command?")
|
||||
.setColor(supportbot.ErrorColour)
|
||||
|
||||
if (!permission)
|
||||
return message.reply(
|
||||
`You do not have the permission \`${command.permission}\` to run this command!`
|
||||
);
|
||||
|
||||
command.run(message, args, client);
|
||||
});
|
||||
@@ -1,93 +0,0 @@
|
||||
// SupportBot | Emerald Services
|
||||
// Ready Event
|
||||
|
||||
const fs = require("fs");
|
||||
|
||||
const Discord = require("discord.js");
|
||||
const yaml = require('js-yaml');
|
||||
const supportbot = yaml.load(fs.readFileSync('./Data/supportbot.yml', 'utf8'));
|
||||
const panelconfig = yaml.load(fs.readFileSync('./Data/ticket-panel.yml', 'utf8'));
|
||||
|
||||
const Event = require("../Structures/Event.js");
|
||||
|
||||
module.exports = new Event("ready", client => {
|
||||
client.user.setActivity(supportbot.BotActivity, {
|
||||
type: supportbot.ActivityType,
|
||||
url: supportbot.StreamingURL
|
||||
});
|
||||
|
||||
console.log(`\u001b[32m`, `┏━━━┓╋╋╋╋╋╋╋╋╋╋╋╋╋┏┓┏━━┓╋╋╋┏┓`)
|
||||
console.log(`\u001b[32m`, `┃┏━┓┃╋╋╋╋╋╋╋╋╋╋╋╋┏┛┗┫┏┓┃╋╋┏┛┗┓`)
|
||||
console.log(`\u001b[32m`, `┃┗━━┳┓┏┳━━┳━━┳━━┳┻┓┏┫┗┛┗┳━┻┓┏┛`)
|
||||
console.log(`\u001b[32m`, `┗━━┓┃┃┃┃┏┓┃┏┓┃┏┓┃┏┫┃┃┏━┓┃┏┓┃┃`)
|
||||
console.log(`\u001b[32m`, `┃┗━┛┃┗┛┃┗┛┃┗┛┃┗┛┃┃┃┗┫┗━┛┃┗┛┃┗┓`)
|
||||
console.log(`\u001b[32m`, `┗━━━┻━━┫┏━┫┏━┻━━┻┛┗━┻━━━┻━━┻━┛`)
|
||||
console.log(`\u001b[32m`, `┗╋╋╋╋╋╋╋┃┃╋┃┃`)
|
||||
console.log(`\u001b[32m`, `╋╋╋╋╋╋╋┗┛╋┗┛`)
|
||||
console.log(` `)
|
||||
console.log(`\u001b[31m`, `――――――――――――――――――――――――――――――――――――――――――――`)
|
||||
console.log(` `)
|
||||
console.log(`\u001b[33m`, `${supportbot.Name} v${supportbot.SupportBot_Version}`, `\u001b[36m`, `Connected to Discord`)
|
||||
console.log(`\u001b[33m`, `Resources:`, `\u001b[36m`, `https://emeraldsrv.dev/resources`)
|
||||
console.log(`\u001b[33m`, `Documentation:`, `\u001b[36m`, `https://supportbot.emeraldsrv.dev`)
|
||||
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`, `――――――――――――――――――――――――――――――――――――――――――――`)
|
||||
|
||||
if (panelconfig.TicketPanel) {
|
||||
let chan1 = client.channels.cache.find(channel => channel.name === panelconfig.Channel)
|
||||
|
||||
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 = yaml.load(fs.readFileSync('./Data/ticket-panel-id.yml', 'utf8'));
|
||||
|
||||
chan1.messages.fetch(panelid.TicketPanelID).catch(async r => {
|
||||
let embed = new Discord.MessageEmbed()
|
||||
.setTitle(`${panelconfig.PanelTitle}`)
|
||||
.setColor(supportbot.SuccessColour)
|
||||
.setFooter(supportbot.EmbedFooter);
|
||||
|
||||
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 = new Discord.MessageButton()
|
||||
.setLabel(panelconfig.ButtonLabel)
|
||||
.setCustomId('openticket')
|
||||
.setStyle(panelconfig.ButtonColour)
|
||||
|
||||
let row = new Discord.MessageActionRow()
|
||||
.addComponents(button)
|
||||
|
||||
|
||||
await chan1.send({
|
||||
embeds: [embed],
|
||||
components: [row]
|
||||
}).then(r => {
|
||||
let data = {
|
||||
"TicketPanelID": `${r.id}`
|
||||
}
|
||||
let yamlStr = yaml.dump(data);
|
||||
fs.writeFileSync('./Data/ticket-panel-id.yml', yamlStr, 'utf8');
|
||||
|
||||
}).catch(e => {
|
||||
console.log("Raw"+e)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
});
|
||||
@@ -1,24 +1,83 @@
|
||||
// ___ _ ___ _
|
||||
// / __> _ _ ___ ___ ___ _ _ _| |_ | . > ___ _| |_
|
||||
// \__ \| | || . \| . \/ . \| '_> | | | . \/ . \ | |
|
||||
// <___/`___|| _/| _/\___/|_| |_| |___/\___/ |_|
|
||||
// |_| |_|
|
||||
// ___ _ ___ _
|
||||
// / __> _ _ ___ ___ ___ _ _ _| |_ | . > ___ _| |_
|
||||
// \__ \| | || . \| . \/ . \| '_> | | | . \/ . \ | |
|
||||
// <___/`___|| _/| _/\___/|_| |_| |___/\___/ |_|
|
||||
// |_| |_|
|
||||
//
|
||||
// SupportBot created by Emerald Services
|
||||
// Installed with MIT License
|
||||
//
|
||||
// Discord Support: https://emeraldsrv.dev/discord
|
||||
// Community Resources: https://emeraldsrv.dev/resources
|
||||
// Community Resources: https://community.emeraldsrv.dev
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const Discord = require("discord.js");
|
||||
const yaml = require('js-yaml');
|
||||
const supportbot = yaml.load(fs.readFileSync('./Data/supportbot.yml', 'utf8'));
|
||||
const yaml = require("js-yaml");
|
||||
const supportbot = yaml.load(
|
||||
fs.readFileSync("./Configs/supportbot.yml", "utf8")
|
||||
);
|
||||
|
||||
const Client = require("./Structures/Client.js");
|
||||
const client = new Client({});
|
||||
const client = new Client({
|
||||
intents: ['Guilds', 'GuildMembers', 'GuildMessages', 'MessageContent']
|
||||
});
|
||||
|
||||
client.start(supportbot.General.Token);
|
||||
|
||||
// SupportBot - New Logging System
|
||||
|
||||
const logTypes = ["Output", "Warn", "Error"];
|
||||
|
||||
if (!fs.existsSync("./Logs")) {
|
||||
fs.mkdirSync("./Logs");
|
||||
}
|
||||
logTypes.forEach((type) => {
|
||||
const dir = `./Logs/${type}`;
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir);
|
||||
}
|
||||
});
|
||||
|
||||
function logToFile(type, data) {
|
||||
const date = new Date().toISOString().split("T")[0];
|
||||
const file = path.join(`./logs/${type}`, `${type}-${date}.log`);
|
||||
fs.appendFileSync(file, `[${new Date().toISOString()}] ${data}\n`);
|
||||
}
|
||||
|
||||
const origLog = console.log;
|
||||
console.log = (...args) => {
|
||||
origLog(...args);
|
||||
logToFile("output", args.join(" "));
|
||||
};
|
||||
|
||||
const origWarn = console.warn;
|
||||
console.warn = (...args) => {
|
||||
origWarn(...args);
|
||||
logToFile("warn", args.join(" "));
|
||||
};
|
||||
|
||||
const origError = console.error;
|
||||
console.error = (...args) => {
|
||||
origError(...args);
|
||||
logToFile("error", args.join(" "));
|
||||
};
|
||||
|
||||
process.on("unhandledRejection", (reason) => {
|
||||
console.error("Unhandled Rejection:", reason);
|
||||
});
|
||||
|
||||
process.on("uncaughtException", (err) => {
|
||||
console.error("Uncaught Exception:", err);
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
client.start(supportbot.Token);
|
||||
|
||||
|
||||
BIN
Binary file not shown.
-1
@@ -1 +0,0 @@
|
||||
../js-yaml/bin/js-yaml.js
|
||||
-297
@@ -1,297 +0,0 @@
|
||||
{
|
||||
"name": "supportbot",
|
||||
"version": "7.2.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"node_modules/@discordjs/builders": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.5.0.tgz",
|
||||
"integrity": "sha512-HP5y4Rqw68o61Qv4qM5tVmDbWi4mdTFftqIOGRo33SNPpLJ1Ga3KEIR2ibKofkmsoQhEpLmopD1AZDs3cKpHuw==",
|
||||
"dependencies": {
|
||||
"@sindresorhus/is": "^4.0.1",
|
||||
"discord-api-types": "^0.22.0",
|
||||
"ow": "^0.27.0",
|
||||
"ts-mixer": "^6.0.0",
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0",
|
||||
"npm": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@discordjs/collection": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.2.1.tgz",
|
||||
"integrity": "sha512-vhxqzzM8gkomw0TYRF3tgx7SwElzUlXT/Aa41O7mOcyN6wIJfj5JmDWaO5XGKsGSsNx7F3i5oIlrucCCWV1Nog==",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@discordjs/form-data": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@discordjs/form-data/-/form-data-3.0.1.tgz",
|
||||
"integrity": "sha512-ZfFsbgEXW71Rw/6EtBdrP5VxBJy4dthyC0tpQKGKmYFImlmmrykO14Za+BiIVduwjte0jXEBlhSKf0MWbFp9Eg==",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/@sapphire/async-queue": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.1.4.tgz",
|
||||
"integrity": "sha512-fFrlF/uWpGOX5djw5Mu2Hnnrunao75WGey0sP0J3jnhmrJ5TAPzHYOmytD5iN/+pMxS+f+u/gezqHa9tPhRHEA==",
|
||||
"engines": {
|
||||
"node": ">=14",
|
||||
"npm": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@sindresorhus/is": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz",
|
||||
"integrity": "sha512-VkE3KLBmJwcCaVARtQpfuKcKv8gcBmUubrfHGF84dXuuW6jgsRYxPtzcIhPyK9WAPpRt2/xY6zkD9MnRaJzSyw==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sindresorhus/is?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "16.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.2.tgz",
|
||||
"integrity": "sha512-zCclL4/rx+W5SQTzFs9wyvvyCwoK9QtBpratqz2IYJ3O8Umrn0m3nsTv0wQBk9sRGpvUe9CwPDrQFB10f1FIjQ=="
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "7.4.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz",
|
||||
"integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
|
||||
},
|
||||
"node_modules/callsites": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/discord-api-types": {
|
||||
"version": "0.22.0",
|
||||
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.22.0.tgz",
|
||||
"integrity": "sha512-l8yD/2zRbZItUQpy7ZxBJwaLX/Bs2TGaCthRppk8Sw24LOIWg12t9JEreezPoYD0SQcC2htNNo27kYEpYW/Srg==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/discord.js": {
|
||||
"version": "13.1.0",
|
||||
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-13.1.0.tgz",
|
||||
"integrity": "sha512-gxO4CXKdHpqA+WKG+f5RNnd3srTDj5uFJHgOathksDE90YNq/Qijkd2WlMgTTMS6AJoEnHxI7G9eDQHCuZ+xDA==",
|
||||
"dependencies": {
|
||||
"@discordjs/builders": "^0.5.0",
|
||||
"@discordjs/collection": "^0.2.1",
|
||||
"@discordjs/form-data": "^3.0.1",
|
||||
"@sapphire/async-queue": "^1.1.4",
|
||||
"@types/ws": "^7.4.7",
|
||||
"discord-api-types": "^0.22.0",
|
||||
"node-fetch": "^2.6.1",
|
||||
"ws": "^7.5.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.6.0",
|
||||
"npm": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dot-prop": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz",
|
||||
"integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==",
|
||||
"dependencies": {
|
||||
"is-obj": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-obj": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
|
||||
"integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||
"dependencies": {
|
||||
"argparse": "^2.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash.isequal": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
||||
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.50.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.50.0.tgz",
|
||||
"integrity": "sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.33",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.33.tgz",
|
||||
"integrity": "sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==",
|
||||
"dependencies": {
|
||||
"mime-db": "1.50.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.6.5",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz",
|
||||
"integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ow": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmjs.org/ow/-/ow-0.27.0.tgz",
|
||||
"integrity": "sha512-SGnrGUbhn4VaUGdU0EJLMwZWSupPmF46hnTRII7aCLCrqixTAC5eKo8kI4/XXf1eaaI8YEVT+3FeGNJI9himAQ==",
|
||||
"dependencies": {
|
||||
"@sindresorhus/is": "^4.0.1",
|
||||
"callsites": "^3.1.0",
|
||||
"dot-prop": "^6.0.1",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"type-fest": "^1.2.1",
|
||||
"vali-date": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
|
||||
},
|
||||
"node_modules/ts-mixer": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.0.tgz",
|
||||
"integrity": "sha512-nXIb1fvdY5CBSrDIblLn73NW0qRDk5yJ0Sk1qPBF560OdJfQp9jhl+0tzcY09OZ9U+6GpeoI9RjwoIKFIoB9MQ=="
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
||||
},
|
||||
"node_modules/type-fest": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz",
|
||||
"integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/vali-date": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz",
|
||||
"integrity": "sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY=",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "7.5.5",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz",
|
||||
"integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==",
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": "^5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
-190
@@ -1,190 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2021 Vlad Frangu
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
<div align="center">
|
||||
<br />
|
||||
<p>
|
||||
<a href="https://discord.js.org"><img src="https://discord.js.org/static/logo.svg" width="546" alt="discord.js" /></a>
|
||||
</p>
|
||||
<br />
|
||||
<p>
|
||||
<a href="https://discord.gg/djs"><img src="https://img.shields.io/discord/222078108977594368?color=5865F2&logo=discord&logoColor=white" alt="Discord server" /></a>
|
||||
<a href="https://github.com/discordjs/builders/actions"><img src="https://github.com/discordjs/builders/workflows/Tests/badge.svg" alt="Test status" /></a>
|
||||
<a href="https://github.com/discordjs/builders/actions"><img src="https://github.com/discordjs/builders/workflows/Lint/badge.svg" alt="Lint status" /></a>
|
||||
<a href="https://codecov.io/gh/discordjs/builders"><img src="https://codecov.io/gh/discordjs/builders/branch/main/graph/badge.svg" alt="Code coverage" /></a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
## Installation
|
||||
|
||||
Install with [npm](https://www.npmjs.com/) / [yarn](https://yarnpkg.com) / [pnpm](https://pnpm.js.org/):
|
||||
|
||||
```sh
|
||||
npm install @discordjs/builders
|
||||
yarn add @discordjs/builders
|
||||
pnpm add @discordjs/builders
|
||||
```
|
||||
|
||||
## Contribution
|
||||
|
||||
See [Contributing Guide](https://github.com/discordjs/builders/blob/main/.github/CONTRIBUTING.md).
|
||||
|
||||
## Examples
|
||||
|
||||
Here are some examples for the builders and utilities you can find in this package:
|
||||
|
||||
- [Slash Command Builders](./docs/examples/Slash%20Command%20Builders.md)
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
export * from './messages/formatters';
|
||||
export * as SlashCommandAssertions from './interactions/slashCommands/Assertions';
|
||||
export * from './interactions/slashCommands/SlashCommandBuilder';
|
||||
export * from './interactions/slashCommands/SlashCommandSubcommands';
|
||||
export * from './interactions/slashCommands/options/boolean';
|
||||
export * from './interactions/slashCommands/options/channel';
|
||||
export * from './interactions/slashCommands/options/integer';
|
||||
export * from './interactions/slashCommands/options/mentionable';
|
||||
export * from './interactions/slashCommands/options/role';
|
||||
export * from './interactions/slashCommands/options/string';
|
||||
export * from './interactions/slashCommands/options/user';
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"/","sources":["index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AAEtC,OAAO,KAAK,sBAAsB,MAAM,yCAAyC,CAAC;AAClF,cAAc,kDAAkD,CAAC;AACjE,cAAc,sDAAsD,CAAC;AACrE,cAAc,8CAA8C,CAAC;AAC7D,cAAc,8CAA8C,CAAC;AAC7D,cAAc,8CAA8C,CAAC;AAC7D,cAAc,kDAAkD,CAAC;AACjE,cAAc,2CAA2C,CAAC;AAC1D,cAAc,6CAA6C,CAAC;AAC5D,cAAc,2CAA2C,CAAC"}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.SlashCommandAssertions = void 0;
|
||||
const tslib_1 = require("tslib");
|
||||
tslib_1.__exportStar(require("./messages/formatters"), exports);
|
||||
exports.SlashCommandAssertions = tslib_1.__importStar(require("./interactions/slashCommands/Assertions"));
|
||||
tslib_1.__exportStar(require("./interactions/slashCommands/SlashCommandBuilder"), exports);
|
||||
tslib_1.__exportStar(require("./interactions/slashCommands/SlashCommandSubcommands"), exports);
|
||||
tslib_1.__exportStar(require("./interactions/slashCommands/options/boolean"), exports);
|
||||
tslib_1.__exportStar(require("./interactions/slashCommands/options/channel"), exports);
|
||||
tslib_1.__exportStar(require("./interactions/slashCommands/options/integer"), exports);
|
||||
tslib_1.__exportStar(require("./interactions/slashCommands/options/mentionable"), exports);
|
||||
tslib_1.__exportStar(require("./interactions/slashCommands/options/role"), exports);
|
||||
tslib_1.__exportStar(require("./interactions/slashCommands/options/string"), exports);
|
||||
tslib_1.__exportStar(require("./interactions/slashCommands/options/user"), exports);
|
||||
//# sourceMappingURL=index.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"/","sources":["index.ts"],"names":[],"mappings":";;;;AAAA,gEAAsC;AAEtC,0GAAkF;AAClF,2FAAiE;AACjE,+FAAqE;AACrE,uFAA6D;AAC7D,uFAA6D;AAC7D,uFAA6D;AAC7D,2FAAiE;AACjE,oFAA0D;AAC1D,sFAA4D;AAC5D,oFAA0D","sourcesContent":["export * from './messages/formatters';\n\nexport * as SlashCommandAssertions from './interactions/slashCommands/Assertions';\nexport * from './interactions/slashCommands/SlashCommandBuilder';\nexport * from './interactions/slashCommands/SlashCommandSubcommands';\nexport * from './interactions/slashCommands/options/boolean';\nexport * from './interactions/slashCommands/options/channel';\nexport * from './interactions/slashCommands/options/integer';\nexport * from './interactions/slashCommands/options/mentionable';\nexport * from './interactions/slashCommands/options/role';\nexport * from './interactions/slashCommands/options/string';\nexport * from './interactions/slashCommands/options/user';\n"]}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
import mod from "./index.js";
|
||||
|
||||
export default mod;
|
||||
export const Faces = mod.Faces;
|
||||
export const SlashCommandAssertions = mod.SlashCommandAssertions;
|
||||
export const SlashCommandBooleanOption = mod.SlashCommandBooleanOption;
|
||||
export const SlashCommandBuilder = mod.SlashCommandBuilder;
|
||||
export const SlashCommandChannelOption = mod.SlashCommandChannelOption;
|
||||
export const SlashCommandIntegerOption = mod.SlashCommandIntegerOption;
|
||||
export const SlashCommandMentionableOption = mod.SlashCommandMentionableOption;
|
||||
export const SlashCommandRoleOption = mod.SlashCommandRoleOption;
|
||||
export const SlashCommandStringOption = mod.SlashCommandStringOption;
|
||||
export const SlashCommandSubcommandBuilder = mod.SlashCommandSubcommandBuilder;
|
||||
export const SlashCommandSubcommandGroupBuilder = mod.SlashCommandSubcommandGroupBuilder;
|
||||
export const SlashCommandUserOption = mod.SlashCommandUserOption;
|
||||
export const TimestampStyles = mod.TimestampStyles;
|
||||
export const blockQuote = mod.blockQuote;
|
||||
export const bold = mod.bold;
|
||||
export const channelMention = mod.channelMention;
|
||||
export const codeBlock = mod.codeBlock;
|
||||
export const formatEmoji = mod.formatEmoji;
|
||||
export const hideLinkEmbed = mod.hideLinkEmbed;
|
||||
export const hyperlink = mod.hyperlink;
|
||||
export const inlineCode = mod.inlineCode;
|
||||
export const italic = mod.italic;
|
||||
export const memberNicknameMention = mod.memberNicknameMention;
|
||||
export const quote = mod.quote;
|
||||
export const roleMention = mod.roleMention;
|
||||
export const spoiler = mod.spoiler;
|
||||
export const strikethrough = mod.strikethrough;
|
||||
export const time = mod.time;
|
||||
export const underscore = mod.underscore;
|
||||
export const userMention = mod.userMention;
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
import type { APIApplicationCommandOptionChoice } from 'discord-api-types/v9';
|
||||
import type { SlashCommandOptionBase } from './mixins/CommandOptionBase';
|
||||
import type { ToAPIApplicationCommandOptions } from './SlashCommandBuilder';
|
||||
import type { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder } from './SlashCommandSubcommands';
|
||||
export declare function validateRequiredParameters(name: string, description: string, options: ToAPIApplicationCommandOptions[]): void;
|
||||
export declare function validateName(name: unknown): asserts name is string;
|
||||
export declare function validateDescription(description: unknown): asserts description is string;
|
||||
export declare function validateMaxOptionsLength(options: unknown): asserts options is ToAPIApplicationCommandOptions[];
|
||||
export declare function validateMaxChoicesLength(choices: APIApplicationCommandOptionChoice[]): void;
|
||||
export declare function assertReturnOfBuilder<T extends SlashCommandOptionBase | SlashCommandSubcommandBuilder | SlashCommandSubcommandGroupBuilder>(input: unknown, ExpectedInstanceOf: new () => T): asserts input is T;
|
||||
//# sourceMappingURL=Assertions.d.ts.map
|
||||
Generated
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"Assertions.d.ts","sourceRoot":"/","sources":["interactions/slashCommands/Assertions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iCAAiC,EAAE,MAAM,sBAAsB,CAAC;AAE9E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACzE,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,uBAAuB,CAAC;AAC5E,OAAO,KAAK,EAAE,6BAA6B,EAAE,kCAAkC,EAAE,MAAM,2BAA2B,CAAC;AAEnH,wBAAgB,0BAA0B,CACzC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,8BAA8B,EAAE,QAUzC;AAUD,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,IAAI,MAAM,CAElE;AAID,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,IAAI,MAAM,CAEvF;AAID,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,8BAA8B,EAAE,CAE9G;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,iCAAiC,EAAE,QAEpF;AAED,wBAAgB,qBAAqB,CACpC,CAAC,SAAS,sBAAsB,GAAG,6BAA6B,GAAG,kCAAkC,EACpG,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,UAAU,CAAC,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC,CAuBrE"}
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.assertReturnOfBuilder = exports.validateMaxChoicesLength = exports.validateMaxOptionsLength = exports.validateDescription = exports.validateName = exports.validateRequiredParameters = void 0;
|
||||
const tslib_1 = require("tslib");
|
||||
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
|
||||
const ow_1 = tslib_1.__importDefault(require("ow"));
|
||||
function validateRequiredParameters(name, description, options) {
|
||||
// Assert name matches all conditions
|
||||
validateName(name);
|
||||
// Assert description conditions
|
||||
validateDescription(description);
|
||||
// Assert options conditions
|
||||
validateMaxOptionsLength(options);
|
||||
}
|
||||
exports.validateRequiredParameters = validateRequiredParameters;
|
||||
const namePredicate = ow_1.default.string.lowercase
|
||||
.minLength(1)
|
||||
.maxLength(32)
|
||||
.addValidator({
|
||||
message: (value, label) => `Expected ${label} to match "^[\\p{L}\\p{N}_-]+$", got ${value} instead`,
|
||||
validator: (value) => /^[\p{L}\p{N}_-]+$/u.test(value),
|
||||
});
|
||||
function validateName(name) {
|
||||
ow_1.default(name, 'name', namePredicate);
|
||||
}
|
||||
exports.validateName = validateName;
|
||||
const descriptionPredicate = ow_1.default.string.minLength(1).maxLength(100);
|
||||
function validateDescription(description) {
|
||||
ow_1.default(description, 'description', descriptionPredicate);
|
||||
}
|
||||
exports.validateDescription = validateDescription;
|
||||
const maxArrayLengthPredicate = ow_1.default.array.maxLength(25);
|
||||
function validateMaxOptionsLength(options) {
|
||||
ow_1.default(options, 'options', maxArrayLengthPredicate);
|
||||
}
|
||||
exports.validateMaxOptionsLength = validateMaxOptionsLength;
|
||||
function validateMaxChoicesLength(choices) {
|
||||
ow_1.default(choices, 'choices', maxArrayLengthPredicate);
|
||||
}
|
||||
exports.validateMaxChoicesLength = validateMaxChoicesLength;
|
||||
function assertReturnOfBuilder(input, ExpectedInstanceOf) {
|
||||
const instanceName = ExpectedInstanceOf.name;
|
||||
if (is_1.default.nullOrUndefined(input)) {
|
||||
throw new TypeError(`Expected to receive a ${instanceName} builder, got ${input === null ? 'null' : 'undefined'} instead.`);
|
||||
}
|
||||
if (is_1.default.primitive(input)) {
|
||||
throw new TypeError(`Expected to receive a ${instanceName} builder, got a primitive (${typeof input}) instead.`);
|
||||
}
|
||||
if (!(input instanceof ExpectedInstanceOf)) {
|
||||
const casted = input;
|
||||
const constructorName = is_1.default.function_(input) ? input.name : casted.constructor.name;
|
||||
const stringTag = Reflect.get(casted, Symbol.toStringTag);
|
||||
const fullResultName = stringTag ? `${constructorName} [${stringTag}]` : constructorName;
|
||||
throw new TypeError(`Expected to receive a ${instanceName} builder, got ${fullResultName} instead.`);
|
||||
}
|
||||
}
|
||||
exports.assertReturnOfBuilder = assertReturnOfBuilder;
|
||||
//# sourceMappingURL=Assertions.js.map
|
||||
Generated
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"Assertions.js","sourceRoot":"/","sources":["interactions/slashCommands/Assertions.ts"],"names":[],"mappings":";;;;AAAA,kEAAkC;AAElC,oDAAoB;AAKpB,SAAgB,0BAA0B,CACzC,IAAY,EACZ,WAAmB,EACnB,OAAyC;IAEzC,qCAAqC;IACrC,YAAY,CAAC,IAAI,CAAC,CAAC;IAEnB,gCAAgC;IAChC,mBAAmB,CAAC,WAAW,CAAC,CAAC;IAEjC,4BAA4B;IAC5B,wBAAwB,CAAC,OAAO,CAAC,CAAC;AACnC,CAAC;AAbD,gEAaC;AAED,MAAM,aAAa,GAAG,YAAE,CAAC,MAAM,CAAC,SAAS;KACvC,SAAS,CAAC,CAAC,CAAC;KACZ,SAAS,CAAC,EAAE,CAAC;KACb,YAAY,CAAC;IACb,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,YAAY,KAAM,wCAAwC,KAAK,UAAU;IACpG,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC;CACtD,CAAC,CAAC;AAEJ,SAAgB,YAAY,CAAC,IAAa;IACzC,YAAE,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;AACjC,CAAC;AAFD,oCAEC;AAED,MAAM,oBAAoB,GAAG,YAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;AAEnE,SAAgB,mBAAmB,CAAC,WAAoB;IACvD,YAAE,CAAC,WAAW,EAAE,aAAa,EAAE,oBAAoB,CAAC,CAAC;AACtD,CAAC;AAFD,kDAEC;AAED,MAAM,uBAAuB,GAAG,YAAE,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;AAEvD,SAAgB,wBAAwB,CAAC,OAAgB;IACxD,YAAE,CAAC,OAAO,EAAE,SAAS,EAAE,uBAAuB,CAAC,CAAC;AACjD,CAAC;AAFD,4DAEC;AAED,SAAgB,wBAAwB,CAAC,OAA4C;IACpF,YAAE,CAAC,OAAO,EAAE,SAAS,EAAE,uBAAuB,CAAC,CAAC;AACjD,CAAC;AAFD,4DAEC;AAED,SAAgB,qBAAqB,CAEnC,KAAc,EAAE,kBAA+B;IAChD,MAAM,YAAY,GAAG,kBAAkB,CAAC,IAAI,CAAC;IAE7C,IAAI,YAAE,CAAC,eAAe,CAAC,KAAK,CAAC,EAAE;QAC9B,MAAM,IAAI,SAAS,CAClB,yBAAyB,YAAY,iBAAiB,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,WAAW,CACtG,CAAC;KACF;IAED,IAAI,YAAE,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE;QACxB,MAAM,IAAI,SAAS,CAAC,yBAAyB,YAAY,8BAA8B,OAAO,KAAK,YAAY,CAAC,CAAC;KACjH;IAED,IAAI,CAAC,CAAC,KAAK,YAAY,kBAAkB,CAAC,EAAE;QAC3C,MAAM,MAAM,GAAG,KAAqC,CAAC;QAErD,MAAM,eAAe,GAAG,YAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC;QACnF,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,WAAW,CAAuB,CAAC;QAEhF,MAAM,cAAc,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,eAAe,KAAK,SAAS,GAAG,CAAC,CAAC,CAAC,eAAe,CAAC;QAEzF,MAAM,IAAI,SAAS,CAAC,yBAAyB,YAAY,iBAAiB,cAAc,WAAW,CAAC,CAAC;KACrG;AACF,CAAC;AAzBD,sDAyBC","sourcesContent":["import is from '@sindresorhus/is';\nimport type { APIApplicationCommandOptionChoice } from 'discord-api-types/v9';\nimport ow from 'ow';\nimport type { SlashCommandOptionBase } from './mixins/CommandOptionBase';\nimport type { ToAPIApplicationCommandOptions } from './SlashCommandBuilder';\nimport type { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder } from './SlashCommandSubcommands';\n\nexport function validateRequiredParameters(\n\tname: string,\n\tdescription: string,\n\toptions: ToAPIApplicationCommandOptions[],\n) {\n\t// Assert name matches all conditions\n\tvalidateName(name);\n\n\t// Assert description conditions\n\tvalidateDescription(description);\n\n\t// Assert options conditions\n\tvalidateMaxOptionsLength(options);\n}\n\nconst namePredicate = ow.string.lowercase\n\t.minLength(1)\n\t.maxLength(32)\n\t.addValidator({\n\t\tmessage: (value, label) => `Expected ${label!} to match \"^[\\\\p{L}\\\\p{N}_-]+$\", got ${value} instead`,\n\t\tvalidator: (value) => /^[\\p{L}\\p{N}_-]+$/u.test(value),\n\t});\n\nexport function validateName(name: unknown): asserts name is string {\n\tow(name, 'name', namePredicate);\n}\n\nconst descriptionPredicate = ow.string.minLength(1).maxLength(100);\n\nexport function validateDescription(description: unknown): asserts description is string {\n\tow(description, 'description', descriptionPredicate);\n}\n\nconst maxArrayLengthPredicate = ow.array.maxLength(25);\n\nexport function validateMaxOptionsLength(options: unknown): asserts options is ToAPIApplicationCommandOptions[] {\n\tow(options, 'options', maxArrayLengthPredicate);\n}\n\nexport function validateMaxChoicesLength(choices: APIApplicationCommandOptionChoice[]) {\n\tow(choices, 'choices', maxArrayLengthPredicate);\n}\n\nexport function assertReturnOfBuilder<\n\tT extends SlashCommandOptionBase | SlashCommandSubcommandBuilder | SlashCommandSubcommandGroupBuilder,\n>(input: unknown, ExpectedInstanceOf: new () => T): asserts input is T {\n\tconst instanceName = ExpectedInstanceOf.name;\n\n\tif (is.nullOrUndefined(input)) {\n\t\tthrow new TypeError(\n\t\t\t`Expected to receive a ${instanceName} builder, got ${input === null ? 'null' : 'undefined'} instead.`,\n\t\t);\n\t}\n\n\tif (is.primitive(input)) {\n\t\tthrow new TypeError(`Expected to receive a ${instanceName} builder, got a primitive (${typeof input}) instead.`);\n\t}\n\n\tif (!(input instanceof ExpectedInstanceOf)) {\n\t\tconst casted = input as Record<PropertyKey, unknown>;\n\n\t\tconst constructorName = is.function_(input) ? input.name : casted.constructor.name;\n\t\tconst stringTag = Reflect.get(casted, Symbol.toStringTag) as string | undefined;\n\n\t\tconst fullResultName = stringTag ? `${constructorName} [${stringTag}]` : constructorName;\n\n\t\tthrow new TypeError(`Expected to receive a ${instanceName} builder, got ${fullResultName} instead.`);\n\t}\n}\n"]}
|
||||
Generated
Vendored
-50
@@ -1,50 +0,0 @@
|
||||
import type { APIApplicationCommandOption } from 'discord-api-types/v9';
|
||||
import { SharedNameAndDescription } from './mixins/NameAndDescription';
|
||||
import { SharedSlashCommandOptions } from './mixins/CommandOptions';
|
||||
import { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder } from './SlashCommandSubcommands';
|
||||
export declare class SlashCommandBuilder {
|
||||
/**
|
||||
* The name of this slash command
|
||||
*/
|
||||
readonly name: string;
|
||||
/**
|
||||
* The description of this slash command
|
||||
*/
|
||||
readonly description: string;
|
||||
/**
|
||||
* The options of this slash command
|
||||
*/
|
||||
readonly options: ToAPIApplicationCommandOptions[];
|
||||
/**
|
||||
* Returns the final data that should be sent to Discord.
|
||||
*
|
||||
* **Note:** Calling this function will validate required properties based on their conditions.
|
||||
*/
|
||||
toJSON(): {
|
||||
name: string;
|
||||
description: string;
|
||||
options: APIApplicationCommandOption[];
|
||||
};
|
||||
/**
|
||||
* Adds a new subcommand group to this command
|
||||
* @param input A function that returns a subcommand group builder, or an already built builder
|
||||
*/
|
||||
addSubcommandGroup(input: SlashCommandSubcommandGroupBuilder | ((subcommandGroup: SlashCommandSubcommandGroupBuilder) => SlashCommandSubcommandGroupBuilder)): SlashCommandSubcommandGroupsOnlyBuilder;
|
||||
/**
|
||||
* Adds a new subcommand to this command
|
||||
* @param input A function that returns a subcommand builder, or an already built builder
|
||||
*/
|
||||
addSubcommand(input: SlashCommandSubcommandBuilder | ((subcommandGroup: SlashCommandSubcommandBuilder) => SlashCommandSubcommandBuilder)): SlashCommandSubcommandsOnlyBuilder;
|
||||
}
|
||||
export interface SlashCommandBuilder extends SharedNameAndDescription, SharedSlashCommandOptions {
|
||||
}
|
||||
export interface SlashCommandSubcommandsOnlyBuilder extends SharedNameAndDescription, Pick<SlashCommandBuilder, 'toJSON' | 'addSubcommand'> {
|
||||
}
|
||||
export interface SlashCommandSubcommandGroupsOnlyBuilder extends SharedNameAndDescription, Pick<SlashCommandBuilder, 'toJSON' | 'addSubcommandGroup'> {
|
||||
}
|
||||
export interface SlashCommandOptionsOnlyBuilder extends SharedNameAndDescription, SharedSlashCommandOptions, Pick<SlashCommandBuilder, 'toJSON'> {
|
||||
}
|
||||
export interface ToAPIApplicationCommandOptions {
|
||||
toJSON(): APIApplicationCommandOption;
|
||||
}
|
||||
//# sourceMappingURL=SlashCommandBuilder.d.ts.map
|
||||
Generated
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"SlashCommandBuilder.d.ts","sourceRoot":"/","sources":["interactions/slashCommands/SlashCommandBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAC;AAGxE,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,6BAA6B,EAAE,kCAAkC,EAAE,MAAM,2BAA2B,CAAC;AAE9G,qBACa,mBAAmB;IAC/B;;OAEG;IACH,SAAgB,IAAI,EAAE,MAAM,CAAc;IAE1C;;OAEG;IACH,SAAgB,WAAW,EAAE,MAAM,CAAc;IAEjD;;OAEG;IACH,SAAgB,OAAO,EAAE,8BAA8B,EAAE,CAAM;IAE/D;;;;OAIG;IACI,MAAM;;;;;IASb;;;OAGG;IACI,kBAAkB,CACxB,KAAK,EACF,kCAAkC,GAClC,CAAC,CAAC,eAAe,EAAE,kCAAkC,KAAK,kCAAkC,CAAC,GAC9F,uCAAuC;IAqB1C;;;OAGG;IACI,aAAa,CACnB,KAAK,EACF,6BAA6B,GAC7B,CAAC,CAAC,eAAe,EAAE,6BAA6B,KAAK,6BAA6B,CAAC,GACpF,kCAAkC;CAqBrC;AAED,MAAM,WAAW,mBAAoB,SAAQ,wBAAwB,EAAE,yBAAyB;CAAG;AAEnG,MAAM,WAAW,kCAChB,SAAQ,wBAAwB,EAC/B,IAAI,CAAC,mBAAmB,EAAE,QAAQ,GAAG,eAAe,CAAC;CAAG;AAE1D,MAAM,WAAW,uCAChB,SAAQ,wBAAwB,EAC/B,IAAI,CAAC,mBAAmB,EAAE,QAAQ,GAAG,oBAAoB,CAAC;CAAG;AAE/D,MAAM,WAAW,8BAChB,SAAQ,wBAAwB,EAC/B,yBAAyB,EACzB,IAAI,CAAC,mBAAmB,EAAE,QAAQ,CAAC;CAAG;AAExC,MAAM,WAAW,8BAA8B;IAC9C,MAAM,IAAI,2BAA2B,CAAC;CACtC"}
|
||||
Generated
Vendored
-96
@@ -1,96 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.SlashCommandBuilder = void 0;
|
||||
const tslib_1 = require("tslib");
|
||||
const ts_mixer_1 = require("ts-mixer");
|
||||
const Assertions_1 = require("./Assertions");
|
||||
const NameAndDescription_1 = require("./mixins/NameAndDescription");
|
||||
const CommandOptions_1 = require("./mixins/CommandOptions");
|
||||
const SlashCommandSubcommands_1 = require("./SlashCommandSubcommands");
|
||||
let SlashCommandBuilder = class SlashCommandBuilder {
|
||||
constructor() {
|
||||
/**
|
||||
* The name of this slash command
|
||||
*/
|
||||
Object.defineProperty(this, "name", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: undefined
|
||||
});
|
||||
/**
|
||||
* The description of this slash command
|
||||
*/
|
||||
Object.defineProperty(this, "description", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: undefined
|
||||
});
|
||||
/**
|
||||
* The options of this slash command
|
||||
*/
|
||||
Object.defineProperty(this, "options", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: []
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Returns the final data that should be sent to Discord.
|
||||
*
|
||||
* **Note:** Calling this function will validate required properties based on their conditions.
|
||||
*/
|
||||
toJSON() {
|
||||
Assertions_1.validateRequiredParameters(this.name, this.description, this.options);
|
||||
return {
|
||||
name: this.name,
|
||||
description: this.description,
|
||||
options: this.options.map((option) => option.toJSON()),
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Adds a new subcommand group to this command
|
||||
* @param input A function that returns a subcommand group builder, or an already built builder
|
||||
*/
|
||||
addSubcommandGroup(input) {
|
||||
const { options } = this;
|
||||
// First, assert options conditions - we cannot have more than 25 options
|
||||
Assertions_1.validateMaxOptionsLength(options);
|
||||
// Make sure there is no subcommand at the root level - if there is, throw
|
||||
const hasSubcommands = options.some((item) => item instanceof SlashCommandSubcommands_1.SlashCommandSubcommandBuilder);
|
||||
if (hasSubcommands)
|
||||
throw new RangeError(`You cannot mix subcommands and subcommand groups at the root level.`);
|
||||
// Get the final result
|
||||
const result = typeof input === 'function' ? input(new SlashCommandSubcommands_1.SlashCommandSubcommandGroupBuilder()) : input;
|
||||
Assertions_1.assertReturnOfBuilder(result, SlashCommandSubcommands_1.SlashCommandSubcommandGroupBuilder);
|
||||
// Push it
|
||||
options.push(result);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Adds a new subcommand to this command
|
||||
* @param input A function that returns a subcommand builder, or an already built builder
|
||||
*/
|
||||
addSubcommand(input) {
|
||||
const { options } = this;
|
||||
// First, assert options conditions - we cannot have more than 25 options
|
||||
Assertions_1.validateMaxOptionsLength(options);
|
||||
// Make sure there is no subcommand at the root level - if there is, throw
|
||||
const hasSubcommandGroups = options.some((item) => item instanceof SlashCommandSubcommands_1.SlashCommandSubcommandGroupBuilder);
|
||||
if (hasSubcommandGroups)
|
||||
throw new RangeError(`You cannot mix subcommands and subcommand groups at the root level.`);
|
||||
// Get the final result
|
||||
const result = typeof input === 'function' ? input(new SlashCommandSubcommands_1.SlashCommandSubcommandBuilder()) : input;
|
||||
Assertions_1.assertReturnOfBuilder(result, SlashCommandSubcommands_1.SlashCommandSubcommandBuilder);
|
||||
// Push it
|
||||
options.push(result);
|
||||
return this;
|
||||
}
|
||||
};
|
||||
SlashCommandBuilder = tslib_1.__decorate([
|
||||
ts_mixer_1.mix(CommandOptions_1.SharedSlashCommandOptions, NameAndDescription_1.SharedNameAndDescription)
|
||||
], SlashCommandBuilder);
|
||||
exports.SlashCommandBuilder = SlashCommandBuilder;
|
||||
//# sourceMappingURL=SlashCommandBuilder.js.map
|
||||
Generated
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"SlashCommandBuilder.js","sourceRoot":"/","sources":["interactions/slashCommands/SlashCommandBuilder.ts"],"names":[],"mappings":";;;;AACA,uCAA+B;AAC/B,6CAA2G;AAC3G,oEAAuE;AACvE,4DAAoE;AACpE,uEAA8G;AAG9G,IAAa,mBAAmB,GAAhC,MAAa,mBAAmB;IAAhC;QACC;;WAEG;QACH;;;;mBAA+B,SAAU;WAAC;QAE1C;;WAEG;QACH;;;;mBAAsC,SAAU;WAAC;QAEjD;;WAEG;QACH;;;;mBAA4D,EAAE;WAAC;IA0EhE,CAAC;IAxEA;;;;OAIG;IACI,MAAM;QACZ,uCAA0B,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACtE,OAAO;YACN,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;SACtD,CAAC;IACH,CAAC;IAED;;;OAGG;IACI,kBAAkB,CACxB,KAEgG;QAEhG,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;QAEzB,yEAAyE;QACzE,qCAAwB,CAAC,OAAO,CAAC,CAAC;QAElC,0EAA0E;QAC1E,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,YAAY,uDAA6B,CAAC,CAAC;QAC7F,IAAI,cAAc;YAAE,MAAM,IAAI,UAAU,CAAC,qEAAqE,CAAC,CAAC;QAEhH,uBAAuB;QACvB,MAAM,MAAM,GAAG,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,4DAAkC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QAErG,kCAAqB,CAAC,MAAM,EAAE,4DAAkC,CAAC,CAAC;QAElE,UAAU;QACV,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAErB,OAAO,IAAI,CAAC;IACb,CAAC;IAED;;;OAGG;IACI,aAAa,CACnB,KAEsF;QAEtF,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;QAEzB,yEAAyE;QACzE,qCAAwB,CAAC,OAAO,CAAC,CAAC;QAElC,0EAA0E;QAC1E,MAAM,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,YAAY,4DAAkC,CAAC,CAAC;QACvG,IAAI,mBAAmB;YACtB,MAAM,IAAI,UAAU,CAAC,qEAAqE,CAAC,CAAC;QAE7F,uBAAuB;QACvB,MAAM,MAAM,GAAG,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,uDAA6B,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QAEhG,kCAAqB,CAAC,MAAM,EAAE,uDAA6B,CAAC,CAAC;QAE7D,UAAU;QACV,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAErB,OAAO,IAAI,CAAC;IACb,CAAC;CACD,CAAA;AAxFY,mBAAmB;IAD/B,cAAG,CAAC,0CAAyB,EAAE,6CAAwB,CAAC;GAC5C,mBAAmB,CAwF/B;AAxFY,kDAAmB","sourcesContent":["import type { APIApplicationCommandOption } from 'discord-api-types/v9';\nimport { mix } from 'ts-mixer';\nimport { assertReturnOfBuilder, validateMaxOptionsLength, validateRequiredParameters } from './Assertions';\nimport { SharedNameAndDescription } from './mixins/NameAndDescription';\nimport { SharedSlashCommandOptions } from './mixins/CommandOptions';\nimport { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder } from './SlashCommandSubcommands';\n\n@mix(SharedSlashCommandOptions, SharedNameAndDescription)\nexport class SlashCommandBuilder {\n\t/**\n\t * The name of this slash command\n\t */\n\tpublic readonly name: string = undefined!;\n\n\t/**\n\t * The description of this slash command\n\t */\n\tpublic readonly description: string = undefined!;\n\n\t/**\n\t * The options of this slash command\n\t */\n\tpublic readonly options: ToAPIApplicationCommandOptions[] = [];\n\n\t/**\n\t * Returns the final data that should be sent to Discord.\n\t *\n\t * **Note:** Calling this function will validate required properties based on their conditions.\n\t */\n\tpublic toJSON() {\n\t\tvalidateRequiredParameters(this.name, this.description, this.options);\n\t\treturn {\n\t\t\tname: this.name,\n\t\t\tdescription: this.description,\n\t\t\toptions: this.options.map((option) => option.toJSON()),\n\t\t};\n\t}\n\n\t/**\n\t * Adds a new subcommand group to this command\n\t * @param input A function that returns a subcommand group builder, or an already built builder\n\t */\n\tpublic addSubcommandGroup(\n\t\tinput:\n\t\t\t| SlashCommandSubcommandGroupBuilder\n\t\t\t| ((subcommandGroup: SlashCommandSubcommandGroupBuilder) => SlashCommandSubcommandGroupBuilder),\n\t): SlashCommandSubcommandGroupsOnlyBuilder {\n\t\tconst { options } = this;\n\n\t\t// First, assert options conditions - we cannot have more than 25 options\n\t\tvalidateMaxOptionsLength(options);\n\n\t\t// Make sure there is no subcommand at the root level - if there is, throw\n\t\tconst hasSubcommands = options.some((item) => item instanceof SlashCommandSubcommandBuilder);\n\t\tif (hasSubcommands) throw new RangeError(`You cannot mix subcommands and subcommand groups at the root level.`);\n\n\t\t// Get the final result\n\t\tconst result = typeof input === 'function' ? input(new SlashCommandSubcommandGroupBuilder()) : input;\n\n\t\tassertReturnOfBuilder(result, SlashCommandSubcommandGroupBuilder);\n\n\t\t// Push it\n\t\toptions.push(result);\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds a new subcommand to this command\n\t * @param input A function that returns a subcommand builder, or an already built builder\n\t */\n\tpublic addSubcommand(\n\t\tinput:\n\t\t\t| SlashCommandSubcommandBuilder\n\t\t\t| ((subcommandGroup: SlashCommandSubcommandBuilder) => SlashCommandSubcommandBuilder),\n\t): SlashCommandSubcommandsOnlyBuilder {\n\t\tconst { options } = this;\n\n\t\t// First, assert options conditions - we cannot have more than 25 options\n\t\tvalidateMaxOptionsLength(options);\n\n\t\t// Make sure there is no subcommand at the root level - if there is, throw\n\t\tconst hasSubcommandGroups = options.some((item) => itLine truncated
|
||||
Generated
Vendored
-64
@@ -1,64 +0,0 @@
|
||||
import { ApplicationCommandOptionType } from 'discord-api-types/v9';
|
||||
import { SharedSlashCommandOptions } from './mixins/CommandOptions';
|
||||
import { SharedNameAndDescription } from './mixins/NameAndDescription';
|
||||
import type { ToAPIApplicationCommandOptions } from './SlashCommandBuilder';
|
||||
/**
|
||||
* Represents a folder for subcommands
|
||||
*
|
||||
* For more information, go to https://discord.com/developers/docs/interactions/slash-commands#subcommands-and-subcommand-groups
|
||||
*/
|
||||
export declare class SlashCommandSubcommandGroupBuilder implements ToAPIApplicationCommandOptions {
|
||||
/**
|
||||
* The name of this subcommand group
|
||||
*/
|
||||
readonly name: string;
|
||||
/**
|
||||
* The description of this subcommand group
|
||||
*/
|
||||
readonly description: string;
|
||||
/**
|
||||
* The subcommands part of this subcommand group
|
||||
*/
|
||||
readonly options: ToAPIApplicationCommandOptions[];
|
||||
/**
|
||||
* Adds a new subcommand to this group
|
||||
* @param input A function that returns a subcommand builder, or an already built builder
|
||||
*/
|
||||
addSubcommand(input: SlashCommandSubcommandBuilder | ((subcommandGroup: SlashCommandSubcommandBuilder) => SlashCommandSubcommandBuilder)): this;
|
||||
toJSON(): {
|
||||
type: ApplicationCommandOptionType;
|
||||
name: string;
|
||||
description: string;
|
||||
options: import("discord-api-types/v9").APIApplicationCommandOption[];
|
||||
};
|
||||
}
|
||||
export interface SlashCommandSubcommandGroupBuilder extends SharedNameAndDescription {
|
||||
}
|
||||
/**
|
||||
* Represents a subcommand
|
||||
*
|
||||
* For more information, go to https://discord.com/developers/docs/interactions/slash-commands#subcommands-and-subcommand-groups
|
||||
*/
|
||||
export declare class SlashCommandSubcommandBuilder implements ToAPIApplicationCommandOptions {
|
||||
/**
|
||||
* The name of this subcommand
|
||||
*/
|
||||
readonly name: string;
|
||||
/**
|
||||
* The description of this subcommand
|
||||
*/
|
||||
readonly description: string;
|
||||
/**
|
||||
* The options of this subcommand
|
||||
*/
|
||||
readonly options: ToAPIApplicationCommandOptions[];
|
||||
toJSON(): {
|
||||
type: ApplicationCommandOptionType;
|
||||
name: string;
|
||||
description: string;
|
||||
options: import("discord-api-types/v9").APIApplicationCommandOption[];
|
||||
};
|
||||
}
|
||||
export interface SlashCommandSubcommandBuilder extends SharedNameAndDescription, SharedSlashCommandOptions<false> {
|
||||
}
|
||||
//# sourceMappingURL=SlashCommandSubcommands.d.ts.map
|
||||
Generated
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"SlashCommandSubcommands.d.ts","sourceRoot":"/","sources":["interactions/slashCommands/SlashCommandSubcommands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAC;AAGpE,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,uBAAuB,CAAC;AAE5E;;;;GAIG;AACH,qBACa,kCAAmC,YAAW,8BAA8B;IACxF;;OAEG;IACH,SAAgB,IAAI,EAAE,MAAM,CAAc;IAE1C;;OAEG;IACH,SAAgB,WAAW,EAAE,MAAM,CAAc;IAEjD;;OAEG;IACH,SAAgB,OAAO,EAAE,8BAA8B,EAAE,CAAM;IAE/D;;;OAGG;IACI,aAAa,CACnB,KAAK,EACF,6BAA6B,GAC7B,CAAC,CAAC,eAAe,EAAE,6BAA6B,KAAK,6BAA6B,CAAC;IAkBhF,MAAM;;;;;;CASb;AAED,MAAM,WAAW,kCAAmC,SAAQ,wBAAwB;CAAG;AAEvF;;;;GAIG;AACH,qBACa,6BAA8B,YAAW,8BAA8B;IACnF;;OAEG;IACH,SAAgB,IAAI,EAAE,MAAM,CAAc;IAE1C;;OAEG;IACH,SAAgB,WAAW,EAAE,MAAM,CAAc;IAEjD;;OAEG;IACH,SAAgB,OAAO,EAAE,8BAA8B,EAAE,CAAM;IAExD,MAAM;;;;;;CASb;AAED,MAAM,WAAW,6BAA8B,SAAQ,wBAAwB,EAAE,yBAAyB,CAAC,KAAK,CAAC;CAAG"}
|
||||
Generated
Vendored
-123
@@ -1,123 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.SlashCommandSubcommandBuilder = exports.SlashCommandSubcommandGroupBuilder = void 0;
|
||||
const tslib_1 = require("tslib");
|
||||
require("discord-api-types/v9");
|
||||
const ts_mixer_1 = require("ts-mixer");
|
||||
const Assertions_1 = require("./Assertions");
|
||||
const CommandOptions_1 = require("./mixins/CommandOptions");
|
||||
const NameAndDescription_1 = require("./mixins/NameAndDescription");
|
||||
/**
|
||||
* Represents a folder for subcommands
|
||||
*
|
||||
* For more information, go to https://discord.com/developers/docs/interactions/slash-commands#subcommands-and-subcommand-groups
|
||||
*/
|
||||
let SlashCommandSubcommandGroupBuilder = class SlashCommandSubcommandGroupBuilder {
|
||||
constructor() {
|
||||
/**
|
||||
* The name of this subcommand group
|
||||
*/
|
||||
Object.defineProperty(this, "name", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: undefined
|
||||
});
|
||||
/**
|
||||
* The description of this subcommand group
|
||||
*/
|
||||
Object.defineProperty(this, "description", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: undefined
|
||||
});
|
||||
/**
|
||||
* The subcommands part of this subcommand group
|
||||
*/
|
||||
Object.defineProperty(this, "options", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: []
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Adds a new subcommand to this group
|
||||
* @param input A function that returns a subcommand builder, or an already built builder
|
||||
*/
|
||||
addSubcommand(input) {
|
||||
const { options } = this;
|
||||
// First, assert options conditions - we cannot have more than 25 options
|
||||
Assertions_1.validateMaxOptionsLength(options);
|
||||
// Get the final result
|
||||
const result = typeof input === 'function' ? input(new SlashCommandSubcommandBuilder()) : input;
|
||||
Assertions_1.assertReturnOfBuilder(result, SlashCommandSubcommandBuilder);
|
||||
// Push it
|
||||
options.push(result);
|
||||
return this;
|
||||
}
|
||||
toJSON() {
|
||||
Assertions_1.validateRequiredParameters(this.name, this.description, this.options);
|
||||
return {
|
||||
type: 2 /* SubcommandGroup */,
|
||||
name: this.name,
|
||||
description: this.description,
|
||||
options: this.options.map((option) => option.toJSON()),
|
||||
};
|
||||
}
|
||||
};
|
||||
SlashCommandSubcommandGroupBuilder = tslib_1.__decorate([
|
||||
ts_mixer_1.mix(NameAndDescription_1.SharedNameAndDescription)
|
||||
], SlashCommandSubcommandGroupBuilder);
|
||||
exports.SlashCommandSubcommandGroupBuilder = SlashCommandSubcommandGroupBuilder;
|
||||
/**
|
||||
* Represents a subcommand
|
||||
*
|
||||
* For more information, go to https://discord.com/developers/docs/interactions/slash-commands#subcommands-and-subcommand-groups
|
||||
*/
|
||||
let SlashCommandSubcommandBuilder = class SlashCommandSubcommandBuilder {
|
||||
constructor() {
|
||||
/**
|
||||
* The name of this subcommand
|
||||
*/
|
||||
Object.defineProperty(this, "name", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: undefined
|
||||
});
|
||||
/**
|
||||
* The description of this subcommand
|
||||
*/
|
||||
Object.defineProperty(this, "description", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: undefined
|
||||
});
|
||||
/**
|
||||
* The options of this subcommand
|
||||
*/
|
||||
Object.defineProperty(this, "options", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: []
|
||||
});
|
||||
}
|
||||
toJSON() {
|
||||
Assertions_1.validateRequiredParameters(this.name, this.description, this.options);
|
||||
return {
|
||||
type: 1 /* Subcommand */,
|
||||
name: this.name,
|
||||
description: this.description,
|
||||
options: this.options.map((option) => option.toJSON()),
|
||||
};
|
||||
}
|
||||
};
|
||||
SlashCommandSubcommandBuilder = tslib_1.__decorate([
|
||||
ts_mixer_1.mix(NameAndDescription_1.SharedNameAndDescription, CommandOptions_1.SharedSlashCommandOptions)
|
||||
], SlashCommandSubcommandBuilder);
|
||||
exports.SlashCommandSubcommandBuilder = SlashCommandSubcommandBuilder;
|
||||
//# sourceMappingURL=SlashCommandSubcommands.js.map
|
||||
Generated
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"SlashCommandSubcommands.js","sourceRoot":"/","sources":["interactions/slashCommands/SlashCommandSubcommands.ts"],"names":[],"mappings":";;;;AAAA,gCAAoE;AACpE,uCAA+B;AAC/B,6CAA2G;AAC3G,4DAAoE;AACpE,oEAAuE;AAGvE;;;;GAIG;AAEH,IAAa,kCAAkC,GAA/C,MAAa,kCAAkC;IAA/C;QACC;;WAEG;QACH;;;;mBAA+B,SAAU;WAAC;QAE1C;;WAEG;QACH;;;;mBAAsC,SAAU;WAAC;QAEjD;;WAEG;QACH;;;;mBAA4D,EAAE;WAAC;IAoChE,CAAC;IAlCA;;;OAGG;IACI,aAAa,CACnB,KAEsF;QAEtF,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;QAEzB,yEAAyE;QACzE,qCAAwB,CAAC,OAAO,CAAC,CAAC;QAElC,uBAAuB;QACvB,MAAM,MAAM,GAAG,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,6BAA6B,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QAEhG,kCAAqB,CAAC,MAAM,EAAE,6BAA6B,CAAC,CAAC;QAE7D,UAAU;QACV,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAErB,OAAO,IAAI,CAAC;IACb,CAAC;IAEM,MAAM;QACZ,uCAA0B,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACtE,OAAO;YACN,IAAI,yBAA8C;YAClD,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;SACtD,CAAC;IACH,CAAC;CACD,CAAA;AAlDY,kCAAkC;IAD9C,cAAG,CAAC,6CAAwB,CAAC;GACjB,kCAAkC,CAkD9C;AAlDY,gFAAkC;AAsD/C;;;;GAIG;AAEH,IAAa,6BAA6B,GAA1C,MAAa,6BAA6B;IAA1C;QACC;;WAEG;QACH;;;;mBAA+B,SAAU;WAAC;QAE1C;;WAEG;QACH;;;;mBAAsC,SAAU;WAAC;QAEjD;;WAEG;QACH;;;;mBAA4D,EAAE;WAAC;IAWhE,CAAC;IATO,MAAM;QACZ,uCAA0B,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACtE,OAAO;YACN,IAAI,oBAAyC;YAC7C,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;SACtD,CAAC;IACH,CAAC;CACD,CAAA;AAzBY,6BAA6B;IADzC,cAAG,CAAC,6CAAwB,EAAE,0CAAyB,CAAC;GAC5C,6BAA6B,CAyBzC;AAzBY,sEAA6B","sourcesContent":["import { ApplicationCommandOptionType } from 'discord-api-types/v9';\nimport { mix } from 'ts-mixer';\nimport { assertReturnOfBuilder, validateMaxOptionsLength, validateRequiredParameters } from './Assertions';\nimport { SharedSlashCommandOptions } from './mixins/CommandOptions';\nimport { SharedNameAndDescription } from './mixins/NameAndDescription';\nimport type { ToAPIApplicationCommandOptions } from './SlashCommandBuilder';\n\n/**\n * Represents a folder for subcommands\n *\n * For more information, go to https://discord.com/developers/docs/interactions/slash-commands#subcommands-and-subcommand-groups\n */\n@mix(SharedNameAndDescription)\nexport class SlashCommandSubcommandGroupBuilder implements ToAPIApplicationCommandOptions {\n\t/**\n\t * The name of this subcommand group\n\t */\n\tpublic readonly name: string = undefined!;\n\n\t/**\n\t * The description of this subcommand group\n\t */\n\tpublic readonly description: string = undefined!;\n\n\t/**\n\t * The subcommands part of this subcommand group\n\t */\n\tpublic readonly options: ToAPIApplicationCommandOptions[] = [];\n\n\t/**\n\t * Adds a new subcommand to this group\n\t * @param input A function that returns a subcommand builder, or an already built builder\n\t */\n\tpublic addSubcommand(\n\t\tinput:\n\t\t\t| SlashCommandSubcommandBuilder\n\t\t\t| ((subcommandGroup: SlashCommandSubcommandBuilder) => SlashCommandSubcommandBuilder),\n\t) {\n\t\tconst { options } = this;\n\n\t\t// First, assert options conditions - we cannot have more than 25 options\n\t\tvalidateMaxOptionsLength(options);\n\n\t\t// Get the final result\n\t\tconst result = typeof input === 'function' ? input(new SlashCommandSubcommandBuilder()) : input;\n\n\t\tassertReturnOfBuilder(result, SlashCommandSubcommandBuilder);\n\n\t\t// Push it\n\t\toptions.push(result);\n\n\t\treturn this;\n\t}\n\n\tpublic toJSON() {\n\t\tvalidateRequiredParameters(this.name, this.description, this.options);\n\t\treturn {\n\t\t\ttype: ApplicationCommandOptionType.SubcommandGroup,\n\t\t\tname: this.name,\n\t\t\tdescription: this.description,\n\t\t\toptions: this.options.map((option) => option.toJSON()),\n\t\t};\n\t}\n}\n\nexport interface SlashCommandSubcommandGroupBuilder extends SharedNameAndDescription {}\n\n/**\n * Represents a subcommand\n *\n * For more information, go to https://discord.com/developers/docs/interactions/slash-commands#subcommands-and-subcommand-groups\n */\n@mix(SharedNameAndDescription, SharedSlashCommandOptions)\nexport class SlashCommandSubcommandBuilder implements ToAPIApplicationCommandOptions {\n\t/**\n\t * The name of this subcommand\n\t */\n\tpublic readonly name: string = undefined!;\n\n\t/**\n\t * The description of this subcommand\n\t */\n\tpublic readonly description: string = undefined!;\n\n\t/**\n\t * The options of this subcommand\n\t */\n\tpublic readonly options: ToAPIApplicationCommandOptions[] = [];\n\n\tpublic toJSON() {\n\t\tvalidateRequiredParameters(this.name, this.description, this.options);\n\t\treturn {\n\t\t\ttype: ApplicationCommandOptionType.Subcommand,\n\t\t\tname: this.name,\n\t\t\tdescription: this.description,\n\t\t\toptions: this.options.map((option) => option.toJSON()),\n\t\t};\n\t}\n}\n\nexport interface SlashCommandSubcommandBuilLine truncated
|
||||
Generated
Vendored
-20
@@ -1,20 +0,0 @@
|
||||
import type { ApplicationCommandOptionType } from 'discord-api-types/v9';
|
||||
import type { ToAPIApplicationCommandOptions } from '../SlashCommandBuilder';
|
||||
import { SharedNameAndDescription } from './NameAndDescription';
|
||||
export declare class SlashCommandOptionBase extends SharedNameAndDescription implements ToAPIApplicationCommandOptions {
|
||||
required: boolean;
|
||||
readonly type: ApplicationCommandOptionType;
|
||||
constructor(type: ApplicationCommandOptionType);
|
||||
/**
|
||||
* Marks the option as required
|
||||
* @param required If this option should be required
|
||||
*/
|
||||
setRequired(required: boolean): this;
|
||||
toJSON(): {
|
||||
type: ApplicationCommandOptionType;
|
||||
name: string;
|
||||
description: string;
|
||||
required: boolean;
|
||||
};
|
||||
}
|
||||
//# sourceMappingURL=CommandOptionBase.d.ts.map
|
||||
Generated
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"CommandOptionBase.d.ts","sourceRoot":"/","sources":["interactions/slashCommands/mixins/CommandOptionBase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAC;AAGzE,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,qBAAa,sBAAuB,SAAQ,wBAAyB,YAAW,8BAA8B;IACtG,QAAQ,UAAS;IACxB,SAAgB,IAAI,EAAE,4BAA4B,CAAC;gBAEhC,IAAI,EAAE,4BAA4B;IAKrD;;;OAGG;IACI,WAAW,CAAC,QAAQ,EAAE,OAAO;IAS7B,MAAM;;;;;;CAab"}
|
||||
Generated
Vendored
-48
@@ -1,48 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.SlashCommandOptionBase = void 0;
|
||||
const tslib_1 = require("tslib");
|
||||
const ow_1 = tslib_1.__importDefault(require("ow"));
|
||||
const Assertions_1 = require("../Assertions");
|
||||
const NameAndDescription_1 = require("./NameAndDescription");
|
||||
class SlashCommandOptionBase extends NameAndDescription_1.SharedNameAndDescription {
|
||||
constructor(type) {
|
||||
super();
|
||||
Object.defineProperty(this, "required", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: false
|
||||
});
|
||||
Object.defineProperty(this, "type", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
this.type = type;
|
||||
}
|
||||
/**
|
||||
* Marks the option as required
|
||||
* @param required If this option should be required
|
||||
*/
|
||||
setRequired(required) {
|
||||
// Assert that you actually passed a boolean
|
||||
ow_1.default(required, 'required', ow_1.default.boolean);
|
||||
this.required = required;
|
||||
return this;
|
||||
}
|
||||
toJSON() {
|
||||
Assertions_1.validateRequiredParameters(this.name, this.description, []);
|
||||
// Assert that you actually passed a boolean
|
||||
ow_1.default(this.required, 'required', ow_1.default.boolean);
|
||||
return {
|
||||
type: this.type,
|
||||
name: this.name,
|
||||
description: this.description,
|
||||
required: this.required,
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.SlashCommandOptionBase = SlashCommandOptionBase;
|
||||
//# sourceMappingURL=CommandOptionBase.js.map
|
||||
Generated
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"CommandOptionBase.js","sourceRoot":"/","sources":["interactions/slashCommands/mixins/CommandOptionBase.ts"],"names":[],"mappings":";;;;AACA,oDAAoB;AACpB,8CAA2D;AAE3D,6DAAgE;AAEhE,MAAa,sBAAuB,SAAQ,6CAAwB;IAInE,YAAmB,IAAkC;QACpD,KAAK,EAAE,CAAC;QAJT;;;;mBAAkB,KAAK;WAAC;QACxB;;;;;WAAmD;QAIlD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAClB,CAAC;IAED;;;OAGG;IACI,WAAW,CAAC,QAAiB;QACnC,4CAA4C;QAC5C,YAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,YAAE,CAAC,OAAO,CAAC,CAAC;QAErC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QAEzB,OAAO,IAAI,CAAC;IACb,CAAC;IAEM,MAAM;QACZ,uCAA0B,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QAE5D,4CAA4C;QAC5C,YAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE,YAAE,CAAC,OAAO,CAAC,CAAC;QAE1C,OAAO;YACN,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,QAAQ,EAAE,IAAI,CAAC,QAAQ;SACvB,CAAC;IACH,CAAC;CACD;AAnCD,wDAmCC","sourcesContent":["import type { ApplicationCommandOptionType } from 'discord-api-types/v9';\nimport ow from 'ow';\nimport { validateRequiredParameters } from '../Assertions';\nimport type { ToAPIApplicationCommandOptions } from '../SlashCommandBuilder';\nimport { SharedNameAndDescription } from './NameAndDescription';\n\nexport class SlashCommandOptionBase extends SharedNameAndDescription implements ToAPIApplicationCommandOptions {\n\tpublic required = false;\n\tpublic readonly type: ApplicationCommandOptionType;\n\n\tpublic constructor(type: ApplicationCommandOptionType) {\n\t\tsuper();\n\t\tthis.type = type;\n\t}\n\n\t/**\n\t * Marks the option as required\n\t * @param required If this option should be required\n\t */\n\tpublic setRequired(required: boolean) {\n\t\t// Assert that you actually passed a boolean\n\t\tow(required, 'required', ow.boolean);\n\n\t\tthis.required = required;\n\n\t\treturn this;\n\t}\n\n\tpublic toJSON() {\n\t\tvalidateRequiredParameters(this.name, this.description, []);\n\n\t\t// Assert that you actually passed a boolean\n\t\tow(this.required, 'required', ow.boolean);\n\n\t\treturn {\n\t\t\ttype: this.type,\n\t\t\tname: this.name,\n\t\t\tdescription: this.description,\n\t\t\trequired: this.required,\n\t\t};\n\t}\n}\n"]}
|
||||
Generated
Vendored
-25
@@ -1,25 +0,0 @@
|
||||
import { APIApplicationCommandOptionChoice, ApplicationCommandOptionType } from 'discord-api-types/v9';
|
||||
import type { ToAPIApplicationCommandOptions } from '../SlashCommandBuilder';
|
||||
import { SlashCommandOptionBase } from './CommandOptionBase';
|
||||
export declare abstract class ApplicationCommandOptionWithChoicesBase<T extends string | number> extends SlashCommandOptionBase implements ToAPIApplicationCommandOptions {
|
||||
choices?: APIApplicationCommandOptionChoice[];
|
||||
/**
|
||||
* Adds a choice for this option
|
||||
* @param name The name of the choice
|
||||
* @param value The value of the choice
|
||||
*/
|
||||
addChoice(name: string, value: T): this;
|
||||
/**
|
||||
* Adds multiple choices for this option
|
||||
* @param choices The choices to add
|
||||
*/
|
||||
addChoices(choices: [name: string, value: T][]): this;
|
||||
toJSON(): {
|
||||
choices: APIApplicationCommandOptionChoice[] | undefined;
|
||||
type: ApplicationCommandOptionType;
|
||||
name: string;
|
||||
description: string;
|
||||
required: boolean;
|
||||
};
|
||||
}
|
||||
//# sourceMappingURL=CommandOptionWithChoices.d.ts.map
|
||||
Generated
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"CommandOptionWithChoices.d.ts","sourceRoot":"/","sources":["interactions/slashCommands/mixins/CommandOptionWithChoices.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iCAAiC,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAC;AAGvG,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAU7D,8BAAsB,uCAAuC,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,CACtF,SAAQ,sBACR,YAAW,8BAA8B;IAElC,OAAO,CAAC,EAAE,iCAAiC,EAAE,CAAC;IAErD;;;;OAIG;IACI,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAqBvC;;;OAGG;IACI,UAAU,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE;IAWrC,MAAM;;;;;;;CAMtB"}
|
||||
Generated
Vendored
-59
@@ -1,59 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ApplicationCommandOptionWithChoicesBase = void 0;
|
||||
const tslib_1 = require("tslib");
|
||||
require("discord-api-types/v9");
|
||||
const ow_1 = tslib_1.__importDefault(require("ow"));
|
||||
const Assertions_1 = require("../Assertions");
|
||||
const CommandOptionBase_1 = require("./CommandOptionBase");
|
||||
const stringPredicate = ow_1.default.string.minLength(1).maxLength(100);
|
||||
const integerPredicate = ow_1.default.number.finite;
|
||||
// TODO: See resolution for sindresorhus/ow#217 in relation to this cast
|
||||
const choicesPredicate = ow_1.default.array.ofType(ow_1.default.array.exactShape([stringPredicate, ow_1.default.any(ow_1.default.string, integerPredicate)]));
|
||||
class ApplicationCommandOptionWithChoicesBase extends CommandOptionBase_1.SlashCommandOptionBase {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
Object.defineProperty(this, "choices", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Adds a choice for this option
|
||||
* @param name The name of the choice
|
||||
* @param value The value of the choice
|
||||
*/
|
||||
addChoice(name, value) {
|
||||
this.choices ?? (this.choices = []);
|
||||
Assertions_1.validateMaxChoicesLength(this.choices);
|
||||
// Validate name
|
||||
ow_1.default(name, `${this.type === 3 /* String */ ? 'string' : 'integer'} choice name`, stringPredicate);
|
||||
// Validate the value
|
||||
if (this.type === 3 /* String */)
|
||||
ow_1.default(value, 'string choice value', stringPredicate);
|
||||
else
|
||||
ow_1.default(value, 'integer choice value', integerPredicate);
|
||||
this.choices.push({ name, value });
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Adds multiple choices for this option
|
||||
* @param choices The choices to add
|
||||
*/
|
||||
addChoices(choices) {
|
||||
ow_1.default(choices, `${this.type === 3 /* String */ ? 'string' : 'integer'} choices`, choicesPredicate);
|
||||
for (const [label, value] of choices)
|
||||
this.addChoice(label, value);
|
||||
return this;
|
||||
}
|
||||
toJSON() {
|
||||
return {
|
||||
...super.toJSON(),
|
||||
choices: this.choices,
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.ApplicationCommandOptionWithChoicesBase = ApplicationCommandOptionWithChoicesBase;
|
||||
//# sourceMappingURL=CommandOptionWithChoices.js.map
|
||||
Generated
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"CommandOptionWithChoices.js","sourceRoot":"/","sources":["interactions/slashCommands/mixins/CommandOptionWithChoices.ts"],"names":[],"mappings":";;;;AAAA,gCAAuG;AACvG,oDAAmC;AACnC,8CAAyD;AAEzD,2DAA6D;AAE7D,MAAM,eAAe,GAAG,YAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;AAC9D,MAAM,gBAAgB,GAAG,YAAE,CAAC,MAAM,CAAC,MAAM,CAAC;AAE1C,wEAAwE;AACxE,MAAM,gBAAgB,GAAG,YAAE,CAAC,KAAK,CAAC,MAAM,CACvC,YAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,eAAe,EAAE,YAAE,CAAC,GAAG,CAAC,YAAE,CAAC,MAAM,EAAE,gBAAgB,CAA0C,CAAC,CAAC,CACpH,CAAC;AAEF,MAAsB,uCACrB,SAAQ,0CAAsB;IAD/B;;QAIC;;;;;WAAqD;IAiDtD,CAAC;IA/CA;;;;OAIG;IACI,SAAS,CAAC,IAAY,EAAE,KAAQ;QACtC,IAAI,CAAC,OAAO,KAAZ,IAAI,CAAC,OAAO,GAAK,EAAE,EAAC;QAEpB,qCAAwB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAEvC,gBAAgB;QAChB,YAAE,CACD,IAAI,EACJ,GAAG,IAAI,CAAC,IAAI,mBAAwC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,cAAc,EACzF,eAAe,CACf,CAAC;QAEF,qBAAqB;QACrB,IAAI,IAAI,CAAC,IAAI,mBAAwC;YAAE,YAAE,CAAC,KAAK,EAAE,qBAAqB,EAAE,eAAe,CAAC,CAAC;;YACpG,YAAE,CAAC,KAAK,EAAE,sBAAsB,EAAE,gBAAgB,CAAC,CAAC;QAEzD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAEnC,OAAO,IAAI,CAAC;IACb,CAAC;IAED;;;OAGG;IACI,UAAU,CAAC,OAAmC;QACpD,YAAE,CACD,OAAO,EACP,GAAG,IAAI,CAAC,IAAI,mBAAwC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,UAAU,EACrF,gBAAgB,CAChB,CAAC;QAEF,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,OAAO;YAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACnE,OAAO,IAAI,CAAC;IACb,CAAC;IAEe,MAAM;QACrB,OAAO;YACN,GAAG,KAAK,CAAC,MAAM,EAAE;YACjB,OAAO,EAAE,IAAI,CAAC,OAAO;SACrB,CAAC;IACH,CAAC;CACD;AArDD,0FAqDC","sourcesContent":["import { APIApplicationCommandOptionChoice, ApplicationCommandOptionType } from 'discord-api-types/v9';\nimport ow, { Predicate } from 'ow';\nimport { validateMaxChoicesLength } from '../Assertions';\nimport type { ToAPIApplicationCommandOptions } from '../SlashCommandBuilder';\nimport { SlashCommandOptionBase } from './CommandOptionBase';\n\nconst stringPredicate = ow.string.minLength(1).maxLength(100);\nconst integerPredicate = ow.number.finite;\n\n// TODO: See resolution for sindresorhus/ow#217 in relation to this cast\nconst choicesPredicate = ow.array.ofType<[string, string | number]>(\n\tow.array.exactShape([stringPredicate, ow.any(ow.string, integerPredicate) as unknown as Predicate<string | number>]),\n);\n\nexport abstract class ApplicationCommandOptionWithChoicesBase<T extends string | number>\n\textends SlashCommandOptionBase\n\timplements ToAPIApplicationCommandOptions\n{\n\tpublic choices?: APIApplicationCommandOptionChoice[];\n\n\t/**\n\t * Adds a choice for this option\n\t * @param name The name of the choice\n\t * @param value The value of the choice\n\t */\n\tpublic addChoice(name: string, value: T) {\n\t\tthis.choices ??= [];\n\n\t\tvalidateMaxChoicesLength(this.choices);\n\n\t\t// Validate name\n\t\tow(\n\t\t\tname,\n\t\t\t`${this.type === ApplicationCommandOptionType.String ? 'string' : 'integer'} choice name`,\n\t\t\tstringPredicate,\n\t\t);\n\n\t\t// Validate the value\n\t\tif (this.type === ApplicationCommandOptionType.String) ow(value, 'string choice value', stringPredicate);\n\t\telse ow(value, 'integer choice value', integerPredicate);\n\n\t\tthis.choices.push({ name, value });\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds multiple choices for this option\n\t * @param choices The choices to add\n\t */\n\tpublic addChoices(choices: [name: string, value: T][]) {\n\t\tow(\n\t\t\tchoices,\n\t\t\t`${this.type === ApplicationCommandOptionType.String ? 'string' : 'integer'} choices`,\n\t\t\tchoicesPredicate,\n\t\t);\n\n\t\tfor (const [label, value] of choices) this.addChoice(label, value);\n\t\treturn this;\n\t}\n\n\tpublic override toJSON() {\n\t\treturn {\n\t\t\t...super.toJSON(),\n\t\t\tchoices: this.choices,\n\t\t};\n\t}\n}\n"]}
|
||||
Generated
Vendored
-48
@@ -1,48 +0,0 @@
|
||||
import { SlashCommandBooleanOption } from '../options/boolean';
|
||||
import { SlashCommandChannelOption } from '../options/channel';
|
||||
import { SlashCommandIntegerOption } from '../options/integer';
|
||||
import { SlashCommandMentionableOption } from '../options/mentionable';
|
||||
import { SlashCommandRoleOption } from '../options/role';
|
||||
import { SlashCommandStringOption } from '../options/string';
|
||||
import { SlashCommandUserOption } from '../options/user';
|
||||
import type { ToAPIApplicationCommandOptions } from '../SlashCommandBuilder';
|
||||
export declare class SharedSlashCommandOptions<ShouldOmitSubcommandFunctions = true> {
|
||||
readonly options: ToAPIApplicationCommandOptions[];
|
||||
/**
|
||||
* Adds a boolean option
|
||||
* @param input A function that returns an option builder, or an already built builder
|
||||
*/
|
||||
addBooleanOption(input: SlashCommandBooleanOption | ((builder: SlashCommandBooleanOption) => SlashCommandBooleanOption)): ShouldOmitSubcommandFunctions extends true ? Omit<this, "addSubcommand" | "addSubcommandGroup"> : this;
|
||||
/**
|
||||
* Adds a user option
|
||||
* @param input A function that returns an option builder, or an already built builder
|
||||
*/
|
||||
addUserOption(input: SlashCommandUserOption | ((builder: SlashCommandUserOption) => SlashCommandUserOption)): ShouldOmitSubcommandFunctions extends true ? Omit<this, "addSubcommand" | "addSubcommandGroup"> : this;
|
||||
/**
|
||||
* Adds a channel option
|
||||
* @param input A function that returns an option builder, or an already built builder
|
||||
*/
|
||||
addChannelOption(input: SlashCommandChannelOption | ((builder: SlashCommandChannelOption) => SlashCommandChannelOption)): ShouldOmitSubcommandFunctions extends true ? Omit<this, "addSubcommand" | "addSubcommandGroup"> : this;
|
||||
/**
|
||||
* Adds a role option
|
||||
* @param input A function that returns an option builder, or an already built builder
|
||||
*/
|
||||
addRoleOption(input: SlashCommandRoleOption | ((builder: SlashCommandRoleOption) => SlashCommandRoleOption)): ShouldOmitSubcommandFunctions extends true ? Omit<this, "addSubcommand" | "addSubcommandGroup"> : this;
|
||||
/**
|
||||
* Adds a mentionable option
|
||||
* @param input A function that returns an option builder, or an already built builder
|
||||
*/
|
||||
addMentionableOption(input: SlashCommandMentionableOption | ((builder: SlashCommandMentionableOption) => SlashCommandMentionableOption)): ShouldOmitSubcommandFunctions extends true ? Omit<this, "addSubcommand" | "addSubcommandGroup"> : this;
|
||||
/**
|
||||
* Adds a string option
|
||||
* @param input A function that returns an option builder, or an already built builder
|
||||
*/
|
||||
addStringOption(input: SlashCommandStringOption | ((builder: SlashCommandStringOption) => SlashCommandStringOption)): ShouldOmitSubcommandFunctions extends true ? Omit<this, "addSubcommand" | "addSubcommandGroup"> : this;
|
||||
/**
|
||||
* Adds an integer option
|
||||
* @param input A function that returns an option builder, or an already built builder
|
||||
*/
|
||||
addIntegerOption(input: SlashCommandIntegerOption | ((builder: SlashCommandIntegerOption) => SlashCommandIntegerOption)): ShouldOmitSubcommandFunctions extends true ? Omit<this, "addSubcommand" | "addSubcommandGroup"> : this;
|
||||
private _sharedAddOptionMethod;
|
||||
}
|
||||
//# sourceMappingURL=CommandOptions.d.ts.map
|
||||
Generated
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"CommandOptions.d.ts","sourceRoot":"/","sources":["interactions/slashCommands/mixins/CommandOptions.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,6BAA6B,EAAE,MAAM,wBAAwB,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,wBAAwB,CAAC;AAE7E,qBAAa,yBAAyB,CAAC,6BAA6B,GAAG,IAAI;IAC1E,SAAgB,OAAO,EAAG,8BAA8B,EAAE,CAAC;IAE3D;;;OAGG;IACI,gBAAgB,CACtB,KAAK,EAAE,yBAAyB,GAAG,CAAC,CAAC,OAAO,EAAE,yBAAyB,KAAK,yBAAyB,CAAC;IAKvG;;;OAGG;IACI,aAAa,CAAC,KAAK,EAAE,sBAAsB,GAAG,CAAC,CAAC,OAAO,EAAE,sBAAsB,KAAK,sBAAsB,CAAC;IAIlH;;;OAGG;IACI,gBAAgB,CACtB,KAAK,EAAE,yBAAyB,GAAG,CAAC,CAAC,OAAO,EAAE,yBAAyB,KAAK,yBAAyB,CAAC;IAKvG;;;OAGG;IACI,aAAa,CAAC,KAAK,EAAE,sBAAsB,GAAG,CAAC,CAAC,OAAO,EAAE,sBAAsB,KAAK,sBAAsB,CAAC;IAIlH;;;OAGG;IACI,oBAAoB,CAC1B,KAAK,EAAE,6BAA6B,GAAG,CAAC,CAAC,OAAO,EAAE,6BAA6B,KAAK,6BAA6B,CAAC;IAKnH;;;OAGG;IACI,eAAe,CACrB,KAAK,EAAE,wBAAwB,GAAG,CAAC,CAAC,OAAO,EAAE,wBAAwB,KAAK,wBAAwB,CAAC;IAKpG;;;OAGG;IACI,gBAAgB,CACtB,KAAK,EAAE,yBAAyB,GAAG,CAAC,CAAC,OAAO,EAAE,yBAAyB,KAAK,yBAAyB,CAAC;IAKvG,OAAO,CAAC,sBAAsB;CAmB9B"}
|
||||
Generated
Vendored
-83
@@ -1,83 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.SharedSlashCommandOptions = void 0;
|
||||
const Assertions_1 = require("../Assertions");
|
||||
const boolean_1 = require("../options/boolean");
|
||||
const channel_1 = require("../options/channel");
|
||||
const integer_1 = require("../options/integer");
|
||||
const mentionable_1 = require("../options/mentionable");
|
||||
const role_1 = require("../options/role");
|
||||
const string_1 = require("../options/string");
|
||||
const user_1 = require("../options/user");
|
||||
class SharedSlashCommandOptions {
|
||||
constructor() {
|
||||
Object.defineProperty(this, "options", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Adds a boolean option
|
||||
* @param input A function that returns an option builder, or an already built builder
|
||||
*/
|
||||
addBooleanOption(input) {
|
||||
return this._sharedAddOptionMethod(input, boolean_1.SlashCommandBooleanOption);
|
||||
}
|
||||
/**
|
||||
* Adds a user option
|
||||
* @param input A function that returns an option builder, or an already built builder
|
||||
*/
|
||||
addUserOption(input) {
|
||||
return this._sharedAddOptionMethod(input, user_1.SlashCommandUserOption);
|
||||
}
|
||||
/**
|
||||
* Adds a channel option
|
||||
* @param input A function that returns an option builder, or an already built builder
|
||||
*/
|
||||
addChannelOption(input) {
|
||||
return this._sharedAddOptionMethod(input, channel_1.SlashCommandChannelOption);
|
||||
}
|
||||
/**
|
||||
* Adds a role option
|
||||
* @param input A function that returns an option builder, or an already built builder
|
||||
*/
|
||||
addRoleOption(input) {
|
||||
return this._sharedAddOptionMethod(input, role_1.SlashCommandRoleOption);
|
||||
}
|
||||
/**
|
||||
* Adds a mentionable option
|
||||
* @param input A function that returns an option builder, or an already built builder
|
||||
*/
|
||||
addMentionableOption(input) {
|
||||
return this._sharedAddOptionMethod(input, mentionable_1.SlashCommandMentionableOption);
|
||||
}
|
||||
/**
|
||||
* Adds a string option
|
||||
* @param input A function that returns an option builder, or an already built builder
|
||||
*/
|
||||
addStringOption(input) {
|
||||
return this._sharedAddOptionMethod(input, string_1.SlashCommandStringOption);
|
||||
}
|
||||
/**
|
||||
* Adds an integer option
|
||||
* @param input A function that returns an option builder, or an already built builder
|
||||
*/
|
||||
addIntegerOption(input) {
|
||||
return this._sharedAddOptionMethod(input, integer_1.SlashCommandIntegerOption);
|
||||
}
|
||||
_sharedAddOptionMethod(input, Instance) {
|
||||
const { options } = this;
|
||||
// First, assert options conditions - we cannot have more than 25 options
|
||||
Assertions_1.validateMaxOptionsLength(options);
|
||||
// Get the final result
|
||||
const result = typeof input === 'function' ? input(new Instance()) : input;
|
||||
Assertions_1.assertReturnOfBuilder(result, Instance);
|
||||
// Push it
|
||||
options.push(result);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
exports.SharedSlashCommandOptions = SharedSlashCommandOptions;
|
||||
//# sourceMappingURL=CommandOptions.js.map
|
||||
Generated
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"CommandOptions.js","sourceRoot":"/","sources":["interactions/slashCommands/mixins/CommandOptions.ts"],"names":[],"mappings":";;;AAAA,8CAAgF;AAEhF,gDAA+D;AAC/D,gDAA+D;AAC/D,gDAA+D;AAC/D,wDAAuE;AACvE,0CAAyD;AACzD,8CAA6D;AAC7D,0CAAyD;AAGzD,MAAa,yBAAyB;IAAtC;QACC;;;;;WAA2D;IAuF5D,CAAC;IArFA;;;OAGG;IACI,gBAAgB,CACtB,KAAsG;QAEtG,OAAO,IAAI,CAAC,sBAAsB,CAAC,KAAK,EAAE,mCAAyB,CAAC,CAAC;IACtE,CAAC;IAED;;;OAGG;IACI,aAAa,CAAC,KAA6F;QACjH,OAAO,IAAI,CAAC,sBAAsB,CAAC,KAAK,EAAE,6BAAsB,CAAC,CAAC;IACnE,CAAC;IAED;;;OAGG;IACI,gBAAgB,CACtB,KAAsG;QAEtG,OAAO,IAAI,CAAC,sBAAsB,CAAC,KAAK,EAAE,mCAAyB,CAAC,CAAC;IACtE,CAAC;IAED;;;OAGG;IACI,aAAa,CAAC,KAA6F;QACjH,OAAO,IAAI,CAAC,sBAAsB,CAAC,KAAK,EAAE,6BAAsB,CAAC,CAAC;IACnE,CAAC;IAED;;;OAGG;IACI,oBAAoB,CAC1B,KAAkH;QAElH,OAAO,IAAI,CAAC,sBAAsB,CAAC,KAAK,EAAE,2CAA6B,CAAC,CAAC;IAC1E,CAAC;IAED;;;OAGG;IACI,eAAe,CACrB,KAAmG;QAEnG,OAAO,IAAI,CAAC,sBAAsB,CAAC,KAAK,EAAE,iCAAwB,CAAC,CAAC;IACrE,CAAC;IAED;;;OAGG;IACI,gBAAgB,CACtB,KAAsG;QAEtG,OAAO,IAAI,CAAC,sBAAsB,CAAC,KAAK,EAAE,mCAAyB,CAAC,CAAC;IACtE,CAAC;IAEO,sBAAsB,CAC7B,KAA8B,EAC9B,QAAqB;QAErB,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;QAEzB,yEAAyE;QACzE,qCAAwB,CAAC,OAAO,CAAC,CAAC;QAElC,uBAAuB;QACvB,MAAM,MAAM,GAAG,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QAE3E,kCAAqB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAExC,UAAU;QACV,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAErB,OAAO,IAAI,CAAC;IACb,CAAC;CACD;AAxFD,8DAwFC","sourcesContent":["import { assertReturnOfBuilder, validateMaxOptionsLength } from '../Assertions';\nimport type { SlashCommandOptionBase } from './CommandOptionBase';\nimport { SlashCommandBooleanOption } from '../options/boolean';\nimport { SlashCommandChannelOption } from '../options/channel';\nimport { SlashCommandIntegerOption } from '../options/integer';\nimport { SlashCommandMentionableOption } from '../options/mentionable';\nimport { SlashCommandRoleOption } from '../options/role';\nimport { SlashCommandStringOption } from '../options/string';\nimport { SlashCommandUserOption } from '../options/user';\nimport type { ToAPIApplicationCommandOptions } from '../SlashCommandBuilder';\n\nexport class SharedSlashCommandOptions<ShouldOmitSubcommandFunctions = true> {\n\tpublic readonly options!: ToAPIApplicationCommandOptions[];\n\n\t/**\n\t * Adds a boolean option\n\t * @param input A function that returns an option builder, or an already built builder\n\t */\n\tpublic addBooleanOption(\n\t\tinput: SlashCommandBooleanOption | ((builder: SlashCommandBooleanOption) => SlashCommandBooleanOption),\n\t) {\n\t\treturn this._sharedAddOptionMethod(input, SlashCommandBooleanOption);\n\t}\n\n\t/**\n\t * Adds a user option\n\t * @param input A function that returns an option builder, or an already built builder\n\t */\n\tpublic addUserOption(input: SlashCommandUserOption | ((builder: SlashCommandUserOption) => SlashCommandUserOption)) {\n\t\treturn this._sharedAddOptionMethod(input, SlashCommandUserOption);\n\t}\n\n\t/**\n\t * Adds a channel option\n\t * @param input A function that returns an option builder, or an already built builder\n\t */\n\tpublic addChannelOption(\n\t\tinput: SlashCommandChannelOption | ((builder: SlashCommandChannelOption) => SlashCommandChannelOption),\n\t) {\n\t\treturn this._sharedAddOptionMethod(input, SlashCommandChannelOption);\n\t}\n\n\t/**\n\t * Adds a role option\n\t * @param input A function that returns an option builder, or an already built builder\n\t */\n\tpublic addRoleOption(input: SlashCommandRoleOption | ((builder: SlashCommandRoleOption) => SlashCommandRoleOption)) {\n\t\treturn this._sharedAddOptionMethod(input, SlashCommandRoleOption);\n\t}\n\n\t/**\n\t * Adds a mentionable option\n\t * @param input A function that returns an option builder, or an already built builder\n\t */\n\tpublic addMentionableOption(\n\t\tinput: SlashCommandMentionableOption | ((builder: SlashCommandMentionableOption) => SlashCommandMentionableOption),\n\t) {\n\t\treturn this._sharedAddOptionMethod(input, SlashCommandMentionableOption);\n\t}\n\n\t/**\n\t * Adds a string option\n\t * @param input A function that returns an option builder, or an already built builder\n\t */\n\tpublic addStringOption(\n\t\tinput: SlashCommandStringOption | ((builder: SlashCommandStringOption) => SlashCommandStringOption),\n\t) {\n\t\treturn this._sharedAddOptionMethod(input, SlashCommandStringOption);\n\t}\n\n\t/**\n\t * Adds an integer option\n\t * @param input A function that returns an option builder, or an already built builder\n\t */\n\tpublic addIntegerOption(\n\t\tinput: SlashCommandIntegerOption | ((builder: SlashCommandIntegerOption) => SlashCommandIntegerOption),\n\t) {\n\t\treturn this._sharedAddOptionMethod(input, SlashCommandIntegerOption);\n\t}\n\n\tprivate _sharedAddOptionMethod<T extends SlashCommandOptionBase>(\n\t\tinput: T | ((builder: T) => T),\n\t\tInstance: new () => T,\n\t): ShouldOmitSubcommandFunctions extends true ? Omit<this, 'addSubcommand' | 'addSubcommandGroup'> : this {\n\t\tconLine truncated
|
||||
Generated
Vendored
-15
@@ -1,15 +0,0 @@
|
||||
export declare class SharedNameAndDescription {
|
||||
readonly name: string;
|
||||
readonly description: string;
|
||||
/**
|
||||
* Sets the name
|
||||
* @param name The name
|
||||
*/
|
||||
setName(name: string): this;
|
||||
/**
|
||||
* Sets the description
|
||||
* @param description The description
|
||||
*/
|
||||
setDescription(description: string): this;
|
||||
}
|
||||
//# sourceMappingURL=NameAndDescription.d.ts.map
|
||||
Generated
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"NameAndDescription.d.ts","sourceRoot":"/","sources":["interactions/slashCommands/mixins/NameAndDescription.ts"],"names":[],"mappings":"AAEA,qBAAa,wBAAwB;IACpC,SAAgB,IAAI,EAAG,MAAM,CAAC;IAC9B,SAAgB,WAAW,EAAG,MAAM,CAAC;IAErC;;;OAGG;IACI,OAAO,CAAC,IAAI,EAAE,MAAM;IAS3B;;;OAGG;IACI,cAAc,CAAC,WAAW,EAAE,MAAM;CAQzC"}
|
||||
Generated
Vendored
-42
@@ -1,42 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.SharedNameAndDescription = void 0;
|
||||
const Assertions_1 = require("../Assertions");
|
||||
class SharedNameAndDescription {
|
||||
constructor() {
|
||||
Object.defineProperty(this, "name", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
Object.defineProperty(this, "description", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Sets the name
|
||||
* @param name The name
|
||||
*/
|
||||
setName(name) {
|
||||
// Assert the name matches the conditions
|
||||
Assertions_1.validateName(name);
|
||||
Reflect.set(this, 'name', name);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Sets the description
|
||||
* @param description The description
|
||||
*/
|
||||
setDescription(description) {
|
||||
// Assert the description matches the conditions
|
||||
Assertions_1.validateDescription(description);
|
||||
Reflect.set(this, 'description', description);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
exports.SharedNameAndDescription = SharedNameAndDescription;
|
||||
//# sourceMappingURL=NameAndDescription.js.map
|
||||
Generated
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"NameAndDescription.js","sourceRoot":"/","sources":["interactions/slashCommands/mixins/NameAndDescription.ts"],"names":[],"mappings":";;;AAAA,8CAAkE;AAElE,MAAa,wBAAwB;IAArC;QACC;;;;;WAA8B;QAC9B;;;;;WAAqC;IA2BtC,CAAC;IAzBA;;;OAGG;IACI,OAAO,CAAC,IAAY;QAC1B,yCAAyC;QACzC,yBAAY,CAAC,IAAI,CAAC,CAAC;QAEnB,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QAEhC,OAAO,IAAI,CAAC;IACb,CAAC;IAED;;;OAGG;IACI,cAAc,CAAC,WAAmB;QACxC,gDAAgD;QAChD,gCAAmB,CAAC,WAAW,CAAC,CAAC;QAEjC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC;QAE9C,OAAO,IAAI,CAAC;IACb,CAAC;CACD;AA7BD,4DA6BC","sourcesContent":["import { validateDescription, validateName } from '../Assertions';\n\nexport class SharedNameAndDescription {\n\tpublic readonly name!: string;\n\tpublic readonly description!: string;\n\n\t/**\n\t * Sets the name\n\t * @param name The name\n\t */\n\tpublic setName(name: string) {\n\t\t// Assert the name matches the conditions\n\t\tvalidateName(name);\n\n\t\tReflect.set(this, 'name', name);\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sets the description\n\t * @param description The description\n\t */\n\tpublic setDescription(description: string) {\n\t\t// Assert the description matches the conditions\n\t\tvalidateDescription(description);\n\n\t\tReflect.set(this, 'description', description);\n\n\t\treturn this;\n\t}\n}\n"]}
|
||||
Loaded 100 of 1310 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user