blob: f95eb6ebd80dcba482f135874aa200bd014357b4 [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
TheCodedProfca29ebb2023-03-10 17:40:09 -050025const command = new SlashCommandBuilder()
26 .setName("help")
27 .setDescription("Shows help for commands")
28 .setDMPermission(true);
pineafan4f164f32022-02-26 22:07:12 +000029
Skyler Greyda16adf2023-03-05 10:22:12 +000030const styles: Record<string, { emoji: string }> = {
31 help: { emoji: "NUCLEUS.LOGO" },
32 mod: { emoji: "PUNISH.BAN.RED" },
33 nucleus: { emoji: "NUCLEUS.LOGO" },
34 privacy: { emoji: "NUCLEUS.LOGO" },
35 role: { emoji: "GUILD.ROLES.DELETE" },
36 rolemenu: { emoji: "GUILD.ROLES.DELETE" },
37 server: { emoji: "GUILD.RED" },
38 settings: { emoji: "GUILD.SETTINGS.RED" },
39 tag: { emoji: "PUNISH.NICKNAME.RED" },
40 tags: { emoji: "PUNISH.NICKNAME.RED" },
41 ticket: { emoji: "GUILD.TICKET.CLOSE" },
42 user: { emoji: "MEMBER.LEAVE" },
43 verify: { emoji: "CONTROL.REDTICK" }
44};
TheCodedProff86ba092023-01-27 17:10:07 -050045
pineafan63fc5e22022-08-04 22:04:10 +010046const callback = async (interaction: CommandInteraction): Promise<void> => {
TheCodedProff86ba092023-01-27 17:10:07 -050047 const m = await interaction.reply({ embeds: LoadingEmbed, ephemeral: true, fetchReply: true });
TheCodedProf1c3ad3c2023-01-25 17:58:36 -050048 const commands = client.fetchedCommands;
49
TheCodedProf1c3ad3c2023-01-25 17:58:36 -050050 let closed = false;
Skyler Greyda16adf2023-03-05 10:22:12 +000051 let currentPath: [string, string, string] = ["", "", ""];
TheCodedProf1c3ad3c2023-01-25 17:58:36 -050052 do {
Skyler Greyda16adf2023-03-05 10:22:12 +000053 const commandRow = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
54 new StringSelectMenuBuilder()
55 .setCustomId("commandRow")
56 .setPlaceholder("Select a command")
57 .addOptions(
58 ...commands
59 .filter((command) => command.type === ApplicationCommandType.ChatInput)
60 .map((command) => {
TheCodedProff86ba092023-01-27 17:10:07 -050061 const builder = new StringSelectMenuOptionBuilder()
62 .setLabel(capitalize(command.name))
63 .setValue(command.name)
64 .setDescription(command.description)
Skyler Greyda16adf2023-03-05 10:22:12 +000065 .setDefault(currentPath[0] === command.name);
66 if (styles[command.name])
67 builder.setEmoji(
68 getEmojiByName(styles[command.name]!.emoji, "id") as APIMessageComponentEmoji
69 );
70 return builder;
TheCodedProff86ba092023-01-27 17:10:07 -050071 })
Skyler Greyda16adf2023-03-05 10:22:12 +000072 )
TheCodedProff86ba092023-01-27 17:10:07 -050073 );
Skyler Greyda16adf2023-03-05 10:22:12 +000074 const subcommandGroupRow = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
75 new StringSelectMenuBuilder().setCustomId("subcommandGroupRow")
76 );
77 const subcommandRow = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
78 new StringSelectMenuBuilder().setCustomId("subcommandRow")
79 );
80 const embed = new EmojiEmbed().setTitle("Help").setStatus("Danger").setEmoji("NUCLEUS.LOGO");
TheCodedProf1c3ad3c2023-01-25 17:58:36 -050081
Skyler Greyda16adf2023-03-05 10:22:12 +000082 if (currentPath[0] === "" || currentPath[0] === "help") {
TheCodedProf1c3ad3c2023-01-25 17:58:36 -050083 embed.setDescription(
TheCodedProff86ba092023-01-27 17:10:07 -050084 `Welcome to Nucleus\n\n` +
Skyler Greyda16adf2023-03-05 10:22:12 +000085 `Select a command to get started${
86 (interaction.member?.permissions as PermissionsBitField).has("ManageGuild")
87 ? `, or run ${getCommandMentionByName("nucleus/guide")} for commands to set up your server`
88 : ``
TheCodedProf1f675042023-02-16 17:01:29 -050089 }\n\n\n` +
Skyler Greyda16adf2023-03-05 10:22:12 +000090 `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 -050091 `You can invite Nucleus to your server using ${getCommandMentionByName("nucleus/invite")}\n` +
92 `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 +000093 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -050094 } else {
Skyler Greyda16adf2023-03-05 10:22:12 +000095 const currentData = getCommandByName(
96 currentPath.filter((value) => value !== "" && value !== "none").join("/")
97 );
PineaFanb0d0c242023-02-05 10:59:45 +000098 const current = commands.find((command) => command.name === currentPath[0])!;
TheCodedProff86ba092023-01-27 17:10:07 -050099
Skyler Greyda16adf2023-03-05 10:22:12 +0000100 let optionString = ``;
TheCodedProff86ba092023-01-27 17:10:07 -0500101 let options: (ApplicationCommandOption & {
102 nameLocalized?: string;
103 descriptionLocalized?: string;
104 })[] = [];
TheCodedProf1807fb32023-02-20 14:33:48 -0500105 //options
Skyler Greyda16adf2023-03-05 10:22:12 +0000106 if (
107 currentPath[1] !== "" &&
108 currentPath[1] !== "none" &&
109 currentPath[2] !== "" &&
110 currentPath[2] !== "none"
111 ) {
112 const Op = current.options.find(
113 (option) => option.name === currentPath[1]
114 )! as ApplicationCommandSubGroup;
115 const Op2 = Op.options!.find((option) => option.name === currentPath[2])!;
116 options = Op2.options ?? [];
117 } else if (currentPath[1] !== "" && currentPath[1] !== "none") {
118 let Op = current.options.find((option) => option.name === currentPath[1])!;
119 if (Op.type === ApplicationCommandOptionType.SubcommandGroup) {
120 options = [];
TheCodedProfcd187ee2023-01-27 20:46:12 -0500121 } else {
Skyler Greyda16adf2023-03-05 10:22:12 +0000122 Op = Op as ApplicationCommandSubCommand;
123 options = Op.options ?? [];
TheCodedProfcd187ee2023-01-27 20:46:12 -0500124 }
125 } else {
Skyler Greyda16adf2023-03-05 10:22:12 +0000126 options = current.options.filter(
127 (option) =>
128 option.type !== ApplicationCommandOptionType.SubcommandGroup &&
129 option.type !== ApplicationCommandOptionType.Subcommand
130 );
TheCodedProfcd187ee2023-01-27 20:46:12 -0500131 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000132 for (const option of options) {
TheCodedProfca29ebb2023-03-10 17:40:09 -0500133 optionString += ` - \`${option.name}\` (${ApplicationCommandOptionType[option.type]
134 .replace("Integer", "Number")
135 .replace("String", "Text")}) - ${option.description}\n`;
TheCodedProff86ba092023-01-27 17:10:07 -0500136 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000137 const APICommand =
138 client.commands[
139 "commands/" + currentPath.filter((value) => value !== "" && value !== "none").join("/")
140 ]![0];
TheCodedProff86ba092023-01-27 17:10:07 -0500141 let allowedToRun = true;
TheCodedProf35e73712023-03-10 17:35:35 -0500142 if (interaction.guild && APICommand?.check) {
Skyler Greyda16adf2023-03-05 10:22:12 +0000143 allowedToRun = await APICommand.check(interaction as Interaction, true);
TheCodedProff86ba092023-01-27 17:10:07 -0500144 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500145 embed.setDescription(
TheCodedProfca29ebb2023-03-10 17:40:09 -0500146 `${getEmojiByName(styles[currentPath[0]]!.emoji)} **${capitalize(currentData.name)}** | ${
147 currentData.mention
148 }\n\n` +
TheCodedProf35e73712023-03-10 17:35:35 -0500149 `${currentData.description}\n\n` +
Skyler Greyda16adf2023-03-05 10:22:12 +0000150 (APICommand
151 ? `${getEmojiByName(allowedToRun ? "CONTROL.TICK" : "CONTROL.CROSS")} You ${
152 allowedToRun ? "" : "don't "
153 }` + `have permission to use this command\n\n`
154 : "") +
155 (optionString.length > 0 ? "**Options:**\n" + optionString : "")
156 );
157 const subcommands = current.options.filter(
158 (option) => option.type === ApplicationCommandOptionType.Subcommand
159 );
160 const subcommandGroups = current.options.filter(
161 (option) => option.type === ApplicationCommandOptionType.SubcommandGroup
162 );
TheCodedProff86ba092023-01-27 17:10:07 -0500163
Skyler Greyda16adf2023-03-05 10:22:12 +0000164 if (subcommandGroups.length > 0) {
165 subcommandGroupRow.components[0]!.addOptions(
166 new StringSelectMenuOptionBuilder()
167 .setLabel("Select a subcommand")
168 .setValue("none")
169 .setDefault(currentPath[1] === "none"),
170 ...subcommandGroups.map((option) =>
171 new StringSelectMenuOptionBuilder()
172 .setLabel(capitalize(option.name))
173 .setValue(option.name)
174 .setDefault(currentPath[1] === option.name)
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500175 )
Skyler Greyda16adf2023-03-05 10:22:12 +0000176 );
177 if (
178 subcommandGroupRow.components[0]!.options.find(
179 (option) => option.data.default && option.data.value !== "none"
180 )
181 ) {
182 const subsubcommands =
183 (
184 subcommandGroups.find(
185 (option) => option.name === currentPath[1]
186 )! as ApplicationCommandSubGroup
187 ).options ?? [];
188 subcommandRow.components[0]!.addOptions(
189 new StringSelectMenuOptionBuilder()
190 .setLabel("Select a subcommand")
191 .setValue("none")
192 .setDefault(currentPath[2] === "none"),
193 ...subsubcommands.map((option) =>
194 new StringSelectMenuOptionBuilder()
195 .setLabel(capitalize(option.name))
196 .setValue(option.name)
197 .setDefault(currentPath[2] === option.name)
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500198 )
Skyler Greyda16adf2023-03-05 10:22:12 +0000199 );
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500200 }
201 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000202 if (subcommands.length > 0) {
203 subcommandGroupRow.components[0]!.addOptions(
204 ...subcommands.map((option) =>
205 new StringSelectMenuOptionBuilder()
206 .setLabel(capitalize(option.name))
207 .setValue(option.name)
208 .setDefault(currentPath[1] === option.name)
TheCodedProff86ba092023-01-27 17:10:07 -0500209 )
Skyler Greyda16adf2023-03-05 10:22:12 +0000210 );
TheCodedProff86ba092023-01-27 17:10:07 -0500211 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500212 }
TheCodedProff86ba092023-01-27 17:10:07 -0500213
PineaFanb0d0c242023-02-05 10:59:45 +0000214 const cmps = [commandRow];
Skyler Greyda16adf2023-03-05 10:22:12 +0000215 if (subcommandGroupRow.components[0]!.options.length > 0) cmps.push(subcommandGroupRow);
216 if (subcommandRow.components[0]!.options.length > 0) cmps.push(subcommandRow);
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500217
218 await interaction.editReply({ embeds: [embed], components: cmps });
219
TheCodedProff86ba092023-01-27 17:10:07 -0500220 let i: StringSelectMenuInteraction;
221 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000222 i = await m.awaitMessageComponent<ComponentType.StringSelect>({
223 filter: (newInteraction) => interaction.user.id === newInteraction.user.id,
224 time: 300000
225 });
TheCodedProff86ba092023-01-27 17:10:07 -0500226 } catch (e) {
227 closed = true;
PineaFanb0d0c242023-02-05 10:59:45 +0000228 continue;
TheCodedProff86ba092023-01-27 17:10:07 -0500229 }
230 await i.deferUpdate();
PineaFanb0d0c242023-02-05 10:59:45 +0000231 const value = i.values[0]!;
Skyler Greyda16adf2023-03-05 10:22:12 +0000232 switch (i.customId) {
PineaFanb0d0c242023-02-05 10:59:45 +0000233 case "commandRow": {
TheCodedProff86ba092023-01-27 17:10:07 -0500234 currentPath = [value, "", ""];
235 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000236 }
237 case "subcommandGroupRow": {
Skyler Greyda16adf2023-03-05 10:22:12 +0000238 currentPath = [currentPath[0], value, ""];
TheCodedProff86ba092023-01-27 17:10:07 -0500239 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000240 }
241 case "subcommandRow": {
TheCodedProff86ba092023-01-27 17:10:07 -0500242 currentPath[2] = value;
243 break;
PineaFanb0d0c242023-02-05 10:59:45 +0000244 }
TheCodedProff86ba092023-01-27 17:10:07 -0500245 }
TheCodedProf1c3ad3c2023-01-25 17:58:36 -0500246 } while (!closed);
pineafan63fc5e22022-08-04 22:04:10 +0100247};
pineafan4f164f32022-02-26 22:07:12 +0000248
TheCodedProff86ba092023-01-27 17:10:07 -0500249export { command as command };
pineafan4f164f32022-02-26 22:07:12 +0000250export { callback };