UNPKG

djshandler-core

Version:

Core command handler framework for Discord.js bots

309 lines (307 loc) 8.56 kB
// src/handler.ts import { Client, Collection, GatewayIntentBits, Events, REST, Routes } from "discord.js"; var DJSHandler = class { constructor(options) { this.options = options; this.commands = new Collection(); this.events = new Collection(); this.client = new Client({ intents: this.options.intents || [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent ] }); if (this.options.token) { this.rest = new REST().setToken(this.options.token); } this.setupDefaultEvents(); } /** * Register a command with the handler */ registerCommand(command) { this.commands.set(command.data.name, command); return this; } /** * Register an event with the handler */ registerEvent(event) { this.events.set(event.name, event); if (event.once) { this.client.once(event.name, (...args) => event.execute(...args)); } else { this.client.on(event.name, (...args) => event.execute(...args)); } return this; } /** * Load all commands from a directory */ async loadCommands(commandsPath) { return this; } /** * Load all events from a directory */ async loadEvents(eventsPath) { return this; } /** * Deploy slash commands to Discord */ async deployCommands(guildId) { if (!this.rest || !this.options.clientId) { throw new Error("REST client not initialized or clientId not provided"); } const commandsData = Array.from(this.commands.values()).map( (command) => command.data.toJSON() ); try { console.log(`Started refreshing ${commandsData.length} application (/) commands.`); const route = guildId ? Routes.applicationGuildCommands(this.options.clientId, guildId) : Routes.applicationCommands(this.options.clientId); await this.rest.put(route, { body: commandsData }); console.log(`Successfully reloaded ${commandsData.length} application (/) commands.`); } catch (error) { console.error("Error deploying commands:", error); throw error; } } /** * Start the bot */ async start() { if (!this.options.token) { throw new Error("Bot token is required to start the client"); } try { await this.client.login(this.options.token); console.log("Bot started successfully!"); } catch (error) { console.error("Error starting bot:", error); throw error; } } /** * Get the Discord.js client instance */ getClient() { return this.client; } /** * Get registered commands */ getCommands() { return this.commands; } /** * Get registered events */ getEvents() { return this.events; } /** * Setup default event handlers */ setupDefaultEvents() { this.client.on(Events.InteractionCreate, async (interaction) => { if (!interaction.isChatInputCommand()) return; const command = this.commands.get(interaction.commandName); if (!command) return; try { await command.execute(interaction); } catch (error) { console.error(`Error executing command ${interaction.commandName}:`, error); const reply = { content: "There was an error while executing this command!", ephemeral: true }; if (interaction.replied || interaction.deferred) { await interaction.followUp(reply); } else { await interaction.reply(reply); } } }); this.client.on(Events.ClientReady, () => { console.log(`Ready! Logged in as ${this.client.user?.tag}`); }); } }; // src/decorators.ts import "reflect-metadata"; import { SlashCommandBuilder as SlashCommandBuilder2 } from "discord.js"; var COMMAND_METADATA_KEY = Symbol("command"); var EVENT_METADATA_KEY = Symbol("event"); function CommandClass(metadata) { return function(constructor) { Reflect.defineMetadata(COMMAND_METADATA_KEY, metadata, constructor); return constructor; }; } function Execute() { return function(target, propertyKey, descriptor) { Reflect.defineMetadata("execute", true, target, propertyKey); }; } function EventClass(metadata) { return function(constructor) { Reflect.defineMetadata(EVENT_METADATA_KEY, metadata, constructor); return constructor; }; } function On(eventName) { return function(target, propertyKey, descriptor) { Reflect.defineMetadata("eventName", eventName, target, propertyKey); Reflect.defineMetadata("once", false, target, propertyKey); }; } function Once(eventName) { return function(target, propertyKey, descriptor) { Reflect.defineMetadata("eventName", eventName, target, propertyKey); Reflect.defineMetadata("once", true, target, propertyKey); }; } function extractCommand(CommandClass2) { const metadata = Reflect.getMetadata(COMMAND_METADATA_KEY, CommandClass2); if (!metadata) return null; const instance = new CommandClass2(); const prototype = Object.getPrototypeOf(instance); const methods = Object.getOwnPropertyNames(prototype); let executeMethod = null; for (const method of methods) { if (Reflect.getMetadata("execute", prototype, method)) { executeMethod = instance[method].bind(instance); break; } } if (!executeMethod) return null; const commandBuilder = new SlashCommandBuilder2().setName(metadata.name).setDescription(metadata.description); return { data: commandBuilder, execute: executeMethod, category: metadata.category, disabled: metadata.disabled, cooldown: metadata.cooldown }; } function extractEvent(EventClass2) { const metadata = Reflect.getMetadata(EVENT_METADATA_KEY, EventClass2); if (!metadata) return null; const instance = new EventClass2(); const prototype = Object.getPrototypeOf(instance); const methods = Object.getOwnPropertyNames(prototype); let executeMethod = null; for (const method of methods) { const eventName = Reflect.getMetadata("eventName", prototype, method); if (eventName) { executeMethod = instance[method].bind(instance); break; } } if (!executeMethod) return null; return { name: metadata.name, once: metadata.once, execute: executeMethod }; } // src/utils.ts import * as fs from "fs/promises"; import * as path from "path"; async function loadFiles(dirPath, extension = ".js") { const files = []; try { const items = await fs.readdir(dirPath, { withFileTypes: true }); for (const item of items) { const fullPath = path.join(dirPath, item.name); if (item.isDirectory()) { const subFiles = await loadFiles(fullPath, extension); files.push(...subFiles); } else if (item.isFile() && item.name.endsWith(extension)) { files.push(fullPath); } } } catch (error) { console.warn(`Could not read directory ${dirPath}:`, error); } return files; } async function importModule(filePath) { try { const module = await import(filePath); return module.default || module; } catch (error) { console.warn(`Could not import module from ${filePath}:`, error); return null; } } async function fileExists(filePath) { try { await fs.access(filePath); return true; } catch { return false; } } async function ensureDir(dirPath) { try { await fs.mkdir(dirPath, { recursive: true }); } catch (error) { console.warn(`Could not create directory ${dirPath}:`, error); } } function formatCommandName(name) { return name.toLowerCase().replace(/[^a-z0-9-_]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, ""); } function isValidCommandName(name) { return /^[a-z0-9-_]{1,32}$/.test(name); } var Logger = class { constructor(prefix = "DJSHandler") { this.prefix = prefix; } info(message, ...args) { console.log(`[${this.prefix}] ${message}`, ...args); } warn(message, ...args) { console.warn(`[${this.prefix}] ${message}`, ...args); } error(message, ...args) { console.error(`[${this.prefix}] ${message}`, ...args); } debug(message, ...args) { if (process.env.NODE_ENV === "development") { console.debug(`[${this.prefix}] ${message}`, ...args); } } }; var logger = new Logger(); export { CommandClass, DJSHandler as CommandHandler, DJSHandler, EventClass, Execute, Logger, On, Once, ensureDir, extractCommand, extractEvent, fileExists, formatCommandName, importModule, isValidCommandName, loadFiles, logger }; //# sourceMappingURL=index.mjs.map