* Better handle of errors

* Add Modal support
* Use modal for reminder
* Add TODOs
This commit is contained in:
Mylloon 2022-11-04 21:10:56 +01:00
parent 8c7b87dbac
commit 694d41a128
Signed by: Anri
GPG key ID: A82D63DFF8D1317F
9 changed files with 159 additions and 24 deletions

View file

@ -1,5 +1,5 @@
import { SlashCommandBuilder } from '@discordjs/builders'; import { ModalActionRowComponentBuilder, SlashCommandBuilder } from '@discordjs/builders';
import { Client, ChatInputCommandInteraction } from 'discord.js'; import { Client, ChatInputCommandInteraction, ModalBuilder, TextInputBuilder, TextInputStyle, ActionRowBuilder } from 'discord.js';
import { getLocalizations } from '../../utils/locales'; import { getLocalizations } from '../../utils/locales';
import { getFilename } from '../../utils/misc'; import { getFilename } from '../../utils/misc';
import { strToSeconds } from '../../utils/time'; import { strToSeconds } from '../../utils/time';
@ -160,7 +160,6 @@ export default {
// plus user-friendly avec une interface (et en plus c'est // plus user-friendly avec une interface (et en plus c'est
// nouveau et cool) // nouveau et cool)
// eslint-disable-next-line no-case-declarations
let time = interaction.options.getString(loc_default?.get(`c_${filename}_sub1_opt1_name`) as string); let time = interaction.options.getString(loc_default?.get(`c_${filename}_sub1_opt1_name`) as string);
const message = interaction.options.getString(loc_default?.get(`c_${filename}_sub1_opt2_name`) as string); const message = interaction.options.getString(loc_default?.get(`c_${filename}_sub1_opt2_name`) as string);
let option = OptionReminder.Nothing; let option = OptionReminder.Nothing;
@ -178,16 +177,36 @@ export default {
} }
seconds = strToSeconds(time); seconds = strToSeconds(time);
await interaction.reply(`${option} - ${seconds}`);
} else {
// Boîte de dialogue
seconds = 0;
}
// Add the remind to the db
client.db.run('INSERT INTO reminder ( \ client.db.run('INSERT INTO reminder ( \
data, expiration_date, option_id, channel_id, creation_date, user_id, guild_id \ data, expiration_date, option_id, channel_id, creation_date, user_id, guild_id \
) VALUES ( ?, ?, ?, ?, ?, ?, ?);', [message, interaction.createdAt.getTime() + seconds, option.valueOf(), interaction.channelId, interaction.createdAt.getTime(), interaction.user.id, interaction.guildId]); ) VALUES ( ?, ?, ?, ?, ?, ?, ?);', [message, interaction.createdAt.getTime() + seconds, option.valueOf(), interaction.channelId, interaction.createdAt.getTime(), interaction.user.id, interaction.guildId]);
// Send confirmation to user
// TODO: local
await interaction.reply(`${option} - ${seconds}`);
} else {
// GUI
// TODO: local
const modal = new ModalBuilder()
.setCustomId('reminderGUI')
.setTitle('LOC_reminder');
// TODO: local
const timeGUI = new TextInputBuilder()
.setCustomId('timeGUI')
.setLabel('LOC_time')
.setStyle(TextInputStyle.Short)
.setPlaceholder('1h')
.setRequired(true);
modal.addComponents(new ActionRowBuilder<ModalActionRowComponentBuilder>().addComponents(timeGUI));
await interaction.showModal(modal);
// Response of the modal in /src/modals/misc/reminder
}
break; break;
} }
// List reminders // List reminders

View file

