blob: 041fb47b8cad5ec2fd073e17ff64f3073d309100 [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
TheCodedProf35e73712023-03-10 17:35:35 -050025const command = new SlashCommandBuilder().setName("help").setDescription("Shows help for commands").setDMPermission(true);
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` +
TheCodedProfe915a382023-03-05 16:44:29 -050088 `You can invite Nucleus to your server using ${getCommandMentionByName("nucleus/invite")}\n` +
89 `Our support server can be found [here](https://discord.gg/bPaNnxe), and we can be emailed at support@clicks.codes`
Skyler Greyda16adf2023-03-05 10:22:12 +000090 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -050091 } else {
Skyler Greyda16adf2023-03-05 10:22:12 +000092 const currentData = getCommandByName(
93 currentPath.filter((value) => value !== "" && value !== "none").join("/")
94 );
PineaFanb0d0c242023-02-05 10:59:45 +000095 const current = commands.find((command) => command.name === currentPath[0])!;
TheCodedProff86ba092023-01-27 17:10:07 -050096
Skyler Greyda16adf2023-03-05 10:22:12 +000097 let optionString = ``;
TheCodedProff86ba092023-01-27 17:10:07 -050098 let options: (ApplicationCommandOption & {
99 nameLocalized?: string;
100 descriptionLocalized?: string;
101 })[] = [];
TheCodedProf1807fb32023-02-20 14:33:48 -0500102 //options
Skyler Greyda16adf2023-03-05 10:22:12 +0000103 if (
104 currentPath[1] !== "" &&
105 currentPath[1] !== "none" &&
106 currentPath[2] !== "" &&
107 currentPath[2] !== "none"
108 ) {
109 const Op = current.options.find(
110 (option) => option.name === currentPath[1]
111 )! as ApplicationCommandSubGroup;
112 const Op2 = Op.options!.find((option) => option.name === currentPath[2])!;
113 options = Op2.options ?? [];
114 } else if (currentPath[1] !== "" && currentPath[1] !== "none") {
115 let Op = current.options.find((option) => option.name === currentPath[1])!;
116 if (Op.type === ApplicationCommandOptionType.SubcommandGroup) {
117 options = [];
TheCodedProfcd187ee2023-01-27 20:46:12 -0500118 } else {
Skyler Greyda16adf2023-03-05 10:22:12 +0000119 Op = Op as ApplicationCommandSubCommand;
120 options = Op.options ?? [];
TheCodedProfcd187ee2023-01-27 20:46:12 -0500121 }
122 } else {
Skyler Greyda16adf2023-03-05 10:22:12 +0000123 options = current.options.filter(
124 (option) =>
125 option.type !== ApplicationCommandOptionType.SubcommandGroup &&
126 option.type !== ApplicationCommandOptionType.Subcommand
127 );
TheCodedProfcd187ee2023-01-27 20:46:12 -0500128 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000129 for (const option of options) {
TheCodedProf35e73712023-03-10 17:35:35 -0500130 optionString += ` - \`${option.name}\` (${ApplicationCommandOptionType[option.type].replace("Integer", "Number").replace("String", "Text")}) - ${
Skyler Greyda16adf2023-03-05 10:22:12 +0000131 option.description
132 }\n`;
TheCodedProff86ba092023-01-27 17:10:07 -0500133 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000134 const APICommand =
135 client.commands[
136 "commands/" + currentPath.filter((value) => value !== "" && value !== "none").join("/")
137 ]![0];
TheCodedProff86ba092023-01-27 17:10:07 -0500138 let allowedToRun = true;
TheCodedProf35e73712023-03-10 17:35:35 -0500139 if (interaction.guild && APICommand?.check) {
Skyler Greyda16adf2023-03-05 10:22:12 +0000140 allowedToRun = await APICommand.check(interaction as Interaction, true);
TheCodedProff86ba092023-01-27 17:10:07 -0500141 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500142 embed.setDescription(
TheCodedProf35e73712023-03-10 17:35:35 -0500143 `${getEmojiByName(styles[currentPath[0]]!.emoji)} **${capitalize(currentData.name)}** | ${currentData.mention}\n\n` +
144 `${currentData.description}\n\n` +
Skyler Greyda16adf2023-03-05 10:22:12 +0000145 (APICommand
146 ? `${getEmojiByName(allowedToRun ? "CONTROL.TICK" : "CONTROL.CROSS")} You ${
147 allowedToRun ? "" : "don't "
148 }` + `have permission to use this command\n\n`
149 : "") +
150 (optionString.length > 0 ? "**Options:**\n" + optionString : "")
151 );
152 const subcommands = current.options.filter(
153 (option) => option.type === ApplicationCommandOptionType.Subcommand
154 );
155 const subcommandGroups = current.options.filter(
156 (option) => option.type === ApplicationCommandOptionType.SubcommandGroup
157 );
TheCodedProff86ba092023-01-27 17:10:07 -0500158
Skyler Greyda16adf2023-03-05 10:22:12 +0000159 if (subcommandGroups.length > 0) {
160 subcommandGroupRow.components[0]!.addOptions(
161 new StringSelectMenuOptionBuilder()
162 .setLabel("Select a subcommand")
163 .setValue("none")
164 .setDefault(currentPath[1] === "none"),
165 ...subcommandGroups.map((option) =>
166 new StringSelectMenuOptionBuilder()
167 .setLabel(capitalize(option.name))
168 .setValue(option.name)
169 .setDefault(currentPath[1] === option.name)
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500170 )
Skyler Greyda16adf2023-03-05 10:22:12 +0000171 );
172 if (
173 subcommandGroupRow.components[0]!.options.find(
174 (option) => option.data.default && option.data.value !== "none"
175 )
176 ) {
177 const subsubcommands =
178 (
179 subcommandGroups.find(
180 (option) => option.name === currentPath[1]
181 )! as ApplicationCommandSubGroup
182 ).options ?? [];
183 subcommandRow.components[0]!.addOptions(
184 new StringSelectMenuOptionBuilder()
185 .setLabel("Select a subcommand")
186 .setValue("none")
187 .setDefault(currentPath[2] === "none"),
188 ...subsubcommands.map((option) =>
189 new StringSelectMenuOptionBuilder()
190 .setLabel(capitalize(option.name))
191 .setValue(option.name)
192 .setDefault(currentPath[2] === option.name)
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500193 )
Skyler Greyda16adf2023-03-05 10:22:12 +0000194 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500195 }
196 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000197 if (subcommands.length > 0) {
198 subcommandGroupRow.components[0]!.addOptions(
199 ...subcommands.map((option) =>
200 new StringSelectMenuOptionBuilder()
201 .setLabel(capitalize(option.name))
202 .setValue(option.name)
203 .setDefault(currentPath[1] === option.name)
TheCodedProff86ba092023-01-27 17:10:07 -0500204 )
Skyler Greyda16adf2023-03-05 10:22:12 +0000205 );
TheCodedProff86ba092023-01-27 17:10:07 -0500206 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500207 }
TheCodedProff86ba092023-01-27 17:10:07 -0500208
PineaFanb0d0c242023-02-05 10:59:45 +0000209 const cmps = [commandRow];
Skyler Greyda16adf2023-03-05 10:22:12 +0000210 if (subcommandGroupRow.components[0]!.options.length > 0) cmps.push(subcommandGroupRow);
211 if (subcommandRow.components[0]!.options.length > 0) cmps.push(subcommandRow);
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500212
213 await interaction.editReply({ embeds: [embed], components: cmps });
214
TheCodedProff86ba092023-01-27 17:10:07 -0500215 let i: StringSelectMenuInteraction;
216 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000217 i = await m.awaitMessageComponent<ComponentType.StringSelect>({
218 filter: (newInteraction) => interaction.user.id === newInteraction.user.id,
219 time: 300000
220 });
TheCodedProff86ba092023-01-27 17:10:07 -0500221 } catch (e) {
222 closed = true;
PineaFanb0d0c242023-02-05 10:59:45 +0000223 continue;
TheCodedProff86ba092023-01-27 17:10:07 -0500224 }
225 await i.deferUpdate();
PineaFanb0d0c242023-02-05 10:59:45 +0000226 const value = i.values[0]!;
Skyler Greyda16adf2023-03-05 10:22:12 +0000227 switch (i.customId) {
PineaFanb0d0c242023-02-05 10:59:45 +0000228 case "commandRow": {
TheCodedProff86ba092023-01-27 17:10:07 -0500229 currentPath = [value, "", ""];
230 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000231 }
232 case "subcommandGroupRow": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000233 currentPath = [currentPath[0], value, ""];
TheCodedProff86ba092023-01-27 17:10:07 -0500234 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000235 }
236 case "subcommandRow": {
TheCodedProff86ba092023-01-27 17:10:07 -0500237 currentPath[2] = value;
238 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000239 }
TheCodedProff86ba092023-01-27 17:10:07 -0500240 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500241 } while (!closed);
pineafan63fc5e22022-08-04 22:04:10 +0100242};
pineafan4f164f32022-02-26 22:07:12 +0000243
TheCodedProff86ba092023-01-27 17:10:07 -0500244export { command as command };
pineafan4f164f32022-02-26 22:07:12 +0000245export { callback };