blob: 3d1901b5df3d2c843501681816d60799726aaec2 [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";
5import Discord, { CommandInteraction, MessageActionRow, MessageButton, MessageSelectMenu, TextInputComponent } from "discord.js";
6import { SelectMenuOption, SlashCommandSubcommandBuilder } from "@discordjs/builders";
pineafan4f164f32022-02-26 22:07:12 +00007import { WrappedCheck } from "jshaiku";
pineafan63fc5e22022-08-04 22:04:10 +01008import { ChannelType } from "discord-api-types";
pineafan6702cef2022-06-13 17:52:37 +01009import client from "../../utils/client.js";
10import { toHexInteger, toHexArray, tickets as ticketTypes } from "../../utils/calculate.js";
pineafan63fc5e22022-08-04 22:04:10 +010011import { capitalize } from "../../utils/generateKeyValueList.js";
pineafan6702cef2022-06-13 17:52:37 +010012import { modalInteractionCollector } from "../../utils/dualCollector.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
pineafan6702cef2022-06-13 17:52:37 +010023const callback = async (interaction: CommandInteraction): Promise<any> => {
24 let m;
pineafan63fc5e22022-08-04 22:04:10 +010025 m = await interaction.reply({embeds: LoadingEmbed, ephemeral: true, fetchReply: true});
26 const options = {
pineafan6702cef2022-06-13 17:52:37 +010027 enabled: interaction.options.getString("enabled") as string | boolean,
28 category: interaction.options.getChannel("category"),
29 maxtickets: interaction.options.getNumber("maxticketsperuser"),
30 supportping: interaction.options.getRole("supportrole")
pineafan63fc5e22022-08-04 22:04:10 +010031 };
32 console.log(m);
pineafan6702cef2022-06-13 17:52:37 +010033 if (options.enabled !== null || options.category || options.maxtickets || options.supportping) {
34 options.enabled = options.enabled === "yes" ? true : false;
35 if (options.category) {
pineafan63fc5e22022-08-04 22:04:10 +010036 let channel;
pineafan6702cef2022-06-13 17:52:37 +010037 try {
pineafan63fc5e22022-08-04 22:04:10 +010038 channel = interaction.guild.channels.cache.get(options.category.id);
pineafan6702cef2022-06-13 17:52:37 +010039 } catch {
40 return await interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +010041 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +010042 .setEmoji("CHANNEL.TEXT.DELETE")
43 .setTitle("Tickets > Category")
44 .setDescription("The channel you provided is not a valid category")
45 .setStatus("Danger")
46 ]
pineafan63fc5e22022-08-04 22:04:10 +010047 });
pineafan6702cef2022-06-13 17:52:37 +010048 }
pineafan63fc5e22022-08-04 22:04:10 +010049 channel = channel as Discord.CategoryChannel;
pineafane23c4ec2022-07-27 21:56:27 +010050 if (channel.guild.id !== interaction.guild.id) return interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +010051 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +010052 .setTitle("Tickets > Category")
pineafan63fc5e22022-08-04 22:04:10 +010053 .setDescription("You must choose a category in this server")
pineafan6702cef2022-06-13 17:52:37 +010054 .setStatus("Danger")
55 .setEmoji("CHANNEL.TEXT.DELETE")
56 ]
57 });
58 }
59 if (options.maxtickets) {
60 if (options.maxtickets < 1) return interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +010061 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +010062 .setTitle("Tickets > Max Tickets")
pineafan63fc5e22022-08-04 22:04:10 +010063 .setDescription("You must choose a number greater than 0")
pineafan6702cef2022-06-13 17:52:37 +010064 .setStatus("Danger")
65 .setEmoji("CHANNEL.TEXT.DELETE")
66 ]
67 });
68 }
pineafan63fc5e22022-08-04 22:04:10 +010069 let role;
pineafan6702cef2022-06-13 17:52:37 +010070 if (options.supportping) {
71 try {
pineafan63fc5e22022-08-04 22:04:10 +010072 role = interaction.guild.roles.cache.get(options.supportping.id);
pineafan6702cef2022-06-13 17:52:37 +010073 } catch {
74 return await interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +010075 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +010076 .setEmoji("GUILD.ROLE.DELETE")
77 .setTitle("Tickets > Support Ping")
78 .setDescription("The role you provided is not a valid role")
79 .setStatus("Danger")
80 ]
pineafan63fc5e22022-08-04 22:04:10 +010081 });
pineafan6702cef2022-06-13 17:52:37 +010082 }
pineafan63fc5e22022-08-04 22:04:10 +010083 role = role as Discord.Role;
pineafane23c4ec2022-07-27 21:56:27 +010084 if (role.guild.id !== interaction.guild.id) return interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +010085 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +010086 .setTitle("Tickets > Support Ping")
pineafan63fc5e22022-08-04 22:04:10 +010087 .setDescription("You must choose a role in this server")
pineafan6702cef2022-06-13 17:52:37 +010088 .setStatus("Danger")
89 .setEmoji("GUILD.ROLE.DELETE")
90 ]
91 });
92 }
93
pineafan63fc5e22022-08-04 22:04:10 +010094 const confirmation = await new confirmationMessage(interaction)
pineafan6702cef2022-06-13 17:52:37 +010095 .setEmoji("GUILD.TICKET.ARCHIVED")
96 .setTitle("Tickets")
97 .setDescription(
98 (options.category ? `**Category:** ${options.category.name}\n` : "") +
99 (options.maxtickets ? `**Max Tickets:** ${options.maxtickets}\n` : "") +
100 (options.supportping ? `**Support Ping:** ${options.supportping.name}\n` : "") +
101 (options.enabled !== null ? `**Enabled:** ${options.enabled ? `${getEmojiByName("CONTROL.TICK")} Yes` : `${getEmojiByName("CONTROL.CROSS")} No`
pineafan63fc5e22022-08-04 22:04:10 +0100102 }\n` : "") +
103 "\nAre you sure you want to apply these settings?"
pineafan6702cef2022-06-13 17:52:37 +0100104 )
105 .setColor("Warning")
106 .setInverted(true)
pineafan63fc5e22022-08-04 22:04:10 +0100107 .send(true);
108 if (confirmation.cancelled) return;
pineafan6702cef2022-06-13 17:52:37 +0100109 if (confirmation.success) {
pineafan63fc5e22022-08-04 22:04:10 +0100110 const toUpdate = {};
111 if (options.enabled !== null) toUpdate["tickets.enabled"] = options.enabled;
112 if (options.category) toUpdate["tickets.category"] = options.category.id;
113 if (options.maxtickets) toUpdate["tickets.maxTickets"] = options.maxtickets;
114 if (options.supportping) toUpdate["tickets.supportRole"] = options.supportping.id;
pineafan6702cef2022-06-13 17:52:37 +0100115 try {
pineafan63fc5e22022-08-04 22:04:10 +0100116 await client.database.guilds.write(interaction.guild.id, toUpdate);
pineafan6702cef2022-06-13 17:52:37 +0100117 } catch (e) {
118 return interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +0100119 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +0100120 .setTitle("Tickets")
pineafan63fc5e22022-08-04 22:04:10 +0100121 .setDescription("Something went wrong and the staff notifications channel could not be set")
pineafan6702cef2022-06-13 17:52:37 +0100122 .setStatus("Danger")
123 .setEmoji("GUILD.TICKET.DELETE")
124 ], components: []
125 });
126 }
127 } else {
128 return interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +0100129 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +0100130 .setTitle("Tickets")
pineafan63fc5e22022-08-04 22:04:10 +0100131 .setDescription("No changes were made")
pineafan6702cef2022-06-13 17:52:37 +0100132 .setStatus("Success")
133 .setEmoji("GUILD.TICKET.OPEN")
134 ], components: []
135 });
136 }
137 }
pineafan4edb7762022-06-26 19:21:04 +0100138 let data = await client.database.guilds.read(interaction.guild.id);
pineafan63fc5e22022-08-04 22:04:10 +0100139 data.tickets.customTypes = (data.tickets.customTypes || []).filter((v, i, a) => a.indexOf(v) === i);
pineafan6702cef2022-06-13 17:52:37 +0100140 let lastClicked = "";
141 let embed;
142 data = {
143 enabled: data.tickets.enabled,
144 category: data.tickets.category,
145 maxTickets: data.tickets.maxTickets,
146 supportRole: data.tickets.supportRole,
147 useCustom: data.tickets.useCustom,
148 types: data.tickets.types,
149 customTypes: data.tickets.customTypes
pineafan63fc5e22022-08-04 22:04:10 +0100150 };
pineafan6702cef2022-06-13 17:52:37 +0100151 while (true) {
pineafan4edb7762022-06-26 19:21:04 +0100152 embed = new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +0100153 .setTitle("Tickets")
154 .setDescription(
155 `${data.enabled ? "" : getEmojiByName("TICKETS.REPORT")} **Enabled:** ${data.enabled ? `${getEmojiByName("CONTROL.TICK")} Yes` : `${getEmojiByName("CONTROL.CROSS")} No`}\n` +
156 `${data.category ? "" : getEmojiByName("TICKETS.REPORT")} **Category:** ${data.category ? `<#${data.category}>` : "*None set*"}\n` +
157 `**Max Tickets:** ${data.maxTickets ? data.maxTickets : "*No limit*"}\n` +
158 `**Support Ping:** ${data.supportRole ? `<@&${data.supportRole}>` : "*None set*"}\n\n` +
159 ((data.useCustom && data.customTypes === null) ? `${getEmojiByName("TICKETS.REPORT")} ` : "") +
160 `${data.useCustom ? "Custom" : "Default"} types in use` + "\n\n" +
161 `${getEmojiByName("TICKETS.REPORT")} *Indicates a setting stopping tickets from being used*`
162 )
163 .setStatus("Success")
pineafan63fc5e22022-08-04 22:04:10 +0100164 .setEmoji("GUILD.TICKET.OPEN");
pineafan6702cef2022-06-13 17:52:37 +0100165 m = await interaction.editReply({
166 embeds: [embed], components: [new MessageActionRow().addComponents([
167 new MessageButton()
168 .setLabel("Tickets " + (data.enabled ? "enabled" : "disabled"))
169 .setEmoji(getEmojiByName("CONTROL." + (data.enabled ? "TICK" : "CROSS"), "id"))
170 .setStyle(data.enabled ? "SUCCESS" : "DANGER")
171 .setCustomId("enabled"),
172 new MessageButton()
pineafane23c4ec2022-07-27 21:56:27 +0100173 .setLabel(lastClicked === "cat" ? "Click again to confirm" : "Clear category")
pineafan6702cef2022-06-13 17:52:37 +0100174 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
175 .setStyle("DANGER")
176 .setCustomId("clearCategory")
pineafane23c4ec2022-07-27 21:56:27 +0100177 .setDisabled(data.category === null),
pineafan6702cef2022-06-13 17:52:37 +0100178 new MessageButton()
pineafane23c4ec2022-07-27 21:56:27 +0100179 .setLabel(lastClicked === "max" ? "Click again to confirm" : "Reset max tickets")
pineafan6702cef2022-06-13 17:52:37 +0100180 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
181 .setStyle("DANGER")
182 .setCustomId("clearMaxTickets")
pineafane23c4ec2022-07-27 21:56:27 +0100183 .setDisabled(data.maxTickets === 5),
pineafan6702cef2022-06-13 17:52:37 +0100184 new MessageButton()
pineafane23c4ec2022-07-27 21:56:27 +0100185 .setLabel(lastClicked === "sup" ? "Click again to confirm" : "Clear support ping")
pineafan6702cef2022-06-13 17:52:37 +0100186 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
187 .setStyle("DANGER")
188 .setCustomId("clearSupportPing")
pineafan63fc5e22022-08-04 22:04:10 +0100189 .setDisabled(data.supportRole === null)
pineafan6702cef2022-06-13 17:52:37 +0100190 ]), new MessageActionRow().addComponents([
191 new MessageButton()
192 .setLabel("Manage types")
193 .setEmoji(getEmojiByName("TICKETS.OTHER", "id"))
194 .setStyle("SECONDARY")
195 .setCustomId("manageTypes"),
pineafan41d93562022-07-30 22:10:15 +0100196 new MessageButton()
197 .setLabel("Add create ticket button")
198 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
199 .setStyle("PRIMARY")
pineafan63fc5e22022-08-04 22:04:10 +0100200 .setCustomId("send")
pineafan6702cef2022-06-13 17:52:37 +0100201 ])]
202 });
203 let i;
204 try {
pineafanc6158ab2022-06-17 16:34:07 +0100205 i = await m.awaitMessageComponent({ time: 300000 });
pineafan63fc5e22022-08-04 22:04:10 +0100206 } catch (e) { break; }
207 i.deferUpdate();
pineafane23c4ec2022-07-27 21:56:27 +0100208 if (i.component.customId === "clearCategory") {
209 if (lastClicked === "cat") {
pineafan6702cef2022-06-13 17:52:37 +0100210 lastClicked = "";
pineafan63fc5e22022-08-04 22:04:10 +0100211 await client.database.guilds.write(interaction.guild.id, null, ["tickets.category"]);
pineafan6702cef2022-06-13 17:52:37 +0100212 data.category = undefined;
213 } else lastClicked = "cat";
pineafane23c4ec2022-07-27 21:56:27 +0100214 } else if (i.component.customId === "clearMaxTickets") {
215 if (lastClicked === "max") {
pineafan6702cef2022-06-13 17:52:37 +0100216 lastClicked = "";
pineafan63fc5e22022-08-04 22:04:10 +0100217 await client.database.guilds.write(interaction.guild.id, null, ["tickets.maxTickets"]);
pineafan6702cef2022-06-13 17:52:37 +0100218 data.maxTickets = 5;
219 } else lastClicked = "max";
pineafane23c4ec2022-07-27 21:56:27 +0100220 } else if (i.component.customId === "clearSupportPing") {
221 if (lastClicked === "sup") {
pineafan6702cef2022-06-13 17:52:37 +0100222 lastClicked = "";
pineafan63fc5e22022-08-04 22:04:10 +0100223 await client.database.guilds.write(interaction.guild.id, null, ["tickets.supportRole"]);
pineafan6702cef2022-06-13 17:52:37 +0100224 data.supportRole = undefined;
225 } else lastClicked = "sup";
pineafan41d93562022-07-30 22:10:15 +0100226 } else if (i.component.customId === "send") {
227 const ticketMessages = [
228 {label: "Create ticket", description: "Click the button below to create a ticket"},
229 {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 +0100230 {label: "Contact Us", description: "Click the button below to speak to us privately"}
231 ];
pineafan41d93562022-07-30 22:10:15 +0100232 while (true) {
pineafan63fc5e22022-08-04 22:04:10 +0100233 const enabled = data.enabled && data.category !== null;
pineafan41d93562022-07-30 22:10:15 +0100234 await interaction.editReply({embeds: [new EmojiEmbed()
235 .setTitle("Ticket Button")
236 .setDescription("Select a message template to send in this channel")
237 .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."})
238 .setStatus(enabled ? "Success" : "Warning")
239 .setEmoji("GUILD.ROLES.CREATE")
240 ], components: [
241 new MessageActionRow().addComponents([
242 new MessageSelectMenu().setOptions(ticketMessages.map((t: {label: string, description: string, value?: string}, index) => {
pineafan63fc5e22022-08-04 22:04:10 +0100243 t.value = index.toString(); return t as {value: string, label: string, description: string};
244 })).setCustomId("template").setMaxValues(1).setMinValues(1).setPlaceholder("Select a message template")
pineafan41d93562022-07-30 22:10:15 +0100245 ]),
246 new MessageActionRow().addComponents([
247 new MessageButton()
248 .setCustomId("back")
249 .setLabel("Back")
250 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
251 .setStyle("DANGER"),
252 new MessageButton()
253 .setCustomId("blank")
254 .setLabel("Empty")
255 .setStyle("SECONDARY"),
256 new MessageButton()
257 .setCustomId("custom")
258 .setLabel("Custom")
259 .setEmoji(getEmojiByName("TICKETS.OTHER", "id"))
260 .setStyle("PRIMARY")
261 ])
262 ]});
263 let i;
264 try {
265 i = await m.awaitMessageComponent({time: 300000});
pineafan63fc5e22022-08-04 22:04:10 +0100266 } catch(e) { break; }
pineafan41d93562022-07-30 22:10:15 +0100267 if (i.component.customId === "template") {
pineafan63fc5e22022-08-04 22:04:10 +0100268 i.deferUpdate();
pineafan41d93562022-07-30 22:10:15 +0100269 await interaction.channel.send({embeds: [new EmojiEmbed()
270 .setTitle(ticketMessages[parseInt(i.values[0])].label)
271 .setDescription(ticketMessages[parseInt(i.values[0])].description)
272 .setStatus("Success")
273 .setEmoji("GUILD.TICKET.OPEN")
274 ], components: [new MessageActionRow().addComponents([new MessageButton()
275 .setLabel("Create Ticket")
276 .setEmoji(getEmojiByName("CONTROL.TICK", "id"))
277 .setStyle("SUCCESS")
278 .setCustomId("createticket")
279 ])]});
pineafan63fc5e22022-08-04 22:04:10 +0100280 break;
pineafan41d93562022-07-30 22:10:15 +0100281 } else if (i.component.customId === "blank") {
pineafan63fc5e22022-08-04 22:04:10 +0100282 i.deferUpdate();
pineafan41d93562022-07-30 22:10:15 +0100283 await interaction.channel.send({components: [new MessageActionRow().addComponents([new MessageButton()
284 .setLabel("Create Ticket")
285 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
286 .setStyle("SUCCESS")
287 .setCustomId("createticket")
288 ])]});
pineafan63fc5e22022-08-04 22:04:10 +0100289 break;
pineafan41d93562022-07-30 22:10:15 +0100290 } else if (i.component.customId === "custom") {
pineafan63fc5e22022-08-04 22:04:10 +0100291 await i.showModal(new Discord.Modal().setCustomId("modal").setTitle("Enter embed details").addComponents(
pineafan41d93562022-07-30 22:10:15 +0100292 new MessageActionRow<TextInputComponent>().addComponents(new TextInputComponent()
293 .setCustomId("title")
294 .setLabel("Title")
295 .setMaxLength(256)
296 .setRequired(true)
297 .setStyle("SHORT")
298 ),
299 new MessageActionRow<TextInputComponent>().addComponents(new TextInputComponent()
300 .setCustomId("description")
301 .setLabel("Description")
302 .setMaxLength(4000)
303 .setRequired(true)
304 .setStyle("PARAGRAPH")
305 )
pineafan63fc5e22022-08-04 22:04:10 +0100306 ));
pineafan41d93562022-07-30 22:10:15 +0100307 await interaction.editReply({
308 embeds: [new EmojiEmbed()
309 .setTitle("Ticket Button")
310 .setDescription("Modal opened. If you can't see it, click back and try again.")
311 .setStatus("Success")
312 .setEmoji("GUILD.TICKET.OPEN")
313 ], components: [new MessageActionRow().addComponents([new MessageButton()
314 .setLabel("Back")
315 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
316 .setStyle("PRIMARY")
317 .setCustomId("back")
318 ])]
319 });
320 let out;
321 try {
pineafan63fc5e22022-08-04 22:04:10 +0100322 out = await modalInteractionCollector(m, (m) => m.channel.id === interaction.channel.id, (m) => m.customId === "modify");
323 } catch (e) { break; }
pineafan41d93562022-07-30 22:10:15 +0100324 if (out.fields) {
pineafan63fc5e22022-08-04 22:04:10 +0100325 const title = out.fields.getTextInputValue("title");
326 const description = out.fields.getTextInputValue("description");
pineafan41d93562022-07-30 22:10:15 +0100327 await interaction.channel.send({embeds: [new EmojiEmbed()
328 .setTitle(title)
329 .setDescription(description)
330 .setStatus("Success")
331 .setEmoji("GUILD.TICKET.OPEN")
332 ], components: [new MessageActionRow().addComponents([new MessageButton()
333 .setLabel("Create Ticket")
334 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
335 .setStyle("SUCCESS")
336 .setCustomId("createticket")
337 ])]});
pineafan63fc5e22022-08-04 22:04:10 +0100338 break;
339 } else { continue; }
pineafan41d93562022-07-30 22:10:15 +0100340 }
341 }
pineafane23c4ec2022-07-27 21:56:27 +0100342 } else if (i.component.customId === "enabled") {
pineafan63fc5e22022-08-04 22:04:10 +0100343 await client.database.guilds.write(interaction.guild.id, { "tickets.enabled": !data.enabled });
pineafan6702cef2022-06-13 17:52:37 +0100344 data.enabled = !data.enabled;
pineafane23c4ec2022-07-27 21:56:27 +0100345 } else if (i.component.customId === "manageTypes") {
pineafan6702cef2022-06-13 17:52:37 +0100346 data = await manageTypes(interaction, data, m);
347 } else {
pineafan63fc5e22022-08-04 22:04:10 +0100348 break;
pineafan6702cef2022-06-13 17:52:37 +0100349 }
350 }
351 await interaction.editReply({ embeds: [embed.setFooter({ text: "Message closed" })], components: [] });
pineafan63fc5e22022-08-04 22:04:10 +0100352};
pineafan4f164f32022-02-26 22:07:12 +0000353
pineafan6702cef2022-06-13 17:52:37 +0100354async function manageTypes(interaction, data, m) {
355 while (true) {
356 if (data.useCustom) {
pineafan63fc5e22022-08-04 22:04:10 +0100357 const customTypes = data.customTypes;
pineafanc6158ab2022-06-17 16:34:07 +0100358 await interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +0100359 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +0100360 .setTitle("Tickets > Types")
361 .setDescription(
362 "**Custom types enabled**\n\n" +
363 "**Types in use:**\n" + ((customTypes !== null) ?
364 (customTypes.map((t) => `> ${t}`).join("\n")) :
365 "*None set*"
366 ) + "\n\n" + (customTypes === null ?
367 `${getEmojiByName("TICKETS.REPORT")} Having no types will disable tickets. Please add at least 1 type or use default types` : ""
368 )
369 )
370 .setStatus("Success")
371 .setEmoji("GUILD.TICKET.OPEN")
372 ], components: (customTypes ? [
373 new MessageActionRow().addComponents([new Discord.MessageSelectMenu()
374 .setCustomId("removeTypes")
375 .setPlaceholder("Select types to remove")
376 .setMaxValues(customTypes.length)
377 .setMinValues(1)
378 .addOptions(customTypes.map((t) => new SelectMenuOption().setLabel(t).setValue(t)))
379 ])
380 ] : []).concat([
381 new MessageActionRow().addComponents([
382 new MessageButton()
383 .setLabel("Back")
384 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
385 .setStyle("PRIMARY")
386 .setCustomId("back"),
387 new MessageButton()
388 .setLabel("Add new type")
389 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
390 .setStyle("PRIMARY")
391 .setCustomId("addType")
392 .setDisabled(customTypes !== null && customTypes.length >= 25),
393 new MessageButton()
394 .setLabel("Switch to default types")
395 .setStyle("SECONDARY")
pineafan63fc5e22022-08-04 22:04:10 +0100396 .setCustomId("switchToDefault")
pineafan6702cef2022-06-13 17:52:37 +0100397 ])
398 ])
399 });
400 } else {
pineafan63fc5e22022-08-04 22:04:10 +0100401 const inUse = toHexArray(data.types, ticketTypes);
402 const options = [];
pineafan6702cef2022-06-13 17:52:37 +0100403 ticketTypes.forEach(type => {
404 options.push(new SelectMenuOption({
405 label: capitalize(type),
406 value: type,
PineappleFanb3dd83c2022-06-17 10:53:48 +0100407 emoji: client.emojis.cache.get(getEmojiByName(`TICKETS.${type.toUpperCase()}`, "id")),
pineafan6702cef2022-06-13 17:52:37 +0100408 default: inUse.includes(type)
pineafan63fc5e22022-08-04 22:04:10 +0100409 }));
410 });
411 const selectPane = new MessageActionRow().addComponents([
pineafan6702cef2022-06-13 17:52:37 +0100412 new Discord.MessageSelectMenu()
413 .addOptions(options)
414 .setCustomId("types")
415 .setMaxValues(ticketTypes.length)
416 .setMinValues(1)
417 .setPlaceholder("Select types to use")
pineafan63fc5e22022-08-04 22:04:10 +0100418 ]);
pineafanc6158ab2022-06-17 16:34:07 +0100419 await interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +0100420 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +0100421 .setTitle("Tickets > Types")
422 .setDescription(
423 "**Default types enabled**\n\n" +
424 "**Types in use:**\n" +
425 (inUse.map((t) => `> ${getEmojiByName("TICKETS." + t.toUpperCase())} ${capitalize(t)}`).join("\n"))
426 )
427 .setStatus("Success")
428 .setEmoji("GUILD.TICKET.OPEN")
429 ], components: [
430 selectPane,
431 new MessageActionRow().addComponents([
432 new MessageButton()
433 .setLabel("Back")
434 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
435 .setStyle("PRIMARY")
436 .setCustomId("back"),
437 new MessageButton()
438 .setLabel("Switch to custom types")
439 .setStyle("SECONDARY")
pineafan63fc5e22022-08-04 22:04:10 +0100440 .setCustomId("switchToCustom")
pineafan6702cef2022-06-13 17:52:37 +0100441 ])
442 ]
443 });
444 }
445 let i;
446 try {
pineafanc6158ab2022-06-17 16:34:07 +0100447 i = await m.awaitMessageComponent({ time: 300000 });
pineafan63fc5e22022-08-04 22:04:10 +0100448 } catch (e) { break; }
pineafane23c4ec2022-07-27 21:56:27 +0100449 if (i.component.customId === "types") {
pineafan63fc5e22022-08-04 22:04:10 +0100450 i.deferUpdate();
451 const types = toHexInteger(i.values, ticketTypes);
452 await client.database.guilds.write(interaction.guild.id, { "tickets.types": types });
pineafan6702cef2022-06-13 17:52:37 +0100453 data.types = types;
pineafane23c4ec2022-07-27 21:56:27 +0100454 } else if (i.component.customId === "removeTypes") {
pineafan63fc5e22022-08-04 22:04:10 +0100455 i.deferUpdate();
456 const types = i.values;
pineafan6702cef2022-06-13 17:52:37 +0100457 let customTypes = data.customTypes;
458 if (customTypes) {
459 customTypes = customTypes.filter((t) => !types.includes(t));
460 customTypes = customTypes.length > 0 ? customTypes : null;
pineafan63fc5e22022-08-04 22:04:10 +0100461 await client.database.guilds.write(interaction.guild.id, { "tickets.customTypes": customTypes });
pineafan6702cef2022-06-13 17:52:37 +0100462 data.customTypes = customTypes;
463 }
pineafane23c4ec2022-07-27 21:56:27 +0100464 } else if (i.component.customId === "addType") {
pineafan6702cef2022-06-13 17:52:37 +0100465 await i.showModal(new Discord.Modal().setCustomId("modal").setTitle("Enter a name for the new type").addComponents(
pineafan02ba0232022-07-24 22:16:15 +0100466 new MessageActionRow<TextInputComponent>().addComponents(new TextInputComponent()
pineafan6702cef2022-06-13 17:52:37 +0100467 .setCustomId("type")
468 .setLabel("Name")
469 .setMaxLength(100)
470 .setMinLength(1)
471 .setPlaceholder("E.g. \"Server Idea\"")
472 .setRequired(true)
473 .setStyle("SHORT")
474 )
pineafan63fc5e22022-08-04 22:04:10 +0100475 ));
pineafan6702cef2022-06-13 17:52:37 +0100476 await interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +0100477 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +0100478 .setTitle("Tickets > Types")
479 .setDescription("Modal opened. If you can't see it, click back and try again.")
480 .setStatus("Success")
481 .setEmoji("GUILD.TICKET.OPEN")
482 ], components: [new MessageActionRow().addComponents([new MessageButton()
483 .setLabel("Back")
484 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
485 .setStyle("PRIMARY")
486 .setCustomId("back")
487 ])]
488 });
pineafan4edb7762022-06-26 19:21:04 +0100489 let out;
pineafan6702cef2022-06-13 17:52:37 +0100490 try {
pineafan63fc5e22022-08-04 22:04:10 +0100491 out = await modalInteractionCollector(m, (m) => m.channel.id === interaction.channel.id, (m) => m.customId === "addType");
492 } catch (e) { continue; }
pineafan6702cef2022-06-13 17:52:37 +0100493 if (out.fields) {
494 let toAdd = out.fields.getTextInputValue("type");
pineafan63fc5e22022-08-04 22:04:10 +0100495 if (!toAdd) { continue; }
496 toAdd = toAdd.substring(0, 80);
pineafan6702cef2022-06-13 17:52:37 +0100497 try {
pineafan63fc5e22022-08-04 22:04:10 +0100498 await client.database.guilds.append(interaction.guild.id, "tickets.customTypes", toAdd);
499 } catch { continue; }
pineafan6702cef2022-06-13 17:52:37 +0100500 data.customTypes = data.customTypes || [];
501 if (!data.customTypes.includes(toAdd)) {
502 data.customTypes.push(toAdd);
503 }
pineafan63fc5e22022-08-04 22:04:10 +0100504 } else { continue; }
pineafane23c4ec2022-07-27 21:56:27 +0100505 } else if (i.component.customId === "switchToDefault") {
pineafan63fc5e22022-08-04 22:04:10 +0100506 i.deferUpdate();
507 await client.database.guilds.write(interaction.guild.id, { "tickets.useCustom": false }, []);
pineafan6702cef2022-06-13 17:52:37 +0100508 data.useCustom = false;
pineafane23c4ec2022-07-27 21:56:27 +0100509 } else if (i.component.customId === "switchToCustom") {
pineafan63fc5e22022-08-04 22:04:10 +0100510 i.deferUpdate();
511 await client.database.guilds.write(interaction.guild.id, { "tickets.useCustom": true }, []);
pineafan6702cef2022-06-13 17:52:37 +0100512 data.useCustom = true;
513 } else {
pineafan63fc5e22022-08-04 22:04:10 +0100514 i.deferUpdate();
515 break;
pineafan6702cef2022-06-13 17:52:37 +0100516 }
517 }
pineafan63fc5e22022-08-04 22:04:10 +0100518 return data;
pineafan6702cef2022-06-13 17:52:37 +0100519}
520
521
pineafan4f164f32022-02-26 22:07:12 +0000522const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
pineafan63fc5e22022-08-04 22:04:10 +0100523 const member = (interaction.member as Discord.GuildMember);
524 if (!member.permissions.has("MANAGE_GUILD")) throw "You must have the *Manage Server* permission to use this command";
pineafan6702cef2022-06-13 17:52:37 +0100525 return true;
pineafan63fc5e22022-08-04 22:04:10 +0100526};
pineafan4f164f32022-02-26 22:07:12 +0000527
528export { command };
529export { callback };
530export { check };