blob: 31df394046adcff912b81be2e50ee98ed46bafbb [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 Grey1a67e182022-08-04 23:05:44 +01005import Discord, { CommandInteraction, GuildChannel, Interaction, 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> => {
24 let m = await interaction.reply({embeds: LoadingEmbed, ephemeral: true, fetchReply: true});
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 };
31 console.log(m);
pineafan6702cef2022-06-13 17:52:37 +010032 if (options.enabled !== null || options.category || options.maxtickets || options.supportping) {
33 options.enabled = options.enabled === "yes" ? true : false;
34 if (options.category) {
Skyler Grey1a67e182022-08-04 23:05:44 +010035 let channel: GuildChannel;
pineafan6702cef2022-06-13 17:52:37 +010036 try {
Skyler Grey1a67e182022-08-04 23:05:44 +010037 channel = await interaction.guild.channels.fetch(options.category.id);
pineafan6702cef2022-06-13 17:52:37 +010038 } catch {
39 return await interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +010040 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +010041 .setEmoji("CHANNEL.TEXT.DELETE")
42 .setTitle("Tickets > Category")
43 .setDescription("The channel you provided is not a valid category")
44 .setStatus("Danger")
45 ]
pineafan63fc5e22022-08-04 22:04:10 +010046 });
pineafan6702cef2022-06-13 17:52:37 +010047 }
pineafan63fc5e22022-08-04 22:04:10 +010048 channel = channel as Discord.CategoryChannel;
pineafane23c4ec2022-07-27 21:56:27 +010049 if (channel.guild.id !== interaction.guild.id) return interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +010050 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +010051 .setTitle("Tickets > Category")
pineafan63fc5e22022-08-04 22:04:10 +010052 .setDescription("You must choose a category in this server")
pineafan6702cef2022-06-13 17:52:37 +010053 .setStatus("Danger")
54 .setEmoji("CHANNEL.TEXT.DELETE")
55 ]
56 });
57 }
58 if (options.maxtickets) {
59 if (options.maxtickets < 1) return interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +010060 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +010061 .setTitle("Tickets > Max Tickets")
pineafan63fc5e22022-08-04 22:04:10 +010062 .setDescription("You must choose a number greater than 0")
pineafan6702cef2022-06-13 17:52:37 +010063 .setStatus("Danger")
64 .setEmoji("CHANNEL.TEXT.DELETE")
65 ]
66 });
67 }
Skyler Grey1a67e182022-08-04 23:05:44 +010068 let role: Role;
pineafan6702cef2022-06-13 17:52:37 +010069 if (options.supportping) {
70 try {
Skyler Grey1a67e182022-08-04 23:05:44 +010071 role = await interaction.guild.roles.fetch(options.supportping.id);
pineafan6702cef2022-06-13 17:52:37 +010072 } catch {
73 return await interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +010074 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +010075 .setEmoji("GUILD.ROLE.DELETE")
76 .setTitle("Tickets > Support Ping")
77 .setDescription("The role you provided is not a valid role")
78 .setStatus("Danger")
79 ]
pineafan63fc5e22022-08-04 22:04:10 +010080 });
pineafan6702cef2022-06-13 17:52:37 +010081 }
pineafan63fc5e22022-08-04 22:04:10 +010082 role = role as Discord.Role;
pineafane23c4ec2022-07-27 21:56:27 +010083 if (role.guild.id !== interaction.guild.id) return interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +010084 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +010085 .setTitle("Tickets > Support Ping")
pineafan63fc5e22022-08-04 22:04:10 +010086 .setDescription("You must choose a role in this server")
pineafan6702cef2022-06-13 17:52:37 +010087 .setStatus("Danger")
88 .setEmoji("GUILD.ROLE.DELETE")
89 ]
90 });
91 }
92
pineafan63fc5e22022-08-04 22:04:10 +010093 const confirmation = await new confirmationMessage(interaction)
pineafan6702cef2022-06-13 17:52:37 +010094 .setEmoji("GUILD.TICKET.ARCHIVED")
95 .setTitle("Tickets")
96 .setDescription(
97 (options.category ? `**Category:** ${options.category.name}\n` : "") +
98 (options.maxtickets ? `**Max Tickets:** ${options.maxtickets}\n` : "") +
99 (options.supportping ? `**Support Ping:** ${options.supportping.name}\n` : "") +
100 (options.enabled !== null ? `**Enabled:** ${options.enabled ? `${getEmojiByName("CONTROL.TICK")} Yes` : `${getEmojiByName("CONTROL.CROSS")} No`
pineafan63fc5e22022-08-04 22:04:10 +0100101 }\n` : "") +
102 "\nAre you sure you want to apply these settings?"
pineafan6702cef2022-06-13 17:52:37 +0100103 )
104 .setColor("Warning")
105 .setInverted(true)
pineafan63fc5e22022-08-04 22:04:10 +0100106 .send(true);
107 if (confirmation.cancelled) return;
pineafan6702cef2022-06-13 17:52:37 +0100108 if (confirmation.success) {
pineafan63fc5e22022-08-04 22:04:10 +0100109 const toUpdate = {};
110 if (options.enabled !== null) toUpdate["tickets.enabled"] = options.enabled;
111 if (options.category) toUpdate["tickets.category"] = options.category.id;
112 if (options.maxtickets) toUpdate["tickets.maxTickets"] = options.maxtickets;
113 if (options.supportping) toUpdate["tickets.supportRole"] = options.supportping.id;
pineafan6702cef2022-06-13 17:52:37 +0100114 try {
pineafan63fc5e22022-08-04 22:04:10 +0100115 await client.database.guilds.write(interaction.guild.id, toUpdate);
pineafan6702cef2022-06-13 17:52:37 +0100116 } catch (e) {
117 return interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +0100118 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +0100119 .setTitle("Tickets")
pineafan63fc5e22022-08-04 22:04:10 +0100120 .setDescription("Something went wrong and the staff notifications channel could not be set")
pineafan6702cef2022-06-13 17:52:37 +0100121 .setStatus("Danger")
122 .setEmoji("GUILD.TICKET.DELETE")
123 ], components: []
124 });
125 }
126 } else {
127 return interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +0100128 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +0100129 .setTitle("Tickets")
pineafan63fc5e22022-08-04 22:04:10 +0100130 .setDescription("No changes were made")
pineafan6702cef2022-06-13 17:52:37 +0100131 .setStatus("Success")
132 .setEmoji("GUILD.TICKET.OPEN")
133 ], components: []
134 });
135 }
136 }
pineafan4edb7762022-06-26 19:21:04 +0100137 let data = await client.database.guilds.read(interaction.guild.id);
Skyler Grey1a67e182022-08-04 23:05:44 +0100138 data.tickets.customTypes = (data.tickets.customTypes || []).filter((value: string, index: number, array: string[]) => array.indexOf(value) === index);
pineafan6702cef2022-06-13 17:52:37 +0100139 let lastClicked = "";
Skyler Grey1a67e182022-08-04 23:05:44 +0100140 let embed: EmojiEmbed;
pineafan6702cef2022-06-13 17:52:37 +0100141 data = {
142 enabled: data.tickets.enabled,
143 category: data.tickets.category,
144 maxTickets: data.tickets.maxTickets,
145 supportRole: data.tickets.supportRole,
146 useCustom: data.tickets.useCustom,
147 types: data.tickets.types,
148 customTypes: data.tickets.customTypes
pineafan63fc5e22022-08-04 22:04:10 +0100149 };
pineafan6702cef2022-06-13 17:52:37 +0100150 while (true) {
pineafan4edb7762022-06-26 19:21:04 +0100151 embed = new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +0100152 .setTitle("Tickets")
153 .setDescription(
154 `${data.enabled ? "" : getEmojiByName("TICKETS.REPORT")} **Enabled:** ${data.enabled ? `${getEmojiByName("CONTROL.TICK")} Yes` : `${getEmojiByName("CONTROL.CROSS")} No`}\n` +
155 `${data.category ? "" : getEmojiByName("TICKETS.REPORT")} **Category:** ${data.category ? `<#${data.category}>` : "*None set*"}\n` +
156 `**Max Tickets:** ${data.maxTickets ? data.maxTickets : "*No limit*"}\n` +
157 `**Support Ping:** ${data.supportRole ? `<@&${data.supportRole}>` : "*None set*"}\n\n` +
158 ((data.useCustom && data.customTypes === null) ? `${getEmojiByName("TICKETS.REPORT")} ` : "") +
159 `${data.useCustom ? "Custom" : "Default"} types in use` + "\n\n" +
160 `${getEmojiByName("TICKETS.REPORT")} *Indicates a setting stopping tickets from being used*`
161 )
162 .setStatus("Success")
pineafan63fc5e22022-08-04 22:04:10 +0100163 .setEmoji("GUILD.TICKET.OPEN");
pineafan6702cef2022-06-13 17:52:37 +0100164 m = await interaction.editReply({
165 embeds: [embed], components: [new MessageActionRow().addComponents([
166 new MessageButton()
167 .setLabel("Tickets " + (data.enabled ? "enabled" : "disabled"))
168 .setEmoji(getEmojiByName("CONTROL." + (data.enabled ? "TICK" : "CROSS"), "id"))
169 .setStyle(data.enabled ? "SUCCESS" : "DANGER")
170 .setCustomId("enabled"),
171 new MessageButton()
pineafane23c4ec2022-07-27 21:56:27 +0100172 .setLabel(lastClicked === "cat" ? "Click again to confirm" : "Clear category")
pineafan6702cef2022-06-13 17:52:37 +0100173 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
174 .setStyle("DANGER")
175 .setCustomId("clearCategory")
pineafane23c4ec2022-07-27 21:56:27 +0100176 .setDisabled(data.category === null),
pineafan6702cef2022-06-13 17:52:37 +0100177 new MessageButton()
pineafane23c4ec2022-07-27 21:56:27 +0100178 .setLabel(lastClicked === "max" ? "Click again to confirm" : "Reset max tickets")
pineafan6702cef2022-06-13 17:52:37 +0100179 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
180 .setStyle("DANGER")
181 .setCustomId("clearMaxTickets")
pineafane23c4ec2022-07-27 21:56:27 +0100182 .setDisabled(data.maxTickets === 5),
pineafan6702cef2022-06-13 17:52:37 +0100183 new MessageButton()
pineafane23c4ec2022-07-27 21:56:27 +0100184 .setLabel(lastClicked === "sup" ? "Click again to confirm" : "Clear support ping")
pineafan6702cef2022-06-13 17:52:37 +0100185 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
186 .setStyle("DANGER")
187 .setCustomId("clearSupportPing")
pineafan63fc5e22022-08-04 22:04:10 +0100188 .setDisabled(data.supportRole === null)
pineafan6702cef2022-06-13 17:52:37 +0100189 ]), new MessageActionRow().addComponents([
190 new MessageButton()
191 .setLabel("Manage types")
192 .setEmoji(getEmojiByName("TICKETS.OTHER", "id"))
193 .setStyle("SECONDARY")
194 .setCustomId("manageTypes"),
pineafan41d93562022-07-30 22:10:15 +0100195 new MessageButton()
196 .setLabel("Add create ticket button")
197 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
198 .setStyle("PRIMARY")
pineafan63fc5e22022-08-04 22:04:10 +0100199 .setCustomId("send")
pineafan6702cef2022-06-13 17:52:37 +0100200 ])]
201 });
Skyler Grey1a67e182022-08-04 23:05:44 +0100202 let i: MessageComponentInteraction;
pineafan6702cef2022-06-13 17:52:37 +0100203 try {
Skyler Grey1a67e182022-08-04 23:05:44 +0100204 i = await (m as Message).awaitMessageComponent({ time: 300000 });
pineafan63fc5e22022-08-04 22:04:10 +0100205 } catch (e) { break; }
206 i.deferUpdate();
Skyler Grey1a67e182022-08-04 23:05:44 +0100207 if ((i.component as MessageActionRowComponent).customId === "clearCategory") {
pineafane23c4ec2022-07-27 21:56:27 +0100208 if (lastClicked === "cat") {
pineafan6702cef2022-06-13 17:52:37 +0100209 lastClicked = "";
pineafan63fc5e22022-08-04 22:04:10 +0100210 await client.database.guilds.write(interaction.guild.id, null, ["tickets.category"]);
pineafan6702cef2022-06-13 17:52:37 +0100211 data.category = undefined;
212 } else lastClicked = "cat";
Skyler Grey1a67e182022-08-04 23:05:44 +0100213 } else if ((i.component as MessageActionRowComponent).customId === "clearMaxTickets") {
pineafane23c4ec2022-07-27 21:56:27 +0100214 if (lastClicked === "max") {
pineafan6702cef2022-06-13 17:52:37 +0100215 lastClicked = "";
pineafan63fc5e22022-08-04 22:04:10 +0100216 await client.database.guilds.write(interaction.guild.id, null, ["tickets.maxTickets"]);
pineafan6702cef2022-06-13 17:52:37 +0100217 data.maxTickets = 5;
218 } else lastClicked = "max";
Skyler Grey1a67e182022-08-04 23:05:44 +0100219 } else if ((i.component as MessageActionRowComponent).customId === "clearSupportPing") {
pineafane23c4ec2022-07-27 21:56:27 +0100220 if (lastClicked === "sup") {
pineafan6702cef2022-06-13 17:52:37 +0100221 lastClicked = "";
pineafan63fc5e22022-08-04 22:04:10 +0100222 await client.database.guilds.write(interaction.guild.id, null, ["tickets.supportRole"]);
pineafan6702cef2022-06-13 17:52:37 +0100223 data.supportRole = undefined;
224 } else lastClicked = "sup";
Skyler Grey1a67e182022-08-04 23:05:44 +0100225 } else if ((i.component as MessageActionRowComponent).customId === "send") {
pineafan41d93562022-07-30 22:10:15 +0100226 const ticketMessages = [
227 {label: "Create ticket", description: "Click the button below to create a ticket"},
228 {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 +0100229 {label: "Contact Us", description: "Click the button below to speak to us privately"}
230 ];
pineafan41d93562022-07-30 22:10:15 +0100231 while (true) {
pineafan63fc5e22022-08-04 22:04:10 +0100232 const enabled = data.enabled && data.category !== null;
pineafan41d93562022-07-30 22:10:15 +0100233 await interaction.editReply({embeds: [new EmojiEmbed()
234 .setTitle("Ticket Button")
235 .setDescription("Select a message template to send in this channel")
236 .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."})
237 .setStatus(enabled ? "Success" : "Warning")
238 .setEmoji("GUILD.ROLES.CREATE")
239 ], components: [
240 new MessageActionRow().addComponents([
241 new MessageSelectMenu().setOptions(ticketMessages.map((t: {label: string, description: string, value?: string}, index) => {
pineafan63fc5e22022-08-04 22:04:10 +0100242 t.value = index.toString(); return t as {value: string, label: string, description: string};
243 })).setCustomId("template").setMaxValues(1).setMinValues(1).setPlaceholder("Select a message template")
pineafan41d93562022-07-30 22:10:15 +0100244 ]),
245 new MessageActionRow().addComponents([
246 new MessageButton()
247 .setCustomId("back")
248 .setLabel("Back")
249 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
250 .setStyle("DANGER"),
251 new MessageButton()
252 .setCustomId("blank")
253 .setLabel("Empty")
254 .setStyle("SECONDARY"),
255 new MessageButton()
256 .setCustomId("custom")
257 .setLabel("Custom")
258 .setEmoji(getEmojiByName("TICKETS.OTHER", "id"))
259 .setStyle("PRIMARY")
260 ])
261 ]});
Skyler Grey1a67e182022-08-04 23:05:44 +0100262 let i: MessageComponentInteraction;
pineafan41d93562022-07-30 22:10:15 +0100263 try {
Skyler Grey1a67e182022-08-04 23:05:44 +0100264 i = await (m as Message).awaitMessageComponent({time: 300000});
pineafan63fc5e22022-08-04 22:04:10 +0100265 } catch(e) { break; }
Skyler Grey1a67e182022-08-04 23:05:44 +0100266 if ((i.component as MessageActionRowComponent).customId === "template") {
pineafan63fc5e22022-08-04 22:04:10 +0100267 i.deferUpdate();
pineafan41d93562022-07-30 22:10:15 +0100268 await interaction.channel.send({embeds: [new EmojiEmbed()
Skyler Grey1a67e182022-08-04 23:05:44 +0100269 .setTitle(ticketMessages[parseInt((i as SelectMenuInteraction).values[0])].label)
270 .setDescription(ticketMessages[parseInt((i as SelectMenuInteraction).values[0])].description)
pineafan41d93562022-07-30 22:10:15 +0100271 .setStatus("Success")
272 .setEmoji("GUILD.TICKET.OPEN")
273 ], components: [new MessageActionRow().addComponents([new MessageButton()
274 .setLabel("Create Ticket")
275 .setEmoji(getEmojiByName("CONTROL.TICK", "id"))
276 .setStyle("SUCCESS")
277 .setCustomId("createticket")
278 ])]});
pineafan63fc5e22022-08-04 22:04:10 +0100279 break;
Skyler Grey1a67e182022-08-04 23:05:44 +0100280 } else if ((i.component as MessageActionRowComponent).customId === "blank") {
pineafan63fc5e22022-08-04 22:04:10 +0100281 i.deferUpdate();
pineafan41d93562022-07-30 22:10:15 +0100282 await interaction.channel.send({components: [new MessageActionRow().addComponents([new MessageButton()
283 .setLabel("Create Ticket")
284 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
285 .setStyle("SUCCESS")
286 .setCustomId("createticket")
287 ])]});
pineafan63fc5e22022-08-04 22:04:10 +0100288 break;
Skyler Grey1a67e182022-08-04 23:05:44 +0100289 } else if ((i.component as MessageActionRowComponent).customId === "custom") {
pineafan63fc5e22022-08-04 22:04:10 +0100290 await i.showModal(new Discord.Modal().setCustomId("modal").setTitle("Enter embed details").addComponents(
pineafan41d93562022-07-30 22:10:15 +0100291 new MessageActionRow<TextInputComponent>().addComponents(new TextInputComponent()
292 .setCustomId("title")
293 .setLabel("Title")
294 .setMaxLength(256)
295 .setRequired(true)
296 .setStyle("SHORT")
297 ),
298 new MessageActionRow<TextInputComponent>().addComponents(new TextInputComponent()
299 .setCustomId("description")
300 .setLabel("Description")
301 .setMaxLength(4000)
302 .setRequired(true)
303 .setStyle("PARAGRAPH")
304 )
pineafan63fc5e22022-08-04 22:04:10 +0100305 ));
pineafan41d93562022-07-30 22:10:15 +0100306 await interaction.editReply({
307 embeds: [new EmojiEmbed()
308 .setTitle("Ticket Button")
309 .setDescription("Modal opened. If you can't see it, click back and try again.")
310 .setStatus("Success")
311 .setEmoji("GUILD.TICKET.OPEN")
312 ], components: [new MessageActionRow().addComponents([new MessageButton()
313 .setLabel("Back")
314 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
315 .setStyle("PRIMARY")
316 .setCustomId("back")
317 ])]
318 });
319 let out;
320 try {
pineafan63fc5e22022-08-04 22:04:10 +0100321 out = await modalInteractionCollector(m, (m) => m.channel.id === interaction.channel.id, (m) => m.customId === "modify");
322 } catch (e) { break; }
pineafan41d93562022-07-30 22:10:15 +0100323 if (out.fields) {
pineafan63fc5e22022-08-04 22:04:10 +0100324 const title = out.fields.getTextInputValue("title");
325 const description = out.fields.getTextInputValue("description");
pineafan41d93562022-07-30 22:10:15 +0100326 await interaction.channel.send({embeds: [new EmojiEmbed()
327 .setTitle(title)
328 .setDescription(description)
329 .setStatus("Success")
330 .setEmoji("GUILD.TICKET.OPEN")
331 ], components: [new MessageActionRow().addComponents([new MessageButton()
332 .setLabel("Create Ticket")
333 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
334 .setStyle("SUCCESS")
335 .setCustomId("createticket")
336 ])]});
pineafan63fc5e22022-08-04 22:04:10 +0100337 break;
338 } else { continue; }
pineafan41d93562022-07-30 22:10:15 +0100339 }
340 }
Skyler Grey1a67e182022-08-04 23:05:44 +0100341 } else if ((i.component as MessageActionRowComponent).customId === "enabled") {
pineafan63fc5e22022-08-04 22:04:10 +0100342 await client.database.guilds.write(interaction.guild.id, { "tickets.enabled": !data.enabled });
pineafan6702cef2022-06-13 17:52:37 +0100343 data.enabled = !data.enabled;
Skyler Grey1a67e182022-08-04 23:05:44 +0100344 } else if ((i.component as MessageActionRowComponent).customId === "manageTypes") {
345 data = await manageTypes(interaction, data, m as Message);
pineafan6702cef2022-06-13 17:52:37 +0100346 } else {
pineafan63fc5e22022-08-04 22:04:10 +0100347 break;
pineafan6702cef2022-06-13 17:52:37 +0100348 }
349 }
350 await interaction.editReply({ embeds: [embed.setFooter({ text: "Message closed" })], components: [] });
pineafan63fc5e22022-08-04 22:04:10 +0100351};
pineafan4f164f32022-02-26 22:07:12 +0000352
Skyler Grey1a67e182022-08-04 23:05:44 +0100353async function manageTypes(interaction: Interaction, data: GuildConfig["tickets"], m: Message) {
pineafan6702cef2022-06-13 17:52:37 +0100354 while (true) {
355 if (data.useCustom) {
pineafan63fc5e22022-08-04 22:04:10 +0100356 const customTypes = data.customTypes;
pineafanc6158ab2022-06-17 16:34:07 +0100357 await interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +0100358 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +0100359 .setTitle("Tickets > Types")
360 .setDescription(
361 "**Custom types enabled**\n\n" +
362 "**Types in use:**\n" + ((customTypes !== null) ?
363 (customTypes.map((t) => `> ${t}`).join("\n")) :
364 "*None set*"
365 ) + "\n\n" + (customTypes === null ?
366 `${getEmojiByName("TICKETS.REPORT")} Having no types will disable tickets. Please add at least 1 type or use default types` : ""
367 )
368 )
369 .setStatus("Success")
370 .setEmoji("GUILD.TICKET.OPEN")
371 ], components: (customTypes ? [
372 new MessageActionRow().addComponents([new Discord.MessageSelectMenu()
373 .setCustomId("removeTypes")
374 .setPlaceholder("Select types to remove")
375 .setMaxValues(customTypes.length)
376 .setMinValues(1)
377 .addOptions(customTypes.map((t) => new SelectMenuOption().setLabel(t).setValue(t)))
378 ])
379 ] : []).concat([
380 new MessageActionRow().addComponents([
381 new MessageButton()
382 .setLabel("Back")
383 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
384 .setStyle("PRIMARY")
385 .setCustomId("back"),
386 new MessageButton()
387 .setLabel("Add new type")
388 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
389 .setStyle("PRIMARY")
390 .setCustomId("addType")
391 .setDisabled(customTypes !== null && customTypes.length >= 25),
392 new MessageButton()
393 .setLabel("Switch to default types")
394 .setStyle("SECONDARY")
pineafan63fc5e22022-08-04 22:04:10 +0100395 .setCustomId("switchToDefault")
pineafan6702cef2022-06-13 17:52:37 +0100396 ])
397 ])
398 });
399 } else {
pineafan63fc5e22022-08-04 22:04:10 +0100400 const inUse = toHexArray(data.types, ticketTypes);
401 const options = [];
pineafan6702cef2022-06-13 17:52:37 +0100402 ticketTypes.forEach(type => {
403 options.push(new SelectMenuOption({
404 label: capitalize(type),
405 value: type,
PineappleFanb3dd83c2022-06-17 10:53:48 +0100406 emoji: client.emojis.cache.get(getEmojiByName(`TICKETS.${type.toUpperCase()}`, "id")),
pineafan6702cef2022-06-13 17:52:37 +0100407 default: inUse.includes(type)
pineafan63fc5e22022-08-04 22:04:10 +0100408 }));
409 });
410 const selectPane = new MessageActionRow().addComponents([
pineafan6702cef2022-06-13 17:52:37 +0100411 new Discord.MessageSelectMenu()
412 .addOptions(options)
413 .setCustomId("types")
414 .setMaxValues(ticketTypes.length)
415 .setMinValues(1)
416 .setPlaceholder("Select types to use")
pineafan63fc5e22022-08-04 22:04:10 +0100417 ]);
pineafanc6158ab2022-06-17 16:34:07 +0100418 await interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +0100419 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +0100420 .setTitle("Tickets > Types")
421 .setDescription(
422 "**Default types enabled**\n\n" +
423 "**Types in use:**\n" +
424 (inUse.map((t) => `> ${getEmojiByName("TICKETS." + t.toUpperCase())} ${capitalize(t)}`).join("\n"))
425 )
426 .setStatus("Success")
427 .setEmoji("GUILD.TICKET.OPEN")
428 ], components: [
429 selectPane,
430 new MessageActionRow().addComponents([
431 new MessageButton()
432 .setLabel("Back")
433 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
434 .setStyle("PRIMARY")
435 .setCustomId("back"),
436 new MessageButton()
437 .setLabel("Switch to custom types")
438 .setStyle("SECONDARY")
pineafan63fc5e22022-08-04 22:04:10 +0100439 .setCustomId("switchToCustom")
pineafan6702cef2022-06-13 17:52:37 +0100440 ])
441 ]
442 });
443 }
444 let i;
445 try {
pineafanc6158ab2022-06-17 16:34:07 +0100446 i = await m.awaitMessageComponent({ time: 300000 });
pineafan63fc5e22022-08-04 22:04:10 +0100447 } catch (e) { break; }
pineafane23c4ec2022-07-27 21:56:27 +0100448 if (i.component.customId === "types") {
pineafan63fc5e22022-08-04 22:04:10 +0100449 i.deferUpdate();
450 const types = toHexInteger(i.values, ticketTypes);
451 await client.database.guilds.write(interaction.guild.id, { "tickets.types": types });
pineafan6702cef2022-06-13 17:52:37 +0100452 data.types = types;
pineafane23c4ec2022-07-27 21:56:27 +0100453 } else if (i.component.customId === "removeTypes") {
pineafan63fc5e22022-08-04 22:04:10 +0100454 i.deferUpdate();
455 const types = i.values;
pineafan6702cef2022-06-13 17:52:37 +0100456 let customTypes = data.customTypes;
457 if (customTypes) {
458 customTypes = customTypes.filter((t) => !types.includes(t));
459 customTypes = customTypes.length > 0 ? customTypes : null;
pineafan63fc5e22022-08-04 22:04:10 +0100460 await client.database.guilds.write(interaction.guild.id, { "tickets.customTypes": customTypes });
pineafan6702cef2022-06-13 17:52:37 +0100461 data.customTypes = customTypes;
462 }
pineafane23c4ec2022-07-27 21:56:27 +0100463 } else if (i.component.customId === "addType") {
pineafan6702cef2022-06-13 17:52:37 +0100464 await i.showModal(new Discord.Modal().setCustomId("modal").setTitle("Enter a name for the new type").addComponents(
pineafan02ba0232022-07-24 22:16:15 +0100465 new MessageActionRow<TextInputComponent>().addComponents(new TextInputComponent()
pineafan6702cef2022-06-13 17:52:37 +0100466 .setCustomId("type")
467 .setLabel("Name")
468 .setMaxLength(100)
469 .setMinLength(1)
470 .setPlaceholder("E.g. \"Server Idea\"")
471 .setRequired(true)
472 .setStyle("SHORT")
473 )
pineafan63fc5e22022-08-04 22:04:10 +0100474 ));
pineafan6702cef2022-06-13 17:52:37 +0100475 await interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +0100476 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +0100477 .setTitle("Tickets > Types")
478 .setDescription("Modal opened. If you can't see it, click back and try again.")
479 .setStatus("Success")
480 .setEmoji("GUILD.TICKET.OPEN")
481 ], components: [new MessageActionRow().addComponents([new MessageButton()
482 .setLabel("Back")
483 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
484 .setStyle("PRIMARY")
485 .setCustomId("back")
486 ])]
487 });
pineafan4edb7762022-06-26 19:21:04 +0100488 let out;
pineafan6702cef2022-06-13 17:52:37 +0100489 try {
pineafan63fc5e22022-08-04 22:04:10 +0100490 out = await modalInteractionCollector(m, (m) => m.channel.id === interaction.channel.id, (m) => m.customId === "addType");
491 } catch (e) { continue; }
pineafan6702cef2022-06-13 17:52:37 +0100492 if (out.fields) {
493 let toAdd = out.fields.getTextInputValue("type");
pineafan63fc5e22022-08-04 22:04:10 +0100494 if (!toAdd) { continue; }
495 toAdd = toAdd.substring(0, 80);
pineafan6702cef2022-06-13 17:52:37 +0100496 try {
pineafan63fc5e22022-08-04 22:04:10 +0100497 await client.database.guilds.append(interaction.guild.id, "tickets.customTypes", toAdd);
498 } catch { continue; }
pineafan6702cef2022-06-13 17:52:37 +0100499 data.customTypes = data.customTypes || [];
500 if (!data.customTypes.includes(toAdd)) {
501 data.customTypes.push(toAdd);
502 }
pineafan63fc5e22022-08-04 22:04:10 +0100503 } else { continue; }
pineafane23c4ec2022-07-27 21:56:27 +0100504 } else if (i.component.customId === "switchToDefault") {
pineafan63fc5e22022-08-04 22:04:10 +0100505 i.deferUpdate();
506 await client.database.guilds.write(interaction.guild.id, { "tickets.useCustom": false }, []);
pineafan6702cef2022-06-13 17:52:37 +0100507 data.useCustom = false;
pineafane23c4ec2022-07-27 21:56:27 +0100508 } else if (i.component.customId === "switchToCustom") {
pineafan63fc5e22022-08-04 22:04:10 +0100509 i.deferUpdate();
510 await client.database.guilds.write(interaction.guild.id, { "tickets.useCustom": true }, []);
pineafan6702cef2022-06-13 17:52:37 +0100511 data.useCustom = true;
512 } else {
pineafan63fc5e22022-08-04 22:04:10 +0100513 i.deferUpdate();
514 break;
pineafan6702cef2022-06-13 17:52:37 +0100515 }
516 }
pineafan63fc5e22022-08-04 22:04:10 +0100517 return data;
pineafan6702cef2022-06-13 17:52:37 +0100518}
519
520
Skyler Grey1a67e182022-08-04 23:05:44 +0100521const check = (interaction: CommandInteraction) => {
pineafan63fc5e22022-08-04 22:04:10 +0100522 const member = (interaction.member as Discord.GuildMember);
523 if (!member.permissions.has("MANAGE_GUILD")) throw "You must have the *Manage Server* permission to use this command";
pineafan6702cef2022-06-13 17:52:37 +0100524 return true;
pineafan63fc5e22022-08-04 22:04:10 +0100525};
pineafan4f164f32022-02-26 22:07:12 +0000526
527export { command };
528export { callback };
Skyler Grey1a67e182022-08-04 23:05:44 +0100529export { check };