blob: 1cb1ae2a49b78769362762de525499f1625ee898 [file] [log] [blame]
import {
ActionRowBuilder,
CommandInteraction,
StringSelectMenuBuilder,
ApplicationCommandOptionType,
ApplicationCommandType,
StringSelectMenuOptionBuilder,
SlashCommandBuilder,
StringSelectMenuInteraction,
ComponentType,
APIMessageComponentEmoji,
ApplicationCommandSubGroup,
PermissionsBitField,
Interaction,
ApplicationCommandOption,
ApplicationCommandSubCommand
} from "discord.js";
import client from "../utils/client.js";
import EmojiEmbed from "../utils/generateEmojiEmbed.js";
import { LoadingEmbed } from "../utils/defaults.js";
import { capitalize } from "../utils/generateKeyValueList.js";
import { getCommandByName, getCommandMentionByName } from "../utils/getCommandDataByName.js";
import getEmojiByName from "../utils/getEmojiByName.js";
const command = new SlashCommandBuilder()
.setName("help")
.setDescription("Shows help for commands");
const styles: Record<string, {emoji: string}> = {
"help": {emoji: "NUCLEUS.LOGO"},
"mod": {emoji: "PUNISH.BAN.RED"},
"nucleus": {emoji: "NUCLEUS.LOGO"},
"privacy": {emoji: "NUCLEUS.LOGO"},
"role": {emoji: "GUILD.ROLES.DELETE"},
"rolemenu": {emoji: "GUILD.ROLES.DELETE"},
"server": {emoji: "GUILD.RED"},
"settings": {emoji: "GUILD.SETTINGS.RED"},
"tag": {emoji: "PUNISH.NICKNAME.RED"},
"tags": {emoji: "PUNISH.NICKNAME.RED"},
"ticket": {emoji: "GUILD.TICKET.CLOSE"},
"user": {emoji: "MEMBER.LEAVE"},
"verify": {emoji: "CONTROL.BLOCKTICK"}
}
const callback = async (interaction: CommandInteraction): Promise<void> => {
const m = await interaction.reply({ embeds: LoadingEmbed, ephemeral: true, fetchReply: true });
const commands = client.fetchedCommands;
let closed = false;
let currentPath: [string, string, string] = ["","",""]
do {
const commandRow = new ActionRowBuilder<StringSelectMenuBuilder>()
.addComponents(
new StringSelectMenuBuilder()
.setCustomId("commandRow")
.setPlaceholder("Select a command")
.addOptions(
...commands.filter(command => command.type === ApplicationCommandType.ChatInput).map((command) => {
const builder = new StringSelectMenuOptionBuilder()
.setLabel(capitalize(command.name))
.setValue(command.name)
.setDescription(command.description)
.setDefault(currentPath[0] === command.name)
if (styles[command.name]) builder.setEmoji(getEmojiByName(styles[command.name]!.emoji, "id") as APIMessageComponentEmoji)
return builder
})
)
);
const subcommandGroupRow = new ActionRowBuilder<StringSelectMenuBuilder>()
.addComponents(
new StringSelectMenuBuilder()
.setCustomId("subcommandGroupRow")
);
const subcommandRow = new ActionRowBuilder<StringSelectMenuBuilder>()
.addComponents(
new StringSelectMenuBuilder()
.setCustomId("subcommandRow")
);
const embed = new EmojiEmbed()
.setTitle("Help")
.setStatus("Danger")
.setEmoji("NUCLEUS.LOGO")
if(currentPath[0] === "" || currentPath[0] === "help") {
embed.setDescription(
`Welcome to Nucleus\n\n` +
`Select a command to get started${(interaction.member?.permissions as PermissionsBitField).has("ManageGuild") ? `, or run ${getCommandMentionByName("nucleus/guide")} for commands to set up your server` : ``}` // FIXME
)
} else {
let currentData = getCommandByName(currentPath.filter(value => value !== "" && value !== "none").join('/'));
let current = commands.find((command) => command.name === currentPath[0])!;
let optionString = ``
let options: (ApplicationCommandOption & {
nameLocalized?: string;
descriptionLocalized?: string;
})[] = [];
//options
if(currentPath[1] !== "" && currentPath[1] !== "none" && currentPath[2] !== "" && currentPath[2] !== "none") {
let Op = current.options.find(option => option.name === currentPath[1])! as ApplicationCommandSubGroup
let Op2 = Op.options!.find(option => option.name === currentPath[2])!
options = Op2.options || []
} else if(currentPath[1] !== "" && currentPath[1] !== "none") {
let Op = current.options.find(option => option.name === currentPath[1])!
if(Op.type === ApplicationCommandOptionType.SubcommandGroup) {
options = []
} else {
Op = Op as ApplicationCommandSubCommand
options = Op.options || []
}
} else {
options = current.options.filter(option => option.type !== ApplicationCommandOptionType.SubcommandGroup && option.type !== ApplicationCommandOptionType.Subcommand) || [];
}
for(const option of options) {
optionString += `> ${option.name} (${ApplicationCommandOptionType[option.type]})- ${option.description}\n`
}
const APICommand = client.commands["commands/" + currentPath.filter(value => value !== "" && value !== "none").join("/")]![0]
let allowedToRun = true;
if(APICommand?.check) {
APICommand?.check(interaction as Interaction, true)
}
embed.setDescription(
`${getEmojiByName(styles[currentPath[0]]!.emoji)} **${capitalize(currentData.name)}**\n> ${currentData.mention}\n\n` +
`> ${currentData.description}\n\n` +
(APICommand ? (`${getEmojiByName(allowedToRun ? "CONTROL.TICK" : "CONTROL.CROSS")} You ${allowedToRun ? "" : "don't "}` +
`have permission to use this command\n\n`) : "") +
((optionString.length > 0) ? "**Options:**\n" + optionString : "")
)
const subcommands = current.options.filter((option) => option.type === ApplicationCommandOptionType.Subcommand);
const subcommandGroups = current.options.filter((option) => option.type === ApplicationCommandOptionType.SubcommandGroup);
if(subcommandGroups.length > 0) {
subcommandGroupRow.components[0]!
.addOptions(
new StringSelectMenuOptionBuilder().setLabel("Select a subcommand").setValue("none").setDefault(currentPath[1] === "none"),
...subcommandGroups.map((option) => new StringSelectMenuOptionBuilder().setLabel(capitalize(option.name)).setValue(option.name).setDefault(currentPath[1] === option.name))
)
if(subcommandGroupRow.components[0]!.options.find((option) => option.data.default && option.data.value !== "none")) {
let subsubcommands = (subcommandGroups.find((option) => option.name === currentPath[1])! as ApplicationCommandSubGroup).options?.filter((option) => option.type === ApplicationCommandOptionType.Subcommand) || [];
subcommandRow.components[0]!
.addOptions(
new StringSelectMenuOptionBuilder().setLabel("Select a subcommand").setValue("none").setDefault(currentPath[2] === "none"),
...subsubcommands.map((option) => new StringSelectMenuOptionBuilder().setLabel(capitalize(option.name)).setValue(option.name).setDefault(currentPath[2] === option.name))
)
}
}
if(subcommands.length > 0) {
subcommandGroupRow.components[0]!
.addOptions(
...subcommands.map((option) => new StringSelectMenuOptionBuilder().setLabel(capitalize(option.name)).setValue(option.name).setDefault(currentPath[1] === option.name))
)
}
}
let cmps = [commandRow];
if(subcommandGroupRow.components[0]!.options.length > 0) cmps.push(subcommandGroupRow);
if(subcommandRow.components[0]!.options.length > 0) cmps.push(subcommandRow);
await interaction.editReply({ embeds: [embed], components: cmps });
let i: StringSelectMenuInteraction;
try {
i = await m.awaitMessageComponent<ComponentType.StringSelect>({filter: (newInteraction) => interaction.user.id === newInteraction.user.id,time: 300000})
} catch (e) {
closed = true;
break;
}
await i.deferUpdate();
let value = i.values[0]!;
switch(i.customId) {
case "commandRow":
currentPath = [value, "", ""];
break;
case "subcommandGroupRow":
currentPath = [currentPath[0], value , ""];
break;
case "subcommandRow":
currentPath[2] = value;
break;
}
} while (!closed);
};
export { command as command };
export { callback };