blob: f199ac3b1dbf2f808341466cbab5e837e38afb2f [file] [log] [blame]
pineafane23c4ec2022-07-27 21:56:27 +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";
pineafan1dc15722022-03-14 21:27:34 +00008import { 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";
11import { capitalize } from '../../utils/generateKeyValueList.js';
12import { 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))
pineafan6702cef2022-06-13 17:52:37 +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;
pineafane23c4ec2022-07-27 21:56:27 +010025 m = await interaction.reply({embeds: LoadingEmbed, ephemeral: true, fetchReply: true});
pineafan6702cef2022-06-13 17:52:37 +010026 let options = {
27 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")
31 }
32 if (options.enabled !== null || options.category || options.maxtickets || options.supportping) {
33 options.enabled = options.enabled === "yes" ? true : false;
34 if (options.category) {
35 let channel
36 try {
37 channel = interaction.guild.channels.cache.get(options.category.id)
38 } 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 ]
46 })
47 }
48 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")
52 .setDescription(`You must choose a category in this server`)
53 .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")
62 .setDescription(`You must choose a number greater than 0`)
63 .setStatus("Danger")
64 .setEmoji("CHANNEL.TEXT.DELETE")
65 ]
66 });
67 }
68 let role
69 if (options.supportping) {
70 try {
71 role = interaction.guild.roles.cache.get(options.supportping.id)
72 } 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 ]
80 })
81 }
82 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")
86 .setDescription(`You must choose a role in this server`)
87 .setStatus("Danger")
88 .setEmoji("GUILD.ROLE.DELETE")
89 ]
90 });
91 }
92
93 let confirmation = await new confirmationMessage(interaction)
94 .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`
101 }\n` : "") +
102 `\nAre you sure you want to apply these settings?`
103 )
104 .setColor("Warning")
105 .setInverted(true)
106 .send(true)
pineafan02ba0232022-07-24 22:16:15 +0100107 if (confirmation.cancelled) return
pineafan6702cef2022-06-13 17:52:37 +0100108 if (confirmation.success) {
109 let 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
114 try {
pineafan4edb7762022-06-26 19:21:04 +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")
120 .setDescription(`Something went wrong and the staff notifications channel could not be set`)
121 .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")
130 .setDescription(`No changes were made`)
131 .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);
pineafan73a7c4a2022-07-24 10:38:04 +0100138 data.tickets.customTypes = (data.tickets.customTypes || []).filter((v, i, a) => a.indexOf(v) === i)
pineafan6702cef2022-06-13 17:52:37 +0100139 let lastClicked = "";
140 let embed;
141 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
149 }
150 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")
163 .setEmoji("GUILD.TICKET.OPEN")
164 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")
pineafane23c4ec2022-07-27 21:56:27 +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"),
195 ])]
196 });
197 let i;
198 try {
pineafanc6158ab2022-06-17 16:34:07 +0100199 i = await m.awaitMessageComponent({ time: 300000 });
pineafan6702cef2022-06-13 17:52:37 +0100200 } catch (e) { break }
201 i.deferUpdate()
pineafane23c4ec2022-07-27 21:56:27 +0100202 if (i.component.customId === "clearCategory") {
203 if (lastClicked === "cat") {
pineafan6702cef2022-06-13 17:52:37 +0100204 lastClicked = "";
pineafane23c4ec2022-07-27 21:56:27 +0100205 await client.database.guilds.write(interaction.guild.id, null, ["tickets.category"])
pineafan6702cef2022-06-13 17:52:37 +0100206 data.category = undefined;
207 } else lastClicked = "cat";
pineafane23c4ec2022-07-27 21:56:27 +0100208 } else if (i.component.customId === "clearMaxTickets") {
209 if (lastClicked === "max") {
pineafan6702cef2022-06-13 17:52:37 +0100210 lastClicked = "";
pineafane23c4ec2022-07-27 21:56:27 +0100211 await client.database.guilds.write(interaction.guild.id, null, ["tickets.maxTickets"])
pineafan6702cef2022-06-13 17:52:37 +0100212 data.maxTickets = 5;
213 } else lastClicked = "max";
pineafane23c4ec2022-07-27 21:56:27 +0100214 } else if (i.component.customId === "clearSupportPing") {
215 if (lastClicked === "sup") {
pineafan6702cef2022-06-13 17:52:37 +0100216 lastClicked = "";
pineafane23c4ec2022-07-27 21:56:27 +0100217 await client.database.guilds.write(interaction.guild.id, null, ["tickets.supportRole"])
pineafan6702cef2022-06-13 17:52:37 +0100218 data.supportRole = undefined;
219 } else lastClicked = "sup";
pineafane23c4ec2022-07-27 21:56:27 +0100220 } else if (i.component.customId === "enabled") {
pineafan4edb7762022-06-26 19:21:04 +0100221 await client.database.guilds.write(interaction.guild.id, { "tickets.enabled": !data.enabled })
pineafan6702cef2022-06-13 17:52:37 +0100222 data.enabled = !data.enabled;
pineafane23c4ec2022-07-27 21:56:27 +0100223 } else if (i.component.customId === "manageTypes") {
pineafan6702cef2022-06-13 17:52:37 +0100224 data = await manageTypes(interaction, data, m);
225 } else {
226 break
227 }
228 }
229 await interaction.editReply({ embeds: [embed.setFooter({ text: "Message closed" })], components: [] });
pineafan4f164f32022-02-26 22:07:12 +0000230}
231
pineafan6702cef2022-06-13 17:52:37 +0100232async function manageTypes(interaction, data, m) {
233 while (true) {
234 if (data.useCustom) {
235 let customTypes = data.customTypes;
pineafanc6158ab2022-06-17 16:34:07 +0100236 await interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +0100237 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +0100238 .setTitle("Tickets > Types")
239 .setDescription(
240 "**Custom types enabled**\n\n" +
241 "**Types in use:**\n" + ((customTypes !== null) ?
242 (customTypes.map((t) => `> ${t}`).join("\n")) :
243 "*None set*"
244 ) + "\n\n" + (customTypes === null ?
245 `${getEmojiByName("TICKETS.REPORT")} Having no types will disable tickets. Please add at least 1 type or use default types` : ""
246 )
247 )
248 .setStatus("Success")
249 .setEmoji("GUILD.TICKET.OPEN")
250 ], components: (customTypes ? [
251 new MessageActionRow().addComponents([new Discord.MessageSelectMenu()
252 .setCustomId("removeTypes")
253 .setPlaceholder("Select types to remove")
254 .setMaxValues(customTypes.length)
255 .setMinValues(1)
256 .addOptions(customTypes.map((t) => new SelectMenuOption().setLabel(t).setValue(t)))
257 ])
258 ] : []).concat([
259 new MessageActionRow().addComponents([
260 new MessageButton()
261 .setLabel("Back")
262 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
263 .setStyle("PRIMARY")
264 .setCustomId("back"),
265 new MessageButton()
266 .setLabel("Add new type")
267 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
268 .setStyle("PRIMARY")
269 .setCustomId("addType")
270 .setDisabled(customTypes !== null && customTypes.length >= 25),
271 new MessageButton()
272 .setLabel("Switch to default types")
273 .setStyle("SECONDARY")
274 .setCustomId("switchToDefault"),
275 ])
276 ])
277 });
278 } else {
279 let inUse = toHexArray(data.types, ticketTypes)
280 let options = [];
281 ticketTypes.forEach(type => {
282 options.push(new SelectMenuOption({
283 label: capitalize(type),
284 value: type,
PineappleFanb3dd83c2022-06-17 10:53:48 +0100285 emoji: client.emojis.cache.get(getEmojiByName(`TICKETS.${type.toUpperCase()}`, "id")),
pineafan6702cef2022-06-13 17:52:37 +0100286 default: inUse.includes(type)
287 }))
288 })
289 let selectPane = new MessageActionRow().addComponents([
290 new Discord.MessageSelectMenu()
291 .addOptions(options)
292 .setCustomId("types")
293 .setMaxValues(ticketTypes.length)
294 .setMinValues(1)
295 .setPlaceholder("Select types to use")
296 ])
pineafanc6158ab2022-06-17 16:34:07 +0100297 await interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +0100298 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +0100299 .setTitle("Tickets > Types")
300 .setDescription(
301 "**Default types enabled**\n\n" +
302 "**Types in use:**\n" +
303 (inUse.map((t) => `> ${getEmojiByName("TICKETS." + t.toUpperCase())} ${capitalize(t)}`).join("\n"))
304 )
305 .setStatus("Success")
306 .setEmoji("GUILD.TICKET.OPEN")
307 ], components: [
308 selectPane,
309 new MessageActionRow().addComponents([
310 new MessageButton()
311 .setLabel("Back")
312 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
313 .setStyle("PRIMARY")
314 .setCustomId("back"),
315 new MessageButton()
316 .setLabel("Switch to custom types")
317 .setStyle("SECONDARY")
318 .setCustomId("switchToCustom"),
319 ])
320 ]
321 });
322 }
323 let i;
324 try {
pineafanc6158ab2022-06-17 16:34:07 +0100325 i = await m.awaitMessageComponent({ time: 300000 });
pineafan6702cef2022-06-13 17:52:37 +0100326 } catch (e) { break }
pineafane23c4ec2022-07-27 21:56:27 +0100327 if (i.component.customId === "types") {
pineafan6702cef2022-06-13 17:52:37 +0100328 i.deferUpdate()
329 let types = toHexInteger(i.values, ticketTypes);
pineafan4edb7762022-06-26 19:21:04 +0100330 await client.database.guilds.write(interaction.guild.id, { "tickets.types": types })
pineafan6702cef2022-06-13 17:52:37 +0100331 data.types = types;
pineafane23c4ec2022-07-27 21:56:27 +0100332 } else if (i.component.customId === "removeTypes") {
pineafan6702cef2022-06-13 17:52:37 +0100333 i.deferUpdate()
334 let types = i.values
335 let customTypes = data.customTypes;
336 if (customTypes) {
337 customTypes = customTypes.filter((t) => !types.includes(t));
338 customTypes = customTypes.length > 0 ? customTypes : null;
pineafan4edb7762022-06-26 19:21:04 +0100339 await client.database.guilds.write(interaction.guild.id, { "tickets.customTypes": customTypes })
pineafan6702cef2022-06-13 17:52:37 +0100340 data.customTypes = customTypes;
341 }
pineafane23c4ec2022-07-27 21:56:27 +0100342 } else if (i.component.customId === "addType") {
pineafan6702cef2022-06-13 17:52:37 +0100343 await i.showModal(new Discord.Modal().setCustomId("modal").setTitle("Enter a name for the new type").addComponents(
pineafan02ba0232022-07-24 22:16:15 +0100344 new MessageActionRow<TextInputComponent>().addComponents(new TextInputComponent()
pineafan6702cef2022-06-13 17:52:37 +0100345 .setCustomId("type")
346 .setLabel("Name")
347 .setMaxLength(100)
348 .setMinLength(1)
349 .setPlaceholder("E.g. \"Server Idea\"")
350 .setRequired(true)
351 .setStyle("SHORT")
352 )
353 ))
354 await interaction.editReply({
pineafan4edb7762022-06-26 19:21:04 +0100355 embeds: [new EmojiEmbed()
pineafan6702cef2022-06-13 17:52:37 +0100356 .setTitle("Tickets > Types")
357 .setDescription("Modal opened. If you can't see it, click back and try again.")
358 .setStatus("Success")
359 .setEmoji("GUILD.TICKET.OPEN")
360 ], components: [new MessageActionRow().addComponents([new MessageButton()
361 .setLabel("Back")
362 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
363 .setStyle("PRIMARY")
364 .setCustomId("back")
365 ])]
366 });
pineafan4edb7762022-06-26 19:21:04 +0100367 let out;
pineafan6702cef2022-06-13 17:52:37 +0100368 try {
pineafane23c4ec2022-07-27 21:56:27 +0100369 out = await modalInteractionCollector(m, (m) => m.channel.id === interaction.channel.id, (m) => m.customId === "addType")
pineafan6702cef2022-06-13 17:52:37 +0100370 } catch (e) { continue }
371 if (out.fields) {
372 let toAdd = out.fields.getTextInputValue("type");
373 if (!toAdd) { continue }
pineafan4edb7762022-06-26 19:21:04 +0100374 toAdd = toAdd.substring(0, 80)
pineafan6702cef2022-06-13 17:52:37 +0100375 try {
pineafan4edb7762022-06-26 19:21:04 +0100376 await client.database.guilds.append(interaction.guild.id, "tickets.customTypes", toAdd)
pineafan6702cef2022-06-13 17:52:37 +0100377 } catch { continue }
378 data.customTypes = data.customTypes || [];
379 if (!data.customTypes.includes(toAdd)) {
380 data.customTypes.push(toAdd);
381 }
382 } else { continue }
pineafane23c4ec2022-07-27 21:56:27 +0100383 } else if (i.component.customId === "switchToDefault") {
pineafan6702cef2022-06-13 17:52:37 +0100384 i.deferUpdate()
pineafan4edb7762022-06-26 19:21:04 +0100385 await client.database.guilds.write(interaction.guild.id, { "tickets.useCustom": false }, [])
pineafan6702cef2022-06-13 17:52:37 +0100386 data.useCustom = false;
pineafane23c4ec2022-07-27 21:56:27 +0100387 } else if (i.component.customId === "switchToCustom") {
pineafan6702cef2022-06-13 17:52:37 +0100388 i.deferUpdate()
pineafan4edb7762022-06-26 19:21:04 +0100389 await client.database.guilds.write(interaction.guild.id, { "tickets.useCustom": true }, [])
pineafan6702cef2022-06-13 17:52:37 +0100390 data.useCustom = true;
391 } else {
392 i.deferUpdate()
393 break
394 }
395 }
396 return data
397}
398
399
pineafan4f164f32022-02-26 22:07:12 +0000400const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
pineafan6702cef2022-06-13 17:52:37 +0100401 let member = (interaction.member as Discord.GuildMember)
pineafane23c4ec2022-07-27 21:56:27 +0100402 if (!member.permissions.has("MANAGE_GUILD")) throw "You must have the *Manage Server* permission to use this command"
pineafan6702cef2022-06-13 17:52:37 +0100403 return true;
pineafan4f164f32022-02-26 22:07:12 +0000404}
405
406export { command };
407export { callback };
408export { check };