@ -3,10 +3,11 @@ 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.type === InteractionType.ApplicationCommand) { const loc = getLocale(client, interaction.locale);
switch (interaction.type) {
case InteractionType.ApplicationCommand: {
const command = client.commands.list.get(interaction.commandName); const command = client.commands.list.get(interaction.commandName);
if (!command) { if (!command) {
const loc = getLocale(client, interaction.locale);
return interaction.reply({ return interaction.reply({
content: loc.get('e_interacreate_no_command'), content: loc.get('e_interacreate_no_command'),
ephemeral: true, ephemeral: true,
@ -15,4 +16,21 @@ export default (interaction: Interaction, client: Client) => {
return command.interaction(interaction, client); return command.interaction(interaction, client);
} }
case InteractionType.ModalSubmit: {
const modal = client.modals.list.get(interaction.customId);
if (!modal) {
return interaction.reply({
// TODO: locale
content: `can't find ${interaction.customId}`,
ephemeral: true,
});
}
return modal.interaction(interaction, client);
}
default:
break;
}
}; };

View file

@ -18,6 +18,7 @@ export default async (client: Client) => {
); );
// Remove extension // Remove extension
// TODO: use utils functions
const event_type_ext = event_file.split('.'); const event_type_ext = event_file.split('.');
const ext = event_type_ext.pop(); const ext = event_type_ext.pop();
if (!(ext === 'js' || ext === 'ts')) { if (!(ext === 'js' || ext === 'ts')) {

View file

@ -1,5 +1,6 @@
import loadClient, { quit } from './utils/client'; import loadClient, { quit } from './utils/client';
import loadEvents from './events/loader'; import loadEvents from './events/loader';
import loadModals from './modals/loader';
import loadCommands from './commands/loader'; import loadCommands from './commands/loader';
import { logStart } from './utils/misc'; import { logStart } from './utils/misc';
@ -19,27 +20,37 @@ const run = async () => {
const client_name = 'Client'; const client_name = 'Client';
await loadClient() await loadClient()
.then(async client => { .then(async client => {
console.log(logStart(client_name, true));
// Events Discord.JS // Events Discord.JS
const events_name = 'Events'; const events_name = 'Events';
await loadEvents(client) await loadEvents(client)
.then(() => console.log(logStart(events_name, true))) .then(() => console.log(logStart(events_name, true)))
.catch(() => { .catch((err) => {
console.error(err);
throw logStart(events_name, false); throw logStart(events_name, false);
}); });
// Connect the bot to Discord.com // Connect the bot to Discord.com
await client.login(client.config.token_discord); await client.login(client.config.token_discord);
// Modals Discord.JS
const modals_name = 'Modals';
await loadModals(client)
.then(() => console.log(logStart(modals_name, true)))
.catch((err) => {
console.error(err);
throw logStart(modals_name, false);
});
// Commands Slash Discord.JS // Commands Slash Discord.JS
const commands_name = 'Commands'; const commands_name = 'Commands';
await loadCommands(client) await loadCommands(client)
.then(() => console.log(logStart(commands_name, true))) .then(() => console.log(logStart(commands_name, true)))
.catch(() => { .catch((err) => {
console.error(err);
throw logStart(commands_name, false); throw logStart(commands_name, false);
}); });
console.log(logStart(client_name, true));
console.log(`Botanique "${client.user?.username}" v${client.config.version} started!`); console.log(`Botanique "${client.user?.username}" v${client.config.version} started!`);
// ^C // ^C
@ -48,7 +59,8 @@ const run = async () => {
// Container force closed // Container force closed
process.on('SIGTERM', () => quit(client)); process.on('SIGTERM', () => quit(client));
}) })
.catch(() => { .catch((err) => {
console.error(err);
throw logStart(client_name, false); throw logStart(client_name, false);
}); });
}; };

36
src/modals/loader.ts Normal file
View file

@ -0,0 +1,36 @@
import { readdir } from 'fs/promises';
import { removeExtension } from '../utils/misc';
import { Client } from 'discord.js';
export default async (client: Client) => {
// Dossier des modals
const modals_categories = (await readdir(__dirname))
.filter(element => !element.endsWith('.js') && !element.endsWith('.ts'));
await Promise.all(
// For each categorie
modals_categories.map(async modals_category => {
// Retrieve all the commands
const modal_files = await readdir(`${__dirname}/${modals_category}`);
// Add the category to the collection for the help command
client.modals.categories.set(
modals_category,
modal_files.map(removeExtension),
);
// Add the modal
return Promise.all(
modal_files.map(async modal_file => {
const modal = (
await import(`../modals/${modals_category}/${modal_file}`)
).default;
// Add it to the collection so the interaction will work
client.modals.list.set(modal.data.name, modal);
return modal.data;
}),
);
}),
);
};

View file

@ -0,0 +1,21 @@
import { ModalSubmitInteraction } from 'discord.js';
import { getFilename } from '../../utils/misc';
export default {
data: {
name: getFilename(__filename),
},
interaction: async (interaction: ModalSubmitInteraction) => {
if (!interaction.isModalSubmit()) {
console.log('not modal called modal :/');
return;
}
const time = interaction.fields.fields.get('timeGUI')?.value;
return interaction.reply({
content: `${time}`,
ephemeral: true,
});
},
};

View file

@ -16,6 +16,28 @@ declare module 'discord.js' {
/** Default lang used */ /** Default lang used */
default_lang: string default_lang: string
}, },
/** Store all the modals */
modals: {
categories: Collection<
/** Category name */
string,
/** Name of the modals in the category */
string[]
>,
list: Collection<
/** Modal name */
string,
/** Modal itself */
{
/** Data about the modal */
data: {
name: string
},
/** How the modal interact */
interaction: (interaction: ModalSubmitInteraction, client: Client) => unknown
}
>,
}
/** Store all the slash commands */ /** Store all the slash commands */
commands: { commands: {
categories: Collection< categories: Collection<

View file

@ -19,6 +19,11 @@ export default async () => {
default_lang: process.env.DEFAULT_LANG ?? 'fr', default_lang: process.env.DEFAULT_LANG ?? 'fr',
}; };
client.modals = {
categories: new Collection(),
list: new Collection(),
};
client.commands = { client.commands = {
categories: new Collection(), categories: new Collection(),
list: new Collection(), list: new Collection(),

View file

@ -7,6 +7,7 @@ import { GuildMember } from 'discord.js';
* @returns String * @returns String
*/ */
export const logStart = (name: string, status: boolean) => { export const logStart = (name: string, status: boolean) => {
// TODO Handle precision about the error if status is false
return `> ${name} ${status === true ? '✅' : '❌'}`; return `> ${name} ${status === true ? '✅' : '❌'}`;
}; };