blob: f9df71c46a89434b8a4dd8c5e52dd842ee64db00 [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";
pineafanbd02b4a2022-08-05 22:01:38 +01005import Discord, { CommandInteraction, GuildChannel, Message, MessageActionRow, MessageActionRowComponent, MessageButton, MessageComponentInteraction, MessageSelectMenu, Role, SelectMenuInteraction, TextInputComponent } from "discord.js";
pineafan6702cef2022-06-13 17:52:37 +01006import { SelectMenuOption, SlashCommandSubcommandBuilder } from "@discordjs/builders";
pineafan63fc5e22022-08-04 22:04:10 +01007import { ChannelType } from "discord-api-types";
pineafan6702cef2022-06-13 17:52:37 +01008import client from "../../utils/client.js";
9import { toHexInteger, toHexArray, tickets as ticketTypes } from "../../utils/calculate.js";
pineafan63fc5e22022-08-04 22:04:10 +010010import { capitalize } from "../../utils/generateKeyValueList.js";
pineafan6702cef2022-06-13 17:52:37 +010011import { modalInteractionCollector } from "../../utils/dualCollector.js";
Skyler Grey1a67e182022-08-04 23:05:44 +010012import {GuildConfig} from "../../utils/database.js";
pineafan4f164f32022-02-26 22:07:12 +000013
pineafan6702cef2022-06-13 17:52:37 +010014const command = (builder: SlashCommandSubcommandBuilder) => builder
pineafan4f164f32022-02-26 22:07:12 +000015 .setName("tickets")
pineafan6702cef2022-06-13 17:52:37 +010016 .setDescription("Shows settings for tickets | Use no arguments to manage custom types")
17 .addStringOption(option => option.setName("enabled").setDescription("If users should be able to create tickets").setRequired(false)
pineafan1dc15722022-03-14 21:27:34 +000018 .addChoices([["Yes", "yes"], ["No", "no"]]))
19 .addChannelOption(option => option.setName("category").setDescription("The category where tickets are created").addChannelType(ChannelType.GuildCategory).setRequired(false))
pineafan73a7c4a2022-07-24 10:38:04 +010020 .addNumberOption(option => option.setName("maxticketsperuser").setDescription("The maximum amount of tickets a user can create | Default: 5").setRequired(false).setMinValue(1))
pineafan63fc5e22022-08-04 22:04:10 +010021 .addRoleOption(option => option.setName("supportrole").setDescription("This role will have view access to all tickets and will be pinged when a ticket is created").setRequired(false));
pineafan4f164f32022-02-26 22:07:12 +000022
Skyler Grey1a67e182022-08-04 23:05:44 +010023const callback = async (interaction: CommandInteraction): Promise<void | unknown> => {
pineafanbd02b4a2022-08-05 22:01:38 +010024 let m = await interaction.reply({embeds: LoadingEmbed, ephemeral: true, fetchReply: true}) as Message;
pineafan63fc5e22022-08-04 22:04:10 +010025 const options = {
pineafan6702cef2022-06-13 17:52:37 +010026 enabled: interaction.options.getString("enabled") as string | boolean,
27 category: interaction.options.getChannel("category"),
28 maxtickets: interaction.options.getNumber("maxticketsperuser"),
29 supportping: interaction.options.getRole("supportrole")
pineafan63fc5e22022-08-04 22:04:10 +010030 };
pineafan6702cef2022-06-13 17:52:37 +010031 if (options.enabled !== null || options.category || options.maxtickets || options.supportping) {
32 options.enabled = options.enabled === "yes" ? true : false;
33 if (options.category) {
Skyler Grey1a67e182022-08-04 23:05:44 +010034 let channel: GuildChannel;
pineafan6702cef2022-06-13 17:52:37 +010035 try {
Skyler Grey1a67e182022-08-04 23:05:44 +010036 channel = await interaction.guild.channels.fetch(options.category.id);
pineafan6702cef2022-06-13 17:52:37 +010037 } catch {
38 return await interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +010039 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +010040 .setEmoji("CHANNEL.TEXT.DELETE")
41 .setTitle("Tickets > Category")
42 .setDescription("The channel you provided is not a valid category")
43 .setStatus("Danger")
44 ]
pineafan63fc5e22022-08-04 22:04:10 +010045 });
pineafan6702cef2022-06-13 17:52:37 +010046 }
pineafan63fc5e22022-08-04 22:04:10 +010047 channel = channel as Discord.CategoryChannel;
pineafane23c4ec2022-07-27 21:56:27 +010048 if (channel.guild.id !== interaction.guild.id) return interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +010049 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +010050 .setTitle("Tickets > Category")
pineafan63fc5e22022-08-04 22:04:10 +010051 .setDescription("You must choose a category in this server")
pineafan6702cef2022-06-13 17:52:37 +010052 .setStatus("Danger")
53 .setEmoji("CHANNEL.TEXT.DELETE")
54 ]
55 });
56 }
57 if (options.maxtickets) {
58 if (options.maxtickets < 1) return interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +010059 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +010060 .setTitle("Tickets > Max Tickets")
pineafan63fc5e22022-08-04 22:04:10 +010061 .setDescription("You must choose a number greater than 0")
pineafan6702cef2022-06-13 17:52:37 +010062 .setStatus("Danger")
63 .setEmoji("CHANNEL.TEXT.DELETE")
64 ]
65 });
66 }
Skyler Grey1a67e182022-08-04 23:05:44 +010067 let role: Role;
pineafan6702cef2022-06-13 17:52:37 +010068 if (options.supportping) {
69 try {
Skyler Grey1a67e182022-08-04 23:05:44 +010070 role = await interaction.guild.roles.fetch(options.supportping.id);
pineafan6702cef2022-06-13 17:52:37 +010071 } catch {
72 return await interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +010073 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +010074 .setEmoji("GUILD.ROLE.DELETE")
75 .setTitle("Tickets > Support Ping")
76 .setDescription("The role you provided is not a valid role")
77 .setStatus("Danger")
78 ]
pineafan63fc5e22022-08-04 22:04:10 +010079 });
pineafan6702cef2022-06-13 17:52:37 +010080 }
pineafan63fc5e22022-08-04 22:04:10 +010081 role = role as Discord.Role;
pineafane23c4ec2022-07-27 21:56:27 +010082 if (role.guild.id !== interaction.guild.id) return interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +010083 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +010084 .setTitle("Tickets > Support Ping")
pineafan63fc5e22022-08-04 22:04:10 +010085 .setDescription("You must choose a role in this server")
pineafan6702cef2022-06-13 17:52:37 +010086 .setStatus("Danger")
87 .setEmoji("GUILD.ROLE.DELETE")
88 ]
89 });
90 }
91
pineafan63fc5e22022-08-04 22:04:10 +010092 const confirmation = await new confirmationMessage(interaction)
pineafan6702cef2022-06-13 17:52:37 +010093 .setEmoji("GUILD.TICKET.ARCHIVED")
94 .setTitle("Tickets")
95 .setDescription(
96 (options.category ? `**Category:** ${options.category.name}\n` : "") +
97 (options.maxtickets ? `**Max Tickets:** ${options.maxtickets}\n` : "") +
98 (options.supportping ? `**Support Ping:** ${options.supportping.name}\n` : "") +
99 (options.enabled !== null ? `**Enabled:** ${options.enabled ? `${getEmojiByName("CONTROL.TICK")} Yes` : `${getEmojiByName("CONTROL.CROSS")} No`
pineafan63fc5e22022-08-04 22:04:10 +0100100 }\n` : "") +
101 "\nAre you sure you want to apply these settings?"
pineafan6702cef2022-06-13 17:52:37 +0100102 )
103 .setColor("Warning")
104 .setInverted(true)
pineafan63fc5e22022-08-04 22:04:10 +0100105 .send(true);
106 if (confirmation.cancelled) return;
pineafan6702cef2022-06-13 17:52:37 +0100107 if (confirmation.success) {
pineafan63fc5e22022-08-04 22:04:10 +0100108 const toUpdate = {};
109 if (options.enabled !== null) toUpdate["tickets.enabled"] = options.enabled;
110 if (options.category) toUpdate["tickets.category"] = options.category.id;
111 if (options.maxtickets) toUpdate["tickets.maxTickets"] = options.maxtickets;
112 if (options.supportping) toUpdate["tickets.supportRole"] = options.supportping.id;
pineafan6702cef2022-06-13 17:52:37 +0100113 try {
pineafan63fc5e22022-08-04 22:04:10 +0100114 await client.database.guilds.write(interaction.guild.id, toUpdate);
pineafan6702cef2022-06-13 17:52:37 +0100115 } catch (e) {
116 return interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +0100117 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +0100118 .setTitle("Tickets")
pineafan63fc5e22022-08-04 22:04:10 +0100119 .setDescription("Something went wrong and the staff notifications channel could not be set")
pineafan6702cef2022-06-13 17:52:37 +0100120 .setStatus("Danger")
121 .setEmoji("GUILD.TICKET.DELETE")
122 ], components: []
123 });
124 }
125 } else {
126 return interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +0100127 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +0100128 .setTitle("Tickets")
pineafan63fc5e22022-08-04 22:04:10 +0100129 .setDescription("No changes were made")
pineafan6702cef2022-06-13 17:52:37 +0100130 .setStatus("Success")
131 .setEmoji("GUILD.TICKET.OPEN")
132 ], components: []
133 });
134 }
135 }
pineafan4edb7762022-06-26 19:21:04 +0100136 let data = await client.database.guilds.read(interaction.guild.id);
Skyler Grey1a67e182022-08-04 23:05:44 +0100137 data.tickets.customTypes = (data.tickets.customTypes || []).filter((value: string, index: number, array: string[]) => array.indexOf(value) === index);
pineafan6702cef2022-06-13 17:52:37 +0100138 let lastClicked = "";
Skyler Grey1a67e182022-08-04 23:05:44 +0100139 let embed: EmojiEmbed;
pineafan6702cef2022-06-13 17:52:37 +0100140 data = {
141 enabled: data.tickets.enabled,
142 category: data.tickets.category,
143 maxTickets: data.tickets.maxTickets,
144 supportRole: data.tickets.supportRole,
145 useCustom: data.tickets.useCustom,
146 types: data.tickets.types,
147 customTypes: data.tickets.customTypes
pineafan63fc5e22022-08-04 22:04:10 +0100148 };
pineafan6702cef2022-06-13 17:52:37 +0100149 while (true) {
pineafan4edb7762022-06-26 19:21:04 +0100150 embed = new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +0100151 .setTitle("Tickets")
152 .setDescription(
153 `${data.enabled ? "" : getEmojiByName("TICKETS.REPORT")} **Enabled:** ${data.enabled ? `${getEmojiByName("CONTROL.TICK")} Yes` : `${getEmojiByName("CONTROL.CROSS")} No`}\n` +
154 `${data.category ? "" : getEmojiByName("TICKETS.REPORT")} **Category:** ${data.category ? `<#${data.category}>` : "*None set*"}\n` +
155 `**Max Tickets:** ${data.maxTickets ? data.maxTickets : "*No limit*"}\n` +
156 `**Support Ping:** ${data.supportRole ? `<@&${data.supportRole}>` : "*None set*"}\n\n` +
157 ((data.useCustom && data.customTypes === null) ? `${getEmojiByName("TICKETS.REPORT")} ` : "") +
158 `${data.useCustom ? "Custom" : "Default"} types in use` + "\n\n" +
159 `${getEmojiByName("TICKETS.REPORT")} *Indicates a setting stopping tickets from being used*`
160 )
161 .setStatus("Success")
pineafan63fc5e22022-08-04 22:04:10 +0100162 .setEmoji("GUILD.TICKET.OPEN");
pineafan6702cef2022-06-13 17:52:37 +0100163 m = await interaction.editReply({
164 embeds: [embed], components: [new MessageActionRow().addComponents([
165 new MessageButton()
166 .setLabel("Tickets " + (data.enabled ? "enabled" : "disabled"))
167 .setEmoji(getEmojiByName("CONTROL." + (data.enabled ? "TICK" : "CROSS"), "id"))
168 .setStyle(data.enabled ? "SUCCESS" : "DANGER")
169 .setCustomId("enabled"),
170 new MessageButton()
pineafane23c4ec2022-07-27 21:56:27 +0100171 .setLabel(lastClicked === "cat" ? "Click again to confirm" : "Clear category")
pineafan6702cef2022-06-13 17:52:37 +0100172 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
173 .setStyle("DANGER")
174 .setCustomId("clearCategory")
pineafane23c4ec2022-07-27 21:56:27 +0100175 .setDisabled(data.category === null),
pineafan6702cef2022-06-13 17:52:37 +0100176 new MessageButton()
pineafane23c4ec2022-07-27 21:56:27 +0100177 .setLabel(lastClicked === "max" ? "Click again to confirm" : "Reset max tickets")
pineafan6702cef2022-06-13 17:52:37 +0100178 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
179 .setStyle("DANGER")
180 .setCustomId("clearMaxTickets")
pineafane23c4ec2022-07-27 21:56:27 +0100181 .setDisabled(data.maxTickets === 5),
pineafan6702cef2022-06-13 17:52:37 +0100182 new MessageButton()
pineafane23c4ec2022-07-27 21:56:27 +0100183 .setLabel(lastClicked === "sup" ? "Click again to confirm" : "Clear support ping")
pineafan6702cef2022-06-13 17:52:37 +0100184 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
185 .setStyle("DANGER")
186 .setCustomId("clearSupportPing")
pineafan63fc5e22022-08-04 22:04:10 +0100187 .setDisabled(data.supportRole === null)
pineafan6702cef2022-06-13 17:52:37 +0100188 ]), new MessageActionRow().addComponents([
189 new MessageButton()
190 .setLabel("Manage types")
191 .setEmoji(getEmojiByName("TICKETS.OTHER", "id"))
192 .setStyle("SECONDARY")
193 .setCustomId("manageTypes"),
pineafan41d93562022-07-30 22:10:15 +0100194 new MessageButton()
195 .setLabel("Add create ticket button")
196 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
197 .setStyle("PRIMARY")
pineafan63fc5e22022-08-04 22:04:10 +0100198 .setCustomId("send")
pineafan6702cef2022-06-13 17:52:37 +0100199 ])]
pineafanbd02b4a2022-08-05 22:01:38 +0100200 }) as Message;
Skyler Grey1a67e182022-08-04 23:05:44 +0100201 let i: MessageComponentInteraction;
pineafan6702cef2022-06-13 17:52:37 +0100202 try {
pineafanbd02b4a2022-08-05 22:01:38 +0100203 i = await m.awaitMessageComponent({ time: 300000 });
pineafan63fc5e22022-08-04 22:04:10 +0100204 } catch (e) { break; }
205 i.deferUpdate();
Skyler Grey1a67e182022-08-04 23:05:44 +0100206 if ((i.component as MessageActionRowComponent).customId === "clearCategory") {
pineafane23c4ec2022-07-27 21:56:27 +0100207 if (lastClicked === "cat") {
pineafan6702cef2022-06-13 17:52:37 +0100208 lastClicked = "";
pineafan63fc5e22022-08-04 22:04:10 +0100209 await client.database.guilds.write(interaction.guild.id, null, ["tickets.category"]);
pineafan6702cef2022-06-13 17:52:37 +0100210 data.category = undefined;
211 } else lastClicked = "cat";
Skyler Grey1a67e182022-08-04 23:05:44 +0100212 } else if ((i.component as MessageActionRowComponent).customId === "clearMaxTickets") {
pineafane23c4ec2022-07-27 21:56:27 +0100213 if (lastClicked === "max") {
pineafan6702cef2022-06-13 17:52:37 +0100214 lastClicked = "";
pineafan63fc5e22022-08-04 22:04:10 +0100215 await client.database.guilds.write(interaction.guild.id, null, ["tickets.maxTickets"]);
pineafan6702cef2022-06-13 17:52:37 +0100216 data.maxTickets = 5;
217 } else lastClicked = "max";
Skyler Grey1a67e182022-08-04 23:05:44 +0100218 } else if ((i.component as MessageActionRowComponent).customId === "clearSupportPing") {
pineafane23c4ec2022-07-27 21:56:27 +0100219 if (lastClicked === "sup") {
pineafan6702cef2022-06-13 17:52:37 +0100220 lastClicked = "";
pineafan63fc5e22022-08-04 22:04:10 +0100221 await client.database.guilds.write(interaction.guild.id, null, ["tickets.supportRole"]);
pineafan6702cef2022-06-13 17:52:37 +0100222 data.supportRole = undefined;
223 } else lastClicked = "sup";
Skyler Grey1a67e182022-08-04 23:05:44 +0100224 } else if ((i.component as MessageActionRowComponent).customId === "send") {
pineafan41d93562022-07-30 22:10:15 +0100225 const ticketMessages = [
226 {label: "Create ticket", description: "Click the button below to create a ticket"},
227 {label: "Issues, questions or feedback?", description: "Click below to open a ticket and get help from our staff team"},
pineafan63fc5e22022-08-04 22:04:10 +0100228 {label: "Contact Us", description: "Click the button below to speak to us privately"}
229 ];
pineafan41d93562022-07-30 22:10:15 +0100230 while (true) {
pineafan63fc5e22022-08-04 22:04:10 +0100231 const enabled = data.enabled && data.category !== null;
pineafan41d93562022-07-30 22:10:15 +0100232 await interaction.editReply({embeds: [new EmojiEmbed()
233 .setTitle("Ticket Button")
234 .setDescription("Select a message template to send in this channel")
235 .setFooter({text: enabled ? "" : "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."})
236 .setStatus(enabled ? "Success" : "Warning")
237 .setEmoji("GUILD.ROLES.CREATE")
238 ], components: [
239 new MessageActionRow().addComponents([
240 new MessageSelectMenu().setOptions(ticketMessages.map((t: {label: string, description: string, value?: string}, index) => {
pineafan63fc5e22022-08-04 22:04:10 +0100241 t.value = index.toString(); return t as {value: string, label: string, description: string};
242 })).setCustomId("template").setMaxValues(1).setMinValues(1).setPlaceholder("Select a message template")
pineafan41d93562022-07-30 22:10:15 +0100243 ]),
244 new MessageActionRow().addComponents([
245 new MessageButton()
246 .setCustomId("back")
247 .setLabel("Back")
248 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
249 .setStyle("DANGER"),
250 new MessageButton()
251 .setCustomId("blank")
252 .setLabel("Empty")
253 .setStyle("SECONDARY"),
254 new MessageButton()
255 .setCustomId("custom")
256 .setLabel("Custom")
257 .setEmoji(getEmojiByName("TICKETS.OTHER", "id"))
258 .setStyle("PRIMARY")
259 ])
260 ]});
Skyler Grey1a67e182022-08-04 23:05:44 +0100261 let i: MessageComponentInteraction;
pineafan41d93562022-07-30 22:10:15 +0100262 try {
pineafanbd02b4a2022-08-05 22:01:38 +0100263 i = await m.awaitMessageComponent({time: 300000});
pineafan63fc5e22022-08-04 22:04:10 +0100264 } catch(e) { break; }
Skyler Grey1a67e182022-08-04 23:05:44 +0100265 if ((i.component as MessageActionRowComponent).customId === "template") {
pineafan63fc5e22022-08-04 22:04:10 +0100266 i.deferUpdate();
pineafan41d93562022-07-30 22:10:15 +0100267 await interaction.channel.send({embeds: [new EmojiEmbed()
Skyler Grey1a67e182022-08-04 23:05:44 +0100268 .setTitle(ticketMessages[parseInt((i as SelectMenuInteraction).values[0])].label)
269 .setDescription(ticketMessages[parseInt((i as SelectMenuInteraction).values[0])].description)
pineafan41d93562022-07-30 22:10:15 +0100270 .setStatus("Success")
271 .setEmoji("GUILD.TICKET.OPEN")
272 ], components: [new MessageActionRow().addComponents([new MessageButton()
273 .setLabel("Create Ticket")
274 .setEmoji(getEmojiByName("CONTROL.TICK", "id"))
275 .setStyle("SUCCESS")
276 .setCustomId("createticket")
277 ])]});
pineafan63fc5e22022-08-04 22:04:10 +0100278 break;
Skyler Grey1a67e182022-08-04 23:05:44 +0100279 } else if ((i.component as MessageActionRowComponent).customId === "blank") {
pineafan63fc5e22022-08-04 22:04:10 +0100280 i.deferUpdate();
pineafan41d93562022-07-30 22:10:15 +0100281 await interaction.channel.send({components: [new MessageActionRow().addComponents([new MessageButton()
282 .setLabel("Create Ticket")
283 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
284 .setStyle("SUCCESS")
285 .setCustomId("createticket")
286 ])]});
pineafan63fc5e22022-08-04 22:04:10 +0100287 break;
Skyler Grey1a67e182022-08-04 23:05:44 +0100288 } else if ((i.component as MessageActionRowComponent).customId === "custom") {
pineafan63fc5e22022-08-04 22:04:10 +0100289 await i.showModal(new Discord.Modal().setCustomId("modal").setTitle("Enter embed details").addComponents(
pineafan41d93562022-07-30 22:10:15 +0100290 new MessageActionRow<TextInputComponent>().addComponents(new TextInputComponent()
291 .setCustomId("title")
292 .setLabel("Title")
293 .setMaxLength(256)
294 .setRequired(true)
295 .setStyle("SHORT")
296 ),
297 new MessageActionRow<TextInputComponent>().addComponents(new TextInputComponent()
298 .setCustomId("description")
299 .setLabel("Description")
300 .setMaxLength(4000)
301 .setRequired(true)
302 .setStyle("PARAGRAPH")
303 )
pineafan63fc5e22022-08-04 22:04:10 +0100304 ));
pineafan41d93562022-07-30 22:10:15 +0100305 await interaction.editReply({
306 embeds: [new EmojiEmbed()
307 .setTitle("Ticket Button")
308 .setDescription("Modal opened. If you can't see it, click back and try again.")
309 .setStatus("Success")
310 .setEmoji("GUILD.TICKET.OPEN")
311 ], components: [new MessageActionRow().addComponents([new MessageButton()
312 .setLabel("Back")
313 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
314 .setStyle("PRIMARY")
315 .setCustomId("back")
316 ])]
317 });
318 let out;
319 try {
pineafan63fc5e22022-08-04 22:04:10 +0100320 out = await modalInteractionCollector(m, (m) => m.channel.id === interaction.channel.id, (m) => m.customId === "modify");
321 } catch (e) { break; }
pineafan41d93562022-07-30 22:10:15 +0100322 if (out.fields) {
pineafan63fc5e22022-08-04 22:04:10 +0100323 const title = out.fields.getTextInputValue("title");
324 const description = out.fields.getTextInputValue("description");
pineafan41d93562022-07-30 22:10:15 +0100325 await interaction.channel.send({embeds: [new EmojiEmbed()
326 .setTitle(title)
327 .setDescription(description)
328 .setStatus("Success")
329 .setEmoji("GUILD.TICKET.OPEN")
330 ], components: [new MessageActionRow().addComponents([new MessageButton()
331 .setLabel("Create Ticket")
332 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
333 .setStyle("SUCCESS")
334 .setCustomId("createticket")
335 ])]});
pineafan63fc5e22022-08-04 22:04:10 +0100336 break;
337 } else { continue; }
pineafan41d93562022-07-30 22:10:15 +0100338 }
339 }
Skyler Grey1a67e182022-08-04 23:05:44 +0100340 } else if ((i.component as MessageActionRowComponent).customId === "enabled") {
pineafan63fc5e22022-08-04 22:04:10 +0100341 await client.database.guilds.write(interaction.guild.id, { "tickets.enabled": !data.enabled });
pineafan6702cef2022-06-13 17:52:37 +0100342 data.enabled = !data.enabled;
Skyler Grey1a67e182022-08-04 23:05:44 +0100343 } else if ((i.component as MessageActionRowComponent).customId === "manageTypes") {
344 data = await manageTypes(interaction, data, m as Message);
pineafan6702cef2022-06-13 17:52:37 +0100345 } else {
pineafan63fc5e22022-08-04 22:04:10 +0100346 break;
pineafan6702cef2022-06-13 17:52:37 +0100347 }
348 }
349 await interaction.editReply({ embeds: [embed.setFooter({ text: "Message closed" })], components: [] });
pineafan63fc5e22022-08-04 22:04:10 +0100350};
pineafan4f164f32022-02-26 22:07:12 +0000351
pineafanbd02b4a2022-08-05 22:01:38 +0100352async function manageTypes(interaction: CommandInteraction, data: GuildConfig["tickets"], m: Message) {
pineafan6702cef2022-06-13 17:52:37 +0100353 while (true) {
354 if (data.useCustom) {
pineafan63fc5e22022-08-04 22:04:10 +0100355 const customTypes = data.customTypes;
pineafanc6158ab2022-06-17 16:34:07 +0100356 await interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +0100357 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +0100358 .setTitle("Tickets > Types")
359 .setDescription(
360 "**Custom types enabled**\n\n" +
361 "**Types in use:**\n" + ((customTypes !== null) ?
362 (customTypes.map((t) => `> ${t}`).join("\n")) :
363 "*None set*"
364 ) + "\n\n" + (customTypes === null ?
365 `${getEmojiByName("TICKETS.REPORT")} Having no types will disable tickets. Please add at least 1 type or use default types` : ""
366 )
367 )
368 .setStatus("Success")
369 .setEmoji("GUILD.TICKET.OPEN")
370 ], components: (customTypes ? [
371 new MessageActionRow().addComponents([new Discord.MessageSelectMenu()
372 .setCustomId("removeTypes")
373 .setPlaceholder("Select types to remove")
374 .setMaxValues(customTypes.length)
375 .setMinValues(1)
pineafanbd02b4a2022-08-05 22:01:38 +0100376 .addOptions(customTypes.map((t) => ({
377 label: t,
378 value: t
379 })))
pineafan6702cef2022-06-13 17:52:37 +0100380 ])
381 ] : []).concat([
382 new MessageActionRow().addComponents([
383 new MessageButton()
384 .setLabel("Back")
385 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
386 .setStyle("PRIMARY")
387 .setCustomId("back"),
388 new MessageButton()
389 .setLabel("Add new type")
390 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
391 .setStyle("PRIMARY")
392 .setCustomId("addType")
393 .setDisabled(customTypes !== null && customTypes.length >= 25),
394 new MessageButton()
395 .setLabel("Switch to default types")
396 .setStyle("SECONDARY")
pineafan63fc5e22022-08-04 22:04:10 +0100397 .setCustomId("switchToDefault")
pineafan6702cef2022-06-13 17:52:37 +0100398 ])
399 ])
400 });
401 } else {
pineafan63fc5e22022-08-04 22:04:10 +0100402 const inUse = toHexArray(data.types, ticketTypes);
403 const options = [];
pineafan6702cef2022-06-13 17:52:37 +0100404 ticketTypes.forEach(type => {
405 options.push(new SelectMenuOption({
406 label: capitalize(type),
407 value: type,
PineappleFanb3dd83c2022-06-17 10:53:48 +0100408 emoji: client.emojis.cache.get(getEmojiByName(`TICKETS.${type.toUpperCase()}`, "id")),
pineafan6702cef2022-06-13 17:52:37 +0100409 default: inUse.includes(type)
pineafan63fc5e22022-08-04 22:04:10 +0100410 }));
411 });
412 const selectPane = new MessageActionRow().addComponents([
pineafan6702cef2022-06-13 17:52:37 +0100413 new Discord.MessageSelectMenu()
414 .addOptions(options)
415 .setCustomId("types")
416 .setMaxValues(ticketTypes.length)
417 .setMinValues(1)
418 .setPlaceholder("Select types to use")
pineafan63fc5e22022-08-04 22:04:10 +0100419 ]);
pineafanc6158ab2022-06-17 16:34:07 +0100420 await interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +0100421 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +0100422 .setTitle("Tickets > Types")
423 .setDescription(
424 "**Default types enabled**\n\n" +
425 "**Types in use:**\n" +
426 (inUse.map((t) => `> ${getEmojiByName("TICKETS." + t.toUpperCase())} ${capitalize(t)}`).join("\n"))
427 )
428 .setStatus("Success")
429 .setEmoji("GUILD.TICKET.OPEN")
430 ], components: [
431 selectPane,
432 new MessageActionRow().addComponents([
433 new MessageButton()
434 .setLabel("Back")
435 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
436 .setStyle("PRIMARY")
437 .setCustomId("back"),
438 new MessageButton()
439 .setLabel("Switch to custom types")
440 .setStyle("SECONDARY")
pineafan63fc5e22022-08-04 22:04:10 +0100441 .setCustomId("switchToCustom")
pineafan6702cef2022-06-13 17:52:37 +0100442 ])
443 ]
444 });
445 }
446 let i;
447 try {
pineafanc6158ab2022-06-17 16:34:07 +0100448 i = await m.awaitMessageComponent({ time: 300000 });
pineafan63fc5e22022-08-04 22:04:10 +0100449 } catch (e) { break; }
pineafane23c4ec2022-07-27 21:56:27 +0100450 if (i.component.customId === "types") {
pineafan63fc5e22022-08-04 22:04:10 +0100451 i.deferUpdate();
452 const types = toHexInteger(i.values, ticketTypes);
453 await client.database.guilds.write(interaction.guild.id, { "tickets.types": types });
pineafan6702cef2022-06-13 17:52:37 +0100454 data.types = types;
pineafane23c4ec2022-07-27 21:56:27 +0100455 } else if (i.component.customId === "removeTypes") {
pineafan63fc5e22022-08-04 22:04:10 +0100456 i.deferUpdate();
457 const types = i.values;
pineafan6702cef2022-06-13 17:52:37 +0100458 let customTypes = data.customTypes;
459 if (customTypes) {
460 customTypes = customTypes.filter((t) => !types.includes(t));
461 customTypes = customTypes.length > 0 ? customTypes : null;
pineafan63fc5e22022-08-04 22:04:10 +0100462 await client.database.guilds.write(interaction.guild.id, { "tickets.customTypes": customTypes });
pineafan6702cef2022-06-13 17:52:37 +0100463 data.customTypes = customTypes;
464 }
pineafane23c4ec2022-07-27 21:56:27 +0100465 } else if (i.component.customId === "addType") {
pineafan6702cef2022-06-13 17:52:37 +0100466 await i.showModal(new Discord.Modal().setCustomId("modal").setTitle("Enter a name for the new type").addComponents(
pineafan02ba0232022-07-24 22:16:15 +0100467 new MessageActionRow<TextInputComponent>().addComponents(new TextInputComponent()
pineafan6702cef2022-06-13 17:52:37 +0100468 .setCustomId("type")
469 .setLabel("Name")
470 .setMaxLength(100)
471 .setMinLength(1)
472 .setPlaceholder("E.g. \"Server Idea\"")
473 .setRequired(true)
474 .setStyle("SHORT")
475 )
pineafan63fc5e22022-08-04 22:04:10 +0100476 ));
pineafan6702cef2022-06-13 17:52:37 +0100477 await interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +0100478 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +0100479 .setTitle("Tickets > Types")
480 .setDescription("Modal opened. If you can't see it, click back and try again.")
481 .setStatus("Success")
482 .setEmoji("GUILD.TICKET.OPEN")
483 ], components: [new MessageActionRow().addComponents([new MessageButton()
484 .setLabel("Back")
485 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
486 .setStyle("PRIMARY")
487 .setCustomId("back")
488 ])]
489 });
pineafan4edb7762022-06-26 19:21:04 +0100490 let out;
pineafan6702cef2022-06-13 17:52:37 +0100491 try {
pineafan63fc5e22022-08-04 22:04:10 +0100492 out = await modalInteractionCollector(m, (m) => m.channel.id === interaction.channel.id, (m) => m.customId === "addType");
493 } catch (e) { continue; }
pineafan6702cef2022-06-13 17:52:37 +0100494 if (out.fields) {
495 let toAdd = out.fields.getTextInputValue("type");
pineafan63fc5e22022-08-04 22:04:10 +0100496 if (!toAdd) { continue; }
497 toAdd = toAdd.substring(0, 80);
pineafan6702cef2022-06-13 17:52:37 +0100498 try {
pineafan63fc5e22022-08-04 22:04:10 +0100499 await client.database.guilds.append(interaction.guild.id, "tickets.customTypes", toAdd);
500 } catch { continue; }
pineafan6702cef2022-06-13 17:52:37 +0100501 data.customTypes = data.customTypes || [];
502 if (!data.customTypes.includes(toAdd)) {
503 data.customTypes.push(toAdd);
504 }
pineafan63fc5e22022-08-04 22:04:10 +0100505 } else { continue; }
pineafane23c4ec2022-07-27 21:56:27 +0100506 } else if (i.component.customId === "switchToDefault") {
pineafan63fc5e22022-08-04 22:04:10 +0100507 i.deferUpdate();
508 await client.database.guilds.write(interaction.guild.id, { "tickets.useCustom": false }, []);
pineafan6702cef2022-06-13 17:52:37 +0100509 data.useCustom = false;
pineafane23c4ec2022-07-27 21:56:27 +0100510 } else if (i.component.customId === "switchToCustom") {
pineafan63fc5e22022-08-04 22:04:10 +0100511 i.deferUpdate();
512 await client.database.guilds.write(interaction.guild.id, { "tickets.useCustom": true }, []);
pineafan6702cef2022-06-13 17:52:37 +0100513 data.useCustom = true;
514 } else {
pineafan63fc5e22022-08-04 22:04:10 +0100515 i.deferUpdate();
516 break;
pineafan6702cef2022-06-13 17:52:37 +0100517 }
518 }
pineafan63fc5e22022-08-04 22:04:10 +0100519 return data;
pineafan6702cef2022-06-13 17:52:37 +0100520}
521
522
Skyler Grey1a67e182022-08-04 23:05:44 +0100523const check = (interaction: CommandInteraction) => {
pineafan63fc5e22022-08-04 22:04:10 +0100524 const member = (interaction.member as Discord.GuildMember);
525 if (!member.permissions.has("MANAGE_GUILD")) throw "You must have the *Manage Server* permission to use this command";
pineafan6702cef2022-06-13 17:52:37 +0100526 return true;
pineafan63fc5e22022-08-04 22:04:10 +0100527};
pineafan4f164f32022-02-26 22:07:12 +0000528
529export { command };
530export { callback };
Skyler Grey1a67e182022-08-04 23:05:44 +0100531export { check };