UNPKG

@shadow-dev/core

Version:

A modular core framework for Discord bot development, providing commands, buttons, menus, middleware, and more.

227 lines (192 loc) 7.78 kB
import { Client, ClientEvents, GatewayIntentsString, } from "discord.js"; import { CommandManager } from "./command/commandManager"; import { EventManager } from "./event/eventManager"; import { ButtonManager } from "./button/buttonManager"; import { MenuManager } from "./menu/menuManager"; import { glob } from "glob"; import path from "path"; import fs from "fs"; import { Command } from "./command"; import { Event } from "./event"; import { Button } from "./button"; import { Menu } from "./menu"; import { importFile, registerModule } from "./util"; import { PluginLoader } from "./plugin"; export class Bot { public client: Client; public debug: boolean; private readonly commandManager: CommandManager; private eventManager: EventManager; private buttonManager: ButtonManager; private menuManager: MenuManager; private pluginLoader: PluginLoader; constructor(token: string, intents: GatewayIntentsString[], debug = false) { this.client = new Client({ intents, }); this.debug = debug; this.commandManager = new CommandManager(this.client); this.eventManager = new EventManager(this.client); this.buttonManager = new ButtonManager(this.client); this.menuManager = new MenuManager(this.client); this.pluginLoader = new PluginLoader(this); registerModule<Event<keyof ClientEvents>>("events", this.eventManager, this.client, this.debug).then(() => { this.client.login(token).then(async () => { await this.registerModules(); }); }); // this.registerEvents().then(() => { // this.client.login(token).then(async () => { // await this.registerModules(); // ButtonManager.LogAllButtons(); // }); // }); } private async registerModules() { if (this.debug) console.log("🔍 Registering modules..."); await registerModule<Command>("commands", this.commandManager, this.client, this.debug); await registerModule<Button>("buttons", this.buttonManager, this.client, this.debug); await registerModule<Menu>("menus", this.menuManager, this.client, this.debug); console.log(`✅ Successfully loaded ${CommandManager.getAllCommands().size} commands, ${ButtonManager.getAllButtons().size} buttons, ${MenuManager.getAllMenus().size} menus.`) // await this.registerCommands(); // await this.registerButtons(); // await this.registerMenus(); await this.pluginLoader.registerPlugins(); if (this.debug) console.log("✅ All modules registered."); } private async registerCommands() { if (this.debug) console.log("🔍 Scanning for commands..."); const baseDir = process.cwd(); // Detect if running from `src/commands` (development) or `dist/commands` (production) const isDev = fs.existsSync(path.join(baseDir, "src/commands")); const commandsDir = isDev ? "src/commands" : "dist/commands"; if (this.debug) console.log("🟢 Using commands directory:", commandsDir); // Automatically use `.ts` in dev, `.js` in production const ext = isDev ? "ts" : "js"; const commandFiles = await glob(`**/*.${ext}`, { cwd: path.join(baseDir, commandsDir), absolute: true, }); if (this.debug) console.log("🔍 Found command files:", commandFiles); if (commandFiles.length === 0) { console.warn("⚠️ No command files found. Check your folder structure."); } // Use forEach with async handling commandFiles.forEach(async (filePath) => { try { const command: Command = await importFile(filePath); if (!command?.name) return; this.commandManager.registerCommand(command); if (this.debug) console.log(`✅ Registered command: ${command.name}`); } catch (err) { console.error(`❌ Error loading command at ${filePath}:`, err); } }); if (this.debug) { console.log( `✅ Successfully loaded ${ CommandManager.getAllCommands().size } commands.` ); } } private async registerEvents() { if (this.debug) console.log("🔍 Scanning for events..."); const baseDir = process.cwd(); // Detect if running from `src/events` (development) or `dist/events` (production) const isDev = fs.existsSync(path.join(baseDir, "src/events")); const eventsDir = isDev ? "src/events" : "dist/events"; if (this.debug) console.log("🟢 Using Events directory:", eventsDir); // Automatically use `.ts` in dev, `.js` in production const ext = isDev ? "ts" : "js"; const eventFiles = await glob(`**/*.${ext}`, { cwd: path.join(baseDir, eventsDir), absolute: true, }); for (const filePath of eventFiles) { try { const event: Event<keyof ClientEvents> = await importFile( filePath ); if (!event || !event.event || typeof event.run !== "function") { console.warn(`⚠️ Skipping invalid event file: ${filePath}`); continue; } if (event.once) { this.client.once(event.event, (...args) => { event.run(...args); }); } else { this.client.on(event.event, (...args) => event.run(...args)); } } catch (err) { console.error(`❌ Error loading event at ${filePath}:`, err); } } if (this.debug) console.log("✅ Events registered."); } private async registerButtons() { if (this.debug) console.log("🔍 Scanning for buttons..."); const baseDir = process.cwd(); // Detect if running from `src/commands` (development) or `dist/commands` (production) const isDev = fs.existsSync(path.join(baseDir, "src/buttons")); const buttonsDir = isDev ? "src/buttons" : "dist/buttons"; const ext = isDev ? "ts" : "js"; const buttonFiles = await glob(`**/*.${ext}`, { cwd: path.join(baseDir, buttonsDir), absolute: true, }); for (const filePath of buttonFiles) { try { const button: Button = await importFile(filePath); console.log(button); this.buttonManager.registerButton(button); } catch (err) { console.error(`❌ Error loading button at ${filePath}:`, err); } } if (this.debug) console.log("✅ Buttons registered."); } private async registerMenus() { if (this.debug) console.log("🔍 Scanning for select menus..."); const baseDir = process.cwd(); // Detect if running from `src/commands` (development) or `dist/commands` (production) const isDev = fs.existsSync(path.join(baseDir, "src/menus")); const menuDir = isDev ? "src/menus" : "dist/menus"; if (this.debug) console.log("🟢 Using menus directory:", menuDir); // Automatically use `.ts` in dev, `.js` in production const ext = isDev ? "ts" : "js"; const menuFiles = await glob(`**/*.${ext}`, { cwd: path.join(baseDir, menuDir), absolute: true, }); for (const filePath of menuFiles) { try { const menu: Menu = await importFile(filePath); this.menuManager.registerMenu(menu); } catch (err) { console.error(`❌ Error loading menu at ${filePath}:`, err); } } if (this.debug) console.log("✅ Select menus registered."); } getCommandManager() { return this.commandManager; } getEventManager() { return this.eventManager; } getButtonManager() { return this.buttonManager; } getMenuManager() { return this.menuManager; } getClient() { return this.client; } }