UNPKG

meocord

Version:

Decorator-based Discord bot framework built on discord.js. Brings NestJS-style controllers, dependency injection, guards, and testing utilities to bot development — with a full CLI and TypeScript-first design.

64 lines (61 loc) 2.81 kB
import 'reflect-metadata'; import { Container, injectable } from 'inversify'; import { Client } from 'discord.js'; import { Logger } from '../common/logger.js'; import '../common/theme.js'; import { MeoCordApp } from './meocord.app.js'; import { loadMeoCordConfig } from '../util/meocord-config-loader.util.js'; import { MetadataKey } from '../enum/metadata-key.enum.js'; /** * Recursively binds a class and all its constructor dependencies to the container in singleton scope. */ function bindDependencies(container, cls) { if (container.isBound(cls)) return; if (!Reflect.hasMetadata(MetadataKey.Injectable, cls)) { injectable()(cls); } container.bind(cls).toSelf().inSingletonScope(); const deps = Reflect.getMetadata(MetadataKey.ParamTypes, cls) || []; for (const dep of deps){ if (dep === Client) continue; bindDependencies(container, dep); } } class MeoCordFactory { static create(target) { const options = Reflect.getMetadata(MetadataKey.AppOptions, target); if (!options) { if (typeof target === 'function') { this.logger.error(`No @MeoCord() options found for class: ${target.name}`); } else { this.logger.error('No @MeoCord() options found for the provided target.'); } throw new Error('Target class is not decorated with @MeoCord().'); } const meocordConfig = loadMeoCordConfig(); if (!meocordConfig) { throw new Error('MeoCord config not found. Ensure meocord.config.ts exists.'); } const container = new Container(); // Bind the Discord client as a constant value const discordClient = new Client(options.clientOptions); container.bind(Client).toConstantValue(discordClient); // Bind all controllers and their transitive dependencies for (const ctrl of options.controllers){ bindDependencies(container, ctrl); } // Bind and eagerly instantiate standalone services so their constructors run. // This is critical for event-driven services that register Discord event // listeners (or connect to external systems) inside their constructor. for (const svc of options.services ?? []){ bindDependencies(container, svc); container.get(svc); } // Stamp each controller class with the container so @UseGuard can resolve guards for (const ctrl of options.controllers){ Reflect.defineMetadata(MetadataKey.Container, container, ctrl); } return new MeoCordApp(options.controllers, container, discordClient, meocordConfig.discordToken, options.activities); } } MeoCordFactory.logger = new Logger(); export { MeoCordFactory };