blob: 8b84770fa170b83bd5a41233ec87a93ca97227d4 [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 = {
pineafan6702cef2022-06-13 17:52:37 +010070 enabled: interaction.options.getString("enabled") as string | boolean,
71 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 options.enabled = options.enabled === "yes" ? true : false;
77 if (options.category) {
pineafan3a02ea32022-08-11 21:35:04 +010078 let channel: GuildChannel | null;
pineafan6702cef2022-06-13 17:52:37 +010079 try {
pineafan3a02ea32022-08-11 21:35:04 +010080 channel = await interaction.guild!.channels.fetch(options.category.id);
pineafan6702cef2022-06-13 17:52:37 +010081 } catch {
82 return await interaction.editReply({
Skyler Grey75ea9172022-08-06 10:22:23 +010083 embeds: [
84 new EmojiEmbed()
85 .setEmoji("CHANNEL.TEXT.DELETE")
86 .setTitle("Tickets > Category")
Skyler Grey11236ba2022-08-08 21:13:33 +010087 .setDescription("The channel you provided is not a valid category")
Skyler Grey75ea9172022-08-06 10:22:23 +010088 .setStatus("Danger")
pineafan6702cef2022-06-13 17:52:37 +010089 ]
pineafan63fc5e22022-08-04 22:04:10 +010090 });
pineafan6702cef2022-06-13 17:52:37 +010091 }
pineafan3a02ea32022-08-11 21:35:04 +010092 if (!channel) return;
pineafan63fc5e22022-08-04 22:04:10 +010093 channel = channel as Discord.CategoryChannel;
pineafan3a02ea32022-08-11 21:35:04 +010094 if (channel.guild.id !== interaction.guild!.id)
Skyler Grey75ea9172022-08-06 10:22:23 +010095 return interaction.editReply({
96 embeds: [
97 new EmojiEmbed()
98 .setTitle("Tickets > Category")
Skyler Grey11236ba2022-08-08 21:13:33 +010099 .setDescription("You must choose a category in this server")
Skyler Grey75ea9172022-08-06 10:22:23 +0100100 .setStatus("Danger")
101 .setEmoji("CHANNEL.TEXT.DELETE")
102 ]
103 });
pineafan6702cef2022-06-13 17:52:37 +0100104 }
105 if (options.maxtickets) {
Skyler Grey75ea9172022-08-06 10:22:23 +0100106 if (options.maxtickets < 1)
107 return interaction.editReply({
108 embeds: [
109 new EmojiEmbed()
110 .setTitle("Tickets > Max Tickets")
Skyler Grey11236ba2022-08-08 21:13:33 +0100111 .setDescription("You must choose a number greater than 0")
Skyler Grey75ea9172022-08-06 10:22:23 +0100112 .setStatus("Danger")
113 .setEmoji("CHANNEL.TEXT.DELETE")
114 ]
115 });
pineafan6702cef2022-06-13 17:52:37 +0100116 }
pineafan3a02ea32022-08-11 21:35:04 +0100117 let role: Role | null;
pineafan6702cef2022-06-13 17:52:37 +0100118 if (options.supportping) {
119 try {
pineafan3a02ea32022-08-11 21:35:04 +0100120 role = await interaction.guild!.roles.fetch(options.supportping.id);
pineafan6702cef2022-06-13 17:52:37 +0100121 } catch {
122 return await interaction.editReply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100123 embeds: [
124 new EmojiEmbed()
125 .setEmoji("GUILD.ROLE.DELETE")
126 .setTitle("Tickets > Support Ping")
Skyler Grey11236ba2022-08-08 21:13:33 +0100127 .setDescription("The role you provided is not a valid role")
Skyler Grey75ea9172022-08-06 10:22:23 +0100128 .setStatus("Danger")
pineafan6702cef2022-06-13 17:52:37 +0100129 ]
pineafan63fc5e22022-08-04 22:04:10 +0100130 });
pineafan6702cef2022-06-13 17:52:37 +0100131 }
pineafan3a02ea32022-08-11 21:35:04 +0100132 if (!role) return;
pineafan63fc5e22022-08-04 22:04:10 +0100133 role = role as Discord.Role;
pineafan3a02ea32022-08-11 21:35:04 +0100134 if (role.guild.id !== interaction.guild!.id)
Skyler Grey75ea9172022-08-06 10:22:23 +0100135 return interaction.editReply({
136 embeds: [
137 new EmojiEmbed()
138 .setTitle("Tickets > Support Ping")
Skyler Grey11236ba2022-08-08 21:13:33 +0100139 .setDescription("You must choose a role in this server")
Skyler Grey75ea9172022-08-06 10:22:23 +0100140 .setStatus("Danger")
141 .setEmoji("GUILD.ROLE.DELETE")
142 ]
143 });
pineafan6702cef2022-06-13 17:52:37 +0100144 }
145
pineafan63fc5e22022-08-04 22:04:10 +0100146 const confirmation = await new confirmationMessage(interaction)
pineafan6702cef2022-06-13 17:52:37 +0100147 .setEmoji("GUILD.TICKET.ARCHIVED")
148 .setTitle("Tickets")
149 .setDescription(
Skyler Grey11236ba2022-08-08 21:13:33 +0100150 (options.category ? `**Category:** ${options.category.name}\n` : "") +
151 (options.maxtickets ? `**Max Tickets:** ${options.maxtickets}\n` : "") +
152 (options.supportping ? `**Support Ping:** ${options.supportping.name}\n` : "") +
Skyler Grey75ea9172022-08-06 10:22:23 +0100153 (options.enabled !== null
154 ? `**Enabled:** ${
155 options.enabled
156 ? `${getEmojiByName("CONTROL.TICK")} Yes`
157 : `${getEmojiByName("CONTROL.CROSS")} No`
158 }\n`
159 : "") +
160 "\nAre you sure you want to apply these settings?"
pineafan6702cef2022-06-13 17:52:37 +0100161 )
162 .setColor("Warning")
163 .setInverted(true)
pineafan63fc5e22022-08-04 22:04:10 +0100164 .send(true);
165 if (confirmation.cancelled) return;
pineafan6702cef2022-06-13 17:52:37 +0100166 if (confirmation.success) {
pineafan3a02ea32022-08-11 21:35:04 +0100167 const toUpdate: Record<string, string | boolean | number> = {};
Skyler Grey11236ba2022-08-08 21:13:33 +0100168 if (options.enabled !== null) toUpdate["tickets.enabled"] = options.enabled;
169 if (options.category) toUpdate["tickets.category"] = options.category.id;
170 if (options.maxtickets) toUpdate["tickets.maxTickets"] = options.maxtickets;
171 if (options.supportping) toUpdate["tickets.supportRole"] = options.supportping.id;
pineafan6702cef2022-06-13 17:52:37 +0100172 try {
pineafan3a02ea32022-08-11 21:35:04 +0100173 await client.database.guilds.write(interaction.guild!.id, toUpdate);
pineafan6702cef2022-06-13 17:52:37 +0100174 } catch (e) {
175 return interaction.editReply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100176 embeds: [
177 new EmojiEmbed()
178 .setTitle("Tickets")
Skyler Grey11236ba2022-08-08 21:13:33 +0100179 .setDescription("Something went wrong and the staff notifications channel could not be set")
Skyler Grey75ea9172022-08-06 10:22:23 +0100180 .setStatus("Danger")
181 .setEmoji("GUILD.TICKET.DELETE")
182 ],
183 components: []
pineafan6702cef2022-06-13 17:52:37 +0100184 });
185 }
186 } else {
187 return interaction.editReply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100188 embeds: [
189 new EmojiEmbed()
190 .setTitle("Tickets")
191 .setDescription("No changes were made")
192 .setStatus("Success")
193 .setEmoji("GUILD.TICKET.OPEN")
194 ],
195 components: []
pineafan6702cef2022-06-13 17:52:37 +0100196 });
197 }
198 }
pineafan3a02ea32022-08-11 21:35:04 +0100199 let data = await client.database.guilds.read(interaction.guild!.id);
Skyler Grey75ea9172022-08-06 10:22:23 +0100200 data.tickets.customTypes = (data.tickets.customTypes || []).filter(
Skyler Grey11236ba2022-08-08 21:13:33 +0100201 (value: string, index: number, array: string[]) => array.indexOf(value) === index
Skyler Grey75ea9172022-08-06 10:22:23 +0100202 );
pineafan6702cef2022-06-13 17:52:37 +0100203 let lastClicked = "";
Skyler Grey1a67e182022-08-04 23:05:44 +0100204 let embed: EmojiEmbed;
pineafan6702cef2022-06-13 17:52:37 +0100205 data = {
206 enabled: data.tickets.enabled,
207 category: data.tickets.category,
208 maxTickets: data.tickets.maxTickets,
209 supportRole: data.tickets.supportRole,
210 useCustom: data.tickets.useCustom,
211 types: data.tickets.types,
212 customTypes: data.tickets.customTypes
pineafan63fc5e22022-08-04 22:04:10 +0100213 };
pineafan6702cef2022-06-13 17:52:37 +0100214 while (true) {
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) {
279 break;
280 }
pineafan63fc5e22022-08-04 22:04:10 +0100281 i.deferUpdate();
Skyler Grey11236ba2022-08-08 21:13:33 +0100282 if ((i.component as MessageActionRowComponent).customId === "clearCategory") {
pineafane23c4ec2022-07-27 21:56:27 +0100283 if (lastClicked === "cat") {
pineafan6702cef2022-06-13 17:52:37 +0100284 lastClicked = "";
pineafan3a02ea32022-08-11 21:35:04 +0100285 await client.database.guilds.write(interaction.guild!.id, null, ["tickets.category"]);
pineafan6702cef2022-06-13 17:52:37 +0100286 data.category = undefined;
287 } else lastClicked = "cat";
Skyler Grey11236ba2022-08-08 21:13:33 +0100288 } else if ((i.component as MessageActionRowComponent).customId === "clearMaxTickets") {
pineafane23c4ec2022-07-27 21:56:27 +0100289 if (lastClicked === "max") {
pineafan6702cef2022-06-13 17:52:37 +0100290 lastClicked = "";
pineafan3a02ea32022-08-11 21:35:04 +0100291 await client.database.guilds.write(interaction.guild!.id, null, ["tickets.maxTickets"]);
pineafan6702cef2022-06-13 17:52:37 +0100292 data.maxTickets = 5;
293 } else lastClicked = "max";
Skyler Grey11236ba2022-08-08 21:13:33 +0100294 } else if ((i.component as MessageActionRowComponent).customId === "clearSupportPing") {
pineafane23c4ec2022-07-27 21:56:27 +0100295 if (lastClicked === "sup") {
pineafan6702cef2022-06-13 17:52:37 +0100296 lastClicked = "";
pineafan3a02ea32022-08-11 21:35:04 +0100297 await client.database.guilds.write(interaction.guild!.id, null, ["tickets.supportRole"]);
pineafan6702cef2022-06-13 17:52:37 +0100298 data.supportRole = undefined;
299 } else lastClicked = "sup";
Skyler Grey11236ba2022-08-08 21:13:33 +0100300 } else if ((i.component as MessageActionRowComponent).customId === "send") {
pineafan41d93562022-07-30 22:10:15 +0100301 const ticketMessages = [
Skyler Grey75ea9172022-08-06 10:22:23 +0100302 {
303 label: "Create ticket",
304 description: "Click the button below to create a ticket"
305 },
306 {
307 label: "Issues, questions or feedback?",
Skyler Grey11236ba2022-08-08 21:13:33 +0100308 description: "Click below to open a ticket and get help from our staff team"
Skyler Grey75ea9172022-08-06 10:22:23 +0100309 },
310 {
311 label: "Contact Us",
Skyler Grey11236ba2022-08-08 21:13:33 +0100312 description: "Click the button below to speak to us privately"
Skyler Grey75ea9172022-08-06 10:22:23 +0100313 }
pineafan63fc5e22022-08-04 22:04:10 +0100314 ];
pineafan41d93562022-07-30 22:10:15 +0100315 while (true) {
pineafan63fc5e22022-08-04 22:04:10 +0100316 const enabled = data.enabled && data.category !== null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100317 await interaction.editReply({
318 embeds: [
319 new EmojiEmbed()
320 .setTitle("Ticket Button")
Skyler Grey11236ba2022-08-08 21:13:33 +0100321 .setDescription("Select a message template to send in this channel")
Skyler Grey75ea9172022-08-06 10:22:23 +0100322 .setFooter({
323 text: enabled
324 ? ""
325 : "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."
326 })
327 .setStatus(enabled ? "Success" : "Warning")
328 .setEmoji("GUILD.ROLES.CREATE")
329 ],
330 components: [
331 new MessageActionRow().addComponents([
332 new MessageSelectMenu()
333 .setOptions(
334 ticketMessages.map(
335 (
336 t: {
337 label: string;
338 description: string;
339 value?: string;
340 },
341 index
342 ) => {
343 t.value = index.toString();
344 return t as {
345 value: string;
346 label: string;
347 description: string;
348 };
349 }
350 )
351 )
352 .setCustomId("template")
353 .setMaxValues(1)
354 .setMinValues(1)
355 .setPlaceholder("Select a message template")
356 ]),
357 new MessageActionRow().addComponents([
358 new MessageButton()
359 .setCustomId("back")
360 .setLabel("Back")
361 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
362 .setStyle("DANGER"),
Skyler Grey11236ba2022-08-08 21:13:33 +0100363 new MessageButton().setCustomId("blank").setLabel("Empty").setStyle("SECONDARY"),
Skyler Grey75ea9172022-08-06 10:22:23 +0100364 new MessageButton()
365 .setCustomId("custom")
366 .setLabel("Custom")
367 .setEmoji(getEmojiByName("TICKETS.OTHER", "id"))
368 .setStyle("PRIMARY")
369 ])
370 ]
371 });
Skyler Grey1a67e182022-08-04 23:05:44 +0100372 let i: MessageComponentInteraction;
pineafan41d93562022-07-30 22:10:15 +0100373 try {
Skyler Grey75ea9172022-08-06 10:22:23 +0100374 i = await m.awaitMessageComponent({ time: 300000 });
375 } catch (e) {
pineafan63fc5e22022-08-04 22:04:10 +0100376 break;
Skyler Grey75ea9172022-08-06 10:22:23 +0100377 }
Skyler Grey11236ba2022-08-08 21:13:33 +0100378 if ((i.component as MessageActionRowComponent).customId === "template") {
pineafan63fc5e22022-08-04 22:04:10 +0100379 i.deferUpdate();
pineafan3a02ea32022-08-11 21:35:04 +0100380 await interaction.channel!.send({
Skyler Grey75ea9172022-08-06 10:22:23 +0100381 embeds: [
382 new EmojiEmbed()
pineafan3a02ea32022-08-11 21:35:04 +0100383 .setTitle(ticketMessages[parseInt((i as SelectMenuInteraction).values[0]!)]!.label)
Skyler Grey75ea9172022-08-06 10:22:23 +0100384 .setDescription(
pineafan3a02ea32022-08-11 21:35:04 +0100385 ticketMessages[parseInt((i as SelectMenuInteraction).values[0]!)]!.description
Skyler Grey75ea9172022-08-06 10:22:23 +0100386 )
387 .setStatus("Success")
388 .setEmoji("GUILD.TICKET.OPEN")
389 ],
390 components: [
391 new MessageActionRow().addComponents([
392 new MessageButton()
393 .setLabel("Create Ticket")
Skyler Grey11236ba2022-08-08 21:13:33 +0100394 .setEmoji(getEmojiByName("CONTROL.TICK", "id"))
Skyler Grey75ea9172022-08-06 10:22:23 +0100395 .setStyle("SUCCESS")
396 .setCustomId("createticket")
397 ])
398 ]
399 });
pineafan63fc5e22022-08-04 22:04:10 +0100400 break;
Skyler Grey11236ba2022-08-08 21:13:33 +0100401 } else if ((i.component as MessageActionRowComponent).customId === "blank") {
Skyler Grey75ea9172022-08-06 10:22:23 +0100402 i.deferUpdate();
pineafan3a02ea32022-08-11 21:35:04 +0100403 await interaction.channel!.send({
Skyler Grey75ea9172022-08-06 10:22:23 +0100404 components: [
405 new MessageActionRow().addComponents([
406 new MessageButton()
407 .setLabel("Create Ticket")
Skyler Grey11236ba2022-08-08 21:13:33 +0100408 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
Skyler Grey75ea9172022-08-06 10:22:23 +0100409 .setStyle("SUCCESS")
410 .setCustomId("createticket")
411 ])
412 ]
413 });
414 break;
Skyler Grey11236ba2022-08-08 21:13:33 +0100415 } else if ((i.component as MessageActionRowComponent).customId === "custom") {
Skyler Grey75ea9172022-08-06 10:22:23 +0100416 await i.showModal(
417 new Discord.Modal()
418 .setCustomId("modal")
419 .setTitle("Enter embed details")
420 .addComponents(
421 new MessageActionRow<TextInputComponent>().addComponents(
422 new TextInputComponent()
423 .setCustomId("title")
424 .setLabel("Title")
425 .setMaxLength(256)
426 .setRequired(true)
427 .setStyle("SHORT")
428 ),
429 new MessageActionRow<TextInputComponent>().addComponents(
430 new TextInputComponent()
431 .setCustomId("description")
432 .setLabel("Description")
433 .setMaxLength(4000)
434 .setRequired(true)
435 .setStyle("PARAGRAPH")
436 )
437 )
438 );
pineafan41d93562022-07-30 22:10:15 +0100439 await interaction.editReply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100440 embeds: [
441 new EmojiEmbed()
442 .setTitle("Ticket Button")
Skyler Grey11236ba2022-08-08 21:13:33 +0100443 .setDescription("Modal opened. If you can't see it, click back and try again.")
Skyler Grey75ea9172022-08-06 10:22:23 +0100444 .setStatus("Success")
445 .setEmoji("GUILD.TICKET.OPEN")
446 ],
447 components: [
448 new MessageActionRow().addComponents([
449 new MessageButton()
450 .setLabel("Back")
Skyler Grey11236ba2022-08-08 21:13:33 +0100451 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
Skyler Grey75ea9172022-08-06 10:22:23 +0100452 .setStyle("PRIMARY")
453 .setCustomId("back")
454 ])
455 ]
pineafan41d93562022-07-30 22:10:15 +0100456 });
457 let out;
458 try {
Skyler Grey75ea9172022-08-06 10:22:23 +0100459 out = await modalInteractionCollector(
460 m,
pineafan3a02ea32022-08-11 21:35:04 +0100461 (m) => m.channel!.id === interaction.channel!.id,
Skyler Grey75ea9172022-08-06 10:22:23 +0100462 (m) => m.customId === "modify"
463 );
464 } catch (e) {
465 break;
466 }
pineafan41d93562022-07-30 22:10:15 +0100467 if (out.fields) {
pineafan63fc5e22022-08-04 22:04:10 +0100468 const title = out.fields.getTextInputValue("title");
Skyler Grey11236ba2022-08-08 21:13:33 +0100469 const description = out.fields.getTextInputValue("description");
Skyler Grey75ea9172022-08-06 10:22:23 +0100470 await interaction.channel.send({
471 embeds: [
472 new EmojiEmbed()
473 .setTitle(title)
474 .setDescription(description)
475 .setStatus("Success")
476 .setEmoji("GUILD.TICKET.OPEN")
477 ],
478 components: [
479 new MessageActionRow().addComponents([
480 new MessageButton()
481 .setLabel("Create Ticket")
Skyler Grey11236ba2022-08-08 21:13:33 +0100482 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
Skyler Grey75ea9172022-08-06 10:22:23 +0100483 .setStyle("SUCCESS")
484 .setCustomId("createticket")
485 ])
486 ]
487 });
pineafan63fc5e22022-08-04 22:04:10 +0100488 break;
Skyler Grey75ea9172022-08-06 10:22:23 +0100489 } else {
490 continue;
491 }
pineafan41d93562022-07-30 22:10:15 +0100492 }
493 }
Skyler Grey11236ba2022-08-08 21:13:33 +0100494 } else if ((i.component as MessageActionRowComponent).customId === "enabled") {
Skyler Grey75ea9172022-08-06 10:22:23 +0100495 await client.database.guilds.write(interaction.guild.id, {
496 "tickets.enabled": !data.enabled
497 });
pineafan6702cef2022-06-13 17:52:37 +0100498 data.enabled = !data.enabled;
Skyler Grey11236ba2022-08-08 21:13:33 +0100499 } else if ((i.component as MessageActionRowComponent).customId === "manageTypes") {
Skyler Grey1a67e182022-08-04 23:05:44 +0100500 data = await manageTypes(interaction, data, m as Message);
pineafan6702cef2022-06-13 17:52:37 +0100501 } else {
pineafan63fc5e22022-08-04 22:04:10 +0100502 break;
pineafan6702cef2022-06-13 17:52:37 +0100503 }
504 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100505 await interaction.editReply({
506 embeds: [embed.setFooter({ text: "Message closed" })],
507 components: []
508 });
pineafan63fc5e22022-08-04 22:04:10 +0100509};
pineafan4f164f32022-02-26 22:07:12 +0000510
Skyler Grey11236ba2022-08-08 21:13:33 +0100511async function manageTypes(interaction: CommandInteraction, data: GuildConfig["tickets"], m: Message) {
pineafan6702cef2022-06-13 17:52:37 +0100512 while (true) {
513 if (data.useCustom) {
pineafan63fc5e22022-08-04 22:04:10 +0100514 const customTypes = data.customTypes;
pineafanc6158ab2022-06-17 16:34:07 +0100515 await interaction.editReply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100516 embeds: [
517 new EmojiEmbed()
518 .setTitle("Tickets > Types")
519 .setDescription(
520 "**Custom types enabled**\n\n" +
521 "**Types in use:**\n" +
Skyler Grey11236ba2022-08-08 21:13:33 +0100522 (customTypes !== null ? customTypes.map((t) => `> ${t}`).join("\n") : "*None set*") +
Skyler Grey75ea9172022-08-06 10:22:23 +0100523 "\n\n" +
524 (customTypes === null
525 ? `${getEmojiByName(
526 "TICKETS.REPORT"
527 )} Having no types will disable tickets. Please add at least 1 type or use default types`
528 : "")
pineafan6702cef2022-06-13 17:52:37 +0100529 )
Skyler Grey75ea9172022-08-06 10:22:23 +0100530 .setStatus("Success")
531 .setEmoji("GUILD.TICKET.OPEN")
532 ],
533 components: (customTypes
534 ? [
535 new MessageActionRow().addComponents([
536 new Discord.MessageSelectMenu()
537 .setCustomId("removeTypes")
538 .setPlaceholder("Select types to remove")
539 .setMaxValues(customTypes.length)
540 .setMinValues(1)
541 .addOptions(
542 customTypes.map((t) => ({
543 label: t,
544 value: t
545 }))
546 )
547 ])
548 ]
549 : []
550 ).concat([
pineafan6702cef2022-06-13 17:52:37 +0100551 new MessageActionRow().addComponents([
552 new MessageButton()
553 .setLabel("Back")
554 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
555 .setStyle("PRIMARY")
556 .setCustomId("back"),
557 new MessageButton()
558 .setLabel("Add new type")
Skyler Grey11236ba2022-08-08 21:13:33 +0100559 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
pineafan6702cef2022-06-13 17:52:37 +0100560 .setStyle("PRIMARY")
561 .setCustomId("addType")
Skyler Grey11236ba2022-08-08 21:13:33 +0100562 .setDisabled(customTypes !== null && customTypes.length >= 25),
pineafan6702cef2022-06-13 17:52:37 +0100563 new MessageButton()
564 .setLabel("Switch to default types")
565 .setStyle("SECONDARY")
pineafan63fc5e22022-08-04 22:04:10 +0100566 .setCustomId("switchToDefault")
pineafan6702cef2022-06-13 17:52:37 +0100567 ])
568 ])
569 });
570 } else {
pineafan63fc5e22022-08-04 22:04:10 +0100571 const inUse = toHexArray(data.types, ticketTypes);
572 const options = [];
Skyler Grey75ea9172022-08-06 10:22:23 +0100573 ticketTypes.forEach((type) => {
574 options.push(
575 new SelectMenuOption({
576 label: capitalize(type),
577 value: type,
Skyler Grey11236ba2022-08-08 21:13:33 +0100578 emoji: client.emojis.cache.get(getEmojiByName(`TICKETS.${type.toUpperCase()}`, "id")),
Skyler Grey75ea9172022-08-06 10:22:23 +0100579 default: inUse.includes(type)
580 })
581 );
pineafan63fc5e22022-08-04 22:04:10 +0100582 });
583 const selectPane = new MessageActionRow().addComponents([
pineafan6702cef2022-06-13 17:52:37 +0100584 new Discord.MessageSelectMenu()
585 .addOptions(options)
586 .setCustomId("types")
587 .setMaxValues(ticketTypes.length)
588 .setMinValues(1)
589 .setPlaceholder("Select types to use")
pineafan63fc5e22022-08-04 22:04:10 +0100590 ]);
pineafanc6158ab2022-06-17 16:34:07 +0100591 await interaction.editReply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100592 embeds: [
593 new EmojiEmbed()
594 .setTitle("Tickets > Types")
595 .setDescription(
596 "**Default types enabled**\n\n" +
597 "**Types in use:**\n" +
598 inUse
Skyler Grey11236ba2022-08-08 21:13:33 +0100599 .map((t) => `> ${getEmojiByName("TICKETS." + t.toUpperCase())} ${capitalize(t)}`)
Skyler Grey75ea9172022-08-06 10:22:23 +0100600 .join("\n")
601 )
602 .setStatus("Success")
603 .setEmoji("GUILD.TICKET.OPEN")
604 ],
605 components: [
pineafan6702cef2022-06-13 17:52:37 +0100606 selectPane,
607 new MessageActionRow().addComponents([
608 new MessageButton()
609 .setLabel("Back")
610 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
611 .setStyle("PRIMARY")
612 .setCustomId("back"),
613 new MessageButton()
614 .setLabel("Switch to custom types")
615 .setStyle("SECONDARY")
pineafan63fc5e22022-08-04 22:04:10 +0100616 .setCustomId("switchToCustom")
pineafan6702cef2022-06-13 17:52:37 +0100617 ])
618 ]
619 });
620 }
621 let i;
622 try {
pineafanc6158ab2022-06-17 16:34:07 +0100623 i = await m.awaitMessageComponent({ time: 300000 });
Skyler Grey75ea9172022-08-06 10:22:23 +0100624 } catch (e) {
625 break;
626 }
pineafane23c4ec2022-07-27 21:56:27 +0100627 if (i.component.customId === "types") {
pineafan63fc5e22022-08-04 22:04:10 +0100628 i.deferUpdate();
629 const types = toHexInteger(i.values, ticketTypes);
Skyler Grey75ea9172022-08-06 10:22:23 +0100630 await client.database.guilds.write(interaction.guild.id, {
631 "tickets.types": types
632 });
pineafan6702cef2022-06-13 17:52:37 +0100633 data.types = types;
pineafane23c4ec2022-07-27 21:56:27 +0100634 } else if (i.component.customId === "removeTypes") {
pineafan63fc5e22022-08-04 22:04:10 +0100635 i.deferUpdate();
636 const types = i.values;
pineafan6702cef2022-06-13 17:52:37 +0100637 let customTypes = data.customTypes;
638 if (customTypes) {
639 customTypes = customTypes.filter((t) => !types.includes(t));
640 customTypes = customTypes.length > 0 ? customTypes : null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100641 await client.database.guilds.write(interaction.guild.id, {
642 "tickets.customTypes": customTypes
643 });
pineafan6702cef2022-06-13 17:52:37 +0100644 data.customTypes = customTypes;
645 }
pineafane23c4ec2022-07-27 21:56:27 +0100646 } else if (i.component.customId === "addType") {
Skyler Grey75ea9172022-08-06 10:22:23 +0100647 await i.showModal(
648 new Discord.Modal()
649 .setCustomId("modal")
650 .setTitle("Enter a name for the new type")
651 .addComponents(
652 new MessageActionRow<TextInputComponent>().addComponents(
653 new TextInputComponent()
654 .setCustomId("type")
655 .setLabel("Name")
656 .setMaxLength(100)
657 .setMinLength(1)
658 .setPlaceholder('E.g. "Server Idea"')
659 .setRequired(true)
660 .setStyle("SHORT")
661 )
662 )
663 );
pineafan6702cef2022-06-13 17:52:37 +0100664 await interaction.editReply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100665 embeds: [
666 new EmojiEmbed()
667 .setTitle("Tickets > Types")
Skyler Grey11236ba2022-08-08 21:13:33 +0100668 .setDescription("Modal opened. If you can't see it, click back and try again.")
Skyler Grey75ea9172022-08-06 10:22:23 +0100669 .setStatus("Success")
670 .setEmoji("GUILD.TICKET.OPEN")
671 ],
672 components: [
673 new MessageActionRow().addComponents([
674 new MessageButton()
675 .setLabel("Back")
676 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
677 .setStyle("PRIMARY")
678 .setCustomId("back")
679 ])
680 ]
pineafan6702cef2022-06-13 17:52:37 +0100681 });
pineafan4edb7762022-06-26 19:21:04 +0100682 let out;
pineafan6702cef2022-06-13 17:52:37 +0100683 try {
Skyler Grey75ea9172022-08-06 10:22:23 +0100684 out = await modalInteractionCollector(
685 m,
686 (m) => m.channel.id === interaction.channel.id,
687 (m) => m.customId === "addType"
688 );
689 } catch (e) {
690 continue;
691 }
pineafan6702cef2022-06-13 17:52:37 +0100692 if (out.fields) {
693 let toAdd = out.fields.getTextInputValue("type");
Skyler Grey75ea9172022-08-06 10:22:23 +0100694 if (!toAdd) {
695 continue;
696 }
pineafan63fc5e22022-08-04 22:04:10 +0100697 toAdd = toAdd.substring(0, 80);
pineafan6702cef2022-06-13 17:52:37 +0100698 try {
Skyler Grey11236ba2022-08-08 21:13:33 +0100699 await client.database.guilds.append(interaction.guild.id, "tickets.customTypes", toAdd);
Skyler Grey75ea9172022-08-06 10:22:23 +0100700 } catch {
701 continue;
702 }
pineafan6702cef2022-06-13 17:52:37 +0100703 data.customTypes = data.customTypes || [];
704 if (!data.customTypes.includes(toAdd)) {
705 data.customTypes.push(toAdd);
706 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100707 } else {
708 continue;
709 }
pineafane23c4ec2022-07-27 21:56:27 +0100710 } else if (i.component.customId === "switchToDefault") {
pineafan63fc5e22022-08-04 22:04:10 +0100711 i.deferUpdate();
Skyler Grey11236ba2022-08-08 21:13:33 +0100712 await client.database.guilds.write(interaction.guild.id, { "tickets.useCustom": false }, []);
pineafan6702cef2022-06-13 17:52:37 +0100713 data.useCustom = false;
pineafane23c4ec2022-07-27 21:56:27 +0100714 } else if (i.component.customId === "switchToCustom") {
pineafan63fc5e22022-08-04 22:04:10 +0100715 i.deferUpdate();
Skyler Grey11236ba2022-08-08 21:13:33 +0100716 await client.database.guilds.write(interaction.guild.id, { "tickets.useCustom": true }, []);
pineafan6702cef2022-06-13 17:52:37 +0100717 data.useCustom = true;
718 } else {
pineafan63fc5e22022-08-04 22:04:10 +0100719 i.deferUpdate();
720 break;
pineafan6702cef2022-06-13 17:52:37 +0100721 }
722 }
pineafan63fc5e22022-08-04 22:04:10 +0100723 return data;
pineafan6702cef2022-06-13 17:52:37 +0100724}
725
Skyler Grey1a67e182022-08-04 23:05:44 +0100726const check = (interaction: CommandInteraction) => {
Skyler Grey75ea9172022-08-06 10:22:23 +0100727 const member = interaction.member as Discord.GuildMember;
728 if (!member.permissions.has("MANAGE_GUILD"))
pineafan3a02ea32022-08-11 21:35:04 +0100729 throw new Error("You must have the *Manage Server* permission to use this command");
pineafan6702cef2022-06-13 17:52:37 +0100730 return true;
pineafan63fc5e22022-08-04 22:04:10 +0100731};
pineafan4f164f32022-02-26 22:07:12 +0000732
733export { command };
734export { callback };
Skyler Grey1a67e182022-08-04 23:05:44 +0100735export { check };