Début de l'implémentation du command handler

This commit is contained in:
Melaine Gérard 2024-09-18 14:13:32 +02:00
parent c09fc4f4e8
commit a9b91137a1
No known key found for this signature in database
GPG Key ID: 89A9588EB2726A53
18 changed files with 240 additions and 35 deletions

View File

@ -0,0 +1,32 @@
import { ChatInputCommandInteraction, AutocompleteInteraction } from "discord.js";
import { Category } from "../enums/Category";
import {ICommand} from "../interfaces/ICommand";
import {CustomClient} from "./CustomClient";
import {ICommandOptions} from "../interfaces/ICommandOptions";
export class Command implements ICommand {
client: CustomClient;
name: string;
description: string;
category: Category;
options: object;
default_member_permissions: bigint;
dm_permissions: boolean;
cooldown: number;
constructor(client: CustomClient, options: ICommandOptions) {
this.client = client;
this.name = options.name;
this.description = options.description;
this.category = options.category;
this.options = options.options;
this.default_member_permissions = options.default_member_permissions;
this.dm_permissions = options.dm_permissions;
this.cooldown = options.cooldown;
}
execute(interaction: ChatInputCommandInteraction): void {
}
autocomplete(interaction: AutocompleteInteraction): void {
}
}

View File

@ -1,18 +1,29 @@
import IConfig from "../interfaces/IConfig"; import {IConfig} from "../interfaces/IConfig";
import ICustomClient from "../interfaces/ICustomClient"; import {ICustomClient} from "../interfaces/ICustomClient";
import {Client} from "discord.js"; import {Client, Collection, GatewayIntentBits} from "discord.js";
import Handler from "./Handler"; import {Handler} from "./Handler";
import {Command} from "./Command";
import {SubCommand} from "./SubCommand";
export default class CustomClient extends Client implements ICustomClient { export class CustomClient extends Client implements ICustomClient {
config: IConfig; config: IConfig;
handler: Handler; handler: Handler;
commands: Collection<string, Command>;
subCommands: Collection<string, SubCommand>;
cooldowns: Collection<string, Collection<string, number>>;
constructor() { constructor() {
super({ super({
intents: [], intents: Object.keys(GatewayIntentBits).map((a: string) => {
return GatewayIntentBits[a as keyof typeof GatewayIntentBits];
}),
}) })
this.config = require(`${process.cwd()}/data/config.json`); this.config = require(`${process.cwd()}/data/config.json`);
this.handler = new Handler(this); this.handler = new Handler(this);
this.commands = new Collection();
this.subCommands = new Collection();
this.cooldowns = new Collection();
} }
async init(): Promise<void> { async init(): Promise<void> {
await this.LoadHandlers(); await this.LoadHandlers();
@ -21,6 +32,7 @@ export default class CustomClient extends Client implements ICustomClient {
async LoadHandlers(): Promise<void> { async LoadHandlers(): Promise<void> {
await this.handler.loadEvents(); await this.handler.loadEvents();
await this.handler.loadCommands();
} }
} }

View File

@ -1,11 +1,11 @@
import { Events } from "discord.js"; import {ClientEvents} from "discord.js";
import IEvent from "../interfaces/IEvent"; import {IEvent} from "../interfaces/IEvent";
import CustomClient from "./CustomClient"; import {CustomClient} from "./CustomClient";
import IEventOptions from "../interfaces/IEventOptions"; import {IEventOptions} from "../interfaces/IEventOptions";
export default class Event implements IEvent { export class Event implements IEvent {
client: CustomClient; client: CustomClient;
name: Events; name: keyof ClientEvents;
description: string; description: string;
once: boolean; once: boolean;

View File

@ -1,10 +1,12 @@
import IHandler from "../interfaces/IHandler"; import {IHandler} from "../interfaces/IHandler";
import path from "node:path"; import path from "node:path";
import {glob} from "glob"; import {glob} from "glob";
import CustomClient from "./CustomClient"; import {CustomClient} from "./CustomClient";
import Event from "./Event"; import {Event} from "./Event";
import {Command} from "./Command";
import {SubCommand} from "./SubCommand";
export default class Handler implements IHandler { export class Handler implements IHandler {
client: CustomClient; client: CustomClient;
constructor(client: CustomClient) { constructor(client: CustomClient) {
this.client = client; this.client = client;
@ -13,18 +15,28 @@ export default class Handler implements IHandler {
const files = (await glob(`dist/events/**/*.js`)).map(filePath => path.resolve(filePath)); const files = (await glob(`dist/events/**/*.js`)).map(filePath => path.resolve(filePath));
files.map(async (file: string) => { files.map(async (file: string) => {
const event : Event = new (await import(file)).default(this.client); let evt = await import(file);
let EventClass: any;
for (const key in evt) {
if (typeof evt[key] === 'function') {
EventClass = evt[key];
break;
}
}
if (!EventClass) {
return delete require.cache[require.resolve(file)] && console.log(`Event ${file.split('/').pop()} n'a pas de classe`);
}
const event : Event = new EventClass(this.client);
if (!event.name) if (!event.name)
return delete require.cache[require.resolve(file)] && console.log(`Event ${file.split('/').pop()} n'a pas de nom`); return delete require.cache[require.resolve(file)] && console.log(`Event ${file.split('/').pop()} n'a pas de nom`);
const execute = (...args: any[]) => event.execute(...args); const execute = (...args: any[]) => event.execute(...args);
if (event.once) { if (event.once) {
// @ts-ignore
this.client.once(event.name, execute); this.client.once(event.name, execute);
} else { } else {
// @ts-ignore
this.client.on(event.name, execute); this.client.on(event.name, execute);
} }
@ -34,4 +46,39 @@ export default class Handler implements IHandler {
}); });
} }
async loadCommands(): Promise<void> {
const files = (await glob(`dist/commands/**/*.js`)).map(filePath => path.resolve(filePath));
files.map(async (file: string) => {
let evt = await import(file);
let EventClass: any;
for (const key in evt) {
if (typeof evt[key] === 'function') {
EventClass = evt[key];
break;
}
}
if (!EventClass) {
return delete require.cache[require.resolve(file)] && console.log(`Event ${file.split('/').pop()} n'a pas de classe`);
}
const command : Command|SubCommand = new EventClass(this.client);
if (!command.name)
return delete require.cache[require.resolve(file)] && console.log(`Event ${file.split('/').pop()} n'a pas de nom`);
if (file.split('/').pop()?.split(".")[2]) {
console.log(`Sous Commande ${file.split('/').pop()} chargé`);
this.client.subCommands.set(command.name, command as SubCommand);
} else {
console.log(`Commande ${file.split('/').pop()} chargé`);
this.client.commands.set(command.name, command as Command);
}
return delete require.cache[require.resolve(file)];
});
}
} }

View File

@ -0,0 +1,18 @@
import { ChatInputCommandInteraction } from "discord.js";
import {ISubCommand} from "../interfaces/ISubCommand";
import {CustomClient} from "./CustomClient";
import {ISubCommandOptions} from "../interfaces/ISubCommandOptions";
export class SubCommand implements ISubCommand {
client: CustomClient;
name: string;
constructor(client: CustomClient,options: ISubCommandOptions) {
this.client = client;
this.name = options.name;
}
execute(interaction: ChatInputCommandInteraction): void {
}
}

View File

@ -0,0 +1,4 @@
export enum Category {
UTILITIES = 'Utilitaires',
}

View File

@ -0,0 +1,17 @@
import {CustomClient} from "../classes/CustomClient";
import {AutocompleteInteraction, ChatInputCommandInteraction} from "discord.js";
import {Category} from "../enums/Category";
export interface ICommand {
client: CustomClient;
name: string;
description: string;
category: Category;
options: object;
default_member_permissions: bigint;
dm_permissions: boolean;
cooldown: number;
execute(interaction: ChatInputCommandInteraction): void;
autocomplete(interaction: AutocompleteInteraction): void;
}

View File

@ -0,0 +1,11 @@
import {Category} from "../enums/Category";
export interface ICommandOptions {
name: string;
description: string;
category: Category;
options: object;
default_member_permissions: bigint;
dm_permissions: boolean;
cooldown: number;
}

View File

@ -1,3 +1,3 @@
export default interface IConfig { export interface IConfig {
token: string; token: string;
} }

View File

@ -1,7 +1,14 @@
import IConfig from "./IConfig"; import {IConfig} from "./IConfig";
import {Command} from "../classes/Command";
import {Collection} from "discord.js";
import {SubCommand} from "../classes/SubCommand";
export default interface ICustomClient { export interface ICustomClient {
config: IConfig; config: IConfig;
commands: Collection<string, Command>;
subCommands: Collection<string, SubCommand>;
cooldowns: Collection<string, Collection<string, number>>;
init(): void; init(): void;
LoadHandlers(): void; LoadHandlers(): void;
} }

View File

@ -1,9 +1,9 @@
import CustomClient from "../classes/CustomClient"; import {CustomClient} from "../classes/CustomClient";
import {Events} from "discord.js"; import {ClientEvents} from "discord.js";
export default interface IEvent { export interface IEvent {
client: CustomClient; client: CustomClient;
name: Events; name: keyof ClientEvents;
description: string; description: string;
once: boolean; once: boolean;

View File

@ -1,7 +1,7 @@
import {Events} from "discord.js"; import {ClientEvents} from "discord.js";
export default interface IEventOptions { export interface IEventOptions {
name: Events; name: keyof ClientEvents;
description: string; description: string;
once: boolean; once: boolean;
} }

View File

@ -1,3 +1,4 @@
export default interface IHandler { export interface IHandler {
loadEvents(): void; loadEvents(): void;
loadCommands(): void;
} }

View File

@ -0,0 +1,9 @@
import {CustomClient} from "../classes/CustomClient";
import {ChatInputCommandInteraction} from "discord.js";
export interface ISubCommand {
client: CustomClient;
name: string;
execute(interaction: ChatInputCommandInteraction): void;
}

View File

@ -0,0 +1,3 @@
export interface ISubCommandOptions {
name: string;
}

View File

@ -1,7 +1,7 @@
import Event from '../../base/classes/Event'; import {Event} from '../../base/classes/Event';
import CustomClient from "../../base/classes/CustomClient"; import {CustomClient} from "../../base/classes/CustomClient";
import {Events} from "discord.js"; import {Events} from "discord.js";
export default class Ready extends Event { export class Ready extends Event {
constructor(client: CustomClient) { constructor(client: CustomClient) {
super(client, { super(client, {
name: Events.ClientReady, name: Events.ClientReady,

View File

@ -0,0 +1,44 @@
import {ChatInputCommandInteraction, Collection, Events} from "discord.js";
import {CustomClient} from "../../base/classes/CustomClient";
import {Event} from "../../base/classes/Event";
import {Command} from "../../base/classes/Command";
export class CommandHandler extends Event {
constructor(client: CustomClient) {
super(client, {
name: Events.InteractionCreate,
once: false,
description: 'Event se déclenchant lorsqu\'une interaction est créée'
});
}
async execute(interaction: ChatInputCommandInteraction): Promise<void> {
if (!interaction.isChatInputCommand()) return;
const command: Command = this.client.commands.get(interaction.commandName) as Command;
if (!command) {
await interaction.reply({content: 'Commande inconnue', ephemeral: true})
this.client.commands.delete(interaction.commandName);
return;
}
const {cooldowns} = this.client;
if (!cooldowns.has(command.name)) {
cooldowns.set(command.name, new Collection());
}
const now = Date.now();
const timestamps = cooldowns.get(command.name)!;
const cooldownAmount = (command.cooldown || 3) * 1000;
if (timestamps.has(interaction.user.id) && now < (timestamps.get(interaction.user.id) || 0) + cooldownAmount) {
await interaction.reply({
content: `Veuillez patienter ${((timestamps.get(interaction.user.id) || 0) + cooldownAmount - now) / 1000} secondes avant de réutiliser la commande \`${command.name}\``
})
return;
}
command.execute(interaction);
}
}

View File

@ -1,2 +1,2 @@
import CustomClient from "./base/classes/CustomClient"; import {CustomClient} from "./base/classes/CustomClient";
(async () => await new CustomClient().init())(); (async () => await new CustomClient().init())();