blob: f887f64d0f2486eb653289a4164e828b9021273c [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(
TheCodedProf920d7292023-06-05 11:02:32 -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 );
490 if(currentObject.options.length > 0) {
491 pageSelect.addOptions(
492 currentObject.options.map((key: ObjectSchema, index) => {
493 return new StringSelectMenuOptionBuilder()
494 .setLabel(ellipsis(key.name, 50))
495 .setDescription(ellipsis(key.description?.length ? (key.description.length > 0 ? key.description : "No description set") : "No description set", 50))
496 .setValue(index.toString());
497 })
498 );
499 } else {
500 pageSelect.setDisabled(true);
501 pageSelect.addOptions(new StringSelectMenuOptionBuilder().setLabel("No role menu pages").setValue("none"));
502 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500503 } else {
TheCodedProf920d7292023-06-05 11:02:32 -0400504 page = Math.max(Math.min(page, currentObject.options.length), 0);
505 current = currentObject.options[page - 1]!;
Skyler Greyda16adf2023-03-05 10:22:12 +0000506 embed.setDescription(
507 `**Currently Editing:** ${current.name}\n\n` +
TheCodedProf920d7292023-06-05 11:02:32 -0400508 `**Description:**\n> ${current.description ?? "*No description set*"}\n` +
Skyler Greyda16adf2023-03-05 10:22:12 +0000509 `\n\n${createPageIndicator(Object.keys(config.roleMenu.options).length, page)}`
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500510 );
511
512 pageSelect.addOptions(
TheCodedProf920d7292023-06-05 11:02:32 -0400513 currentObject.options.map((key: ObjectSchema, index) => {
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500514 return new StringSelectMenuOptionBuilder()
515 .setLabel(ellipsis(key.name, 50))
TheCodedProf920d7292023-06-05 11:02:32 -0400516 .setDescription(ellipsis(key.description?.length ? (key.description.length > 0 ? key.description : "No description set") : "No description set", 50))
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500517 .setValue(index.toString());
518 })
519 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500520 }
521
Skyler Greyda16adf2023-03-05 10:22:12 +0000522 await interaction.editReply({
523 embeds: [embed],
524 components: [
TheCodedProf920d7292023-06-05 11:02:32 -0400525 page === 0
526 ? (actionSelect as ActionRowBuilder<ButtonBuilder>)
527 : new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
528 actionSelect as StringSelectMenuBuilder
529 ),
Skyler Greyda16adf2023-03-05 10:22:12 +0000530 new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(pageSelect),
TheCodedProf920d7292023-06-05 11:02:32 -0400531 buttonRow as ActionRowBuilder<ButtonBuilder>
Skyler Greyda16adf2023-03-05 10:22:12 +0000532 ]
533 });
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500534 let i: StringSelectMenuInteraction | ButtonInteraction;
535 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000536 i = (await m.awaitMessageComponent({
537 time: 300000,
538 filter: (i) =>
539 i.user.id === interaction.user.id && i.message.id === m.id && i.channelId === interaction.channelId
540 })) as ButtonInteraction | StringSelectMenuInteraction;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500541 } catch (e) {
542 closed = true;
PineaFanb0d0c242023-02-05 10:59:45 +0000543 continue;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500544 }
545
546 await i.deferUpdate();
547 if (i.isButton()) {
548 switch (i.customId) {
PineaFanb0d0c242023-02-05 10:59:45 +0000549 case "back": {
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500550 page--;
551 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000552 }
553 case "next": {
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500554 page++;
555 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000556 }
557 case "add": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000558 const newPage = await editRoleMenuPage(i, m);
pineafan44713d42023-06-03 20:29:49 +0100559 if (_.isEqual(newPage, defaultRoleMenuData)) {
560 break;
561 }
pineafan6f600f02023-06-03 20:27:07 +0100562 if (newPage) {
TheCodedProf920d7292023-06-05 11:02:32 -0400563 currentObject.options.push(newPage);
564 page = currentObject.options.length;
565 modified = true;
pineafan6f600f02023-06-03 20:27:07 +0100566 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500567 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000568 }
569 case "reorder": {
TheCodedProf920d7292023-06-05 11:02:32 -0400570 const reordered = await reorderRoleMenuPages(interaction, m, currentObject.options);
Skyler Greyda16adf2023-03-05 10:22:12 +0000571 if (!reordered) break;
TheCodedProf920d7292023-06-05 11:02:32 -0400572 currentObject.options = reordered;
573 modified = true;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500574 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000575 }
576 case "save": {
TheCodedProf920d7292023-06-05 11:02:32 -0400577 await client.database.guilds.write(interaction.guild.id, {
578 "roleMenu.options": currentObject.options
579 });
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500580 modified = false;
Skyler Grey16ecb172023-03-05 07:30:32 +0000581 await client.memory.forceUpdate(interaction.guild.id);
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500582 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000583 }
TheCodedProf920d7292023-06-05 11:02:32 -0400584 case "switch": {
585 currentObject.enabled = !currentObject.enabled;
586 modified = true;
587 break;
588 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500589 }
590 } else if (i.isStringSelectMenu()) {
591 switch (i.customId) {
PineaFanb0d0c242023-02-05 10:59:45 +0000592 case "action": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000593 switch (i.values[0]) {
PineaFanb0d0c242023-02-05 10:59:45 +0000594 case "edit": {
595 const edited = await editRoleMenuPage(i, m, current!);
Skyler Greyda16adf2023-03-05 10:22:12 +0000596 if (!edited) break;
TheCodedProf920d7292023-06-05 11:02:32 -0400597 currentObject.options[page] = edited;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500598 modified = true;
599 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000600 }
601 case "delete": {
TheCodedProf920d7292023-06-05 11:02:32 -0400602 if (page === 0 && currentObject.options.keys.length - 1 > 0) page++;
TheCodedProff4facde2023-01-28 13:42:48 -0500603 else page--;
TheCodedProf920d7292023-06-05 11:02:32 -0400604 currentObject.options.splice(page, 1);
605 modified = true;
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500606 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000607 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500608 }
609 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000610 }
611 case "page": {
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500612 page = parseInt(i.values[0]!);
613 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000614 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500615 }
616 }
TheCodedProf01cba762023-02-18 15:55:05 -0500617 } while (!closed);
Skyler Greyda16adf2023-03-05 10:22:12 +0000618 await interaction.deleteReply();
pineafan63fc5e22022-08-04 22:04:10 +0100619};
pineafanda6e5342022-07-03 10:03:16 +0100620
TheCodedProff86ba092023-01-27 17:10:07 -0500621const check = (interaction: CommandInteraction, _partial: boolean = false) => {
Skyler Grey75ea9172022-08-06 10:22:23 +0100622 const member = interaction.member as Discord.GuildMember;
PineaFan0d06edc2023-01-17 22:10:31 +0000623 if (!member.permissions.has("ManageRoles"))
624 return "You must have the *Manage Roles* permission to use this command";
pineafanda6e5342022-07-03 10:03:16 +0100625 return true;
pineafan63fc5e22022-08-04 22:04:10 +0100626};
pineafanda6e5342022-07-03 10:03:16 +0100627
628export { command };
629export { callback };
Skyler Grey75ea9172022-08-06 10:22:23 +0100630export { check };