diff --git a/.eslintrc.json b/.eslintrc.json index 10787ca..5d752e9 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,54 +1,43 @@ { - - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended" - ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "project": ["./tsconfig.json"] - }, - "plugins": [ - "@typescript-eslint" - ], - "rules": { - "arrow-spacing": ["warn", { "before": true, "after": true }], - "brace-style": ["error"], - "comma-dangle": ["error", "always-multiline"], - "comma-spacing": "error", - "comma-style": "error", - "curly": ["error", "multi-line", "consistent"], - "dot-location": ["error", "property"], - "handle-callback-err": "off", - "indent": ["error", "tab", { "SwitchCase": 1 }], - "keyword-spacing": "error", - "max-nested-callbacks": ["error", { "max": 4 }], - "max-statements-per-line": ["error", { "max": 2 }], - "no-console": "off", - "no-empty-function": "error", - "no-floating-decimal": "error", - "no-inline-comments": "error", - "no-lonely-if": "error", - "no-multi-spaces": "error", - "no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }], - "no-shadow": "off", - "@typescript-eslint/no-shadow": ["error", { "allow": ["err", "resolve", "reject"] }], - "no-trailing-spaces": ["error"], - "no-var": "error", - "object-curly-spacing": ["error", "always"], - "prefer-const": "error", - "quotes": ["error", "single"], - "semi": ["error", "always"], - "space-before-blocks": "error", - "space-before-function-paren": ["error", { - "anonymous": "never", - "named": "never", - "asyncArrow": "always" - }], - "space-in-parens": "error", - "space-infix-ops": "error", - "space-unary-ops": "error", - "spaced-comment": "error", - "yoda": "error" - } + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "prettier" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "project": ["./tsconfig.json"] + }, + "plugins": ["@typescript-eslint"], + "rules": { + "arrow-spacing": ["warn", { "before": true, "after": true }], + "comma-style": "error", + "curly": ["error", "multi-line", "consistent"], + "dot-location": ["error", "property"], + "handle-callback-err": "off", + "max-nested-callbacks": ["error", { "max": 4 }], + "max-statements-per-line": ["error", { "max": 2 }], + "no-console": "off", + "no-empty-function": "error", + "no-floating-decimal": "error", + "no-inline-comments": "error", + "no-lonely-if": "error", + "no-multi-spaces": "error", + "no-multiple-empty-lines": [ + "error", + { "max": 2, "maxEOF": 1, "maxBOF": 0 } + ], + "no-shadow": "off", + "@typescript-eslint/no-shadow": [ + "error", + { "allow": ["err", "resolve", "reject"] } + ], + "no-trailing-spaces": ["error"], + "no-var": "error", + "prefer-const": "error", + "space-in-parens": "error", + "space-unary-ops": "error", + "spaced-comment": "error", + "yoda": "error" + } } diff --git a/.gitea/issue_template/BUG.md b/.gitea/issue_template/BUG.md index 699f95b..cf83544 100644 --- a/.gitea/issue_template/BUG.md +++ b/.gitea/issue_template/BUG.md @@ -1,13 +1,10 @@ --- - name: "🐞 Rapport d'un bug" about: "Signal un problème rencontré" ref: "main" labels: - -- bug -- "help wanted" - + - bug + - "help wanted" --- Bot version: v`X.Y.Z` diff --git a/.gitea/issue_template/FEATURE.md b/.gitea/issue_template/FEATURE.md index be5e348..dc8411f 100644 --- a/.gitea/issue_template/FEATURE.md +++ b/.gitea/issue_template/FEATURE.md @@ -1,10 +1,7 @@ --- - name: "💫 Demande une fonctionnalitée" about: "Propose une nouvelle fonctionnalité à ajouter" ref: "main" labels: - -- enhancement - + - enhancement --- diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..1521c8b --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +dist diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1 @@ +{} diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 978d630..1d7ac85 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,5 +1,3 @@ { - "recommendations": [ - "dbaeumer.vscode-eslint" - ] + "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 00426da..f9604d4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,8 +1,8 @@ { - "editor.tabSize": 4, - "editor.insertSpaces": false, + "editor.tabSize": 2, + "editor.insertSpaces": false, - "files.insertFinalNewline": true, - "files.trimFinalNewlines": true, - "files.trimTrailingWhitespace": true + "files.insertFinalNewline": true, + "files.trimFinalNewlines": true, + "files.trimTrailingWhitespace": true } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7d1db25..d40e86b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -40,26 +40,26 @@ contient toujours toutes les chaînes de caractère dont le bot a besoin. La norme pour les nom dans les fichiers est la suivante : -- Chaîne de charactère des commandes : - `c` est utilisé pour `C`ommande. +- Chaîne de charactère des commandes : + `c` est utilisé pour `C`ommande. - - `c_NOM-COMMANDE_name` : Nom de la commande - - `c_NOM-COMMANDE_desc` : Description de la commande - - `c_NOM-COMMANDE_optX_name` : Nom de l'option X - - `c_NOM-COMMANDE_optX_desc` : Description de l'option X - - `c_NOM-COMMANDE_subX_name` : Nom de la sous-commande X - - `c_NOM-COMMANDE_subX_desc` : Description de la sous-commande X - - `c_NOM-COMMANDEX` : `X` le numéro de la chaîne de caractère + - `c_NOM-COMMANDE_name` : Nom de la commande + - `c_NOM-COMMANDE_desc` : Description de la commande + - `c_NOM-COMMANDE_optX_name` : Nom de l'option X + - `c_NOM-COMMANDE_optX_desc` : Description de l'option X + - `c_NOM-COMMANDE_subX_name` : Nom de la sous-commande X + - `c_NOM-COMMANDE_subX_desc` : Description de la sous-commande X + - `c_NOM-COMMANDEX` : `X` le numéro de la chaîne de caractère - Évidemment ça peut s'additionner, - par exemple : `c_NOM-COMMANDE_subX_optX_desc`. + Évidemment ça peut s'additionner, + par exemple : `c_NOM-COMMANDE_subX_optX_desc`. -- Chaîne de charactère des évènements : - `e` est utilisé pour `E`vènements. - - `e_NOM-EVENEMENT_N` : `N` le nom de la chaîne de caractère -- Chaîne de charactère des utils : - `u` est utilisé pour `U`tilitaires. - - `u_NOM-FICHIER-UTILS_N` : `N` le nom de la chaîne de caractère +- Chaîne de charactère des évènements : + `e` est utilisé pour `E`vènements. + - `e_NOM-EVENEMENT_N` : `N` le nom de la chaîne de caractère +- Chaîne de charactère des utils : + `u` est utilisé pour `U`tilitaires. + - `u_NOM-FICHIER-UTILS_N` : `N` le nom de la chaîne de caractère ### Ajouter une langue @@ -70,8 +70,8 @@ La norme pour les nom dans les fichiers est la suivante : [cf. au dessus](#langues). 3. Ce sont les valeurs des clés (le texte à gauche des `:`) qui doivent être traduits. Merci par avance ! - > Ne vous forcez pas à tout traduire. Même une contribution avec - > une seule variable de modifiée compte ! + > Ne vous forcez pas à tout traduire. Même une contribution avec + > une seule variable de modifiée compte ! 4. Une fois terminée, [ouvrez une Pull Request](#soumettre-ses-modifications). ### Mettre à jour une langue @@ -79,23 +79,23 @@ La norme pour les nom dans les fichiers est la suivante : 1. Rechercher la langue dans le dossier [src/locales/](./src/locales/). 2. Modifier/Ajouter des traductions comme [expliquer au dessus](#ajouter-une-langue) (à partir du `3.`). - > Pensez à vérifier si de nouvelles valeurs n'ont pas été ajouté dans - > le fichier langue par défaut, [cf. au dessus](#langues). + > Pensez à vérifier si de nouvelles valeurs n'ont pas été ajouté dans + > le fichier langue par défaut, [cf. au dessus](#langues). ## Projet Le code se trouve dans le dosier [src/](./src/). Dans ce dossier il y a : -- [commands/](./src/commands/) qui contient toutes les commandes, rangés par - catégories -- [events/](./src/events/) qui contient tous les évènements, rangés par - catégories -- [locales/](./src/locales/) qui contient tous les fichiers de langue -- [modules/](./src/modules/) qui contient les extensions utilisé, - par exemple, pour utiliser la fonction `capitalize()` d'un string, il faut - importer le fichier `string.ts` qui se trouve dans le dossier -- [utils/](./src/utils/) qui contient toutes les fonctions utilitaires, rangés - par fichiers +- [commands/](./src/commands/) qui contient toutes les commandes, rangés par + catégories +- [events/](./src/events/) qui contient tous les évènements, rangés par + catégories +- [locales/](./src/locales/) qui contient tous les fichiers de langue +- [modules/](./src/modules/) qui contient les extensions utilisé, + par exemple, pour utiliser la fonction `capitalize()` d'un string, il faut + importer le fichier `string.ts` qui se trouve dans le dossier +- [utils/](./src/utils/) qui contient toutes les fonctions utilitaires, rangés + par fichiers Les dossiers [commands/](./src/commands/) et [events/](./src/events/) contiennent chaquin un fichier `loader.js` qui charge respectivement @@ -116,42 +116,42 @@ import { getLocale, getLocalizations } from "../../utils/locales"; import { getFilename } from "../../utils/misc"; export default { - scope: () => [], + scope: () => [], - 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`) - ); - }, + 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`) + ); + }, - interaction: async ( - interaction: ChatInputCommandInteraction, - client: Client - ) => { - const loc = getLocale(client, interaction.locale); + interaction: async ( + interaction: ChatInputCommandInteraction, + client: Client + ) => { + const loc = getLocale(client, interaction.locale); - /* Votre code ici */ - }, + /* Votre code ici */ + }, }; ``` Rapidement, cette structure comporte 3 éléments : -- `scope` : une liste de guildId où la commande est disponible, si la liste est - est vide, la commande est disponible partout -- `data` : représente les données envoyées à l'API de Discord -- `interaction` : représente le comportement de la commande +- `scope` : une liste de guildId où la commande est disponible, si la liste est + est vide, la commande est disponible partout +- `data` : représente les données envoyées à l'API de Discord +- `interaction` : représente le comportement de la commande Ce template vous permet de commencé rapidement votre commande car il contient déjà tout ce qu'il faut pour le support des langues. Pensez bien à ne pas écrire @@ -161,10 +161,10 @@ les [fichiers de langues](./src/locales/), c'est à ça que la variable Vous devez aussi ajouter **obligatoirement** : -- `"c_COMMANDE_name": "NOM"` au fichier de langue, avec `COMMANDE` le nom de - la commande et `NOM` le nom de votre commande. -- `"c_COMMANDE_desc": "DESCRIPTION"` au fichier de langue, avec `COMMANDE` - le nom de la commande et `DESCRIPTION` la description de votre commande. +- `"c_COMMANDE_name": "NOM"` au fichier de langue, avec `COMMANDE` le nom de + la commande et `NOM` le nom de votre commande. +- `"c_COMMANDE_desc": "DESCRIPTION"` au fichier de langue, avec `COMMANDE` + le nom de la commande et `DESCRIPTION` la description de votre commande. ## Ajouter un évènement @@ -203,14 +203,14 @@ Quand vous modifiez quelque chose, pensez à mettre-à-jour les langues. Si vous ne savez pas traduire dans une langue, ne vous forcez pas, supprimer simplement la traduction. -- [Créez un fork](https://git.kennel.ml/repo/fork/76) et poussez - vos modifications dans ce dernier. +- [Créez un fork](https://git.kennel.ml/repo/fork/76) et poussez + vos modifications dans ce dernier. Pour commencer, vous pouvez jeté un oeil aux [tickets facilement résolvable](https://git.kennel.ml/ConfrerieDuKassoulait/Botanique/issues?state=open&labels=82). -- De préférences, les fonctions, méthodes et variables seront écrites - en anglais, ainsi que les commits afin que chacun puisse contribuer. +- De préférences, les fonctions, méthodes et variables seront écrites + en anglais, ainsi que les commits afin que chacun puisse contribuer. ## Soumettre ses modifications @@ -243,10 +243,10 @@ Pour commencer, vous pouvez jeté un oeil aux ## Gestion du dépôt -- On ne push jamais directement sur la branche `main`. -- Quand on merge des modifications vers `main`, on fait un _squash_, - l'historique des modifications reste disponible dans - [le graph](https://git.kennel.ml/ConfrerieDuKassoulait/Botanique/graph). -- De préférences, suivre les indications de - [ce post](https://gist.github.com/revett/88ee5abf5a9a097b4c88) (c'est un peu la - même que dans le `4.` de [la partie précédente](#soumettre-ses-modifications)). +- On ne push jamais directement sur la branche `main`. +- Quand on merge des modifications vers `main`, on fait un _squash_, + l'historique des modifications reste disponible dans + [le graph](https://git.kennel.ml/ConfrerieDuKassoulait/Botanique/graph). +- De préférences, suivre les indications de + [ce post](https://gist.github.com/revett/88ee5abf5a9a097b4c88) (c'est un peu la + même que dans le `4.` de [la partie précédente](#soumettre-ses-modifications)). diff --git a/README.md b/README.md index 5cd5d24..e2b0a7b 100644 --- a/README.md +++ b/README.md @@ -3,23 +3,29 @@ [**Ajoute le bot à ton serveur**](https://discord.com/api/oauth2/authorize?client_id=965598852407230494&permissions=8&scope=bot%20applications.commands) ## Lancer le bot + ### En local + > Cloner le repo. > Spécifier un fichier `.env` en suivant [l'exemple](config/example.env). > Installer les dépendences du bot. + ```bash npm install ``` > Lancer le bot. + ```bash npm run main ``` ### Avec Docker (Recommandé) + > Facile avec `docker-compose` + ```docker version: "3.9" services: @@ -34,23 +40,28 @@ services: ``` ## Variables d'environnements -| Nom | Description | Par défaut | Commentaire -| :-----------: | :--------------: | :--------: | :-: -| TOKEN_DISCORD | Token Discord | Aucune | -| DEFAULT_LANG | Langue par défaut | `fr` | Expérimental, si la langue par défaut n'est pas complète (càd 100%), le bot pourrait ne pas fonctionner correctement.
Liste des traductions disponibles [ici](./src/locales/). + +| Nom | Description | Par défaut | Commentaire | +| :-----------: | :---------------: | :--------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| TOKEN_DISCORD | Token Discord | Aucune | +| DEFAULT_LANG | Langue par défaut | `fr` | Expérimental, si la langue par défaut n'est pas complète (càd 100%), le bot pourrait ne pas fonctionner correctement.
Liste des traductions disponibles [ici](./src/locales/). | ## Volumes -| Chemin | Description -| :-------: | :-: -| `/config` | Dossier de configuration, par exemple, c'est ici que la base de donnée est. + +| Chemin | Description | +| :-------: | :-------------------------------------------------------------------------: | +| `/config` | Dossier de configuration, par exemple, c'est ici que la base de donnée est. | # Contribuer + Toute contribution est la bienvenue ! Pour commencer, lis le [fichier de contribution](./CONTRIBUTING.md). # Licence + Voir le [fichier LICENCE](./LICENCE). # Références + [Photo de profil](https://picrew.me/image_maker/1497656) diff --git a/package-lock.json b/package-lock.json index ee38fad..e3a3fb0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "@typescript-eslint/parser": "^5.30.7", "dotenv": "^16.0.1", "eslint": "^8.20.0", + "prettier": "2.8.3", "ts-node-dev": "^2.0.0" } }, @@ -2438,6 +2439,21 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.3.tgz", + "integrity": "sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", @@ -5110,6 +5126,12 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "prettier": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.3.tgz", + "integrity": "sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==", + "dev": true + }, "promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", diff --git a/package.json b/package.json index 1ef6342..5b8e30f 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "scripts": { "main": "rm -r dist 2> /dev/null; npx tsc && node ./dist/index.js", "debug": "npx tsnd --respawn ./src/index.ts", - "lint": "npx eslint src" + "lint": "npx eslint src", + "format": "npx prettier --check src" }, "repository": { "type": "git", @@ -17,18 +18,19 @@ "dependencies": { "@discordjs/rest": "^1.1.0", "@types/sqlite3": "^3.1.8", + "@types/uuid": "^9.0.0", "discord-api-types": "^0.36.3", "discord.js": "^14.3.0", "sqlite3": "^5.0.11", "typescript": "^4.7.4", - "uuid": "^9.0.0", - "@types/uuid": "^9.0.0" + "uuid": "^9.0.0" }, "devDependencies": { "@typescript-eslint/eslint-plugin": "^5.30.7", "@typescript-eslint/parser": "^5.30.7", "dotenv": "^16.0.1", "eslint": "^8.20.0", + "prettier": "2.8.3", "ts-node-dev": "^2.0.0" } } diff --git a/src/buttons/loader.ts b/src/buttons/loader.ts index 0dbad68..83f79b5 100644 --- a/src/buttons/loader.ts +++ b/src/buttons/loader.ts @@ -1,39 +1,44 @@ -import { readdir } from 'fs/promises'; -import { removeExtension } from '../utils/misc'; -import { ChatInputCommandInteraction, Client, MessageComponentInteraction } from 'discord.js'; -import { getLocale } from '../utils/locales'; +import { readdir } from "fs/promises"; +import { removeExtension } from "../utils/misc"; +import { + ChatInputCommandInteraction, + Client, + MessageComponentInteraction, +} from "discord.js"; +import { getLocale } from "../utils/locales"; export default async (client: Client) => { - // Dossier des buttons - const buttons_categories = (await readdir(__dirname)) - .filter(element => !element.endsWith('.js') && !element.endsWith('.ts')); + // Dossier des buttons + const buttons_categories = (await readdir(__dirname)).filter( + (element) => !element.endsWith(".js") && !element.endsWith(".ts") + ); - await Promise.all( - // For each categorie - buttons_categories.map(async buttons_category => { - // Retrieve all the commands - const button_files = await readdir(`${__dirname}/${buttons_category}`); + await Promise.all( + // For each categorie + buttons_categories.map(async (buttons_category) => { + // Retrieve all the commands + const button_files = await readdir(`${__dirname}/${buttons_category}`); - // Add the category to the collection for the help command - client.buttons.categories.set( - buttons_category, - button_files.map(removeExtension), - ); + // Add the category to the collection for the help command + client.buttons.categories.set( + buttons_category, + button_files.map(removeExtension) + ); - // Add the button - return Promise.all( - button_files.map(async button_file => { - const button = ( - await import(`../buttons/${buttons_category}/${button_file}`) - ).default; + // Add the button + return Promise.all( + button_files.map(async (button_file) => { + const button = ( + await import(`../buttons/${buttons_category}/${button_file}`) + ).default; - // Add it to the collection so the interaction will work - client.buttons.list.set(button.data.name, button); - return button.data; - }), - ); - }), - ); + // Add it to the collection so the interaction will work + client.buttons.list.set(button.data.name, button); + return button.data; + }) + ); + }) + ); }; /** @@ -43,26 +48,34 @@ export default async (client: Client) => { * @param id Button ID * @param deferUpdate defer update in case update take time */ -export const collect = (client: Client, interaction: ChatInputCommandInteraction | MessageComponentInteraction, id: string, deferUpdate = false) => { - const loc = getLocale(client, interaction.locale); - const button = client.buttons.list.get(id.split('_')[0]); +export const collect = ( + client: Client, + interaction: ChatInputCommandInteraction | MessageComponentInteraction, + id: string, + deferUpdate = false +) => { + const loc = getLocale(client, interaction.locale); + const button = client.buttons.list.get(id.split("_")[0]); - if (!button) { - interaction.reply({ - content: loc.get('e_interacreate_no_button'), - ephemeral: true, - }); - } + if (!button) { + interaction.reply({ + content: loc.get("e_interacreate_no_button"), + ephemeral: true, + }); + } - const filter = (i: MessageComponentInteraction) => i.customId === id; - const collector = interaction.channel?.createMessageComponentCollector({ filter, max: 1 }); - collector?.on('collect', async (i) => { - if (deferUpdate) { - await i.deferUpdate(); - } - const msg = await button?.interaction(i, client); - if (msg !== undefined) { - await i.update(msg); - } - }); + const filter = (i: MessageComponentInteraction) => i.customId === id; + const collector = interaction.channel?.createMessageComponentCollector({ + filter, + max: 1, + }); + collector?.on("collect", async (i) => { + if (deferUpdate) { + await i.deferUpdate(); + } + const msg = await button?.interaction(i, client); + if (msg !== undefined) { + await i.update(msg); + } + }); }; diff --git a/src/buttons/misc/reminderList-next.ts b/src/buttons/misc/reminderList-next.ts index 76cc5ae..0b1a64f 100644 --- a/src/buttons/misc/reminderList-next.ts +++ b/src/buttons/misc/reminderList-next.ts @@ -1,56 +1,73 @@ -import { ActionRowBuilder, ButtonBuilder, ButtonStyle, Client, MessageComponentInteraction, User } from 'discord.js'; -import { v4 as uuidv4 } from 'uuid'; -import { getLocale } from '../../utils/locales'; -import { getFilename } from '../../utils/misc'; -import { embedListReminders } from '../../utils/reminder'; -import { collect } from '../loader'; +import { + ActionRowBuilder, + ButtonBuilder, + ButtonStyle, + Client, + MessageComponentInteraction, + User, +} from "discord.js"; +import { v4 as uuidv4 } from "uuid"; +import { getLocale } from "../../utils/locales"; +import { getFilename } from "../../utils/misc"; +import { embedListReminders } from "../../utils/reminder"; +import { collect } from "../loader"; export default { - data: { - name: getFilename(__filename), - }, - interaction: async (interaction: MessageComponentInteraction, client: Client) => { - const loc = getLocale(client, interaction.locale); - const embed_desc = interaction.message.embeds.at(0)?.description as string; + data: { + name: getFilename(__filename), + }, + interaction: async ( + interaction: MessageComponentInteraction, + client: Client + ) => { + const loc = getLocale(client, interaction.locale); + const embed_desc = interaction.message.embeds.at(0)?.description as string; - // Retrieve Pages - const pageMax = Number(/(\d+)(?!.*\d)/gm.exec(embed_desc)?.[0]); - let page = Number(/(?!• \s+)\d(?=\/)/gm.exec(embed_desc)?.[0]); - if (page + 1 > pageMax) { - page = 1; - } else { - page++; - } + // Retrieve Pages + const pageMax = Number(/(\d+)(?!.*\d)/gm.exec(embed_desc)?.[0]); + let page = Number(/(?!• \s+)\d(?=\/)/gm.exec(embed_desc)?.[0]); + if (page + 1 > pageMax) { + page = 1; + } else { + page++; + } - // Retrieve user - const userId = /(?!<@)\d+(?=>)/gm.exec(embed_desc)?.[0] as string; - const user = client.users.cache.get(userId) as User; + // Retrieve user + const userId = /(?!<@)\d+(?=>)/gm.exec(embed_desc)?.[0] as string; + const user = client.users.cache.get(userId) as User; - // Fetch list - const list = await embedListReminders(client, user, interaction.guildId, page, interaction.locale); + // Fetch list + const list = await embedListReminders( + client, + user, + interaction.guildId, + page, + interaction.locale + ); - const idPrec = 'reminderList-prec_' + uuidv4(); - const idNext = 'reminderList-next_' + uuidv4(); - const row = new ActionRowBuilder() - .addComponents( - new ButtonBuilder() - .setCustomId(idPrec) - .setLabel(loc.get('c_reminder12')) - .setStyle(ButtonStyle.Primary)) - .addComponents( - new ButtonBuilder() - .setCustomId(idNext) - .setLabel(loc.get('c_reminder13')) - .setStyle(ButtonStyle.Primary), - ); + const idPrec = "reminderList-prec_" + uuidv4(); + const idNext = "reminderList-next_" + uuidv4(); + const row = new ActionRowBuilder() + .addComponents( + new ButtonBuilder() + .setCustomId(idPrec) + .setLabel(loc.get("c_reminder12")) + .setStyle(ButtonStyle.Primary) + ) + .addComponents( + new ButtonBuilder() + .setCustomId(idNext) + .setLabel(loc.get("c_reminder13")) + .setStyle(ButtonStyle.Primary) + ); - // Buttons interactions - collect(client, interaction, idPrec); - collect(client, interaction, idNext); + // Buttons interactions + collect(client, interaction, idPrec); + collect(client, interaction, idNext); - return { - embeds: [list], - components: [row], - }; - }, + return { + embeds: [list], + components: [row], + }; + }, }; diff --git a/src/buttons/misc/reminderList-prec.ts b/src/buttons/misc/reminderList-prec.ts index 06a1019..33c91fd 100644 --- a/src/buttons/misc/reminderList-prec.ts +++ b/src/buttons/misc/reminderList-prec.ts @@ -1,56 +1,73 @@ -import { ActionRowBuilder, ButtonBuilder, ButtonStyle, Client, MessageComponentInteraction, User } from 'discord.js'; -import { v4 as uuidv4 } from 'uuid'; -import { getLocale } from '../../utils/locales'; -import { getFilename } from '../../utils/misc'; -import { embedListReminders } from '../../utils/reminder'; -import { collect } from '../loader'; +import { + ActionRowBuilder, + ButtonBuilder, + ButtonStyle, + Client, + MessageComponentInteraction, + User, +} from "discord.js"; +import { v4 as uuidv4 } from "uuid"; +import { getLocale } from "../../utils/locales"; +import { getFilename } from "../../utils/misc"; +import { embedListReminders } from "../../utils/reminder"; +import { collect } from "../loader"; export default { - data: { - name: getFilename(__filename), - }, - interaction: async (interaction: MessageComponentInteraction, client: Client) => { - const loc = getLocale(client, interaction.locale); - const embed_desc = interaction.message.embeds.at(0)?.description as string; + data: { + name: getFilename(__filename), + }, + interaction: async ( + interaction: MessageComponentInteraction, + client: Client + ) => { + const loc = getLocale(client, interaction.locale); + const embed_desc = interaction.message.embeds.at(0)?.description as string; - // Retrieve Pages - const pageMax = Number(/(\d+)(?!.*\d)/gm.exec(embed_desc)?.[0]); - let page = Number(/(?!• \s+)\d(?=\/)/gm.exec(embed_desc)?.[0]); - if (page - 1 == 0) { - page = pageMax; - } else { - page--; - } + // Retrieve Pages + const pageMax = Number(/(\d+)(?!.*\d)/gm.exec(embed_desc)?.[0]); + let page = Number(/(?!• \s+)\d(?=\/)/gm.exec(embed_desc)?.[0]); + if (page - 1 == 0) { + page = pageMax; + } else { + page--; + } - // Retrieve user - const userId = /(?!<@)\d+(?=>)/gm.exec(embed_desc)?.[0] as string; - const user = client.users.cache.get(userId) as User; + // Retrieve user + const userId = /(?!<@)\d+(?=>)/gm.exec(embed_desc)?.[0] as string; + const user = client.users.cache.get(userId) as User; - // Fetch list - const list = await embedListReminders(client, user, interaction.guildId, page, interaction.locale); + // Fetch list + const list = await embedListReminders( + client, + user, + interaction.guildId, + page, + interaction.locale + ); - const idPrec = 'reminderList-prec_' + uuidv4(); - const idNext = 'reminderList-next_' + uuidv4(); - const row = new ActionRowBuilder() - .addComponents( - new ButtonBuilder() - .setCustomId(idPrec) - .setLabel(loc.get('c_reminder12')) - .setStyle(ButtonStyle.Primary)) - .addComponents( - new ButtonBuilder() - .setCustomId(idNext) - .setLabel(loc.get('c_reminder13')) - .setStyle(ButtonStyle.Primary), - ); + const idPrec = "reminderList-prec_" + uuidv4(); + const idNext = "reminderList-next_" + uuidv4(); + const row = new ActionRowBuilder() + .addComponents( + new ButtonBuilder() + .setCustomId(idPrec) + .setLabel(loc.get("c_reminder12")) + .setStyle(ButtonStyle.Primary) + ) + .addComponents( + new ButtonBuilder() + .setCustomId(idNext) + .setLabel(loc.get("c_reminder13")) + .setStyle(ButtonStyle.Primary) + ); - // Buttons interactions - collect(client, interaction, idPrec); - collect(client, interaction, idNext); + // Buttons interactions + collect(client, interaction, idPrec); + collect(client, interaction, idNext); - return { - embeds: [list], - components: [row], - }; - }, + return { + embeds: [list], + components: [row], + }; + }, }; diff --git a/src/commands/loader.ts b/src/commands/loader.ts index ca44dc2..8f02d48 100644 --- a/src/commands/loader.ts +++ b/src/commands/loader.ts @@ -1,77 +1,84 @@ -import { REST } from '@discordjs/rest'; -import { Routes } from 'discord-api-types/v9'; -import { Client } from 'discord.js'; -import { readdir } from 'fs/promises'; -import { removeExtension } from '../utils/misc'; +import { REST } from "@discordjs/rest"; +import { Routes } from "discord-api-types/v9"; +import { Client } from "discord.js"; +import { readdir } from "fs/promises"; +import { removeExtension } from "../utils/misc"; /** Load all the commands. */ export default async (client: Client) => { - const rest = new REST({ version: '10' }).setToken(client.token ?? ''); + const rest = new REST({ version: "10" }).setToken(client.token ?? ""); - const command_categories = (await readdir(__dirname)) - .filter(element => !element.endsWith('.js') && !element.endsWith('.ts')); + const command_categories = (await readdir(__dirname)).filter( + (element) => !element.endsWith(".js") && !element.endsWith(".ts") + ); - const commands = ( - await Promise.all( - // For each categorie - command_categories.map(async command_category => { - // Retrieve all the commands - const command_files = await readdir(`${__dirname}/${command_category}`); + const commands = ( + await Promise.all( + // For each categorie + command_categories.map(async (command_category) => { + // Retrieve all the commands + 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 category to the collection for the help command + client.commands.categories.set( + command_category, + command_files.map(removeExtension) + ); - // Add the command - return Promise.all( - command_files.map(async command_file => { - const command = ( - await import(`../commands/${command_category}/${command_file}`) - ).default; + // Add the command + return Promise.all( + command_files.map(async (command_file) => { + const command = ( + await import(`../commands/${command_category}/${command_file}`) + ).default; - // Add it to the collection so the interaction will work - command.data = command.data(client); - client.commands.list.set(command.data.name, command); + // Add it to the collection so the interaction will work + command.data = command.data(client); + client.commands.list.set(command.data.name, command); - return command; - }), - ); - }), - ) - ).flat(2); + return command; + }) + ); + }) + ) + ).flat(2); - // Send guilds commands to Discord - const scopedCommands = new Map(); + // Send guilds commands to Discord + const scopedCommands = new Map(); - // Add commands to guild where the bot is - const allowedGuilds = client.guilds.cache; + // Add commands to guild where the bot is + const allowedGuilds = client.guilds.cache; - // Assign each commands to the guilds - commands.filter((c) => c.scope().length > 0).forEach((c) => { - c.scope().forEach((guild: string) => { - if (allowedGuilds.get(guild) !== undefined) { - const guildCommands = scopedCommands.get(guild); - if (guildCommands === undefined) { - scopedCommands.set(guild, [c.data.toJSON()]); - } else { - guildCommands.push(c.data.toJSON()); - scopedCommands.set(guild, guildCommands); - } - } - }); - }); + // Assign each commands to the guilds + commands + .filter((c) => c.scope().length > 0) + .forEach((c) => { + c.scope().forEach((guild: string) => { + if (allowedGuilds.get(guild) !== undefined) { + const guildCommands = scopedCommands.get(guild); + if (guildCommands === undefined) { + scopedCommands.set(guild, [c.data.toJSON()]); + } else { + guildCommands.push(c.data.toJSON()); + scopedCommands.set(guild, guildCommands); + } + } + }); + }); - scopedCommands - .forEach(async (command, guild) => await rest.put( - Routes.applicationGuildCommands(client.user?.id as string, guild), { - body: command, - })); + scopedCommands.forEach( + async (command, guild) => + await rest.put( + Routes.applicationGuildCommands(client.user?.id as string, guild), + { + body: command, + } + ) + ); - // Send global commands to Discord - const globalCommands = commands.filter((c) => c.scope().length == 0); - return await rest.put(Routes.applicationCommands(client.user?.id as string), { - body: globalCommands.map((c) => c.data.toJSON()), - }); + // Send global commands to Discord + const globalCommands = commands.filter((c) => c.scope().length == 0); + return await rest.put(Routes.applicationCommands(client.user?.id as string), { + body: globalCommands.map((c) => c.data.toJSON()), + }); }; diff --git a/src/commands/misc/archive.ts b/src/commands/misc/archive.ts index 46b3fb1..8d1ecbb 100644 --- a/src/commands/misc/archive.ts +++ b/src/commands/misc/archive.ts @@ -1,129 +1,159 @@ -import { SlashCommandBuilder } from '@discordjs/builders'; -import { ChannelType, Client, Colors, CommandInteraction, EmbedBuilder, NonThreadGuildBasedChannel } from 'discord.js'; -import '../../modules/string'; -import { getLocale, getLocalizations } from '../../utils/locales'; -import { getFilename } from '../../utils/misc'; +import { SlashCommandBuilder } from "@discordjs/builders"; +import { + ChannelType, + Client, + Colors, + CommandInteraction, + EmbedBuilder, + NonThreadGuildBasedChannel, +} from "discord.js"; +import "../../modules/string"; +import { getLocale, getLocalizations } from "../../utils/locales"; +import { getFilename } from "../../utils/misc"; export default { - scope: () => ['807244911350906920'], + scope: () => ["807244911350906920"], - 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`)) + 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`)) - ); - }, + // 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 desiredCat = interaction.options.get(client - .locales - .get(client.config.default_lang) - ?.get(`c_${getFilename(__filename)}_opt1_name`) ?? '')?.value as string; + interaction: async (interaction: CommandInteraction, client: Client) => { + const loc = getLocale(client, interaction.locale); + const desiredCat = interaction.options.get( + client.locales + .get(client.config.default_lang) + ?.get(`c_${getFilename(__filename)}_opt1_name`) ?? "" + )?.value as string; - // If a category isn't specified - if (!desiredCat) { + // If a category isn't specified + if (!desiredCat) { + // Sends a list of commands sorted into categories + return interaction.reply({ + embeds: [ + new EmbedBuilder() + .setColor(Colors.Blurple) + .setTitle(loc.get("c_archive1")) + .setDescription(loc.get("c_archive2")), + ], + }); + } - // Sends a list of commands sorted into categories - return interaction.reply({ - embeds: [ - new EmbedBuilder() - .setColor(Colors.Blurple) - .setTitle(loc.get('c_archive1')) - .setDescription(loc.get('c_archive2')), - ], - }); - } + // If a category is specified + const cleanCat = ["L1", "L2", "L3", "M1", "M2"]; + const channel = cleanCat.includes(desiredCat); + if (!channel) { + // Category doesn't exist or is not included + return interaction.reply({ + content: `${loc.get("c_archive3")} \`${desiredCat}\``, + ephemeral: true, + }); + } - // If a category is specified - const cleanCat = ['L1', 'L2', 'L3', 'M1', 'M2']; - const channel = cleanCat.includes(desiredCat); - if (!channel) { - // Category doesn't exist or is not included - return interaction.reply({ - content: `${loc.get('c_archive3')} \`${desiredCat}\``, - ephemeral: true, - }); - } + const allChannel = interaction.guild?.channels.fetch(); + allChannel?.then(async (channelGuild) => { + // Retrieve category to archive + const catToArchive = channelGuild + .filter((chan) => chan?.type == ChannelType.GuildCategory) + .filter((chan) => chan?.name == desiredCat); - const allChannel = interaction.guild?.channels.fetch(); - allChannel?.then(async channelGuild => { - // Retrieve category to archive - const catToArchive = channelGuild - .filter(chan => chan?.type == ChannelType.GuildCategory) - .filter(chan => chan?.name == desiredCat); + // Create/Retrieve the archive category + const catArchivedName = "archive - " + desiredCat; + const catArchivedMap = channelGuild + .filter((chan) => chan?.type == ChannelType.GuildCategory) + .filter((chan) => chan?.name == catArchivedName); - // Create/Retrieve the archive category - const catArchivedName = 'archive - ' + desiredCat; - const catArchivedMap = channelGuild - .filter(chan => chan?.type == ChannelType.GuildCategory) - .filter(chan => chan?.name == catArchivedName); + let catArchived: NonThreadGuildBasedChannel | null | undefined; + if (catArchivedMap.size > 0) { + catArchived = catArchivedMap.at(0); + } else { + catArchived = await interaction.guild?.channels.create({ + name: catArchivedName, + type: ChannelType.GuildCategory, + }); + } - let catArchived: NonThreadGuildBasedChannel | null | undefined; - if (catArchivedMap.size > 0) { - catArchived = catArchivedMap.at(0); - } else { - catArchived = await interaction.guild?.channels - .create({ name: catArchivedName, type: ChannelType.GuildCategory }); - } + const allChannelDesired = channelGuild + .filter((chan) => chan?.type == 0) + .filter( + (chan) => chan?.parentId == catToArchive.map((cat) => cat?.id)[0] + ); - const allChannelDesired = channelGuild - .filter(chan => chan?.type == 0) - .filter(chan => chan?.parentId == catToArchive.map(cat => cat?.id)[0]); + // If no channels in the source category + if (allChannelDesired.size == 0) { + return interaction.reply({ + embeds: [ + new EmbedBuilder() + .setColor(Colors.Blurple) + .setTitle(loc.get("c_archive6")) + .setDescription(loc.get("c_archive7")), + ], + }); + } - // If no channels in the source category - if (allChannelDesired.size == 0) { - return interaction.reply({ - embeds: [ - new EmbedBuilder() - .setColor(Colors.Blurple) - .setTitle(loc.get('c_archive6')) - .setDescription( - loc.get('c_archive7') - ), - ], - }); - } + // Move channels to the archived categoryx + allChannelDesired.forEach((elem) => + elem?.setParent(catArchived?.id as string) + ); - // Move channels to the archived categoryx - allChannelDesired.forEach(elem => elem?.setParent(catArchived?.id as string)); - - return interaction.reply({ - embeds: [ - new EmbedBuilder() - .setColor(Colors.Blurple) - .setTitle(loc.get('c_archive4') - + ' `' - + catToArchive.map(cat => cat?.name) - + '` ' - + loc.get('c_archive5') - + ' `' - + catArchivedName - + '`') - .setDescription( - allChannelDesired - .map(cgD => cgD?.name).toString().replaceAll(',', '\n') - ), - ], - }); - }); - }, + return interaction.reply({ + embeds: [ + new EmbedBuilder() + .setColor(Colors.Blurple) + .setTitle( + loc.get("c_archive4") + + " `" + + catToArchive.map((cat) => cat?.name) + + "` " + + loc.get("c_archive5") + + " `" + + catArchivedName + + "`" + ) + .setDescription( + allChannelDesired + .map((cgD) => cgD?.name) + .toString() + .replaceAll(",", "\n") + ), + ], + }); + }); + }, }; diff --git a/src/commands/misc/help.ts b/src/commands/misc/help.ts index 3593ab2..83f940e 100644 --- a/src/commands/misc/help.ts +++ b/src/commands/misc/help.ts @@ -1,98 +1,126 @@ -import { SlashCommandBuilder } from '@discordjs/builders'; -import { Locale } from 'discord-api-types/v9'; -import { Client, ChatInputCommandInteraction, EmbedBuilder, Colors } from 'discord.js'; -import { getLocale, getLocalizations } from '../../utils/locales'; -import { getFilename } from '../../utils/misc'; -import '../../modules/string'; +import { SlashCommandBuilder } from "@discordjs/builders"; +import { Locale } from "discord-api-types/v9"; +import { + Client, + ChatInputCommandInteraction, + EmbedBuilder, + Colors, +} from "discord.js"; +import { getLocale, getLocalizations } from "../../utils/locales"; +import { getFilename } from "../../utils/misc"; +import "../../modules/string"; export default { - scope: () => [], + scope: () => [], - 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`)) + 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`)) - ); - }, + // 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: ChatInputCommandInteraction, 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`) ?? ''); + interaction: async ( + interaction: ChatInputCommandInteraction, + 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; - }[] = []; + // If a command isn't specified + if (!desired_command) { + const fields: { + name: string; + value: string; + }[] = []; - // Load all the command per categories - // TODO: Check if the command exist in the context (guild) - client.commands.categories.forEach((commands_name, category) => { - const commands = commands_name.reduce((data, command_name) => { - return data + `\`/${command_name}\`, `; - }, ''); + // Load all the command per categories + // TODO: Check if the command exist in the context (guild) + 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), - }); - }); + 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 EmbedBuilder() - .setColor(Colors.Blurple) - .setTitle(loc.get('c_help1')) - .setDescription(loc.get('c_help2')) - .addFields(fields), - ] }); - } + // Sends a list of commands sorted into categories + return interaction.reply({ + embeds: [ + new EmbedBuilder() + .setColor(Colors.Blurple) + .setTitle(loc.get("c_help1")) + .setDescription(loc.get("c_help2")) + .addFields(fields), + ], + }); + } - // If a command is specified - // TODO: Check if the command exist in the context (guild) - 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, - }); - } + // If a command is specified + // TODO: Check if the command exist in the context (guild) + 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 EmbedBuilder() - .setColor(Colors.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 - ), - ] }); - }, + // Send information about the command + return interaction.reply({ + embeds: [ + new EmbedBuilder() + .setColor(Colors.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 + ), + ], + }); + }, }; diff --git a/src/commands/misc/ping.ts b/src/commands/misc/ping.ts index 90b5c2b..b46f9b1 100644 --- a/src/commands/misc/ping.ts +++ b/src/commands/misc/ping.ts @@ -1,36 +1,43 @@ -import { SlashCommandBuilder } from '@discordjs/builders'; -import { ChatInputCommandInteraction, Client, Message } from 'discord.js'; -import { getLocale, getLocalizations } from '../../utils/locales'; -import { getFilename } from '../../utils/misc'; +import { SlashCommandBuilder } from "@discordjs/builders"; +import { ChatInputCommandInteraction, Client, Message } from "discord.js"; +import { getLocale, getLocalizations } from "../../utils/locales"; +import { getFilename } from "../../utils/misc"; export default { - scope: () => [], + scope: () => [], - 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`) - ); - }, + 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`) + ); + }, - interaction: async (interaction: ChatInputCommandInteraction, client: Client) => { - const loc = getLocale(client, interaction.locale); + interaction: async ( + interaction: ChatInputCommandInteraction, + client: Client + ) => { + 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( - `${loc?.get('c_ping1')}: \ + interaction.editReply( + `${loc?.get("c_ping1")}: \ ${sent.createdTimestamp - interaction.createdTimestamp}ms -${loc?.get('c_ping2')}: ${client.ws.ping}ms.`); - }, +${loc?.get("c_ping2")}: ${client.ws.ping}ms.` + ); + }, }; diff --git a/src/commands/misc/prep.ts b/src/commands/misc/prep.ts index 301ed0f..9f34fb0 100644 --- a/src/commands/misc/prep.ts +++ b/src/commands/misc/prep.ts @@ -1,117 +1,144 @@ -import { SlashCommandBuilder } from '@discordjs/builders'; -import { ChannelType, Client, Colors, CommandInteraction, EmbedBuilder } from 'discord.js'; -import '../../modules/string'; -import { getLocale, getLocalizations } from '../../utils/locales'; -import { getFilename } from '../../utils/misc'; +import { SlashCommandBuilder } from "@discordjs/builders"; +import { + ChannelType, + Client, + Colors, + CommandInteraction, + EmbedBuilder, +} from "discord.js"; +import "../../modules/string"; +import { getLocale, getLocalizations } from "../../utils/locales"; +import { getFilename } from "../../utils/misc"; export default { - scope: () => ['807244911350906920'], + scope: () => ["807244911350906920"], - 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`)) + 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`)) - ); - }, + // 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_cat = interaction.options.get(client - .locales - .get(client.config.default_lang) - ?.get(`c_${getFilename(__filename)}_opt1_name`) ?? '')?.value as string; + interaction: async (interaction: CommandInteraction, client: Client) => { + const loc = getLocale(client, interaction.locale); + const desired_cat = interaction.options.get( + client.locales + .get(client.config.default_lang) + ?.get(`c_${getFilename(__filename)}_opt1_name`) ?? "" + )?.value as string; - // If a category isn't specified - if (!desired_cat) { + // If a category isn't specified + if (!desired_cat) { + // Sends a list of commands sorted into categories + return interaction.reply({ + embeds: [ + new EmbedBuilder() + .setColor(Colors.Blurple) + .setTitle(loc.get("c_prep1")) + .setDescription(loc.get("c_prep2")), + ], + }); + } - // Sends a list of commands sorted into categories - return interaction.reply({ - embeds: [ - new EmbedBuilder() - .setColor(Colors.Blurple) - .setTitle(loc.get('c_prep1')) - .setDescription(loc.get('c_prep2')), - ], - }); - } + // If a category is specified + const allowedCategories = ["L1", "L2", "L3", "M1", "M2"]; + const channel = allowedCategories.includes(desired_cat); + if (!channel) { + // Category doesn't exist or is not allowed + return interaction.reply({ + content: `${loc.get("c_prep3")} \`${desired_cat}\``, + ephemeral: true, + }); + } - // If a category is specified - const allowedCategories = ['L1', 'L2', 'L3', 'M1', 'M2']; - const channel = allowedCategories.includes(desired_cat); - if (!channel) { - // Category doesn't exist or is not allowed - return interaction.reply({ - content: `${loc.get('c_prep3')} \`${desired_cat}\``, - ephemeral: true, - }); - } + // Send information about the command + const allChannel = interaction.guild?.channels.fetch(); + allChannel?.then((channel_guild) => { + const cat_to_prep = channel_guild + .filter((chan) => chan?.type == ChannelType.GuildCategory) + .filter((chan) => chan?.name == desired_cat); + const cat_to_prep_id = cat_to_prep.map((cat) => cat?.id); + const cat_to_prep_name = cat_to_prep.map((cat) => cat?.name); - // Send information about the command - const allChannel = interaction.guild?.channels.fetch(); - allChannel?.then(channel_guild => { - const cat_to_prep = channel_guild.filter(chan => chan?.type == ChannelType.GuildCategory).filter(chan => chan?.name == desired_cat); - const cat_to_prep_id = cat_to_prep.map(cat => cat?.id); - const cat_to_prep_name = cat_to_prep.map(cat => cat?.name); + // console.log(cat_to_prep); + const all_channel_desired = channel_guild + .filter((chan) => chan?.type == 0) + .filter((chan) => chan?.parentId == cat_to_prep_id[0]); + const all_channel_desired_name = all_channel_desired.map( + (c_d) => c_d?.name + ); - // console.log(cat_to_prep); - const all_channel_desired = channel_guild.filter(chan => chan?.type == 0).filter(chan => chan?.parentId == cat_to_prep_id[0]); - const all_channel_desired_name = all_channel_desired.map(c_d => c_d?.name); + let desc = ""; - let desc = ''; + const general = "général"; + if ( + all_channel_desired_name.filter((cdn) => cdn == general).length == 0 + ) { + interaction.guild?.channels.create({ + name: general, + type: 0, + parent: cat_to_prep_id[0], + }); + desc = general + loc.get("c_prep5") + "\n"; + } - const general = 'général'; - if (all_channel_desired_name.filter(cdn => cdn == general).length == 0) { - interaction.guild?.channels.create({ - name: general, - type: 0, - parent: cat_to_prep_id[0], - }); - desc = general + loc.get('c_prep5') + '\n'; - } + const info = "informations"; + if (all_channel_desired_name.filter((cdn) => cdn == info).length == 0) { + interaction.guild?.channels.create({ + name: info, + type: 0, + parent: cat_to_prep_id[0], + }); - const info = 'informations'; - if (all_channel_desired_name.filter(cdn => cdn == info).length == 0) { - interaction.guild?.channels.create({ - name: info, - type: 0, - parent: cat_to_prep_id[0], - }); + desc += "`" + info + "` " + loc.get("c_prep5") + "\n"; + } - desc += '`' + info + '` ' + loc.get('c_prep5') + '\n'; - } + if (desc == "") { + desc = loc.get("c_prep6"); + } - if (desc == '') { - desc = loc.get('c_prep6'); - } - - return interaction.reply({ - embeds: [ - new EmbedBuilder() - .setColor(Colors.Blurple) - .setTitle(loc.get('c_prep4') + cat_to_prep_name) - .setDescription( - desc, - ), - ], - }); - }); - }, + return interaction.reply({ + embeds: [ + new EmbedBuilder() + .setColor(Colors.Blurple) + .setTitle(loc.get("c_prep4") + cat_to_prep_name) + .setDescription(desc), + ], + }); + }); + }, }; diff --git a/src/commands/misc/reminder.ts b/src/commands/misc/reminder.ts index 70e4a53..4d61cb5 100644 --- a/src/commands/misc/reminder.ts +++ b/src/commands/misc/reminder.ts @@ -1,277 +1,354 @@ -import { ModalActionRowComponentBuilder, SlashCommandBuilder } from '@discordjs/builders'; -import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChatInputCommandInteraction, Client, ModalBuilder, TextInputBuilder, TextInputStyle } from 'discord.js'; -import { v4 as uuidv4 } from 'uuid'; -import { collect } from '../../buttons/loader'; -import { getLocale, getLocalizations } from '../../utils/locales'; -import { getFilename } from '../../utils/misc'; -import { checkOwnershipReminder, deleteReminder, embedListReminders, getReminderInfo, newReminder } from '../../utils/reminder'; +import { + ModalActionRowComponentBuilder, + SlashCommandBuilder, +} from "@discordjs/builders"; +import { + ActionRowBuilder, + ButtonBuilder, + ButtonStyle, + ChatInputCommandInteraction, + Client, + ModalBuilder, + TextInputBuilder, + TextInputStyle, +} from "discord.js"; +import { v4 as uuidv4 } from "uuid"; +import { collect } from "../../buttons/loader"; +import { getLocale, getLocalizations } from "../../utils/locales"; +import { getFilename } from "../../utils/misc"; +import { + checkOwnershipReminder, + deleteReminder, + embedListReminders, + getReminderInfo, + newReminder, +} from "../../utils/reminder"; export default { - scope: () => [], + scope: () => [], - data: (client: Client) => { - const filename = getFilename(__filename); - const loc_default = client.locales.get(client.config.default_lang); - if (!loc_default) { - return; - } + data: (client: Client) => { + const filename = getFilename(__filename); + const loc_default = client.locales.get(client.config.default_lang); + if (!loc_default) { + return; + } - return new SlashCommandBuilder() - // Command - .setName(filename.toLowerCase()) - .setDescription(loc_default.get(`c_${filename}_desc`) ?? '') - .setNameLocalizations( - getLocalizations(client, `c_${filename}_name`, true) - ).setDescriptionLocalizations( - getLocalizations(client, `c_${filename}_desc`) - ) + return ( + new SlashCommandBuilder() + // Command + .setName(filename.toLowerCase()) + .setDescription(loc_default.get(`c_${filename}_desc`) ?? "") + .setNameLocalizations( + getLocalizations(client, `c_${filename}_name`, true) + ) + .setDescriptionLocalizations( + getLocalizations(client, `c_${filename}_desc`) + ) - // New reminder - .addSubcommand(subcommand => subcommand - .setName( - loc_default.get(`c_${filename}_sub1_name`) - ?.toLowerCase() ?? '' - ).setDescription( - loc_default.get(`c_${filename}_sub1_desc`) ?? '' - ).setNameLocalizations( - getLocalizations(client, `c_${filename}_sub1_name`, true) - ).setDescriptionLocalizations( - getLocalizations(client, `c_${filename}_sub1_desc`) - ) + // New reminder + .addSubcommand((subcommand) => + subcommand + .setName( + loc_default.get(`c_${filename}_sub1_name`)?.toLowerCase() ?? "" + ) + .setDescription(loc_default.get(`c_${filename}_sub1_desc`) ?? "") + .setNameLocalizations( + getLocalizations(client, `c_${filename}_sub1_name`, true) + ) + .setDescriptionLocalizations( + getLocalizations(client, `c_${filename}_sub1_desc`) + ) - // Specified Time - .addStringOption(option => option - .setName( - loc_default.get(`c_${filename}_sub1_opt1_name`) - ?.toLowerCase() ?? '' - ).setDescription( - loc_default.get(`c_${filename}_sub1_opt1_desc`) ?? '' - ).setNameLocalizations( - getLocalizations( - client, - `c_${filename}_sub1_opt1_name`, - true - ) - ).setDescriptionLocalizations( - getLocalizations(client, `c_${filename}_sub1_opt1_desc`) - ) - ) + // Specified Time + .addStringOption((option) => + option + .setName( + loc_default + .get(`c_${filename}_sub1_opt1_name`) + ?.toLowerCase() ?? "" + ) + .setDescription( + loc_default.get(`c_${filename}_sub1_opt1_desc`) ?? "" + ) + .setNameLocalizations( + getLocalizations(client, `c_${filename}_sub1_opt1_name`, true) + ) + .setDescriptionLocalizations( + getLocalizations(client, `c_${filename}_sub1_opt1_desc`) + ) + ) - // Specified message (not required) - .addStringOption(option => option - .setName( - loc_default.get(`c_${filename}_sub1_opt2_name`) - ?.toLowerCase() ?? '' - ).setDescription( - loc_default.get(`c_${filename}_sub1_opt2_desc`) ?? '' - ).setNameLocalizations( - getLocalizations( - client, - `c_${filename}_sub1_opt2_name`, - true - ) - ).setDescriptionLocalizations( - getLocalizations(client, `c_${filename}_sub1_opt2_desc`) - ) - ) - ) + // Specified message (not required) + .addStringOption((option) => + option + .setName( + loc_default + .get(`c_${filename}_sub1_opt2_name`) + ?.toLowerCase() ?? "" + ) + .setDescription( + loc_default.get(`c_${filename}_sub1_opt2_desc`) ?? "" + ) + .setNameLocalizations( + getLocalizations(client, `c_${filename}_sub1_opt2_name`, true) + ) + .setDescriptionLocalizations( + getLocalizations(client, `c_${filename}_sub1_opt2_desc`) + ) + ) + ) - // List reminders - .addSubcommand(subcommand => subcommand - .setName( - loc_default.get(`c_${filename}_sub2_name`) - ?.toLowerCase() ?? '' - ).setDescription( - loc_default.get(`c_${filename}_sub2_desc`) ?? '' - ).setNameLocalizations( - getLocalizations(client, `c_${filename}_sub2_name`, true) - ).setDescriptionLocalizations( - getLocalizations(client, `c_${filename}_sub2_desc`) - ) + // List reminders + .addSubcommand((subcommand) => + subcommand + .setName( + loc_default.get(`c_${filename}_sub2_name`)?.toLowerCase() ?? "" + ) + .setDescription(loc_default.get(`c_${filename}_sub2_desc`) ?? "") + .setNameLocalizations( + getLocalizations(client, `c_${filename}_sub2_name`, true) + ) + .setDescriptionLocalizations( + getLocalizations(client, `c_${filename}_sub2_desc`) + ) - // User - .addUserOption(option => option - .setName( - loc_default.get(`c_${filename}_sub2_opt1_name`) - ?.toLowerCase() ?? '' - ).setDescription( - loc_default.get(`c_${filename}_sub2_opt1_desc`) ?? '' - ).setNameLocalizations( - getLocalizations( - client, - `c_${filename}_sub2_opt1_name`, - true - ) - ).setDescriptionLocalizations( - getLocalizations(client, `c_${filename}_sub2_opt1_desc`) - ) - ) + // User + .addUserOption((option) => + option + .setName( + loc_default + .get(`c_${filename}_sub2_opt1_name`) + ?.toLowerCase() ?? "" + ) + .setDescription( + loc_default.get(`c_${filename}_sub2_opt1_desc`) ?? "" + ) + .setNameLocalizations( + getLocalizations(client, `c_${filename}_sub2_opt1_name`, true) + ) + .setDescriptionLocalizations( + getLocalizations(client, `c_${filename}_sub2_opt1_desc`) + ) + ) - // Page - .addIntegerOption(option => option - .setName( - loc_default.get(`c_${filename}_sub2_opt2_name`) - ?.toLowerCase() ?? '' - ).setDescription( - loc_default.get(`c_${filename}_sub2_opt2_desc`) ?? '' - ).setNameLocalizations( - getLocalizations( - client, - `c_${filename}_sub2_opt2_name`, - true - ) - ).setDescriptionLocalizations( - getLocalizations(client, `c_${filename}_sub2_opt2_desc`) - ) - ) - ) + // Page + .addIntegerOption((option) => + option + .setName( + loc_default + .get(`c_${filename}_sub2_opt2_name`) + ?.toLowerCase() ?? "" + ) + .setDescription( + loc_default.get(`c_${filename}_sub2_opt2_desc`) ?? "" + ) + .setNameLocalizations( + getLocalizations(client, `c_${filename}_sub2_opt2_name`, true) + ) + .setDescriptionLocalizations( + getLocalizations(client, `c_${filename}_sub2_opt2_desc`) + ) + ) + ) - // Delete a reminder - .addSubcommand(subcommand => subcommand - .setName( - loc_default.get(`c_${filename}_sub3_name`) - ?.toLowerCase() ?? '' - ).setDescription( - loc_default.get(`c_${filename}_sub3_desc`) ?? '' - ).setNameLocalizations( - getLocalizations(client, `c_${filename}_sub3_name`, true) - ).setDescriptionLocalizations( - getLocalizations(client, `c_${filename}_sub3_desc`) - ) + // Delete a reminder + .addSubcommand((subcommand) => + subcommand + .setName( + loc_default.get(`c_${filename}_sub3_name`)?.toLowerCase() ?? "" + ) + .setDescription(loc_default.get(`c_${filename}_sub3_desc`) ?? "") + .setNameLocalizations( + getLocalizations(client, `c_${filename}_sub3_name`, true) + ) + .setDescriptionLocalizations( + getLocalizations(client, `c_${filename}_sub3_desc`) + ) - // ID - .addIntegerOption(option => option - .setName( - loc_default.get(`c_${filename}_sub3_opt1_name`) - ?.toLowerCase() ?? '' - ).setDescription( - loc_default.get(`c_${filename}_sub3_opt1_desc`) ?? '' - ).setNameLocalizations( - getLocalizations( - client, - `c_${filename}_sub3_opt1_name`, - true - ) - ).setDescriptionLocalizations( - getLocalizations(client, `c_${filename}_sub3_opt1_desc`) - ).setRequired(true) - ), - ); - }, + // ID + .addIntegerOption((option) => + option + .setName( + loc_default + .get(`c_${filename}_sub3_opt1_name`) + ?.toLowerCase() ?? "" + ) + .setDescription( + loc_default.get(`c_${filename}_sub3_opt1_desc`) ?? "" + ) + .setNameLocalizations( + getLocalizations(client, `c_${filename}_sub3_opt1_name`, true) + ) + .setDescriptionLocalizations( + getLocalizations(client, `c_${filename}_sub3_opt1_desc`) + ) + .setRequired(true) + ) + ) + ); + }, - interaction: async (interaction: ChatInputCommandInteraction, client: Client) => { - const loc_default = client.locales.get(client.config.default_lang); - const filename = getFilename(__filename); - const loc = getLocale(client, interaction.locale); + interaction: async ( + interaction: ChatInputCommandInteraction, + client: Client + ) => { + const loc_default = client.locales.get(client.config.default_lang); + const filename = getFilename(__filename); + const loc = getLocale(client, interaction.locale); - const subcommand = interaction.options.getSubcommand(); - switch (subcommand) { - // New reminder - case loc_default?.get(`c_${filename}_sub1_name`) - ?.toLowerCase() ?? '': { + const subcommand = interaction.options.getSubcommand(); + switch (subcommand) { + // New reminder + case loc_default?.get(`c_${filename}_sub1_name`)?.toLowerCase() ?? "": { + // If time is already renseigned + const time = interaction.options.getString( + loc_default?.get(`c_${filename}_sub1_opt1_name`) as string + ); + if (time != null) { + // Use the cli because we already have enough data + return newReminder(client, time, { + locale: interaction.locale, + message: interaction.options.getString( + loc_default?.get(`c_${filename}_sub1_opt2_name`) as string + ), + createdAt: interaction.createdAt.getTime(), + channelId: interaction.channelId, + userId: interaction.user.id, + guildId: interaction.guildId, + }).then((msg) => + interaction.reply({ + content: msg as string, + ephemeral: true, + }) + ); + } else { + // Show modal to user to get at least the time + const modal = new ModalBuilder() + .setCustomId("reminderGUI") + .setTitle(loc.get(`c_${filename}_name`).capitalize()); - // If time is already renseigned - const time = interaction.options.getString(loc_default?.get(`c_${filename}_sub1_opt1_name`) as string); - if (time != null) { - // Use the cli because we already have enough data - return newReminder(client, time, { - locale: interaction.locale, - message: interaction.options.getString(loc_default?.get(`c_${filename}_sub1_opt2_name`) as string), - createdAt: interaction.createdAt.getTime(), - channelId: interaction.channelId, - userId: interaction.user.id, - guildId: interaction.guildId, - }).then((msg) => interaction.reply({ - content: msg as string, - ephemeral: true, - })); - } else { - // Show modal to user to get at least the time - const modal = new ModalBuilder() - .setCustomId('reminderGUI') - .setTitle(loc.get(`c_${filename}_name`).capitalize()); + const timeGUI = new TextInputBuilder() + .setCustomId("reminderGUI-time") + .setLabel(loc.get(`c_${filename}_sub1_opt1_name`).capitalize()) + .setStyle(TextInputStyle.Short) + .setPlaceholder("1h") + .setRequired(true); - const timeGUI = new TextInputBuilder() - .setCustomId('reminderGUI-time') - .setLabel(loc.get(`c_${filename}_sub1_opt1_name`).capitalize()) - .setStyle(TextInputStyle.Short) - .setPlaceholder('1h') - .setRequired(true); + const messageGUI = new TextInputBuilder() + .setCustomId("reminderGUI-message") + .setLabel(loc.get(`c_${filename}_sub1_opt2_name`).capitalize()) + .setStyle(TextInputStyle.Paragraph) + .setPlaceholder(loc.get(`c_${filename}_sub1_opt2_desc`)) + .setRequired(false); - const messageGUI = new TextInputBuilder() - .setCustomId('reminderGUI-message') - .setLabel(loc.get(`c_${filename}_sub1_opt2_name`).capitalize()) - .setStyle(TextInputStyle.Paragraph) - .setPlaceholder(loc.get(`c_${filename}_sub1_opt2_desc`)) - .setRequired(false); + modal.addComponents( + new ActionRowBuilder().addComponents( + timeGUI + ), + new ActionRowBuilder().addComponents( + messageGUI + ) + ); - modal.addComponents( - new ActionRowBuilder().addComponents(timeGUI), - new ActionRowBuilder().addComponents(messageGUI) - ); + return interaction.showModal(modal); + } + } + // List reminders + case loc_default?.get(`c_${filename}_sub2_name`)?.toLowerCase() ?? "": { + // Which user to show + let user = interaction.options.getUser( + loc_default?.get(`c_${filename}_sub2_opt1_name`) as string + ); + if (user == null) { + user = interaction.user; + } - return interaction.showModal(modal); - } - } - // List reminders - case loc_default?.get(`c_${filename}_sub2_name`) - ?.toLowerCase() ?? '': { - // Which user to show - let user = interaction.options.getUser(loc_default?.get(`c_${filename}_sub2_opt1_name`) as string); - if (user == null) { - user = interaction.user; - } + const page = + interaction.options.getInteger( + loc_default?.get(`c_${filename}_sub2_opt2_name`) as string + ) ?? 1; + const list = await embedListReminders( + client, + user, + interaction.guildId, + page, + interaction.locale + ); - const page = interaction.options.getInteger(loc_default?.get(`c_${filename}_sub2_opt2_name`) as string) ?? 1; - const list = await embedListReminders(client, user, interaction.guildId, page, interaction.locale); + const idPrec = "reminderList-prec_" + uuidv4(); + const idNext = "reminderList-next_" + uuidv4(); + const row = new ActionRowBuilder() + .addComponents( + new ButtonBuilder() + .setCustomId(idPrec) + .setLabel(loc.get(`c_${filename}12`)) + .setStyle(ButtonStyle.Primary) + ) + .addComponents( + new ButtonBuilder() + .setCustomId(idNext) + .setLabel(loc.get(`c_${filename}13`)) + .setStyle(ButtonStyle.Primary) + ); - const idPrec = 'reminderList-prec_' + uuidv4(); - const idNext = 'reminderList-next_' + uuidv4(); - const row = new ActionRowBuilder() - .addComponents( - new ButtonBuilder() - .setCustomId(idPrec) - .setLabel(loc.get(`c_${filename}12`)) - .setStyle(ButtonStyle.Primary)) - .addComponents( - new ButtonBuilder() - .setCustomId(idNext) - .setLabel(loc.get(`c_${filename}13`)) - .setStyle(ButtonStyle.Primary), - ); + // Buttons interactions + collect(client, interaction, idPrec); + collect(client, interaction, idNext); - // Buttons interactions - collect(client, interaction, idPrec); - collect(client, interaction, idNext); + return await interaction.reply({ + ephemeral: true, + embeds: [list], + components: [row], + }); + } + // Delete a reminder + case loc_default?.get(`c_${filename}_sub3_name`)?.toLowerCase() ?? "": { + const id = interaction.options.getInteger( + loc_default?.get(`c_${filename}_sub3_opt1_name`) as string + ); + if (id === null) { + return interaction.reply({ + content: loc.get(`c_${filename}2`), + ephemeral: true, + }); + } - return await interaction.reply({ ephemeral: true, embeds: [list], components: [row] }); - } - // Delete a reminder - case loc_default?.get(`c_${filename}_sub3_name`) - ?.toLowerCase() ?? '': { - const id = interaction.options.getInteger(loc_default?.get(`c_${filename}_sub3_opt1_name`) as string); - if (id === null) { - return interaction.reply({ content: loc.get(`c_${filename}2`), ephemeral: true }); - } + // Check if the ID exists and belongs to the user + if ( + await checkOwnershipReminder( + client, + id, + interaction.user.id, + interaction.guildId ?? "0" + ) + ) { + return interaction.reply({ + content: loc.get(`c_${filename}3`), + ephemeral: true, + }); + } - // Check if the ID exists and belongs to the user - if (await checkOwnershipReminder(client, id, interaction.user.id, interaction.guildId ?? '0')) { - return interaction.reply({ content: loc.get(`c_${filename}3`), ephemeral: true }); - } + // Stop timeout + const reminderInfo = await getReminderInfo(client, id); + clearTimeout(reminderInfo.timeout_id); - // Stop timeout - const reminderInfo = await getReminderInfo(client, id); - clearTimeout(reminderInfo.timeout_id); - - // Delete from database - return deleteReminder(client, reminderInfo.creation_date, reminderInfo.user_id) - .then(() => interaction.reply({ content: `Reminder **#${id}** supprimé !`, ephemeral: true })); - - } - default: { - console.error(`${__filename}: unknown subcommand (${subcommand})`); - break; - } - } - }, + // Delete from database + return deleteReminder( + client, + reminderInfo.creation_date, + reminderInfo.user_id + ).then(() => + interaction.reply({ + content: `Reminder **#${id}** supprimé !`, + ephemeral: true, + }) + ); + } + default: { + console.error(`${__filename}: unknown subcommand (${subcommand})`); + break; + } + } + }, }; diff --git a/src/events/client/ready.ts b/src/events/client/ready.ts index 764a4bd..f645667 100644 --- a/src/events/client/ready.ts +++ b/src/events/client/ready.ts @@ -1,59 +1,78 @@ -import { Client } from 'discord.js'; -import { logStart } from '../../utils/misc'; -import { dbReminder, deleteReminder, infoReminder, OptionReminder, sendReminder, setTimeoutReminder, updateReminder } from '../../utils/reminder'; +import { Client } from "discord.js"; +import { logStart } from "../../utils/misc"; +import { + dbReminder, + deleteReminder, + infoReminder, + OptionReminder, + sendReminder, + setTimeoutReminder, + updateReminder, +} from "../../utils/reminder"; export const once = true; /** https://discord.js.org/#/docs/discord.js/main/class/Client?scrollTo=e-ready */ export default async (client: Client) => { - console.log(logStart('Connection', true)); + console.log(logStart("Connection", true)); - // Restart all the timeout about reminders here - new Promise((ok, ko) => { - // Fetch all reminders - client.db.all('SELECT * FROM reminder', [], (err, row) => { - if (err) { - ko(err); - } + // Restart all the timeout about reminders here + new Promise((ok, ko) => { + // Fetch all reminders + client.db.all("SELECT * FROM reminder", [], (err, row) => { + if (err) { + ko(err); + } - // Send all the current reminders - ok(row); - }); - }).then((data) => { - const now = Date.now(); - (data as dbReminder[]).forEach((element) => { - const info = { - locale: element.locale, - message: element.data, - createdAt: Number(element.creation_date), - channelId: `${element.channel_id}`, - userId: `${element.user_id}`, - guildId: `${element.guild_id}`, - } as infoReminder; + // Send all the current reminders + ok(row); + }); + }) + .then((data) => { + const now = Date.now(); + (data as dbReminder[]).forEach((element) => { + const info = { + locale: element.locale, + message: element.data, + createdAt: Number(element.creation_date), + channelId: `${element.channel_id}`, + userId: `${element.user_id}`, + guildId: `${element.guild_id}`, + } as infoReminder; - if (element.expiration_date <= now) { - // Reminder expired - deleteReminder(client, element.creation_date, `${element.user_id}`).then((res) => { - if (res != true) { - throw res; - } - }); + if (element.expiration_date <= now) { + // Reminder expired + deleteReminder( + client, + element.creation_date, + `${element.user_id}` + ).then((res) => { + if (res != true) { + throw res; + } + }); - sendReminder(client, info, element.option_id as OptionReminder); - } else { - // Restart timeout - const timeoutId = setTimeoutReminder(client, info, element.option_id, (element.expiration_date - now) / 1000); + sendReminder(client, info, element.option_id as OptionReminder); + } else { + // Restart timeout + const timeoutId = setTimeoutReminder( + client, + info, + element.option_id, + (element.expiration_date - now) / 1000 + ); - // Update timeout in database - element.timeout_id = String(timeoutId); - updateReminder(client, element).then((res) => { - if (res != true) { - throw res; - } - }); - } - }); - }).catch(err => { - throw err; - }); + // Update timeout in database + element.timeout_id = String(timeoutId); + updateReminder(client, element).then((res) => { + if (res != true) { + throw res; + } + }); + } + }); + }) + .catch((err) => { + throw err; + }); }; diff --git a/src/events/interactions/interactionCreate.ts b/src/events/interactions/interactionCreate.ts index cef1087..1e1e5b5 100644 --- a/src/events/interactions/interactionCreate.ts +++ b/src/events/interactions/interactionCreate.ts @@ -1,35 +1,35 @@ -import { Client, Interaction, InteractionType } from 'discord.js'; -import { getLocale } from '../../utils/locales'; +import { Client, Interaction, InteractionType } from "discord.js"; +import { getLocale } from "../../utils/locales"; /** https://discord.js.org/#/docs/discord.js/main/class/Client?scrollTo=e-interactionCreate */ export default (interaction: Interaction, client: Client) => { - const loc = getLocale(client, interaction.locale); - switch (interaction.type) { - case InteractionType.ApplicationCommand: { - const command = client.commands.list.get(interaction.commandName); - if (!command) { - return interaction.reply({ - content: loc.get('e_interacreate_no_command'), - ephemeral: true, - }); - } + const loc = getLocale(client, interaction.locale); + switch (interaction.type) { + case InteractionType.ApplicationCommand: { + const command = client.commands.list.get(interaction.commandName); + if (!command) { + return interaction.reply({ + content: loc.get("e_interacreate_no_command"), + ephemeral: true, + }); + } - 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({ - content: loc.get('e_interacreate_no_modal'), - ephemeral: true, - }); - } + case InteractionType.ModalSubmit: { + const modal = client.modals.list.get(interaction.customId); + if (!modal) { + return interaction.reply({ + content: loc.get("e_interacreate_no_modal"), + ephemeral: true, + }); + } - return modal.interaction(interaction, client); - } + return modal.interaction(interaction, client); + } - default: - break; - } + default: + break; + } }; diff --git a/src/events/loader.ts b/src/events/loader.ts index e8b2a69..9446bbf 100644 --- a/src/events/loader.ts +++ b/src/events/loader.ts @@ -1,40 +1,41 @@ -import { Client } from 'discord.js'; -import { readdir } from 'fs/promises'; +import { Client } from "discord.js"; +import { readdir } from "fs/promises"; /** Load all the events. */ export default async (client: Client) => { - const events_categories = (await readdir(__dirname)) - .filter(element => !element.endsWith('.js') && !element.endsWith('.ts')); + const events_categories = (await readdir(__dirname)).filter( + (element) => !element.endsWith(".js") && !element.endsWith(".ts") + ); - events_categories.forEach(async event_category => { - // Retrieve events - const events = await readdir(`${__dirname}/${event_category}`); + events_categories.forEach(async (event_category) => { + // Retrieve events + const events = await readdir(`${__dirname}/${event_category}`); - // Load them into the client - Promise.all( - events.map(async event_file => { - const { once, default: execute } = await import( - `../events/${event_category}/${event_file}` - ); + // Load them into the client + Promise.all( + events.map(async (event_file) => { + const { once, default: execute } = await import( + `../events/${event_category}/${event_file}` + ); - // Remove extension - // TODO: use utils functions - const event_type_ext = event_file.split('.'); - const ext = event_type_ext.pop(); - if (!(ext === 'js' || ext === 'ts')) { - throw `Unknown file in ${event_category}: ${event_file}`; - } - const event_type = event_type_ext.join('.'); + // Remove extension + // TODO: use utils functions + const event_type_ext = event_file.split("."); + const ext = event_type_ext.pop(); + if (!(ext === "js" || ext === "ts")) { + throw `Unknown file in ${event_category}: ${event_file}`; + } + const event_type = event_type_ext.join("."); - if (once) { - return client.once(event_type, (...args) => { - execute(...args, client); - }); - } - return client.on(event_type, (...args) => { - execute(...args, client); - }); - }), - ); - }); + if (once) { + return client.once(event_type, (...args) => { + execute(...args, client); + }); + } + return client.on(event_type, (...args) => { + execute(...args, client); + }); + }) + ); + }); }; diff --git a/src/events/message/messageCreate.ts b/src/events/message/messageCreate.ts index 1dafda4..5d1527f 100644 --- a/src/events/message/messageCreate.ts +++ b/src/events/message/messageCreate.ts @@ -1,183 +1,191 @@ -import { Client, GuildMember, Message, TextBasedChannel, EmbedBuilder } from 'discord.js'; -import { getLocale } from '../../utils/locales'; -import { isImage, userWithNickname } from '../../utils/misc'; -import { showDate } from '../../utils/time'; +import { + Client, + GuildMember, + Message, + TextBasedChannel, + EmbedBuilder, +} from "discord.js"; +import { getLocale } from "../../utils/locales"; +import { isImage, userWithNickname } from "../../utils/misc"; +import { showDate } from "../../utils/time"; /** https://discord.js.org/#/docs/discord.js/main/class/Client?scrollTo=e-messageCreate */ export default async (message: Message, client: Client) => { - // Ignore message if - if ( - // Author is a bot - message.author.bot || - // Author is Discord - message.author.system || - // Message isn't a message - message.system || - // Message is in PM (future-proof if we add Intents.FLAGS.DIRECT_MESSAGES) - !message.guild || - // Guild is offline - !message.guild.available - ) { - return; - } + // Ignore message if + if ( + // Author is a bot + message.author.bot || + // Author is Discord + message.author.system || + // Message isn't a message + message.system || + // Message is in PM (future-proof if we add Intents.FLAGS.DIRECT_MESSAGES) + !message.guild || + // Guild is offline + !message.guild.available + ) { + return; + } - /* Citation */ - const regex = 'https://(?:canary\\.|ptb\\.)?discord(?:app)?\\.com/channels/(\\d{17,19})/(\\d{17,19})/(\\d{17,19})'; - const urls = message.content.match(new RegExp(regex, 'g')); + /* Citation */ + const regex = + "https://(?:canary\\.|ptb\\.)?discord(?:app)?\\.com/channels/(\\d{17,19})/(\\d{17,19})/(\\d{17,19})"; + const urls = message.content.match(new RegExp(regex, "g")); - // Ignore message if there is no URLs - if (!urls) { - return; - } + // Ignore message if there is no URLs + if (!urls) { + return; + } - const messages = ( - await Promise.all( - urls.reduce( - (data: { - message_id: string; - channel: TextBasedChannel; - }[] = [], match) => { - const [, - guild_id, - channel_id, - message_id, - ] = new RegExp(regex).exec(match) as RegExpExecArray; + const messages = ( + await Promise.all( + urls + .reduce( + ( + data: { + message_id: string; + channel: TextBasedChannel; + }[] = [], + match + ) => { + const [, guild_id, channel_id, message_id] = new RegExp(regex).exec( + match + ) as RegExpExecArray; - // If message posted in another guild - if (guild_id !== message.guild?.id) { - return data; - } + // If message posted in another guild + if (guild_id !== message.guild?.id) { + return data; + } - const channel = - message.guild.channels.cache.get(channel_id) as TextBasedChannel; + const channel = message.guild.channels.cache.get( + channel_id + ) as TextBasedChannel; - // If channel doesn't exist in the guild and isn't text - if (!channel) { - return data; - } + // If channel doesn't exist in the guild and isn't text + if (!channel) { + return data; + } - data.push({ message_id, channel }); + data.push({ message_id, channel }); - return data; - }, [] - ).map(async ({ message_id, channel }) => { - const quoted_message = await channel.messages - .fetch(message_id) - .catch(() => undefined); + return data; + }, + [] + ) + .map(async ({ message_id, channel }) => { + const quoted_message = await channel.messages + .fetch(message_id) + .catch(() => undefined); - // If message doesn't exist or empty - if (!quoted_message || ( - !quoted_message.content && - quoted_message.attachments.size == 0) - ) { - return; - } + // If message doesn't exist or empty + if ( + !quoted_message || + (!quoted_message.content && quoted_message.attachments.size == 0) + ) { + return; + } - return quoted_message; - }) - ) - // Remove undefined elements - ).filter(Boolean); + return quoted_message; + }) + ) + ) + // Remove undefined elements + .filter(Boolean); - const loc = getLocale(client, client.config.default_lang); + const loc = getLocale(client, client.config.default_lang); - // Remove duplicates then map the quoted posts - [...new Set(messages)].map(quoted_post => { - const embed = new EmbedBuilder() - .setColor('#2f3136') - .setAuthor({ - name: 'Citation', - iconURL: quoted_post?.author.displayAvatarURL(), - }); + // Remove duplicates then map the quoted posts + [...new Set(messages)].map((quoted_post) => { + const embed = new EmbedBuilder().setColor("#2f3136").setAuthor({ + name: "Citation", + iconURL: quoted_post?.author.displayAvatarURL(), + }); - // Handle attachments - if (quoted_post?.attachments.size !== 0) { - if (quoted_post?.attachments.size === 1 && isImage( - quoted_post.attachments.first()?.name as string - )) { - // Only contains one image - embed.setImage(quoted_post.attachments.first()?.url as string); - } else { - // Contains more than one image and/or other files - let files = ''; - quoted_post?.attachments.forEach(file => files += - `[${file.name}](${file.url}), ` - ); - embed.addFields({ - // TODO: Don't pluralize when there is only one file. - // TODO: Locales - name: 'Fichiers joints', - // TODO: Check if don't exceed char limit, if yes, split - // files into multiples field. - value: `${files.slice(0, -2)}.`, - }); - } - } + // Handle attachments + if (quoted_post?.attachments.size !== 0) { + if ( + quoted_post?.attachments.size === 1 && + isImage(quoted_post.attachments.first()?.name as string) + ) { + // Only contains one image + embed.setImage(quoted_post.attachments.first()?.url as string); + } else { + // Contains more than one image and/or other files + let files = ""; + quoted_post?.attachments.forEach( + (file) => (files += `[${file.name}](${file.url}), `) + ); + embed.addFields({ + // TODO: Don't pluralize when there is only one file. + // TODO: Locales + name: "Fichiers joints", + // TODO: Check if don't exceed char limit, if yes, split + // files into multiples field. + value: `${files.slice(0, -2)}.`, + }); + } + } - // Description as post content - embed.setDescription(quoted_post?.content ?? ''); + // Description as post content + embed.setDescription(quoted_post?.content ?? ""); - // Footer - let footer = `Posté le ${showDate( - client.config.default_lang, - loc, - quoted_post?.createdAt as Date - )}`; - if (quoted_post?.editedAt) { - footer += ` et modifié le ${showDate( - client.config.default_lang, - loc, - quoted_post.editedAt - )}`; - } + // Footer + let footer = `Posté le ${showDate( + client.config.default_lang, + loc, + quoted_post?.createdAt as Date + )}`; + if (quoted_post?.editedAt) { + footer += ` et modifié le ${showDate( + client.config.default_lang, + loc, + quoted_post.editedAt + )}`; + } - let author = 'Auteur'; - if (message.author == quoted_post?.author) { - author += ' & Citateur'; - } else { - footer += `\nCité par ${userWithNickname( - message.member as GuildMember - ) ?? '?'} le ${showDate( - client.config.default_lang, - loc, - message.createdAt - )}`; - } + let author = "Auteur"; + if (message.author == quoted_post?.author) { + author += " & Citateur"; + } else { + footer += `\nCité par ${ + userWithNickname(message.member as GuildMember) ?? "?" + } le ${showDate(client.config.default_lang, loc, message.createdAt)}`; + } - embed.setFooter({ - text: footer, - iconURL: message.author.avatarURL() ?? undefined, - }); + embed.setFooter({ + text: footer, + iconURL: message.author.avatarURL() ?? undefined, + }); - // Location/author of the quoted post - embed.addFields( - { - name: author, - value: `${quoted_post?.author}`, - inline: true, - }, - { - name: 'Message', - value: `${quoted_post?.channel} - [Lien Message](${quoted_post?.url})`, - inline: true, - } - ); + // Location/author of the quoted post + embed.addFields( + { + name: author, + value: `${quoted_post?.author}`, + inline: true, + }, + { + name: "Message", + value: `${quoted_post?.channel} - [Lien Message](${quoted_post?.url})`, + inline: true, + } + ); - // Delete source message if no content when removing links - if ( - !message.content.replace(new RegExp(regex, 'g'), '').trim() && - messages.length === urls.length && - !message.mentions.repliedUser - ) { - message.delete(); - return message.channel.send({ embeds: [embed] }); - } else { - return message.reply({ - embeds: [embed], - allowedMentions: { - repliedUser: false, - }, - }); - } - }); + // Delete source message if no content when removing links + if ( + !message.content.replace(new RegExp(regex, "g"), "").trim() && + messages.length === urls.length && + !message.mentions.repliedUser + ) { + message.delete(); + return message.channel.send({ embeds: [embed] }); + } else { + return message.reply({ + embeds: [embed], + allowedMentions: { + repliedUser: false, + }, + }); + } + }); }; diff --git a/src/index.ts b/src/index.ts index 7ce6835..0fa3d81 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,78 +1,80 @@ -import loadClient, { quit } from './utils/client'; -import loadEvents from './events/loader'; -import loadModals from './modals/loader'; -import loadButtons from './buttons/loader'; -import loadCommands from './commands/loader'; +import loadClient, { quit } from "./utils/client"; +import loadEvents from "./events/loader"; +import loadModals from "./modals/loader"; +import loadButtons from "./buttons/loader"; +import loadCommands from "./commands/loader"; -import { logStart } from './utils/misc'; +import { logStart } from "./utils/misc"; /** Run the bot. */ const run = async () => { - console.log('Starting Botanique...'); + console.log("Starting Botanique..."); - // Load .env if not in prod - if (process.env.NODE_ENV !== 'production') { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - (await import('dotenv')).config({ path: './config/.env' }); - } + // Load .env if not in prod + if (process.env.NODE_ENV !== "production") { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + (await import("dotenv")).config({ path: "./config/.env" }); + } - // Client Discord.JS - const client_name = 'Client'; - await loadClient() - .then(async client => { - // Events Discord.JS - const events_name = 'Events'; - await loadEvents(client) - .then(() => console.log(logStart(events_name, true))) - .catch((err) => { - console.error(err); - throw logStart(events_name, false); - }); + // Client Discord.JS + const client_name = "Client"; + await loadClient() + .then(async (client) => { + // Events Discord.JS + const events_name = "Events"; + await loadEvents(client) + .then(() => console.log(logStart(events_name, true))) + .catch((err) => { + console.error(err); + throw logStart(events_name, false); + }); - // Connect the bot to Discord.com - await client.login(client.config.token_discord); + // Connect the bot to Discord.com + 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); - }); + // 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); + }); - // Buttons Discord.JS - const buttons_name = 'Buttons'; - await loadButtons(client) - .then(() => console.log(logStart(buttons_name, true))) - .catch((err) => { - console.error(err); - throw logStart(buttons_name, false); - }); + // Buttons Discord.JS + const buttons_name = "Buttons"; + await loadButtons(client) + .then(() => console.log(logStart(buttons_name, true))) + .catch((err) => { + console.error(err); + throw logStart(buttons_name, false); + }); - // Commands Slash Discord.JS - const commands_name = 'Commands'; - await loadCommands(client) - .then(() => console.log(logStart(commands_name, true))) - .catch((err) => { - console.error(err); - throw logStart(commands_name, false); - }); + // Commands Slash Discord.JS + const commands_name = "Commands"; + await loadCommands(client) + .then(() => console.log(logStart(commands_name, true))) + .catch((err) => { + console.error(err); + throw logStart(commands_name, false); + }); - console.log(logStart(client_name, true)); - console.log(`Botanique "${client.user?.username}" v${client.config.version} started!`); + console.log(logStart(client_name, true)); + console.log( + `Botanique "${client.user?.username}" v${client.config.version} started!` + ); - // ^C - process.on('SIGINT', () => quit(client)); + // ^C + process.on("SIGINT", () => quit(client)); - // Container force closed - process.on('SIGTERM', () => quit(client)); - }) - .catch((err) => { - console.error(err); - throw logStart(client_name, false); - }); + // Container force closed + process.on("SIGTERM", () => quit(client)); + }) + .catch((err) => { + console.error(err); + throw logStart(client_name, false); + }); }; -run().catch(error => console.error(error)); +run().catch((error) => console.error(error)); diff --git a/src/locales/en-US.json b/src/locales/en-US.json index 2ed735a..44da063 100644 --- a/src/locales/en-US.json +++ b/src/locales/en-US.json @@ -1,18 +1,18 @@ { - "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_ping1": "Roundtrip latency", - "c_ping2": "Websocket heartbeat", + "c_ping_name": "Ping", + "c_ping_desc": "Pong!", + "c_ping1": "Roundtrip latency", + "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 ` to get more information about a command.", - "c_help3": "Can't find :", + "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 ` to get more information about a command.", + "c_help3": "Can't find :", - "u_time_at": "at" + "u_time_at": "at" } diff --git a/src/locales/fr.json b/src/locales/fr.json index 05a1d16..81a30da 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -1,80 +1,80 @@ { - "e_interacreate_no_command": "Désolé, la commande n'existe plus...", - "e_interacreate_no_modal": "Désolé, le modèle n'existe plus...", - "e_interacreate_no_button": "Désolé, le bouton n'existe plus...", + "e_interacreate_no_command": "Désolé, la commande n'existe plus...", + "e_interacreate_no_modal": "Désolé, le modèle n'existe plus...", + "e_interacreate_no_button": "Désolé, le bouton n'existe plus...", - "c_ping_name": "Ping", - "c_ping_desc": "Pong!", - "c_ping1": "Latence totale", - "c_ping2": "Latence du Websocket", + "c_ping_name": "Ping", + "c_ping_desc": "Pong!", + "c_ping1": "Latence totale", + "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 ` pour obtenir plus d'informations sur une commande.", - "c_help3": "Impossible de trouver :", + "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 ` pour obtenir plus d'informations sur une commande.", + "c_help3": "Impossible de trouver :", - "c_archive_name": "Nettoyer", - "c_archive_desc": "Nettoyage pour le passage à niveau", - "c_archive_opt1_name": "catégorie", - "c_archive_opt1_desc": "Nom de la catégorie à nettoyer", - "c_archive1": "Liste des catégories soumis au nettoyage", - "c_archive2": "`L1`, `L2`, `L3`, `M1`, `M2`", - "c_archive3": "Impossible de trouver/nettoyer le salon :", - "c_archive4": "Liste des salons archivés de la catégorie", - "c_archive5": "vers", - "c_archive6": "Nettoyage", - "c_archive7": "Catégorie déjà nettoyée", + "c_archive_name": "Nettoyer", + "c_archive_desc": "Nettoyage pour le passage à niveau", + "c_archive_opt1_name": "catégorie", + "c_archive_opt1_desc": "Nom de la catégorie à nettoyer", + "c_archive1": "Liste des catégories soumis au nettoyage", + "c_archive2": "`L1`, `L2`, `L3`, `M1`, `M2`", + "c_archive3": "Impossible de trouver/nettoyer le salon :", + "c_archive4": "Liste des salons archivés de la catégorie", + "c_archive5": "vers", + "c_archive6": "Nettoyage", + "c_archive7": "Catégorie déjà nettoyée", - "c_prep_name": "Préparation", - "c_prep_desc": "Préparation des salons généraux pour la nouvelle année", - "c_prep_opt1_name": "année", - "c_prep_opt1_desc": "Nom de l'année à préparer'", - "c_prep1": "Liste des catégories soumis à la préparation", - "c_prep2": "`L1`, `L2`, `L3`, `M1`, `M2`", - "c_prep3": "Impossible de trouver/nettoyer le salon :", - "c_prep4": "Listes des Salons préparés `", - "c_prep5": "créé", - "c_prep6": "Pas besoin de préparation", + "c_prep_name": "Préparation", + "c_prep_desc": "Préparation des salons généraux pour la nouvelle année", + "c_prep_opt1_name": "année", + "c_prep_opt1_desc": "Nom de l'année à préparer'", + "c_prep1": "Liste des catégories soumis à la préparation", + "c_prep2": "`L1`, `L2`, `L3`, `M1`, `M2`", + "c_prep3": "Impossible de trouver/nettoyer le salon :", + "c_prep4": "Listes des Salons préparés `", + "c_prep5": "créé", + "c_prep6": "Pas besoin de préparation", - "u_time_at": "à", + "u_time_at": "à", - "c_reminder_name": "rappel", - "c_reminder_desc": "Commande relative aux rappels", - "c_reminder_sub1_name": "nouveau", - "c_reminder_sub1_desc": "Met en place un rappel", - "c_reminder_sub1_opt1_name": "temps", - "c_reminder_sub1_opt1_desc": "Temps désiré avant le rappel, accolez un @ pour activer la mention ou un p pour envoyer en DM", - "c_reminder_sub1_opt2_name": "message", - "c_reminder_sub1_opt2_desc": "Message du rappel", - "c_reminder_sub2_name": "liste", - "c_reminder_sub2_desc": "Affiche la liste des rappels d'un utilisateur", - "c_reminder_sub2_opt1_name": "utilisateur", - "c_reminder_sub2_opt1_desc": "Affiche la liste de l'utilisateur en question", - "c_reminder_sub2_opt2_name": "page", - "c_reminder_sub2_opt2_desc": "Page à afficher", - "c_reminder_sub3_name": "efface", - "c_reminder_sub3_desc": "Supprime un rappel", - "c_reminder_sub3_opt1_name": "id", - "c_reminder_sub3_opt1_desc": "Rappel à supprimé", - "c_reminder1": "Un rappel a été configuré pour dans", - "c_reminder2": "L'ID renseigné n'est pas valide.", - "c_reminder3": "Rappel non trouvé, pas sur le bon serveur ou qui ne vous appartiens pas.", - "c_reminder4": "Utilisateur inconnu.", - "c_reminder5": "Rappels de", - "c_reminder6": "Page", - "c_reminder7": "Pas de message", - "c_reminder8": "Expire dans", - "c_reminder9": "Fais le", - "c_reminder10": "L'utilisateur n'a aucun rappel en attente ou page n°", - "c_reminder11": "vide", - "c_reminder12": "Précédent", - "c_reminder13": "Suivant", - "c_reminder14": "Message envoyé en DM car le salon n'est plus disponible.", - "c_reminder15": "Message envoyé en DM car vous avez quitté", - "c_reminder16": "Message envoyé en DM car le serveur Discord n'est plus disponible.", - "c_reminder17": "Message d'il y a", - "c_reminder18": "Pas de message" + "c_reminder_name": "rappel", + "c_reminder_desc": "Commande relative aux rappels", + "c_reminder_sub1_name": "nouveau", + "c_reminder_sub1_desc": "Met en place un rappel", + "c_reminder_sub1_opt1_name": "temps", + "c_reminder_sub1_opt1_desc": "Temps désiré avant le rappel, accolez un @ pour activer la mention ou un p pour envoyer en DM", + "c_reminder_sub1_opt2_name": "message", + "c_reminder_sub1_opt2_desc": "Message du rappel", + "c_reminder_sub2_name": "liste", + "c_reminder_sub2_desc": "Affiche la liste des rappels d'un utilisateur", + "c_reminder_sub2_opt1_name": "utilisateur", + "c_reminder_sub2_opt1_desc": "Affiche la liste de l'utilisateur en question", + "c_reminder_sub2_opt2_name": "page", + "c_reminder_sub2_opt2_desc": "Page à afficher", + "c_reminder_sub3_name": "efface", + "c_reminder_sub3_desc": "Supprime un rappel", + "c_reminder_sub3_opt1_name": "id", + "c_reminder_sub3_opt1_desc": "Rappel à supprimé", + "c_reminder1": "Un rappel a été configuré pour dans", + "c_reminder2": "L'ID renseigné n'est pas valide.", + "c_reminder3": "Rappel non trouvé, pas sur le bon serveur ou qui ne vous appartiens pas.", + "c_reminder4": "Utilisateur inconnu.", + "c_reminder5": "Rappels de", + "c_reminder6": "Page", + "c_reminder7": "Pas de message", + "c_reminder8": "Expire dans", + "c_reminder9": "Fais le", + "c_reminder10": "L'utilisateur n'a aucun rappel en attente ou page n°", + "c_reminder11": "vide", + "c_reminder12": "Précédent", + "c_reminder13": "Suivant", + "c_reminder14": "Message envoyé en DM car le salon n'est plus disponible.", + "c_reminder15": "Message envoyé en DM car vous avez quitté", + "c_reminder16": "Message envoyé en DM car le serveur Discord n'est plus disponible.", + "c_reminder17": "Message d'il y a", + "c_reminder18": "Pas de message" } diff --git a/src/modals/loader.ts b/src/modals/loader.ts index 56e2aea..268a1f0 100644 --- a/src/modals/loader.ts +++ b/src/modals/loader.ts @@ -1,36 +1,37 @@ -import { readdir } from 'fs/promises'; -import { removeExtension } from '../utils/misc'; -import { Client } from 'discord.js'; +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')); + // 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}`); + 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 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 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; - }), - ); - }), - ); + // Add it to the collection so the interaction will work + client.modals.list.set(modal.data.name, modal); + return modal.data; + }) + ); + }) + ); }; diff --git a/src/modals/misc/reminderGUI.ts b/src/modals/misc/reminderGUI.ts index d848dd4..b74a496 100644 --- a/src/modals/misc/reminderGUI.ts +++ b/src/modals/misc/reminderGUI.ts @@ -1,21 +1,28 @@ -import { Client, ModalSubmitInteraction } from 'discord.js'; -import { getFilename } from '../../utils/misc'; -import { newReminder } from '../../utils/reminder'; +import { Client, ModalSubmitInteraction } from "discord.js"; +import { getFilename } from "../../utils/misc"; +import { newReminder } from "../../utils/reminder"; export default { - data: { - name: getFilename(__filename), - }, - interaction: async (interaction: ModalSubmitInteraction, client: Client) => - newReminder(client, interaction.fields.fields.get('reminderGUI-time')?.value as string, { - locale: interaction.locale, - message: interaction.fields.fields.get('reminderGUI-message')?.value ?? null, - createdAt: interaction.createdAt.getTime(), - channelId: interaction.channelId, - userId: interaction.user.id, - guildId: interaction.guildId, - }).then((msg) => interaction.reply({ - content: msg as string, - ephemeral: true, - })), + data: { + name: getFilename(__filename), + }, + interaction: async (interaction: ModalSubmitInteraction, client: Client) => + newReminder( + client, + interaction.fields.fields.get("reminderGUI-time")?.value as string, + { + locale: interaction.locale, + message: + interaction.fields.fields.get("reminderGUI-message")?.value ?? null, + createdAt: interaction.createdAt.getTime(), + channelId: interaction.channelId, + userId: interaction.user.id, + guildId: interaction.guildId, + } + ).then((msg) => + interaction.reply({ + content: msg as string, + ephemeral: true, + }) + ), }; diff --git a/src/modules/client.ts b/src/modules/client.ts index 9af4913..559604a 100644 --- a/src/modules/client.ts +++ b/src/modules/client.ts @@ -1,87 +1,96 @@ -import { Collection } from 'discord.js'; -import { SlashCommandBuilder } from '@discordjs/builders'; -import { Database } from 'sqlite3'; +import { Collection } from "discord.js"; +import { SlashCommandBuilder } from "@discordjs/builders"; +import { Database } from "sqlite3"; -export { }; +export {}; -declare module 'discord.js' { - // eslint-disable-next-line no-shadow - export interface Client { - /** Store the configuration */ - config: { - /** Bot version */ - version: string, - /** Bot token from env variable */ - token_discord: string | undefined, - /** Default lang used */ - 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 buttons */ - buttons: { - categories: Collection< - /** Category name */ - string, - /** Name of the buttons in the category */ - string[] - >, - list: Collection< - /** Button name */ - string, - /** Button itself */ - { - /** Data about the button */ - data: { - name: string - }, - /** How the button interact */ - interaction: (interaction: MessageComponentInteraction, client: Client) => Promise - } - >, - }, - /** Store all the slash commands */ - commands: { - categories: Collection< - /** Category name */ - string, - /** 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 */ - locales: Map>, - db: Database, - } +declare module "discord.js" { + // eslint-disable-next-line no-shadow + export interface Client { + /** Store the configuration */ + config: { + /** Bot version */ + version: string; + /** Bot token from env variable */ + token_discord: string | undefined; + /** Default lang used */ + 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 buttons */ + buttons: { + categories: Collection< + /** Category name */ + string, + /** Name of the buttons in the category */ + string[] + >; + list: Collection< + /** Button name */ + string, + /** Button itself */ + { + /** Data about the button */ + data: { + name: string; + }; + /** How the button interact */ + interaction: ( + interaction: MessageComponentInteraction, + client: Client + ) => Promise; + } + >; + }; + /** Store all the slash commands */ + commands: { + categories: Collection< + /** Category name */ + string, + /** 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 */ + locales: Map>; + db: Database; + } } diff --git a/src/modules/string.ts b/src/modules/string.ts index 01bbe12..b0c5cf9 100644 --- a/src/modules/string.ts +++ b/src/modules/string.ts @@ -1,16 +1,16 @@ export {}; declare global { - // Declarations - interface String { - /** - * Returns a copy of the string with the first letter capitalized. - */ - capitalize(): string, - } + // 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); +String.prototype.capitalize = function (this: string) { + return this[0].toUpperCase() + this.substring(1); }; diff --git a/src/utils/client.ts b/src/utils/client.ts index 916c85b..7a2a407 100644 --- a/src/utils/client.ts +++ b/src/utils/client.ts @@ -1,47 +1,46 @@ -import { Client, Collection, GatewayIntentBits } from 'discord.js'; -import { readFileSync } from 'fs'; -import { loadLocales } from './locales'; -import '../modules/client'; -import { Database } from 'sqlite3'; +import { Client, Collection, GatewayIntentBits } from "discord.js"; +import { readFileSync } from "fs"; +import { loadLocales } from "./locales"; +import "../modules/client"; +import { Database } from "sqlite3"; /** Creation of the client and definition of its properties. */ export default async () => { - const client: Client = new Client({ - intents: [ - GatewayIntentBits.Guilds, - GatewayIntentBits.GuildMessages, - ], - }); + const client: Client = new Client({ + intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages], + }); - client.config = { - version: JSON.parse(readFileSync('./package.json').toString()).version, - token_discord: process.env.TOKEN_DISCORD, - default_lang: process.env.DEFAULT_LANG ?? 'fr', - }; + client.config = { + version: JSON.parse(readFileSync("./package.json").toString()).version, + token_discord: process.env.TOKEN_DISCORD, + default_lang: process.env.DEFAULT_LANG ?? "fr", + }; - client.modals = { - categories: new Collection(), - list: new Collection(), - }; + client.modals = { + categories: new Collection(), + list: new Collection(), + }; - client.buttons = { - categories: new Collection(), - list: new Collection(), - }; + client.buttons = { + categories: new Collection(), + list: new Collection(), + }; - client.commands = { - categories: new Collection(), - list: new Collection(), - }; + client.commands = { + categories: new Collection(), + list: new Collection(), + }; - console.log('Translations progression :'); - client.locales = await loadLocales(client.config.default_lang); + console.log("Translations progression :"); + client.locales = await loadLocales(client.config.default_lang); - client.db = new Database(`${process.env.DOCKERIZED === '1' ? '/config' : './config'}/db.sqlite3`); + client.db = new Database( + `${process.env.DOCKERIZED === "1" ? "/config" : "./config"}/db.sqlite3` + ); - initDatabase(client.db); + initDatabase(client.db); - return client; + return client; }; /** @@ -49,11 +48,11 @@ export default async () => { * @param client Client */ export const quit = (client: Client) => { - // Close DB - client.db.close(); + // Close DB + client.db.close(); - // Close client - client.destroy(); + // Close client + client.destroy(); }; /** @@ -61,8 +60,9 @@ export const quit = (client: Client) => { * @param db Database */ const initDatabase = (db: Database) => { - // Table for reminders - db.run('CREATE TABLE IF NOT EXISTS reminder ( \ + // Table for reminders + db.run( + "CREATE TABLE IF NOT EXISTS reminder ( \ id INTEGER PRIMARY KEY, \ data TEXT, \ expiration_date TEXT, \ @@ -73,5 +73,6 @@ const initDatabase = (db: Database) => { guild_id TEXT, \ locale TEXT, \ timeout_id TEXT \ - );'); + );" + ); }; diff --git a/src/utils/locales.ts b/src/utils/locales.ts index b960587..074b0cb 100644 --- a/src/utils/locales.ts +++ b/src/utils/locales.ts @@ -1,6 +1,6 @@ -import { Client } from 'discord.js'; -import { readdir } from 'fs/promises'; -import { removeExtension } from './misc'; +import { Client } from "discord.js"; +import { readdir } from "fs/promises"; +import { removeExtension } from "./misc"; /** * Load the localizations files into memory. @@ -10,40 +10,39 @@ import { removeExtension } from './misc'; * @returns Map of map with all the localizations */ export const loadLocales = async (default_lang: string) => { - // Get files from locales/ directory - const old_path = __dirname.split('/'); - old_path.pop(); - const files = await readdir(`${old_path.join('/')}/locales`); + // Get files from locales/ directory + const old_path = __dirname.split("/"); + old_path.pop(); + const files = await readdir(`${old_path.join("/")}/locales`); - // Read JSON files content and load it into the memory - const locales = new Map>(); - await Promise.all( - files.map(async lang => { - // Import file - const content: { - [key: string]: string - } = await import( - `../locales/${lang}` - ); + // Read JSON files content and load it into the memory + const locales = new Map>(); + await Promise.all( + files.map(async (lang) => { + // Import file + const content: { + [key: string]: string; + } = await import(`../locales/${lang}`); - // Add it to the memory - locales.set( - removeExtension(lang), - new Map(Object.keys(content) - // Ignore the default key - .filter(str => str !== 'default') - .map(str => { - return [str, content[str]]; - }), - ) - ); - }) - ); + // Add it to the memory + locales.set( + removeExtension(lang), + new Map( + Object.keys(content) + // Ignore the default key + .filter((str) => str !== "default") + .map((str) => { + return [str, content[str]]; + }) + ) + ); + }) + ); - // Check locales sanity - checkLocales(locales, default_lang); + // Check locales sanity + checkLocales(locales, default_lang); - return locales; + return locales; }; /** @@ -53,26 +52,31 @@ export const loadLocales = async (default_lang: string) => { * @param text Name of string to fetch * @returns the dictionary */ -export const getLocalizations = (client: Client, text: string, lowercase = false) => { - const data: Record = {}; +export const getLocalizations = ( + client: Client, + text: string, + lowercase = false +) => { + const data: Record = {}; - // Load all the localizations - client.locales.forEach((locale, lang) => { - // Fetch the text and fallback to default lang if needed - // See getLocale for more info on why we *can* fallback - let str = locale.get(text) - ?? client.locales.get(client.config.default_lang)?.get(text); + // Load all the localizations + client.locales.forEach((locale, lang) => { + // Fetch the text and fallback to default lang if needed + // See getLocale for more info on why we *can* fallback + let str = + locale.get(text) ?? + client.locales.get(client.config.default_lang)?.get(text); - // Store it if defined - if (str !== undefined) { - if (lowercase) { - str = str.toLowerCase(); - } - data[lang] = str; - } - }); + // Store it if defined + if (str !== undefined) { + if (lowercase) { + str = str.toLowerCase(); + } + data[lang] = str; + } + }); - return data; + return data; }; /** @@ -83,22 +87,22 @@ export const getLocalizations = (client: Client, text: string, lowercase = false * @returns the map with the desired languaged clogged with the default one */ export const getLocale = (client: Client, lang: string) => { - // Load default lang - const default_locales = client.locales.get(client.config.default_lang); - // Load desired lang - const desired_locales = client.locales.get(lang); + // Load default lang + const default_locales = client.locales.get(client.config.default_lang); + // Load desired lang + const desired_locales = client.locales.get(lang); - // Get text and fallback to default lang if needed - // - // We can fallback to the default one without any problem - // because we make sure that the default language always contains - // the desired text, and that the other languages are only translations - const locales = new Map(); - default_locales?.forEach((_, key) => { - locales.set(key, desired_locales?.get(key) ?? default_locales.get(key)); - }); + // Get text and fallback to default lang if needed + // + // We can fallback to the default one without any problem + // because we make sure that the default language always contains + // the desired text, and that the other languages are only translations + const locales = new Map(); + default_locales?.forEach((_, key) => { + locales.set(key, desired_locales?.get(key) ?? default_locales.get(key)); + }); - return locales; + return locales; }; /** @@ -110,65 +114,72 @@ export const getLocale = (client: Client, lang: string) => { * @param default_lang default lang * @returns void */ -const checkLocales = -async (locales: Map>, default_lang: string) => { - // Associate each lang with the number of locale it has - let locales_size = new Map(); - locales.forEach((locales_data, lang) => { - locales_size.set(lang, locales_data.size); - }); +const checkLocales = async ( + locales: Map>, + default_lang: string +) => { + // Associate each lang with the number of locale it has + let locales_size = new Map(); + locales.forEach((locales_data, lang) => { + locales_size.set(lang, locales_data.size); + }); - // Sort the map - locales_size = new Map([...locales_size.entries()] - .sort((a, b) => b[1] - a[1])); + // Sort the map + locales_size = new Map( + [...locales_size.entries()].sort((a, b) => b[1] - a[1]) + ); - // Check if default lang is 100% - const [max_size_name] = locales_size.keys(); - const [max_size] = locales_size.values(); - const default_lang_size = locales_size.get(default_lang) ?? 0; - if (max_size > default_lang_size) { - // Throw error because in this case we are sure than the security - // explained in getLocale isn't true. - // However, it is possible that this condition is true - // and the security is poor, but it's better than nothing. - throw new Error( - `The default locale (${default_lang} = ${default_lang_size}) isn't complete ` - + `(${max_size_name} = ${max_size}).` - ); - } + // Check if default lang is 100% + const [max_size_name] = locales_size.keys(); + const [max_size] = locales_size.values(); + const default_lang_size = locales_size.get(default_lang) ?? 0; + if (max_size > default_lang_size) { + // Throw error because in this case we are sure than the security + // explained in getLocale isn't true. + // However, it is possible that this condition is true + // and the security is poor, but it's better than nothing. + throw new Error( + `The default locale (${default_lang} = ${default_lang_size}) isn't complete ` + + `(${max_size_name} = ${max_size}).` + ); + } - // Remove the default language as it is used as a reference - locales_size.delete(default_lang); + // Remove the default language as it is used as a reference + locales_size.delete(default_lang); - // Displays the percentages according to the default language - // lower is bigger - const bar_size = 4; - locales_size.forEach((size, lang) => { - const percentage = (size / max_size) * 100; - // Colored bar part - const blocks = ' '.repeat(Math.floor(percentage / bar_size)); - // Blank bar part - const blank = ' '.repeat(Math.ceil((100 - percentage) / bar_size)); - const color = () => { - switch (true) { - case percentage <= 25: - // Red - return '\x1b[41m'; - case percentage <= 50: - // Mangeta - return '\x1b[45m'; - case percentage <= 75: - // Cyan - return '\x1b[46m'; - case percentage <= 100: - // Green - return '\x1b[42m'; - default: - return ''; - } - }; - const padding = ' '.repeat(lang.length === 5 ? 1 : 4); + // Displays the percentages according to the default language + // lower is bigger + const bar_size = 4; + locales_size.forEach((size, lang) => { + const percentage = (size / max_size) * 100; + // Colored bar part + const blocks = " ".repeat(Math.floor(percentage / bar_size)); + // Blank bar part + const blank = " ".repeat(Math.ceil((100 - percentage) / bar_size)); + const color = () => { + switch (true) { + case percentage <= 25: + // Red + return "\x1b[41m"; + case percentage <= 50: + // Mangeta + return "\x1b[45m"; + case percentage <= 75: + // Cyan + return "\x1b[46m"; + case percentage <= 100: + // Green + return "\x1b[42m"; + default: + return ""; + } + }; + const padding = " ".repeat(lang.length === 5 ? 1 : 4); - console.log(`${padding}${lang} | ${color()}${blocks}\x1b[0m${blank} | ${percentage.toPrecision(3)}%`); - }); + console.log( + `${padding}${lang} | ${color()}${blocks}\x1b[0m${blank} | ${percentage.toPrecision( + 3 + )}%` + ); + }); }; diff --git a/src/utils/misc.ts b/src/utils/misc.ts index ae42ef5..beac077 100644 --- a/src/utils/misc.ts +++ b/src/utils/misc.ts @@ -1,4 +1,4 @@ -import { GuildMember } from 'discord.js'; +import { GuildMember } from "discord.js"; /** * Log module status. @@ -7,8 +7,8 @@ import { GuildMember } from 'discord.js'; * @returns String */ export const logStart = (name: string, status: boolean) => { - // TODO Handle precision about the error if status is false - return `> ${name}\t${status === true ? '✅' : '❌'}`; + // TODO Handle precision about the error if status is false + return `> ${name}\t${status === true ? "✅" : "❌"}`; }; /** @@ -17,15 +17,15 @@ export const logStart = (name: string, status: boolean) => { * @returns string */ export const getFilename = (path: string) => { - const path_list = path.split('/'); + const path_list = path.split("/"); - // Check if filename exist - const filename_with_ext = path_list.pop(); - if (filename_with_ext === undefined) { - throw new Error(`Filename error: don't exist in ${path}`); - } + // Check if filename exist + const filename_with_ext = path_list.pop(); + if (filename_with_ext === undefined) { + throw new Error(`Filename error: don't exist in ${path}`); + } - return removeExtension(filename_with_ext); + return removeExtension(filename_with_ext); }; /** @@ -34,10 +34,10 @@ export const getFilename = (path: string) => { * @returns string of the filename without an extension */ export const removeExtension = (filename: string) => { - const array = filename.split('.'); - array.pop(); + const array = filename.split("."); + array.pop(); - return array.join('.'); + return array.join("."); }; /** @@ -46,9 +46,9 @@ export const removeExtension = (filename: string) => { * @returns string of the extension if it exists */ export const getExtension = (filename: string) => { - const array = filename.split('.'); + const array = filename.split("."); - return array.pop(); + return array.pop(); }; /** @@ -57,9 +57,7 @@ export const getExtension = (filename: string) => { * @returns true is file is a media */ export const isImage = (filename: string) => { - return Boolean(getExtension(filename)?.match( - /jpg|jpeg|png|webp|gif/ - )); + return Boolean(getExtension(filename)?.match(/jpg|jpeg|png|webp|gif/)); }; /** @@ -68,14 +66,14 @@ export const isImage = (filename: string) => { * @returns string */ export const userWithNickname = (member: GuildMember) => { - if (!member) { - return undefined; - } - if (member.nickname) { - return `${member.nickname} (${member.user.tag})`; - } else { - return member.user.tag; - } + if (!member) { + return undefined; + } + if (member.nickname) { + return `${member.nickname} (${member.user.tag})`; + } else { + return member.user.tag; + } }; /** @@ -84,20 +82,23 @@ export const userWithNickname = (member: GuildMember) => { * @returns Formatted text */ export const cleanCodeBlock = (text: string) => { - text = `\`${text.trim()}\``; + text = `\`${text.trim()}\``; - // Keep mentions - text = text.replace(/(<@\d+>)/g, function(mention: string) { - return `\`${mention}\``; - }); + // Keep mentions + text = text.replace(/(<@\d+>)/g, function (mention: string) { + return `\`${mention}\``; + }); - // Keep links - text = text.replace(/(http[s]?:\/\/(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*(),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)/g, function(url: string) { - return `\`${url}\``; - }); + // Keep links + text = text.replace( + /(http[s]?:\/\/(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*(),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)/g, + function (url: string) { + return `\`${url}\``; + } + ); - // Fix issues - text = text.replace('``', ''); + // Fix issues + text = text.replace("``", ""); - return text; + return text; }; diff --git a/src/utils/reminder.ts b/src/utils/reminder.ts index 7200bcd..1412cb5 100644 --- a/src/utils/reminder.ts +++ b/src/utils/reminder.ts @@ -1,44 +1,44 @@ -import { Client, Colors, EmbedBuilder, User } from 'discord.js'; -import { getLocale } from './locales'; -import { cleanCodeBlock } from './misc'; -import { showDate, strToSeconds, timeDeltaToString } from './time'; +import { Client, Colors, EmbedBuilder, User } from "discord.js"; +import { getLocale } from "./locales"; +import { cleanCodeBlock } from "./misc"; +import { showDate, strToSeconds, timeDeltaToString } from "./time"; /** * Option possible for reminders */ export enum OptionReminder { - /** No parameters */ - Nothing, - /** @ */ - Mention, - /** p */ - DirectMessage, + /** No parameters */ + Nothing, + /** @ */ + Mention, + /** p */ + DirectMessage, } /** * Store data about the remidner */ export type infoReminder = { - locale: string, - message: string | null, - createdAt: number, - channelId: string | null, - userId: string, - guildId: string | null -} + locale: string; + message: string | null; + createdAt: number; + channelId: string | null; + userId: string; + guildId: string | null; +}; export type dbReminder = { - id: number, - data: string | null, - expiration_date: number, - option_id: OptionReminder, - channel_id: string | null, - creation_date: string, - user_id: string, - guild_id: string | null, - locale: string, - timeout_id: string -} + id: number; + data: string | null; + expiration_date: number; + option_id: OptionReminder; + channel_id: string | null; + creation_date: string; + user_id: string; + guild_id: string | null; + locale: string; + timeout_id: string; +}; /** * Split the time and the extra args `p` and `@` @@ -46,13 +46,13 @@ export type dbReminder = { * @returns An object with the time and the option */ const splitTime = (time: string) => { - if (time?.endsWith('@')) { - return { time: time.slice(0, -1), option: OptionReminder.Mention }; - } else if (time?.toLowerCase().endsWith('p')) { - return { time: time.slice(0, -1), option: OptionReminder.DirectMessage }; - } + if (time?.endsWith("@")) { + return { time: time.slice(0, -1), option: OptionReminder.Mention }; + } else if (time?.toLowerCase().endsWith("p")) { + return { time: time.slice(0, -1), option: OptionReminder.DirectMessage }; + } - return { time: time, option: OptionReminder.Nothing }; + return { time: time, option: OptionReminder.Nothing }; }; /** @@ -62,34 +62,43 @@ const splitTime = (time: string) => { * @param info data about the context of the reminder * @returns Promise resolution of the sql request */ -export const newReminder = async (client: Client, time: string, info: infoReminder) => - new Promise((ok, ko) => { - const data = splitTime(time); - const timeout = strToSeconds(data.time); - const timeoutId = setTimeoutReminder(client, info, data.option, timeout); +export const newReminder = async ( + client: Client, + time: string, + info: infoReminder +) => + new Promise((ok, ko) => { + const data = splitTime(time); + const timeout = strToSeconds(data.time); + const timeoutId = setTimeoutReminder(client, info, data.option, timeout); - // Add the remind to the db - client.db.run('INSERT INTO reminder ( \ + // Add the remind to the db + client.db.run( + "INSERT INTO reminder ( \ data, expiration_date, option_id, channel_id, creation_date, user_id, guild_id, locale, timeout_id \ - ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ? );', [ - info.message, - `${info.createdAt + (timeout * 1000)}`, - data.option.valueOf(), - info.channelId, - `${info.createdAt}`, - info.userId, - info.guildId, - info.locale, - timeoutId], (err) => { - if (err) { - ko(err); - } + ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ? );", + [ + info.message, + `${info.createdAt + timeout * 1000}`, + data.option.valueOf(), + info.channelId, + `${info.createdAt}`, + info.userId, + info.guildId, + info.locale, + timeoutId, + ], + (err) => { + if (err) { + ko(err); + } - // Send confirmation to user - const loc = getLocale(client, info.locale); - ok(`${loc.get('c_reminder1')} ${data.time}.`); - }); - }); + // Send confirmation to user + const loc = getLocale(client, info.locale); + ok(`${loc.get("c_reminder1")} ${data.time}.`); + } + ); + }); /** * Delete a reminder from the database @@ -98,83 +107,99 @@ export const newReminder = async (client: Client, time: string, info: infoRemind * @param userId User ID who created the reminder * @returns what the SQlite request sended */ -export const deleteReminder = (client: Client, createdAt: string, userId: string) => { - // Delete the reminder for the database - return new Promise((ok, ko) => { - // Add the remind to the db - client.db.run('DELETE FROM reminder WHERE creation_date = ? AND user_id = ?', [createdAt, userId], (err) => { - if (err) { - ko(err); - } +export const deleteReminder = ( + client: Client, + createdAt: string, + userId: string +) => { + // Delete the reminder for the database + return new Promise((ok, ko) => { + // Add the remind to the db + client.db.run( + "DELETE FROM reminder WHERE creation_date = ? AND user_id = ?", + [createdAt, userId], + (err) => { + if (err) { + ko(err); + } - // Send confirmation to user - ok(true); - }); - }); + // Send confirmation to user + ok(true); + } + ); + }); }; -export const sendReminder = (client: Client, info: infoReminder, option: OptionReminder) => { - const loc = getLocale(client, info.locale); - // Send the message in the appropriate channel - // TODO: Embed - let message: string; - if (info.message === null) { - message = loc.get('c_reminder18'); - } else { - message = cleanCodeBlock(info.message); - } - const embed = new EmbedBuilder() - .setColor('Random') - .setDescription(message) - .setTimestamp(info.createdAt); +export const sendReminder = ( + client: Client, + info: infoReminder, + option: OptionReminder +) => { + const loc = getLocale(client, info.locale); + // Send the message in the appropriate channel + // TODO: Embed + let message: string; + if (info.message === null) { + message = loc.get("c_reminder18"); + } else { + message = cleanCodeBlock(info.message); + } + const embed = new EmbedBuilder() + .setColor("Random") + .setDescription(message) + .setTimestamp(info.createdAt); - let channelOk = false; - if (info.channelId !== null) { - if (client.channels.cache.get(info.channelId) !== undefined) { - channelOk = true; - } else { - embed.setFooter({ text: loc.get('c_reminder14') }); - } - } + let channelOk = false; + if (info.channelId !== null) { + if (client.channels.cache.get(info.channelId) !== undefined) { + channelOk = true; + } else { + embed.setFooter({ text: loc.get("c_reminder14") }); + } + } - let guildOk = false; - if (info.guildId !== null) { - const guild = client.guilds.cache.get(info.guildId); - if (guild !== undefined) { - if (guild.members.cache.get(info.userId) !== undefined) { - guildOk = true; - } else { - embed.setFooter({ text: `${loc.get('c_reminder15')} ${guild.name}.` }); - } - } else { - embed.setFooter({ text: loc.get('c_reminder16') }); - } - } + let guildOk = false; + if (info.guildId !== null) { + const guild = client.guilds.cache.get(info.guildId); + if (guild !== undefined) { + if (guild.members.cache.get(info.userId) !== undefined) { + guildOk = true; + } else { + embed.setFooter({ text: `${loc.get("c_reminder15")} ${guild.name}.` }); + } + } else { + embed.setFooter({ text: loc.get("c_reminder16") }); + } + } - if (option == OptionReminder.DirectMessage || !channelOk || !guildOk) { - // Direct message - const user = client.users.cache.get(info.userId); - if (user !== undefined) { - user.send({ embeds: [embed] }); - } - } else { - // Channel - client.channels.fetch(info.channelId ?? '').then((channel) => { - if (channel?.isTextBased()) { - let content = `<@${info.userId}>`; - embed.setFooter({ text: `${loc.get('c_reminder17')} ${timeDeltaToString(info.createdAt)}` }); + if (option == OptionReminder.DirectMessage || !channelOk || !guildOk) { + // Direct message + const user = client.users.cache.get(info.userId); + if (user !== undefined) { + user.send({ embeds: [embed] }); + } + } else { + // Channel + client.channels.fetch(info.channelId ?? "").then((channel) => { + if (channel?.isTextBased()) { + let content = `<@${info.userId}>`; + embed.setFooter({ + text: `${loc.get("c_reminder17")} ${timeDeltaToString( + info.createdAt + )}`, + }); - // Mention everybody if needed - if (option == OptionReminder.Mention) { - (info.message?.match(/<@\d+>/g) ?? []).forEach(mention => { - content += ' ' + mention; - }); - } + // Mention everybody if needed + if (option == OptionReminder.Mention) { + (info.message?.match(/<@\d+>/g) ?? []).forEach((mention) => { + content += " " + mention; + }); + } - channel.send({ content, embeds: [embed] }); - } - }); - } + channel.send({ content, embeds: [embed] }); + } + }); + } }; /** @@ -185,16 +210,25 @@ export const sendReminder = (client: Client, info: infoReminder, option: OptionR * @param timeout Amout of time before the reminder ends * @returns Timeout's ID */ -export const setTimeoutReminder = (client: Client, info: infoReminder, option: OptionReminder, timeout: number) => { - return Number(setTimeout(() => { - deleteReminder(client, String(info.createdAt), info.userId).then((val) => { - if (val != true) { - throw val; - } +export const setTimeoutReminder = ( + client: Client, + info: infoReminder, + option: OptionReminder, + timeout: number +) => { + return Number( + setTimeout(() => { + deleteReminder(client, String(info.createdAt), info.userId).then( + (val) => { + if (val != true) { + throw val; + } - sendReminder(client, info, option); - }); - }, timeout * 1000)); + sendReminder(client, info, option); + } + ); + }, timeout * 1000) + ); }; /** @@ -204,26 +238,33 @@ export const setTimeoutReminder = (client: Client, info: infoReminder, option: O * @param userId user ID to check * @param guildId guild ID where the ownership request as been send, 0 if DM */ -export const checkOwnershipReminder = async (client: Client, id: number, userId: string, guildId: string) => { - const data = await new Promise((ok, ko) => { - // Check the ownership - client.db.all('SELECT EXISTS ( \ +export const checkOwnershipReminder = async ( + client: Client, + id: number, + userId: string, + guildId: string +) => { + const data = (await new Promise((ok, ko) => { + // Check the ownership + client.db.all( + "SELECT EXISTS ( \ SELECT 1 FROM reminder \ WHERE id = ? \ AND user_id = ? \ AND (guild_id = ? OR guild_id = 0) \ - )', [ - id, userId, guildId, - ], (err, row) => { - if (err) { - ko(err); - } + )", + [id, userId, guildId], + (err, row) => { + if (err) { + ko(err); + } - // Send all the current reminders - ok(row[0]); - }); - }) as { [key: string]: number }; - return Object.keys(data).map((key) => data[key])[0] === 0 ? true : false; + // Send all the current reminders + ok(row[0]); + } + ); + })) as { [key: string]: number }; + return Object.keys(data).map((key) => data[key])[0] === 0 ? true : false; }; /** @@ -232,19 +273,22 @@ export const checkOwnershipReminder = async (client: Client, id: number, userId: * @param id Reminder's ID */ export const getReminderInfo = async (client: Client, id: number) => { - return await new Promise((ok, ko) => { - // Check the ownership - client.db.all('SELECT * FROM reminder \ - WHERE id = ?', [ - id], (err, row) => { - if (err) { - ko(err); - } + return (await new Promise((ok, ko) => { + // Check the ownership + client.db.all( + "SELECT * FROM reminder \ + WHERE id = ?", + [id], + (err, row) => { + if (err) { + ko(err); + } - // Send all the current reminders - ok(row[0]); - }); - }) as dbReminder; + // Send all the current reminders + ok(row[0]); + } + ); + })) as dbReminder; }; /** @@ -253,10 +297,11 @@ export const getReminderInfo = async (client: Client, id: number) => { * @param data Data who will override the data in database */ export const updateReminder = (client: Client, data: dbReminder) => { - // Delete the reminder for the database - return new Promise((ok, ko) => { - // Update the db - client.db.run('UPDATE reminder \ + // Delete the reminder for the database + return new Promise((ok, ko) => { + // Update the db + client.db.run( + "UPDATE reminder \ SET data = ?, \ expiration_date = ?, \ option_id = ?, \ @@ -266,24 +311,28 @@ export const updateReminder = (client: Client, data: dbReminder) => { guild_id = ?, \ locale = ?, \ timeout_id = ? \ - WHERE ID = ?', [ - data.data, - data.expiration_date, - data.option_id, - data.channel_id, - data.creation_date, - data.user_id, - data.guild_id, - data.locale, - data.timeout_id, - data.id], (err) => { - if (err) { - ko(err); - } + WHERE ID = ?", + [ + data.data, + data.expiration_date, + data.option_id, + data.channel_id, + data.creation_date, + data.user_id, + data.guild_id, + data.locale, + data.timeout_id, + data.id, + ], + (err) => { + if (err) { + ko(err); + } - ok(true); - }); - }); + ok(true); + } + ); + }); }; /** @@ -293,20 +342,27 @@ export const updateReminder = (client: Client, data: dbReminder) => { * @param guildId guild ID * @returns List of reminders of a user in a guild */ -const listReminders = async (client: Client, userId: string, guildId: string | null) => { - return await new Promise((ok, ko) => { - // Check the ownership - client.db.all('SELECT data, creation_date, expiration_date, id FROM reminder \ - WHERE user_id = ? AND (guild_id = ? OR guild_id = 0)', [ - userId, guildId ?? 0], (err, row) => { - if (err) { - ko(err); - } +const listReminders = async ( + client: Client, + userId: string, + guildId: string | null +) => { + return (await new Promise((ok, ko) => { + // Check the ownership + client.db.all( + "SELECT data, creation_date, expiration_date, id FROM reminder \ + WHERE user_id = ? AND (guild_id = ? OR guild_id = 0)", + [userId, guildId ?? 0], + (err, row) => { + if (err) { + ko(err); + } - // Send all the current reminders - ok(row); - }); - }) as dbReminder[]; + // Send all the current reminders + ok(row); + } + ); + })) as dbReminder[]; }; /** @@ -318,46 +374,65 @@ const listReminders = async (client: Client, userId: string, guildId: string | n * @param local Lang * @returns Pretty embed who list reminders */ -export const embedListReminders = async (client: Client, user: User, guildId: string | null, page: number, local: string) => { - const loc = getLocale(client, local); - const reminders = await listReminders(client, user.id, guildId); +export const embedListReminders = async ( + client: Client, + user: User, + guildId: string | null, + page: number, + local: string +) => { + const loc = getLocale(client, local); + const reminders = await listReminders(client, user.id, guildId); - const elementPerPage = 5; - const pageMax = Math.ceil(reminders.length / elementPerPage); + const elementPerPage = 5; + const pageMax = Math.ceil(reminders.length / elementPerPage); - if (pageMax <= 1) { - page = 1; - } - // TODO: Use Random color or force a color from args - const embed = new EmbedBuilder() - .setColor(Colors.DarkGrey) - .setDescription(`${loc.get('c_reminder5')} ${user} • ${loc.get('c_reminder6')} ${page}/${pageMax}`) - .setThumbnail(user.displayAvatarURL()); + if (pageMax <= 1) { + page = 1; + } + // TODO: Use Random color or force a color from args + const embed = new EmbedBuilder() + .setColor(Colors.DarkGrey) + .setDescription( + `${loc.get("c_reminder5")} ${user} • ${loc.get( + "c_reminder6" + )} ${page}/${pageMax}` + ) + .setThumbnail(user.displayAvatarURL()); - const limit = elementPerPage * page; + const limit = elementPerPage * page; - if (reminders.length > 0 && page <= pageMax) { - let curseur = limit - elementPerPage; - reminders.splice(0, limit - elementPerPage); - reminders.forEach((remind) => { - if (curseur < limit) { - let text = remind.data ?? loc.get('c_reminder7'); - if (text.length > 1024) { - text = `${text.substring(0, 1021)}...`; - } - const expiration = `${loc.get('c_reminder8')} ${timeDeltaToString(remind.expiration_date)}`; - embed.addFields({ - name: `#${remind.id} • ${loc.get('c_reminder9')} ${showDate(local, loc, new Date(Number(remind.creation_date)))}\n${expiration}`, - value: text, - inline: false, - }); + if (reminders.length > 0 && page <= pageMax) { + let curseur = limit - elementPerPage; + reminders.splice(0, limit - elementPerPage); + reminders.forEach((remind) => { + if (curseur < limit) { + let text = remind.data ?? loc.get("c_reminder7"); + if (text.length > 1024) { + text = `${text.substring(0, 1021)}...`; + } + const expiration = `${loc.get("c_reminder8")} ${timeDeltaToString( + remind.expiration_date + )}`; + embed.addFields({ + name: `#${remind.id} • ${loc.get("c_reminder9")} ${showDate( + local, + loc, + new Date(Number(remind.creation_date)) + )}\n${expiration}`, + value: text, + inline: false, + }); - curseur++; - } - }); - } else { - embed.addFields({ name: '\u200b', value: `${loc.get('c_reminder10')}${page} ${loc.get('c_reminder11')}.` }); - } + curseur++; + } + }); + } else { + embed.addFields({ + name: "\u200b", + value: `${loc.get("c_reminder10")}${page} ${loc.get("c_reminder11")}.`, + }); + } - return embed; + return embed; }; diff --git a/src/utils/time.ts b/src/utils/time.ts index 6e54c22..eb713f6 100644 --- a/src/utils/time.ts +++ b/src/utils/time.ts @@ -6,20 +6,20 @@ * @returns String */ export const showDate = ( - tz: string, - locale: Map, - date: Date + tz: string, + locale: Map, + date: Date ) => { - return date.toLocaleString(tz).replace(' ', ` ${locale.get('u_time_at')} `); + return date.toLocaleString(tz).replace(" ", ` ${locale.get("u_time_at")} `); }; enum TimeSecond { - Year = 31536000, - Week = 604800, - Day = 86400, - Hour = 3600, - Minute = 60, - Second = 1 + Year = 31536000, + Week = 604800, + Day = 86400, + Hour = 3600, + Minute = 60, + Second = 1, } /** @@ -28,18 +28,26 @@ enum TimeSecond { * @returns time in seconds */ export const strToSeconds = (time: string) => { - const regex = new RegExp(`(?<${TimeSecond[TimeSecond.Year]}>[0-9]+(?=[y|a]))|(?<${TimeSecond[TimeSecond.Week]}>[0-9]+(?=[w]))|(?<${TimeSecond[TimeSecond.Day]}>[0-9]+(?=[d|j]))|(?<${TimeSecond[TimeSecond.Hour]}>[0-9]+(?=[h]))|(?<${TimeSecond[TimeSecond.Minute]}>[0-9]+(?=[m]))|(?<${TimeSecond[TimeSecond.Second]}>[0-9]+(?=[s]?))`); + const regex = new RegExp( + `(?<${TimeSecond[TimeSecond.Year]}>[0-9]+(?=[y|a]))|(?<${ + TimeSecond[TimeSecond.Week] + }>[0-9]+(?=[w]))|(?<${TimeSecond[TimeSecond.Day]}>[0-9]+(?=[d|j]))|(?<${ + TimeSecond[TimeSecond.Hour] + }>[0-9]+(?=[h]))|(?<${TimeSecond[TimeSecond.Minute]}>[0-9]+(?=[m]))|(?<${ + TimeSecond[TimeSecond.Second] + }>[0-9]+(?=[s]?))` + ); - const data = Object.assign({}, regex.exec(time)?.groups); + const data = Object.assign({}, regex.exec(time)?.groups); - let res = 0; - Object.entries(data).forEach(([key, value]) => { - if (value) { - res += +value * TimeSecond[key as keyof typeof TimeSecond]; - } - }); + let res = 0; + Object.entries(data).forEach(([key, value]) => { + if (value) { + res += +value * TimeSecond[key as keyof typeof TimeSecond]; + } + }); - return res; + return res; }; /** @@ -48,7 +56,7 @@ export const strToSeconds = (time: string) => { * @returns Delta between the time and now */ export const timeDeltaToString = (time: number) => { - const now = Date.now(); - // TODO adapt the output and not always parse the time as seconds - return `${strToSeconds(`${(now - time) / 1000}`)} secs`; + const now = Date.now(); + // TODO adapt the output and not always parse the time as seconds + return `${strToSeconds(`${(now - time) / 1000}`)} secs`; }; diff --git a/tsconfig.json b/tsconfig.json index 5d0dfd6..333de2e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,10 +11,10 @@ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ /* Language and Environment */ - "target": "es6", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "target": "es6" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, "lib": [ "ES2021.String" - ], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */, // "jsx": "preserve", /* Specify what JSX code is generated. */ // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ @@ -27,8 +27,8 @@ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ - "rootDir": "./src", /* Specify the root folder within your source files. */ + "module": "commonjs" /* Specify what module code is generated. */, + "rootDir": "./src" /* Specify the root folder within your source files. */, // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ @@ -37,7 +37,7 @@ // "types": [], /* Specify type package names to be included without being referenced in a source file. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - "resolveJsonModule": true, /* Enable importing .json files. */ + "resolveJsonModule": true /* Enable importing .json files. */, // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ /* JavaScript Support */ @@ -51,7 +51,7 @@ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./dist", /* Specify an output folder for all emitted files. */ + "outDir": "./dist" /* Specify an output folder for all emitted files. */, // "removeComments": true, /* Disable emitting comments. */ // "noEmit": true, /* Disable emitting files from a compilation. */ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ @@ -73,12 +73,12 @@ /* Interop Constraints */ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, /* Type Checking */ - "strict": true, /* Enable all strict type-checking options. */ + "strict": true /* Enable all strict type-checking options. */, // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ @@ -100,10 +100,7 @@ /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ }, - "include": [ - "./**/*.ts", - "./src/locales/*.json" - ] + "include": ["./**/*.ts", "./src/locales/*.json"] }