307 lines
11 KiB
Markdown
307 lines
11 KiB
Markdown
# Comment contribuer ? <!-- omit in toc -->
|
||
|
||
Ce guide contient méthodes et conseils sur comment aider le projet.
|
||
Lisez attentivement si vous êtes un nouveau contributeur.
|
||
|
||
Ce guide n'est pas fixe et est mis à jour régulièrement. Si vous
|
||
trouvez un problème quelconque, n'hésitez pas à le signaler par le biais
|
||
d'un [ticket](https://git.mylloon.fr/ConfrerieDuKassoulait/Botanique/issues) ou
|
||
à le corriger directement en soumettant
|
||
une [Pull Request](https://git.mylloon.fr/ConfrerieDuKassoulait/Botanique/pulls).
|
||
|
||
## Sommaire <!-- omit in toc -->
|
||
|
||
- [Recevoir de l'aide](#recevoir-de-laide)
|
||
- [Langues](#langues)
|
||
- [Ajouter une langue](#ajouter-une-langue)
|
||
- [Mettre à jour une langue](#mettre-à-jour-une-langue)
|
||
- [Projet](#projet)
|
||
- [Ajouter une commande](#ajouter-une-commande)
|
||
- [Ajouter un évènement](#ajouter-un-évènement)
|
||
- [Player](#player)
|
||
- [Modèles](#modèles)
|
||
- [Boutons](#boutons)
|
||
- [Autocomplétion](#autocomplétion)
|
||
- [Modifier du code](#modifier-du-code)
|
||
- [Soumettre ses modifications](#soumettre-ses-modifications)
|
||
- [Gestion du dépôt](#gestion-du-dépôt)
|
||
- [Tester son code](#tester-son-code)
|
||
|
||
## Recevoir de l'aide
|
||
|
||
Si tu as besoin d'aide, tu peux poser ta question sur
|
||
le [Discord](https://discord.gg/Z5ePxH4).
|
||
|
||
## Langues
|
||
|
||
La langue par défaut est définie dans
|
||
[`src/utils/client.ts`](src/utils/client.ts) dans `client.config.default_lang`.
|
||
|
||
La langue par défaut fait office de solution de secours dans le cas où une
|
||
traduction est incomplète. On part donc du postulat que la langue par défaut
|
||
contient toujours toutes les chaînes de caractère dont le bot a besoin.
|
||
|
||
La norme pour les nom dans les fichiers est la suivante :
|
||
|
||
- Chaîne de charactère des commandes :
|
||
`c` est utilisé pour `C`ommande.
|
||
|
||
- `c_NOM-COMMANDE_name` : Nom de la commande
|
||
- `c_NOM-COMMANDE_desc` : Description de la commande
|
||
- `c_NOM-COMMANDE_optX_name` : Nom de l'option X
|
||
- `c_NOM-COMMANDE_optX_desc` : Description de l'option X
|
||
- `c_NOM-COMMANDE_subX_name` : Nom de la sous-commande X
|
||
- `c_NOM-COMMANDE_subX_desc` : Description de la sous-commande X
|
||
- `c_NOM-COMMANDEX` : `X` le numéro de la chaîne de caractère
|
||
|
||
Évidemment ça peut s'additionner,
|
||
par exemple : `c_NOM-COMMANDE_subX_optX_desc`.
|
||
|
||
- Chaîne de caractère des évènements :
|
||
`e` est utilisé pour `E`vènements.
|
||
- `e_NOM-EVENEMENT_N` : `N` le nom de la chaîne de caractère
|
||
- Chaîne de caractère des utils :
|
||
`u` est utilisé pour `U`tilitaires.
|
||
- `u_NOM-FICHIER-UTILS_N` : `N` le nom de la chaîne de caractère
|
||
|
||
### Ajouter une langue
|
||
|
||
1. Créer un nouveau fichier dans [src/locales/](./src/locales/), le fichier
|
||
doit être nommé `langue.json` avec `langue` suivant
|
||
[cette liste](https://discord.com/developers/docs/reference#locales).
|
||
2. Le contenu du fichier peut être copié du fichier de la langue par défaut,
|
||
[cf. au-dessus](#langues).
|
||
3. Ce sont les valeurs des clés (le texte à gauche des `:`) qui doivent
|
||
être traduits. Merci par avance !
|
||
> Ne vous forcez pas à tout traduire. Même une contribution avec
|
||
> une seule variable de modifiée compte !
|
||
4. Une fois terminée, [ouvrez une Pull Request](#soumettre-ses-modifications).
|
||
|
||
### Mettre à jour une langue
|
||
|
||
1. Rechercher la langue dans le dossier [src/locales/](./src/locales/).
|
||
2. Modifier/Ajouter des traductions comme
|
||
[expliquer au-dessus](#ajouter-une-langue) (à partir du `3.`).
|
||
> Pensez à vérifier si de nouvelles valeurs n'ont pas été ajouté dans
|
||
> le fichier langue par défaut, [cf. au-dessus](#langues).
|
||
|
||
## Projet
|
||
|
||
Le code se trouve dans le dossier [src/](./src/). Dans ce dossier il y a :
|
||
|
||
- [commands/](./src/commands/) qui contient toutes les commandes, rangés par
|
||
catégories
|
||
- [events/](./src/events/) qui contient tous les évènements, rangés par
|
||
catégories
|
||
- [locales/](./src/locales/) qui contient tous les fichiers de langue
|
||
- [modules/](./src/modules/) qui contient les extensions utilisées,
|
||
par exemple, pour utiliser la fonction `capitalize()` d'un string, il faut
|
||
importer le fichier `string.ts` qui se trouve dans le dossier
|
||
- [utils/](./src/utils/) qui contient toutes les fonctions utilitaires, rangés
|
||
par fichiers
|
||
|
||
Les dossiers [commands/](./src/commands/) et [events/](./src/events/)
|
||
contiennent chacun un fichier `loader.js` qui charge respectivement
|
||
les commandes et les évènements dans le bot.
|
||
|
||
## Ajouter une commande
|
||
|
||
Pour ajouter une commande au bot, créez un fichier `nom-de-la-commande.ts` dans
|
||
un sous dossier de [`src/commands/`](./src/commands/). Vous pouvez créer une
|
||
nouvelle catégorie si votre commande n'entre dans aucune qui existe déjà.
|
||
|
||
Le contenu du fichier doit commencer comme suit :
|
||
|
||
```typescript
|
||
import { SlashCommandBuilder } from "@discordjs/builders";
|
||
import { Client, ChatInputCommandInteraction } from "discord.js";
|
||
import { getLocale, getLocalizations } from "../../utils/locales";
|
||
import { getFilename } from "../../utils/misc";
|
||
|
||
export default {
|
||
scope: () => [],
|
||
|
||
data: (client: Client) => {
|
||
const filename = getFilename(__filename);
|
||
return new SlashCommandBuilder()
|
||
.setName(filename.toLowerCase())
|
||
.setDescription(client.locales.get(client.config.default_lang)!.get(`c_${filename}_desc`)!)
|
||
.setNameLocalizations(getLocalizations(client, `c_${filename}_name`, true))
|
||
.setDescriptionLocalizations(getLocalizations(client, `c_${filename}_desc`));
|
||
},
|
||
|
||
interaction: async (interaction: ChatInputCommandInteraction, client: Client) => {
|
||
const loc = getLocale(client, interaction.locale);
|
||
|
||
/* Votre code ici */
|
||
},
|
||
};
|
||
```
|
||
|
||
Rapidement, cette structure comporte 3 éléments :
|
||
|
||
- `scope` : une liste de guildId où la commande est disponible, si la liste est vide,
|
||
la commande est disponible partout
|
||
- `data` : représente les données envoyées à l'API de Discord
|
||
- `interaction` : représente le comportement de la commande
|
||
|
||
Ce modèle vous permet de commencer rapidement votre commande, car il contient
|
||
déjà tout ce qu'il faut pour le support des langues. Pensez bien à ne pas écrire
|
||
directement vos chaînes de caractères ici, mais bien dans
|
||
les [fichiers de langues](./src/locales/), c'est à ça que la variable
|
||
`loc` sert.
|
||
|
||
Vous devez aussi ajouter **obligatoirement** :
|
||
|
||
- `"c_COMMANDE_name": "NOM"` au fichier de langue, avec `COMMANDE` le nom de
|
||
la commande et `NOM` le nom de votre commande.
|
||
- `"c_COMMANDE_desc": "DESCRIPTION"` au fichier de langue, avec `COMMANDE`
|
||
le nom de la commande et `DESCRIPTION` la description de votre commande.
|
||
|
||
> Note : Il est possible d'ajouter de l'autocomplétion via
|
||
> un 4ᵉ élément : `autocomplete`.
|
||
|
||
## Ajouter un évènement
|
||
|
||
Pour ajouter le support d'un évènement au bot, créez un fichier
|
||
`nom-evenement.ts` dans un sous dossier de [`src/events/`](./src/events/). Vous
|
||
pouvez créer une nouvelle catégorie si votre commande n'entre dans aucune qui
|
||
existe déjà.
|
||
|
||
Vous pouvez préciser que l'évènement ne sera déclenché qu'une seule fois avec
|
||
|
||
```typescript
|
||
export const once = true;
|
||
```
|
||
|
||
De préférence, merci de mettre un lien en commentaire vers la documentation
|
||
de discord.js de l'évènement
|
||
([exemple ici pour l'évènement `ready`](./src/events/client/ready.ts#L3))
|
||
|
||
### Player
|
||
|
||
Les évènements du player ont la même logique les autres, mais sont placés
|
||
dans le dossier [`player`](./src/events/player/).
|
||
|
||
> Pour déboguer le player, il est possible d'ajouter un évènement `debug`, en
|
||
> voici un exemple :
|
||
>
|
||
> ```ts
|
||
> import { GuildQueue } from "discord-player";
|
||
>
|
||
> /** https://discord-player.js.org/docs/types/discord-player/GuildQueueEvents */
|
||
> export default (_: GuildQueue, message: string) => {
|
||
> console.info(message);
|
||
> };
|
||
> ```
|
||
|
||
## Modèles
|
||
|
||
Les modèles sont gérés [en dehors séparément du reste](./src/modals/).
|
||
|
||
## Boutons
|
||
|
||
Les boutons sont gérés [en dehors séparément du reste](./src/buttons/)
|
||
|
||
Chaque bouton à une implémentation séparée des autres, même s'ils sont dans le
|
||
même message.
|
||
|
||
Contrairement aux autres éléments, les boutons doivent se faire collecter via
|
||
la fonction [`collect`](./src/buttons/loader.ts#L46) juste après leur déclaration.
|
||
|
||
## Autocomplétion
|
||
|
||
La réponse qu'attend Discord doit se faire obligatoirement sous 3 secondes.
|
||
Pour se faire on peut utiliser un timeout avec
|
||
[une race](https://fr.wikipedia.org/wiki/Situation_de_comp%C3%A9tition).
|
||
|
||
```typescript
|
||
let timeoutId: NodeJS.Timeout;
|
||
const delay = new Promise(function (_, reject) {
|
||
timeoutId = setTimeout(function () {
|
||
reject(new Error());
|
||
}, 2900); // correspond au temps du timeout en ms
|
||
});
|
||
|
||
const resultat = await Promise.race([delay, commandeQuiRenvoieUnPromise])
|
||
.then((res) => {
|
||
clearTimeout(timeoutId);
|
||
return resultatVoulu;
|
||
})
|
||
.catch(() => {
|
||
return resultatErreur; // correspond à temps écoulé ou erreur de notre commande
|
||
});
|
||
```
|
||
|
||
## Modifier du code
|
||
|
||
Quand vous modifiez quelque chose, pensez à mettre-à-jour les langues. Si vous
|
||
ne savez pas traduire dans une langue, ne vous forcez pas, supprimer simplement
|
||
la traduction.
|
||
|
||
- [Créez un fork](https://git.mylloon.fr/repo/fork/76) et poussez
|
||
vos modifications dans ce dernier.
|
||
|
||
Pour commencer, vous pouvez jeter un œil aux
|
||
[tickets facilement résolvable](https://git.mylloon.fr/ConfrerieDuKassoulait/Botanique/issues?state=open&labels=82).
|
||
|
||
- De préférences, les fonctions, méthodes et variables seront écrites
|
||
en anglais, ainsi que les commits afin que chacun puisse contribuer.
|
||
|
||
## Soumettre ses modifications
|
||
|
||
1. Pensez à bien commenter votre code (en anglais) pour que n'importe qui
|
||
comprennent vos modifications. Vérifier bien dans tous les fichiers si ce que
|
||
vous avez modifié n'est pas référencer ailleurs (exemple : si vous modifier
|
||
une variable d'environnement, il faut penser à mettre à jour le
|
||
[`README`](./README.md#variables-denvironnements)).
|
||
|
||
2. N'oubliez pas d'utiliser [les fichiers de langues](./src/locales/) pour vos
|
||
chaînes de caractère, [cf. cette partie](#langues) pour plus de précisions.
|
||
|
||
3. Veuillez tester vos modifications avant de les soumettre. **Attention**, ce
|
||
n'est pas parce que vos modifications fonctionnent avec `npm run debug` qu'elles
|
||
fonctionneront avec `npm run main`, ainsi que dans l'image Docker.
|
||
|
||
4. Lorsque vous vous sentez confiant dans vos modifications, ouvrez
|
||
une [Pull Request](https://git.mylloon.fr/ConfrerieDuKassoulait/Botanique/pulls)
|
||
afin que votre code puisse être revu et fusionné. Vous pouvez suivre cette
|
||
[condition de nommage](https://www.conventionalcommits.org/fr/v1.0.0/),
|
||
ça aide à s'y retrouver plus rapidement.
|
||
|
||
> **Explication**
|
||
>
|
||
> `npm run debug` exécute le code depuis le dossier [`src`](src/)
|
||
> tandis que `npm run main` et l'image Docker le fait depuis le dossier `dist`.
|
||
>
|
||
> Docker est cependant différent, car dans l'image, le dossier [`src`](src/) est
|
||
> supprimé.
|
||
|
||
## Gestion du dépôt
|
||
|
||
- On ne push jamais directement sur la branche `main`.
|
||
- Quand on merge des modifications vers `main`, on fait un _squash_,
|
||
l'historique des modifications reste disponible dans
|
||
[le graphe](https://git.mylloon.fr/ConfrerieDuKassoulait/Botanique/graph).
|
||
- De préférences, suivre [ces conventions](https://www.conventionalcommits.org/fr/v1.0.0/)
|
||
(cf. cette [partie précédente](#soumettre-ses-modifications)).
|
||
|
||
## Tester son code
|
||
|
||
Il est souhaité d'écrire des tests quand cela est possible.
|
||
|
||
```ts
|
||
import { fnReturnsTrue } from "../src/utils/file";
|
||
|
||
import { describe, it } from "node:test";
|
||
import assert from "node:assert/strict";
|
||
|
||
describe("test name", () => {
|
||
{
|
||
const name = "to be tested";
|
||
it(name, () => {
|
||
assert.strictEqual(fnReturnsTrue() /* function to test */, true /* expected result */);
|
||
});
|
||
}
|
||
});
|
||
```
|