feat: help command #36
9 changed files with 182 additions and 20 deletions
|
@ -2,6 +2,7 @@ import { REST } from '@discordjs/rest';
|
||||||
import { Routes } from 'discord-api-types/v9';
|
import { Routes } from 'discord-api-types/v9';
|
||||||
import { Client } from 'discord.js';
|
import { Client } from 'discord.js';
|
||||||
import { readdir } from 'fs/promises';
|
import { readdir } from 'fs/promises';
|
||||||
|
import { removeExtension } from '../utils/misc';
|
||||||
|
|
||||||
/** Load all the commands. */
|
/** Load all the commands. */
|
||||||
export default async (client: Client) => {
|
export default async (client: Client) => {
|
||||||
|
@ -17,6 +18,12 @@ export default async (client: Client) => {
|
||||||
// Retrieve all the commands
|
// Retrieve all the commands
|
||||||
const command_files = await readdir(`${__dirname}/${command_category}`);
|
const command_files = await readdir(`${__dirname}/${command_category}`);
|
||||||
|
|
||||||
|
// Add the category to the collection for the help command
|
||||||
|
client.commands.categories.set(
|
||||||
|
command_category,
|
||||||
|
command_files.map(removeExtension),
|
||||||
|
);
|
||||||
|
|
||||||
// Add the command
|
// Add the command
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
command_files.map(async command_file => {
|
command_files.map(async command_file => {
|
||||||
|
@ -25,9 +32,10 @@ export default async (client: Client) => {
|
||||||
).default;
|
).default;
|
||||||
|
|
||||||
// Add it to the collection so the interaction will work
|
// Add it to the collection so the interaction will work
|
||||||
client.commands.set(command.data(client).name, command);
|
command.data = command.data(client);
|
||||||
|
client.commands.list.set(command.data.name, command);
|
||||||
|
|
||||||
return command.data(client).toJSON();
|
return command.data.toJSON();
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
|
94
src/commands/misc/help.ts
Normal file
94
src/commands/misc/help.ts
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
import { SlashCommandBuilder } from '@discordjs/builders';
|
||||||
|
import { Locale } from 'discord-api-types/v9';
|
||||||
|
import { Client, CommandInteraction, MessageEmbed } from 'discord.js';
|
||||||
|
import { getLocale, getLocalizations } from '../../utils/locales';
|
||||||
|
import { getFilename } from '../../utils/misc';
|
||||||
|
import '../../modules/string';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data: (client: Client) => {
|
||||||
|
const filename = getFilename(__filename);
|
||||||
|
return new SlashCommandBuilder()
|
||||||
|
.setName(
|
||||||
|
filename.toLowerCase())
|
||||||
|
.setDescription(client.locales.get(client.config.default_lang)
|
||||||
|
?.get(`c_${filename}_desc`) ?? '')
|
||||||
|
.setNameLocalizations(
|
||||||
|
getLocalizations(client, `c_${filename}_name`, true))
|
||||||
|
.setDescriptionLocalizations(
|
||||||
|
getLocalizations(client, `c_${filename}_desc`))
|
||||||
|
|
||||||
|
// Command option
|
||||||
|
.addStringOption(option => option
|
||||||
|
.setName(client.locales.get(client.config.default_lang)
|
||||||
|
?.get(`c_${filename}_opt1_name`) ?? '')
|
||||||
|
.setDescription(client.locales.get(client.config.default_lang)
|
||||||
|
?.get(`c_${filename}_opt1_desc`) ?? '')
|
||||||
|
.setNameLocalizations(
|
||||||
|
getLocalizations(client, `c_${filename}_opt1_name`, true))
|
||||||
|
.setDescriptionLocalizations(
|
||||||
|
getLocalizations(client, `c_${filename}_opt1_desc`))
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
interaction: async (interaction: CommandInteraction, client: Client) => {
|
||||||
|
const loc = getLocale(client, interaction.locale);
|
||||||
|
const desired_command = interaction.options.getString(client
|
||||||
|
.locales
|
||||||
|
.get(client.config.default_lang)
|
||||||
|
?.get(`c_${getFilename(__filename)}_opt1_name`) ?? '');
|
||||||
|
|
||||||
|
// If a command isn't specified
|
||||||
|
if (!desired_command) {
|
||||||
|
const fields: {
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
}[] = [];
|
||||||
|
|
||||||
|
// Load all the command per categories
|
||||||
|
client.commands.categories.forEach((commands_name, category) => {
|
||||||
|
const commands = commands_name.reduce((data, command_name) => {
|
||||||
|
return data + `\`/${command_name}\`, `;
|
||||||
|
}, '');
|
||||||
|
|
||||||
|
fields.push({
|
||||||
|
name: category.capitalize() + ` (${commands_name.length})`,
|
||||||
|
value: commands.slice(0, -2),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sends a list of commands sorted into categories
|
||||||
|
return interaction.reply({ embeds: [
|
||||||
|
new MessageEmbed()
|
||||||
|
.setColor('BLURPLE')
|
||||||
|
.setTitle(loc.get('c_help1'))
|
||||||
|
.setDescription(loc.get('c_help2'))
|
||||||
|
.addFields(fields),
|
||||||
|
] });
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a command is specified
|
||||||
|
const command = client.commands.list.get(desired_command);
|
||||||
|
if (!command) {
|
||||||
|
// Command don't exist
|
||||||
|
return interaction.reply({
|
||||||
|
content: `${loc.get('c_help3')} \`${desired_command}\``,
|
||||||
|
ephemeral: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send information about the command
|
||||||
|
return interaction.reply({ embeds: [
|
||||||
|
new MessageEmbed()
|
||||||
|
.setColor('BLURPLE')
|
||||||
|
.setTitle('`/' + command.data.name + '`')
|
||||||
|
.setDescription(
|
||||||
|
// Loads the description
|
||||||
|
// according to the user's locals
|
||||||
|
command.data.description_localizations
|
||||||
|
?.[interaction.locale as Locale]
|
||||||
|
?? command.data.description
|
||||||
|
),
|
||||||
|
] });
|
||||||
|
},
|
||||||
|
};
|
|
@ -7,16 +7,24 @@ export default {
|
||||||
data: (client: Client) => {
|
data: (client: Client) => {
|
||||||
const filename = getFilename(__filename);
|
const filename = getFilename(__filename);
|
||||||
return new SlashCommandBuilder()
|
return new SlashCommandBuilder()
|
||||||
.setName(filename.toLowerCase())
|
.setName(
|
||||||
.setDescription(client.locales.get(client.config.default_lang)?.get(`c_${filename}_desc`) ?? '?')
|
filename.toLowerCase())
|
||||||
.setNameLocalizations(getLocalizations(client, `c_${filename}_name`))
|
.setDescription(client.locales.get(client.config.default_lang)
|
||||||
.setDescriptionLocalizations(getLocalizations(client, `c_${filename}_desc`));
|
?.get(`c_${filename}_desc`) ?? '')
|
||||||
|
.setNameLocalizations(
|
||||||
|
getLocalizations(client, `c_${filename}_name`, true))
|
||||||
|
.setDescriptionLocalizations(
|
||||||
|
getLocalizations(client, `c_${filename}_desc`)
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
interaction: async (interaction: CommandInteraction, client: Client) => {
|
interaction: async (interaction: CommandInteraction, client: Client) => {
|
||||||
const loc = getLocale(client, interaction.locale);
|
const loc = getLocale(client, interaction.locale);
|
||||||
|
|
||||||
const sent = await interaction.reply({ content: 'Pinging...', fetchReply: true }) as Message;
|
const sent = await interaction.reply({
|
||||||
|
content: 'Pinging...',
|
||||||
|
fetchReply: true,
|
||||||
|
}) as Message;
|
||||||
|
|
||||||
interaction.editReply(
|
interaction.editReply(
|
||||||
`${loc?.get('c_ping1')}: \
|
`${loc?.get('c_ping1')}: \
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { getLocale } from '../../utils/locales';
|
||||||
/** https://discord.js.org/#/docs/discord.js/main/class/Client?scrollTo=e-interactionCreate */
|
/** https://discord.js.org/#/docs/discord.js/main/class/Client?scrollTo=e-interactionCreate */
|
||||||
export default (interaction: Interaction, client: Client) => {
|
export default (interaction: Interaction, client: Client) => {
|
||||||
if (interaction.isCommand()) {
|
if (interaction.isCommand()) {
|
||||||
const command = client.commands.get(interaction.commandName);
|
const command = client.commands.list.get(interaction.commandName);
|
||||||
if (!command) {
|
if (!command) {
|
||||||
const loc = getLocale(client, interaction.locale);
|
const loc = getLocale(client, interaction.locale);
|
||||||
return interaction.reply({
|
return interaction.reply({
|
||||||
|
|
|
@ -1,7 +1,16 @@
|
||||||
{
|
{
|
||||||
"e_interacreate_no_command": "Sorry, the command probably no longer exists...",
|
"e_interacreate_no_command": "Sorry, the command probably no longer exists...",
|
||||||
|
|
||||||
|
"c_ping_name": "Ping",
|
||||||
"c_ping_desc": "Pong!",
|
"c_ping_desc": "Pong!",
|
||||||
"c_ping1": "Roundtrip latency",
|
"c_ping1": "Roundtrip latency",
|
||||||
"c_ping2": "Websocket heartbeat"
|
"c_ping2": "Websocket heartbeat",
|
||||||
|
|
||||||
|
"c_help_name": "Help",
|
||||||
|
"c_help_desc": "Informations about commands",
|
||||||
|
"c_help_opt1_name": "command",
|
||||||
|
"c_help_opt1_desc": "Command wanted in depth.",
|
||||||
|
"c_help1": "List of categories and associated commands",
|
||||||
|
"c_help2": "`/help <command>` to get more information about a command.",
|
||||||
|
"c_help3": "Can't find :"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,16 @@
|
||||||
{
|
{
|
||||||
"e_interacreate_no_command": "Désolé, la commande n'existe plus...",
|
"e_interacreate_no_command": "Désolé, la commande n'existe plus...",
|
||||||
|
|
||||||
|
"c_ping_name": "Ping",
|
||||||
"c_ping_desc": "Pong!",
|
"c_ping_desc": "Pong!",
|
||||||
"c_ping1": "Latence totale",
|
"c_ping1": "Latence totale",
|
||||||
"c_ping2": "Latence du Websocket"
|
"c_ping2": "Latence du Websocket",
|
||||||
|
|
||||||
|
"c_help_name": "Aide",
|
||||||
|
"c_help_desc": "Informations sur les commandes",
|
||||||
|
"c_help_opt1_name": "commande",
|
||||||
|
"c_help_opt1_desc": "Commande voulu en détail.",
|
||||||
|
"c_help1": "Liste des catégories et des commandes associées",
|
||||||
|
"c_help2": "`/help <commande>` pour obtenir plus d'informations sur une commande.",
|
||||||
|
"c_help3": "Impossible de trouver :"
|
||||||
}
|
}
|
||||||
|
|
16
src/modules/string.ts
Normal file
16
src/modules/string.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
export {};
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
// Declarations
|
||||||
|
interface String {
|
||||||
|
/**
|
||||||
|
* Returns a copy of the string with the first letter capitalized.
|
||||||
|
*/
|
||||||
|
capitalize(): string,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Capitalize definition */
|
||||||
|
String.prototype.capitalize = function(this: string) {
|
||||||
|
return this[0].toUpperCase() + this.substring(1);
|
||||||
|
};
|
|
@ -16,13 +16,25 @@ declare module 'discord.js' {
|
||||||
default_lang: string
|
default_lang: string
|
||||||
},
|
},
|
||||||
/** Store all the slash commands */
|
/** Store all the slash commands */
|
||||||
commands: Collection<
|
commands: {
|
||||||
string,
|
categories: Collection<
|
||||||
{
|
/** Category name */
|
||||||
data: SlashCommandBuilder,
|
string,
|
||||||
interaction: (interaction: CommandInteraction, client: Client) => unknown
|
/** Name of the commands in the category */
|
||||||
}
|
string[]
|
||||||
>,
|
>,
|
||||||
|
list: Collection<
|
||||||
|
/** Command name */
|
||||||
|
string,
|
||||||
|
/** Command itself */
|
||||||
|
{
|
||||||
|
/** Data about the command */
|
||||||
|
data: SlashCommandBuilder,
|
||||||
|
/** How the command interact */
|
||||||
|
interaction: (interaction: CommandInteraction, client: Client) => unknown
|
||||||
|
}
|
||||||
|
>,
|
||||||
|
}
|
||||||
/** Store all the localizations */
|
/** Store all the localizations */
|
||||||
locales: Map<string, Map<string, string>>
|
locales: Map<string, Map<string, string>>
|
||||||
}
|
}
|
||||||
|
@ -42,7 +54,10 @@ export default async () => {
|
||||||
default_lang: process.env.DEFAULT_LANG ?? 'fr',
|
default_lang: process.env.DEFAULT_LANG ?? 'fr',
|
||||||
};
|
};
|
||||||
|
|
||||||
client.commands = new Collection();
|
client.commands = {
|
||||||
|
categories: new Collection(),
|
||||||
|
list: new Collection(),
|
||||||
|
};
|
||||||
|
|
||||||
console.log('Translations progression :');
|
console.log('Translations progression :');
|
||||||
client.locales = await loadLocales(client.config.default_lang);
|
client.locales = await loadLocales(client.config.default_lang);
|
||||||
|
|
|
@ -53,18 +53,21 @@ export const loadLocales = async (default_lang: string) => {
|
||||||
* @param text Name of string to fetch
|
* @param text Name of string to fetch
|
||||||
* @returns the dictionary
|
* @returns the dictionary
|
||||||
*/
|
*/
|
||||||
export const getLocalizations = (client: Client, text: string) => {
|
export const getLocalizations = (client: Client, text: string, lowercase = false) => {
|
||||||
const data: Record<string, string> = {};
|
const data: Record<string, string> = {};
|
||||||
|
|
||||||
// Load all the localizations
|
// Load all the localizations
|
||||||
client.locales.forEach((locale, lang) => {
|
client.locales.forEach((locale, lang) => {
|
||||||
// Fetch the text and fallback to default lang if needed
|
// Fetch the text and fallback to default lang if needed
|
||||||
// See getLocale for more info on why we *can* fallback
|
// See getLocale for more info on why we *can* fallback
|
||||||
const str = locale.get(text)
|
let str = locale.get(text)
|
||||||
?? client.locales.get(client.config.default_lang)?.get(text);
|
?? client.locales.get(client.config.default_lang)?.get(text);
|
||||||
|
|
||||||
// Store it if defined
|
// Store it if defined
|
||||||
if (str !== undefined) {
|
if (str !== undefined) {
|
||||||
|
if (lowercase) {
|
||||||
|
str = str.toLowerCase();
|
||||||
|
}
|
||||||
data[lang] = str;
|
data[lang] = str;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue