blob: 1bce4bba6400f5e9090e7b682146ebf273f12554 [file] [log] [blame]
TheCodedProfafca98b2023-01-17 22:25:43 -05001import type Discord from "discord.js";
Skyler Greyda16adf2023-03-05 10:22:12 +00002import {
3 ActionRowBuilder,
4 APIMessageComponentEmoji,
5 ButtonBuilder,
6 ButtonInteraction,
7 ButtonStyle,
8 CommandInteraction,
9 Message,
10 ModalBuilder,
11 RoleSelectMenuBuilder,
12 RoleSelectMenuInteraction,
13 StringSelectMenuBuilder,
14 StringSelectMenuInteraction,
15 StringSelectMenuOptionBuilder,
16 TextInputBuilder,
17 TextInputStyle
18} from "discord.js";
TheCodedProff86ba092023-01-27 17:10:07 -050019import type { SlashCommandSubcommandBuilder } from "discord.js";
TheCodedProf1c3ad3c2023-01-25 17:58:36 -050020import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
21import { LoadingEmbed } from "../../utils/defaults.js";
22import client from "../../utils/client.js";
23import getEmojiByName from "../../utils/getEmojiByName.js";
24import createPageIndicator from "../../utils/createPageIndicator.js";
25import { configToDropdown } from "../../actions/roleMenu.js";
TheCodedProff4facde2023-01-28 13:42:48 -050026import { modalInteractionCollector } from "../../utils/dualCollector.js";
TheCodedProfb5e9d552023-01-29 15:43:26 -050027import ellipsis from "../../utils/ellipsis.js";
TheCodedProfe92b9b52023-03-06 17:07:34 -050028import _ from "lodash";
TheCodedProfa112f612023-01-28 18:06:45 -050029
TheCodedProfe92b9b52023-03-06 17:07:34 -050030const isEqual = _.isEqual;
TheCodedProfa112f612023-01-28 18:06:45 -050031
Skyler Greyda16adf2023-03-05 10:22:12 +000032const command = (builder: SlashCommandSubcommandBuilder) => builder.setName("rolemenu").setDescription("rolemenu");
TheCodedProf1c3ad3c2023-01-25 17:58:36 -050033
34interface ObjectSchema {
35 name: string;
36 description: string;
37 min: number;
38 max: number;
39 options: {
40 name: string;
41 description: string | null;
42 role: string;
43 }[];
44}
45
TheCodedProff4facde2023-01-28 13:42:48 -050046const defaultRolePageConfig = {
47 name: "Role Menu Page",
48 description: "A new role menu page",
49 min: 0,
50 max: 0,
Skyler Greyda16adf2023-03-05 10:22:12 +000051 options: [{ name: "Role 1", description: null, role: "No role set" }]
52};
TheCodedProff4facde2023-01-28 13:42:48 -050053
TheCodedProfa112f612023-01-28 18:06:45 -050054const reorderRoleMenuPages = async (interaction: CommandInteraction, m: Message, currentObj: ObjectSchema[]) => {
Skyler Greyda16adf2023-03-05 10:22:12 +000055 const reorderRow = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
56 new StringSelectMenuBuilder()
57 .setCustomId("reorder")
58 .setPlaceholder("Select all pages in the order you want them to appear.")
59 .setMinValues(currentObj.length)
60 .setMaxValues(currentObj.length)
61 .addOptions(
62 currentObj.map((o, i) => new StringSelectMenuOptionBuilder().setLabel(o.name).setValue(i.toString()))
63 )
64 );
65 const buttonRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
66 new ButtonBuilder()
67 .setCustomId("back")
68 .setLabel("Back")
69 .setStyle(ButtonStyle.Secondary)
70 .setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji)
71 );
TheCodedProfa112f612023-01-28 18:06:45 -050072 await interaction.editReply({
73 embeds: [
74 new EmojiEmbed()
75 .setTitle("Role Menu")
76 .setDescription("Select pages in the order you want them to appear.")
77 .setStatus("Success")
78 ],
79 components: [reorderRow, buttonRow]
80 });
81 let out: StringSelectMenuInteraction | ButtonInteraction | null;
82 try {
Skyler Greyda16adf2023-03-05 10:22:12 +000083 out = (await m.awaitMessageComponent({
TheCodedProfa112f612023-01-28 18:06:45 -050084 filter: (i) => i.channel!.id === interaction.channel!.id,
85 time: 300000
Skyler Greyda16adf2023-03-05 10:22:12 +000086 })) as StringSelectMenuInteraction | ButtonInteraction | null;
TheCodedProfa112f612023-01-28 18:06:45 -050087 } catch (e) {
88 console.error(e);
89 out = null;
90 }
Skyler Greyda16adf2023-03-05 10:22:12 +000091 if (!out) return;
Skyler Greyf4f21c42023-03-08 14:36:29 +000092 await out.deferUpdate();
TheCodedProfa112f612023-01-28 18:06:45 -050093 if (out.isButton()) return;
TheCodedProfa112f612023-01-28 18:06:45 -050094 const values = out.values;
95
96 const newOrder: ObjectSchema[] = currentObj.map((_, i) => {
Skyler Greyda16adf2023-03-05 10:22:12 +000097 const index = values.findIndex((v) => v === i.toString());
TheCodedProfa112f612023-01-28 18:06:45 -050098 return currentObj[index];
99 }) as ObjectSchema[];
100
101 return newOrder;
Skyler Greyda16adf2023-03-05 10:22:12 +0000102};
TheCodedProfa112f612023-01-28 18:06:45 -0500103
Skyler Greyda16adf2023-03-05 10:22:12 +0000104const editNameDescription = async (
105 i: ButtonInteraction,
106 interaction: StringSelectMenuInteraction | ButtonInteraction,
107 m: Message,
108 data: { name?: string; description?: string }
109) => {
110 let { name, description } = data;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500111 const modal = new ModalBuilder()
112 .setTitle("Edit Name and Description")
113 .setCustomId("editNameDescription")
114 .addComponents(
Skyler Greyda16adf2023-03-05 10:22:12 +0000115 new ActionRowBuilder<TextInputBuilder>().addComponents(
116 new TextInputBuilder()
117 .setLabel("Name")
118 .setCustomId("name")
119 .setPlaceholder("The name of the role (e.g. Programmer)")
120 .setStyle(TextInputStyle.Short)
121 .setValue(name ?? "")
122 .setRequired(true)
pineafan72659cc2023-05-28 13:36:44 +0100123 .setMaxLength(100)
Skyler Greyda16adf2023-03-05 10:22:12 +0000124 ),
125 new ActionRowBuilder<TextInputBuilder>().addComponents(
126 new TextInputBuilder()
127 .setLabel("Description")
128 .setCustomId("description")
129 .setPlaceholder("A short description of the role (e.g. A role for people who code)")
130 .setStyle(TextInputStyle.Short)
131 .setValue(description ?? "")
pineafan72659cc2023-05-28 13:36:44 +0100132 .setRequired(false)
133 .setMaxLength(100)
Skyler Greyda16adf2023-03-05 10:22:12 +0000134 )
135 );
136 const button = new ActionRowBuilder<ButtonBuilder>().addComponents(
137 new ButtonBuilder()
138 .setCustomId("back")
139 .setLabel("Back")
140 .setStyle(ButtonStyle.Secondary)
141 .setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji)
142 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500143
Skyler Greyda16adf2023-03-05 10:22:12 +0000144 await i.showModal(modal);
TheCodedProff4facde2023-01-28 13:42:48 -0500145 await interaction.editReply({
146 embeds: [
147 new EmojiEmbed()
148 .setTitle("Role Menu")
149 .setDescription("Modal opened. If you can't see it, click back and try again.")
150 .setStatus("Success")
151 ],
152 components: [button]
153 });
154
155 let out: Discord.ModalSubmitInteraction | null;
156 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000157 out = (await modalInteractionCollector(m, interaction.user)) as Discord.ModalSubmitInteraction | null;
TheCodedProff4facde2023-01-28 13:42:48 -0500158 } catch (e) {
159 console.error(e);
160 out = null;
161 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000162 if (!out) return [name, description];
TheCodedProff4facde2023-01-28 13:42:48 -0500163 if (out.isButton()) return [name, description];
TheCodedProff4facde2023-01-28 13:42:48 -0500164 name = out.fields.fields.find((f) => f.customId === "name")?.value ?? name;
pineafan72659cc2023-05-28 13:36:44 +0100165 description = out.fields.fields.find((f) => f.customId === "description")?.value ?? "";
Skyler Greyda16adf2023-03-05 10:22:12 +0000166 return [name, description];
167};
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500168
TheCodedProf7a83f762023-03-06 17:17:00 -0500169const defaultRoleMenuData = {
pineafan72659cc2023-05-28 13:36:44 +0100170 name: "New Page",
171 description: "",
TheCodedProf7a83f762023-03-06 17:17:00 -0500172 min: 0,
pineafan72659cc2023-05-28 13:36:44 +0100173 max: 1,
TheCodedProf7a83f762023-03-06 17:17:00 -0500174 options: []
175};
176
Skyler Greyda16adf2023-03-05 10:22:12 +0000177const editRoleMenuPage = async (
178 interaction: StringSelectMenuInteraction | ButtonInteraction,
179 m: Message,
180 data?: ObjectSchema
181): Promise<ObjectSchema | null> => {
pineafan1e462ab2023-03-07 21:34:06 +0000182 if (!data) data = _.cloneDeep(defaultRoleMenuData);
Skyler Greyda16adf2023-03-05 10:22:12 +0000183 const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
184 new ButtonBuilder()
185 .setCustomId("back")
186 .setLabel("Back")
187 .setStyle(ButtonStyle.Secondary)
188 .setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji),
189 new ButtonBuilder()
190 .setCustomId("edit")
191 .setLabel("Edit")
192 .setStyle(ButtonStyle.Primary)
193 .setEmoji(getEmojiByName("ICONS.EDIT", "id") as APIMessageComponentEmoji),
194 new ButtonBuilder()
195 .setCustomId("addRole")
196 .setLabel("Add Role")
197 .setStyle(ButtonStyle.Secondary)
198 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id") as APIMessageComponentEmoji)
199 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500200
Skyler Greyda16adf2023-03-05 10:22:12 +0000201 let back = false;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500202 do {
pineafan72659cc2023-05-28 13:36:44 +0100203 const noRoles = data.options.length === 0;
204 const previewSelect = configToDropdown(
205 "Edit Roles",
206 {
207 name: data.name,
208 description: data.description,
209 min: 1,
210 max: 1,
211 options: noRoles ? [{ name: "Role 1", description: null, role: "No role set" }] : data.options
212 },
213 undefined,
214 noRoles
215 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500216 const embed = new EmojiEmbed()
217 .setTitle(`${data.name}`)
218 .setStatus("Success")
219 .setDescription(
220 `**Description:**\n> ${data.description}\n\n` +
Skyler Greyda16adf2023-03-05 10:22:12 +0000221 `**Min:** ${data.min}` +
222 (data.min === 0 ? " (Members will be given a skip button)" : "") +
223 "\n" +
pineafan72659cc2023-05-28 13:36:44 +0100224 `**Max:** ${data.max}\n` +
225 `\n**Roles:** ${data.options.length === 0 ? "*No roles set*" : data.options.length}`
Skyler Greyda16adf2023-03-05 10:22:12 +0000226 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500227
Skyler Greyf4f21c42023-03-08 14:36:29 +0000228 await interaction.editReply({ embeds: [embed], components: [previewSelect, buttons] });
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500229 let i: StringSelectMenuInteraction | ButtonInteraction;
230 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000231 i = (await m.awaitMessageComponent({
232 time: 300000,
233 filter: (i) =>
234 i.user.id === interaction.user.id && i.message.id === m.id && i.channelId === interaction.channelId
235 })) as ButtonInteraction | StringSelectMenuInteraction;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500236 } catch (e) {
237 back = true;
238 break;
239 }
240
241 if (i.isStringSelectMenu()) {
Skyler Greyda16adf2023-03-05 10:22:12 +0000242 if (i.customId === "roles") {
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500243 await i.deferUpdate();
Skyler Greyda16adf2023-03-05 10:22:12 +0000244 await createRoleMenuOptionPage(
245 interaction,
246 m,
pineafan72659cc2023-05-28 13:36:44 +0100247 data.options.find((o) => o.role === (i as StringSelectMenuInteraction).values[0]),
248 false
Skyler Greyda16adf2023-03-05 10:22:12 +0000249 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500250 }
251 } else if (i.isButton()) {
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500252 switch (i.customId) {
PineaFanb0d0c242023-02-05 10:59:45 +0000253 case "back": {
TheCodedProff4facde2023-01-28 13:42:48 -0500254 await i.deferUpdate();
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500255 back = true;
256 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000257 }
258 case "edit": {
259 const [name, description] = await editNameDescription(i, interaction, m, data);
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500260 data.name = name ? name : data.name;
261 data.description = description ? description : data.description;
262 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000263 }
264 case "addRole": {
TheCodedProff4facde2023-01-28 13:42:48 -0500265 await i.deferUpdate();
pineafan72659cc2023-05-28 13:36:44 +0100266 const out = await createRoleMenuOptionPage(interaction, m, undefined, true);
267 if (out) data.options.push(out);
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500268 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000269 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500270 }
271 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500272 } while (!back);
Skyler Greyda16adf2023-03-05 10:22:12 +0000273 if (isEqual(data, defaultRolePageConfig)) return null;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500274 return data;
Skyler Greyda16adf2023-03-05 10:22:12 +0000275};
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500276
Skyler Greyda16adf2023-03-05 10:22:12 +0000277const createRoleMenuOptionPage = async (
278 interaction: StringSelectMenuInteraction | ButtonInteraction,
279 m: Message,
pineafan72659cc2023-05-28 13:36:44 +0100280 data?: { name: string; description: string | null; role: string },
281 newRole: boolean = false
Skyler Greyda16adf2023-03-05 10:22:12 +0000282) => {
pineafan72659cc2023-05-28 13:36:44 +0100283 const initialData = _.cloneDeep(data);
Skyler Greyda16adf2023-03-05 10:22:12 +0000284 const { renderRole } = client.logger;
285 if (!data)
286 data = {
287 name: "New role Menu Option",
288 description: null,
289 role: ""
290 };
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500291 let back = false;
Skyler Greyda16adf2023-03-05 10:22:12 +0000292 const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
293 new ButtonBuilder()
294 .setCustomId("back")
pineafan72659cc2023-05-28 13:36:44 +0100295 .setLabel(newRole ? "Add" : "Back")
296 .setStyle(newRole ? ButtonStyle.Success : ButtonStyle.Secondary)
297 .setEmoji(getEmojiByName("ICONS.SAVE", "id") as APIMessageComponentEmoji),
Skyler Greyda16adf2023-03-05 10:22:12 +0000298 new ButtonBuilder()
299 .setCustomId("edit")
300 .setLabel("Edit Details")
301 .setStyle(ButtonStyle.Primary)
pineafan72659cc2023-05-28 13:36:44 +0100302 .setEmoji(getEmojiByName("ICONS.EDIT", "id") as APIMessageComponentEmoji),
303 new ButtonBuilder()
304 .setCustomId("delete")
305 .setLabel("Delete")
306 .setStyle(ButtonStyle.Danger)
307 .setEmoji(getEmojiByName("TICKETS.ISSUE", "id") as APIMessageComponentEmoji),
308 new ButtonBuilder()
309 .setCustomId("cancel")
310 .setLabel("Cancel")
311 .setStyle(ButtonStyle.Secondary)
312 .setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji)
Skyler Greyda16adf2023-03-05 10:22:12 +0000313 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500314 do {
Skyler Greyda16adf2023-03-05 10:22:12 +0000315 const roleSelect = new RoleSelectMenuBuilder()
316 .setCustomId("role")
pineafan72659cc2023-05-28 13:36:44 +0100317 .setPlaceholder(data.role ? "Change role to" : "Select a role");
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500318 const embed = new EmojiEmbed()
PineaFanb0d0c242023-02-05 10:59:45 +0000319 .setTitle(`${data.name}`)
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500320 .setStatus("Success")
321 .setDescription(
322 `**Description:**\n> ${data.description ?? "No description set"}\n\n` +
Skyler Greyda16adf2023-03-05 10:22:12 +0000323 `**Role:** ${
324 data.role ? renderRole((await interaction.guild!.roles.fetch(data.role))!) : "No role set"
325 }\n`
326 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500327
Skyler Greyf4f21c42023-03-08 14:36:29 +0000328 await interaction.editReply({
Skyler Greyda16adf2023-03-05 10:22:12 +0000329 embeds: [embed],
330 components: [new ActionRowBuilder<RoleSelectMenuBuilder>().addComponents(roleSelect), buttons]
331 });
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500332
333 let i: RoleSelectMenuInteraction | ButtonInteraction;
334 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000335 i = (await m.awaitMessageComponent({
336 time: 300000,
337 filter: (i) =>
338 i.user.id === interaction.user.id && i.message.id === m.id && i.channelId === interaction.channelId
339 })) as ButtonInteraction | RoleSelectMenuInteraction;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500340 } catch (e) {
341 back = true;
342 break;
343 }
344
345 if (i.isRoleSelectMenu()) {
Skyler Greyda16adf2023-03-05 10:22:12 +0000346 if (i.customId === "role") {
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500347 await i.deferUpdate();
348 data.role = (i as RoleSelectMenuInteraction).values[0]!;
pineafan72659cc2023-05-28 13:36:44 +0100349 await interaction.editReply({
350 embeds: [
351 new EmojiEmbed().setTitle(`Applying changes`).setStatus("Danger").setEmoji("NUCLEUS.LOADING")
352 ],
353 components: []
354 });
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500355 }
356 } else if (i.isButton()) {
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500357 switch (i.customId) {
PineaFanb0d0c242023-02-05 10:59:45 +0000358 case "back": {
TheCodedProff4facde2023-01-28 13:42:48 -0500359 await i.deferUpdate();
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500360 back = true;
361 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000362 }
363 case "edit": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000364 const [name, description] = await editNameDescription(
365 i,
366 interaction,
367 m,
368 data as { name: string; description: string }
369 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500370 data.name = name ? name : data.name;
371 data.description = description ? description : data.description;
372 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000373 }
pineafan72659cc2023-05-28 13:36:44 +0100374 case "delete": {
375 await i.deferUpdate();
376 return null;
377 }
378 case "cancel": {
379 await i.deferUpdate();
380 if (newRole) return null;
381 else return initialData;
382 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500383 }
384 }
385 } while (!back);
386 return data;
Skyler Greyda16adf2023-03-05 10:22:12 +0000387};
pineafanda6e5342022-07-03 10:03:16 +0100388
pineafan63fc5e22022-08-04 22:04:10 +0100389const callback = async (interaction: CommandInteraction): Promise<void> => {
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500390 if (!interaction.guild) return;
Skyler Greyda16adf2023-03-05 10:22:12 +0000391 const m = await interaction.reply({ embeds: LoadingEmbed, ephemeral: true, fetchReply: true });
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500392
393 let page = 0;
394 let closed = false;
395 const config = await client.database.guilds.read(interaction.guild.id);
TheCodedProfe92b9b52023-03-06 17:07:34 -0500396 let currentObject: ObjectSchema[] = _.cloneDeep(config.roleMenu.options);
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500397 let modified = false;
398 do {
Skyler Greyda16adf2023-03-05 10:22:12 +0000399 const embed = new EmojiEmbed().setTitle("Role Menu").setEmoji("GUILD.GREEN").setStatus("Success");
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500400 const noRoleMenus = currentObject.length === 0;
401 let current: ObjectSchema;
402
403 const pageSelect = new StringSelectMenuBuilder()
404 .setCustomId("page")
405 .setPlaceholder("Select a Role Menu page to manage");
406 const actionSelect = new StringSelectMenuBuilder()
407 .setCustomId("action")
408 .setPlaceholder("Perform an action")
409 .addOptions(
410 new StringSelectMenuOptionBuilder()
411 .setLabel("Edit")
412 .setDescription("Edit this page")
413 .setValue("edit")
414 .setEmoji(getEmojiByName("ICONS.EDIT", "id") as APIMessageComponentEmoji),
415 new StringSelectMenuOptionBuilder()
416 .setLabel("Delete")
417 .setDescription("Delete this page")
418 .setValue("delete")
419 .setEmoji(getEmojiByName("TICKETS.ISSUE", "id") as APIMessageComponentEmoji)
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500420 );
Skyler Greyda16adf2023-03-05 10:22:12 +0000421 const buttonRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
422 new ButtonBuilder()
423 .setCustomId("back")
424 .setStyle(ButtonStyle.Primary)
425 .setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji)
426 .setDisabled(page === 0),
427 new ButtonBuilder()
428 .setCustomId("next")
429 .setEmoji(getEmojiByName("CONTROL.RIGHT", "id") as APIMessageComponentEmoji)
430 .setStyle(ButtonStyle.Primary)
TheCodedProfe92b9b52023-03-06 17:07:34 -0500431 .setDisabled(page === Object.keys(currentObject).length - 1 || noRoleMenus),
Skyler Greyda16adf2023-03-05 10:22:12 +0000432 new ButtonBuilder()
433 .setCustomId("add")
434 .setLabel("New Page")
435 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id") as APIMessageComponentEmoji)
436 .setStyle(ButtonStyle.Secondary)
437 .setDisabled(Object.keys(currentObject).length >= 24),
438 new ButtonBuilder()
439 .setCustomId("reorder")
440 .setLabel("Reorder Pages")
441 .setEmoji(getEmojiByName("ICONS.REORDER", "id") as APIMessageComponentEmoji)
442 .setStyle(ButtonStyle.Secondary)
443 .setDisabled(Object.keys(currentObject).length <= 1),
444 new ButtonBuilder()
445 .setCustomId("save")
446 .setLabel("Save")
447 .setEmoji(getEmojiByName("ICONS.SAVE", "id") as APIMessageComponentEmoji)
448 .setStyle(ButtonStyle.Success)
449 .setDisabled(!modified)
450 );
451 if (noRoleMenus) {
452 embed.setDescription(
453 "No role menu pages have been set up yet. Use the button below to add one.\n\n" +
454 createPageIndicator(1, 1, undefined, true)
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500455 );
456 pageSelect.setDisabled(true);
457 actionSelect.setDisabled(true);
Skyler Greyda16adf2023-03-05 10:22:12 +0000458 pageSelect.addOptions(new StringSelectMenuOptionBuilder().setLabel("No role menu pages").setValue("none"));
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500459 } else {
pineafan72659cc2023-05-28 13:36:44 +0100460 page = Math.max(Math.min(page, currentObject.length - 1), 0);
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500461 current = currentObject[page]!;
Skyler Greyda16adf2023-03-05 10:22:12 +0000462 embed.setDescription(
463 `**Currently Editing:** ${current.name}\n\n` +
pineafan6f600f02023-06-03 20:27:07 +0100464 `**Description:**\n> ${current.description || "*No description set*"}\n` +
Skyler Greyda16adf2023-03-05 10:22:12 +0000465 `\n\n${createPageIndicator(Object.keys(config.roleMenu.options).length, page)}`
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500466 );
467
468 pageSelect.addOptions(
469 currentObject.map((key: ObjectSchema, index) => {
470 return new StringSelectMenuOptionBuilder()
471 .setLabel(ellipsis(key.name, 50))
pineafan6f600f02023-06-03 20:27:07 +0100472 .setDescription(ellipsis(key.description || "No description set", 50))
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500473 .setValue(index.toString());
474 })
475 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500476 }
477
Skyler Greyda16adf2023-03-05 10:22:12 +0000478 await interaction.editReply({
479 embeds: [embed],
480 components: [
481 new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(actionSelect),
482 new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(pageSelect),
483 buttonRow
484 ]
485 });
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500486 let i: StringSelectMenuInteraction | ButtonInteraction;
487 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000488 i = (await m.awaitMessageComponent({
489 time: 300000,
490 filter: (i) =>
491 i.user.id === interaction.user.id && i.message.id === m.id && i.channelId === interaction.channelId
492 })) as ButtonInteraction | StringSelectMenuInteraction;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500493 } catch (e) {
494 closed = true;
PineaFanb0d0c242023-02-05 10:59:45 +0000495 continue;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500496 }
497
498 await i.deferUpdate();
499 if (i.isButton()) {
500 switch (i.customId) {
PineaFanb0d0c242023-02-05 10:59:45 +0000501 case "back": {
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500502 page--;
503 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000504 }
505 case "next": {
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500506 page++;
507 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000508 }
509 case "add": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000510 const newPage = await editRoleMenuPage(i, m);
pineafan6f600f02023-06-03 20:27:07 +0100511 if (_.isEqual(newPage, defaultRoleMenuData)) { break; }
512 if (newPage) {
513 currentObject.push(newPage);
514 page = currentObject.length - 1;
515 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500516 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000517 }
518 case "reorder": {
519 const reordered = await reorderRoleMenuPages(interaction, m, currentObject);
Skyler Greyda16adf2023-03-05 10:22:12 +0000520 if (!reordered) break;
TheCodedProfa112f612023-01-28 18:06:45 -0500521 currentObject = reordered;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500522 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000523 }
524 case "save": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000525 await client.database.guilds.write(interaction.guild.id, { "roleMenu.options": currentObject });
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500526 modified = false;
Skyler Grey16ecb172023-03-05 07:30:32 +0000527 await client.memory.forceUpdate(interaction.guild.id);
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500528 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000529 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500530 }
531 } else if (i.isStringSelectMenu()) {
532 switch (i.customId) {
PineaFanb0d0c242023-02-05 10:59:45 +0000533 case "action": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000534 switch (i.values[0]) {
PineaFanb0d0c242023-02-05 10:59:45 +0000535 case "edit": {
536 const edited = await editRoleMenuPage(i, m, current!);
Skyler Greyda16adf2023-03-05 10:22:12 +0000537 if (!edited) break;
TheCodedProff4facde2023-01-28 13:42:48 -0500538 currentObject[page] = edited;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500539 modified = true;
540 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000541 }
542 case "delete": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000543 if (page === 0 && currentObject.keys.length - 1 > 0) page++;
TheCodedProff4facde2023-01-28 13:42:48 -0500544 else page--;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500545 currentObject.splice(page, 1);
546 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000547 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500548 }
549 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000550 }
551 case "page": {
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500552 page = parseInt(i.values[0]!);
553 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000554 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500555 }
556 }
TheCodedProf01cba762023-02-18 15:55:05 -0500557 } while (!closed);
Skyler Greyda16adf2023-03-05 10:22:12 +0000558 await interaction.deleteReply();
pineafan63fc5e22022-08-04 22:04:10 +0100559};
pineafanda6e5342022-07-03 10:03:16 +0100560
TheCodedProff86ba092023-01-27 17:10:07 -0500561const check = (interaction: CommandInteraction, _partial: boolean = false) => {
Skyler Grey75ea9172022-08-06 10:22:23 +0100562 const member = interaction.member as Discord.GuildMember;
PineaFan0d06edc2023-01-17 22:10:31 +0000563 if (!member.permissions.has("ManageRoles"))
564 return "You must have the *Manage Roles* permission to use this command";
pineafanda6e5342022-07-03 10:03:16 +0100565 return true;
pineafan63fc5e22022-08-04 22:04:10 +0100566};
pineafanda6e5342022-07-03 10:03:16 +0100567
568export { command };
569export { callback };
Skyler Grey75ea9172022-08-06 10:22:23 +0100570export { check };