blob: 5e5cbe7cb158191e13d198d4255e6616cf7d4d17 [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";
18import {
19 SelectMenuOption,
20 SlashCommandSubcommandBuilder
21} from "@discordjs/builders";
pineafan63fc5e22022-08-04 22:04:10 +010022import { ChannelType } from "discord-api-types";
pineafan6702cef2022-06-13 17:52:37 +010023import client from "../../utils/client.js";
Skyler Grey75ea9172022-08-06 10:22:23 +010024import {
25 toHexInteger,
26 toHexArray,
27 tickets as ticketTypes
28} from "../../utils/calculate.js";
pineafan63fc5e22022-08-04 22:04:10 +010029import { capitalize } from "../../utils/generateKeyValueList.js";
pineafan6702cef2022-06-13 17:52:37 +010030import { modalInteractionCollector } from "../../utils/dualCollector.js";
Skyler Grey75ea9172022-08-06 10:22:23 +010031import { GuildConfig } from "../../utils/database.js";
pineafan4f164f32022-02-26 22:07:12 +000032
Skyler Grey75ea9172022-08-06 10:22:23 +010033const command = (builder: SlashCommandSubcommandBuilder) =>
34 builder
35 .setName("tickets")
36 .setDescription(
37 "Shows settings for tickets | Use no arguments to manage custom types"
38 )
39 .addStringOption((option) =>
40 option
41 .setName("enabled")
42 .setDescription("If users should be able to create tickets")
43 .setRequired(false)
44 .addChoices([
45 ["Yes", "yes"],
46 ["No", "no"]
47 ])
48 )
49 .addChannelOption((option) =>
50 option
51 .setName("category")
52 .setDescription("The category where tickets are created")
53 .addChannelType(ChannelType.GuildCategory)
54 .setRequired(false)
55 )
56 .addNumberOption((option) =>
57 option
58 .setName("maxticketsperuser")
59 .setDescription(
60 "The maximum amount of tickets a user can create | Default: 5"
61 )
62 .setRequired(false)
63 .setMinValue(1)
64 )
65 .addRoleOption((option) =>
66 option
67 .setName("supportrole")
68 .setDescription(
69 "This role will have view access to all tickets and will be pinged when a ticket is created"
70 )
71 .setRequired(false)
72 );
pineafan4f164f32022-02-26 22:07:12 +000073
Skyler Grey75ea9172022-08-06 10:22:23 +010074const callback = async (
75 interaction: CommandInteraction
76): Promise<void | unknown> => {
77 let m = (await interaction.reply({
78 embeds: LoadingEmbed,
79 ephemeral: true,
80 fetchReply: true
81 })) as Message;
pineafan63fc5e22022-08-04 22:04:10 +010082 const options = {
pineafan6702cef2022-06-13 17:52:37 +010083 enabled: interaction.options.getString("enabled") as string | boolean,
84 category: interaction.options.getChannel("category"),
85 maxtickets: interaction.options.getNumber("maxticketsperuser"),
86 supportping: interaction.options.getRole("supportrole")
pineafan63fc5e22022-08-04 22:04:10 +010087 };
Skyler Grey75ea9172022-08-06 10:22:23 +010088 if (
89 options.enabled !== null ||
90 options.category ||
91 options.maxtickets ||
92 options.supportping
93 ) {
pineafan6702cef2022-06-13 17:52:37 +010094 options.enabled = options.enabled === "yes" ? true : false;
95 if (options.category) {
Skyler Grey1a67e182022-08-04 23:05:44 +010096 let channel: GuildChannel;
pineafan6702cef2022-06-13 17:52:37 +010097 try {
Skyler Grey75ea9172022-08-06 10:22:23 +010098 channel = await interaction.guild.channels.fetch(
99 options.category.id
100 );
pineafan6702cef2022-06-13 17:52:37 +0100101 } catch {
102 return await interaction.editReply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100103 embeds: [
104 new EmojiEmbed()
105 .setEmoji("CHANNEL.TEXT.DELETE")
106 .setTitle("Tickets > Category")
107 .setDescription(
108 "The channel you provided is not a valid category"
109 )
110 .setStatus("Danger")
pineafan6702cef2022-06-13 17:52:37 +0100111 ]
pineafan63fc5e22022-08-04 22:04:10 +0100112 });
pineafan6702cef2022-06-13 17:52:37 +0100113 }
pineafan63fc5e22022-08-04 22:04:10 +0100114 channel = channel as Discord.CategoryChannel;
Skyler Grey75ea9172022-08-06 10:22:23 +0100115 if (channel.guild.id !== interaction.guild.id)
116 return interaction.editReply({
117 embeds: [
118 new EmojiEmbed()
119 .setTitle("Tickets > Category")
120 .setDescription(
121 "You must choose a category in this server"
122 )
123 .setStatus("Danger")
124 .setEmoji("CHANNEL.TEXT.DELETE")
125 ]
126 });
pineafan6702cef2022-06-13 17:52:37 +0100127 }
128 if (options.maxtickets) {
Skyler Grey75ea9172022-08-06 10:22:23 +0100129 if (options.maxtickets < 1)
130 return interaction.editReply({
131 embeds: [
132 new EmojiEmbed()
133 .setTitle("Tickets > Max Tickets")
134 .setDescription(
135 "You must choose a number greater than 0"
136 )
137 .setStatus("Danger")
138 .setEmoji("CHANNEL.TEXT.DELETE")
139 ]
140 });
pineafan6702cef2022-06-13 17:52:37 +0100141 }
Skyler Grey1a67e182022-08-04 23:05:44 +0100142 let role: Role;
pineafan6702cef2022-06-13 17:52:37 +0100143 if (options.supportping) {
144 try {
Skyler Grey75ea9172022-08-06 10:22:23 +0100145 role = await interaction.guild.roles.fetch(
146 options.supportping.id
147 );
pineafan6702cef2022-06-13 17:52:37 +0100148 } catch {
149 return await interaction.editReply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100150 embeds: [
151 new EmojiEmbed()
152 .setEmoji("GUILD.ROLE.DELETE")
153 .setTitle("Tickets > Support Ping")
154 .setDescription(
155 "The role you provided is not a valid role"
156 )
157 .setStatus("Danger")
pineafan6702cef2022-06-13 17:52:37 +0100158 ]
pineafan63fc5e22022-08-04 22:04:10 +0100159 });
pineafan6702cef2022-06-13 17:52:37 +0100160 }
pineafan63fc5e22022-08-04 22:04:10 +0100161 role = role as Discord.Role;
Skyler Grey75ea9172022-08-06 10:22:23 +0100162 if (role.guild.id !== interaction.guild.id)
163 return interaction.editReply({
164 embeds: [
165 new EmojiEmbed()
166 .setTitle("Tickets > Support Ping")
167 .setDescription(
168 "You must choose a role in this server"
169 )
170 .setStatus("Danger")
171 .setEmoji("GUILD.ROLE.DELETE")
172 ]
173 });
pineafan6702cef2022-06-13 17:52:37 +0100174 }
175
pineafan63fc5e22022-08-04 22:04:10 +0100176 const confirmation = await new confirmationMessage(interaction)
pineafan6702cef2022-06-13 17:52:37 +0100177 .setEmoji("GUILD.TICKET.ARCHIVED")
178 .setTitle("Tickets")
179 .setDescription(
Skyler Grey75ea9172022-08-06 10:22:23 +0100180 (options.category
181 ? `**Category:** ${options.category.name}\n`
182 : "") +
183 (options.maxtickets
184 ? `**Max Tickets:** ${options.maxtickets}\n`
185 : "") +
186 (options.supportping
187 ? `**Support Ping:** ${options.supportping.name}\n`
188 : "") +
189 (options.enabled !== null
190 ? `**Enabled:** ${
191 options.enabled
192 ? `${getEmojiByName("CONTROL.TICK")} Yes`
193 : `${getEmojiByName("CONTROL.CROSS")} No`
194 }\n`
195 : "") +
196 "\nAre you sure you want to apply these settings?"
pineafan6702cef2022-06-13 17:52:37 +0100197 )
198 .setColor("Warning")
199 .setInverted(true)
pineafan63fc5e22022-08-04 22:04:10 +0100200 .send(true);
201 if (confirmation.cancelled) return;
pineafan6702cef2022-06-13 17:52:37 +0100202 if (confirmation.success) {
pineafan63fc5e22022-08-04 22:04:10 +0100203 const toUpdate = {};
Skyler Grey75ea9172022-08-06 10:22:23 +0100204 if (options.enabled !== null)
205 toUpdate["tickets.enabled"] = options.enabled;
206 if (options.category)
207 toUpdate["tickets.category"] = options.category.id;
208 if (options.maxtickets)
209 toUpdate["tickets.maxTickets"] = options.maxtickets;
210 if (options.supportping)
211 toUpdate["tickets.supportRole"] = options.supportping.id;
pineafan6702cef2022-06-13 17:52:37 +0100212 try {
Skyler Grey75ea9172022-08-06 10:22:23 +0100213 await client.database.guilds.write(
214 interaction.guild.id,
215 toUpdate
216 );
pineafan6702cef2022-06-13 17:52:37 +0100217 } catch (e) {
218 return interaction.editReply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100219 embeds: [
220 new EmojiEmbed()
221 .setTitle("Tickets")
222 .setDescription(
223 "Something went wrong and the staff notifications channel could not be set"
224 )
225 .setStatus("Danger")
226 .setEmoji("GUILD.TICKET.DELETE")
227 ],
228 components: []
pineafan6702cef2022-06-13 17:52:37 +0100229 });
230 }
231 } else {
232 return interaction.editReply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100233 embeds: [
234 new EmojiEmbed()
235 .setTitle("Tickets")
236 .setDescription("No changes were made")
237 .setStatus("Success")
238 .setEmoji("GUILD.TICKET.OPEN")
239 ],
240 components: []
pineafan6702cef2022-06-13 17:52:37 +0100241 });
242 }
243 }
pineafan4edb7762022-06-26 19:21:04 +0100244 let data = await client.database.guilds.read(interaction.guild.id);
Skyler Grey75ea9172022-08-06 10:22:23 +0100245 data.tickets.customTypes = (data.tickets.customTypes || []).filter(
246 (value: string, index: number, array: string[]) =>
247 array.indexOf(value) === index
248 );
pineafan6702cef2022-06-13 17:52:37 +0100249 let lastClicked = "";
Skyler Grey1a67e182022-08-04 23:05:44 +0100250 let embed: EmojiEmbed;
pineafan6702cef2022-06-13 17:52:37 +0100251 data = {
252 enabled: data.tickets.enabled,
253 category: data.tickets.category,
254 maxTickets: data.tickets.maxTickets,
255 supportRole: data.tickets.supportRole,
256 useCustom: data.tickets.useCustom,
257 types: data.tickets.types,
258 customTypes: data.tickets.customTypes
pineafan63fc5e22022-08-04 22:04:10 +0100259 };
pineafan6702cef2022-06-13 17:52:37 +0100260 while (true) {
pineafan4edb7762022-06-26 19:21:04 +0100261 embed = new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +0100262 .setTitle("Tickets")
263 .setDescription(
Skyler Grey75ea9172022-08-06 10:22:23 +0100264 `${
265 data.enabled ? "" : getEmojiByName("TICKETS.REPORT")
266 } **Enabled:** ${
267 data.enabled
268 ? `${getEmojiByName("CONTROL.TICK")} Yes`
269 : `${getEmojiByName("CONTROL.CROSS")} No`
270 }\n` +
271 `${
272 data.category ? "" : getEmojiByName("TICKETS.REPORT")
273 } **Category:** ${
274 data.category ? `<#${data.category}>` : "*None set*"
275 }\n` +
276 `**Max Tickets:** ${
277 data.maxTickets ? data.maxTickets : "*No limit*"
278 }\n` +
279 `**Support Ping:** ${
280 data.supportRole
281 ? `<@&${data.supportRole}>`
282 : "*None set*"
283 }\n\n` +
284 (data.useCustom && data.customTypes === null
285 ? `${getEmojiByName("TICKETS.REPORT")} `
286 : "") +
287 `${data.useCustom ? "Custom" : "Default"} types in use` +
288 "\n\n" +
289 `${getEmojiByName(
290 "TICKETS.REPORT"
291 )} *Indicates a setting stopping tickets from being used*`
pineafan6702cef2022-06-13 17:52:37 +0100292 )
293 .setStatus("Success")
pineafan63fc5e22022-08-04 22:04:10 +0100294 .setEmoji("GUILD.TICKET.OPEN");
Skyler Grey75ea9172022-08-06 10:22:23 +0100295 m = (await interaction.editReply({
296 embeds: [embed],
297 components: [
298 new MessageActionRow().addComponents([
299 new MessageButton()
300 .setLabel(
301 "Tickets " + (data.enabled ? "enabled" : "disabled")
302 )
303 .setEmoji(
304 getEmojiByName(
305 "CONTROL." + (data.enabled ? "TICK" : "CROSS"),
306 "id"
307 )
308 )
309 .setStyle(data.enabled ? "SUCCESS" : "DANGER")
310 .setCustomId("enabled"),
311 new MessageButton()
312 .setLabel(
313 lastClicked === "cat"
314 ? "Click again to confirm"
315 : "Clear category"
316 )
317 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
318 .setStyle("DANGER")
319 .setCustomId("clearCategory")
320 .setDisabled(data.category === null),
321 new MessageButton()
322 .setLabel(
323 lastClicked === "max"
324 ? "Click again to confirm"
325 : "Reset max tickets"
326 )
327 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
328 .setStyle("DANGER")
329 .setCustomId("clearMaxTickets")
330 .setDisabled(data.maxTickets === 5),
331 new MessageButton()
332 .setLabel(
333 lastClicked === "sup"
334 ? "Click again to confirm"
335 : "Clear support ping"
336 )
337 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
338 .setStyle("DANGER")
339 .setCustomId("clearSupportPing")
340 .setDisabled(data.supportRole === null)
341 ]),
342 new MessageActionRow().addComponents([
343 new MessageButton()
344 .setLabel("Manage types")
345 .setEmoji(getEmojiByName("TICKETS.OTHER", "id"))
346 .setStyle("SECONDARY")
347 .setCustomId("manageTypes"),
348 new MessageButton()
349 .setLabel("Add create ticket button")
350 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
351 .setStyle("PRIMARY")
352 .setCustomId("send")
353 ])
354 ]
355 })) as Message;
Skyler Grey1a67e182022-08-04 23:05:44 +0100356 let i: MessageComponentInteraction;
pineafan6702cef2022-06-13 17:52:37 +0100357 try {
pineafanbd02b4a2022-08-05 22:01:38 +0100358 i = await m.awaitMessageComponent({ time: 300000 });
Skyler Grey75ea9172022-08-06 10:22:23 +0100359 } catch (e) {
360 break;
361 }
pineafan63fc5e22022-08-04 22:04:10 +0100362 i.deferUpdate();
Skyler Grey75ea9172022-08-06 10:22:23 +0100363 if (
364 (i.component as MessageActionRowComponent).customId ===
365 "clearCategory"
366 ) {
pineafane23c4ec2022-07-27 21:56:27 +0100367 if (lastClicked === "cat") {
pineafan6702cef2022-06-13 17:52:37 +0100368 lastClicked = "";
Skyler Grey75ea9172022-08-06 10:22:23 +0100369 await client.database.guilds.write(interaction.guild.id, null, [
370 "tickets.category"
371 ]);
pineafan6702cef2022-06-13 17:52:37 +0100372 data.category = undefined;
373 } else lastClicked = "cat";
Skyler Grey75ea9172022-08-06 10:22:23 +0100374 } else if (
375 (i.component as MessageActionRowComponent).customId ===
376 "clearMaxTickets"
377 ) {
pineafane23c4ec2022-07-27 21:56:27 +0100378 if (lastClicked === "max") {
pineafan6702cef2022-06-13 17:52:37 +0100379 lastClicked = "";
Skyler Grey75ea9172022-08-06 10:22:23 +0100380 await client.database.guilds.write(interaction.guild.id, null, [
381 "tickets.maxTickets"
382 ]);
pineafan6702cef2022-06-13 17:52:37 +0100383 data.maxTickets = 5;
384 } else lastClicked = "max";
Skyler Grey75ea9172022-08-06 10:22:23 +0100385 } else if (
386 (i.component as MessageActionRowComponent).customId ===
387 "clearSupportPing"
388 ) {
pineafane23c4ec2022-07-27 21:56:27 +0100389 if (lastClicked === "sup") {
pineafan6702cef2022-06-13 17:52:37 +0100390 lastClicked = "";
Skyler Grey75ea9172022-08-06 10:22:23 +0100391 await client.database.guilds.write(interaction.guild.id, null, [
392 "tickets.supportRole"
393 ]);
pineafan6702cef2022-06-13 17:52:37 +0100394 data.supportRole = undefined;
395 } else lastClicked = "sup";
Skyler Grey75ea9172022-08-06 10:22:23 +0100396 } else if (
397 (i.component as MessageActionRowComponent).customId === "send"
398 ) {
pineafan41d93562022-07-30 22:10:15 +0100399 const ticketMessages = [
Skyler Grey75ea9172022-08-06 10:22:23 +0100400 {
401 label: "Create ticket",
402 description: "Click the button below to create a ticket"
403 },
404 {
405 label: "Issues, questions or feedback?",
406 description:
407 "Click below to open a ticket and get help from our staff team"
408 },
409 {
410 label: "Contact Us",
411 description:
412 "Click the button below to speak to us privately"
413 }
pineafan63fc5e22022-08-04 22:04:10 +0100414 ];
pineafan41d93562022-07-30 22:10:15 +0100415 while (true) {
pineafan63fc5e22022-08-04 22:04:10 +0100416 const enabled = data.enabled && data.category !== null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100417 await interaction.editReply({
418 embeds: [
419 new EmojiEmbed()
420 .setTitle("Ticket Button")
421 .setDescription(
422 "Select a message template to send in this channel"
423 )
424 .setFooter({
425 text: enabled
426 ? ""
427 : "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."
428 })
429 .setStatus(enabled ? "Success" : "Warning")
430 .setEmoji("GUILD.ROLES.CREATE")
431 ],
432 components: [
433 new MessageActionRow().addComponents([
434 new MessageSelectMenu()
435 .setOptions(
436 ticketMessages.map(
437 (
438 t: {
439 label: string;
440 description: string;
441 value?: string;
442 },
443 index
444 ) => {
445 t.value = index.toString();
446 return t as {
447 value: string;
448 label: string;
449 description: string;
450 };
451 }
452 )
453 )
454 .setCustomId("template")
455 .setMaxValues(1)
456 .setMinValues(1)
457 .setPlaceholder("Select a message template")
458 ]),
459 new MessageActionRow().addComponents([
460 new MessageButton()
461 .setCustomId("back")
462 .setLabel("Back")
463 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
464 .setStyle("DANGER"),
465 new MessageButton()
466 .setCustomId("blank")
467 .setLabel("Empty")
468 .setStyle("SECONDARY"),
469 new MessageButton()
470 .setCustomId("custom")
471 .setLabel("Custom")
472 .setEmoji(getEmojiByName("TICKETS.OTHER", "id"))
473 .setStyle("PRIMARY")
474 ])
475 ]
476 });
Skyler Grey1a67e182022-08-04 23:05:44 +0100477 let i: MessageComponentInteraction;
pineafan41d93562022-07-30 22:10:15 +0100478 try {
Skyler Grey75ea9172022-08-06 10:22:23 +0100479 i = await m.awaitMessageComponent({ time: 300000 });
480 } catch (e) {
pineafan63fc5e22022-08-04 22:04:10 +0100481 break;
Skyler Grey75ea9172022-08-06 10:22:23 +0100482 }
483 if (
484 (i.component as MessageActionRowComponent).customId ===
485 "template"
486 ) {
pineafan63fc5e22022-08-04 22:04:10 +0100487 i.deferUpdate();
Skyler Grey75ea9172022-08-06 10:22:23 +0100488 await interaction.channel.send({
489 embeds: [
490 new EmojiEmbed()
491 .setTitle(
492 ticketMessages[
493 parseInt(
494 (i as SelectMenuInteraction)
495 .values[0]
496 )
497 ].label
498 )
499 .setDescription(
500 ticketMessages[
501 parseInt(
502 (i as SelectMenuInteraction)
503 .values[0]
504 )
505 ].description
506 )
507 .setStatus("Success")
508 .setEmoji("GUILD.TICKET.OPEN")
509 ],
510 components: [
511 new MessageActionRow().addComponents([
512 new MessageButton()
513 .setLabel("Create Ticket")
514 .setEmoji(
515 getEmojiByName("CONTROL.TICK", "id")
516 )
517 .setStyle("SUCCESS")
518 .setCustomId("createticket")
519 ])
520 ]
521 });
pineafan63fc5e22022-08-04 22:04:10 +0100522 break;
Skyler Grey75ea9172022-08-06 10:22:23 +0100523 } else if (
524 (i.component as MessageActionRowComponent).customId ===
525 "blank"
526 ) {
527 i.deferUpdate();
528 await interaction.channel.send({
529 components: [
530 new MessageActionRow().addComponents([
531 new MessageButton()
532 .setLabel("Create Ticket")
533 .setEmoji(
534 getEmojiByName(
535 "TICKETS.SUGGESTION",
536 "id"
537 )
538 )
539 .setStyle("SUCCESS")
540 .setCustomId("createticket")
541 ])
542 ]
543 });
544 break;
545 } else if (
546 (i.component as MessageActionRowComponent).customId ===
547 "custom"
548 ) {
549 await i.showModal(
550 new Discord.Modal()
551 .setCustomId("modal")
552 .setTitle("Enter embed details")
553 .addComponents(
554 new MessageActionRow<TextInputComponent>().addComponents(
555 new TextInputComponent()
556 .setCustomId("title")
557 .setLabel("Title")
558 .setMaxLength(256)
559 .setRequired(true)
560 .setStyle("SHORT")
561 ),
562 new MessageActionRow<TextInputComponent>().addComponents(
563 new TextInputComponent()
564 .setCustomId("description")
565 .setLabel("Description")
566 .setMaxLength(4000)
567 .setRequired(true)
568 .setStyle("PARAGRAPH")
569 )
570 )
571 );
pineafan41d93562022-07-30 22:10:15 +0100572 await interaction.editReply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100573 embeds: [
574 new EmojiEmbed()
575 .setTitle("Ticket Button")
576 .setDescription(
577 "Modal opened. If you can't see it, click back and try again."
578 )
579 .setStatus("Success")
580 .setEmoji("GUILD.TICKET.OPEN")
581 ],
582 components: [
583 new MessageActionRow().addComponents([
584 new MessageButton()
585 .setLabel("Back")
586 .setEmoji(
587 getEmojiByName("CONTROL.LEFT", "id")
588 )
589 .setStyle("PRIMARY")
590 .setCustomId("back")
591 ])
592 ]
pineafan41d93562022-07-30 22:10:15 +0100593 });
594 let out;
595 try {
Skyler Grey75ea9172022-08-06 10:22:23 +0100596 out = await modalInteractionCollector(
597 m,
598 (m) => m.channel.id === interaction.channel.id,
599 (m) => m.customId === "modify"
600 );
601 } catch (e) {
602 break;
603 }
pineafan41d93562022-07-30 22:10:15 +0100604 if (out.fields) {
pineafan63fc5e22022-08-04 22:04:10 +0100605 const title = out.fields.getTextInputValue("title");
Skyler Grey75ea9172022-08-06 10:22:23 +0100606 const description =
607 out.fields.getTextInputValue("description");
608 await interaction.channel.send({
609 embeds: [
610 new EmojiEmbed()
611 .setTitle(title)
612 .setDescription(description)
613 .setStatus("Success")
614 .setEmoji("GUILD.TICKET.OPEN")
615 ],
616 components: [
617 new MessageActionRow().addComponents([
618 new MessageButton()
619 .setLabel("Create Ticket")
620 .setEmoji(
621 getEmojiByName(
622 "TICKETS.SUGGESTION",
623 "id"
624 )
625 )
626 .setStyle("SUCCESS")
627 .setCustomId("createticket")
628 ])
629 ]
630 });
pineafan63fc5e22022-08-04 22:04:10 +0100631 break;
Skyler Grey75ea9172022-08-06 10:22:23 +0100632 } else {
633 continue;
634 }
pineafan41d93562022-07-30 22:10:15 +0100635 }
636 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100637 } else if (
638 (i.component as MessageActionRowComponent).customId === "enabled"
639 ) {
640 await client.database.guilds.write(interaction.guild.id, {
641 "tickets.enabled": !data.enabled
642 });
pineafan6702cef2022-06-13 17:52:37 +0100643 data.enabled = !data.enabled;
Skyler Grey75ea9172022-08-06 10:22:23 +0100644 } else if (
645 (i.component as MessageActionRowComponent).customId ===
646 "manageTypes"
647 ) {
Skyler Grey1a67e182022-08-04 23:05:44 +0100648 data = await manageTypes(interaction, data, m as Message);
pineafan6702cef2022-06-13 17:52:37 +0100649 } else {
pineafan63fc5e22022-08-04 22:04:10 +0100650 break;
pineafan6702cef2022-06-13 17:52:37 +0100651 }
652 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100653 await interaction.editReply({
654 embeds: [embed.setFooter({ text: "Message closed" })],
655 components: []
656 });
pineafan63fc5e22022-08-04 22:04:10 +0100657};
pineafan4f164f32022-02-26 22:07:12 +0000658
Skyler Grey75ea9172022-08-06 10:22:23 +0100659async function manageTypes(
660 interaction: CommandInteraction,
661 data: GuildConfig["tickets"],
662 m: Message
663) {
pineafan6702cef2022-06-13 17:52:37 +0100664 while (true) {
665 if (data.useCustom) {
pineafan63fc5e22022-08-04 22:04:10 +0100666 const customTypes = data.customTypes;
pineafanc6158ab2022-06-17 16:34:07 +0100667 await interaction.editReply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100668 embeds: [
669 new EmojiEmbed()
670 .setTitle("Tickets > Types")
671 .setDescription(
672 "**Custom types enabled**\n\n" +
673 "**Types in use:**\n" +
674 (customTypes !== null
675 ? customTypes
676 .map((t) => `> ${t}`)
677 .join("\n")
678 : "*None set*") +
679 "\n\n" +
680 (customTypes === null
681 ? `${getEmojiByName(
682 "TICKETS.REPORT"
683 )} Having no types will disable tickets. Please add at least 1 type or use default types`
684 : "")
pineafan6702cef2022-06-13 17:52:37 +0100685 )
Skyler Grey75ea9172022-08-06 10:22:23 +0100686 .setStatus("Success")
687 .setEmoji("GUILD.TICKET.OPEN")
688 ],
689 components: (customTypes
690 ? [
691 new MessageActionRow().addComponents([
692 new Discord.MessageSelectMenu()
693 .setCustomId("removeTypes")
694 .setPlaceholder("Select types to remove")
695 .setMaxValues(customTypes.length)
696 .setMinValues(1)
697 .addOptions(
698 customTypes.map((t) => ({
699 label: t,
700 value: t
701 }))
702 )
703 ])
704 ]
705 : []
706 ).concat([
pineafan6702cef2022-06-13 17:52:37 +0100707 new MessageActionRow().addComponents([
708 new MessageButton()
709 .setLabel("Back")
710 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
711 .setStyle("PRIMARY")
712 .setCustomId("back"),
713 new MessageButton()
714 .setLabel("Add new type")
Skyler Grey75ea9172022-08-06 10:22:23 +0100715 .setEmoji(
716 getEmojiByName("TICKETS.SUGGESTION", "id")
717 )
pineafan6702cef2022-06-13 17:52:37 +0100718 .setStyle("PRIMARY")
719 .setCustomId("addType")
Skyler Grey75ea9172022-08-06 10:22:23 +0100720 .setDisabled(
721 customTypes !== null && customTypes.length >= 25
722 ),
pineafan6702cef2022-06-13 17:52:37 +0100723 new MessageButton()
724 .setLabel("Switch to default types")
725 .setStyle("SECONDARY")
pineafan63fc5e22022-08-04 22:04:10 +0100726 .setCustomId("switchToDefault")
pineafan6702cef2022-06-13 17:52:37 +0100727 ])
728 ])
729 });
730 } else {
pineafan63fc5e22022-08-04 22:04:10 +0100731 const inUse = toHexArray(data.types, ticketTypes);
732 const options = [];
Skyler Grey75ea9172022-08-06 10:22:23 +0100733 ticketTypes.forEach((type) => {
734 options.push(
735 new SelectMenuOption({
736 label: capitalize(type),
737 value: type,
738 emoji: client.emojis.cache.get(
739 getEmojiByName(
740 `TICKETS.${type.toUpperCase()}`,
741 "id"
742 )
743 ),
744 default: inUse.includes(type)
745 })
746 );
pineafan63fc5e22022-08-04 22:04:10 +0100747 });
748 const selectPane = new MessageActionRow().addComponents([
pineafan6702cef2022-06-13 17:52:37 +0100749 new Discord.MessageSelectMenu()
750 .addOptions(options)
751 .setCustomId("types")
752 .setMaxValues(ticketTypes.length)
753 .setMinValues(1)
754 .setPlaceholder("Select types to use")
pineafan63fc5e22022-08-04 22:04:10 +0100755 ]);
pineafanc6158ab2022-06-17 16:34:07 +0100756 await interaction.editReply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100757 embeds: [
758 new EmojiEmbed()
759 .setTitle("Tickets > Types")
760 .setDescription(
761 "**Default types enabled**\n\n" +
762 "**Types in use:**\n" +
763 inUse
764 .map(
765 (t) =>
766 `> ${getEmojiByName(
767 "TICKETS." + t.toUpperCase()
768 )} ${capitalize(t)}`
769 )
770 .join("\n")
771 )
772 .setStatus("Success")
773 .setEmoji("GUILD.TICKET.OPEN")
774 ],
775 components: [
pineafan6702cef2022-06-13 17:52:37 +0100776 selectPane,
777 new MessageActionRow().addComponents([
778 new MessageButton()
779 .setLabel("Back")
780 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
781 .setStyle("PRIMARY")
782 .setCustomId("back"),
783 new MessageButton()
784 .setLabel("Switch to custom types")
785 .setStyle("SECONDARY")
pineafan63fc5e22022-08-04 22:04:10 +0100786 .setCustomId("switchToCustom")
pineafan6702cef2022-06-13 17:52:37 +0100787 ])
788 ]
789 });
790 }
791 let i;
792 try {
pineafanc6158ab2022-06-17 16:34:07 +0100793 i = await m.awaitMessageComponent({ time: 300000 });
Skyler Grey75ea9172022-08-06 10:22:23 +0100794 } catch (e) {
795 break;
796 }
pineafane23c4ec2022-07-27 21:56:27 +0100797 if (i.component.customId === "types") {
pineafan63fc5e22022-08-04 22:04:10 +0100798 i.deferUpdate();
799 const types = toHexInteger(i.values, ticketTypes);
Skyler Grey75ea9172022-08-06 10:22:23 +0100800 await client.database.guilds.write(interaction.guild.id, {
801 "tickets.types": types
802 });
pineafan6702cef2022-06-13 17:52:37 +0100803 data.types = types;
pineafane23c4ec2022-07-27 21:56:27 +0100804 } else if (i.component.customId === "removeTypes") {
pineafan63fc5e22022-08-04 22:04:10 +0100805 i.deferUpdate();
806 const types = i.values;
pineafan6702cef2022-06-13 17:52:37 +0100807 let customTypes = data.customTypes;
808 if (customTypes) {
809 customTypes = customTypes.filter((t) => !types.includes(t));
810 customTypes = customTypes.length > 0 ? customTypes : null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100811 await client.database.guilds.write(interaction.guild.id, {
812 "tickets.customTypes": customTypes
813 });
pineafan6702cef2022-06-13 17:52:37 +0100814 data.customTypes = customTypes;
815 }
pineafane23c4ec2022-07-27 21:56:27 +0100816 } else if (i.component.customId === "addType") {
Skyler Grey75ea9172022-08-06 10:22:23 +0100817 await i.showModal(
818 new Discord.Modal()
819 .setCustomId("modal")
820 .setTitle("Enter a name for the new type")
821 .addComponents(
822 new MessageActionRow<TextInputComponent>().addComponents(
823 new TextInputComponent()
824 .setCustomId("type")
825 .setLabel("Name")
826 .setMaxLength(100)
827 .setMinLength(1)
828 .setPlaceholder('E.g. "Server Idea"')
829 .setRequired(true)
830 .setStyle("SHORT")
831 )
832 )
833 );
pineafan6702cef2022-06-13 17:52:37 +0100834 await interaction.editReply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100835 embeds: [
836 new EmojiEmbed()
837 .setTitle("Tickets > Types")
838 .setDescription(
839 "Modal opened. If you can't see it, click back and try again."
840 )
841 .setStatus("Success")
842 .setEmoji("GUILD.TICKET.OPEN")
843 ],
844 components: [
845 new MessageActionRow().addComponents([
846 new MessageButton()
847 .setLabel("Back")
848 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
849 .setStyle("PRIMARY")
850 .setCustomId("back")
851 ])
852 ]
pineafan6702cef2022-06-13 17:52:37 +0100853 });
pineafan4edb7762022-06-26 19:21:04 +0100854 let out;
pineafan6702cef2022-06-13 17:52:37 +0100855 try {
Skyler Grey75ea9172022-08-06 10:22:23 +0100856 out = await modalInteractionCollector(
857 m,
858 (m) => m.channel.id === interaction.channel.id,
859 (m) => m.customId === "addType"
860 );
861 } catch (e) {
862 continue;
863 }
pineafan6702cef2022-06-13 17:52:37 +0100864 if (out.fields) {
865 let toAdd = out.fields.getTextInputValue("type");
Skyler Grey75ea9172022-08-06 10:22:23 +0100866 if (!toAdd) {
867 continue;
868 }
pineafan63fc5e22022-08-04 22:04:10 +0100869 toAdd = toAdd.substring(0, 80);
pineafan6702cef2022-06-13 17:52:37 +0100870 try {
Skyler Grey75ea9172022-08-06 10:22:23 +0100871 await client.database.guilds.append(
872 interaction.guild.id,
873 "tickets.customTypes",
874 toAdd
875 );
876 } catch {
877 continue;
878 }
pineafan6702cef2022-06-13 17:52:37 +0100879 data.customTypes = data.customTypes || [];
880 if (!data.customTypes.includes(toAdd)) {
881 data.customTypes.push(toAdd);
882 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100883 } else {
884 continue;
885 }
pineafane23c4ec2022-07-27 21:56:27 +0100886 } else if (i.component.customId === "switchToDefault") {
pineafan63fc5e22022-08-04 22:04:10 +0100887 i.deferUpdate();
Skyler Grey75ea9172022-08-06 10:22:23 +0100888 await client.database.guilds.write(
889 interaction.guild.id,
890 { "tickets.useCustom": false },
891 []
892 );
pineafan6702cef2022-06-13 17:52:37 +0100893 data.useCustom = false;
pineafane23c4ec2022-07-27 21:56:27 +0100894 } else if (i.component.customId === "switchToCustom") {
pineafan63fc5e22022-08-04 22:04:10 +0100895 i.deferUpdate();
Skyler Grey75ea9172022-08-06 10:22:23 +0100896 await client.database.guilds.write(
897 interaction.guild.id,
898 { "tickets.useCustom": true },
899 []
900 );
pineafan6702cef2022-06-13 17:52:37 +0100901 data.useCustom = true;
902 } else {
pineafan63fc5e22022-08-04 22:04:10 +0100903 i.deferUpdate();
904 break;
pineafan6702cef2022-06-13 17:52:37 +0100905 }
906 }
pineafan63fc5e22022-08-04 22:04:10 +0100907 return data;
pineafan6702cef2022-06-13 17:52:37 +0100908}
909
Skyler Grey1a67e182022-08-04 23:05:44 +0100910const check = (interaction: CommandInteraction) => {
Skyler Grey75ea9172022-08-06 10:22:23 +0100911 const member = interaction.member as Discord.GuildMember;
912 if (!member.permissions.has("MANAGE_GUILD"))
913 throw "You must have the *Manage Server* permission to use this command";
pineafan6702cef2022-06-13 17:52:37 +0100914 return true;
pineafan63fc5e22022-08-04 22:04:10 +0100915};
pineafan4f164f32022-02-26 22:07:12 +0000916
917export { command };
918export { callback };
Skyler Grey1a67e182022-08-04 23:05:44 +0100919export { check };