blob: fe829b921c4b052a7692ed2f1615f8284e8cdf97 [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
Skyler Greyda16adf2023-03-05 10:22:12 +000025const command = new SlashCommandBuilder().setName("help").setDescription("Shows help for commands");
pineafan4f164f32022-02-26 22:07:12 +000026
Skyler Greyda16adf2023-03-05 10:22:12 +000027const styles: Record<string, { emoji: string }> = {
28 help: { emoji: "NUCLEUS.LOGO" },
29 mod: { emoji: "PUNISH.BAN.RED" },
30 nucleus: { emoji: "NUCLEUS.LOGO" },
31 privacy: { emoji: "NUCLEUS.LOGO" },
32 role: { emoji: "GUILD.ROLES.DELETE" },
33 rolemenu: { emoji: "GUILD.ROLES.DELETE" },
34 server: { emoji: "GUILD.RED" },
35 settings: { emoji: "GUILD.SETTINGS.RED" },
36 tag: { emoji: "PUNISH.NICKNAME.RED" },
37 tags: { emoji: "PUNISH.NICKNAME.RED" },
38 ticket: { emoji: "GUILD.TICKET.CLOSE" },
39 user: { emoji: "MEMBER.LEAVE" },
40 verify: { emoji: "CONTROL.REDTICK" }
41};
TheCodedProff86ba092023-01-27 17:10:07 -050042
pineafan63fc5e22022-08-04 22:04:10 +010043const callback = async (interaction: CommandInteraction): Promise<void> => {
TheCodedProff86ba092023-01-27 17:10:07 -050044 const m = await interaction.reply({ embeds: LoadingEmbed, ephemeral: true, fetchReply: true });
TheCodedProf1c3ad3c2023-01-25 17:58:36 -050045 const commands = client.fetchedCommands;
46
TheCodedProf1c3ad3c2023-01-25 17:58:36 -050047 let closed = false;
Skyler Greyda16adf2023-03-05 10:22:12 +000048 let currentPath: [string, string, string] = ["", "", ""];
TheCodedProf1c3ad3c2023-01-25 17:58:36 -050049 do {
Skyler Greyda16adf2023-03-05 10:22:12 +000050 const commandRow = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
51 new StringSelectMenuBuilder()
52 .setCustomId("commandRow")
53 .setPlaceholder("Select a command")
54 .addOptions(
55 ...commands
56 .filter((command) => command.type === ApplicationCommandType.ChatInput)
57 .map((command) => {
TheCodedProff86ba092023-01-27 17:10:07 -050058 const builder = new StringSelectMenuOptionBuilder()
59 .setLabel(capitalize(command.name))
60 .setValue(command.name)
61 .setDescription(command.description)
Skyler Greyda16adf2023-03-05 10:22:12 +000062 .setDefault(currentPath[0] === command.name);
63 if (styles[command.name])
64 builder.setEmoji(
65 getEmojiByName(styles[command.name]!.emoji, "id") as APIMessageComponentEmoji
66 );
67 return builder;
TheCodedProff86ba092023-01-27 17:10:07 -050068 })
Skyler Greyda16adf2023-03-05 10:22:12 +000069 )
TheCodedProff86ba092023-01-27 17:10:07 -050070 );
Skyler Greyda16adf2023-03-05 10:22:12 +000071 const subcommandGroupRow = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
72 new StringSelectMenuBuilder().setCustomId("subcommandGroupRow")
73 );
74 const subcommandRow = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
75 new StringSelectMenuBuilder().setCustomId("subcommandRow")
76 );
77 const embed = new EmojiEmbed().setTitle("Help").setStatus("Danger").setEmoji("NUCLEUS.LOGO");
TheCodedProf1c3ad3c2023-01-25 17:58:36 -050078
Skyler Greyda16adf2023-03-05 10:22:12 +000079 if (currentPath[0] === "" || currentPath[0] === "help") {
TheCodedProf1c3ad3c2023-01-25 17:58:36 -050080 embed.setDescription(
TheCodedProff86ba092023-01-27 17:10:07 -050081 `Welcome to Nucleus\n\n` +
Skyler Greyda16adf2023-03-05 10:22:12 +000082 `Select a command to get started${
83 (interaction.member?.permissions as PermissionsBitField).has("ManageGuild")
84 ? `, or run ${getCommandMentionByName("nucleus/guide")} for commands to set up your server`
85 : ``
TheCodedProf1f675042023-02-16 17:01:29 -050086 }\n\n\n` +
Skyler Greyda16adf2023-03-05 10:22:12 +000087 `Nucleus is fully [open source](https://github.com/clicksminuteper/Nucleus), and all currently free features will remain free forever.\n\n` +
88 `You can invite Nucleus to your server using ${getCommandMentionByName("nucleus/invite")}`
89 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -050090 } else {
Skyler Greyda16adf2023-03-05 10:22:12 +000091 const currentData = getCommandByName(
92 currentPath.filter((value) => value !== "" && value !== "none").join("/")
93 );
PineaFanb0d0c242023-02-05 10:59:45 +000094 const current = commands.find((command) => command.name === currentPath[0])!;
TheCodedProff86ba092023-01-27 17:10:07 -050095
Skyler Greyda16adf2023-03-05 10:22:12 +000096 let optionString = ``;
TheCodedProff86ba092023-01-27 17:10:07 -050097 let options: (ApplicationCommandOption & {
98 nameLocalized?: string;
99 descriptionLocalized?: string;
100 })[] = [];
TheCodedProf1807fb32023-02-20 14:33:48 -0500101 //options
Skyler Greyda16adf2023-03-05 10:22:12 +0000102 if (
103 currentPath[1] !== "" &&
104 currentPath[1] !== "none" &&
105 currentPath[2] !== "" &&
106 currentPath[2] !== "none"
107 ) {
108 const Op = current.options.find(
109 (option) => option.name === currentPath[1]
110 )! as ApplicationCommandSubGroup;
111 const Op2 = Op.options!.find((option) => option.name === currentPath[2])!;
112 options = Op2.options ?? [];
113 } else if (currentPath[1] !== "" && currentPath[1] !== "none") {
114 let Op = current.options.find((option) => option.name === currentPath[1])!;
115 if (Op.type === ApplicationCommandOptionType.SubcommandGroup) {
116 options = [];
TheCodedProfcd187ee2023-01-27 20:46:12 -0500117 } else {
Skyler Greyda16adf2023-03-05 10:22:12 +0000118 Op = Op as ApplicationCommandSubCommand;
119 options = Op.options ?? [];
TheCodedProfcd187ee2023-01-27 20:46:12 -0500120 }
121 } else {
Skyler Greyda16adf2023-03-05 10:22:12 +0000122 options = current.options.filter(
123 (option) =>
124 option.type !== ApplicationCommandOptionType.SubcommandGroup &&
125 option.type !== ApplicationCommandOptionType.Subcommand
126 );
TheCodedProfcd187ee2023-01-27 20:46:12 -0500127 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000128 for (const option of options) {
129 optionString += `> ${option.name} (${ApplicationCommandOptionType[option.type]})- ${
130 option.description
131 }\n`;
TheCodedProff86ba092023-01-27 17:10:07 -0500132 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000133 const APICommand =
134 client.commands[
135 "commands/" + currentPath.filter((value) => value !== "" && value !== "none").join("/")
136 ]![0];
TheCodedProff86ba092023-01-27 17:10:07 -0500137 let allowedToRun = true;
Skyler Greyda16adf2023-03-05 10:22:12 +0000138 if (APICommand?.check) {
139 allowedToRun = await APICommand.check(interaction as Interaction, true);
TheCodedProff86ba092023-01-27 17:10:07 -0500140 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500141 embed.setDescription(
Skyler Greyda16adf2023-03-05 10:22:12 +0000142 `${getEmojiByName(styles[currentPath[0]]!.emoji)} **${capitalize(currentData.name)}**\n> ${
143 currentData.mention
144 }\n\n` +
145 `> ${currentData.description}\n\n` +
146 (APICommand
147 ? `${getEmojiByName(allowedToRun ? "CONTROL.TICK" : "CONTROL.CROSS")} You ${
148 allowedToRun ? "" : "don't "
149 }` + `have permission to use this command\n\n`
150 : "") +
151 (optionString.length > 0 ? "**Options:**\n" + optionString : "")
152 );
153 const subcommands = current.options.filter(
154 (option) => option.type === ApplicationCommandOptionType.Subcommand
155 );
156 const subcommandGroups = current.options.filter(
157 (option) => option.type === ApplicationCommandOptionType.SubcommandGroup
158 );
TheCodedProff86ba092023-01-27 17:10:07 -0500159
Skyler Greyda16adf2023-03-05 10:22:12 +0000160 if (subcommandGroups.length > 0) {
161 subcommandGroupRow.components[0]!.addOptions(
162 new StringSelectMenuOptionBuilder()
163 .setLabel("Select a subcommand")
164 .setValue("none")
165 .setDefault(currentPath[1] === "none"),
166 ...subcommandGroups.map((option) =>
167 new StringSelectMenuOptionBuilder()
168 .setLabel(capitalize(option.name))
169 .setValue(option.name)
170 .setDefault(currentPath[1] === option.name)
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500171 )
Skyler Greyda16adf2023-03-05 10:22:12 +0000172 );
173 if (
174 subcommandGroupRow.components[0]!.options.find(
175 (option) => option.data.default && option.data.value !== "none"
176 )
177 ) {
178 const subsubcommands =
179 (
180 subcommandGroups.find(
181 (option) => option.name === currentPath[1]
182 )! as ApplicationCommandSubGroup
183 ).options ?? [];
184 subcommandRow.components[0]!.addOptions(
185 new StringSelectMenuOptionBuilder()
186 .setLabel("Select a subcommand")
187 .setValue("none")
188 .setDefault(currentPath[2] === "none"),
189 ...subsubcommands.map((option) =>
190 new StringSelectMenuOptionBuilder()
191 .setLabel(capitalize(option.name))
192 .setValue(option.name)
193 .setDefault(currentPath[2] === option.name)
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500194 )
Skyler Greyda16adf2023-03-05 10:22:12 +0000195 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500196 }
197 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000198 if (subcommands.length > 0) {
199 subcommandGroupRow.components[0]!.addOptions(
200 ...subcommands.map((option) =>
201 new StringSelectMenuOptionBuilder()
202 .setLabel(capitalize(option.name))
203 .setValue(option.name)
204 .setDefault(currentPath[1] === option.name)
TheCodedProff86ba092023-01-27 17:10:07 -0500205 )
Skyler Greyda16adf2023-03-05 10:22:12 +0000206 );
TheCodedProff86ba092023-01-27 17:10:07 -0500207 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500208 }
TheCodedProff86ba092023-01-27 17:10:07 -0500209
PineaFanb0d0c242023-02-05 10:59:45 +0000210 const cmps = [commandRow];
Skyler Greyda16adf2023-03-05 10:22:12 +0000211 if (subcommandGroupRow.components[0]!.options.length > 0) cmps.push(subcommandGroupRow);
212 if (subcommandRow.components[0]!.options.length > 0) cmps.push(subcommandRow);
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500213
214 await interaction.editReply({ embeds: [embed], components: cmps });
215
TheCodedProff86ba092023-01-27 17:10:07 -0500216 let i: StringSelectMenuInteraction;
217 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000218 i = await m.awaitMessageComponent<ComponentType.StringSelect>({
219 filter: (newInteraction) => interaction.user.id === newInteraction.user.id,
220 time: 300000
221 });
TheCodedProff86ba092023-01-27 17:10:07 -0500222 } catch (e) {
223 closed = true;
PineaFanb0d0c242023-02-05 10:59:45 +0000224 continue;
TheCodedProff86ba092023-01-27 17:10:07 -0500225 }
226 await i.deferUpdate();
PineaFanb0d0c242023-02-05 10:59:45 +0000227 const value = i.values[0]!;
Skyler Greyda16adf2023-03-05 10:22:12 +0000228 switch (i.customId) {
PineaFanb0d0c242023-02-05 10:59:45 +0000229 case "commandRow": {
TheCodedProff86ba092023-01-27 17:10:07 -0500230 currentPath = [value, "", ""];
231 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000232 }
233 case "subcommandGroupRow": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000234 currentPath = [currentPath[0], value, ""];
TheCodedProff86ba092023-01-27 17:10:07 -0500235 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000236 }
237 case "subcommandRow": {
TheCodedProff86ba092023-01-27 17:10:07 -0500238 currentPath[2] = value;
239 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000240 }
TheCodedProff86ba092023-01-27 17:10:07 -0500241 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500242 } while (!closed);
pineafan63fc5e22022-08-04 22:04:10 +0100243};
pineafan4f164f32022-02-26 22:07:12 +0000244
TheCodedProff86ba092023-01-27 17:10:07 -0500245export { command as command };
pineafan4f164f32022-02-26 22:07:12 +0000246export { callback };