blob: 90ef13328048a89e408a9df56a0f72bc292009bb [file] [log] [blame]
Samuel Shuert27bf3cd2023-03-03 15:51:25 -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";
18import client from "../utils/client.js";
19import EmojiEmbed from "../utils/generateEmojiEmbed.js";
20import { LoadingEmbed } from "../utils/defaults.js";
21import { 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
Samuel Shuert27bf3cd2023-03-03 15:51:25 -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"},
42 "verify": {emoji: "CONTROL.REDTICK"}
43}
44
pineafan63fc5e22022-08-04 22:04:10 +010045const callback = async (interaction: CommandInteraction): Promise<void> => {
Samuel Shuert27bf3cd2023-03-03 15:51:25 -050046 const m = await interaction.reply({ embeds: LoadingEmbed, ephemeral: true, fetchReply: true });
47 const commands = client.fetchedCommands;
48
49 let closed = false;
50 let currentPath: [string, string, string] = ["", "", ""]
51 do {
52 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 );
69 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")
81 .setStatus("Danger")
82 .setEmoji("NUCLEUS.LOGO")
83
84 if(currentPath[0] === "" || currentPath[0] === "help") {
85 embed.setDescription(
86 `Welcome to Nucleus\n\n` +
87 `Select a command to get started${
88 (interaction.member?.permissions as PermissionsBitField).has("ManageGuild") ?
89 `, or run ${getCommandMentionByName("nucleus/guide")} for commands to set up your server` : ``
90 }\n\n\n` +
91 `Nucleus is fully [open source](https://github.com/clicksminuteper/Nucleus), and all currently free features will remain free forever.\n\n` +
92 `You can invite Nucleus to your server using ${getCommandMentionByName("nucleus/invite")}`
93 )
94 } else {
95 const currentData = getCommandByName(currentPath.filter(value => value !== "" && value !== "none").join('/'));
96 const current = commands.find((command) => command.name === currentPath[0])!;
97
98 let optionString = ``
99 let options: (ApplicationCommandOption & {
100 nameLocalized?: string;
101 descriptionLocalized?: string;
102 })[] = [];
103 //options
104 if(currentPath[1] !== "" && currentPath[1] !== "none" && currentPath[2] !== "" && currentPath[2] !== "none") {
105 const Op = current.options.find(option => option.name === currentPath[1])! as ApplicationCommandSubGroup
106 const Op2 = Op.options!.find(option => option.name === currentPath[2])!
107 options = Op2.options ?? []
108 } else if(currentPath[1] !== "" && currentPath[1] !== "none") {
109 let Op = current.options.find(option => option.name === currentPath[1])!
110 if(Op.type === ApplicationCommandOptionType.SubcommandGroup) {
111 options = []
112 } else {
113 Op = Op as ApplicationCommandSubCommand
114 options = Op.options ?? []
115 }
116 } else {
117 options = current.options.filter(option => (option.type !== ApplicationCommandOptionType.SubcommandGroup) && (option.type !== ApplicationCommandOptionType.Subcommand));
118 }
119 for(const option of options) {
120 optionString += `> ${option.name} (${ApplicationCommandOptionType[option.type]})- ${option.description}\n`
121 }
122 const APICommand = client.commands["commands/" + currentPath.filter(value => value !== "" && value !== "none").join("/")]![0]
123 let allowedToRun = true;
124 if(APICommand?.check) {
125 allowedToRun = await APICommand.check(interaction as Interaction, true)
126 }
127 embed.setDescription(
128 `${getEmojiByName(styles[currentPath[0]]!.emoji)} **${capitalize(currentData.name)}**\n> ${currentData.mention}\n\n` +
129 `> ${currentData.description}\n\n` +
130 (APICommand ? (`${getEmojiByName(allowedToRun ? "CONTROL.TICK" : "CONTROL.CROSS")} You ${allowedToRun ? "" : "don't "}` +
131 `have permission to use this command\n\n`) : "") +
132 ((optionString.length > 0) ? "**Options:**\n" + optionString : "")
133 )
134 const subcommands = current.options.filter((option) => option.type === ApplicationCommandOptionType.Subcommand);
135 const subcommandGroups = current.options.filter((option) => option.type === ApplicationCommandOptionType.SubcommandGroup);
136
137 if(subcommandGroups.length > 0) {
138 subcommandGroupRow.components[0]!
139 .addOptions(
140 new StringSelectMenuOptionBuilder().setLabel("Select a subcommand").setValue("none").setDefault(currentPath[1] === "none"),
141 ...subcommandGroups.map((option) => new StringSelectMenuOptionBuilder().setLabel(capitalize(option.name)).setValue(option.name).setDefault(currentPath[1] === option.name))
142 )
143 if(subcommandGroupRow.components[0]!.options.find((option) => option.data.default && option.data.value !== "none")) {
144 const subsubcommands = (subcommandGroups.find((option) => option.name === currentPath[1])! as ApplicationCommandSubGroup).options ?? [];
145 subcommandRow.components[0]!
146 .addOptions(
147 new StringSelectMenuOptionBuilder().setLabel("Select a subcommand").setValue("none").setDefault(currentPath[2] === "none"),
148 ...subsubcommands.map((option) => new StringSelectMenuOptionBuilder().setLabel(capitalize(option.name)).setValue(option.name).setDefault(currentPath[2] === option.name))
149 )
150 }
151 }
152 if(subcommands.length > 0) {
153 subcommandGroupRow.components[0]!
154 .addOptions(
155 ...subcommands.map((option) => new StringSelectMenuOptionBuilder().setLabel(capitalize(option.name)).setValue(option.name).setDefault(currentPath[1] === option.name))
156 )
157 }
158 }
159
160 const cmps = [commandRow];
161 if(subcommandGroupRow.components[0]!.options.length > 0) cmps.push(subcommandGroupRow);
162 if(subcommandRow.components[0]!.options.length > 0) cmps.push(subcommandRow);
163
164 await interaction.editReply({ embeds: [embed], components: cmps });
165
166 let i: StringSelectMenuInteraction;
167 try {
168 i = await m.awaitMessageComponent<ComponentType.StringSelect>({filter: (newInteraction) => interaction.user.id === newInteraction.user.id,time: 300000})
169 } catch (e) {
170 closed = true;
171 continue;
172 }
173 await i.deferUpdate();
174 const value = i.values[0]!;
175 switch(i.customId) {
176 case "commandRow": {
177 currentPath = [value, "", ""];
178 break;
179 }
180 case "subcommandGroupRow": {
181 currentPath = [currentPath[0], value , ""];
182 break;
183 }
184 case "subcommandRow": {
185 currentPath[2] = value;
186 break;
187 }
188 }
189
190 } while (!closed);
pineafan63fc5e22022-08-04 22:04:10 +0100191};
pineafan4f164f32022-02-26 22:07:12 +0000192
pineafan4f164f32022-02-26 22:07:12 +0000193
Samuel Shuert27bf3cd2023-03-03 15:51:25 -0500194export { command as command };
pineafan4f164f32022-02-26 22:07:12 +0000195export { callback };