Skip to content

Commit

Permalink
feat: New help command (very WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
dev-737 committed Mar 18, 2023
1 parent 7b0640f commit c8d3a3b
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 111 deletions.
202 changes: 115 additions & 87 deletions src/Commands/Main/help.ts
Original file line number Diff line number Diff line change
@@ -1,96 +1,124 @@
import { SlashCommandBuilder, ApplicationCommandOptionType, PermissionsBitField, EmbedBuilder, ChatInputCommandInteraction, SlashCommandStringOption, SlashCommandUserOption, PermissionsString, AutocompleteInteraction } from 'discord.js';
import { colors, toTitleCase } from '../../Utils/functions/utils';
import { SlashCommandBuilder, ChatInputCommandInteraction, ActionRowBuilder, StringSelectMenuBuilder, EmbedBuilder, PermissionsBitField, PermissionsString, APISelectMenuOption, Client, ApplicationCommandType } from 'discord.js';
import { checkIfStaff, colors, toTitleCase } from '../../Utils/functions/utils';
import { InterchatCommand } from '../../Utils/typings/discord';

export default {
data: new SlashCommandBuilder()
.setName('help')
.setDescription('Want help? Here it comes!')
.addStringOption((option) =>
option
.setName('command')
.setRequired(false)
.setDescription('Name of command')
.setAutocomplete(true),
),
.setDescription('Want help? Here it comes!'),
async execute(interaction: ChatInputCommandInteraction) {
const command_option = interaction.options.getString('command');

if (!command_option) {
const embed = new EmbedBuilder()
.setAuthor({
name: interaction.client.user?.username + ' Help',
iconURL: interaction.client.user?.avatarURL() as string,
})
.setDescription(`[Invite](${interaction.client.inviteLink}) • [Support](https://discord.gg/6bhXQynAPs) • [Privacy](https://interchat.gitbook.io/important/privacy)`)
.setFields(interaction.client.commandsArray)
.setFooter({ text: 'Use /help <command> to get more info about a command.' })
.setColor(colors('chatbot'))
.setTimestamp();

return await interaction.reply({ embeds: [embed] });
}

// TODO: Rewrite this holy shi its bad
const command = interaction.client.commands.get(command_option);
const commanddata = command?.data.toJSON();
const commandOps = commanddata?.options;
const permissions = new PermissionsBitField(commanddata?.default_member_permissions as PermissionsString | undefined).toArray().join(', ');
let options = '';

if (!command) return interaction.reply('Unkown command!');

const command_embed = new EmbedBuilder()
.setTitle(toTitleCase(command.data.name) + ' Help')
.setDescription(
command.data.description || command.description as string || 'No Description',
)
.addFields([
{ name: 'Permissions:', value: `**${permissions || 'None'}**` },
])
.setFooter({
text: '<> - Required | [] - Optional',
iconURL: interaction.client.user?.avatarURL() as string,
})
.setColor(colors());


if (
commandOps &&
commandOps[0]?.type != ApplicationCommandOptionType.Subcommand &&
commandOps[0]?.type != ApplicationCommandOptionType.SubcommandGroup
) {
commandOps.forEach((value) =>
value.required ? options += ` <${value.name}>` : options += ` [${value.name}]`,
);
command_embed.addFields([
{ name: 'Usage: ', value: `\`/${command.data.name + options}\`` },
]);
}

if (commandOps?.at(0)?.type === ApplicationCommandOptionType.Subcommand) {
commandOps.forEach((subcommand: any) => {
const subOptions = subcommand.options.map((optionValue: SlashCommandStringOption | SlashCommandUserOption) => {
return optionValue.required ? ` <${optionValue.name}>` : ` [${optionValue.name}]`;
});

const data = {
name: `${subcommand.name}`,
value: `${subcommand.description || 'No Description'}\n**Usage: **\`/${command.data.name} ${subcommand.name}${subOptions.length === 0 ? '' : subOptions.join('')}\``,
};

command_embed.addFields([data]);
});
}

return interaction.reply({ embeds: [command_embed] });
},
async autocomplete(interaction: AutocompleteInteraction) {
const focusedValue = interaction.options.getFocused();

const commands = interaction.client.commands;
const choices = commands.map((command) => command.data.name);
const filtered = choices.filter((choice) => choice.startsWith(focusedValue));
const emojis = interaction.client.emoji.normal;
const isStaff = await checkIfStaff(interaction.user);

const ignoreDirs = isStaff ? [] : ['Developer', 'Staff'];
const menuOptionsObj = commands.reduce((obj: Record<string, APISelectMenuOption>, command) => {
if (!ignoreDirs.includes(command.directory) && !obj[command.directory]) {
obj[command.directory] = { label: command.directory, value: command.directory };
}
return obj;
}, {});

const menuOptions = Object.values(menuOptionsObj);
menuOptions[0].default = true;

const categorySelect = new ActionRowBuilder<StringSelectMenuBuilder>()
.addComponents(new StringSelectMenuBuilder({ customId: 'categorySelect', options: menuOptions, placeholder: 'Select a Category' }));

const commandSelect = new ActionRowBuilder<StringSelectMenuBuilder>()
.addComponents(new StringSelectMenuBuilder({ customId: 'commandSelect', placeholder: 'Select a Command' }));

const firstCategory = menuOptions[0].label;
let allCommands = '';

commands.forEach(command => {
if (command.directory === firstCategory) {
allCommands += prettifyCommand(command, emojis);
commandSelect.components[0].addOptions({ label: toTitleCase(command.data.name), value: command.data.name });
}
});

const embed = new EmbedBuilder()
.setTitle(firstCategory + ' Commands')
.setAuthor({ name: `${interaction.client.user.username} Help`, iconURL: interaction.client.user.avatarURL() || undefined })
.setDescription(allCommands)
.setColor(colors('chatbot'))
.setFooter({ text: `Requested by ${interaction.user.tag}`, iconURL: interaction.user.avatarURL() || interaction.user.defaultAvatarURL });

const firstReply = await interaction.reply({ embeds: [embed], components: [categorySelect, commandSelect] });

const collector = firstReply.createMessageComponentCollector({ filter: i => i.user.id === interaction.user.id });
collector.on('collect', (i) => {
if (i.isStringSelectMenu()) {
switch (i.customId) {
case 'categorySelect': {
const category = i.values[0];

// reset values
allCommands = '';
commandSelect.components[0].setOptions();

await interaction.respond(filtered.map((choice) => ({ name: choice, value: choice })));
commands.forEach((command) => {
if (command.directory === category) {
allCommands += prettifyCommand(command, emojis);
commandSelect.components[0].addOptions({ label: command.data.name, value: command.data.name });
}
});

const categoryEmbed = new EmbedBuilder()
.setTitle(category + ' Commands')
.setAuthor({ name: `${interaction.client.user.username} Help`, iconURL: interaction.client.user.avatarURL() || undefined })
.setDescription(allCommands)
.setColor(colors('chatbot'))
.setFooter({ text: `Requested by ${interaction.user.tag}`, iconURL: interaction.user.avatarURL() || interaction.user.defaultAvatarURL });

categorySelect.components[0].options.find(option => option.data.default)?.setDefault(false);
categorySelect.components[0].options.find(option => option.data.value === category)?.setDefault(true);

i.update({ embeds: [categoryEmbed], components: [categorySelect, commandSelect] });
break;
}
case 'commandSelect': {
const commandName = i.values[0];
const selectedCommand = commands.find(command => command.data.name === commandName);
const commandData = selectedCommand?.data.toJSON();
const permissions = new PermissionsBitField(commandData?.default_member_permissions as PermissionsString | undefined).toArray().join(', ');

const commandEmbed = new EmbedBuilder()
.setTitle(toTitleCase(commandName))
.setDescription(`${getCommandDescription(selectedCommand).description}`)
.setColor(colors('chatbot'))
.addFields({ name: 'Permissions Required', value: permissions || 'None.' });

i.update({ embeds: [commandEmbed] });
break;
}

default:
break;
}
}

});
},
};

function getCommandDescription(command: InterchatCommand | undefined) {
const commandData = command?.data as any;
let description = command?.description;
let commandType: ApplicationCommandType = commandData.type;

if (!commandData.type) {
description = commandData.description;
commandType = ApplicationCommandType.ChatInput;
}

return { description, commandType };
}

function prettifyCommand(command: InterchatCommand, emojis: Client['emoji']['normal']) {
const commandDesc = getCommandDescription(command);
const commandType = commandDesc.commandType !== ApplicationCommandType.ChatInput ? ' ' + emojis.contextMenu : emojis.slashCommand;

return `${commandType} **${toTitleCase(command.data.name)}**\n${emojis.dividerEnd} ${commandDesc.description}\n`;
}
1 change: 1 addition & 0 deletions src/Commands/Network/deleteMsg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import logger from '../../Utils/logger';
import { networkMessageDelete } from '../../Scripts/networkLogs/msgDelete';

export default {
description: 'Delete a message that was sent in the network.',
data: new ContextMenuCommandBuilder()
.setName('Delete Message')
.setType(ApplicationCommandType.Message),
Expand Down
1 change: 1 addition & 0 deletions src/Commands/Network/editMsg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import wordFiler from '../../Utils/functions/wordFilter';
import logger from '../../Utils/logger';

export default {
description: 'Edit a message that was sent in the network.',
data: new ContextMenuCommandBuilder()
.setName('Edit Message')
.setType(ApplicationCommandType.Message),
Expand Down
1 change: 1 addition & 0 deletions src/Commands/Network/serverInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { colors, getDb } from '../../Utils/functions/utils';
import { stripIndent } from 'common-tags';

export default {
description: 'Get information about a server in the network!',
data: new ContextMenuCommandBuilder()
.setName('Server Info')
.setType(ApplicationCommandType.Message),
Expand Down
1 change: 1 addition & 0 deletions src/Commands/Network/userInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { badgeToEmoji, colors, getDb } from '../../Utils/functions/utils';
import { profileImage } from 'discord-arts';

export default {
description: 'Get information about a user in the network!',
data: new ContextMenuCommandBuilder()
.setName('User Info')
.setType(ApplicationCommandType.Message),
Expand Down
2 changes: 1 addition & 1 deletion src/Commands/Support/support.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ChatInputCommandInteraction, SlashCommandBuilder } from 'discord.js';
export default {
data: new SlashCommandBuilder()
.setName('support')
.setDescription('Contact the developers for suggestions/reports.')
.setDescription('Send the developers suggestions/reports.')
.setDMPermission(false)
.addSubcommand(subcommand =>
subcommand
Expand Down
17 changes: 1 addition & 16 deletions src/Structures/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export class ExtendedClient extends Client {
});

this.commands = new Collection();
this.commandsArray = [];
this.description = project.description;
this.version = project.version;
this.emoji = emojis;
Expand Down Expand Up @@ -57,23 +56,9 @@ export class ExtendedClient extends Client {
for (const commandFile of commandFiles) {
const command = require(`../Commands/${dir}/${commandFile}`);

command.default.directory = dir;
this.commands.set(command.default.data.name, command.default);
}

// loading the help command
const IgnoredDirs = ['Developer', 'Staff'];
if (IgnoredDirs.includes(dir)) return;

const cmds = commandFiles.map((command: string) => {
const file = require(`../Commands/${dir}/${command}`);
const name = file.default.data.name || 'No name';
return `\`${name}\``;
});

this.commandsArray.push({
name: dir,
value: cmds.length === 0 ? 'No commands' : cmds.join(', '),
});
}
});
}
Expand Down
11 changes: 8 additions & 3 deletions src/Utils/JSON/emoji.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@
"chatbot_circle": "<:chatbot_circle:1077200892157243503>",
"dotYellow": "<:yellowdot:986617776196190248>",
"dotRed": "<:reddot:986617774006738944>",
"webhook": "<:webhook:1065979539320217621> ",
"dotBlue": "<:dotBlue:1086649062775865364>",
"webhook": "<:webhook:1065979539320217621>",
"online_anim": "<a:online:983962418558435418>",
"idle_anim": "<a:idle:983962415567888445> ",
"idle_anim": "<a:idle:983962415567888445>",
"dnd_anim": "<a:dnd:983962413646876682>",
"offline_anim": "<a:offline:983962407049244714>",
"onlineMobile": "<:onlinemobile:715050614429712384>",
Expand All @@ -32,7 +33,11 @@
"reply": "<:reply:1064781138477985792>",
"blobFastBan": "<a:blob_fastban:1082322164763594822>",
"back": "<:back:1082924285674401852>",
"forward": "<:forward:1082924287922536508>"
"forward": "<:forward:1082924287922536508>",
"dividerEnd": "<:divider_end:1077200586803527690>",
"contextMenu": "<:ContextMenuCommand:1086624007949930597>",
"slashCommand": "<:slashCommand:1086631449337266276>",
"blueLine": "<:blueLine:1086634970539360296>"
},


Expand Down
8 changes: 4 additions & 4 deletions src/Utils/typings/discord.d.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import discord from 'discord.js';
import EmojiIDs from '../../Utils/JSON/emoji.json';

type commands = {
type InterchatCommand = {
developer?: boolean,
staff?: boolean,
description?: string | undefined
data: discord.SlashCommandBuilder,
directory: string,
data: discord.SlashCommandBuilder | discord.ContextMenuCommandBuilder,
execute: (interaction: discord.ChatInputCommandInteraction | discord.MessageContextMenuCommandInteraction) => unknown
autocomplete?: (interaction: discord.AutocompleteInteraction) => unknown
}

declare module 'discord.js' {
export interface Client {
commands: discord.Collection<string, commands>,
commands: discord.Collection<string, InterchatCommand>,
description: string,
version: string,
emoji: typeof EmojiIDs,
commandsArray: Array<{name: string, value: string}>
inviteLink: string;
sendInNetwork(message: string | MessageCreateOptions): Promise<void>;
}
Expand Down

0 comments on commit c8d3a3b

Please sign in to comment.