Skip to content

Commit

Permalink
feat(framework): slash commands
Browse files Browse the repository at this point in the history
  • Loading branch information
Mesteery committed Jul 11, 2021
1 parent ad4a15b commit ada53f3
Show file tree
Hide file tree
Showing 11 changed files with 424 additions and 26 deletions.
93 changes: 71 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,45 +1,43 @@
# Bot Discord de la communauté

## Développement

### Prérequis
## Prérequis

- Node.js v16
- npm v7
- Un bot Discord installé sur une copie du serveur ES Community.
- Template: https://discord.new/T3mtuFqjR8Tm
- Template : https://discord.new/T3mtuFqjR8Tm

### Préparation de l'environnement
## Préparation de l'environnement

Installez les dépendances avec npm:
Installez les dépendances avec npm :

```console
npm ci
```

Créez un fichier `.env` avec votre token de bot:
Créez un fichier `.env` avec votre token de bot :

```env
DISCORD_TOKEN=votretoken
```

### Exécution du bot
## Exécution du bot

```console
npm start
```

Cette commande exécute le fichier `src/bot.ts`, qui démarre le bot. Les changements dans le dossier `src` sont observés par `nodemon` et le bot est redémarré automatiquement.

### Tests
## Tests

Le projet contient 3 scripts de test qui doivent passer pour tout commit poussé sur la branche `main`. Vous pouvez exécuter tous les tests avec la commande suivante:
Le projet contient 3 scripts de test qui doivent passer pour tout commit poussé sur la branche `main`. Vous pouvez exécuter tous les tests avec la commande suivante :

```console
npm test
```

#### Tests TS
### Tests TS

```console
# Exécution des tests.
Expand All @@ -51,7 +49,7 @@ npm run test-coverage

Le framework de test [Jest](https://jestjs.io/) est utilisé pour exécuter les tests. Ceux-ci doivent être écrits en TypeScript dans le dossier `tests`. Essayez de conserver la même structure de dossiers que dans `src` pour organiser les tests.

#### Lint
### Lint

```console
# Exécution d'ESLint
Expand All @@ -61,34 +59,85 @@ npm run lint
npm run lint-fix
```

Nous utilisons [ESLint](https://eslint.org/) ainsi que [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint) pour l'analyse statique du code.
Nous utilisons [ESLint](https://eslint.org/) ainsi que [TypeScript ESLint](https://github.com/typescript-eslint/typescript-eslint) pour l'analyse statique du code.

#### Vérification des types TypeScript
### Vérification des types TypeScript

```console
npm run check-types
```

Cette commande exécute le compilateur TypeScript avec l'option `--noEmit`. Elle permet de valider les types de l'entier du projet, y compris sur les fichiers qui ne sont pas testés avec Jest.

### Écriture de fonctionnalités
## Écriture de fonctionnalités

### Commandes

Chaque commande doit être écrite dans un fichier du dossier `src/commands`. Ce
fichier doit instancier et exporter par défaut une instance de la classe `Command`,
en lui passant les paramètres de configuration suivants :

- `enabled`: boolean. Peut être mis à `false` pour désactiver la commande.
- `name`: string. Nom de la commande..
- `description`: string. Description de ce que fait la commande (en français).
- `options`?: object. Options (arguments) de la commande.
- `guildId`?: Snowflake. L'identifiant d'une guilde, si cette commande est spécifique à une guilde.
- `defaultPermission`?: boolean. Si la commande doit être activé par défaut quand le bot est ajouté à un serveur (`true` par défaut).
- `handle`: function. Fonction exécutée lorsque cette commande est appellé. Elle recevra un argument `context`, avec les propriétés :
- `args`: Objet correctement typé, contenant les options fournis par l'éxecuteur de la commande (abstraction d'`interaction.options`).
- `interaction`: Instance de CommandInteraction (discord.js).
- `client`: Instance du Client (discord.js).
- `logger`: Instance du Logger (pino).

#### Tâches cron
#### Exemple

**Fichier exemplaire :** [src/commands/Hello.ts](src/commands/Hello.ts).

```ts
import { Command, CommandOptionTypes } from '../framework';

