Meta: Use of Prettier #58

Merged
Anri merged 6 commits from meta/prettier into main 2023-01-17 23:11:23 +01:00
37 changed files with 2343 additions and 1962 deletions
Showing only changes of commit 983333238f - Show all commits

View file

@ -1,27 +1,20 @@
{ {
"extends": [ "extends": [
"eslint:recommended", "eslint:recommended",
"plugin:@typescript-eslint/recommended" "plugin:@typescript-eslint/recommended",
"prettier"
], ],
"parser": "@typescript-eslint/parser", "parser": "@typescript-eslint/parser",
"parserOptions": { "parserOptions": {
"project": ["./tsconfig.json"] "project": ["./tsconfig.json"]
}, },
"plugins": [ "plugins": ["@typescript-eslint"],
"@typescript-eslint"
],
"rules": { "rules": {
"arrow-spacing": ["warn", { "before": true, "after": true }], "arrow-spacing": ["warn", { "before": true, "after": true }],
"brace-style": ["error"],
"comma-dangle": ["error", "always-multiline"],
"comma-spacing": "error",
"comma-style": "error", "comma-style": "error",
"curly": ["error", "multi-line", "consistent"], "curly": ["error", "multi-line", "consistent"],
"dot-location": ["error", "property"], "dot-location": ["error", "property"],
"handle-callback-err": "off", "handle-callback-err": "off",
"indent": ["error", "tab", { "SwitchCase": 1 }],
"keyword-spacing": "error",
"max-nested-callbacks": ["error", { "max": 4 }], "max-nested-callbacks": ["error", { "max": 4 }],
"max-statements-per-line": ["error", { "max": 2 }], "max-statements-per-line": ["error", { "max": 2 }],
"no-console": "off", "no-console": "off",
@ -30,23 +23,19 @@
"no-inline-comments": "error", "no-inline-comments": "error",
"no-lonely-if": "error", "no-lonely-if": "error",
"no-multi-spaces": "error", "no-multi-spaces": "error",
"no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }], "no-multiple-empty-lines": [
"error",
{ "max": 2, "maxEOF": 1, "maxBOF": 0 }
],
"no-shadow": "off", "no-shadow": "off",
"@typescript-eslint/no-shadow": ["error", { "allow": ["err", "resolve", "reject"] }], "@typescript-eslint/no-shadow": [
"error",
{ "allow": ["err", "resolve", "reject"] }
],
"no-trailing-spaces": ["error"], "no-trailing-spaces": ["error"],
"no-var": "error", "no-var": "error",
"object-curly-spacing": ["error", "always"],
"prefer-const": "error", "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-in-parens": "error",
"space-infix-ops": "error",
"space-unary-ops": "error", "space-unary-ops": "error",
"spaced-comment": "error", "spaced-comment": "error",
"yoda": "error" "yoda": "error"

View file

@ -1,13 +1,10 @@
--- ---
name: "🐞 Rapport d'un bug" name: "🐞 Rapport d'un bug"
about: "Signal un problème rencontré" about: "Signal un problème rencontré"
ref: "main" ref: "main"
labels: labels:
- bug - bug
- "help wanted" - "help wanted"
--- ---
Bot version: v`X.Y.Z` Bot version: v`X.Y.Z`

View file

@ -1,10 +1,7 @@
--- ---
name: "💫 Demande une fonctionnalitée" name: "💫 Demande une fonctionnalitée"
about: "Propose une nouvelle fonctionnalité à ajouter" about: "Propose une nouvelle fonctionnalité à ajouter"
ref: "main" ref: "main"
labels: labels:
- enhancement - enhancement
--- ---

1
.prettierignore Normal file
View file

@ -0,0 +1 @@
dist

1
.prettierrc.json Normal file
View file

@ -0,0 +1 @@
{}

View file

@ -1,5 +1,3 @@
{ {
"recommendations": [ "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
"dbaeumer.vscode-eslint"
]
} }

View file

@ -1,5 +1,5 @@
{ {
"editor.tabSize": 4, "editor.tabSize": 2,
"editor.insertSpaces": false, "editor.insertSpaces": false,
"files.insertFinalNewline": true, "files.insertFinalNewline": true,

View file

@ -3,23 +3,29 @@
[**Ajoute le bot à ton serveur**](https://discord.com/api/oauth2/authorize?client_id=965598852407230494&permissions=8&scope=bot%20applications.commands) [**Ajoute le bot à ton serveur**](https://discord.com/api/oauth2/authorize?client_id=965598852407230494&permissions=8&scope=bot%20applications.commands)
## Lancer le bot ## Lancer le bot
### En local ### En local
> Cloner le repo. > Cloner le repo.
> Spécifier un fichier `.env` en suivant [l'exemple](config/example.env). > Spécifier un fichier `.env` en suivant [l'exemple](config/example.env).
> Installer les dépendences du bot. > Installer les dépendences du bot.
```bash ```bash
npm install npm install
``` ```
> Lancer le bot. > Lancer le bot.
```bash ```bash
npm run main npm run main
``` ```
### Avec Docker (Recommandé) ### Avec Docker (Recommandé)
> Facile avec `docker-compose` > Facile avec `docker-compose`
```docker ```docker
version: "3.9" version: "3.9"
services: services:
@ -34,23 +40,28 @@ services:
``` ```
## Variables d'environnements ## Variables d'environnements
| Nom | Description | Par défaut | Commentaire
| :-----------: | :--------------: | :--------: | :-: | Nom | Description | Par défaut | Commentaire |
| :-----------: | :---------------: | :--------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| TOKEN_DISCORD | Token Discord | Aucune | | 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.<br>Liste des traductions disponibles [ici](./src/locales/). | 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.<br>Liste des traductions disponibles [ici](./src/locales/). |
## Volumes ## Volumes
| Chemin | Description
| :-------: | :-: | Chemin | Description |
| `/config` | Dossier de configuration, par exemple, c'est ici que la base de donnée est. | :-------: | :-------------------------------------------------------------------------: |
| `/config` | Dossier de configuration, par exemple, c'est ici que la base de donnée est. |
# Contribuer # Contribuer
Toute contribution est la bienvenue ! Toute contribution est la bienvenue !
Pour commencer, lis le [fichier de contribution](./CONTRIBUTING.md). Pour commencer, lis le [fichier de contribution](./CONTRIBUTING.md).
# Licence # Licence
Voir le [fichier LICENCE](./LICENCE). Voir le [fichier LICENCE](./LICENCE).
# Références # Références
[Photo de profil](https://picrew.me/image_maker/1497656) [Photo de profil](https://picrew.me/image_maker/1497656)

22
package-lock.json generated
View file

@ -23,6 +23,7 @@
"@typescript-eslint/parser": "^5.30.7", "@typescript-eslint/parser": "^5.30.7",
"dotenv": "^16.0.1", "dotenv": "^16.0.1",
"eslint": "^8.20.0", "eslint": "^8.20.0",
"prettier": "2.8.3",
"ts-node-dev": "^2.0.0" "ts-node-dev": "^2.0.0"
} }
}, },
@ -2438,6 +2439,21 @@
"node": ">= 0.8.0" "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": { "node_modules/promise-inflight": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
@ -5110,6 +5126,12 @@
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
"dev": true "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": { "promise-inflight": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",

View file

@ -6,7 +6,8 @@
"scripts": { "scripts": {
"main": "rm -r dist 2> /dev/null; npx tsc && node ./dist/index.js", "main": "rm -r dist 2> /dev/null; npx tsc && node ./dist/index.js",
"debug": "npx tsnd --respawn ./src/index.ts", "debug": "npx tsnd --respawn ./src/index.ts",
"lint": "npx eslint src" "lint": "npx eslint src",
"format": "npx prettier --check src"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -17,18 +18,19 @@
"dependencies": { "dependencies": {
"@discordjs/rest": "^1.1.0", "@discordjs/rest": "^1.1.0",
"@types/sqlite3": "^3.1.8", "@types/sqlite3": "^3.1.8",
"@types/uuid": "^9.0.0",
"discord-api-types": "^0.36.3", "discord-api-types": "^0.36.3",
"discord.js": "^14.3.0", "discord.js": "^14.3.0",
"sqlite3": "^5.0.11", "sqlite3": "^5.0.11",
"typescript": "^4.7.4", "typescript": "^4.7.4",
"uuid": "^9.0.0", "uuid": "^9.0.0"
"@types/uuid": "^9.0.0"
}, },
"devDependencies": { "devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.30.7", "@typescript-eslint/eslint-plugin": "^5.30.7",
"@typescript-eslint/parser": "^5.30.7", "@typescript-eslint/parser": "^5.30.7",
"dotenv": "^16.0.1", "dotenv": "^16.0.1",
"eslint": "^8.20.0", "eslint": "^8.20.0",
"prettier": "2.8.3",
"ts-node-dev": "^2.0.0" "ts-node-dev": "^2.0.0"
} }
} }

View file

@ -1,28 +1,33 @@
import { readdir } from 'fs/promises'; import { readdir } from "fs/promises";
import { removeExtension } from '../utils/misc'; import { removeExtension } from "../utils/misc";
import { ChatInputCommandInteraction, Client, MessageComponentInteraction } from 'discord.js'; import {
import { getLocale } from '../utils/locales'; ChatInputCommandInteraction,
Client,
MessageComponentInteraction,
} from "discord.js";
import { getLocale } from "../utils/locales";
export default async (client: Client) => { export default async (client: Client) => {
// Dossier des buttons // Dossier des buttons
const buttons_categories = (await readdir(__dirname)) const buttons_categories = (await readdir(__dirname)).filter(
.filter(element => !element.endsWith('.js') && !element.endsWith('.ts')); (element) => !element.endsWith(".js") && !element.endsWith(".ts")
);
await Promise.all( await Promise.all(
// For each categorie // For each categorie
buttons_categories.map(async buttons_category => { buttons_categories.map(async (buttons_category) => {
// Retrieve all the commands // Retrieve all the commands
const button_files = await readdir(`${__dirname}/${buttons_category}`); const button_files = await readdir(`${__dirname}/${buttons_category}`);
// Add the category to the collection for the help command // Add the category to the collection for the help command
client.buttons.categories.set( client.buttons.categories.set(
buttons_category, buttons_category,
button_files.map(removeExtension), button_files.map(removeExtension)
); );
// Add the button // Add the button
return Promise.all( return Promise.all(
button_files.map(async button_file => { button_files.map(async (button_file) => {
const button = ( const button = (
await import(`../buttons/${buttons_category}/${button_file}`) await import(`../buttons/${buttons_category}/${button_file}`)
).default; ).default;
@ -30,9 +35,9 @@ export default async (client: Client) => {
// Add it to the collection so the interaction will work // Add it to the collection so the interaction will work
client.buttons.list.set(button.data.name, button); client.buttons.list.set(button.data.name, button);
return button.data; return button.data;
}), })
); );
}), })
); );
}; };
@ -43,20 +48,28 @@ export default async (client: Client) => {
* @param id Button ID * @param id Button ID
* @param deferUpdate defer update in case update take time * @param deferUpdate defer update in case update take time
*/ */
export const collect = (client: Client, interaction: ChatInputCommandInteraction | MessageComponentInteraction, id: string, deferUpdate = false) => { export const collect = (
client: Client,
interaction: ChatInputCommandInteraction | MessageComponentInteraction,
id: string,
deferUpdate = false
) => {
const loc = getLocale(client, interaction.locale); const loc = getLocale(client, interaction.locale);
const button = client.buttons.list.get(id.split('_')[0]); const button = client.buttons.list.get(id.split("_")[0]);
if (!button) { if (!button) {
interaction.reply({ interaction.reply({
content: loc.get('e_interacreate_no_button'), content: loc.get("e_interacreate_no_button"),
ephemeral: true, ephemeral: true,
}); });
} }
const filter = (i: MessageComponentInteraction) => i.customId === id; const filter = (i: MessageComponentInteraction) => i.customId === id;
const collector = interaction.channel?.createMessageComponentCollector({ filter, max: 1 }); const collector = interaction.channel?.createMessageComponentCollector({
collector?.on('collect', async (i) => { filter,
max: 1,
});
collector?.on("collect", async (i) => {
if (deferUpdate) { if (deferUpdate) {
await i.deferUpdate(); await i.deferUpdate();
} }

View file

@ -1,15 +1,25 @@
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, Client, MessageComponentInteraction, User } from 'discord.js'; import {
import { v4 as uuidv4 } from 'uuid'; ActionRowBuilder,
import { getLocale } from '../../utils/locales'; ButtonBuilder,
import { getFilename } from '../../utils/misc'; ButtonStyle,
import { embedListReminders } from '../../utils/reminder'; Client,
import { collect } from '../loader'; 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 { export default {
data: { data: {
name: getFilename(__filename), name: getFilename(__filename),
}, },
interaction: async (interaction: MessageComponentInteraction, client: Client) => { interaction: async (
interaction: MessageComponentInteraction,
client: Client
) => {
const loc = getLocale(client, interaction.locale); const loc = getLocale(client, interaction.locale);
const embed_desc = interaction.message.embeds.at(0)?.description as string; const embed_desc = interaction.message.embeds.at(0)?.description as string;
@ -27,21 +37,28 @@ export default {
const user = client.users.cache.get(userId) as User; const user = client.users.cache.get(userId) as User;
// Fetch list // Fetch list
const list = await embedListReminders(client, user, interaction.guildId, page, interaction.locale); const list = await embedListReminders(
client,
user,
interaction.guildId,
page,
interaction.locale
);
const idPrec = 'reminderList-prec_' + uuidv4(); const idPrec = "reminderList-prec_" + uuidv4();
const idNext = 'reminderList-next_' + uuidv4(); const idNext = "reminderList-next_" + uuidv4();
const row = new ActionRowBuilder<ButtonBuilder>() const row = new ActionRowBuilder<ButtonBuilder>()
.addComponents( .addComponents(
new ButtonBuilder() new ButtonBuilder()
.setCustomId(idPrec) .setCustomId(idPrec)
.setLabel(loc.get('c_reminder12')) .setLabel(loc.get("c_reminder12"))
.setStyle(ButtonStyle.Primary)) .setStyle(ButtonStyle.Primary)
)
.addComponents( .addComponents(
new ButtonBuilder() new ButtonBuilder()
.setCustomId(idNext) .setCustomId(idNext)
.setLabel(loc.get('c_reminder13')) .setLabel(loc.get("c_reminder13"))
.setStyle(ButtonStyle.Primary), .setStyle(ButtonStyle.Primary)
); );
// Buttons interactions // Buttons interactions

View file

@ -1,15 +1,25 @@
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, Client, MessageComponentInteraction, User } from 'discord.js'; import {
import { v4 as uuidv4 } from 'uuid'; ActionRowBuilder,
import { getLocale } from '../../utils/locales'; ButtonBuilder,
import { getFilename } from '../../utils/misc'; ButtonStyle,
import { embedListReminders } from '../../utils/reminder'; Client,
import { collect } from '../loader'; 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 { export default {
data: { data: {
name: getFilename(__filename), name: getFilename(__filename),
}, },
interaction: async (interaction: MessageComponentInteraction, client: Client) => { interaction: async (
interaction: MessageComponentInteraction,
client: Client
) => {
const loc = getLocale(client, interaction.locale); const loc = getLocale(client, interaction.locale);
const embed_desc = interaction.message.embeds.at(0)?.description as string; const embed_desc = interaction.message.embeds.at(0)?.description as string;
@ -27,21 +37,28 @@ export default {
const user = client.users.cache.get(userId) as User; const user = client.users.cache.get(userId) as User;
// Fetch list // Fetch list
const list = await embedListReminders(client, user, interaction.guildId, page, interaction.locale); const list = await embedListReminders(
client,
user,
interaction.guildId,
page,
interaction.locale
);
const idPrec = 'reminderList-prec_' + uuidv4(); const idPrec = "reminderList-prec_" + uuidv4();
const idNext = 'reminderList-next_' + uuidv4(); const idNext = "reminderList-next_" + uuidv4();
const row = new ActionRowBuilder<ButtonBuilder>() const row = new ActionRowBuilder<ButtonBuilder>()
.addComponents( .addComponents(
new ButtonBuilder() new ButtonBuilder()
.setCustomId(idPrec) .setCustomId(idPrec)
.setLabel(loc.get('c_reminder12')) .setLabel(loc.get("c_reminder12"))
.setStyle(ButtonStyle.Primary)) .setStyle(ButtonStyle.Primary)
)
.addComponents( .addComponents(
new ButtonBuilder() new ButtonBuilder()
.setCustomId(idNext) .setCustomId(idNext)
.setLabel(loc.get('c_reminder13')) .setLabel(loc.get("c_reminder13"))
.setStyle(ButtonStyle.Primary), .setStyle(ButtonStyle.Primary)
); );
// Buttons interactions // Buttons interactions

View file

@ -1,32 +1,33 @@
import { REST } from '@discordjs/rest'; import { REST } from "@discordjs/rest";
import { Routes } from 'discord-api-types/v9'; import { Routes } from "discord-api-types/v9";
import { Client } from 'discord.js'; import { Client } from "discord.js";
import { readdir } from 'fs/promises'; import { readdir } from "fs/promises";
import { removeExtension } from '../utils/misc'; import { removeExtension } from "../utils/misc";
/** Load all the commands. */ /** Load all the commands. */
export default async (client: Client) => { 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)) const command_categories = (await readdir(__dirname)).filter(
.filter(element => !element.endsWith('.js') && !element.endsWith('.ts')); (element) => !element.endsWith(".js") && !element.endsWith(".ts")
);
const commands = ( const commands = (
await Promise.all( await Promise.all(
// For each categorie // For each categorie
command_categories.map(async command_category => { command_categories.map(async (command_category) => {
// Retrieve all the commands // Retrieve all the commands
const command_files = await readdir(`${__dirname}/${command_category}`); const command_files = await readdir(`${__dirname}/${command_category}`);
// Add the category to the collection for the help command // Add the category to the collection for the help command
client.commands.categories.set( client.commands.categories.set(
command_category, command_category,
command_files.map(removeExtension), command_files.map(removeExtension)
); );
// Add the command // Add the command
return Promise.all( return Promise.all(
command_files.map(async command_file => { command_files.map(async (command_file) => {
const command = ( const command = (
await import(`../commands/${command_category}/${command_file}`) await import(`../commands/${command_category}/${command_file}`)
).default; ).default;
@ -36,9 +37,9 @@ export default async (client: Client) => {
client.commands.list.set(command.data.name, command); client.commands.list.set(command.data.name, command);
return command; return command;
}), })
); );
}), })
) )
).flat(2); ).flat(2);
@ -49,7 +50,9 @@ export default async (client: Client) => {
const allowedGuilds = client.guilds.cache; const allowedGuilds = client.guilds.cache;
// Assign each commands to the guilds // Assign each commands to the guilds
commands.filter((c) => c.scope().length > 0).forEach((c) => { commands
.filter((c) => c.scope().length > 0)
.forEach((c) => {
c.scope().forEach((guild: string) => { c.scope().forEach((guild: string) => {
if (allowedGuilds.get(guild) !== undefined) { if (allowedGuilds.get(guild) !== undefined) {
const guildCommands = scopedCommands.get(guild); const guildCommands = scopedCommands.get(guild);
@ -63,11 +66,15 @@ export default async (client: Client) => {
}); });
}); });
scopedCommands scopedCommands.forEach(
.forEach(async (command, guild) => await rest.put( async (command, guild) =>
Routes.applicationGuildCommands(client.user?.id as string, guild), { await rest.put(
Routes.applicationGuildCommands(client.user?.id as string, guild),
{
body: command, body: command,
})); }
)
);
// Send global commands to Discord // Send global commands to Discord
const globalCommands = commands.filter((c) => c.scope().length == 0); const globalCommands = commands.filter((c) => c.scope().length == 0);

View file

@ -1,93 +1,119 @@
import { SlashCommandBuilder } from '@discordjs/builders'; import { SlashCommandBuilder } from "@discordjs/builders";
import { ChannelType, Client, Colors, CommandInteraction, EmbedBuilder, NonThreadGuildBasedChannel } from 'discord.js'; import {
import '../../modules/string'; ChannelType,
import { getLocale, getLocalizations } from '../../utils/locales'; Client,
import { getFilename } from '../../utils/misc'; Colors,
CommandInteraction,
EmbedBuilder,
NonThreadGuildBasedChannel,
} from "discord.js";
import "../../modules/string";
import { getLocale, getLocalizations } from "../../utils/locales";
import { getFilename } from "../../utils/misc";
export default { export default {
scope: () => ['807244911350906920'], scope: () => ["807244911350906920"],
data: (client: Client) => { data: (client: Client) => {
const filename = getFilename(__filename); const filename = getFilename(__filename);
return new SlashCommandBuilder() return (
.setName( new SlashCommandBuilder()
filename.toLowerCase()) .setName(filename.toLowerCase())
.setDescription(client.locales.get(client.config.default_lang) .setDescription(
?.get(`c_${filename}_desc`) ?? '') client.locales
.get(client.config.default_lang)
?.get(`c_${filename}_desc`) ?? ""
)
.setNameLocalizations( .setNameLocalizations(
getLocalizations(client, `c_${filename}_name`, true)) getLocalizations(client, `c_${filename}_name`, true)
)
.setDescriptionLocalizations( .setDescriptionLocalizations(
getLocalizations(client, `c_${filename}_desc`)) getLocalizations(client, `c_${filename}_desc`)
)
// Command option // Command option
.addStringOption(option => option .addStringOption((option) =>
.setName(client.locales.get(client.config.default_lang) option
?.get(`c_${filename}_opt1_name`) ?? '') .setName(
.setDescription(client.locales.get(client.config.default_lang) client.locales
?.get(`c_${filename}_opt1_desc`) ?? '') .get(client.config.default_lang)
?.get(`c_${filename}_opt1_name`) ?? ""
)
.setDescription(
client.locales
.get(client.config.default_lang)
?.get(`c_${filename}_opt1_desc`) ?? ""
)
.setNameLocalizations( .setNameLocalizations(
getLocalizations(client, `c_${filename}_opt1_name`, true)) getLocalizations(client, `c_${filename}_opt1_name`, true)
)
.setDescriptionLocalizations( .setDescriptionLocalizations(
getLocalizations(client, `c_${filename}_opt1_desc`)) getLocalizations(client, `c_${filename}_opt1_desc`)
)
)
); );
}, },
interaction: async (interaction: CommandInteraction, client: Client) => { interaction: async (interaction: CommandInteraction, client: Client) => {
const loc = getLocale(client, interaction.locale); const loc = getLocale(client, interaction.locale);
const desiredCat = interaction.options.get(client const desiredCat = interaction.options.get(
.locales client.locales
.get(client.config.default_lang) .get(client.config.default_lang)
?.get(`c_${getFilename(__filename)}_opt1_name`) ?? '')?.value as string; ?.get(`c_${getFilename(__filename)}_opt1_name`) ?? ""
)?.value as string;
// If a category isn't specified // If a category isn't specified
if (!desiredCat) { if (!desiredCat) {
// Sends a list of commands sorted into categories // Sends a list of commands sorted into categories
return interaction.reply({ return interaction.reply({
embeds: [ embeds: [
new EmbedBuilder() new EmbedBuilder()
.setColor(Colors.Blurple) .setColor(Colors.Blurple)
.setTitle(loc.get('c_archive1')) .setTitle(loc.get("c_archive1"))
.setDescription(loc.get('c_archive2')), .setDescription(loc.get("c_archive2")),
], ],
}); });
} }
// If a category is specified // If a category is specified
const cleanCat = ['L1', 'L2', 'L3', 'M1', 'M2']; const cleanCat = ["L1", "L2", "L3", "M1", "M2"];
const channel = cleanCat.includes(desiredCat); const channel = cleanCat.includes(desiredCat);
if (!channel) { if (!channel) {
// Category doesn't exist or is not included // Category doesn't exist or is not included
return interaction.reply({ return interaction.reply({
content: `${loc.get('c_archive3')} \`${desiredCat}\``, content: `${loc.get("c_archive3")} \`${desiredCat}\``,
ephemeral: true, ephemeral: true,
}); });
} }
const allChannel = interaction.guild?.channels.fetch(); const allChannel = interaction.guild?.channels.fetch();
allChannel?.then(async channelGuild => { allChannel?.then(async (channelGuild) => {
// Retrieve category to archive // Retrieve category to archive
const catToArchive = channelGuild const catToArchive = channelGuild
.filter(chan => chan?.type == ChannelType.GuildCategory) .filter((chan) => chan?.type == ChannelType.GuildCategory)
.filter(chan => chan?.name == desiredCat); .filter((chan) => chan?.name == desiredCat);
// Create/Retrieve the archive category // Create/Retrieve the archive category
const catArchivedName = 'archive - ' + desiredCat; const catArchivedName = "archive - " + desiredCat;
const catArchivedMap = channelGuild const catArchivedMap = channelGuild
.filter(chan => chan?.type == ChannelType.GuildCategory) .filter((chan) => chan?.type == ChannelType.GuildCategory)
.filter(chan => chan?.name == catArchivedName); .filter((chan) => chan?.name == catArchivedName);
let catArchived: NonThreadGuildBasedChannel | null | undefined; let catArchived: NonThreadGuildBasedChannel | null | undefined;
if (catArchivedMap.size > 0) { if (catArchivedMap.size > 0) {
catArchived = catArchivedMap.at(0); catArchived = catArchivedMap.at(0);
} else { } else {
catArchived = await interaction.guild?.channels catArchived = await interaction.guild?.channels.create({
.create({ name: catArchivedName, type: ChannelType.GuildCategory }); name: catArchivedName,
type: ChannelType.GuildCategory,
});
} }
const allChannelDesired = channelGuild const allChannelDesired = channelGuild
.filter(chan => chan?.type == 0) .filter((chan) => chan?.type == 0)
.filter(chan => chan?.parentId == catToArchive.map(cat => cat?.id)[0]); .filter(
(chan) => chan?.parentId == catToArchive.map((cat) => cat?.id)[0]
);
// If no channels in the source category // If no channels in the source category
if (allChannelDesired.size == 0) { if (allChannelDesired.size == 0) {
@ -95,32 +121,36 @@ export default {
embeds: [ embeds: [
new EmbedBuilder() new EmbedBuilder()
.setColor(Colors.Blurple) .setColor(Colors.Blurple)
.setTitle(loc.get('c_archive6')) .setTitle(loc.get("c_archive6"))
.setDescription( .setDescription(loc.get("c_archive7")),
loc.get('c_archive7')
),
], ],
}); });
} }
// Move channels to the archived categoryx // Move channels to the archived categoryx
allChannelDesired.forEach(elem => elem?.setParent(catArchived?.id as string)); allChannelDesired.forEach((elem) =>
elem?.setParent(catArchived?.id as string)
);
return interaction.reply({ return interaction.reply({
embeds: [ embeds: [
new EmbedBuilder() new EmbedBuilder()
.setColor(Colors.Blurple) .setColor(Colors.Blurple)
.setTitle(loc.get('c_archive4') .setTitle(
+ ' `' loc.get("c_archive4") +
+ catToArchive.map(cat => cat?.name) " `" +
+ '` ' catToArchive.map((cat) => cat?.name) +
+ loc.get('c_archive5') "` " +
+ ' `' loc.get("c_archive5") +
+ catArchivedName " `" +
+ '`') catArchivedName +
"`"
)
.setDescription( .setDescription(
allChannelDesired allChannelDesired
.map(cgD => cgD?.name).toString().replaceAll(',', '\n') .map((cgD) => cgD?.name)
.toString()
.replaceAll(",", "\n")
), ),
], ],
}); });

View file

@ -1,44 +1,68 @@
import { SlashCommandBuilder } from '@discordjs/builders'; import { SlashCommandBuilder } from "@discordjs/builders";
import { Locale } from 'discord-api-types/v9'; import { Locale } from "discord-api-types/v9";
import { Client, ChatInputCommandInteraction, EmbedBuilder, Colors } from 'discord.js'; import {
import { getLocale, getLocalizations } from '../../utils/locales'; Client,
import { getFilename } from '../../utils/misc'; ChatInputCommandInteraction,
import '../../modules/string'; EmbedBuilder,
Colors,
} from "discord.js";
import { getLocale, getLocalizations } from "../../utils/locales";
import { getFilename } from "../../utils/misc";
import "../../modules/string";
export default { export default {
scope: () => [], scope: () => [],
data: (client: Client) => { data: (client: Client) => {
const filename = getFilename(__filename); const filename = getFilename(__filename);
return new SlashCommandBuilder() return (
.setName( new SlashCommandBuilder()
filename.toLowerCase()) .setName(filename.toLowerCase())
.setDescription(client.locales.get(client.config.default_lang) .setDescription(
?.get(`c_${filename}_desc`) ?? '') client.locales
.get(client.config.default_lang)
?.get(`c_${filename}_desc`) ?? ""
)
.setNameLocalizations( .setNameLocalizations(
getLocalizations(client, `c_${filename}_name`, true)) getLocalizations(client, `c_${filename}_name`, true)
)
.setDescriptionLocalizations( .setDescriptionLocalizations(
getLocalizations(client, `c_${filename}_desc`)) getLocalizations(client, `c_${filename}_desc`)
)
// Command option // Command option
.addStringOption(option => option .addStringOption((option) =>
.setName(client.locales.get(client.config.default_lang) option
?.get(`c_${filename}_opt1_name`) ?? '') .setName(
.setDescription(client.locales.get(client.config.default_lang) client.locales
?.get(`c_${filename}_opt1_desc`) ?? '') .get(client.config.default_lang)
?.get(`c_${filename}_opt1_name`) ?? ""
)
.setDescription(
client.locales
.get(client.config.default_lang)
?.get(`c_${filename}_opt1_desc`) ?? ""
)
.setNameLocalizations( .setNameLocalizations(
getLocalizations(client, `c_${filename}_opt1_name`, true)) getLocalizations(client, `c_${filename}_opt1_name`, true)
)
.setDescriptionLocalizations( .setDescriptionLocalizations(
getLocalizations(client, `c_${filename}_opt1_desc`)) getLocalizations(client, `c_${filename}_opt1_desc`)
)
)
); );
}, },
interaction: async (interaction: ChatInputCommandInteraction, client: Client) => { interaction: async (
interaction: ChatInputCommandInteraction,
client: Client
) => {
const loc = getLocale(client, interaction.locale); const loc = getLocale(client, interaction.locale);
const desired_command = interaction.options.getString(client const desired_command = interaction.options.getString(
.locales client.locales
.get(client.config.default_lang) .get(client.config.default_lang)
?.get(`c_${getFilename(__filename)}_opt1_name`) ?? ''); ?.get(`c_${getFilename(__filename)}_opt1_name`) ?? ""
);
// If a command isn't specified // If a command isn't specified
if (!desired_command) { if (!desired_command) {
@ -52,7 +76,7 @@ export default {
client.commands.categories.forEach((commands_name, category) => { client.commands.categories.forEach((commands_name, category) => {
const commands = commands_name.reduce((data, command_name) => { const commands = commands_name.reduce((data, command_name) => {
return data + `\`/${command_name}\`, `; return data + `\`/${command_name}\`, `;
}, ''); }, "");
fields.push({ fields.push({
name: category.capitalize() + ` (${commands_name.length})`, name: category.capitalize() + ` (${commands_name.length})`,
@ -61,13 +85,15 @@ export default {
}); });
// Sends a list of commands sorted into categories // Sends a list of commands sorted into categories
return interaction.reply({ embeds: [ return interaction.reply({
embeds: [
new EmbedBuilder() new EmbedBuilder()
.setColor(Colors.Blurple) .setColor(Colors.Blurple)
.setTitle(loc.get('c_help1')) .setTitle(loc.get("c_help1"))
.setDescription(loc.get('c_help2')) .setDescription(loc.get("c_help2"))
.addFields(fields), .addFields(fields),
] }); ],
});
} }
// If a command is specified // If a command is specified
@ -76,23 +102,25 @@ export default {
if (!command) { if (!command) {
// Command don't exist // Command don't exist
return interaction.reply({ return interaction.reply({
content: `${loc.get('c_help3')} \`${desired_command}\``, content: `${loc.get("c_help3")} \`${desired_command}\``,
ephemeral: true, ephemeral: true,
}); });
} }
// Send information about the command // Send information about the command
return interaction.reply({ embeds: [ return interaction.reply({
embeds: [
new EmbedBuilder() new EmbedBuilder()
.setColor(Colors.Blurple) .setColor(Colors.Blurple)
.setTitle('`/' + command.data.name + '`') .setTitle("`/" + command.data.name + "`")
.setDescription( .setDescription(
// Loads the description // Loads the description
// according to the user's locals // according to the user's locals
command.data.description_localizations command.data.description_localizations?.[
?.[interaction.locale as Locale] interaction.locale as Locale
?? command.data.description ] ?? command.data.description
), ),
] }); ],
});
}, },
}; };

View file

@ -1,7 +1,7 @@
import { SlashCommandBuilder } from '@discordjs/builders'; import { SlashCommandBuilder } from "@discordjs/builders";
import { ChatInputCommandInteraction, Client, Message } from 'discord.js'; import { ChatInputCommandInteraction, Client, Message } from "discord.js";
import { getLocale, getLocalizations } from '../../utils/locales'; import { getLocale, getLocalizations } from "../../utils/locales";
import { getFilename } from '../../utils/misc'; import { getFilename } from "../../utils/misc";
export default { export default {
scope: () => [], scope: () => [],
@ -9,28 +9,35 @@ export default {
data: (client: Client) => { data: (client: Client) => {
const filename = getFilename(__filename); const filename = getFilename(__filename);
return new SlashCommandBuilder() return new SlashCommandBuilder()
.setName( .setName(filename.toLowerCase())
filename.toLowerCase()) .setDescription(
.setDescription(client.locales.get(client.config.default_lang) client.locales
?.get(`c_${filename}_desc`) ?? '') .get(client.config.default_lang)
?.get(`c_${filename}_desc`) ?? ""
)
.setNameLocalizations( .setNameLocalizations(
getLocalizations(client, `c_${filename}_name`, true)) getLocalizations(client, `c_${filename}_name`, true)
)
.setDescriptionLocalizations( .setDescriptionLocalizations(
getLocalizations(client, `c_${filename}_desc`) getLocalizations(client, `c_${filename}_desc`)
); );
}, },
interaction: async (interaction: ChatInputCommandInteraction, client: Client) => { interaction: async (
interaction: ChatInputCommandInteraction,
client: Client
) => {
const loc = getLocale(client, interaction.locale); const loc = getLocale(client, interaction.locale);
const sent = await interaction.reply({ const sent = (await interaction.reply({
content: 'Pinging...', content: "Pinging...",
fetchReply: true, fetchReply: true,
}) as Message; })) as Message;
interaction.editReply( interaction.editReply(
`${loc?.get('c_ping1')}: \ `${loc?.get("c_ping1")}: \
${sent.createdTimestamp - interaction.createdTimestamp}ms ${sent.createdTimestamp - interaction.createdTimestamp}ms
${loc?.get('c_ping2')}: ${client.ws.ping}ms.`); ${loc?.get("c_ping2")}: ${client.ws.ping}ms.`
);
}, },
}; };

View file

@ -1,115 +1,142 @@
import { SlashCommandBuilder } from '@discordjs/builders'; import { SlashCommandBuilder } from "@discordjs/builders";
import { ChannelType, Client, Colors, CommandInteraction, EmbedBuilder } from 'discord.js'; import {
import '../../modules/string'; ChannelType,
import { getLocale, getLocalizations } from '../../utils/locales'; Client,
import { getFilename } from '../../utils/misc'; Colors,
CommandInteraction,
EmbedBuilder,
} from "discord.js";
import "../../modules/string";
import { getLocale, getLocalizations } from "../../utils/locales";
import { getFilename } from "../../utils/misc";
export default { export default {
scope: () => ['807244911350906920'], scope: () => ["807244911350906920"],
data: (client: Client) => { data: (client: Client) => {
const filename = getFilename(__filename); const filename = getFilename(__filename);
return new SlashCommandBuilder() return (
.setName( new SlashCommandBuilder()
filename.toLowerCase()) .setName(filename.toLowerCase())
.setDescription(client.locales.get(client.config.default_lang) .setDescription(
?.get(`c_${filename}_desc`) ?? '') client.locales
.get(client.config.default_lang)
?.get(`c_${filename}_desc`) ?? ""
)
.setNameLocalizations( .setNameLocalizations(
getLocalizations(client, `c_${filename}_name`, true)) getLocalizations(client, `c_${filename}_name`, true)
)
.setDescriptionLocalizations( .setDescriptionLocalizations(
getLocalizations(client, `c_${filename}_desc`)) getLocalizations(client, `c_${filename}_desc`)
)
// Command option // Command option
.addStringOption(option => option .addStringOption((option) =>
.setName(client.locales.get(client.config.default_lang) option
?.get(`c_${filename}_opt1_name`) ?? '') .setName(
.setDescription(client.locales.get(client.config.default_lang) client.locales
?.get(`c_${filename}_opt1_desc`) ?? '') .get(client.config.default_lang)
?.get(`c_${filename}_opt1_name`) ?? ""
)
.setDescription(
client.locales
.get(client.config.default_lang)
?.get(`c_${filename}_opt1_desc`) ?? ""
)
.setNameLocalizations( .setNameLocalizations(
getLocalizations(client, `c_${filename}_opt1_name`, true)) getLocalizations(client, `c_${filename}_opt1_name`, true)
)
.setDescriptionLocalizations( .setDescriptionLocalizations(
getLocalizations(client, `c_${filename}_opt1_desc`)) getLocalizations(client, `c_${filename}_opt1_desc`)
)
)
); );
}, },
interaction: async (interaction: CommandInteraction, client: Client) => { interaction: async (interaction: CommandInteraction, client: Client) => {
const loc = getLocale(client, interaction.locale); const loc = getLocale(client, interaction.locale);
const desired_cat = interaction.options.get(client const desired_cat = interaction.options.get(
.locales client.locales
.get(client.config.default_lang) .get(client.config.default_lang)
?.get(`c_${getFilename(__filename)}_opt1_name`) ?? '')?.value as string; ?.get(`c_${getFilename(__filename)}_opt1_name`) ?? ""
)?.value as string;
// If a category isn't specified // If a category isn't specified
if (!desired_cat) { if (!desired_cat) {
// Sends a list of commands sorted into categories // Sends a list of commands sorted into categories
return interaction.reply({ return interaction.reply({
embeds: [ embeds: [
new EmbedBuilder() new EmbedBuilder()
.setColor(Colors.Blurple) .setColor(Colors.Blurple)
.setTitle(loc.get('c_prep1')) .setTitle(loc.get("c_prep1"))
.setDescription(loc.get('c_prep2')), .setDescription(loc.get("c_prep2")),
], ],
}); });
} }
// If a category is specified // If a category is specified
const allowedCategories = ['L1', 'L2', 'L3', 'M1', 'M2']; const allowedCategories = ["L1", "L2", "L3", "M1", "M2"];
const channel = allowedCategories.includes(desired_cat); const channel = allowedCategories.includes(desired_cat);
if (!channel) { if (!channel) {
// Category doesn't exist or is not allowed // Category doesn't exist or is not allowed
return interaction.reply({ return interaction.reply({
content: `${loc.get('c_prep3')} \`${desired_cat}\``, content: `${loc.get("c_prep3")} \`${desired_cat}\``,
ephemeral: true, ephemeral: true,
}); });
} }
// Send information about the command // Send information about the command
const allChannel = interaction.guild?.channels.fetch(); const allChannel = interaction.guild?.channels.fetch();
allChannel?.then(channel_guild => { 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 = channel_guild
const cat_to_prep_id = cat_to_prep.map(cat => cat?.id); .filter((chan) => chan?.type == ChannelType.GuildCategory)
const cat_to_prep_name = cat_to_prep.map(cat => cat?.name); .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); // 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 = channel_guild
const all_channel_desired_name = all_channel_desired.map(c_d => c_d?.name); .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'; const general = "général";
if (all_channel_desired_name.filter(cdn => cdn == general).length == 0) { if (
all_channel_desired_name.filter((cdn) => cdn == general).length == 0
) {
interaction.guild?.channels.create({ interaction.guild?.channels.create({
name: general, name: general,
type: 0, type: 0,
parent: cat_to_prep_id[0], parent: cat_to_prep_id[0],
}); });
desc = general + loc.get('c_prep5') + '\n'; desc = general + loc.get("c_prep5") + "\n";
} }
const info = 'informations'; const info = "informations";
if (all_channel_desired_name.filter(cdn => cdn == info).length == 0) { if (all_channel_desired_name.filter((cdn) => cdn == info).length == 0) {
interaction.guild?.channels.create({ interaction.guild?.channels.create({
name: info, name: info,
type: 0, type: 0,
parent: cat_to_prep_id[0], parent: cat_to_prep_id[0],
}); });
desc += '`' + info + '` ' + loc.get('c_prep5') + '\n'; desc += "`" + info + "` " + loc.get("c_prep5") + "\n";
} }
if (desc == '') { if (desc == "") {
desc = loc.get('c_prep6'); desc = loc.get("c_prep6");
} }
return interaction.reply({ return interaction.reply({
embeds: [ embeds: [
new EmbedBuilder() new EmbedBuilder()
.setColor(Colors.Blurple) .setColor(Colors.Blurple)
.setTitle(loc.get('c_prep4') + cat_to_prep_name) .setTitle(loc.get("c_prep4") + cat_to_prep_name)
.setDescription( .setDescription(desc),
desc,
),
], ],
}); });
}); });

View file

@ -1,10 +1,28 @@
import { ModalActionRowComponentBuilder, SlashCommandBuilder } from '@discordjs/builders'; import {
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChatInputCommandInteraction, Client, ModalBuilder, TextInputBuilder, TextInputStyle } from 'discord.js'; ModalActionRowComponentBuilder,
import { v4 as uuidv4 } from 'uuid'; SlashCommandBuilder,
import { collect } from '../../buttons/loader'; } from "@discordjs/builders";
import { getLocale, getLocalizations } from '../../utils/locales'; import {
import { getFilename } from '../../utils/misc'; ActionRowBuilder,
import { checkOwnershipReminder, deleteReminder, embedListReminders, getReminderInfo, newReminder } from '../../utils/reminder'; 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 { export default {
scope: () => [], scope: () => [],
@ -16,150 +34,165 @@ export default {
return; return;
} }
return new SlashCommandBuilder() return (
new SlashCommandBuilder()
// Command // Command
.setName(filename.toLowerCase()) .setName(filename.toLowerCase())
.setDescription(loc_default.get(`c_${filename}_desc`) ?? '') .setDescription(loc_default.get(`c_${filename}_desc`) ?? "")
.setNameLocalizations( .setNameLocalizations(
getLocalizations(client, `c_${filename}_name`, true) getLocalizations(client, `c_${filename}_name`, true)
).setDescriptionLocalizations( )
.setDescriptionLocalizations(
getLocalizations(client, `c_${filename}_desc`) getLocalizations(client, `c_${filename}_desc`)
) )
// New reminder // New reminder
.addSubcommand(subcommand => subcommand .addSubcommand((subcommand) =>
subcommand
.setName( .setName(
loc_default.get(`c_${filename}_sub1_name`) loc_default.get(`c_${filename}_sub1_name`)?.toLowerCase() ?? ""
?.toLowerCase() ?? '' )
).setDescription( .setDescription(loc_default.get(`c_${filename}_sub1_desc`) ?? "")
loc_default.get(`c_${filename}_sub1_desc`) ?? '' .setNameLocalizations(
).setNameLocalizations(
getLocalizations(client, `c_${filename}_sub1_name`, true) getLocalizations(client, `c_${filename}_sub1_name`, true)
).setDescriptionLocalizations( )
.setDescriptionLocalizations(
getLocalizations(client, `c_${filename}_sub1_desc`) getLocalizations(client, `c_${filename}_sub1_desc`)
) )
// Specified Time // Specified Time
.addStringOption(option => option .addStringOption((option) =>
option
.setName( .setName(
loc_default.get(`c_${filename}_sub1_opt1_name`) loc_default
?.toLowerCase() ?? '' .get(`c_${filename}_sub1_opt1_name`)
).setDescription( ?.toLowerCase() ?? ""
loc_default.get(`c_${filename}_sub1_opt1_desc`) ?? ''
).setNameLocalizations(
getLocalizations(
client,
`c_${filename}_sub1_opt1_name`,
true
) )
).setDescriptionLocalizations( .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`) getLocalizations(client, `c_${filename}_sub1_opt1_desc`)
) )
) )
// Specified message (not required) // Specified message (not required)
.addStringOption(option => option .addStringOption((option) =>
option
.setName( .setName(
loc_default.get(`c_${filename}_sub1_opt2_name`) loc_default
?.toLowerCase() ?? '' .get(`c_${filename}_sub1_opt2_name`)
).setDescription( ?.toLowerCase() ?? ""
loc_default.get(`c_${filename}_sub1_opt2_desc`) ?? ''
).setNameLocalizations(
getLocalizations(
client,
`c_${filename}_sub1_opt2_name`,
true
) )
).setDescriptionLocalizations( .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`) getLocalizations(client, `c_${filename}_sub1_opt2_desc`)
) )
) )
) )
// List reminders // List reminders
.addSubcommand(subcommand => subcommand .addSubcommand((subcommand) =>
subcommand
.setName( .setName(
loc_default.get(`c_${filename}_sub2_name`) loc_default.get(`c_${filename}_sub2_name`)?.toLowerCase() ?? ""
?.toLowerCase() ?? '' )
).setDescription( .setDescription(loc_default.get(`c_${filename}_sub2_desc`) ?? "")
loc_default.get(`c_${filename}_sub2_desc`) ?? '' .setNameLocalizations(
).setNameLocalizations(
getLocalizations(client, `c_${filename}_sub2_name`, true) getLocalizations(client, `c_${filename}_sub2_name`, true)
).setDescriptionLocalizations( )
.setDescriptionLocalizations(
getLocalizations(client, `c_${filename}_sub2_desc`) getLocalizations(client, `c_${filename}_sub2_desc`)
) )
// User // User
.addUserOption(option => option .addUserOption((option) =>
option
.setName( .setName(
loc_default.get(`c_${filename}_sub2_opt1_name`) loc_default
?.toLowerCase() ?? '' .get(`c_${filename}_sub2_opt1_name`)
).setDescription( ?.toLowerCase() ?? ""
loc_default.get(`c_${filename}_sub2_opt1_desc`) ?? ''
).setNameLocalizations(
getLocalizations(
client,
`c_${filename}_sub2_opt1_name`,
true
) )
).setDescriptionLocalizations( .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`) getLocalizations(client, `c_${filename}_sub2_opt1_desc`)
) )
) )
// Page // Page
.addIntegerOption(option => option .addIntegerOption((option) =>
option
.setName( .setName(
loc_default.get(`c_${filename}_sub2_opt2_name`) loc_default
?.toLowerCase() ?? '' .get(`c_${filename}_sub2_opt2_name`)
).setDescription( ?.toLowerCase() ?? ""
loc_default.get(`c_${filename}_sub2_opt2_desc`) ?? ''
).setNameLocalizations(
getLocalizations(
client,
`c_${filename}_sub2_opt2_name`,
true
) )
).setDescriptionLocalizations( .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`) getLocalizations(client, `c_${filename}_sub2_opt2_desc`)
) )
) )
) )
// Delete a reminder // Delete a reminder
.addSubcommand(subcommand => subcommand .addSubcommand((subcommand) =>
subcommand
.setName( .setName(
loc_default.get(`c_${filename}_sub3_name`) loc_default.get(`c_${filename}_sub3_name`)?.toLowerCase() ?? ""
?.toLowerCase() ?? '' )
).setDescription( .setDescription(loc_default.get(`c_${filename}_sub3_desc`) ?? "")
loc_default.get(`c_${filename}_sub3_desc`) ?? '' .setNameLocalizations(
).setNameLocalizations(
getLocalizations(client, `c_${filename}_sub3_name`, true) getLocalizations(client, `c_${filename}_sub3_name`, true)
).setDescriptionLocalizations( )
.setDescriptionLocalizations(
getLocalizations(client, `c_${filename}_sub3_desc`) getLocalizations(client, `c_${filename}_sub3_desc`)
) )
// ID // ID
.addIntegerOption(option => option .addIntegerOption((option) =>
option
.setName( .setName(
loc_default.get(`c_${filename}_sub3_opt1_name`) loc_default
?.toLowerCase() ?? '' .get(`c_${filename}_sub3_opt1_name`)
).setDescription( ?.toLowerCase() ?? ""
loc_default.get(`c_${filename}_sub3_opt1_desc`) ?? ''
).setNameLocalizations(
getLocalizations(
client,
`c_${filename}_sub3_opt1_name`,
true
) )
).setDescriptionLocalizations( .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`) getLocalizations(client, `c_${filename}_sub3_opt1_desc`)
).setRequired(true) )
), .setRequired(true)
)
)
); );
}, },
interaction: async (interaction: ChatInputCommandInteraction, client: Client) => { interaction: async (
interaction: ChatInputCommandInteraction,
client: Client
) => {
const loc_default = client.locales.get(client.config.default_lang); const loc_default = client.locales.get(client.config.default_lang);
const filename = getFilename(__filename); const filename = getFilename(__filename);
const loc = getLocale(client, interaction.locale); const loc = getLocale(client, interaction.locale);
@ -167,96 +200,133 @@ export default {
const subcommand = interaction.options.getSubcommand(); const subcommand = interaction.options.getSubcommand();
switch (subcommand) { switch (subcommand) {
// New reminder // New reminder
case loc_default?.get(`c_${filename}_sub1_name`) case loc_default?.get(`c_${filename}_sub1_name`)?.toLowerCase() ?? "": {
?.toLowerCase() ?? '': {
// If time is already renseigned // If time is already renseigned
const time = interaction.options.getString(loc_default?.get(`c_${filename}_sub1_opt1_name`) as string); const time = interaction.options.getString(
loc_default?.get(`c_${filename}_sub1_opt1_name`) as string
);
if (time != null) { if (time != null) {
// Use the cli because we already have enough data // Use the cli because we already have enough data
return newReminder(client, time, { return newReminder(client, time, {
locale: interaction.locale, locale: interaction.locale,
message: interaction.options.getString(loc_default?.get(`c_${filename}_sub1_opt2_name`) as string), message: interaction.options.getString(
loc_default?.get(`c_${filename}_sub1_opt2_name`) as string
),
createdAt: interaction.createdAt.getTime(), createdAt: interaction.createdAt.getTime(),
channelId: interaction.channelId, channelId: interaction.channelId,
userId: interaction.user.id, userId: interaction.user.id,
guildId: interaction.guildId, guildId: interaction.guildId,
}).then((msg) => interaction.reply({ }).then((msg) =>
interaction.reply({
content: msg as string, content: msg as string,
ephemeral: true, ephemeral: true,
})); })
);
} else { } else {
// Show modal to user to get at least the time // Show modal to user to get at least the time
const modal = new ModalBuilder() const modal = new ModalBuilder()
.setCustomId('reminderGUI') .setCustomId("reminderGUI")
.setTitle(loc.get(`c_${filename}_name`).capitalize()); .setTitle(loc.get(`c_${filename}_name`).capitalize());
const timeGUI = new TextInputBuilder() const timeGUI = new TextInputBuilder()
.setCustomId('reminderGUI-time') .setCustomId("reminderGUI-time")
.setLabel(loc.get(`c_${filename}_sub1_opt1_name`).capitalize()) .setLabel(loc.get(`c_${filename}_sub1_opt1_name`).capitalize())
.setStyle(TextInputStyle.Short) .setStyle(TextInputStyle.Short)
.setPlaceholder('1h') .setPlaceholder("1h")
.setRequired(true); .setRequired(true);
const messageGUI = new TextInputBuilder() const messageGUI = new TextInputBuilder()
.setCustomId('reminderGUI-message') .setCustomId("reminderGUI-message")
.setLabel(loc.get(`c_${filename}_sub1_opt2_name`).capitalize()) .setLabel(loc.get(`c_${filename}_sub1_opt2_name`).capitalize())
.setStyle(TextInputStyle.Paragraph) .setStyle(TextInputStyle.Paragraph)
.setPlaceholder(loc.get(`c_${filename}_sub1_opt2_desc`)) .setPlaceholder(loc.get(`c_${filename}_sub1_opt2_desc`))
.setRequired(false); .setRequired(false);
modal.addComponents( modal.addComponents(
new ActionRowBuilder<ModalActionRowComponentBuilder>().addComponents(timeGUI), new ActionRowBuilder<ModalActionRowComponentBuilder>().addComponents(
new ActionRowBuilder<ModalActionRowComponentBuilder>().addComponents(messageGUI) timeGUI
),
new ActionRowBuilder<ModalActionRowComponentBuilder>().addComponents(
messageGUI
)
); );
return interaction.showModal(modal); return interaction.showModal(modal);
} }
} }
// List reminders // List reminders
case loc_default?.get(`c_${filename}_sub2_name`) case loc_default?.get(`c_${filename}_sub2_name`)?.toLowerCase() ?? "": {
?.toLowerCase() ?? '': {
// Which user to show // Which user to show
let user = interaction.options.getUser(loc_default?.get(`c_${filename}_sub2_opt1_name`) as string); let user = interaction.options.getUser(
loc_default?.get(`c_${filename}_sub2_opt1_name`) as string
);
if (user == null) { if (user == null) {
user = interaction.user; user = interaction.user;
} }
const page = interaction.options.getInteger(loc_default?.get(`c_${filename}_sub2_opt2_name`) as string) ?? 1; const page =
const list = await embedListReminders(client, user, interaction.guildId, page, interaction.locale); 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 idPrec = "reminderList-prec_" + uuidv4();
const idNext = 'reminderList-next_' + uuidv4(); const idNext = "reminderList-next_" + uuidv4();
const row = new ActionRowBuilder<ButtonBuilder>() const row = new ActionRowBuilder<ButtonBuilder>()
.addComponents( .addComponents(
new ButtonBuilder() new ButtonBuilder()
.setCustomId(idPrec) .setCustomId(idPrec)
.setLabel(loc.get(`c_${filename}12`)) .setLabel(loc.get(`c_${filename}12`))
.setStyle(ButtonStyle.Primary)) .setStyle(ButtonStyle.Primary)
)
.addComponents( .addComponents(
new ButtonBuilder() new ButtonBuilder()
.setCustomId(idNext) .setCustomId(idNext)
.setLabel(loc.get(`c_${filename}13`)) .setLabel(loc.get(`c_${filename}13`))
.setStyle(ButtonStyle.Primary), .setStyle(ButtonStyle.Primary)
); );
// Buttons interactions // Buttons interactions
collect(client, interaction, idPrec); collect(client, interaction, idPrec);
collect(client, interaction, idNext); collect(client, interaction, idNext);
return await interaction.reply({ ephemeral: true, embeds: [list], components: [row] }); return await interaction.reply({
ephemeral: true,
embeds: [list],
components: [row],
});
} }
// Delete a reminder // Delete a reminder
case loc_default?.get(`c_${filename}_sub3_name`) case loc_default?.get(`c_${filename}_sub3_name`)?.toLowerCase() ?? "": {
?.toLowerCase() ?? '': { const id = interaction.options.getInteger(
const id = interaction.options.getInteger(loc_default?.get(`c_${filename}_sub3_opt1_name`) as string); loc_default?.get(`c_${filename}_sub3_opt1_name`) as string
);
if (id === null) { if (id === null) {
return interaction.reply({ content: loc.get(`c_${filename}2`), ephemeral: true }); return interaction.reply({
content: loc.get(`c_${filename}2`),
ephemeral: true,
});
} }
// Check if the ID exists and belongs to the user // Check if the ID exists and belongs to the user
if (await checkOwnershipReminder(client, id, interaction.user.id, interaction.guildId ?? '0')) { if (
return interaction.reply({ content: loc.get(`c_${filename}3`), ephemeral: true }); await checkOwnershipReminder(
client,
id,
interaction.user.id,
interaction.guildId ?? "0"
)
) {
return interaction.reply({
content: loc.get(`c_${filename}3`),
ephemeral: true,
});
} }
// Stop timeout // Stop timeout
@ -264,9 +334,16 @@ export default {
clearTimeout(reminderInfo.timeout_id); clearTimeout(reminderInfo.timeout_id);
// Delete from database // Delete from database
return deleteReminder(client, reminderInfo.creation_date, reminderInfo.user_id) return deleteReminder(
.then(() => interaction.reply({ content: `Reminder **#${id}** supprimé !`, ephemeral: true })); client,
reminderInfo.creation_date,
reminderInfo.user_id
).then(() =>
interaction.reply({
content: `Reminder **#${id}** supprimé !`,
ephemeral: true,
})
);
} }
default: { default: {
console.error(`${__filename}: unknown subcommand (${subcommand})`); console.error(`${__filename}: unknown subcommand (${subcommand})`);

View file

@ -1,17 +1,25 @@
import { Client } from 'discord.js'; import { Client } from "discord.js";
import { logStart } from '../../utils/misc'; import { logStart } from "../../utils/misc";
import { dbReminder, deleteReminder, infoReminder, OptionReminder, sendReminder, setTimeoutReminder, updateReminder } from '../../utils/reminder'; import {
dbReminder,
deleteReminder,
infoReminder,
OptionReminder,
sendReminder,
setTimeoutReminder,
updateReminder,
} from "../../utils/reminder";
export const once = true; export const once = true;
/** https://discord.js.org/#/docs/discord.js/main/class/Client?scrollTo=e-ready */ /** https://discord.js.org/#/docs/discord.js/main/class/Client?scrollTo=e-ready */
export default async (client: Client) => { export default async (client: Client) => {
console.log(logStart('Connection', true)); console.log(logStart("Connection", true));
// Restart all the timeout about reminders here // Restart all the timeout about reminders here
new Promise((ok, ko) => { new Promise((ok, ko) => {
// Fetch all reminders // Fetch all reminders
client.db.all('SELECT * FROM reminder', [], (err, row) => { client.db.all("SELECT * FROM reminder", [], (err, row) => {
if (err) { if (err) {
ko(err); ko(err);
} }
@ -19,7 +27,8 @@ export default async (client: Client) => {
// Send all the current reminders // Send all the current reminders
ok(row); ok(row);
}); });
}).then((data) => { })
.then((data) => {
const now = Date.now(); const now = Date.now();
(data as dbReminder[]).forEach((element) => { (data as dbReminder[]).forEach((element) => {
const info = { const info = {
@ -33,7 +42,11 @@ export default async (client: Client) => {
if (element.expiration_date <= now) { if (element.expiration_date <= now) {
// Reminder expired // Reminder expired
deleteReminder(client, element.creation_date, `${element.user_id}`).then((res) => { deleteReminder(
client,
element.creation_date,
`${element.user_id}`
).then((res) => {
if (res != true) { if (res != true) {
throw res; throw res;
} }
@ -42,7 +55,12 @@ export default async (client: Client) => {
sendReminder(client, info, element.option_id as OptionReminder); sendReminder(client, info, element.option_id as OptionReminder);
} else { } else {
// Restart timeout // Restart timeout
const timeoutId = setTimeoutReminder(client, info, element.option_id, (element.expiration_date - now) / 1000); const timeoutId = setTimeoutReminder(
client,
info,
element.option_id,
(element.expiration_date - now) / 1000
);
// Update timeout in database // Update timeout in database
element.timeout_id = String(timeoutId); element.timeout_id = String(timeoutId);
@ -53,7 +71,8 @@ export default async (client: Client) => {
}); });
} }
}); });
}).catch(err => { })
.catch((err) => {
throw err; throw err;
}); });
}; };

View file

@ -1,5 +1,5 @@
import { Client, Interaction, InteractionType } from 'discord.js'; import { Client, Interaction, InteractionType } from "discord.js";
import { getLocale } from '../../utils/locales'; import { getLocale } from "../../utils/locales";
/** https://discord.js.org/#/docs/discord.js/main/class/Client?scrollTo=e-interactionCreate */ /** https://discord.js.org/#/docs/discord.js/main/class/Client?scrollTo=e-interactionCreate */
export default (interaction: Interaction, client: Client) => { export default (interaction: Interaction, client: Client) => {
@ -9,7 +9,7 @@ export default (interaction: Interaction, client: Client) => {
const command = client.commands.list.get(interaction.commandName); const command = client.commands.list.get(interaction.commandName);
if (!command) { if (!command) {
return interaction.reply({ return interaction.reply({
content: loc.get('e_interacreate_no_command'), content: loc.get("e_interacreate_no_command"),
ephemeral: true, ephemeral: true,
}); });
} }
@ -21,7 +21,7 @@ export default (interaction: Interaction, client: Client) => {
const modal = client.modals.list.get(interaction.customId); const modal = client.modals.list.get(interaction.customId);
if (!modal) { if (!modal) {
return interaction.reply({ return interaction.reply({
content: loc.get('e_interacreate_no_modal'), content: loc.get("e_interacreate_no_modal"),
ephemeral: true, ephemeral: true,
}); });
} }

View file

@ -1,30 +1,31 @@
import { Client } from 'discord.js'; import { Client } from "discord.js";
import { readdir } from 'fs/promises'; import { readdir } from "fs/promises";
/** Load all the events. */ /** Load all the events. */
export default async (client: Client) => { export default async (client: Client) => {
const events_categories = (await readdir(__dirname)) const events_categories = (await readdir(__dirname)).filter(
.filter(element => !element.endsWith('.js') && !element.endsWith('.ts')); (element) => !element.endsWith(".js") && !element.endsWith(".ts")
);
events_categories.forEach(async event_category => { events_categories.forEach(async (event_category) => {
// Retrieve events // Retrieve events
const events = await readdir(`${__dirname}/${event_category}`); const events = await readdir(`${__dirname}/${event_category}`);
// Load them into the client // Load them into the client
Promise.all( Promise.all(
events.map(async event_file => { events.map(async (event_file) => {
const { once, default: execute } = await import( const { once, default: execute } = await import(
`../events/${event_category}/${event_file}` `../events/${event_category}/${event_file}`
); );
// Remove extension // Remove extension
// TODO: use utils functions // TODO: use utils functions
const event_type_ext = event_file.split('.'); const event_type_ext = event_file.split(".");
const ext = event_type_ext.pop(); const ext = event_type_ext.pop();
if (!(ext === 'js' || ext === 'ts')) { if (!(ext === "js" || ext === "ts")) {
throw `Unknown file in ${event_category}: ${event_file}`; throw `Unknown file in ${event_category}: ${event_file}`;
} }
const event_type = event_type_ext.join('.'); const event_type = event_type_ext.join(".");
if (once) { if (once) {
return client.once(event_type, (...args) => { return client.once(event_type, (...args) => {
@ -34,7 +35,7 @@ export default async (client: Client) => {
return client.on(event_type, (...args) => { return client.on(event_type, (...args) => {
execute(...args, client); execute(...args, client);
}); });
}), })
); );
}); });
}; };

View file

@ -1,7 +1,13 @@
import { Client, GuildMember, Message, TextBasedChannel, EmbedBuilder } from 'discord.js'; import {
import { getLocale } from '../../utils/locales'; Client,
import { isImage, userWithNickname } from '../../utils/misc'; GuildMember,
import { showDate } from '../../utils/time'; 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 */ /** https://discord.js.org/#/docs/discord.js/main/class/Client?scrollTo=e-messageCreate */
export default async (message: Message, client: Client) => { export default async (message: Message, client: Client) => {
@ -22,8 +28,9 @@ export default async (message: Message, client: Client) => {
} }
/* Citation */ /* Citation */
const regex = 'https://(?:canary\\.|ptb\\.)?discord(?:app)?\\.com/channels/(\\d{17,19})/(\\d{17,19})/(\\d{17,19})'; const regex =
const urls = message.content.match(new RegExp(regex, 'g')); "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 // Ignore message if there is no URLs
if (!urls) { if (!urls) {
@ -32,24 +39,27 @@ export default async (message: Message, client: Client) => {
const messages = ( const messages = (
await Promise.all( await Promise.all(
urls.reduce( urls
(data: { .reduce(
(
data: {
message_id: string; message_id: string;
channel: TextBasedChannel; channel: TextBasedChannel;
}[] = [], match) => { }[] = [],
const [, match
guild_id, ) => {
channel_id, const [, guild_id, channel_id, message_id] = new RegExp(regex).exec(
message_id, match
] = new RegExp(regex).exec(match) as RegExpExecArray; ) as RegExpExecArray;
// If message posted in another guild // If message posted in another guild
if (guild_id !== message.guild?.id) { if (guild_id !== message.guild?.id) {
return data; return data;
} }
const channel = const channel = message.guild.channels.cache.get(
message.guild.channels.cache.get(channel_id) as TextBasedChannel; channel_id
) as TextBasedChannel;
// If channel doesn't exist in the guild and isn't text // If channel doesn't exist in the guild and isn't text
if (!channel) { if (!channel) {
@ -59,16 +69,18 @@ export default async (message: Message, client: Client) => {
data.push({ message_id, channel }); data.push({ message_id, channel });
return data; return data;
}, [] },
).map(async ({ message_id, channel }) => { []
)
.map(async ({ message_id, channel }) => {
const quoted_message = await channel.messages const quoted_message = await channel.messages
.fetch(message_id) .fetch(message_id)
.catch(() => undefined); .catch(() => undefined);
// If message doesn't exist or empty // If message doesn't exist or empty
if (!quoted_message || ( if (
!quoted_message.content && !quoted_message ||
quoted_message.attachments.size == 0) (!quoted_message.content && quoted_message.attachments.size == 0)
) { ) {
return; return;
} }
@ -76,37 +88,37 @@ export default async (message: Message, client: Client) => {
return quoted_message; return quoted_message;
}) })
) )
)
// Remove undefined elements // Remove undefined elements
).filter(Boolean); .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 // Remove duplicates then map the quoted posts
[...new Set(messages)].map(quoted_post => { [...new Set(messages)].map((quoted_post) => {
const embed = new EmbedBuilder() const embed = new EmbedBuilder().setColor("#2f3136").setAuthor({
.setColor('#2f3136') name: "Citation",
.setAuthor({
name: 'Citation',
iconURL: quoted_post?.author.displayAvatarURL(), iconURL: quoted_post?.author.displayAvatarURL(),
}); });
// Handle attachments // Handle attachments
if (quoted_post?.attachments.size !== 0) { if (quoted_post?.attachments.size !== 0) {
if (quoted_post?.attachments.size === 1 && isImage( if (
quoted_post.attachments.first()?.name as string quoted_post?.attachments.size === 1 &&
)) { isImage(quoted_post.attachments.first()?.name as string)
) {
// Only contains one image // Only contains one image
embed.setImage(quoted_post.attachments.first()?.url as string); embed.setImage(quoted_post.attachments.first()?.url as string);
} else { } else {
// Contains more than one image and/or other files // Contains more than one image and/or other files
let files = ''; let files = "";
quoted_post?.attachments.forEach(file => files += quoted_post?.attachments.forEach(
`[${file.name}](${file.url}), ` (file) => (files += `[${file.name}](${file.url}), `)
); );
embed.addFields({ embed.addFields({
// TODO: Don't pluralize when there is only one file. // TODO: Don't pluralize when there is only one file.
// TODO: Locales // TODO: Locales
name: 'Fichiers joints', name: "Fichiers joints",
// TODO: Check if don't exceed char limit, if yes, split // TODO: Check if don't exceed char limit, if yes, split
// files into multiples field. // files into multiples field.
value: `${files.slice(0, -2)}.`, value: `${files.slice(0, -2)}.`,
@ -115,7 +127,7 @@ export default async (message: Message, client: Client) => {
} }
// Description as post content // Description as post content
embed.setDescription(quoted_post?.content ?? ''); embed.setDescription(quoted_post?.content ?? "");
// Footer // Footer
let footer = `Posté le ${showDate( let footer = `Posté le ${showDate(
@ -131,17 +143,13 @@ export default async (message: Message, client: Client) => {
)}`; )}`;
} }
let author = 'Auteur'; let author = "Auteur";
if (message.author == quoted_post?.author) { if (message.author == quoted_post?.author) {
author += ' & Citateur'; author += " & Citateur";
} else { } else {
footer += `\nCité par ${userWithNickname( footer += `\nCité par ${
message.member as GuildMember userWithNickname(message.member as GuildMember) ?? "?"
) ?? '?'} le ${showDate( } le ${showDate(client.config.default_lang, loc, message.createdAt)}`;
client.config.default_lang,
loc,
message.createdAt
)}`;
} }
embed.setFooter({ embed.setFooter({
@ -157,7 +165,7 @@ export default async (message: Message, client: Client) => {
inline: true, inline: true,
}, },
{ {
name: 'Message', name: "Message",
value: `${quoted_post?.channel} - [Lien Message](${quoted_post?.url})`, value: `${quoted_post?.channel} - [Lien Message](${quoted_post?.url})`,
inline: true, inline: true,
} }
@ -165,7 +173,7 @@ export default async (message: Message, client: Client) => {
// Delete source message if no content when removing links // Delete source message if no content when removing links
if ( if (
!message.content.replace(new RegExp(regex, 'g'), '').trim() && !message.content.replace(new RegExp(regex, "g"), "").trim() &&
messages.length === urls.length && messages.length === urls.length &&
!message.mentions.repliedUser !message.mentions.repliedUser
) { ) {

View file

@ -1,28 +1,28 @@
import loadClient, { quit } from './utils/client'; import loadClient, { quit } from "./utils/client";
import loadEvents from './events/loader'; import loadEvents from "./events/loader";
import loadModals from './modals/loader'; import loadModals from "./modals/loader";
import loadButtons from './buttons/loader'; import loadButtons from "./buttons/loader";
import loadCommands from './commands/loader'; import loadCommands from "./commands/loader";
import { logStart } from './utils/misc'; import { logStart } from "./utils/misc";
/** Run the bot. */ /** Run the bot. */
const run = async () => { const run = async () => {
console.log('Starting Botanique...'); console.log("Starting Botanique...");
// Load .env if not in prod // Load .env if not in prod
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== "production") {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
(await import('dotenv')).config({ path: './config/.env' }); (await import("dotenv")).config({ path: "./config/.env" });
} }
// Client Discord.JS // Client Discord.JS
const client_name = 'Client'; const client_name = "Client";
await loadClient() await loadClient()
.then(async client => { .then(async (client) => {
// Events Discord.JS // Events Discord.JS
const events_name = 'Events'; const events_name = "Events";
await loadEvents(client) await loadEvents(client)
.then(() => console.log(logStart(events_name, true))) .then(() => console.log(logStart(events_name, true)))
.catch((err) => { .catch((err) => {
@ -34,7 +34,7 @@ const run = async () => {
await client.login(client.config.token_discord); await client.login(client.config.token_discord);
// Modals Discord.JS // Modals Discord.JS
const modals_name = 'Modals'; const modals_name = "Modals";
await loadModals(client) await loadModals(client)
.then(() => console.log(logStart(modals_name, true))) .then(() => console.log(logStart(modals_name, true)))
.catch((err) => { .catch((err) => {
@ -43,7 +43,7 @@ const run = async () => {
}); });
// Buttons Discord.JS // Buttons Discord.JS
const buttons_name = 'Buttons'; const buttons_name = "Buttons";
await loadButtons(client) await loadButtons(client)
.then(() => console.log(logStart(buttons_name, true))) .then(() => console.log(logStart(buttons_name, true)))
.catch((err) => { .catch((err) => {
@ -52,7 +52,7 @@ const run = async () => {
}); });
// Commands Slash Discord.JS // Commands Slash Discord.JS
const commands_name = 'Commands'; const commands_name = "Commands";
await loadCommands(client) await loadCommands(client)
.then(() => console.log(logStart(commands_name, true))) .then(() => console.log(logStart(commands_name, true)))
.catch((err) => { .catch((err) => {
@ -61,13 +61,15 @@ const run = async () => {
}); });
console.log(logStart(client_name, true)); console.log(logStart(client_name, true));
console.log(`Botanique "${client.user?.username}" v${client.config.version} started!`); console.log(
`Botanique "${client.user?.username}" v${client.config.version} started!`
);
// ^C // ^C
process.on('SIGINT', () => quit(client)); process.on("SIGINT", () => quit(client));
// Container force closed // Container force closed
process.on('SIGTERM', () => quit(client)); process.on("SIGTERM", () => quit(client));
}) })
.catch((err) => { .catch((err) => {
console.error(err); console.error(err);
@ -75,4 +77,4 @@ const run = async () => {
}); });
}; };
run().catch(error => console.error(error)); run().catch((error) => console.error(error));

View file

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

View file

@ -1,21 +1,28 @@
import { Client, ModalSubmitInteraction } from 'discord.js'; import { Client, ModalSubmitInteraction } from "discord.js";
import { getFilename } from '../../utils/misc'; import { getFilename } from "../../utils/misc";
import { newReminder } from '../../utils/reminder'; import { newReminder } from "../../utils/reminder";
export default { export default {
data: { data: {
name: getFilename(__filename), name: getFilename(__filename),
}, },
interaction: async (interaction: ModalSubmitInteraction, client: Client) => interaction: async (interaction: ModalSubmitInteraction, client: Client) =>
newReminder(client, interaction.fields.fields.get('reminderGUI-time')?.value as string, { newReminder(
client,
interaction.fields.fields.get("reminderGUI-time")?.value as string,
{
locale: interaction.locale, locale: interaction.locale,
message: interaction.fields.fields.get('reminderGUI-message')?.value ?? null, message:
interaction.fields.fields.get("reminderGUI-message")?.value ?? null,
createdAt: interaction.createdAt.getTime(), createdAt: interaction.createdAt.getTime(),
channelId: interaction.channelId, channelId: interaction.channelId,
userId: interaction.user.id, userId: interaction.user.id,
guildId: interaction.guildId, guildId: interaction.guildId,
}).then((msg) => interaction.reply({ }
).then((msg) =>
interaction.reply({
content: msg as string, content: msg as string,
ephemeral: true, ephemeral: true,
})), })
),
}; };

View file

@ -1,21 +1,21 @@
import { Collection } from 'discord.js'; import { Collection } from "discord.js";
import { SlashCommandBuilder } from '@discordjs/builders'; import { SlashCommandBuilder } from "@discordjs/builders";
import { Database } from 'sqlite3'; import { Database } from "sqlite3";
export {}; export {};
declare module 'discord.js' { declare module "discord.js" {
// eslint-disable-next-line no-shadow // eslint-disable-next-line no-shadow
export interface Client { export interface Client {
/** Store the configuration */ /** Store the configuration */
config: { config: {
/** Bot version */ /** Bot version */
version: string, version: string;
/** Bot token from env variable */ /** Bot token from env variable */
token_discord: string | undefined, token_discord: string | undefined;
/** Default lang used */ /** Default lang used */
default_lang: string default_lang: string;
}, };
/** Store all the modals */ /** Store all the modals */
modals: { modals: {
categories: Collection< categories: Collection<
@ -23,7 +23,7 @@ declare module 'discord.js' {
string, string,
/** Name of the modals in the category */ /** Name of the modals in the category */
string[] string[]
>, >;
list: Collection< list: Collection<
/** Modal name */ /** Modal name */
string, string,
@ -31,13 +31,16 @@ declare module 'discord.js' {
{ {
/** Data about the modal */ /** Data about the modal */
data: { data: {
name: string name: string;
}, };
/** How the modal interact */ /** How the modal interact */
interaction: (interaction: ModalSubmitInteraction, client: Client) => unknown interaction: (
interaction: ModalSubmitInteraction,
client: Client
) => unknown;
} }
>, >;
}, };
/** Store all the buttons */ /** Store all the buttons */
buttons: { buttons: {
categories: Collection< categories: Collection<
@ -45,7 +48,7 @@ declare module 'discord.js' {
string, string,
/** Name of the buttons in the category */ /** Name of the buttons in the category */
string[] string[]
>, >;
list: Collection< list: Collection<
/** Button name */ /** Button name */
string, string,
@ -53,13 +56,16 @@ declare module 'discord.js' {
{ {
/** Data about the button */ /** Data about the button */
data: { data: {
name: string name: string;
}, };
/** How the button interact */ /** How the button interact */
interaction: (interaction: MessageComponentInteraction, client: Client) => Promise<InteractionUpdateOptions> interaction: (
interaction: MessageComponentInteraction,
client: Client
) => Promise<InteractionUpdateOptions>;
} }
>, >;
}, };
/** Store all the slash commands */ /** Store all the slash commands */
commands: { commands: {
categories: Collection< categories: Collection<
@ -67,21 +73,24 @@ declare module 'discord.js' {
string, string,
/** Name of the commands in the category */ /** Name of the commands in the category */
string[] string[]
>, >;
list: Collection< list: Collection<
/** Command name */ /** Command name */
string, string,
/** Command itself */ /** Command itself */
{ {
/** Data about the command */ /** Data about the command */
data: SlashCommandBuilder, data: SlashCommandBuilder;
/** How the command interact */ /** How the command interact */
interaction: (interaction: CommandInteraction, client: Client) => unknown interaction: (
interaction: CommandInteraction,
client: Client
) => unknown;
} }
>, >;
}, };
/** Store all the localizations */ /** Store all the localizations */
locales: Map<string, Map<string, string>>, locales: Map<string, Map<string, string>>;
db: Database, db: Database;
} }
} }

View file

@ -6,7 +6,7 @@ declare global {
/** /**
* Returns a copy of the string with the first letter capitalized. * Returns a copy of the string with the first letter capitalized.
*/ */
capitalize(): string, capitalize(): string;
} }
} }

View file

@ -1,22 +1,19 @@
import { Client, Collection, GatewayIntentBits } from 'discord.js'; import { Client, Collection, GatewayIntentBits } from "discord.js";
import { readFileSync } from 'fs'; import { readFileSync } from "fs";
import { loadLocales } from './locales'; import { loadLocales } from "./locales";
import '../modules/client'; import "../modules/client";
import { Database } from 'sqlite3'; import { Database } from "sqlite3";
/** Creation of the client and definition of its properties. */ /** Creation of the client and definition of its properties. */
export default async () => { export default async () => {
const client: Client = new Client({ const client: Client = new Client({
intents: [ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages],
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
],
}); });
client.config = { client.config = {
version: JSON.parse(readFileSync('./package.json').toString()).version, version: JSON.parse(readFileSync("./package.json").toString()).version,
token_discord: process.env.TOKEN_DISCORD, token_discord: process.env.TOKEN_DISCORD,
default_lang: process.env.DEFAULT_LANG ?? 'fr', default_lang: process.env.DEFAULT_LANG ?? "fr",
}; };
client.modals = { client.modals = {
@ -34,10 +31,12 @@ export default async () => {
list: new Collection(), list: new Collection(),
}; };
console.log('Translations progression :'); console.log("Translations progression :");
client.locales = await loadLocales(client.config.default_lang); client.locales = await loadLocales(client.config.default_lang);
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);
@ -62,7 +61,8 @@ export const quit = (client: Client) => {
*/ */
const initDatabase = (db: Database) => { const initDatabase = (db: Database) => {
// Table for reminders // Table for reminders
db.run('CREATE TABLE IF NOT EXISTS reminder ( \ db.run(
"CREATE TABLE IF NOT EXISTS reminder ( \
id INTEGER PRIMARY KEY, \ id INTEGER PRIMARY KEY, \
data TEXT, \ data TEXT, \
expiration_date TEXT, \ expiration_date TEXT, \
@ -73,5 +73,6 @@ const initDatabase = (db: Database) => {
guild_id TEXT, \ guild_id TEXT, \
locale TEXT, \ locale TEXT, \
timeout_id TEXT \ timeout_id TEXT \
);'); );"
);
}; };

View file

@ -1,6 +1,6 @@
import { Client } from 'discord.js'; import { Client } from "discord.js";
import { readdir } from 'fs/promises'; import { readdir } from "fs/promises";
import { removeExtension } from './misc'; import { removeExtension } from "./misc";
/** /**
* Load the localizations files into memory. * Load the localizations files into memory.
@ -11,30 +11,29 @@ import { removeExtension } from './misc';
*/ */
export const loadLocales = async (default_lang: string) => { export const loadLocales = async (default_lang: string) => {
// Get files from locales/ directory // Get files from locales/ directory
const old_path = __dirname.split('/'); const old_path = __dirname.split("/");
old_path.pop(); old_path.pop();
const files = await readdir(`${old_path.join('/')}/locales`); const files = await readdir(`${old_path.join("/")}/locales`);
// Read JSON files content and load it into the memory // Read JSON files content and load it into the memory
const locales = new Map<string, Map<string, string>>(); const locales = new Map<string, Map<string, string>>();
await Promise.all( await Promise.all(
files.map(async lang => { files.map(async (lang) => {
// Import file // Import file
const content: { const content: {
[key: string]: string [key: string]: string;
} = await import( } = await import(`../locales/${lang}`);
`../locales/${lang}`
);
// Add it to the memory // Add it to the memory
locales.set( locales.set(
removeExtension(lang), removeExtension(lang),
new Map(Object.keys(content) new Map(
Object.keys(content)
// Ignore the default key // Ignore the default key
.filter(str => str !== 'default') .filter((str) => str !== "default")
.map(str => { .map((str) => {
return [str, content[str]]; return [str, content[str]];
}), })
) )
); );
}) })
@ -53,15 +52,20 @@ export const loadLocales = async (default_lang: string) => {
* @param text Name of string to fetch * @param text Name of string to fetch
* @returns the dictionary * @returns the dictionary
*/ */
export const getLocalizations = (client: Client, text: string, lowercase = false) => { export const getLocalizations = (
client: Client,
text: string,
lowercase = false
) => {
const data: Record<string, string> = {}; const data: Record<string, string> = {};
// Load all the localizations // Load all the localizations
client.locales.forEach((locale, lang) => { client.locales.forEach((locale, lang) => {
// Fetch the text and fallback to default lang if needed // Fetch the text and fallback to default lang if needed
// See getLocale for more info on why we *can* fallback // See getLocale for more info on why we *can* fallback
let str = locale.get(text) let str =
?? client.locales.get(client.config.default_lang)?.get(text); locale.get(text) ??
client.locales.get(client.config.default_lang)?.get(text);
// Store it if defined // Store it if defined
if (str !== undefined) { if (str !== undefined) {
@ -110,8 +114,10 @@ export const getLocale = (client: Client, lang: string) => {
* @param default_lang default lang * @param default_lang default lang
* @returns void * @returns void
*/ */
const checkLocales = const checkLocales = async (
async (locales: Map<string, Map<string, string>>, default_lang: string) => { locales: Map<string, Map<string, string>>,
default_lang: string
) => {
// Associate each lang with the number of locale it has // Associate each lang with the number of locale it has
let locales_size = new Map<string, number>(); let locales_size = new Map<string, number>();
locales.forEach((locales_data, lang) => { locales.forEach((locales_data, lang) => {
@ -119,8 +125,9 @@ async (locales: Map<string, Map<string, string>>, default_lang: string) => {
}); });
// Sort the map // Sort the map
locales_size = new Map([...locales_size.entries()] locales_size = new Map(
.sort((a, b) => b[1] - a[1])); [...locales_size.entries()].sort((a, b) => b[1] - a[1])
);
// Check if default lang is 100% // Check if default lang is 100%
const [max_size_name] = locales_size.keys(); const [max_size_name] = locales_size.keys();
@ -132,8 +139,8 @@ async (locales: Map<string, Map<string, string>>, default_lang: string) => {
// However, it is possible that this condition is true // However, it is possible that this condition is true
// and the security is poor, but it's better than nothing. // and the security is poor, but it's better than nothing.
throw new Error( throw new Error(
`The default locale (${default_lang} = ${default_lang_size}) isn't complete ` `The default locale (${default_lang} = ${default_lang_size}) isn't complete ` +
+ `(${max_size_name} = ${max_size}).` `(${max_size_name} = ${max_size}).`
); );
} }
@ -146,29 +153,33 @@ async (locales: Map<string, Map<string, string>>, default_lang: string) => {
locales_size.forEach((size, lang) => { locales_size.forEach((size, lang) => {
const percentage = (size / max_size) * 100; const percentage = (size / max_size) * 100;
// Colored bar part // Colored bar part
const blocks = ' '.repeat(Math.floor(percentage / bar_size)); const blocks = " ".repeat(Math.floor(percentage / bar_size));
// Blank bar part // Blank bar part
const blank = ' '.repeat(Math.ceil((100 - percentage) / bar_size)); const blank = " ".repeat(Math.ceil((100 - percentage) / bar_size));
const color = () => { const color = () => {
switch (true) { switch (true) {
case percentage <= 25: case percentage <= 25:
// Red // Red
return '\x1b[41m'; return "\x1b[41m";
case percentage <= 50: case percentage <= 50:
// Mangeta // Mangeta
return '\x1b[45m'; return "\x1b[45m";
case percentage <= 75: case percentage <= 75:
// Cyan // Cyan
return '\x1b[46m'; return "\x1b[46m";
case percentage <= 100: case percentage <= 100:
// Green // Green
return '\x1b[42m'; return "\x1b[42m";
default: default:
return ''; return "";
} }
}; };
const padding = ' '.repeat(lang.length === 5 ? 1 : 4); 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
)}%`
);
}); });
}; };

View file

@ -1,4 +1,4 @@
import { GuildMember } from 'discord.js'; import { GuildMember } from "discord.js";
/** /**
* Log module status. * Log module status.
@ -8,7 +8,7 @@ import { GuildMember } from 'discord.js';
*/ */
export const logStart = (name: string, status: boolean) => { export const logStart = (name: string, status: boolean) => {
// TODO Handle precision about the error if status is false // TODO Handle precision about the error if status is false
return `> ${name}\t${status === true ? '✅' : '❌'}`; return `> ${name}\t${status === true ? "✅" : "❌"}`;
}; };
/** /**
@ -17,7 +17,7 @@ export const logStart = (name: string, status: boolean) => {
* @returns string * @returns string
*/ */
export const getFilename = (path: string) => { export const getFilename = (path: string) => {
const path_list = path.split('/'); const path_list = path.split("/");
// Check if filename exist // Check if filename exist
const filename_with_ext = path_list.pop(); const filename_with_ext = path_list.pop();
@ -34,10 +34,10 @@ export const getFilename = (path: string) => {
* @returns string of the filename without an extension * @returns string of the filename without an extension
*/ */
export const removeExtension = (filename: string) => { export const removeExtension = (filename: string) => {
const array = filename.split('.'); const array = filename.split(".");
array.pop(); array.pop();
return array.join('.'); return array.join(".");
}; };
/** /**
@ -46,7 +46,7 @@ export const removeExtension = (filename: string) => {
* @returns string of the extension if it exists * @returns string of the extension if it exists
*/ */
export const getExtension = (filename: string) => { 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 * @returns true is file is a media
*/ */
export const isImage = (filename: string) => { export const isImage = (filename: string) => {
return Boolean(getExtension(filename)?.match( return Boolean(getExtension(filename)?.match(/jpg|jpeg|png|webp|gif/));
/jpg|jpeg|png|webp|gif/
));
}; };
/** /**
@ -92,12 +90,15 @@ export const cleanCodeBlock = (text: string) => {
}); });
// Keep links // Keep links
text = text.replace(/(http[s]?:\/\/(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*(),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)/g, function(url: string) { text = text.replace(
/(http[s]?:\/\/(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*(),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)/g,
function (url: string) {
return `\`${url}\``; return `\`${url}\``;
}); }
);
// Fix issues // Fix issues
text = text.replace('``', ''); text = text.replace("``", "");
return text; return text;
}; };

View file

@ -1,7 +1,7 @@
import { Client, Colors, EmbedBuilder, User } from 'discord.js'; import { Client, Colors, EmbedBuilder, User } from "discord.js";
import { getLocale } from './locales'; import { getLocale } from "./locales";
import { cleanCodeBlock } from './misc'; import { cleanCodeBlock } from "./misc";
import { showDate, strToSeconds, timeDeltaToString } from './time'; import { showDate, strToSeconds, timeDeltaToString } from "./time";
/** /**
* Option possible for reminders * Option possible for reminders
@ -19,26 +19,26 @@ export enum OptionReminder {
* Store data about the remidner * Store data about the remidner
*/ */
export type infoReminder = { export type infoReminder = {
locale: string, locale: string;
message: string | null, message: string | null;
createdAt: number, createdAt: number;
channelId: string | null, channelId: string | null;
userId: string, userId: string;
guildId: string | null guildId: string | null;
} };
export type dbReminder = { export type dbReminder = {
id: number, id: number;
data: string | null, data: string | null;
expiration_date: number, expiration_date: number;
option_id: OptionReminder, option_id: OptionReminder;
channel_id: string | null, channel_id: string | null;
creation_date: string, creation_date: string;
user_id: string, user_id: string;
guild_id: string | null, guild_id: string | null;
locale: string, locale: string;
timeout_id: string timeout_id: string;
} };
/** /**
* Split the time and the extra args `p` and `@` * Split the time and the extra args `p` and `@`
@ -46,9 +46,9 @@ export type dbReminder = {
* @returns An object with the time and the option * @returns An object with the time and the option
*/ */
const splitTime = (time: string) => { const splitTime = (time: string) => {
if (time?.endsWith('@')) { if (time?.endsWith("@")) {
return { time: time.slice(0, -1), option: OptionReminder.Mention }; return { time: time.slice(0, -1), option: OptionReminder.Mention };
} else if (time?.toLowerCase().endsWith('p')) { } else if (time?.toLowerCase().endsWith("p")) {
return { time: time.slice(0, -1), option: OptionReminder.DirectMessage }; return { time: time.slice(0, -1), option: OptionReminder.DirectMessage };
} }
@ -62,33 +62,42 @@ const splitTime = (time: string) => {
* @param info data about the context of the reminder * @param info data about the context of the reminder
* @returns Promise resolution of the sql request * @returns Promise resolution of the sql request
*/ */
export const newReminder = async (client: Client, time: string, info: infoReminder) => export const newReminder = async (
client: Client,
time: string,
info: infoReminder
) =>
new Promise((ok, ko) => { new Promise((ok, ko) => {
const data = splitTime(time); const data = splitTime(time);
const timeout = strToSeconds(data.time); const timeout = strToSeconds(data.time);
const timeoutId = setTimeoutReminder(client, info, data.option, timeout); const timeoutId = setTimeoutReminder(client, info, data.option, timeout);
// Add the remind to the db // Add the remind to the db
client.db.run('INSERT INTO reminder ( \ client.db.run(
"INSERT INTO reminder ( \
data, expiration_date, option_id, channel_id, creation_date, user_id, guild_id, locale, timeout_id \ data, expiration_date, option_id, channel_id, creation_date, user_id, guild_id, locale, timeout_id \
) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ? );', [ ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ? );",
[
info.message, info.message,
`${info.createdAt + (timeout * 1000)}`, `${info.createdAt + timeout * 1000}`,
data.option.valueOf(), data.option.valueOf(),
info.channelId, info.channelId,
`${info.createdAt}`, `${info.createdAt}`,
info.userId, info.userId,
info.guildId, info.guildId,
info.locale, info.locale,
timeoutId], (err) => { timeoutId,
],
(err) => {
if (err) { if (err) {
ko(err); ko(err);
} }
// Send confirmation to user // Send confirmation to user
const loc = getLocale(client, info.locale); const loc = getLocale(client, info.locale);
ok(`${loc.get('c_reminder1')} ${data.time}.`); ok(`${loc.get("c_reminder1")} ${data.time}.`);
}); }
);
}); });
/** /**
@ -98,33 +107,45 @@ export const newReminder = async (client: Client, time: string, info: infoRemind
* @param userId User ID who created the reminder * @param userId User ID who created the reminder
* @returns what the SQlite request sended * @returns what the SQlite request sended
*/ */
export const deleteReminder = (client: Client, createdAt: string, userId: string) => { export const deleteReminder = (
client: Client,
createdAt: string,
userId: string
) => {
// Delete the reminder for the database // Delete the reminder for the database
return new Promise((ok, ko) => { return new Promise((ok, ko) => {
// Add the remind to the db // Add the remind to the db
client.db.run('DELETE FROM reminder WHERE creation_date = ? AND user_id = ?', [createdAt, userId], (err) => { client.db.run(
"DELETE FROM reminder WHERE creation_date = ? AND user_id = ?",
[createdAt, userId],
(err) => {
if (err) { if (err) {
ko(err); ko(err);
} }
// Send confirmation to user // Send confirmation to user
ok(true); ok(true);
}); }
);
}); });
}; };
export const sendReminder = (client: Client, info: infoReminder, option: OptionReminder) => { export const sendReminder = (
client: Client,
info: infoReminder,
option: OptionReminder
) => {
const loc = getLocale(client, info.locale); const loc = getLocale(client, info.locale);
// Send the message in the appropriate channel // Send the message in the appropriate channel
// TODO: Embed // TODO: Embed
let message: string; let message: string;
if (info.message === null) { if (info.message === null) {
message = loc.get('c_reminder18'); message = loc.get("c_reminder18");
} else { } else {
message = cleanCodeBlock(info.message); message = cleanCodeBlock(info.message);
} }
const embed = new EmbedBuilder() const embed = new EmbedBuilder()
.setColor('Random') .setColor("Random")
.setDescription(message) .setDescription(message)
.setTimestamp(info.createdAt); .setTimestamp(info.createdAt);
@ -133,7 +154,7 @@ export const sendReminder = (client: Client, info: infoReminder, option: OptionR
if (client.channels.cache.get(info.channelId) !== undefined) { if (client.channels.cache.get(info.channelId) !== undefined) {
channelOk = true; channelOk = true;
} else { } else {
embed.setFooter({ text: loc.get('c_reminder14') }); embed.setFooter({ text: loc.get("c_reminder14") });
} }
} }
@ -144,10 +165,10 @@ export const sendReminder = (client: Client, info: infoReminder, option: OptionR
if (guild.members.cache.get(info.userId) !== undefined) { if (guild.members.cache.get(info.userId) !== undefined) {
guildOk = true; guildOk = true;
} else { } else {
embed.setFooter({ text: `${loc.get('c_reminder15')} ${guild.name}.` }); embed.setFooter({ text: `${loc.get("c_reminder15")} ${guild.name}.` });
} }
} else { } else {
embed.setFooter({ text: loc.get('c_reminder16') }); embed.setFooter({ text: loc.get("c_reminder16") });
} }
} }
@ -159,15 +180,19 @@ export const sendReminder = (client: Client, info: infoReminder, option: OptionR
} }
} else { } else {
// Channel // Channel
client.channels.fetch(info.channelId ?? '').then((channel) => { client.channels.fetch(info.channelId ?? "").then((channel) => {
if (channel?.isTextBased()) { if (channel?.isTextBased()) {
let content = `<@${info.userId}>`; let content = `<@${info.userId}>`;
embed.setFooter({ text: `${loc.get('c_reminder17')} ${timeDeltaToString(info.createdAt)}` }); embed.setFooter({
text: `${loc.get("c_reminder17")} ${timeDeltaToString(
info.createdAt
)}`,
});
// Mention everybody if needed // Mention everybody if needed
if (option == OptionReminder.Mention) { if (option == OptionReminder.Mention) {
(info.message?.match(/<@\d+>/g) ?? []).forEach(mention => { (info.message?.match(/<@\d+>/g) ?? []).forEach((mention) => {
content += ' ' + mention; content += " " + mention;
}); });
} }
@ -185,16 +210,25 @@ export const sendReminder = (client: Client, info: infoReminder, option: OptionR
* @param timeout Amout of time before the reminder ends * @param timeout Amout of time before the reminder ends
* @returns Timeout's ID * @returns Timeout's ID
*/ */
export const setTimeoutReminder = (client: Client, info: infoReminder, option: OptionReminder, timeout: number) => { export const setTimeoutReminder = (
return Number(setTimeout(() => { client: Client,
deleteReminder(client, String(info.createdAt), info.userId).then((val) => { info: infoReminder,
option: OptionReminder,
timeout: number
) => {
return Number(
setTimeout(() => {
deleteReminder(client, String(info.createdAt), info.userId).then(
(val) => {
if (val != true) { if (val != true) {
throw val; throw val;
} }
sendReminder(client, info, option); sendReminder(client, info, option);
}); }
}, timeout * 1000)); );
}, timeout * 1000)
);
}; };
/** /**
@ -204,25 +238,32 @@ export const setTimeoutReminder = (client: Client, info: infoReminder, option: O
* @param userId user ID to check * @param userId user ID to check
* @param guildId guild ID where the ownership request as been send, 0 if DM * @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) => { export const checkOwnershipReminder = async (
const data = await new Promise((ok, ko) => { client: Client,
id: number,
userId: string,
guildId: string
) => {
const data = (await new Promise((ok, ko) => {
// Check the ownership // Check the ownership
client.db.all('SELECT EXISTS ( \ client.db.all(
"SELECT EXISTS ( \
SELECT 1 FROM reminder \ SELECT 1 FROM reminder \
WHERE id = ? \ WHERE id = ? \
AND user_id = ? \ AND user_id = ? \
AND (guild_id = ? OR guild_id = 0) \ AND (guild_id = ? OR guild_id = 0) \
)', [ )",
id, userId, guildId, [id, userId, guildId],
], (err, row) => { (err, row) => {
if (err) { if (err) {
ko(err); ko(err);
} }
// Send all the current reminders // Send all the current reminders
ok(row[0]); ok(row[0]);
}); }
}) as { [key: string]: number }; );
})) as { [key: string]: number };
return Object.keys(data).map((key) => data[key])[0] === 0 ? true : false; 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 * @param id Reminder's ID
*/ */
export const getReminderInfo = async (client: Client, id: number) => { export const getReminderInfo = async (client: Client, id: number) => {
return await new Promise((ok, ko) => { return (await new Promise((ok, ko) => {
// Check the ownership // Check the ownership
client.db.all('SELECT * FROM reminder \ client.db.all(
WHERE id = ?', [ "SELECT * FROM reminder \
id], (err, row) => { WHERE id = ?",
[id],
(err, row) => {
if (err) { if (err) {
ko(err); ko(err);
} }
// Send all the current reminders // Send all the current reminders
ok(row[0]); ok(row[0]);
}); }
}) as dbReminder; );
})) as dbReminder;
}; };
/** /**
@ -256,7 +300,8 @@ export const updateReminder = (client: Client, data: dbReminder) => {
// Delete the reminder for the database // Delete the reminder for the database
return new Promise((ok, ko) => { return new Promise((ok, ko) => {
// Update the db // Update the db
client.db.run('UPDATE reminder \ client.db.run(
"UPDATE reminder \
SET data = ?, \ SET data = ?, \
expiration_date = ?, \ expiration_date = ?, \
option_id = ?, \ option_id = ?, \
@ -266,7 +311,8 @@ export const updateReminder = (client: Client, data: dbReminder) => {
guild_id = ?, \ guild_id = ?, \
locale = ?, \ locale = ?, \
timeout_id = ? \ timeout_id = ? \
WHERE ID = ?', [ WHERE ID = ?",
[
data.data, data.data,
data.expiration_date, data.expiration_date,
data.option_id, data.option_id,
@ -276,13 +322,16 @@ export const updateReminder = (client: Client, data: dbReminder) => {
data.guild_id, data.guild_id,
data.locale, data.locale,
data.timeout_id, data.timeout_id,
data.id], (err) => { data.id,
],
(err) => {
if (err) { if (err) {
ko(err); ko(err);
} }
ok(true); ok(true);
}); }
);
}); });
}; };
@ -293,20 +342,27 @@ export const updateReminder = (client: Client, data: dbReminder) => {
* @param guildId guild ID * @param guildId guild ID
* @returns List of reminders of a user in a guild * @returns List of reminders of a user in a guild
*/ */
const listReminders = async (client: Client, userId: string, guildId: string | null) => { const listReminders = async (
return await new Promise((ok, ko) => { client: Client,
userId: string,
guildId: string | null
) => {
return (await new Promise((ok, ko) => {
// Check the ownership // Check the ownership
client.db.all('SELECT data, creation_date, expiration_date, id FROM reminder \ client.db.all(
WHERE user_id = ? AND (guild_id = ? OR guild_id = 0)', [ "SELECT data, creation_date, expiration_date, id FROM reminder \
userId, guildId ?? 0], (err, row) => { WHERE user_id = ? AND (guild_id = ? OR guild_id = 0)",
[userId, guildId ?? 0],
(err, row) => {
if (err) { if (err) {
ko(err); ko(err);
} }
// Send all the current reminders // Send all the current reminders
ok(row); ok(row);
}); }
}) as dbReminder[]; );
})) as dbReminder[];
}; };
/** /**
@ -318,7 +374,13 @@ const listReminders = async (client: Client, userId: string, guildId: string | n
* @param local Lang * @param local Lang
* @returns Pretty embed who list reminders * @returns Pretty embed who list reminders
*/ */
export const embedListReminders = async (client: Client, user: User, guildId: string | null, page: number, local: string) => { export const embedListReminders = async (
client: Client,
user: User,
guildId: string | null,
page: number,
local: string
) => {
const loc = getLocale(client, local); const loc = getLocale(client, local);
const reminders = await listReminders(client, user.id, guildId); const reminders = await listReminders(client, user.id, guildId);
@ -331,7 +393,11 @@ export const embedListReminders = async (client: Client, user: User, guildId: st
// TODO: Use Random color or force a color from args // TODO: Use Random color or force a color from args
const embed = new EmbedBuilder() const embed = new EmbedBuilder()
.setColor(Colors.DarkGrey) .setColor(Colors.DarkGrey)
.setDescription(`${loc.get('c_reminder5')} ${user}${loc.get('c_reminder6')} ${page}/${pageMax}`) .setDescription(
`${loc.get("c_reminder5")} ${user}${loc.get(
"c_reminder6"
)} ${page}/${pageMax}`
)
.setThumbnail(user.displayAvatarURL()); .setThumbnail(user.displayAvatarURL());
const limit = elementPerPage * page; const limit = elementPerPage * page;
@ -341,13 +407,19 @@ export const embedListReminders = async (client: Client, user: User, guildId: st
reminders.splice(0, limit - elementPerPage); reminders.splice(0, limit - elementPerPage);
reminders.forEach((remind) => { reminders.forEach((remind) => {
if (curseur < limit) { if (curseur < limit) {
let text = remind.data ?? loc.get('c_reminder7'); let text = remind.data ?? loc.get("c_reminder7");
if (text.length > 1024) { if (text.length > 1024) {
text = `${text.substring(0, 1021)}...`; text = `${text.substring(0, 1021)}...`;
} }
const expiration = `${loc.get('c_reminder8')} ${timeDeltaToString(remind.expiration_date)}`; const expiration = `${loc.get("c_reminder8")} ${timeDeltaToString(
remind.expiration_date
)}`;
embed.addFields({ embed.addFields({
name: `#${remind.id}${loc.get('c_reminder9')} ${showDate(local, loc, new Date(Number(remind.creation_date)))}\n${expiration}`, name: `#${remind.id}${loc.get("c_reminder9")} ${showDate(
local,
loc,
new Date(Number(remind.creation_date))
)}\n${expiration}`,
value: text, value: text,
inline: false, inline: false,
}); });
@ -356,7 +428,10 @@ export const embedListReminders = async (client: Client, user: User, guildId: st
} }
}); });
} else { } else {
embed.addFields({ name: '\u200b', value: `${loc.get('c_reminder10')}${page} ${loc.get('c_reminder11')}.` }); embed.addFields({
name: "\u200b",
value: `${loc.get("c_reminder10")}${page} ${loc.get("c_reminder11")}.`,
});
} }
return embed; return embed;

View file

@ -10,7 +10,7 @@ export const showDate = (
locale: Map<string, unknown>, locale: Map<string, unknown>,
date: Date date: Date
) => { ) => {
return date.toLocaleString(tz).replace(' ', ` ${locale.get('u_time_at')} `); return date.toLocaleString(tz).replace(" ", ` ${locale.get("u_time_at")} `);
}; };
enum TimeSecond { enum TimeSecond {
@ -19,7 +19,7 @@ enum TimeSecond {
Day = 86400, Day = 86400,
Hour = 3600, Hour = 3600,
Minute = 60, Minute = 60,
Second = 1 Second = 1,
} }
/** /**
@ -28,7 +28,15 @@ enum TimeSecond {
* @returns time in seconds * @returns time in seconds
*/ */
export const strToSeconds = (time: string) => { 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);

View file

@ -11,10 +11,10 @@
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */ /* 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": [ "lib": [
"ES2021.String" "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. */ // "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ // "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. */ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */ /* Modules */
"module": "commonjs", /* Specify what module code is generated. */ "module": "commonjs" /* Specify what module code is generated. */,
"rootDir": "./src", /* Specify the root folder within your source files. */ "rootDir": "./src" /* Specify the root folder within your source files. */,
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ // "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. */ // "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. */ // "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. */ // "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ // "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 '<reference>'s from expanding the number of files TypeScript should add to a project. */ // "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */ /* JavaScript Support */
@ -51,7 +51,7 @@
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted 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. */ // "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. */ // "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */ // "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. */ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
@ -73,12 +73,12 @@
/* Interop Constraints */ /* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ // "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. */ // "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. */ // "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 */ /* 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. */ // "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'. */ // "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. */ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
@ -102,8 +102,5 @@
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ // "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": [ "include": ["./**/*.ts", "./src/locales/*.json"]
"./**/*.ts",
"./src/locales/*.json"
]
} }