882 lines
27 KiB
JavaScript
882 lines
27 KiB
JavaScript
// SupportBot | Emerald Services
|
|
// Ticket Command
|
|
|
|
const fs = require("fs");
|
|
const {
|
|
EmbedBuilder,
|
|
ApplicationCommandOptionType,
|
|
ApplicationCommandType,
|
|
ChannelType,
|
|
ActionRowBuilder,
|
|
ButtonBuilder,
|
|
StringSelectMenuBuilder,
|
|
StringSelectMenuOptionBuilder,
|
|
ButtonStyle,
|
|
MessageCollector,
|
|
MessageFlags,
|
|
SeparatorBuilder,
|
|
ContainerBuilder,
|
|
TextDisplayBuilder,
|
|
} = 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();
|
|
|
|
if (typeof db.getProfile !== "function") {
|
|
console.error("db.getProfile is not a function. Check Structures/Database.js");
|
|
return clockedInUsers;
|
|
}
|
|
|
|
const rows = guild.members.cache.map((m) => m.id);
|
|
|
|
for (const id of rows) {
|
|
try {
|
|
const profile = db.getProfile(id);
|
|
if (profile && profile.clockedIn) {
|
|
clockedInUsers.add(id);
|
|
}
|
|
} catch (error) {
|
|
console.error(`Error getting profile for ${id}:`, error);
|
|
}
|
|
}
|
|
|
|
return clockedInUsers;
|
|
}
|
|
|
|
function getDepartmentSystem() {
|
|
return supportbot.Ticket?.DepartmentSystem || {};
|
|
}
|
|
|
|
function getPrioritySystem() {
|
|
return supportbot.Ticket?.PrioritySystem || {};
|
|
}
|
|
|
|
function areDepartmentsEnabled() {
|
|
return (
|
|
supportbot.Ticket?.TicketType !== "threads" &&
|
|
supportbot.Ticket?.DepartmentSystem?.Enabled === true
|
|
);
|
|
}
|
|
|
|
function arePrioritiesEnabled() {
|
|
return (
|
|
supportbot.Ticket?.TicketType !== "threads" &&
|
|
supportbot.Ticket?.PrioritySystem?.Enabled === true
|
|
);
|
|
}
|
|
|
|
function getPriorityKey(interaction, departmentKey) {
|
|
if (!arePrioritiesEnabled()) {
|
|
return null;
|
|
}
|
|
|
|
const prioritySystem = getPrioritySystem();
|
|
const departmentConfig = getDepartmentConfig(departmentKey);
|
|
|
|
return (
|
|
interaction.options?.getString?.("priority") ||
|
|
interaction.priority ||
|
|
departmentConfig?.DefaultPriority ||
|
|
prioritySystem.DefaultPriority ||
|
|
"medium"
|
|
);
|
|
}
|
|
|
|
function getDepartmentConfig(departmentKey) {
|
|
if (!departmentKey) return null;
|
|
return getDepartmentSystem().Departments?.[departmentKey] || null;
|
|
}
|
|
|
|
function getPriorityKey(interaction, departmentKey) {
|
|
if (!arePrioritiesEnabled()) {
|
|
return null;
|
|
}
|
|
|
|
const prioritySystem = getPrioritySystem();
|
|
const departmentConfig = getDepartmentConfig(departmentKey);
|
|
|
|
return (
|
|
interaction.priority ||
|
|
departmentConfig?.DefaultPriority ||
|
|
prioritySystem.DefaultPriority ||
|
|
"medium"
|
|
);
|
|
}
|
|
|
|
function getPriorityConfig(priorityKey) {
|
|
if (!priorityKey) return null;
|
|
return getPrioritySystem().Priorities?.[priorityKey] || null;
|
|
}
|
|
|
|
function buildTicketChannelName(ticketNumberID, priorityKey, departmentConfig) {
|
|
const defaultPrefix = supportbot.Ticket?.ChannelPrefix || supportbot.Ticket?.Channel || "ticket-";
|
|
|
|
const departmentPrefix = departmentConfig?.ChannelPrefix || defaultPrefix;
|
|
|
|
const safePrefix = String(departmentPrefix).trim() || "ticket-";
|
|
const baseName = `${safePrefix}${ticketNumberID}`;
|
|
|
|
if (!arePrioritiesEnabled() || !priorityKey) {
|
|
return baseName;
|
|
}
|
|
|
|
return `${baseName}-${String(priorityKey).toLowerCase()}`;
|
|
}
|
|
|
|
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().catch(() => {});
|
|
await questionMessage.delete().catch(() => {});
|
|
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,
|
|
departmentRole = null,
|
|
attempts = 0,
|
|
) {
|
|
try {
|
|
const clockedInUsers = await getClockedInUsers(interaction.guild);
|
|
|
|
let eligibleClockedInUsers = Array.from(clockedInUsers);
|
|
|
|
if (departmentRole) {
|
|
eligibleClockedInUsers = eligibleClockedInUsers.filter((userId) => {
|
|
const member = interaction.guild.members.cache.get(userId);
|
|
return member?.roles?.cache?.has(departmentRole.id);
|
|
});
|
|
}
|
|
|
|
const claimChannel = interaction.guild.channels.cache.get(
|
|
supportbot.Ticket.ClaimTickets.Channel,
|
|
);
|
|
|
|
if (!claimChannel) {
|
|
console.error("Claim channel not found");
|
|
return null;
|
|
}
|
|
|
|
const claimPing = departmentRole
|
|
? `<@&${departmentRole.id}> <@&${Admin.id}>`
|
|
: `<@&${Staff.id}> <@&${Admin.id}>`;
|
|
|
|
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")
|
|
.setEmoji(supportbot.Ticket.ClaimTickets.ButtonEmoji || null)
|
|
.setStyle(ButtonStyle.Primary);
|
|
|
|
const row = new ActionRowBuilder().addComponents(claimButton);
|
|
|
|
if (eligibleClockedInUsers.length === 0 || attempts >= 2) {
|
|
const claimMessage = await claimChannel.send({
|
|
content: claimPing,
|
|
embeds: [claimEmbed],
|
|
components: [row],
|
|
});
|
|
|
|
const filter = (i) => {
|
|
if (i.customId !== `claimticket-${ticketChannel.id}`) return false;
|
|
|
|
const hasAdmin = i.member.roles.cache.has(Admin.id);
|
|
const hasStaff = i.member.roles.cache.has(Staff.id);
|
|
const hasDepartmentRole = departmentRole
|
|
? i.member.roles.cache.has(departmentRole.id)
|
|
: false;
|
|
|
|
return hasAdmin || hasStaff || hasDepartmentRole;
|
|
};
|
|
|
|
const collector = claimMessage.createMessageComponentCollector({
|
|
filter,
|
|
time: 120000,
|
|
});
|
|
|
|
collector.on("collect", async (i) => {
|
|
await i.deferUpdate().catch(() => {});
|
|
|
|
if (supportbot.Ticket.TicketType === "threads") {
|
|
await ticketChannel.members.add(i.user.id).catch(() => {});
|
|
}
|
|
|
|
await ticketChannel.send({
|
|
content: `<@${i.user.id}> has claimed the ticket.`,
|
|
});
|
|
|
|
if (typeof db.claimTicket === "function") {
|
|
db.claimTicket(ticketChannel.id, i.user.id);
|
|
}
|
|
|
|
await claimMessage
|
|
.edit({
|
|
components: [],
|
|
})
|
|
.catch(() => {});
|
|
});
|
|
|
|
collector.on("end", async (collected) => {
|
|
if (collected.size === 0) {
|
|
await claimMessage.delete().catch(() => {});
|
|
await claimChannel.send({
|
|
content: `Ticket is still unclaimed.\nCC: ${claimPing}`,
|
|
embeds: [
|
|
new EmbedBuilder()
|
|
.setDescription(`Ticket is still unclaimed: <#${ticketChannel.id}>`)
|
|
.setColor(supportbot.Embed.Colours.Warn),
|
|
],
|
|
});
|
|
}
|
|
});
|
|
|
|
return null;
|
|
}
|
|
|
|
const assignedUserId =
|
|
eligibleClockedInUsers[Math.floor(Math.random() * eligibleClockedInUsers.length)];
|
|
|
|
const assignedUser = await interaction.guild.members.fetch(assignedUserId).catch(() => null);
|
|
|
|
if (!assignedUser) {
|
|
return null;
|
|
}
|
|
|
|
const claimMessage = await claimChannel.send({
|
|
content: `${assignedUser}`,
|
|
embeds: [claimEmbed],
|
|
components: [row],
|
|
});
|
|
|
|
const filter = (i) => {
|
|
if (i.customId !== `claimticket-${ticketChannel.id}`) return false;
|
|
return i.user.id === assignedUserId;
|
|
};
|
|
|
|
const collector = claimMessage.createMessageComponentCollector({
|
|
filter,
|
|
time: 120000,
|
|
});
|
|
|
|
collector.on("collect", async (i) => {
|
|
await i.deferUpdate().catch(() => {});
|
|
|
|
if (supportbot.Ticket.TicketType === "threads") {
|
|
await ticketChannel.members.add(i.user.id).catch(() => {});
|
|
}
|
|
|
|
await ticketChannel
|
|
.send({ content: `<@${assignedUserId}> has claimed the ticket.` })
|
|
.catch(() => {});
|
|
|
|
if (typeof db.claimTicket === "function") {
|
|
db.claimTicket(ticketChannel.id, assignedUserId);
|
|
}
|
|
|
|
await claimMessage
|
|
.edit({
|
|
components: [],
|
|
})
|
|
.catch(() => {});
|
|
});
|
|
|
|
collector.on("end", async (collected) => {
|
|
if (collected.size === 0) {
|
|
await claimMessage.delete().catch(() => {});
|
|
|
|
if (attempts >= 1) {
|
|
await claimChannel.send({
|
|
content: `No one claimed the ticket. Tagging roles: ${claimPing}`,
|
|
embeds: [
|
|
new EmbedBuilder()
|
|
.setDescription(`Ticket is still unclaimed: <#${ticketChannel.id}>`)
|
|
.setColor(supportbot.Embed.Colours.Warn),
|
|
],
|
|
});
|
|
|
|
const retryClaimMessage = await claimChannel.send({
|
|
content: claimPing,
|
|
embeds: [claimEmbed],
|
|
components: [row],
|
|
});
|
|
|
|
const retryFilter = (i) => {
|
|
if (i.customId !== `claimticket-${ticketChannel.id}`) return false;
|
|
|
|
const hasAdmin = i.member.roles.cache.has(Admin.id);
|
|
const hasStaff = i.member.roles.cache.has(Staff.id);
|
|
const hasDepartmentRole = departmentRole
|
|
? i.member.roles.cache.has(departmentRole.id)
|
|
: false;
|
|
|
|
return hasAdmin || hasStaff || hasDepartmentRole;
|
|
};
|
|
|
|
const retryCollector = retryClaimMessage.createMessageComponentCollector({
|
|
filter: retryFilter,
|
|
time: 120000,
|
|
});
|
|
|
|
retryCollector.on("collect", async (i) => {
|
|
await i.deferUpdate().catch(() => {});
|
|
|
|
if (supportbot.Ticket.TicketType === "threads") {
|
|
await ticketChannel.members.add(i.user.id).catch(() => {});
|
|
}
|
|
|
|
await ticketChannel.send({
|
|
content: `<@${i.user.id}> has claimed the ticket.`,
|
|
});
|
|
|
|
if (typeof db.claimTicket === "function") {
|
|
db.claimTicket(ticketChannel.id, i.user.id);
|
|
}
|
|
|
|
await retryClaimMessage
|
|
.edit({
|
|
components: [],
|
|
})
|
|
.catch(() => {});
|
|
});
|
|
|
|
retryCollector.on("end", async (retryCollected) => {
|
|
if (retryCollected.size === 0) {
|
|
await retryClaimMessage.delete().catch(() => {});
|
|
await claimChannel.send({
|
|
content: `No one claimed the ticket. Tagging roles: ${claimPing}`,
|
|
});
|
|
}
|
|
});
|
|
} 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,
|
|
departmentRole,
|
|
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: [
|
|
...(areDepartmentsEnabled()
|
|
? [
|
|
{
|
|
name: "department",
|
|
description: "Choose a department",
|
|
type: ApplicationCommandOptionType.String,
|
|
required: true,
|
|
choices: Object.entries(getDepartmentSystem().Departments || {})
|
|
.slice(0, 25)
|
|
.map(([key, dept]) => ({
|
|
name: dept.Name || key,
|
|
value: key,
|
|
})),
|
|
},
|
|
]
|
|
: []),
|
|
|
|
...(arePrioritiesEnabled()
|
|
? [
|
|
{
|
|
name: "priority",
|
|
description: "Choose a priority",
|
|
type: ApplicationCommandOptionType.String,
|
|
required: false,
|
|
choices: Object.entries(getPrioritySystem().Priorities || {})
|
|
.slice(0, 25)
|
|
.map(([key, priority]) => ({
|
|
name: priority.Name || key,
|
|
value: key,
|
|
})),
|
|
},
|
|
]
|
|
: []),
|
|
|
|
{
|
|
name: "reason",
|
|
description: "Ticket Reason",
|
|
type: ApplicationCommandOptionType.String,
|
|
required: false,
|
|
},
|
|
],
|
|
|
|
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.options?.getString?.("reason") || interaction.reason || null;
|
|
}
|
|
|
|
let TicketData = { tickets: db.getAllTickets() };
|
|
|
|
const { getRole, getChannel } = interaction.client;
|
|
|
|
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,
|
|
});
|
|
}
|
|
|
|
const usingThreads = supportbot.Ticket.TicketType === "threads";
|
|
const departmentsEnabled = areDepartmentsEnabled();
|
|
const prioritiesEnabled = arePrioritiesEnabled();
|
|
|
|
const departmentKey = departmentsEnabled ? getSelectedDepartmentKey(interaction) : null;
|
|
const departmentConfig = departmentsEnabled ? getDepartmentConfig(departmentKey) : null;
|
|
|
|
const priorityKey = prioritiesEnabled ? getPriorityKey(interaction, departmentKey) : null;
|
|
const priorityConfig = prioritiesEnabled ? getPriorityConfig(priorityKey) : null;
|
|
|
|
let ticketNumberID = await TicketNumberID.pad();
|
|
const TicketSubject = TicketReason || msgconfig.Ticket.InvalidSubject;
|
|
const ticketChannelName = buildTicketChannelName(
|
|
ticketNumberID,
|
|
priorityKey,
|
|
departmentConfig,
|
|
);
|
|
|
|
const TicketExists = new EmbedBuilder()
|
|
.setTitle("Ticket Exists!")
|
|
.setDescription(msgconfig.Ticket.TicketExists);
|
|
|
|
if (
|
|
await interaction.guild.channels.cache.find(
|
|
(ticketChannel) => ticketChannel.name === ticketChannelName,
|
|
)
|
|
) {
|
|
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);
|
|
|
|
const departmentRoleId = departmentsEnabled ? departmentConfig?.Role || null : null;
|
|
const DepartmentRole = departmentRoleId
|
|
? await getRole(departmentRoleId, interaction.guild)
|
|
: null;
|
|
|
|
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 (usingThreads) {
|
|
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: ticketChannelName,
|
|
type: ChannelType.PrivateThread,
|
|
parent: TicketHome,
|
|
autoArchiveDuration: 60,
|
|
reason: "support ticket",
|
|
});
|
|
} else if (supportbot.Ticket.TicketType === "channels") {
|
|
let targetCategoryId = supportbot.Ticket.TicketChannelsCategory;
|
|
|
|
if (departmentsEnabled && departmentConfig?.Category) {
|
|
targetCategoryId = departmentConfig.Category;
|
|
}
|
|
|
|
const category = interaction.guild.channels.cache.find(
|
|
(c) => c.name === targetCategoryId || c.id === targetCategoryId,
|
|
);
|
|
|
|
if (!category) {
|
|
return interaction.reply({
|
|
content: "The ticket category does not exist!",
|
|
flags: MessageFlags.Ephemeral,
|
|
});
|
|
}
|
|
|
|
ticketChannel = await interaction.guild.channels.create({
|
|
name: ticketChannelName,
|
|
type: ChannelType.GuildText,
|
|
parent: category.id,
|
|
reason: "support ticket",
|
|
});
|
|
|
|
if (!departmentsEnabled && category.children.cache && category.children.cache.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) {
|
|
await assignTicketToUser(ticketChannel, Staff, Admin, interaction, DepartmentRole || null);
|
|
}
|
|
|
|
if (usingThreads) {
|
|
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.id, {
|
|
ViewChannel: true,
|
|
SendMessages: true,
|
|
ReadMessageHistory: true,
|
|
});
|
|
|
|
await ticketChannel.permissionOverwrites.create(Staff.id, {
|
|
ViewChannel: true,
|
|
SendMessages: true,
|
|
ReadMessageHistory: true,
|
|
});
|
|
|
|
if (DepartmentRole) {
|
|
await ticketChannel.permissionOverwrites.create(DepartmentRole.id, {
|
|
ViewChannel: true,
|
|
SendMessages: true,
|
|
ReadMessageHistory: true,
|
|
});
|
|
}
|
|
|
|
await ticketChannel.permissionOverwrites.create(interaction.guild.roles.everyone.id, {
|
|
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,
|
|
department: departmentKey,
|
|
priority: priorityKey,
|
|
});
|
|
|
|
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 ticketMsgContainer = new ContainerBuilder();
|
|
|
|
if (supportbot.Embed.Colours.General) {
|
|
ticketMsgContainer.setAccentColor(
|
|
parseInt(supportbot.Embed.Colours.General.replace("#", ""), 16),
|
|
);
|
|
}
|
|
|
|
const ticketAuthor = new TextDisplayBuilder().setContent(
|
|
msgconfig.Ticket.TicketAuthorTitle.replace(/%ticketauthor%/g, interaction.user.id)
|
|
.replace(/%ticketid%/g, ticketChannel.id)
|
|
.replace(/%ticketusername%/g, interaction.user.username),
|
|
);
|
|
|
|
ticketMsgContainer.addTextDisplayComponents(ticketAuthor);
|
|
|
|
const ticketTitle = new TextDisplayBuilder().setContent(
|
|
msgconfig.Ticket.TicketTitle.replace(/%ticketauthor%/g, interaction.user.id)
|
|
.replace(/%ticketid%/g, ticketChannel.id)
|
|
.replace(/%ticketusername%/g, interaction.user.username),
|
|
);
|
|
|
|
ticketMsgContainer.addTextDisplayComponents(ticketTitle);
|
|
|
|
const seperator2 = new SeparatorBuilder().setDivider(true);
|
|
ticketMsgContainer.addSeparatorComponents(seperator2);
|
|
|
|
const ticketDescription = new TextDisplayBuilder().setContent(
|
|
msgconfig.Ticket.TicketMessage.replace(/%ticketauthor%/g, interaction.user.id)
|
|
.replace(/%ticketid%/g, ticketChannel.id)
|
|
.replace(/%ticketusername%/g, interaction.user.username)
|
|
.replace(/%ticketreason%/g, TicketSubject),
|
|
);
|
|
|
|
ticketMsgContainer.addTextDisplayComponents(ticketDescription);
|
|
|
|
const seperator3 = new SeparatorBuilder().setDivider(true);
|
|
ticketMsgContainer.addSeparatorComponents(seperator3);
|
|
|
|
if (supportbot.Ticket.TicketReason && TicketReason) {
|
|
const ticketReason = new TextDisplayBuilder().setContent(`**Reason:**\n${TicketReason}`);
|
|
ticketMsgContainer.addTextDisplayComponents(ticketReason);
|
|
}
|
|
|
|
if (departmentsEnabled && departmentConfig) {
|
|
const deptLabel = departmentConfig?.Name || departmentKey || "General";
|
|
const deptEmoji = departmentConfig?.Emoji || "🎫";
|
|
|
|
const departmentText = new TextDisplayBuilder().setContent(
|
|
`**Department:** ${deptEmoji} ${deptLabel}`,
|
|
);
|
|
|
|
ticketMsgContainer.addTextDisplayComponents(departmentText);
|
|
}
|
|
|
|
if (prioritiesEnabled && priorityConfig && priorityKey) {
|
|
const priorityLabel = priorityConfig?.Name || priorityKey || "Medium";
|
|
const priorityEmoji = priorityConfig?.Emoji || "🟡";
|
|
|
|
const priorityText = new TextDisplayBuilder().setContent(
|
|
`**Priority:** ${priorityEmoji} ${priorityLabel}`,
|
|
);
|
|
|
|
ticketMsgContainer.addTextDisplayComponents(priorityText);
|
|
}
|
|
|
|
const selectMenuRow = new ActionRowBuilder();
|
|
|
|
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"),
|
|
);
|
|
}
|
|
|
|
selectMenuRow.addComponents(SelectMenus);
|
|
ticketMsgContainer.addActionRowComponents(selectMenuRow);
|
|
|
|
await ticketChannel.send({
|
|
flags: MessageFlags.IsComponentsV2,
|
|
components: [ticketMsgContainer],
|
|
});
|
|
|
|
const departmentQuestions =
|
|
departmentsEnabled && departmentConfig?.Questions ? departmentConfig.Questions : [];
|
|
|
|
const globalQuestionsEnabled = supportbot.Ticket.Questions?.Enabled;
|
|
const globalQuestions = supportbot.Ticket.Questions?.List || [];
|
|
|
|
let questionsToAsk = [];
|
|
|
|
if (departmentQuestions.length > 0) {
|
|
questionsToAsk = departmentQuestions;
|
|
} else if (globalQuestionsEnabled && globalQuestions.length > 0) {
|
|
questionsToAsk = globalQuestions;
|
|
}
|
|
|
|
if (questionsToAsk.length > 0) {
|
|
await askTicketQuestions(ticketChannel, interaction, questionsToAsk);
|
|
}
|
|
} catch (error) {
|
|
console.error("Error in run method:", error);
|
|
|
|
if (!interaction.replied && !interaction.deferred) {
|
|
return interaction.reply({
|
|
content: "An error occurred while creating the ticket.",
|
|
flags: MessageFlags.Ephemeral,
|
|
});
|
|
}
|
|
}
|
|
},
|
|
});
|