// création d'une commande slash (https://discord.com/developers/docs/interactions/slash-commands)
export default new Command({
enabled: true,
name: 'say', // nom de la commande
description: 'Dit ce que vous lui dites.', // description de la commande
options: {
message: {
// ceci est une option ("argument") de la commande slash
type: CommandOptionTypes.String, // type d'option (en l'occurence, chaine de caractère)
description: 'Ce que le bot doit dire.', // description de cette option
required: true, // option obligatoire (par défaut, false)
},
},
handle({ args, interaction }) {
// args aura comme type : `{ message: string }`
return interaction.reply(
`**${interaction.user.username}** m'a dit de dire : « ${args.message} ».`,
);
// si toutefois, vous voulez accéder aux arguments fourni comme telle par discord.js :
// interaction.options.get('message').value;
},
});
```

### Tâches cron

Chaque tâche cron doit être écrite dans un fichier du dossier `src/crons`. Ce
fichier doit instancier et exporter par défaut une instance de la classe Cron,
en lui passant les paramètres de configuration suivants:
fichier doit instancier et exporter par défaut une instance de la classe `Cron`,
en lui passant les paramètres de configuration suivants :

- `enabled`: boolean. Peut être mis à `false` pour désactiver la tâche.
- `name`: string. Nom de la tâche. Utilisé dans les logs.
- `description`: string. Description de ce que fait la tâche (en français).
- `schedule`: string. Programme d'exécution. Vous pouvez utiliser [crontab guru](https://crontab.guru/) pour le préparer.
- `handle`: function. Fonction exécutée selon le programme. Elle recevra un argument `context`, avec les propriétés:
- `handle`: function. Fonction exécutée selon le programme. Elle recevra un argument `context`, avec les propriétés :
- `date`: Date théorique d'exécution de la tâche.
- `client`: Instance du client discord.js.
- `logger`: Instance du logger pino.
- `client`: Instance du Client (discord.js).
- `logger`: Instance du Logger (pino).

#### Exemple

Exemple:
**Fichier exemplaire :** [src/crons/CommitStrip.ts](src/crons/CommitStrip.ts).

```ts
import { Cron } from '../framework';
Expand Down
9 changes: 9 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"@types/ws": "^7.4.6",
"@typescript-eslint/eslint-plugin": "^4.28.2",
"@typescript-eslint/parser": "^4.28.2",
"discord-api-types": "^0.19.0-next.f393ba520d7d6d2aacaca7b3ca5d355fab614f6e",
"eslint": "^7.30.0",
"jest": "^27.0.6",
"nodemon": "^2.0.12",
Expand Down
1 change: 1 addition & 0 deletions src/bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Dotenv.config();

const bot = new Bot({
token: process.env.DISCORD_TOKEN,
commands: path.join(__dirname, 'commands'),
crons: path.join(__dirname, 'crons'),
formatCheckers: path.join(__dirname, 'format-checkers'),
});
Expand Down
32 changes: 32 additions & 0 deletions src/commands/Hello.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Command, CommandOptionTypes } from '../framework';

export default new Command({
name: 'hello',
description: 'Vous salue.',
enabled: true,
options: {
stars: {
description: "Nombre d'étoiles accompagnant le salut.",
required: true,
type: CommandOptionTypes.Integer,
choices: [
{ name: '1 étoile', value: 1 },
{ name: '2 étoiles', value: 2 },
{ name: '3 étoiles', value: 3 },
{ name: '4 étoiles', value: 4 },
{ name: '5 étoiles', value: 5 },
] as const,
},
user: {
description: 'Salut un utilisateur spécifique (vous par défaut).',
type: CommandOptionTypes.User,
},
},
handle({ args, interaction }) {
return interaction.reply(
`Salut ${(args.user ?? interaction.user).toString()} ${'⭐'.repeat(
args.stars,
)}`,
);
},
});
25 changes: 23 additions & 2 deletions src/framework/Bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import path from 'path';
import { Client, Intents } from 'discord.js';
import pino from 'pino';

import { Cron } from './Cron';
import { Base, BaseConfig } from './Base';
import { Command, CommandManager } from './command';
import { Cron } from './Cron';
import { FormatChecker } from './FormatChecker';

export interface BotOptions {
Expand All @@ -15,6 +16,10 @@ export interface BotOptions {
* Defaults to `process.env.DISCORD_TOKEN`.
*/
token?: string;
/**
* Directory that contains the `Command` definitions.
*/
commands?: string;
/**
* Directory that contains the `Cron` definitions.
*/
Expand All @@ -27,11 +32,14 @@ export interface BotOptions {

type Constructor<T extends Base, U extends BaseConfig> = {
new (config: U): T;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
new <Any>(config: U): T;
};

export class Bot {
private readonly token?: string;
private _client: Client | null;
private commandManager?: CommandManager;
private crons: Cron[] = [];
private formatCheckers: FormatChecker[] = [];

Expand All @@ -42,9 +50,16 @@ export class Bot {
this._client = null;
this.logger = pino();

if (options.commands) {
this.commandManager = new CommandManager(
this.loadDirectory(options.commands, 'commands', Command),
);
}

if (options.crons) {
this.crons = this.loadDirectory(options.crons, 'crons', Cron);
}

if (options.formatCheckers) {
this.formatCheckers = this.loadDirectory(
options.formatCheckers,
Expand Down Expand Up @@ -141,6 +156,9 @@ export class Bot {
this.client.login(this.token),
once(this.client, 'ready'),
]);
if (this.commandManager) {
await this.commandManager.start(this);
}
this.startCrons();
this.startFormatCheckers();
} catch (error) {
Expand All @@ -152,10 +170,13 @@ export class Bot {
/**
* Stop the bot.
*/
public stop(): void {
public async stop(): Promise<void> {
if (!this._client) {
throw new Error('Bot was not started');
}
if (this.commandManager) {
await this.commandManager.stop(this);
}
this.stopCrons();
this.stopFormatCheckers();
this._client.destroy();
Expand Down
Loading

0 comments on commit ada53f3

Please sign in to comment.