123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141 |
- import path from "path";
- import fs from "fs";
- import { Message, Client, ClientEvents } from "discord.js";
- import { EventType, BotEvent, ICommand, isPlugin, BotEventData, isCustomEvent, IPlugin, CommandType } from "./model/plugin";
- import { isAuthorisedAsync } from "./util";
- interface IDocumentationData {
- type: CommandType;
- name: string;
- doc?: string;
- example?: string;
- auth: boolean;
- }
- type BotEventCollection = { [event in EventType]?: BotEvent[] };
- export class PluginManager {
- private plugins: IPlugin[] = [];
- private commands: ICommand[] = [];
- private botEvents: BotEventCollection = {};
- constructor(private cmdPath: string) {
- this.init();
- }
- private init(): void {
- const files = fs.readdirSync(this.cmdPath);
- for (const file of files) {
- const ext = path.extname(file);
- if (ext != ".js")
- continue;
- // eslint-disable-next-line @typescript-eslint/no-var-requires
- this.loadCommand(require(path.resolve(this.cmdPath, file)));
- }
- }
- private loadCommand(mod: Record<string, unknown>) {
- const getBotEventArray = (evtType: EventType) => {
- if (!this.botEvents[evtType])
- this.botEvents[evtType] = [];
- return this.botEvents[evtType];
- };
- for (const i in mod) {
- if (!Object.prototype.hasOwnProperty.call(mod, i))
- continue;
-
- const commandClass = mod[i] as unknown;
- // Ensure this is indeed a command class
- if (!isPlugin(commandClass))
- continue;
-
- const cmd = new commandClass();
- this.plugins.push(cmd);
-
- if (cmd.botCommands)
- this.commands.push(...cmd.botCommands.map(c => ({ ...c, action: c.action.bind(cmd) })));
-
- if (cmd.botEvents)
- for (const [i, event] of Object.entries(cmd.botEvents)) {
- getBotEventArray(i as EventType)?.push((event as BotEvent).bind(cmd));
- }
- }
- }
- get documentation(): IDocumentationData[] {
- return this.commands.filter(m => m.documentation !== undefined).map(m => ({
- type: m.type,
- name: m.pattern.toString(),
- doc: m.documentation?.description,
- example: m.documentation?.example,
- auth: m.auth || false
- }));
- }
- async start(client: Client): Promise<void> {
- for (const evtName of Object.keys(this.botEvents)) {
- if (!Object.prototype.hasOwnProperty.call(this.botEvents, evtName) || isCustomEvent(evtName))
- continue;
- client.on(evtName as keyof ClientEvents, async (...args: unknown[]) =>
- await this.trigger(evtName as EventType, ...args)
- );
- }
-
- for (const plugin of this.plugins) {
- if (plugin.start)
- await plugin.start();
- }
- }
- async trigger(event: EventType, ...params: unknown[]): Promise<boolean> {
- const eventData: BotEventData = {
- actionsDone: false
- };
- const eventHandlers = this.botEvents[event];
- if (!eventHandlers)
- return eventData.actionsDone;
- for (const handler of eventHandlers) {
- const actionResult = handler(eventData, ...params);
- if (actionResult instanceof Promise)
- await actionResult;
- }
-
- return eventData.actionsDone;
- }
- async runCommand(type: CommandType, m: Message, content: string): Promise<boolean> {
- const lowerCaseContent = content.toLowerCase();
- for (const c of this.commands) {
- if (c.type != type)
- continue;
- let match = false;
- let matchData: string | RegExpMatchArray = "";
- if (typeof (c.pattern) == "string" && lowerCaseContent.startsWith(c.pattern)) {
- match = true;
- matchData = c.pattern;
- }
- else if (c.pattern instanceof RegExp) {
- const result = c.pattern.exec(content);
- if (result != null) {
- match = true;
- matchData = result;
- }
- }
- if (match) {
- if (c.auth && !(await isAuthorisedAsync(m.member)))
- return false;
- const eventResult = c.action({ message: m, contents: matchData });
- if (eventResult instanceof Promise)
- await eventResult;
- return true;
- }
- }
- return false;
- }
- }
|