blob: dbdcb4ccac0a2e459229dfe4e137abb451cb4330 [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
TheCodedProf920d7292023-06-05 11:02:32 -040032const command = (builder: SlashCommandSubcommandBuilder) =>
33 builder.setName("rolemenu").setDescription("Allows you to change settings for the servers rolemenu");
TheCodedProf1c3ad3c2023-01-25 17:58:36 -050034
35interface ObjectSchema {
36 name: string;
TheCodedProf920d7292023-06-05 11:02:32 -040037 description: string | null;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -050038 min: number;
39 max: number;
40 options: {
41 name: string;
42 description: string | null;
43 role: string;
44 }[];
45}
46
TheCodedProff4facde2023-01-28 13:42:48 -050047const defaultRolePageConfig = {
48 name: "Role Menu Page",
49 description: "A new role menu page",
50 min: 0,
51 max: 0,
Skyler Greyda16adf2023-03-05 10:22:12 +000052 options: [{ name: "Role 1", description: null, role: "No role set" }]
53};
TheCodedProff4facde2023-01-28 13:42:48 -050054
TheCodedProfa112f612023-01-28 18:06:45 -050055const reorderRoleMenuPages = async (interaction: CommandInteraction, m: Message, currentObj: ObjectSchema[]) => {
Skyler Greyda16adf2023-03-05 10:22:12 +000056 const reorderRow = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
57 new StringSelectMenuBuilder()
58 .setCustomId("reorder")
59 .setPlaceholder("Select all pages in the order you want them to appear.")
60 .setMinValues(currentObj.length)
61 .setMaxValues(currentObj.length)
62 .addOptions(
63 currentObj.map((o, i) => new StringSelectMenuOptionBuilder().setLabel(o.name).setValue(i.toString()))
64 )
65 );
66 const buttonRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
67 new ButtonBuilder()
68 .setCustomId("back")
69 .setLabel("Back")
70 .setStyle(ButtonStyle.Secondary)
71 .setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji)
72 );
TheCodedProfa112f612023-01-28 18:06:45 -050073 await interaction.editReply({
74 embeds: [
75 new EmojiEmbed()
76 .setTitle("Role Menu")
77 .setDescription("Select pages in the order you want them to appear.")
78 .setStatus("Success")
79 ],
80 components: [reorderRow, buttonRow]
81 });
82 let out: StringSelectMenuInteraction | ButtonInteraction | null;
83 try {
Skyler Greyda16adf2023-03-05 10:22:12 +000084 out = (await m.awaitMessageComponent({
TheCodedProfa112f612023-01-28 18:06:45 -050085 filter: (i) => i.channel!.id === interaction.channel!.id,
86 time: 300000
Skyler Greyda16adf2023-03-05 10:22:12 +000087 })) as StringSelectMenuInteraction | ButtonInteraction | null;
TheCodedProfa112f612023-01-28 18:06:45 -050088 } catch (e) {
89 console.error(e);
90 out = null;
91 }
Skyler Greyda16adf2023-03-05 10:22:12 +000092 if (!out) return;
Skyler Greyf4f21c42023-03-08 14:36:29 +000093 await out.deferUpdate();
TheCodedProfa112f612023-01-28 18:06:45 -050094 if (out.isButton()) return;
TheCodedProfa112f612023-01-28 18:06:45 -050095 const values = out.values;
96
97 const newOrder: ObjectSchema[] = currentObj.map((_, i) => {
Skyler Greyda16adf2023-03-05 10:22:12 +000098 const index = values.findIndex((v) => v === i.toString());
TheCodedProfa112f612023-01-28 18:06:45 -050099 return currentObj[index];
100 }) as ObjectSchema[];
101
102 return newOrder;
Skyler Greyda16adf2023-03-05 10:22:12 +0000103};
TheCodedProfa112f612023-01-28 18:06:45 -0500104
Skyler Greyda16adf2023-03-05 10:22:12 +0000105const editNameDescription = async (
106 i: ButtonInteraction,
107 interaction: StringSelectMenuInteraction | ButtonInteraction,
108 m: Message,
TheCodedProf920d7292023-06-05 11:02:32 -0400109 data: { name?: string; description?: string | null }
Skyler Greyda16adf2023-03-05 10:22:12 +0000110) => {
111 let { name, description } = data;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500112 const modal = new ModalBuilder()
113 .setTitle("Edit Name and Description")
114 .setCustomId("editNameDescription")
115 .addComponents(
Skyler Greyda16adf2023-03-05 10:22:12 +0000116 new ActionRowBuilder<TextInputBuilder>().addComponents(
117 new TextInputBuilder()
118 .setLabel("Name")
119 .setCustomId("name")
120 .setPlaceholder("The name of the role (e.g. Programmer)")
121 .setStyle(TextInputStyle.Short)
122 .setValue(name ?? "")
123 .setRequired(true)
pineafan72659cc2023-05-28 13:36:44 +0100124 .setMaxLength(100)
Skyler Greyda16adf2023-03-05 10:22:12 +0000125 ),
126 new ActionRowBuilder<TextInputBuilder>().addComponents(
127 new TextInputBuilder()
128 .setLabel("Description")
129 .setCustomId("description")
130 .setPlaceholder("A short description of the role (e.g. A role for people who code)")
131 .setStyle(TextInputStyle.Short)
132 .setValue(description ?? "")
pineafan72659cc2023-05-28 13:36:44 +0100133 .setRequired(false)
134 .setMaxLength(100)
Skyler Greyda16adf2023-03-05 10:22:12 +0000135 )
136 );
137 const button = new ActionRowBuilder<ButtonBuilder>().addComponents(
138 new ButtonBuilder()
139 .setCustomId("back")
140 .setLabel("Back")
141 .setStyle(ButtonStyle.Secondary)
142 .setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji)
143 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500144
Skyler Greyda16adf2023-03-05 10:22:12 +0000145 await i.showModal(modal);
TheCodedProff4facde2023-01-28 13:42:48 -0500146 await interaction.editReply({
147 embeds: [
148 new EmojiEmbed()
149 .setTitle("Role Menu")
150 .setDescription("Modal opened. If you can't see it, click back and try again.")
151 .setStatus("Success")
152 ],
153 components: [button]
154 });
155
156 let out: Discord.ModalSubmitInteraction | null;
157 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000158 out = (await modalInteractionCollector(m, interaction.user)) as Discord.ModalSubmitInteraction | null;
TheCodedProff4facde2023-01-28 13:42:48 -0500159 } catch (e) {
160 console.error(e);
161 out = null;
162 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000163 if (!out) return [name, description];
TheCodedProff4facde2023-01-28 13:42:48 -0500164 if (out.isButton()) return [name, description];
TheCodedProff4facde2023-01-28 13:42:48 -0500165 name = out.fields.fields.find((f) => f.customId === "name")?.value ?? name;
TheCodedProf920d7292023-06-05 11:02:32 -0400166 description = out.fields.fields.find((f) => f.customId === "description")?.value ?? null;
Skyler Greyda16adf2023-03-05 10:22:12 +0000167 return [name, description];
168};
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500169
TheCodedProf7a83f762023-03-06 17:17:00 -0500170const defaultRoleMenuData = {
pineafan72659cc2023-05-28 13:36:44 +0100171 name: "New Page",
172 description: "",
TheCodedProf7a83f762023-03-06 17:17:00 -0500173 min: 0,
pineafan72659cc2023-05-28 13:36:44 +0100174 max: 1,
TheCodedProf7a83f762023-03-06 17:17:00 -0500175 options: []
176};
177
Skyler Greyda16adf2023-03-05 10:22:12 +0000178const editRoleMenuPage = async (
179 interaction: StringSelectMenuInteraction | ButtonInteraction,
180 m: Message,
181 data?: ObjectSchema
182): Promise<ObjectSchema | null> => {
pineafan1e462ab2023-03-07 21:34:06 +0000183 if (!data) data = _.cloneDeep(defaultRoleMenuData);
Skyler Greyda16adf2023-03-05 10:22:12 +0000184 const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
185 new ButtonBuilder()
186 .setCustomId("back")
187 .setLabel("Back")
188 .setStyle(ButtonStyle.Secondary)
189 .setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji),
190 new ButtonBuilder()
191 .setCustomId("edit")
192 .setLabel("Edit")
193 .setStyle(ButtonStyle.Primary)
194 .setEmoji(getEmojiByName("ICONS.EDIT", "id") as APIMessageComponentEmoji),
195 new ButtonBuilder()
196 .setCustomId("addRole")
197 .setLabel("Add Role")
198 .setStyle(ButtonStyle.Secondary)
199 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id") as APIMessageComponentEmoji)
200 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500201
Skyler Greyda16adf2023-03-05 10:22:12 +0000202 let back = false;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500203 do {
pineafan72659cc2023-05-28 13:36:44 +0100204 const noRoles = data.options.length === 0;
205 const previewSelect = configToDropdown(
206 "Edit Roles",
207 {
208 name: data.name,
TheCodedProf920d7292023-06-05 11:02:32 -0400209 description: data.description ?? null,
pineafan72659cc2023-05-28 13:36:44 +0100210 min: 1,
211 max: 1,
212 options: noRoles ? [{ name: "Role 1", description: null, role: "No role set" }] : data.options
213 },
214 undefined,
215 noRoles
216 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500217 const embed = new EmojiEmbed()
218 .setTitle(`${data.name}`)
219 .setStatus("Success")
220 .setDescription(
TheCodedProffc8d6ba2023-06-05 11:16:35 -0400221 `**Description:**\n> ${data.description ?? "*No description set*"}\n\n` +
Skyler Greyda16adf2023-03-05 10:22:12 +0000222 `**Min:** ${data.min}` +
223 (data.min === 0 ? " (Members will be given a skip button)" : "") +
224 "\n" +
pineafan72659cc2023-05-28 13:36:44 +0100225 `**Max:** ${data.max}\n` +
226 `\n**Roles:** ${data.options.length === 0 ? "*No roles set*" : data.options.length}`
Skyler Greyda16adf2023-03-05 10:22:12 +0000227 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500228
Skyler Greyf4f21c42023-03-08 14:36:29 +0000229 await interaction.editReply({ embeds: [embed], components: [previewSelect, buttons] });
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500230 let i: StringSelectMenuInteraction | ButtonInteraction;
231 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000232 i = (await m.awaitMessageComponent({
233 time: 300000,
234 filter: (i) =>
235 i.user.id === interaction.user.id && i.message.id === m.id && i.channelId === interaction.channelId
236 })) as ButtonInteraction | StringSelectMenuInteraction;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500237 } catch (e) {
238 back = true;
239 break;
240 }
241
242 if (i.isStringSelectMenu()) {
Skyler Greyda16adf2023-03-05 10:22:12 +0000243 if (i.customId === "roles") {
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500244 await i.deferUpdate();
Skyler Greyda16adf2023-03-05 10:22:12 +0000245 await createRoleMenuOptionPage(
246 interaction,
247 m,
pineafan72659cc2023-05-28 13:36:44 +0100248 data.options.find((o) => o.role === (i as StringSelectMenuInteraction).values[0]),
249 false
Skyler Greyda16adf2023-03-05 10:22:12 +0000250 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500251 }
252 } else if (i.isButton()) {
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500253 switch (i.customId) {
PineaFanb0d0c242023-02-05 10:59:45 +0000254 case "back": {
TheCodedProff4facde2023-01-28 13:42:48 -0500255 await i.deferUpdate();
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500256 back = true;
257 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000258 }
259 case "edit": {
260 const [name, description] = await editNameDescription(i, interaction, m, data);
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500261 data.name = name ? name : data.name;
262 data.description = description ? description : data.description;
263 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000264 }
265 case "addRole": {
TheCodedProff4facde2023-01-28 13:42:48 -0500266 await i.deferUpdate();
pineafan72659cc2023-05-28 13:36:44 +0100267 const out = await createRoleMenuOptionPage(interaction, m, undefined, true);
268 if (out) data.options.push(out);
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500269 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000270 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500271 }
272 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500273 } while (!back);
Skyler Greyda16adf2023-03-05 10:22:12 +0000274 if (isEqual(data, defaultRolePageConfig)) return null;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500275 return data;
Skyler Greyda16adf2023-03-05 10:22:12 +0000276};
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500277
Skyler Greyda16adf2023-03-05 10:22:12 +0000278const createRoleMenuOptionPage = async (
279 interaction: StringSelectMenuInteraction | ButtonInteraction,
280 m: Message,
pineafan72659cc2023-05-28 13:36:44 +0100281 data?: { name: string; description: string | null; role: string },
282 newRole: boolean = false
Skyler Greyda16adf2023-03-05 10:22:12 +0000283) => {
pineafan72659cc2023-05-28 13:36:44 +0100284 const initialData = _.cloneDeep(data);
Skyler Greyda16adf2023-03-05 10:22:12 +0000285 const { renderRole } = client.logger;
286 if (!data)
287 data = {
288 name: "New role Menu Option",
289 description: null,
290 role: ""
291 };
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500292 let back = false;
Skyler Greyda16adf2023-03-05 10:22:12 +0000293 const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
294 new ButtonBuilder()
295 .setCustomId("back")
pineafan72659cc2023-05-28 13:36:44 +0100296 .setLabel(newRole ? "Add" : "Back")
297 .setStyle(newRole ? ButtonStyle.Success : ButtonStyle.Secondary)
298 .setEmoji(getEmojiByName("ICONS.SAVE", "id") as APIMessageComponentEmoji),
Skyler Greyda16adf2023-03-05 10:22:12 +0000299 new ButtonBuilder()
300 .setCustomId("edit")
301 .setLabel("Edit Details")
302 .setStyle(ButtonStyle.Primary)
pineafan72659cc2023-05-28 13:36:44 +0100303 .setEmoji(getEmojiByName("ICONS.EDIT", "id") as APIMessageComponentEmoji),
304 new ButtonBuilder()
305 .setCustomId("delete")
306 .setLabel("Delete")
307 .setStyle(ButtonStyle.Danger)
308 .setEmoji(getEmojiByName("TICKETS.ISSUE", "id") as APIMessageComponentEmoji),
309 new ButtonBuilder()
310 .setCustomId("cancel")
311 .setLabel("Cancel")
312 .setStyle(ButtonStyle.Secondary)
313 .setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji)
Skyler Greyda16adf2023-03-05 10:22:12 +0000314 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500315 do {
Skyler Greyda16adf2023-03-05 10:22:12 +0000316 const roleSelect = new RoleSelectMenuBuilder()
317 .setCustomId("role")
pineafan72659cc2023-05-28 13:36:44 +0100318 .setPlaceholder(data.role ? "Change role to" : "Select a role");
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500319 const embed = new EmojiEmbed()
PineaFanb0d0c242023-02-05 10:59:45 +0000320 .setTitle(`${data.name}`)
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500321 .setStatus("Success")
322 .setDescription(
323 `**Description:**\n> ${data.description ?? "No description set"}\n\n` +
Skyler Greyda16adf2023-03-05 10:22:12 +0000324 `**Role:** ${
325 data.role ? renderRole((await interaction.guild!.roles.fetch(data.role))!) : "No role set"
326 }\n`
327 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500328
Skyler Greyf4f21c42023-03-08 14:36:29 +0000329 await interaction.editReply({
Skyler Greyda16adf2023-03-05 10:22:12 +0000330 embeds: [embed],
331 components: [new ActionRowBuilder<RoleSelectMenuBuilder>().addComponents(roleSelect), buttons]
332 });
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500333
334 let i: RoleSelectMenuInteraction | ButtonInteraction;
335 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000336 i = (await m.awaitMessageComponent({
337 time: 300000,
338 filter: (i) =>
339 i.user.id === interaction.user.id && i.message.id === m.id && i.channelId === interaction.channelId
340 })) as ButtonInteraction | RoleSelectMenuInteraction;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500341 } catch (e) {
342 back = true;
343 break;
344 }
345
346 if (i.isRoleSelectMenu()) {
Skyler Greyda16adf2023-03-05 10:22:12 +0000347 if (i.customId === "role") {
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500348 await i.deferUpdate();
349 data.role = (i as RoleSelectMenuInteraction).values[0]!;
pineafan72659cc2023-05-28 13:36:44 +0100350 await interaction.editReply({
351 embeds: [
352 new EmojiEmbed().setTitle(`Applying changes`).setStatus("Danger").setEmoji("NUCLEUS.LOADING")
353 ],
354 components: []
355 });
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500356 }
357 } else if (i.isButton()) {
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500358 switch (i.customId) {
PineaFanb0d0c242023-02-05 10:59:45 +0000359 case "back": {
TheCodedProff4facde2023-01-28 13:42:48 -0500360 await i.deferUpdate();
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500361 back = true;
362 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000363 }
364 case "edit": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000365 const [name, description] = await editNameDescription(
366 i,
367 interaction,
368 m,
369 data as { name: string; description: string }
370 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500371 data.name = name ? name : data.name;
372 data.description = description ? description : data.description;
373 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000374 }
pineafan72659cc2023-05-28 13:36:44 +0100375 case "delete": {
376 await i.deferUpdate();
377 return null;
378 }
379 case "cancel": {
380 await i.deferUpdate();
381 if (newRole) return null;
382 else return initialData;
383 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500384 }
385 }
386 } while (!back);
387 return data;
Skyler Greyda16adf2023-03-05 10:22:12 +0000388};
pineafanda6e5342022-07-03 10:03:16 +0100389
pineafan63fc5e22022-08-04 22:04:10 +0100390const callback = async (interaction: CommandInteraction): Promise<void> => {
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500391 if (!interaction.guild) return;
Skyler Greyda16adf2023-03-05 10:22:12 +0000392 const m = await interaction.reply({ embeds: LoadingEmbed, ephemeral: true, fetchReply: true });
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500393
394 let page = 0;
395 let closed = false;
396 const config = await client.database.guilds.read(interaction.guild.id);
TheCodedProf920d7292023-06-05 11:02:32 -0400397 const currentObject: typeof config.roleMenu = _.cloneDeep(config.roleMenu);
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500398 let modified = false;
399 do {
Skyler Greyda16adf2023-03-05 10:22:12 +0000400 const embed = new EmojiEmbed().setTitle("Role Menu").setEmoji("GUILD.GREEN").setStatus("Success");
TheCodedProf920d7292023-06-05 11:02:32 -0400401 const noRoleMenus = currentObject.options.length === 0;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500402 let current: ObjectSchema;
403
404 const pageSelect = new StringSelectMenuBuilder()
405 .setCustomId("page")
406 .setPlaceholder("Select a Role Menu page to manage");
TheCodedProf920d7292023-06-05 11:02:32 -0400407 let actionSelect;
408 if (page === 0) {
409 actionSelect = new ActionRowBuilder<ButtonBuilder>().addComponents(
410 new ButtonBuilder()
411 .setCustomId("switch")
412 .setLabel(currentObject.enabled ? "Enabled" : "Disabled")
413 .setStyle(currentObject.enabled ? ButtonStyle.Success : ButtonStyle.Danger)
414 .setEmoji(
415 getEmojiByName(
416 currentObject.enabled ? "CONTROL.TICK" : "CONTROL.CROSS",
417 "id"
418 ) as APIMessageComponentEmoji
419 )
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500420 );
TheCodedProf920d7292023-06-05 11:02:32 -0400421 } else {
422 actionSelect = new StringSelectMenuBuilder()
423 .setCustomId("action")
424 .setPlaceholder("Perform an action")
425 .addOptions(
426 new StringSelectMenuOptionBuilder()
427 .setLabel("Edit")
428 .setDescription("Edit this page")
429 .setValue("edit")
430 .setEmoji(getEmojiByName("ICONS.EDIT", "id") as APIMessageComponentEmoji),
431 new StringSelectMenuOptionBuilder()
432 .setLabel("Delete")
433 .setDescription("Delete this page")
434 .setValue("delete")
435 .setEmoji(getEmojiByName("TICKETS.ISSUE", "id") as APIMessageComponentEmoji)
436 );
437 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000438 const buttonRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
439 new ButtonBuilder()
440 .setCustomId("back")
441 .setStyle(ButtonStyle.Primary)
442 .setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji)
443 .setDisabled(page === 0),
444 new ButtonBuilder()
445 .setCustomId("next")
446 .setEmoji(getEmojiByName("CONTROL.RIGHT", "id") as APIMessageComponentEmoji)
447 .setStyle(ButtonStyle.Primary)
TheCodedProf920d7292023-06-05 11:02:32 -0400448 .setDisabled(page === Object.keys(currentObject.options).length || noRoleMenus),
Skyler Greyda16adf2023-03-05 10:22:12 +0000449 new ButtonBuilder()
450 .setCustomId("add")
451 .setLabel("New Page")
452 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id") as APIMessageComponentEmoji)
453 .setStyle(ButtonStyle.Secondary)
TheCodedProf920d7292023-06-05 11:02:32 -0400454 .setDisabled(Object.keys(currentObject.options).length >= 24),
Skyler Greyda16adf2023-03-05 10:22:12 +0000455 new ButtonBuilder()
456 .setCustomId("reorder")
457 .setLabel("Reorder Pages")
458 .setEmoji(getEmojiByName("ICONS.REORDER", "id") as APIMessageComponentEmoji)
459 .setStyle(ButtonStyle.Secondary)
TheCodedProf920d7292023-06-05 11:02:32 -0400460 .setDisabled(Object.keys(currentObject.options).length <= 1),
Skyler Greyda16adf2023-03-05 10:22:12 +0000461 new ButtonBuilder()
462 .setCustomId("save")
463 .setLabel("Save")
464 .setEmoji(getEmojiByName("ICONS.SAVE", "id") as APIMessageComponentEmoji)
465 .setStyle(ButtonStyle.Success)
466 .setDisabled(!modified)
467 );
468 if (noRoleMenus) {
469 embed.setDescription(
470 "No role menu pages have been set up yet. Use the button below to add one.\n\n" +
471 createPageIndicator(1, 1, undefined, true)
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500472 );
473 pageSelect.setDisabled(true);
TheCodedProf920d7292023-06-05 11:02:32 -0400474 if (page > 0) (actionSelect as StringSelectMenuBuilder).setDisabled(true);
Skyler Greyda16adf2023-03-05 10:22:12 +0000475 pageSelect.addOptions(new StringSelectMenuOptionBuilder().setLabel("No role menu pages").setValue("none"));
TheCodedProf920d7292023-06-05 11:02:32 -0400476 } else if (page === 0) {
477 const cross = getEmojiByName("CONTROL.CROSS");
478 const tick = getEmojiByName("CONTROL.TICK");
479 embed.setDescription(
480 `**Enabled:** ${config.roleMenu.enabled ? `${tick} Yes` : `${cross} No`}\n\n` +
481 `**Pages:** ${currentObject.options.length}\n` +
482 (currentObject.options.length > 0
483 ? currentObject.options
484 .map((key: ObjectSchema) => {
485 return `> **${key.name}:** ${key.description ?? "*No description set*"}`;
486 })
487 .join("\n")
488 : "")
489 );
TheCodedProffc8d6ba2023-06-05 11:16:35 -0400490 if (currentObject.options.length > 0) {
TheCodedProf920d7292023-06-05 11:02:32 -0400491 pageSelect.addOptions(
492 currentObject.options.map((key: ObjectSchema, index) => {
493 return new StringSelectMenuOptionBuilder()
494 .setLabel(ellipsis(key.name, 50))
TheCodedProffc8d6ba2023-06-05 11:16:35 -0400495 .setDescription(
496 ellipsis(
497 key.description?.length
498 ? key.description.length > 0
499 ? key.description
500 : "No description set"
501 : "No description set",
502 50
503 )
504 )
TheCodedProf920d7292023-06-05 11:02:32 -0400505 .setValue(index.toString());
506 })
507 );
508 } else {
509 pageSelect.setDisabled(true);
TheCodedProffc8d6ba2023-06-05 11:16:35 -0400510 pageSelect.addOptions(
511 new StringSelectMenuOptionBuilder().setLabel("No role menu pages").setValue("none")
512 );
TheCodedProf920d7292023-06-05 11:02:32 -0400513 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500514 } else {
TheCodedProf920d7292023-06-05 11:02:32 -0400515 page = Math.max(Math.min(page, currentObject.options.length), 0);
516 current = currentObject.options[page - 1]!;
Skyler Greyda16adf2023-03-05 10:22:12 +0000517 embed.setDescription(
518 `**Currently Editing:** ${current.name}\n\n` +
TheCodedProf920d7292023-06-05 11:02:32 -0400519 `**Description:**\n> ${current.description ?? "*No description set*"}\n` +
Skyler Greyda16adf2023-03-05 10:22:12 +0000520 `\n\n${createPageIndicator(Object.keys(config.roleMenu.options).length, page)}`
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500521 );
522
523 pageSelect.addOptions(
TheCodedProf920d7292023-06-05 11:02:32 -0400524 currentObject.options.map((key: ObjectSchema, index) => {
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500525 return new StringSelectMenuOptionBuilder()
526 .setLabel(ellipsis(key.name, 50))
TheCodedProffc8d6ba2023-06-05 11:16:35 -0400527 .setDescription(
528 ellipsis(
529 key.description?.length
530 ? key.description.length > 0
531 ? key.description
532 : "No description set"
533 : "No description set",
534 50
535 )
536 )
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500537 .setValue(index.toString());
538 })
539 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500540 }
541
Skyler Greyda16adf2023-03-05 10:22:12 +0000542 await interaction.editReply({
543 embeds: [embed],
544 components: [
TheCodedProf920d7292023-06-05 11:02:32 -0400545 page === 0
546 ? (actionSelect as ActionRowBuilder<ButtonBuilder>)
547 : new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
548 actionSelect as StringSelectMenuBuilder
549 ),
Skyler Greyda16adf2023-03-05 10:22:12 +0000550 new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(pageSelect),
TheCodedProf920d7292023-06-05 11:02:32 -0400551 buttonRow as ActionRowBuilder<ButtonBuilder>
Skyler Greyda16adf2023-03-05 10:22:12 +0000552 ]
553 });
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500554 let i: StringSelectMenuInteraction | ButtonInteraction;
555 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000556 i = (await m.awaitMessageComponent({
557 time: 300000,
558 filter: (i) =>
559 i.user.id === interaction.user.id && i.message.id === m.id && i.channelId === interaction.channelId
560 })) as ButtonInteraction | StringSelectMenuInteraction;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500561 } catch (e) {
562 closed = true;
PineaFanb0d0c242023-02-05 10:59:45 +0000563 continue;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500564 }
565
566 await i.deferUpdate();
567 if (i.isButton()) {
568 switch (i.customId) {
PineaFanb0d0c242023-02-05 10:59:45 +0000569 case "back": {
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500570 page--;
571 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000572 }
573 case "next": {
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500574 page++;
575 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000576 }
577 case "add": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000578 const newPage = await editRoleMenuPage(i, m);
pineafan44713d42023-06-03 20:29:49 +0100579 if (_.isEqual(newPage, defaultRoleMenuData)) {
580 break;
581 }
pineafan6f600f02023-06-03 20:27:07 +0100582 if (newPage) {
TheCodedProf920d7292023-06-05 11:02:32 -0400583 currentObject.options.push(newPage);
584 page = currentObject.options.length;
585 modified = true;
pineafan6f600f02023-06-03 20:27:07 +0100586 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500587 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000588 }
589 case "reorder": {
TheCodedProf920d7292023-06-05 11:02:32 -0400590 const reordered = await reorderRoleMenuPages(interaction, m, currentObject.options);
Skyler Greyda16adf2023-03-05 10:22:12 +0000591 if (!reordered) break;
TheCodedProf920d7292023-06-05 11:02:32 -0400592 currentObject.options = reordered;
593 modified = true;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500594 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000595 }
596 case "save": {
TheCodedProf920d7292023-06-05 11:02:32 -0400597 await client.database.guilds.write(interaction.guild.id, {
598 "roleMenu.options": currentObject.options
599 });
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500600 modified = false;
Skyler Grey16ecb172023-03-05 07:30:32 +0000601 await client.memory.forceUpdate(interaction.guild.id);
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500602 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000603 }
TheCodedProf920d7292023-06-05 11:02:32 -0400604 case "switch": {
605 currentObject.enabled = !currentObject.enabled;
606 modified = true;
607 break;
608 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500609 }
610 } else if (i.isStringSelectMenu()) {
611 switch (i.customId) {
PineaFanb0d0c242023-02-05 10:59:45 +0000612 case "action": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000613 switch (i.values[0]) {
PineaFanb0d0c242023-02-05 10:59:45 +0000614 case "edit": {
615 const edited = await editRoleMenuPage(i, m, current!);
Skyler Greyda16adf2023-03-05 10:22:12 +0000616 if (!edited) break;
TheCodedProf920d7292023-06-05 11:02:32 -0400617 currentObject.options[page] = edited;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500618 modified = true;
619 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000620 }
621 case "delete": {
TheCodedProf920d7292023-06-05 11:02:32 -0400622 if (page === 0 && currentObject.options.keys.length - 1 > 0) page++;
TheCodedProff4facde2023-01-28 13:42:48 -0500623 else page--;
TheCodedProf920d7292023-06-05 11:02:32 -0400624 currentObject.options.splice(page, 1);
625 modified = true;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500626 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000627 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500628 }
629 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000630 }
631 case "page": {
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500632 page = parseInt(i.values[0]!);
633 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000634 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500635 }
636 }
TheCodedProf01cba762023-02-18 15:55:05 -0500637 } while (!closed);
Skyler Greyda16adf2023-03-05 10:22:12 +0000638 await interaction.deleteReply();
pineafan63fc5e22022-08-04 22:04:10 +0100639};
pineafanda6e5342022-07-03 10:03:16 +0100640
TheCodedProff86ba092023-01-27 17:10:07 -0500641const check = (interaction: CommandInteraction, _partial: boolean = false) => {
Skyler Grey75ea9172022-08-06 10:22:23 +0100642 const member = interaction.member as Discord.GuildMember;
PineaFan0d06edc2023-01-17 22:10:31 +0000643 if (!member.permissions.has("ManageRoles"))
644 return "You must have the *Manage Roles* permission to use this command";
pineafanda6e5342022-07-03 10:03:16 +0100645 return true;
pineafan63fc5e22022-08-04 22:04:10 +0100646};
pineafanda6e5342022-07-03 10:03:16 +0100647
648export { command };
649export { callback };
Skyler Grey75ea9172022-08-06 10:22:23 +0100650export { check };