blob: 96971e6fb5cbd01676f074e2d57b7be178f18b9c [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` +
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) {
TheCodedProf80ad8542023-03-10 12:52:33 -0500130 optionString += `> \`${option.name}\` (${ApplicationCommandOptionType[option.type]}) - ${
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;
Skyler Greyda16adf2023-03-05 10:22:12 +0000139 if (APICommand?.check) {
140 allowedToRun = await APICommand.check(interaction as Interaction, true);
TheCodedProff86ba092023-01-27 17:10:07 -0500141 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500142 embed.setDescription(
Skyler Greyda16adf2023-03-05 10:22:12 +0000143 `${getEmojiByName(styles[currentPath[0]]!.emoji)} **${capitalize(currentData.name)}**\n> ${
144 currentData.mention
145 }\n\n` +
146 `> ${currentData.description}\n\n` +
147 (APICommand
148 ? `${getEmojiByName(allowedToRun ? "CONTROL.TICK" : "CONTROL.CROSS")} You ${
149 allowedToRun ? "" : "don't "
150 }` + `have permission to use this command\n\n`
151 : "") +
152 (optionString.length > 0 ? "**Options:**\n" + optionString : "")
153 );
154 const subcommands = current.options.filter(
155 (option) => option.type === ApplicationCommandOptionType.Subcommand
156 );
157 const subcommandGroups = current.options.filter(
158 (option) => option.type === ApplicationCommandOptionType.SubcommandGroup
159 );
TheCodedProff86ba092023-01-27 17:10:07 -0500160
Skyler Greyda16adf2023-03-05 10:22:12 +0000161 if (subcommandGroups.length > 0) {
162 subcommandGroupRow.components[0]!.addOptions(
163 new StringSelectMenuOptionBuilder()
164 .setLabel("Select a subcommand")
165 .setValue("none")
166 .setDefault(currentPath[1] === "none"),
167 ...subcommandGroups.map((option) =>
168 new StringSelectMenuOptionBuilder()
169 .setLabel(capitalize(option.name))
170 .setValue(option.name)
171 .setDefault(currentPath[1] === option.name)
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500172 )
Skyler Greyda16adf2023-03-05 10:22:12 +0000173 );
174 if (
175 subcommandGroupRow.components[0]!.options.find(
176 (option) => option.data.default && option.data.value !== "none"
177 )
178 ) {
179 const subsubcommands =
180 (
181 subcommandGroups.find(
182 (option) => option.name === currentPath[1]
183 )! as ApplicationCommandSubGroup
184 ).options ?? [];
185 subcommandRow.components[0]!.addOptions(
186 new StringSelectMenuOptionBuilder()
187 .setLabel("Select a subcommand")
188 .setValue("none")
189 .setDefault(currentPath[2] === "none"),
190 ...subsubcommands.map((option) =>
191 new StringSelectMenuOptionBuilder()
192 .setLabel(capitalize(option.name))
193 .setValue(option.name)
194 .setDefault(currentPath[2] === option.name)
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500195 )
Skyler Greyda16adf2023-03-05 10:22:12 +0000196 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500197 }
198 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000199 if (subcommands.length > 0) {
200 subcommandGroupRow.components[0]!.addOptions(
201 ...subcommands.map((option) =>
202 new StringSelectMenuOptionBuilder()
203 .setLabel(capitalize(option.name))
204 .setValue(option.name)
205 .setDefault(currentPath[1] === option.name)
TheCodedProff86ba092023-01-27 17:10:07 -0500206 )
Skyler Greyda16adf2023-03-05 10:22:12 +0000207 );
TheCodedProff86ba092023-01-27 17:10:07 -0500208 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500209 }
TheCodedProff86ba092023-01-27 17:10:07 -0500210
PineaFanb0d0c242023-02-05 10:59:45 +0000211 const cmps = [commandRow];
Skyler Greyda16adf2023-03-05 10:22:12 +0000212 if (subcommandGroupRow.components[0]!.options.length > 0) cmps.push(subcommandGroupRow);
213 if (subcommandRow.components[0]!.options.length > 0) cmps.push(subcommandRow);
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500214
215 await interaction.editReply({ embeds: [embed], components: cmps });
216
TheCodedProff86ba092023-01-27 17:10:07 -0500217 let i: StringSelectMenuInteraction;
218 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000219 i = await m.awaitMessageComponent<ComponentType.StringSelect>({
220 filter: (newInteraction) => interaction.user.id === newInteraction.user.id,
221 time: 300000
222 });
TheCodedProff86ba092023-01-27 17:10:07 -0500223 } catch (e) {
224 closed = true;
PineaFanb0d0c242023-02-05 10:59:45 +0000225 continue;
TheCodedProff86ba092023-01-27 17:10:07 -0500226 }
227 await i.deferUpdate();
PineaFanb0d0c242023-02-05 10:59:45 +0000228 const value = i.values[0]!;
Skyler Greyda16adf2023-03-05 10:22:12 +0000229 switch (i.customId) {
PineaFanb0d0c242023-02-05 10:59:45 +0000230 case "commandRow": {
TheCodedProff86ba092023-01-27 17:10:07 -0500231 currentPath = [value, "", ""];
232 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000233 }
234 case "subcommandGroupRow": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000235 currentPath = [currentPath[0], value, ""];
TheCodedProff86ba092023-01-27 17:10:07 -0500236 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000237 }
238 case "subcommandRow": {
TheCodedProff86ba092023-01-27 17:10:07 -0500239 currentPath[2] = value;
240 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000241 }
TheCodedProff86ba092023-01-27 17:10:07 -0500242 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500243 } while (!closed);
pineafan63fc5e22022-08-04 22:04:10 +0100244};
pineafan4f164f32022-02-26 22:07:12 +0000245
TheCodedProff86ba092023-01-27 17:10:07 -0500246export { command as command };
pineafan4f164f32022-02-26 22:07:12 +0000247export { callback };