Botanique/CONTRIBUTING.md

308 lines
11 KiB
Markdown
Raw Normal View History

# 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";
2024-12-10 10:04:15 +01:00
import { describe, it } from "node:test";
import assert from "node:assert/strict";
describe("test name", () => {
{
const name = "to be tested";
2024-12-10 10:04:15 +01:00
it(name, () => {
assert.strictEqual(fnReturnsTrue() /* function to test */, true /* expected result */);
});
}
});
```