blob: e34500f59168a8ad3ba4e73fb2fc70ba980d37f4 [file] [log] [blame]
TheCodedProff86ba092023-01-27 17:10:07 -05001import {
2 ActionRowBuilder,
3 CommandInteraction,
4 StringSelectMenuBuilder,
5 ApplicationCommandOptionType,
6 ApplicationCommandType,
7 StringSelectMenuOptionBuilder,
8 SlashCommandBuilder,
9 StringSelectMenuInteraction,
10 ComponentType,
11 APIMessageComponentEmoji,
12 ApplicationCommandSubGroup,
13 PermissionsBitField,
14 Interaction,
15 ApplicationCommandOption,
16 ApplicationCommandSubCommand
17} from "discord.js";
TheCodedProf1c3ad3c2023-01-25 17:58:36 -050018import client from "../utils/client.js";
19import EmojiEmbed from "../utils/generateEmojiEmbed.js";
20import { LoadingEmbed } from "../utils/defaults.js";
TheCodedProff86ba092023-01-27 17:10:07 -050021import { capitalize } from "../utils/generateKeyValueList.js";
22import { getCommandByName, getCommandMentionByName } from "../utils/getCommandDataByName.js";
23import getEmojiByName from "../utils/getEmojiByName.js";
pineafan4f164f32022-02-26 22:07:12 +000024
PineaFan752af462022-12-31 21:59:38 +000025const command = new SlashCommandBuilder()
26 .setName("help")
27 .setDescription("Shows help for commands");
pineafan4f164f32022-02-26 22:07:12 +000028
TheCodedProff86ba092023-01-27 17:10:07 -050029const styles: Record<string, {emoji: string}> = {
30 "help": {emoji: "NUCLEUS.LOGO"},
31 "mod": {emoji: "PUNISH.BAN.RED"},
32 "nucleus": {emoji: "NUCLEUS.LOGO"},
33 "privacy": {emoji: "NUCLEUS.LOGO"},
34 "role": {emoji: "GUILD.ROLES.DELETE"},
35 "rolemenu": {emoji: "GUILD.ROLES.DELETE"},
36 "server": {emoji: "GUILD.RED"},
37 "settings": {emoji: "GUILD.SETTINGS.RED"},
38 "tag": {emoji: "PUNISH.NICKNAME.RED"},
39 "tags": {emoji: "PUNISH.NICKNAME.RED"},
40 "ticket": {emoji: "GUILD.TICKET.CLOSE"},
41 "user": {emoji: "MEMBER.LEAVE"},
TheCodedProfa112f612023-01-28 18:06:45 -050042 "verify": {emoji: "CONTROL.REDTICK"}
TheCodedProff86ba092023-01-27 17:10:07 -050043}
44
pineafan63fc5e22022-08-04 22:04:10 +010045const callback = async (interaction: CommandInteraction): Promise<void> => {
TheCodedProff86ba092023-01-27 17:10:07 -050046 const m = await interaction.reply({ embeds: LoadingEmbed, ephemeral: true, fetchReply: true });
TheCodedProf1c3ad3c2023-01-25 17:58:36 -050047 const commands = client.fetchedCommands;
48
TheCodedProf1c3ad3c2023-01-25 17:58:36 -050049 let closed = false;
TheCodedProfa112f612023-01-28 18:06:45 -050050 let currentPath: [string, string, string] = ["", "", ""]
TheCodedProf1c3ad3c2023-01-25 17:58:36 -050051 do {
TheCodedProff86ba092023-01-27 17:10:07 -050052 const commandRow = new ActionRowBuilder<StringSelectMenuBuilder>()
53 .addComponents(
54 new StringSelectMenuBuilder()
55 .setCustomId("commandRow")
56 .setPlaceholder("Select a command")
57 .addOptions(
58 ...commands.filter(command => command.type === ApplicationCommandType.ChatInput).map((command) => {
59 const builder = new StringSelectMenuOptionBuilder()
60 .setLabel(capitalize(command.name))
61 .setValue(command.name)
62 .setDescription(command.description)
63 .setDefault(currentPath[0] === command.name)
64 if (styles[command.name]) builder.setEmoji(getEmojiByName(styles[command.name]!.emoji, "id") as APIMessageComponentEmoji)
65 return builder
66 })
67 )
68 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -050069 const subcommandGroupRow = new ActionRowBuilder<StringSelectMenuBuilder>()
70 .addComponents(
71 new StringSelectMenuBuilder()
72 .setCustomId("subcommandGroupRow")
73 );
74 const subcommandRow = new ActionRowBuilder<StringSelectMenuBuilder>()
75 .addComponents(
76 new StringSelectMenuBuilder()
77 .setCustomId("subcommandRow")
78 );
79 const embed = new EmojiEmbed()
80 .setTitle("Help")
TheCodedProff86ba092023-01-27 17:10:07 -050081 .setStatus("Danger")
82 .setEmoji("NUCLEUS.LOGO")
TheCodedProf1c3ad3c2023-01-25 17:58:36 -050083
TheCodedProff86ba092023-01-27 17:10:07 -050084 if(currentPath[0] === "" || currentPath[0] === "help") {
TheCodedProf1c3ad3c2023-01-25 17:58:36 -050085 embed.setDescription(
TheCodedProff86ba092023-01-27 17:10:07 -050086 `Welcome to Nucleus\n\n` +
87 `Select a command to get started${(interaction.member?.permissions as PermissionsBitField).has("ManageGuild") ? `, or run ${getCommandMentionByName("nucleus/guide")} for commands to set up your server` : ``}` // FIXME
TheCodedProf1c3ad3c2023-01-25 17:58:36 -050088 )
89 } else {
PineaFanb0d0c242023-02-05 10:59:45 +000090 const currentData = getCommandByName(currentPath.filter(value => value !== "" && value !== "none").join('/'));
91 const current = commands.find((command) => command.name === currentPath[0])!;
TheCodedProff86ba092023-01-27 17:10:07 -050092
93 let optionString = ``
94 let options: (ApplicationCommandOption & {
95 nameLocalized?: string;
96 descriptionLocalized?: string;
97 })[] = [];
98 //options
TheCodedProf8c295232023-01-27 20:52:24 -050099 if(currentPath[1] !== "" && currentPath[1] !== "none" && currentPath[2] !== "" && currentPath[2] !== "none") {
PineaFanb0d0c242023-02-05 10:59:45 +0000100 const Op = current.options.find(option => option.name === currentPath[1])! as ApplicationCommandSubGroup
101 const Op2 = Op.options!.find(option => option.name === currentPath[2])!
102 options = Op2.options ?? []
TheCodedProf8c295232023-01-27 20:52:24 -0500103 } else if(currentPath[1] !== "" && currentPath[1] !== "none") {
TheCodedProfcd187ee2023-01-27 20:46:12 -0500104 let Op = current.options.find(option => option.name === currentPath[1])!
105 if(Op.type === ApplicationCommandOptionType.SubcommandGroup) {
106 options = []
107 } else {
108 Op = Op as ApplicationCommandSubCommand
PineaFanb0d0c242023-02-05 10:59:45 +0000109 options = Op.options ?? []
TheCodedProfcd187ee2023-01-27 20:46:12 -0500110 }
111 } else {
112 options = current.options.filter(option => option.type !== ApplicationCommandOptionType.SubcommandGroup && option.type !== ApplicationCommandOptionType.Subcommand) || [];
113 }
TheCodedProff86ba092023-01-27 17:10:07 -0500114 for(const option of options) {
TheCodedProf8c295232023-01-27 20:52:24 -0500115 optionString += `> ${option.name} (${ApplicationCommandOptionType[option.type]})- ${option.description}\n`
TheCodedProff86ba092023-01-27 17:10:07 -0500116 }
117 const APICommand = client.commands["commands/" + currentPath.filter(value => value !== "" && value !== "none").join("/")]![0]
118 let allowedToRun = true;
119 if(APICommand?.check) {
PineaFanb0d0c242023-02-05 10:59:45 +0000120 allowedToRun = await APICommand.check(interaction as Interaction, true)
TheCodedProff86ba092023-01-27 17:10:07 -0500121 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500122 embed.setDescription(
TheCodedProff86ba092023-01-27 17:10:07 -0500123 `${getEmojiByName(styles[currentPath[0]]!.emoji)} **${capitalize(currentData.name)}**\n> ${currentData.mention}\n\n` +
124 `> ${currentData.description}\n\n` +
125 (APICommand ? (`${getEmojiByName(allowedToRun ? "CONTROL.TICK" : "CONTROL.CROSS")} You ${allowedToRun ? "" : "don't "}` +
126 `have permission to use this command\n\n`) : "") +
127 ((optionString.length > 0) ? "**Options:**\n" + optionString : "")
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500128 )
TheCodedProf92a003c2023-01-25 18:01:21 -0500129 const subcommands = current.options.filter((option) => option.type === ApplicationCommandOptionType.Subcommand);
130 const subcommandGroups = current.options.filter((option) => option.type === ApplicationCommandOptionType.SubcommandGroup);
TheCodedProff86ba092023-01-27 17:10:07 -0500131
TheCodedProf92a003c2023-01-25 18:01:21 -0500132 if(subcommandGroups.length > 0) {
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500133 subcommandGroupRow.components[0]!
134 .addOptions(
TheCodedProff86ba092023-01-27 17:10:07 -0500135 new StringSelectMenuOptionBuilder().setLabel("Select a subcommand").setValue("none").setDefault(currentPath[1] === "none"),
136 ...subcommandGroups.map((option) => new StringSelectMenuOptionBuilder().setLabel(capitalize(option.name)).setValue(option.name).setDefault(currentPath[1] === option.name))
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500137 )
TheCodedProffe0a7862023-01-27 20:36:35 -0500138 if(subcommandGroupRow.components[0]!.options.find((option) => option.data.default && option.data.value !== "none")) {
PineaFanb0d0c242023-02-05 10:59:45 +0000139 const subsubcommands = (subcommandGroups.find((option) => option.name === currentPath[1])! as ApplicationCommandSubGroup).options?.filter((option) => option.type === ApplicationCommandOptionType.Subcommand) ?? [];
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500140 subcommandRow.components[0]!
141 .addOptions(
TheCodedProff86ba092023-01-27 17:10:07 -0500142 new StringSelectMenuOptionBuilder().setLabel("Select a subcommand").setValue("none").setDefault(currentPath[2] === "none"),
143 ...subsubcommands.map((option) => new StringSelectMenuOptionBuilder().setLabel(capitalize(option.name)).setValue(option.name).setDefault(currentPath[2] === option.name))
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500144 )
145 }
146 }
TheCodedProff86ba092023-01-27 17:10:07 -0500147 if(subcommands.length > 0) {
148 subcommandGroupRow.components[0]!
149 .addOptions(
150 ...subcommands.map((option) => new StringSelectMenuOptionBuilder().setLabel(capitalize(option.name)).setValue(option.name).setDefault(currentPath[1] === option.name))
151 )
152 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500153 }
TheCodedProff86ba092023-01-27 17:10:07 -0500154
PineaFanb0d0c242023-02-05 10:59:45 +0000155 const cmps = [commandRow];
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500156 if(subcommandGroupRow.components[0]!.options.length > 0) cmps.push(subcommandGroupRow);
157 if(subcommandRow.components[0]!.options.length > 0) cmps.push(subcommandRow);
158
159 await interaction.editReply({ embeds: [embed], components: cmps });
160
TheCodedProff86ba092023-01-27 17:10:07 -0500161 let i: StringSelectMenuInteraction;
162 try {
163 i = await m.awaitMessageComponent<ComponentType.StringSelect>({filter: (newInteraction) => interaction.user.id === newInteraction.user.id,time: 300000})
164 } catch (e) {
165 closed = true;
PineaFanb0d0c242023-02-05 10:59:45 +0000166 continue;
TheCodedProff86ba092023-01-27 17:10:07 -0500167 }
168 await i.deferUpdate();
PineaFanb0d0c242023-02-05 10:59:45 +0000169 const value = i.values[0]!;
TheCodedProff86ba092023-01-27 17:10:07 -0500170 switch(i.customId) {
PineaFanb0d0c242023-02-05 10:59:45 +0000171 case "commandRow": {
TheCodedProff86ba092023-01-27 17:10:07 -0500172 currentPath = [value, "", ""];
173 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000174 }
175 case "subcommandGroupRow": {
TheCodedProff86ba092023-01-27 17:10:07 -0500176 currentPath = [currentPath[0], value , ""];
177 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000178 }
179 case "subcommandRow": {
TheCodedProff86ba092023-01-27 17:10:07 -0500180 currentPath[2] = value;
181 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000182 }
TheCodedProff86ba092023-01-27 17:10:07 -0500183 }
TheCodedProff86ba092023-01-27 17:10:07 -0500184
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500185 } while (!closed);
pineafan63fc5e22022-08-04 22:04:10 +0100186};
pineafan4f164f32022-02-26 22:07:12 +0000187
pineafan4f164f32022-02-26 22:07:12 +0000188
TheCodedProff86ba092023-01-27 17:10:07 -0500189export { command as command };
pineafan4f164f32022-02-26 22:07:12 +0000190export { callback };