blob: 863c65966b16d94048a4424e65596e6bef843aa4 [file] [log] [blame]
pineafan63fc5e22022-08-04 22:04:10 +01001import { LoadingEmbed } from "./../../utils/defaultEmbeds.js";
pineafan6702cef2022-06-13 17:52:37 +01002import getEmojiByName from "../../utils/getEmojiByName.js";
pineafan4edb7762022-06-26 19:21:04 +01003import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
pineafan6702cef2022-06-13 17:52:37 +01004import confirmationMessage from "../../utils/confirmationMessage.js";
Skyler Grey75ea9172022-08-06 10:22:23 +01005import Discord, {
6 CommandInteraction,
7 GuildChannel,
8 Message,
9 MessageActionRow,
10 MessageActionRowComponent,
11 MessageButton,
12 MessageComponentInteraction,
13 MessageSelectMenu,
14 Role,
15 SelectMenuInteraction,
16 TextInputComponent
17} from "discord.js";
Skyler Grey11236ba2022-08-08 21:13:33 +010018import { SelectMenuOption, SlashCommandSubcommandBuilder } from "@discordjs/builders";
Skyler Greyc634e2b2022-08-06 17:50:48 +010019import { ChannelType } from "discord-api-types/v9";
pineafan6702cef2022-06-13 17:52:37 +010020import client from "../../utils/client.js";
Skyler Grey11236ba2022-08-08 21:13:33 +010021import { toHexInteger, toHexArray, tickets as ticketTypes } from "../../utils/calculate.js";
pineafan63fc5e22022-08-04 22:04:10 +010022import { capitalize } from "../../utils/generateKeyValueList.js";
pineafan6702cef2022-06-13 17:52:37 +010023import { modalInteractionCollector } from "../../utils/dualCollector.js";
pineafan3a02ea32022-08-11 21:35:04 +010024import type { GuildConfig } from "../../utils/database.js";
pineafan4f164f32022-02-26 22:07:12 +000025
Skyler Grey75ea9172022-08-06 10:22:23 +010026const command = (builder: SlashCommandSubcommandBuilder) =>
27 builder
28 .setName("tickets")
Skyler Grey11236ba2022-08-08 21:13:33 +010029 .setDescription("Shows settings for tickets | Use no arguments to manage custom types")
Skyler Grey75ea9172022-08-06 10:22:23 +010030 .addStringOption((option) =>
31 option
32 .setName("enabled")
33 .setDescription("If users should be able to create tickets")
34 .setRequired(false)
35 .addChoices([
36 ["Yes", "yes"],
37 ["No", "no"]
38 ])
39 )
40 .addChannelOption((option) =>
41 option
42 .setName("category")
43 .setDescription("The category where tickets are created")
44 .addChannelType(ChannelType.GuildCategory)
45 .setRequired(false)
46 )
47 .addNumberOption((option) =>
48 option
49 .setName("maxticketsperuser")
Skyler Grey11236ba2022-08-08 21:13:33 +010050 .setDescription("The maximum amount of tickets a user can create | Default: 5")
Skyler Grey75ea9172022-08-06 10:22:23 +010051 .setRequired(false)
52 .setMinValue(1)
53 )
54 .addRoleOption((option) =>
55 option
56 .setName("supportrole")
57 .setDescription(
58 "This role will have view access to all tickets and will be pinged when a ticket is created"
59 )
60 .setRequired(false)
61 );
pineafan4f164f32022-02-26 22:07:12 +000062
pineafan3a02ea32022-08-11 21:35:04 +010063const callback = async (interaction: CommandInteraction): Promise<unknown> => {
Skyler Grey75ea9172022-08-06 10:22:23 +010064 let m = (await interaction.reply({
65 embeds: LoadingEmbed,
66 ephemeral: true,
67 fetchReply: true
68 })) as Message;
pineafan63fc5e22022-08-04 22:04:10 +010069 const options = {
Skyler Greyad002172022-08-16 18:48:26 +010070 enabled: interaction.options.getString("enabled")?.startsWith("yes") as boolean | null,
pineafan6702cef2022-06-13 17:52:37 +010071 category: interaction.options.getChannel("category"),
72 maxtickets: interaction.options.getNumber("maxticketsperuser"),
73 supportping: interaction.options.getRole("supportrole")
pineafan63fc5e22022-08-04 22:04:10 +010074 };
Skyler Grey11236ba2022-08-08 21:13:33 +010075 if (options.enabled !== null || options.category || options.maxtickets || options.supportping) {
pineafan6702cef2022-06-13 17:52:37 +010076 if (options.category) {
pineafan3a02ea32022-08-11 21:35:04 +010077 let channel: GuildChannel | null;
pineafan6702cef2022-06-13 17:52:37 +010078 try {
pineafan3a02ea32022-08-11 21:35:04 +010079 channel = await interaction.guild!.channels.fetch(options.category.id);
pineafan6702cef2022-06-13 17:52:37 +010080 } catch {
81 return await interaction.editReply({
Skyler Grey75ea9172022-08-06 10:22:23 +010082 embeds: [
83 new EmojiEmbed()
84 .setEmoji("CHANNEL.TEXT.DELETE")
85 .setTitle("Tickets > Category")
Skyler Grey11236ba2022-08-08 21:13:33 +010086 .setDescription("The channel you provided is not a valid category")
Skyler Grey75ea9172022-08-06 10:22:23 +010087 .setStatus("Danger")
pineafan6702cef2022-06-13 17:52:37 +010088 ]
pineafan63fc5e22022-08-04 22:04:10 +010089 });
pineafan6702cef2022-06-13 17:52:37 +010090 }
pineafan3a02ea32022-08-11 21:35:04 +010091 if (!channel) return;
pineafan63fc5e22022-08-04 22:04:10 +010092 channel = channel as Discord.CategoryChannel;
pineafan3a02ea32022-08-11 21:35:04 +010093 if (channel.guild.id !== interaction.guild!.id)
Skyler Grey75ea9172022-08-06 10:22:23 +010094 return interaction.editReply({
95 embeds: [
96 new EmojiEmbed()
97 .setTitle("Tickets > Category")
Skyler Grey11236ba2022-08-08 21:13:33 +010098 .setDescription("You must choose a category in this server")
Skyler Grey75ea9172022-08-06 10:22:23 +010099 .setStatus("Danger")
100 .setEmoji("CHANNEL.TEXT.DELETE")
101 ]
102 });
pineafan6702cef2022-06-13 17:52:37 +0100103 }
104 if (options.maxtickets) {
Skyler Grey75ea9172022-08-06 10:22:23 +0100105 if (options.maxtickets < 1)
106 return interaction.editReply({
107 embeds: [
108 new EmojiEmbed()
109 .setTitle("Tickets > Max Tickets")
Skyler Grey11236ba2022-08-08 21:13:33 +0100110 .setDescription("You must choose a number greater than 0")
Skyler Grey75ea9172022-08-06 10:22:23 +0100111 .setStatus("Danger")
112 .setEmoji("CHANNEL.TEXT.DELETE")
113 ]
114 });
pineafan6702cef2022-06-13 17:52:37 +0100115 }
pineafan3a02ea32022-08-11 21:35:04 +0100116 let role: Role | null;
pineafan6702cef2022-06-13 17:52:37 +0100117 if (options.supportping) {
118 try {
pineafan3a02ea32022-08-11 21:35:04 +0100119 role = await interaction.guild!.roles.fetch(options.supportping.id);
pineafan6702cef2022-06-13 17:52:37 +0100120 } catch {
121 return await interaction.editReply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100122 embeds: [
123 new EmojiEmbed()
124 .setEmoji("GUILD.ROLE.DELETE")
125 .setTitle("Tickets > Support Ping")
Skyler Grey11236ba2022-08-08 21:13:33 +0100126 .setDescription("The role you provided is not a valid role")
Skyler Grey75ea9172022-08-06 10:22:23 +0100127 .setStatus("Danger")
pineafan6702cef2022-06-13 17:52:37 +0100128 ]
pineafan63fc5e22022-08-04 22:04:10 +0100129 });
pineafan6702cef2022-06-13 17:52:37 +0100130 }
pineafan3a02ea32022-08-11 21:35:04 +0100131 if (!role) return;
pineafan63fc5e22022-08-04 22:04:10 +0100132 role = role as Discord.Role;
pineafan3a02ea32022-08-11 21:35:04 +0100133 if (role.guild.id !== interaction.guild!.id)
Skyler Grey75ea9172022-08-06 10:22:23 +0100134 return interaction.editReply({
135 embeds: [
136 new EmojiEmbed()
137 .setTitle("Tickets > Support Ping")
Skyler Grey11236ba2022-08-08 21:13:33 +0100138 .setDescription("You must choose a role in this server")
Skyler Grey75ea9172022-08-06 10:22:23 +0100139 .setStatus("Danger")
140 .setEmoji("GUILD.ROLE.DELETE")
141 ]
142 });
pineafan6702cef2022-06-13 17:52:37 +0100143 }
144
pineafan63fc5e22022-08-04 22:04:10 +0100145 const confirmation = await new confirmationMessage(interaction)
pineafan62ce1922022-08-25 20:34:45 +0100146 .setEmoji("GUILD.TICKET.ARCHIVED", "GUILD.TICKET.CLOSE")
pineafan6702cef2022-06-13 17:52:37 +0100147 .setTitle("Tickets")
148 .setDescription(
Skyler Grey11236ba2022-08-08 21:13:33 +0100149 (options.category ? `**Category:** ${options.category.name}\n` : "") +
150 (options.maxtickets ? `**Max Tickets:** ${options.maxtickets}\n` : "") +
151 (options.supportping ? `**Support Ping:** ${options.supportping.name}\n` : "") +
Skyler Grey75ea9172022-08-06 10:22:23 +0100152 (options.enabled !== null
153 ? `**Enabled:** ${
pineafan62ce1922022-08-25 20:34:45 +0100154 options.enabled
155 ? `${getEmojiByName("CONTROL.TICK")} Yes`
156 : `${getEmojiByName("CONTROL.CROSS")} No`
157 }\n`
Skyler Grey75ea9172022-08-06 10:22:23 +0100158 : "") +
159 "\nAre you sure you want to apply these settings?"
pineafan6702cef2022-06-13 17:52:37 +0100160 )
161 .setColor("Warning")
162 .setInverted(true)
pineafan63fc5e22022-08-04 22:04:10 +0100163 .send(true);
164 if (confirmation.cancelled) return;
pineafan6702cef2022-06-13 17:52:37 +0100165 if (confirmation.success) {
pineafan3a02ea32022-08-11 21:35:04 +0100166 const toUpdate: Record<string, string | boolean | number> = {};
Skyler Grey11236ba2022-08-08 21:13:33 +0100167 if (options.enabled !== null) toUpdate["tickets.enabled"] = options.enabled;
168 if (options.category) toUpdate["tickets.category"] = options.category.id;
169 if (options.maxtickets) toUpdate["tickets.maxTickets"] = options.maxtickets;
170 if (options.supportping) toUpdate["tickets.supportRole"] = options.supportping.id;
pineafan6702cef2022-06-13 17:52:37 +0100171 try {
pineafan3a02ea32022-08-11 21:35:04 +0100172 await client.database.guilds.write(interaction.guild!.id, toUpdate);
pineafan6702cef2022-06-13 17:52:37 +0100173 } catch (e) {
174 return interaction.editReply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100175 embeds: [
176 new EmojiEmbed()
177 .setTitle("Tickets")
Skyler Grey11236ba2022-08-08 21:13:33 +0100178 .setDescription("Something went wrong and the staff notifications channel could not be set")
Skyler Grey75ea9172022-08-06 10:22:23 +0100179 .setStatus("Danger")
180 .setEmoji("GUILD.TICKET.DELETE")
181 ],
182 components: []
pineafan6702cef2022-06-13 17:52:37 +0100183 });
184 }
185 } else {
186 return interaction.editReply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100187 embeds: [
188 new EmojiEmbed()
189 .setTitle("Tickets")
190 .setDescription("No changes were made")
191 .setStatus("Success")
192 .setEmoji("GUILD.TICKET.OPEN")
193 ],
194 components: []
pineafan6702cef2022-06-13 17:52:37 +0100195 });
196 }
197 }
pineafan3a02ea32022-08-11 21:35:04 +0100198 let data = await client.database.guilds.read(interaction.guild!.id);
Skyler Grey75ea9172022-08-06 10:22:23 +0100199 data.tickets.customTypes = (data.tickets.customTypes || []).filter(
Skyler Grey11236ba2022-08-08 21:13:33 +0100200 (value: string, index: number, array: string[]) => array.indexOf(value) === index
Skyler Grey75ea9172022-08-06 10:22:23 +0100201 );
pineafan6702cef2022-06-13 17:52:37 +0100202 let lastClicked = "";
Skyler Grey1a67e182022-08-04 23:05:44 +0100203 let embed: EmojiEmbed;
pineafan6702cef2022-06-13 17:52:37 +0100204 data = {
205 enabled: data.tickets.enabled,
206 category: data.tickets.category,
207 maxTickets: data.tickets.maxTickets,
208 supportRole: data.tickets.supportRole,
209 useCustom: data.tickets.useCustom,
210 types: data.tickets.types,
211 customTypes: data.tickets.customTypes
pineafan63fc5e22022-08-04 22:04:10 +0100212 };
Skyler Greyad002172022-08-16 18:48:26 +0100213 let timedOut = false;
214 while (!timedOut) {
pineafan4edb7762022-06-26 19:21:04 +0100215 embed = new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +0100216 .setTitle("Tickets")
217 .setDescription(
Skyler Grey11236ba2022-08-08 21:13:33 +0100218 `${data.enabled ? "" : getEmojiByName("TICKETS.REPORT")} **Enabled:** ${
219 data.enabled ? `${getEmojiByName("CONTROL.TICK")} Yes` : `${getEmojiByName("CONTROL.CROSS")} No`
Skyler Grey75ea9172022-08-06 10:22:23 +0100220 }\n` +
Skyler Grey11236ba2022-08-08 21:13:33 +0100221 `${data.category ? "" : getEmojiByName("TICKETS.REPORT")} **Category:** ${
Skyler Grey75ea9172022-08-06 10:22:23 +0100222 data.category ? `<#${data.category}>` : "*None set*"
223 }\n` +
Skyler Grey11236ba2022-08-08 21:13:33 +0100224 `**Max Tickets:** ${data.maxTickets ? data.maxTickets : "*No limit*"}\n` +
225 `**Support Ping:** ${data.supportRole ? `<@&${data.supportRole}>` : "*None set*"}\n\n` +
226 (data.useCustom && data.customTypes === null ? `${getEmojiByName("TICKETS.REPORT")} ` : "") +
Skyler Grey75ea9172022-08-06 10:22:23 +0100227 `${data.useCustom ? "Custom" : "Default"} types in use` +
228 "\n\n" +
Skyler Grey11236ba2022-08-08 21:13:33 +0100229 `${getEmojiByName("TICKETS.REPORT")} *Indicates a setting stopping tickets from being used*`
pineafan6702cef2022-06-13 17:52:37 +0100230 )
231 .setStatus("Success")
pineafan63fc5e22022-08-04 22:04:10 +0100232 .setEmoji("GUILD.TICKET.OPEN");
Skyler Grey75ea9172022-08-06 10:22:23 +0100233 m = (await interaction.editReply({
234 embeds: [embed],
235 components: [
236 new MessageActionRow().addComponents([
237 new MessageButton()
Skyler Grey11236ba2022-08-08 21:13:33 +0100238 .setLabel("Tickets " + (data.enabled ? "enabled" : "disabled"))
239 .setEmoji(getEmojiByName("CONTROL." + (data.enabled ? "TICK" : "CROSS"), "id"))
Skyler Grey75ea9172022-08-06 10:22:23 +0100240 .setStyle(data.enabled ? "SUCCESS" : "DANGER")
241 .setCustomId("enabled"),
242 new MessageButton()
Skyler Grey11236ba2022-08-08 21:13:33 +0100243 .setLabel(lastClicked === "cat" ? "Click again to confirm" : "Clear category")
Skyler Grey75ea9172022-08-06 10:22:23 +0100244 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
245 .setStyle("DANGER")
246 .setCustomId("clearCategory")
247 .setDisabled(data.category === null),
248 new MessageButton()
Skyler Grey11236ba2022-08-08 21:13:33 +0100249 .setLabel(lastClicked === "max" ? "Click again to confirm" : "Reset max tickets")
Skyler Grey75ea9172022-08-06 10:22:23 +0100250 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
251 .setStyle("DANGER")
252 .setCustomId("clearMaxTickets")
253 .setDisabled(data.maxTickets === 5),
254 new MessageButton()
Skyler Grey11236ba2022-08-08 21:13:33 +0100255 .setLabel(lastClicked === "sup" ? "Click again to confirm" : "Clear support ping")
Skyler Grey75ea9172022-08-06 10:22:23 +0100256 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
257 .setStyle("DANGER")
258 .setCustomId("clearSupportPing")
259 .setDisabled(data.supportRole === null)
260 ]),
261 new MessageActionRow().addComponents([
262 new MessageButton()
263 .setLabel("Manage types")
264 .setEmoji(getEmojiByName("TICKETS.OTHER", "id"))
265 .setStyle("SECONDARY")
266 .setCustomId("manageTypes"),
267 new MessageButton()
268 .setLabel("Add create ticket button")
269 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
270 .setStyle("PRIMARY")
271 .setCustomId("send")
272 ])
273 ]
274 })) as Message;
Skyler Grey1a67e182022-08-04 23:05:44 +0100275 let i: MessageComponentInteraction;
pineafan6702cef2022-06-13 17:52:37 +0100276 try {
pineafanbd02b4a2022-08-05 22:01:38 +0100277 i = await m.awaitMessageComponent({ time: 300000 });
Skyler Grey75ea9172022-08-06 10:22:23 +0100278 } catch (e) {
Skyler Greyad002172022-08-16 18:48:26 +0100279 timedOut = true;
280 continue;
Skyler Grey75ea9172022-08-06 10:22:23 +0100281 }
pineafan63fc5e22022-08-04 22:04:10 +0100282 i.deferUpdate();
Skyler Grey11236ba2022-08-08 21:13:33 +0100283 if ((i.component as MessageActionRowComponent).customId === "clearCategory") {
pineafane23c4ec2022-07-27 21:56:27 +0100284 if (lastClicked === "cat") {
pineafan6702cef2022-06-13 17:52:37 +0100285 lastClicked = "";
pineafan3a02ea32022-08-11 21:35:04 +0100286 await client.database.guilds.write(interaction.guild!.id, null, ["tickets.category"]);
pineafan6702cef2022-06-13 17:52:37 +0100287 data.category = undefined;
288 } else lastClicked = "cat";
Skyler Grey11236ba2022-08-08 21:13:33 +0100289 } else if ((i.component as MessageActionRowComponent).customId === "clearMaxTickets") {
pineafane23c4ec2022-07-27 21:56:27 +0100290 if (lastClicked === "max") {
pineafan6702cef2022-06-13 17:52:37 +0100291 lastClicked = "";
pineafan3a02ea32022-08-11 21:35:04 +0100292 await client.database.guilds.write(interaction.guild!.id, null, ["tickets.maxTickets"]);
pineafan6702cef2022-06-13 17:52:37 +0100293 data.maxTickets = 5;
294 } else lastClicked = "max";
Skyler Grey11236ba2022-08-08 21:13:33 +0100295 } else if ((i.component as MessageActionRowComponent).customId === "clearSupportPing") {
pineafane23c4ec2022-07-27 21:56:27 +0100296 if (lastClicked === "sup") {
pineafan6702cef2022-06-13 17:52:37 +0100297 lastClicked = "";
pineafan3a02ea32022-08-11 21:35:04 +0100298 await client.database.guilds.write(interaction.guild!.id, null, ["tickets.supportRole"]);
pineafan6702cef2022-06-13 17:52:37 +0100299 data.supportRole = undefined;
300 } else lastClicked = "sup";
Skyler Grey11236ba2022-08-08 21:13:33 +0100301 } else if ((i.component as MessageActionRowComponent).customId === "send") {
pineafan41d93562022-07-30 22:10:15 +0100302 const ticketMessages = [
Skyler Grey75ea9172022-08-06 10:22:23 +0100303 {
304 label: "Create ticket",
305 description: "Click the button below to create a ticket"
306 },
307 {
308 label: "Issues, questions or feedback?",
Skyler Grey11236ba2022-08-08 21:13:33 +0100309 description: "Click below to open a ticket and get help from our staff team"
Skyler Grey75ea9172022-08-06 10:22:23 +0100310 },
311 {
312 label: "Contact Us",
Skyler Grey11236ba2022-08-08 21:13:33 +0100313 description: "Click the button below to speak to us privately"
Skyler Grey75ea9172022-08-06 10:22:23 +0100314 }
pineafan63fc5e22022-08-04 22:04:10 +0100315 ];
Skyler Greyad002172022-08-16 18:48:26 +0100316 let innerTimedOut = false;
317 let templateSelected = false;
318 while (!innerTimedOut && !templateSelected) {
pineafan63fc5e22022-08-04 22:04:10 +0100319 const enabled = data.enabled && data.category !== null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100320 await interaction.editReply({
321 embeds: [
322 new EmojiEmbed()
323 .setTitle("Ticket Button")
Skyler Grey11236ba2022-08-08 21:13:33 +0100324 .setDescription("Select a message template to send in this channel")
Skyler Grey75ea9172022-08-06 10:22:23 +0100325 .setFooter({
326 text: enabled
327 ? ""
328 : "Tickets are not set up correctly so the button may not work for users. Check the main menu to find which options must be set."
329 })
330 .setStatus(enabled ? "Success" : "Warning")
331 .setEmoji("GUILD.ROLES.CREATE")
332 ],
333 components: [
334 new MessageActionRow().addComponents([
335 new MessageSelectMenu()
336 .setOptions(
337 ticketMessages.map(
338 (
339 t: {
340 label: string;
341 description: string;
342 value?: string;
343 },
344 index
345 ) => {
346 t.value = index.toString();
347 return t as {
348 value: string;
349 label: string;
350 description: string;
351 };
352 }
353 )
354 )
355 .setCustomId("template")
356 .setMaxValues(1)
357 .setMinValues(1)
358 .setPlaceholder("Select a message template")
359 ]),
360 new MessageActionRow().addComponents([
361 new MessageButton()
362 .setCustomId("back")
363 .setLabel("Back")
364 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
365 .setStyle("DANGER"),
Skyler Grey11236ba2022-08-08 21:13:33 +0100366 new MessageButton().setCustomId("blank").setLabel("Empty").setStyle("SECONDARY"),
Skyler Grey75ea9172022-08-06 10:22:23 +0100367 new MessageButton()
368 .setCustomId("custom")
369 .setLabel("Custom")
370 .setEmoji(getEmojiByName("TICKETS.OTHER", "id"))
371 .setStyle("PRIMARY")
372 ])
373 ]
374 });
Skyler Grey1a67e182022-08-04 23:05:44 +0100375 let i: MessageComponentInteraction;
pineafan41d93562022-07-30 22:10:15 +0100376 try {
Skyler Grey75ea9172022-08-06 10:22:23 +0100377 i = await m.awaitMessageComponent({ time: 300000 });
378 } catch (e) {
Skyler Greyad002172022-08-16 18:48:26 +0100379 innerTimedOut = true;
380 continue;
Skyler Grey75ea9172022-08-06 10:22:23 +0100381 }
Skyler Grey11236ba2022-08-08 21:13:33 +0100382 if ((i.component as MessageActionRowComponent).customId === "template") {
pineafan63fc5e22022-08-04 22:04:10 +0100383 i.deferUpdate();
pineafan3a02ea32022-08-11 21:35:04 +0100384 await interaction.channel!.send({
Skyler Grey75ea9172022-08-06 10:22:23 +0100385 embeds: [
386 new EmojiEmbed()
pineafan3a02ea32022-08-11 21:35:04 +0100387 .setTitle(ticketMessages[parseInt((i as SelectMenuInteraction).values[0]!)]!.label)
Skyler Grey75ea9172022-08-06 10:22:23 +0100388 .setDescription(
pineafan3a02ea32022-08-11 21:35:04 +0100389 ticketMessages[parseInt((i as SelectMenuInteraction).values[0]!)]!.description
Skyler Grey75ea9172022-08-06 10:22:23 +0100390 )
391 .setStatus("Success")
392 .setEmoji("GUILD.TICKET.OPEN")
393 ],
394 components: [
395 new MessageActionRow().addComponents([
396 new MessageButton()
397 .setLabel("Create Ticket")
Skyler Grey11236ba2022-08-08 21:13:33 +0100398 .setEmoji(getEmojiByName("CONTROL.TICK", "id"))
Skyler Grey75ea9172022-08-06 10:22:23 +0100399 .setStyle("SUCCESS")
400 .setCustomId("createticket")
401 ])
402 ]
403 });
Skyler Greyad002172022-08-16 18:48:26 +0100404 templateSelected = true;
405 continue;
Skyler Grey11236ba2022-08-08 21:13:33 +0100406 } else if ((i.component as MessageActionRowComponent).customId === "blank") {
Skyler Grey75ea9172022-08-06 10:22:23 +0100407 i.deferUpdate();
pineafan3a02ea32022-08-11 21:35:04 +0100408 await interaction.channel!.send({
Skyler Grey75ea9172022-08-06 10:22:23 +0100409 components: [
410 new MessageActionRow().addComponents([
411 new MessageButton()
412 .setLabel("Create Ticket")
Skyler Grey11236ba2022-08-08 21:13:33 +0100413 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
Skyler Grey75ea9172022-08-06 10:22:23 +0100414 .setStyle("SUCCESS")
415 .setCustomId("createticket")
416 ])
417 ]
418 });
Skyler Greyad002172022-08-16 18:48:26 +0100419 templateSelected = true;
420 continue;
Skyler Grey11236ba2022-08-08 21:13:33 +0100421 } else if ((i.component as MessageActionRowComponent).customId === "custom") {
Skyler Grey75ea9172022-08-06 10:22:23 +0100422 await i.showModal(
423 new Discord.Modal()
424 .setCustomId("modal")
425 .setTitle("Enter embed details")
426 .addComponents(
427 new MessageActionRow<TextInputComponent>().addComponents(
428 new TextInputComponent()
429 .setCustomId("title")
430 .setLabel("Title")
431 .setMaxLength(256)
432 .setRequired(true)
433 .setStyle("SHORT")
434 ),
435 new MessageActionRow<TextInputComponent>().addComponents(
436 new TextInputComponent()
437 .setCustomId("description")
438 .setLabel("Description")
439 .setMaxLength(4000)
440 .setRequired(true)
441 .setStyle("PARAGRAPH")
442 )
443 )
444 );
pineafan41d93562022-07-30 22:10:15 +0100445 await interaction.editReply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100446 embeds: [
447 new EmojiEmbed()
448 .setTitle("Ticket Button")
Skyler Grey11236ba2022-08-08 21:13:33 +0100449 .setDescription("Modal opened. If you can't see it, click back and try again.")
Skyler Grey75ea9172022-08-06 10:22:23 +0100450 .setStatus("Success")
451 .setEmoji("GUILD.TICKET.OPEN")
452 ],
453 components: [
454 new MessageActionRow().addComponents([
455 new MessageButton()
456 .setLabel("Back")
Skyler Grey11236ba2022-08-08 21:13:33 +0100457 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
Skyler Grey75ea9172022-08-06 10:22:23 +0100458 .setStyle("PRIMARY")
459 .setCustomId("back")
460 ])
461 ]
pineafan41d93562022-07-30 22:10:15 +0100462 });
463 let out;
464 try {
Skyler Grey75ea9172022-08-06 10:22:23 +0100465 out = await modalInteractionCollector(
466 m,
pineafan3a02ea32022-08-11 21:35:04 +0100467 (m) => m.channel!.id === interaction.channel!.id,
Skyler Grey75ea9172022-08-06 10:22:23 +0100468 (m) => m.customId === "modify"
469 );
470 } catch (e) {
Skyler Greyad002172022-08-16 18:48:26 +0100471 innerTimedOut = true;
472 continue;
Skyler Grey75ea9172022-08-06 10:22:23 +0100473 }
pineafan41d93562022-07-30 22:10:15 +0100474 if (out.fields) {
pineafan63fc5e22022-08-04 22:04:10 +0100475 const title = out.fields.getTextInputValue("title");
Skyler Grey11236ba2022-08-08 21:13:33 +0100476 const description = out.fields.getTextInputValue("description");
Skyler Grey75ea9172022-08-06 10:22:23 +0100477 await interaction.channel.send({
478 embeds: [
479 new EmojiEmbed()
480 .setTitle(title)
481 .setDescription(description)
482 .setStatus("Success")
483 .setEmoji("GUILD.TICKET.OPEN")
484 ],
485 components: [
486 new MessageActionRow().addComponents([
487 new MessageButton()
488 .setLabel("Create Ticket")
Skyler Grey11236ba2022-08-08 21:13:33 +0100489 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
Skyler Grey75ea9172022-08-06 10:22:23 +0100490 .setStyle("SUCCESS")
491 .setCustomId("createticket")
492 ])
493 ]
494 });
Skyler Greyad002172022-08-16 18:48:26 +0100495 templateSelected = true;
Skyler Grey75ea9172022-08-06 10:22:23 +0100496 }
pineafan41d93562022-07-30 22:10:15 +0100497 }
498 }
Skyler Grey11236ba2022-08-08 21:13:33 +0100499 } else if ((i.component as MessageActionRowComponent).customId === "enabled") {
Skyler Grey75ea9172022-08-06 10:22:23 +0100500 await client.database.guilds.write(interaction.guild.id, {
501 "tickets.enabled": !data.enabled
502 });
pineafan6702cef2022-06-13 17:52:37 +0100503 data.enabled = !data.enabled;
Skyler Grey11236ba2022-08-08 21:13:33 +0100504 } else if ((i.component as MessageActionRowComponent).customId === "manageTypes") {
Skyler Grey1a67e182022-08-04 23:05:44 +0100505 data = await manageTypes(interaction, data, m as Message);
pineafan6702cef2022-06-13 17:52:37 +0100506 }
507 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100508 await interaction.editReply({
Skyler Greyad002172022-08-16 18:48:26 +0100509 embeds: [embed.setFooter({ text: "Message timed out" })],
Skyler Grey75ea9172022-08-06 10:22:23 +0100510 components: []
511 });
pineafan63fc5e22022-08-04 22:04:10 +0100512};
pineafan4f164f32022-02-26 22:07:12 +0000513
Skyler Grey11236ba2022-08-08 21:13:33 +0100514async function manageTypes(interaction: CommandInteraction, data: GuildConfig["tickets"], m: Message) {
Skyler Greyad002172022-08-16 18:48:26 +0100515 let timedOut = false;
516 let backPressed = false;
517 while (!timedOut && !backPressed) {
pineafan6702cef2022-06-13 17:52:37 +0100518 if (data.useCustom) {
pineafan63fc5e22022-08-04 22:04:10 +0100519 const customTypes = data.customTypes;
pineafanc6158ab2022-06-17 16:34:07 +0100520 await interaction.editReply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100521 embeds: [
522 new EmojiEmbed()
523 .setTitle("Tickets > Types")
524 .setDescription(
525 "**Custom types enabled**\n\n" +
526 "**Types in use:**\n" +
Skyler Grey11236ba2022-08-08 21:13:33 +0100527 (customTypes !== null ? customTypes.map((t) => `> ${t}`).join("\n") : "*None set*") +
Skyler Grey75ea9172022-08-06 10:22:23 +0100528 "\n\n" +
529 (customTypes === null
530 ? `${getEmojiByName(
531 "TICKETS.REPORT"
532 )} Having no types will disable tickets. Please add at least 1 type or use default types`
533 : "")
pineafan6702cef2022-06-13 17:52:37 +0100534 )
Skyler Grey75ea9172022-08-06 10:22:23 +0100535 .setStatus("Success")
536 .setEmoji("GUILD.TICKET.OPEN")
537 ],
538 components: (customTypes
539 ? [
540 new MessageActionRow().addComponents([
541 new Discord.MessageSelectMenu()
542 .setCustomId("removeTypes")
543 .setPlaceholder("Select types to remove")
544 .setMaxValues(customTypes.length)
545 .setMinValues(1)
546 .addOptions(
547 customTypes.map((t) => ({
548 label: t,
549 value: t
550 }))
551 )
552 ])
553 ]
554 : []
555 ).concat([
pineafan6702cef2022-06-13 17:52:37 +0100556 new MessageActionRow().addComponents([
557 new MessageButton()
558 .setLabel("Back")
559 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
560 .setStyle("PRIMARY")
561 .setCustomId("back"),
562 new MessageButton()
563 .setLabel("Add new type")
Skyler Grey11236ba2022-08-08 21:13:33 +0100564 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
pineafan6702cef2022-06-13 17:52:37 +0100565 .setStyle("PRIMARY")
566 .setCustomId("addType")
Skyler Grey11236ba2022-08-08 21:13:33 +0100567 .setDisabled(customTypes !== null && customTypes.length >= 25),
pineafan6702cef2022-06-13 17:52:37 +0100568 new MessageButton()
569 .setLabel("Switch to default types")
570 .setStyle("SECONDARY")
pineafan63fc5e22022-08-04 22:04:10 +0100571 .setCustomId("switchToDefault")
pineafan6702cef2022-06-13 17:52:37 +0100572 ])
573 ])
574 });
575 } else {
pineafan63fc5e22022-08-04 22:04:10 +0100576 const inUse = toHexArray(data.types, ticketTypes);
577 const options = [];
Skyler Grey75ea9172022-08-06 10:22:23 +0100578 ticketTypes.forEach((type) => {
579 options.push(
580 new SelectMenuOption({
581 label: capitalize(type),
582 value: type,
Skyler Grey11236ba2022-08-08 21:13:33 +0100583 emoji: client.emojis.cache.get(getEmojiByName(`TICKETS.${type.toUpperCase()}`, "id")),
Skyler Grey75ea9172022-08-06 10:22:23 +0100584 default: inUse.includes(type)
585 })
586 );
pineafan63fc5e22022-08-04 22:04:10 +0100587 });
588 const selectPane = new MessageActionRow().addComponents([
pineafan6702cef2022-06-13 17:52:37 +0100589 new Discord.MessageSelectMenu()
590 .addOptions(options)
591 .setCustomId("types")
592 .setMaxValues(ticketTypes.length)
593 .setMinValues(1)
594 .setPlaceholder("Select types to use")
pineafan63fc5e22022-08-04 22:04:10 +0100595 ]);
pineafanc6158ab2022-06-17 16:34:07 +0100596 await interaction.editReply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100597 embeds: [
598 new EmojiEmbed()
599 .setTitle("Tickets > Types")
600 .setDescription(
601 "**Default types enabled**\n\n" +
602 "**Types in use:**\n" +
603 inUse
Skyler Grey11236ba2022-08-08 21:13:33 +0100604 .map((t) => `> ${getEmojiByName("TICKETS." + t.toUpperCase())} ${capitalize(t)}`)
Skyler Grey75ea9172022-08-06 10:22:23 +0100605 .join("\n")
606 )
607 .setStatus("Success")
608 .setEmoji("GUILD.TICKET.OPEN")
609 ],
610 components: [
pineafan6702cef2022-06-13 17:52:37 +0100611 selectPane,
612 new MessageActionRow().addComponents([
613 new MessageButton()
614 .setLabel("Back")
615 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
616 .setStyle("PRIMARY")
617 .setCustomId("back"),
618 new MessageButton()
619 .setLabel("Switch to custom types")
620 .setStyle("SECONDARY")
pineafan63fc5e22022-08-04 22:04:10 +0100621 .setCustomId("switchToCustom")
pineafan6702cef2022-06-13 17:52:37 +0100622 ])
623 ]
624 });
625 }
626 let i;
627 try {
pineafanc6158ab2022-06-17 16:34:07 +0100628 i = await m.awaitMessageComponent({ time: 300000 });
Skyler Grey75ea9172022-08-06 10:22:23 +0100629 } catch (e) {
Skyler Greyad002172022-08-16 18:48:26 +0100630 timedOut = true;
631 continue;
Skyler Grey75ea9172022-08-06 10:22:23 +0100632 }
pineafane23c4ec2022-07-27 21:56:27 +0100633 if (i.component.customId === "types") {
pineafan63fc5e22022-08-04 22:04:10 +0100634 i.deferUpdate();
635 const types = toHexInteger(i.values, ticketTypes);
Skyler Grey75ea9172022-08-06 10:22:23 +0100636 await client.database.guilds.write(interaction.guild.id, {
637 "tickets.types": types
638 });
pineafan6702cef2022-06-13 17:52:37 +0100639 data.types = types;
pineafane23c4ec2022-07-27 21:56:27 +0100640 } else if (i.component.customId === "removeTypes") {
pineafan63fc5e22022-08-04 22:04:10 +0100641 i.deferUpdate();
642 const types = i.values;
pineafan6702cef2022-06-13 17:52:37 +0100643 let customTypes = data.customTypes;
644 if (customTypes) {
645 customTypes = customTypes.filter((t) => !types.includes(t));
646 customTypes = customTypes.length > 0 ? customTypes : null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100647 await client.database.guilds.write(interaction.guild.id, {
648 "tickets.customTypes": customTypes
649 });
pineafan6702cef2022-06-13 17:52:37 +0100650 data.customTypes = customTypes;
651 }
pineafane23c4ec2022-07-27 21:56:27 +0100652 } else if (i.component.customId === "addType") {
Skyler Grey75ea9172022-08-06 10:22:23 +0100653 await i.showModal(
654 new Discord.Modal()
655 .setCustomId("modal")
656 .setTitle("Enter a name for the new type")
657 .addComponents(
658 new MessageActionRow<TextInputComponent>().addComponents(
659 new TextInputComponent()
660 .setCustomId("type")
661 .setLabel("Name")
662 .setMaxLength(100)
663 .setMinLength(1)
664 .setPlaceholder('E.g. "Server Idea"')
665 .setRequired(true)
666 .setStyle("SHORT")
667 )
668 )
669 );
pineafan6702cef2022-06-13 17:52:37 +0100670 await interaction.editReply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100671 embeds: [
672 new EmojiEmbed()
673 .setTitle("Tickets > Types")
Skyler Grey11236ba2022-08-08 21:13:33 +0100674 .setDescription("Modal opened. If you can't see it, click back and try again.")
Skyler Grey75ea9172022-08-06 10:22:23 +0100675 .setStatus("Success")
676 .setEmoji("GUILD.TICKET.OPEN")
677 ],
678 components: [
679 new MessageActionRow().addComponents([
680 new MessageButton()
681 .setLabel("Back")
682 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
683 .setStyle("PRIMARY")
684 .setCustomId("back")
685 ])
686 ]
pineafan6702cef2022-06-13 17:52:37 +0100687 });
pineafan4edb7762022-06-26 19:21:04 +0100688 let out;
pineafan6702cef2022-06-13 17:52:37 +0100689 try {
Skyler Grey75ea9172022-08-06 10:22:23 +0100690 out = await modalInteractionCollector(
691 m,
692 (m) => m.channel.id === interaction.channel.id,
693 (m) => m.customId === "addType"
694 );
695 } catch (e) {
696 continue;
697 }
pineafan6702cef2022-06-13 17:52:37 +0100698 if (out.fields) {
699 let toAdd = out.fields.getTextInputValue("type");
Skyler Grey75ea9172022-08-06 10:22:23 +0100700 if (!toAdd) {
701 continue;
702 }
pineafan63fc5e22022-08-04 22:04:10 +0100703 toAdd = toAdd.substring(0, 80);
pineafan6702cef2022-06-13 17:52:37 +0100704 try {
Skyler Grey11236ba2022-08-08 21:13:33 +0100705 await client.database.guilds.append(interaction.guild.id, "tickets.customTypes", toAdd);
Skyler Grey75ea9172022-08-06 10:22:23 +0100706 } catch {
707 continue;
708 }
Skyler Greyad002172022-08-16 18:48:26 +0100709 data.customTypes = data.customTypes ?? [];
pineafan6702cef2022-06-13 17:52:37 +0100710 if (!data.customTypes.includes(toAdd)) {
711 data.customTypes.push(toAdd);
712 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100713 } else {
714 continue;
715 }
pineafane23c4ec2022-07-27 21:56:27 +0100716 } else if (i.component.customId === "switchToDefault") {
pineafan63fc5e22022-08-04 22:04:10 +0100717 i.deferUpdate();
Skyler Grey11236ba2022-08-08 21:13:33 +0100718 await client.database.guilds.write(interaction.guild.id, { "tickets.useCustom": false }, []);
pineafan6702cef2022-06-13 17:52:37 +0100719 data.useCustom = false;
pineafane23c4ec2022-07-27 21:56:27 +0100720 } else if (i.component.customId === "switchToCustom") {
pineafan63fc5e22022-08-04 22:04:10 +0100721 i.deferUpdate();
Skyler Grey11236ba2022-08-08 21:13:33 +0100722 await client.database.guilds.write(interaction.guild.id, { "tickets.useCustom": true }, []);
pineafan6702cef2022-06-13 17:52:37 +0100723 data.useCustom = true;
724 } else {
pineafan63fc5e22022-08-04 22:04:10 +0100725 i.deferUpdate();
Skyler Greyad002172022-08-16 18:48:26 +0100726 backPressed = true;
pineafan6702cef2022-06-13 17:52:37 +0100727 }
728 }
pineafan63fc5e22022-08-04 22:04:10 +0100729 return data;
pineafan6702cef2022-06-13 17:52:37 +0100730}
731
Skyler Grey1a67e182022-08-04 23:05:44 +0100732const check = (interaction: CommandInteraction) => {
Skyler Grey75ea9172022-08-06 10:22:23 +0100733 const member = interaction.member as Discord.GuildMember;
734 if (!member.permissions.has("MANAGE_GUILD"))
pineafan3a02ea32022-08-11 21:35:04 +0100735 throw new Error("You must have the *Manage Server* permission to use this command");
pineafan6702cef2022-06-13 17:52:37 +0100736 return true;
pineafan63fc5e22022-08-04 22:04:10 +0100737};
pineafan4f164f32022-02-26 22:07:12 +0000738
739export { command };
740export { callback };
Skyler Grey1a67e182022-08-04 23:05:44 +0100741export { check };