blob: cbc5f177c960b276da3d0cc43e16aac6fc87dd0f [file] [log] [blame]
Skyler Greyda16adf2023-03-05 10:22:12 +00001import {
2 ActionRowBuilder,
3 APIMessageComponentEmoji,
4 ButtonBuilder,
5 ButtonInteraction,
6 ButtonStyle,
7 Collection,
8 CommandInteraction,
9 GuildMember,
10 Message,
11 ModalBuilder,
12 ModalSubmitInteraction,
13 PermissionsBitField,
14 Role,
15 RoleSelectMenuBuilder,
16 RoleSelectMenuInteraction,
17 SlashCommandSubcommandBuilder,
18 StringSelectMenuBuilder,
19 StringSelectMenuInteraction,
20 StringSelectMenuOptionBuilder,
21 TextInputBuilder,
22 TextInputStyle
23} from "discord.js";
TheCodedProfa112f612023-01-28 18:06:45 -050024import client from "../../utils/client.js";
TheCodedProfb5e9d552023-01-29 15:43:26 -050025import createPageIndicator, { createVerticalTrack } from "../../utils/createPageIndicator.js";
26import { LoadingEmbed } from "../../utils/defaults.js";
27import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
28import getEmojiByName from "../../utils/getEmojiByName.js";
29import ellipsis from "../../utils/ellipsis.js";
30import { modalInteractionCollector } from "../../utils/dualCollector.js";
TheCodedProfa112f612023-01-28 18:06:45 -050031
Skyler Greyda16adf2023-03-05 10:22:12 +000032const { renderRole } = client.logger;
TheCodedProf4f79da12023-01-31 16:50:37 -050033
TheCodedProfa112f612023-01-28 18:06:45 -050034const command = (builder: SlashCommandSubcommandBuilder) =>
Skyler Greyda16adf2023-03-05 10:22:12 +000035 builder.setName("tracks").setDescription("Manage the tracks for the server");
TheCodedProfa112f612023-01-28 18:06:45 -050036
TheCodedProfb5e9d552023-01-29 15:43:26 -050037interface ObjectSchema {
38 name: string;
39 retainPrevious: boolean;
40 nullable: boolean;
41 track: string[];
42 manageableBy: string[];
43}
44
Skyler Greyda16adf2023-03-05 10:22:12 +000045const editName = async (
46 i: ButtonInteraction,
47 interaction: StringSelectMenuInteraction | ButtonInteraction,
48 m: Message,
49 current?: string
50) => {
TheCodedProfb5e9d552023-01-29 15:43:26 -050051 let name = current ?? "";
52 const modal = new ModalBuilder()
53 .setTitle("Edit Name and Description")
54 .setCustomId("editNameDescription")
55 .addComponents(
Skyler Greyda16adf2023-03-05 10:22:12 +000056 new ActionRowBuilder<TextInputBuilder>().addComponents(
57 new TextInputBuilder()
58 .setLabel("Name")
59 .setCustomId("name")
60 .setPlaceholder("The name of the track (e.g. Moderators)")
61 .setStyle(TextInputStyle.Short)
62 .setValue(name)
63 .setRequired(true)
64 )
65 );
66 const button = 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 );
TheCodedProfb5e9d552023-01-29 15:43:26 -050073
Skyler Greyda16adf2023-03-05 10:22:12 +000074 await i.showModal(modal);
TheCodedProfb5e9d552023-01-29 15:43:26 -050075 await interaction.editReply({
76 embeds: [
77 new EmojiEmbed()
78 .setTitle("Tracks")
79 .setDescription("Modal opened. If you can't see it, click back and try again.")
80 .setStatus("Success")
81 ],
82 components: [button]
83 });
84
85 let out: ModalSubmitInteraction | null;
86 try {
Skyler Greyda16adf2023-03-05 10:22:12 +000087 out = (await modalInteractionCollector(m, interaction.user)) as ModalSubmitInteraction | null;
TheCodedProfb5e9d552023-01-29 15:43:26 -050088 } catch (e) {
89 console.error(e);
90 out = null;
91 }
Skyler Greyda16adf2023-03-05 10:22:12 +000092 if (!out) return name;
TheCodedProfb5e9d552023-01-29 15:43:26 -050093 if (out.isButton()) return name;
TheCodedProfb5e9d552023-01-29 15:43:26 -050094 name = out.fields.fields.find((f) => f.customId === "name")?.value ?? name;
Skyler Greyda16adf2023-03-05 10:22:12 +000095 return name;
96};
TheCodedProfb5e9d552023-01-29 15:43:26 -050097
Skyler Greyda16adf2023-03-05 10:22:12 +000098const reorderTracks = async (
99 interaction: ButtonInteraction,
100 m: Message,
101 roles: Collection<string, Role>,
102 currentObj: string[]
103) => {
104 const reorderRow = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
105 new StringSelectMenuBuilder()
106 .setCustomId("reorder")
107 .setPlaceholder("Select all roles in the order you want users to gain them (Lowest to highest rank).")
108 .setMinValues(currentObj.length)
109 .setMaxValues(currentObj.length)
110 .addOptions(
111 currentObj.map((o, i) =>
112 new StringSelectMenuOptionBuilder().setLabel(roles.get(o)!.name).setValue(i.toString())
TheCodedProfb5e9d552023-01-29 15:43:26 -0500113 )
Skyler Greyda16adf2023-03-05 10:22:12 +0000114 )
115 );
116 const buttonRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
117 new ButtonBuilder()
118 .setCustomId("back")
119 .setLabel("Back")
120 .setStyle(ButtonStyle.Secondary)
121 .setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji)
122 );
TheCodedProfb5e9d552023-01-29 15:43:26 -0500123 await interaction.editReply({
124 embeds: [
125 new EmojiEmbed()
126 .setTitle("Tracks")
127 .setDescription("Select all roles in the order you want users to gain them (Lowest to highest rank).")
128 .setStatus("Success")
129 ],
130 components: [reorderRow, buttonRow]
131 });
132 let out: StringSelectMenuInteraction | ButtonInteraction | null;
133 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000134 out = (await m.awaitMessageComponent({
TheCodedProfb5e9d552023-01-29 15:43:26 -0500135 filter: (i) => i.channel!.id === interaction.channel!.id,
136 time: 300000
Skyler Greyda16adf2023-03-05 10:22:12 +0000137 })) as StringSelectMenuInteraction | ButtonInteraction | null;
TheCodedProfb5e9d552023-01-29 15:43:26 -0500138 } catch (e) {
139 console.error(e);
140 out = null;
141 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000142 if (!out) return;
TheCodedProfb5e9d552023-01-29 15:43:26 -0500143 out.deferUpdate();
144 if (out.isButton()) return;
TheCodedProfb5e9d552023-01-29 15:43:26 -0500145 const values = out.values;
146
147 const newOrder: string[] = currentObj.map((_, i) => {
Skyler Greyda16adf2023-03-05 10:22:12 +0000148 const index = values.findIndex((v) => v === i.toString());
TheCodedProfb5e9d552023-01-29 15:43:26 -0500149 return currentObj[index];
150 }) as string[];
151
152 return newOrder;
Skyler Greyda16adf2023-03-05 10:22:12 +0000153};
TheCodedProfb5e9d552023-01-29 15:43:26 -0500154
Skyler Greyda16adf2023-03-05 10:22:12 +0000155const editTrack = async (
156 interaction: ButtonInteraction | StringSelectMenuInteraction,
157 message: Message,
158 roles: Collection<string, Role>,
159 current?: ObjectSchema
160) => {
TheCodedProf4f79da12023-01-31 16:50:37 -0500161 const isAdmin = (interaction.member!.permissions as PermissionsBitField).has("Administrator");
Skyler Greyda16adf2023-03-05 10:22:12 +0000162 if (!current) {
TheCodedProfb5e9d552023-01-29 15:43:26 -0500163 current = {
164 name: "",
165 retainPrevious: false,
TheCodedProf48865eb2023-03-05 15:25:25 -0500166 nullable: true,
TheCodedProfb5e9d552023-01-29 15:43:26 -0500167 track: [],
168 manageableBy: []
Skyler Greyda16adf2023-03-05 10:22:12 +0000169 };
TheCodedProfb5e9d552023-01-29 15:43:26 -0500170 }
TheCodedProf4f79da12023-01-31 16:50:37 -0500171
Skyler Greyda16adf2023-03-05 10:22:12 +0000172 const roleSelect = new ActionRowBuilder<RoleSelectMenuBuilder>().addComponents(
173 new RoleSelectMenuBuilder().setCustomId("addRole").setPlaceholder("Select a role to add").setDisabled(!isAdmin)
174 );
TheCodedProfb5e9d552023-01-29 15:43:26 -0500175 let closed = false;
TheCodedProf48865eb2023-03-05 15:25:25 -0500176 let previousMessage = "";
TheCodedProfb5e9d552023-01-29 15:43:26 -0500177 do {
Skyler Greyda16adf2023-03-05 10:22:12 +0000178 const editableRoles: string[] = current.track
179 .map((r) => {
180 if (
181 !(roles.get(r)!.position >= (interaction.member as GuildMember).roles.highest.position) ||
182 interaction.user.id === interaction.guild?.ownerId
183 )
184 return roles.get(r)!.name;
185 })
186 .filter((v) => v !== undefined) as string[];
187 const selectMenu = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
188 new StringSelectMenuBuilder()
189 .setCustomId("removeRole")
190 .setPlaceholder("Select a role to remove")
191 .setDisabled(!isAdmin)
192 .addOptions(
193 editableRoles.map((r, i) => {
194 return new StringSelectMenuOptionBuilder().setLabel(r).setValue(i.toString());
195 })
196 )
197 );
198 const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
199 new ButtonBuilder()
200 .setCustomId("back")
201 .setLabel("Back")
202 .setStyle(ButtonStyle.Secondary)
203 .setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji),
204 new ButtonBuilder()
205 .setCustomId("edit")
206 .setLabel("Edit Name")
207 .setStyle(ButtonStyle.Primary)
208 .setEmoji(getEmojiByName("ICONS.EDIT", "id") as APIMessageComponentEmoji),
209 new ButtonBuilder()
210 .setCustomId("reorder")
211 .setLabel("Reorder")
212 .setDisabled(!isAdmin)
213 .setStyle(ButtonStyle.Primary)
214 .setEmoji(getEmojiByName("ICONS.REORDER", "id") as APIMessageComponentEmoji),
215 new ButtonBuilder()
216 .setCustomId("retainPrevious")
217 .setLabel("Retain Previous")
218 .setStyle(current.retainPrevious ? ButtonStyle.Success : ButtonStyle.Danger)
219 .setEmoji(
220 getEmojiByName(
221 "CONTROL." + (current.retainPrevious ? "TICK" : "CROSS"),
222 "id"
223 ) as APIMessageComponentEmoji
224 ),
225 new ButtonBuilder()
226 .setCustomId("nullable")
227 .setLabel(`Role ${current.nullable ? "Not " : ""}Required`)
TheCodedProf48865eb2023-03-05 15:25:25 -0500228 .setStyle(current.nullable ? ButtonStyle.Danger : ButtonStyle.Success)
Skyler Greyda16adf2023-03-05 10:22:12 +0000229 .setEmoji(
TheCodedProf48865eb2023-03-05 15:25:25 -0500230 getEmojiByName("CONTROL." + (current.nullable ? "CROSS" : "TICK"), "id") as APIMessageComponentEmoji
Skyler Greyda16adf2023-03-05 10:22:12 +0000231 )
TheCodedProf4f79da12023-01-31 16:50:37 -0500232 );
233
PineaFanb0d0c242023-02-05 10:59:45 +0000234 const allowed: boolean[] = [];
TheCodedProfb5e9d552023-01-29 15:43:26 -0500235 for (const role of current.track) {
236 const disabled: boolean =
237 roles.get(role)!.position >= (interaction.member as GuildMember).roles.highest.position;
Skyler Greyda16adf2023-03-05 10:22:12 +0000238 allowed.push(disabled);
TheCodedProfb5e9d552023-01-29 15:43:26 -0500239 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000240 const mapped = current.track.map((role) => roles.find((aRole) => aRole.id === role)!);
TheCodedProfb5e9d552023-01-29 15:43:26 -0500241
242 const embed = new EmojiEmbed()
243 .setTitle("Tracks")
244 .setDescription(
245 `**Currently Editing:** ${current.name}\n\n` +
Skyler Greyda16adf2023-03-05 10:22:12 +0000246 `${getEmojiByName("CONTROL." + (current.nullable ? "CROSS" : "TICK"))} Members ${
247 current.nullable ? "don't " : ""
248 }need a role in this track\n` +
249 `${getEmojiByName("CONTROL." + (current.retainPrevious ? "TICK" : "CROSS"))} Members ${
250 current.retainPrevious ? "" : "don't "
TheCodedProf48865eb2023-03-05 15:25:25 -0500251 }keep all roles below their current highest\n\n` + (previousMessage ? previousMessage + "\n\n": "") +
Skyler Greyda16adf2023-03-05 10:22:12 +0000252 createVerticalTrack(
253 mapped.map((role) => renderRole(role)),
254 new Array(current.track.length).fill(false),
255 allowed
256 )
TheCodedProfb5e9d552023-01-29 15:43:26 -0500257 )
Skyler Greyda16adf2023-03-05 10:22:12 +0000258 .setStatus("Success");
TheCodedProfb5e9d552023-01-29 15:43:26 -0500259
Skyler Greyda16adf2023-03-05 10:22:12 +0000260 const comps: ActionRowBuilder<RoleSelectMenuBuilder | ButtonBuilder | StringSelectMenuBuilder>[] = [
261 roleSelect,
262 buttons
263 ];
264 if (current.track.length >= 1) comps.splice(1, 0, selectMenu);
TheCodedProfd0a166d2023-02-19 00:04:53 -0500265
Skyler Greyda16adf2023-03-05 10:22:12 +0000266 interaction.editReply({ embeds: [embed], components: comps });
TheCodedProfb5e9d552023-01-29 15:43:26 -0500267
268 let out: ButtonInteraction | RoleSelectMenuInteraction | StringSelectMenuInteraction | null;
269
270 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000271 out = (await message.awaitMessageComponent({
TheCodedProfb5e9d552023-01-29 15:43:26 -0500272 filter: (i) => i.channel!.id === interaction.channel!.id,
273 time: 300000
Skyler Greyda16adf2023-03-05 10:22:12 +0000274 })) as ButtonInteraction | RoleSelectMenuInteraction | StringSelectMenuInteraction | null;
TheCodedProfb5e9d552023-01-29 15:43:26 -0500275 } catch (e) {
276 console.error(e);
277 out = null;
278 }
279
Skyler Greyda16adf2023-03-05 10:22:12 +0000280 if (!out) return;
TheCodedProfb5e9d552023-01-29 15:43:26 -0500281 if (out.isButton()) {
Skyler Greyda16adf2023-03-05 10:22:12 +0000282 switch (out.customId) {
PineaFanb0d0c242023-02-05 10:59:45 +0000283 case "back": {
TheCodedProfd0a166d2023-02-19 00:04:53 -0500284 out.deferUpdate();
TheCodedProfb5e9d552023-01-29 15:43:26 -0500285 closed = true;
286 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000287 }
288 case "edit": {
TheCodedProfb5e9d552023-01-29 15:43:26 -0500289 current.name = (await editName(out, interaction, message, current.name))!;
290 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000291 }
292 case "reorder": {
TheCodedProfd0a166d2023-02-19 00:04:53 -0500293 out.deferUpdate();
TheCodedProfb5e9d552023-01-29 15:43:26 -0500294 current.track = (await reorderTracks(out, message, roles, current.track))!;
TheCodedProf4f79da12023-01-31 16:50:37 -0500295 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000296 }
297 case "retainPrevious": {
TheCodedProfd0a166d2023-02-19 00:04:53 -0500298 out.deferUpdate();
TheCodedProf4f79da12023-01-31 16:50:37 -0500299 current.retainPrevious = !current.retainPrevious;
300 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000301 }
302 case "nullable": {
TheCodedProfd0a166d2023-02-19 00:04:53 -0500303 out.deferUpdate();
TheCodedProf4f79da12023-01-31 16:50:37 -0500304 current.nullable = !current.nullable;
305 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000306 }
TheCodedProfb5e9d552023-01-29 15:43:26 -0500307 }
308 } else if (out.isStringSelectMenu()) {
309 out.deferUpdate();
Skyler Greyda16adf2023-03-05 10:22:12 +0000310 switch (out.customId) {
PineaFanb0d0c242023-02-05 10:59:45 +0000311 case "removeRole": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000312 const index = current.track.findIndex(
313 (v) => v === editableRoles[parseInt((out! as StringSelectMenuInteraction).values![0]!)]
314 );
TheCodedProfb5e9d552023-01-29 15:43:26 -0500315 current.track.splice(index, 1);
316 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000317 }
TheCodedProfb5e9d552023-01-29 15:43:26 -0500318 }
319 } else {
TheCodedProf48865eb2023-03-05 15:25:25 -0500320 out.deferUpdate();
Skyler Greyda16adf2023-03-05 10:22:12 +0000321 switch (out.customId) {
PineaFanb0d0c242023-02-05 10:59:45 +0000322 case "addRole": {
TheCodedProfb5e9d552023-01-29 15:43:26 -0500323 const role = out.values![0]!;
TheCodedProf48865eb2023-03-05 15:25:25 -0500324 const roleObj = roles.get(role)!;
325 if (roleObj.position >= (interaction.member as GuildMember).roles.highest.position){
326 previousMessage = "You can't add a role that is higher than your highest role.";
TheCodedProfd0a166d2023-02-19 00:04:53 -0500327 } else {
TheCodedProf48865eb2023-03-05 15:25:25 -0500328 if (!current.track.includes(role)) {
329 current.track.push(role);
330 await interaction.editReply({ embeds: LoadingEmbed, components: [] });
331 } else {
332 previousMessage = "That role is already on this track";
333 }
TheCodedProfb5e9d552023-01-29 15:43:26 -0500334 }
TheCodedProfb5e9d552023-01-29 15:43:26 -0500335 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000336 }
TheCodedProfb5e9d552023-01-29 15:43:26 -0500337 }
338 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000339 } while (!closed);
TheCodedProfb5e9d552023-01-29 15:43:26 -0500340 return current;
Skyler Greyda16adf2023-03-05 10:22:12 +0000341};
TheCodedProfa112f612023-01-28 18:06:45 -0500342
343const callback = async (interaction: CommandInteraction) => {
Skyler Greyda16adf2023-03-05 10:22:12 +0000344 const m = await interaction.reply({ embeds: LoadingEmbed, fetchReply: true, ephemeral: true });
TheCodedProfb5e9d552023-01-29 15:43:26 -0500345 const config = await client.database.guilds.read(interaction.guild!.id);
346 const tracks: ObjectSchema[] = config.tracks;
347 const roles = await interaction.guild!.roles.fetch();
TheCodedProfb5e9d552023-01-29 15:43:26 -0500348
349 let page = 0;
350 let closed = false;
351 let modified = false;
352
353 do {
Skyler Greyda16adf2023-03-05 10:22:12 +0000354 const embed = new EmojiEmbed().setTitle("Track Settings").setEmoji("TRACKS.ICON").setStatus("Success");
TheCodedProfb5e9d552023-01-29 15:43:26 -0500355 const noTracks = config.tracks.length === 0;
356 let current: ObjectSchema;
357
Skyler Greyda16adf2023-03-05 10:22:12 +0000358 const pageSelect = new StringSelectMenuBuilder().setCustomId("page").setPlaceholder("Select a track to manage");
TheCodedProfb5e9d552023-01-29 15:43:26 -0500359 const actionSelect = new StringSelectMenuBuilder()
360 .setCustomId("action")
361 .setPlaceholder("Perform an action")
362 .addOptions(
363 new StringSelectMenuOptionBuilder()
364 .setLabel("Edit")
365 .setDescription("Edit this track")
366 .setValue("edit")
367 .setEmoji(getEmojiByName("ICONS.EDIT", "id") as APIMessageComponentEmoji),
368 new StringSelectMenuOptionBuilder()
369 .setLabel("Delete")
370 .setDescription("Delete this track")
371 .setValue("delete")
372 .setEmoji(getEmojiByName("TICKETS.ISSUE", "id") as APIMessageComponentEmoji)
TheCodedProfb5e9d552023-01-29 15:43:26 -0500373 );
Skyler Greyda16adf2023-03-05 10:22:12 +0000374 const buttonRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
375 new ButtonBuilder()
376 .setCustomId("back")
377 .setStyle(ButtonStyle.Primary)
378 .setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji)
379 .setDisabled(page === 0),
380 new ButtonBuilder()
381 .setCustomId("next")
382 .setEmoji(getEmojiByName("CONTROL.RIGHT", "id") as APIMessageComponentEmoji)
383 .setStyle(ButtonStyle.Primary)
384 .setDisabled(page === tracks.length - 1),
385 new ButtonBuilder()
386 .setCustomId("add")
387 .setLabel("New Track")
388 .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id") as APIMessageComponentEmoji)
389 .setStyle(ButtonStyle.Secondary)
390 .setDisabled(Object.keys(tracks).length >= 24),
391 new ButtonBuilder()
392 .setCustomId("save")
393 .setLabel("Save")
394 .setEmoji(getEmojiByName("ICONS.SAVE", "id") as APIMessageComponentEmoji)
395 .setStyle(ButtonStyle.Success)
396 .setDisabled(!modified)
397 );
398 if (noTracks) {
399 embed.setDescription(
400 "No tracks have been set up yet. Use the button below to add one.\n\n" +
401 createPageIndicator(1, 1, undefined, true)
TheCodedProfb5e9d552023-01-29 15:43:26 -0500402 );
403 pageSelect.setDisabled(true);
404 actionSelect.setDisabled(true);
Skyler Greyda16adf2023-03-05 10:22:12 +0000405 pageSelect.addOptions(new StringSelectMenuOptionBuilder().setLabel("No tracks").setValue("none"));
TheCodedProfb5e9d552023-01-29 15:43:26 -0500406 } else {
407 page = Math.min(page, Object.keys(tracks).length - 1);
408 current = tracks[page]!;
Skyler Greyda16adf2023-03-05 10:22:12 +0000409 const mapped = current.track.map((role) => roles.find((aRole) => aRole.id === role)!);
410 embed.setDescription(
411 `**Currently Editing:** ${current.name}\n\n` +
412 `${getEmojiByName("CONTROL." + (current.nullable ? "CROSS" : "TICK"))} Members ${
413 current.nullable ? "don't " : ""
414 }need a role in this track\n` +
415 `${getEmojiByName("CONTROL." + (current.retainPrevious ? "TICK" : "CROSS"))} Members ${
416 current.retainPrevious ? "" : "don't "
417 }keep all roles below their current highest\n\n` +
418 createVerticalTrack(
419 mapped.map((role) => renderRole(role)),
420 new Array(current.track.length).fill(false)
421 ) +
422 `\n${createPageIndicator(config.tracks.length, page)}`
TheCodedProfb5e9d552023-01-29 15:43:26 -0500423 );
424
425 pageSelect.addOptions(
426 tracks.map((key: ObjectSchema, index) => {
427 return new StringSelectMenuOptionBuilder()
428 .setLabel(ellipsis(key.name, 50))
429 .setDescription(ellipsis(roles.get(key.track[0]!)?.name!, 50))
430 .setValue(index.toString());
431 })
432 );
TheCodedProfb5e9d552023-01-29 15:43:26 -0500433 }
434
Skyler Greyda16adf2023-03-05 10:22:12 +0000435 await interaction.editReply({
436 embeds: [embed],
437 components: [
438 new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(actionSelect),
439 new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(pageSelect),
440 buttonRow
441 ]
442 });
TheCodedProfb5e9d552023-01-29 15:43:26 -0500443 let i: StringSelectMenuInteraction | ButtonInteraction;
444 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000445 i = (await m.awaitMessageComponent({
446 time: 300000,
447 filter: (i) =>
448 i.user.id === interaction.user.id && i.message.id === m.id && i.channelId === interaction.channelId
449 })) as ButtonInteraction | StringSelectMenuInteraction;
TheCodedProfb5e9d552023-01-29 15:43:26 -0500450 } catch (e) {
451 closed = true;
PineaFanb0d0c242023-02-05 10:59:45 +0000452 continue;
TheCodedProfb5e9d552023-01-29 15:43:26 -0500453 }
454
455 await i.deferUpdate();
456 if (i.isButton()) {
457 switch (i.customId) {
PineaFanb0d0c242023-02-05 10:59:45 +0000458 case "back": {
TheCodedProfb5e9d552023-01-29 15:43:26 -0500459 page--;
460 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000461 }
462 case "next": {
TheCodedProfb5e9d552023-01-29 15:43:26 -0500463 page++;
464 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000465 }
466 case "add": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000467 const newPage = await editTrack(i, m, roles);
468 if (!newPage) break;
TheCodedProfb5e9d552023-01-29 15:43:26 -0500469 tracks.push();
470 page = tracks.length - 1;
471 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000472 }
473 case "save": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000474 client.database.guilds.write(interaction.guild!.id, { tracks: tracks });
TheCodedProfb5e9d552023-01-29 15:43:26 -0500475 modified = false;
Skyler Grey16ecb172023-03-05 07:30:32 +0000476 await client.memory.forceUpdate(interaction.guild!.id);
TheCodedProfb5e9d552023-01-29 15:43:26 -0500477 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000478 }
TheCodedProfb5e9d552023-01-29 15:43:26 -0500479 }
480 } else if (i.isStringSelectMenu()) {
481 switch (i.customId) {
PineaFanb0d0c242023-02-05 10:59:45 +0000482 case "action": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000483 switch (i.values[0]) {
PineaFanb0d0c242023-02-05 10:59:45 +0000484 case "edit": {
485 const edited = await editTrack(i, m, roles, current!);
Skyler Greyda16adf2023-03-05 10:22:12 +0000486 if (!edited) break;
TheCodedProfb5e9d552023-01-29 15:43:26 -0500487 tracks[page] = edited;
488 modified = true;
489 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000490 }
491 case "delete": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000492 if (page === 0 && tracks.keys.length - 1 > 0) page++;
TheCodedProfb5e9d552023-01-29 15:43:26 -0500493 else page--;
494 tracks.splice(page, 1);
495 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000496 }
TheCodedProfb5e9d552023-01-29 15:43:26 -0500497 }
498 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000499 }
500 case "page": {
TheCodedProfb5e9d552023-01-29 15:43:26 -0500501 page = parseInt(i.values[0]!);
502 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000503 }
TheCodedProfb5e9d552023-01-29 15:43:26 -0500504 }
505 }
TheCodedProf01cba762023-02-18 15:55:05 -0500506 } while (!closed);
Skyler Greyda16adf2023-03-05 10:22:12 +0000507 await interaction.deleteReply();
508};
TheCodedProfa112f612023-01-28 18:06:45 -0500509
510const check = (interaction: CommandInteraction, _partial: boolean = false) => {
511 const member = interaction.member as GuildMember;
512 if (!member.permissions.has("ManageRoles"))
513 return "You must have the *Manage Server* permission to use this command";
514 return true;
515};
516
517export { command };
518export { callback };
519export { check };