UNPKG

cyberbot-v2

Version:

cyberbot, 基于napcat-ts, nodejs,轻量qq机器人框架。

310 lines (309 loc) 12.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.PluginManager = void 0; const chokidar_1 = require("chokidar"); const fs = __importStar(require("fs")); const path = __importStar(require("path")); const ts_node_1 = require("ts-node"); const index_1 = require("./index.js"); // 注册TypeScript编译器(只执行一次) let tsNodeRegistered = false; const registerTSNode = () => { if (!tsNodeRegistered) { (0, ts_node_1.register)({ transpileOnly: true, skipProject: true, compilerOptions: { module: 'CommonJS', esModuleInterop: true, } }); tsNodeRegistered = true; } }; class PluginManager { bot; plugins = new Map(); pluginDir; config; configPath; builtInPlugins; // 添加内置插件集合 constructor(bot) { this.bot = bot; this.pluginDir = bot.pluginDir; this.configPath = bot.configPath; this.builtInPlugins = bot.builtInPlugins; // 初始化内置插件集合 // 注册TypeScript编译器 registerTSNode(); this.config = bot.config_json; // 监听 config.json 文件变化 this.startWatchingConfig(); } loadConfig() { try { return JSON.parse(fs.readFileSync(this.configPath, 'utf8')); } catch (error) { index_1.logger.error('❌ Failed to load config', error); return { plugins: [] }; } } saveConfig() { try { fs.writeFileSync(this.configPath, JSON.stringify(this.config, null, 2)); } catch (error) { index_1.logger.error('❌ Failed to save config:', error); } } async loadAllPlugins() { await Promise.all(this.config.plugins.map(async (pluginName) => { const pluginEntry = this.findPluginEntry(pluginName); if (!pluginEntry) { index_1.logger.error(`❌ Plugin ${pluginName} not found in ${this.pluginDir}`); return; } await this.loadPlugin(pluginEntry.dirPath, pluginEntry.entryFile); })); } findPluginEntry(pluginName) { const pluginDir = path.join(this.pluginDir, pluginName); if (!fs.existsSync(pluginDir)) return null; // const entryFile = ['index.ts', 'index.js'] // .find(file => fs.existsSync(path.join(pluginDir, file))); // return entryFile ? { // dirPath: pluginDir, // entryFile: path.join(pluginDir, entryFile) // } : null; // 支持直接加载TS文件 const entryFile = ['index.ts', 'index.js'] .map(file => path.join(pluginDir, file)) .find(fs.existsSync); return entryFile ? { dirPath: pluginDir, entryFile: entryFile } : null; } async loadPlugin(pluginDir, entryFile) { try { const resolvedPath = require.resolve(entryFile); delete require.cache[resolvedPath]; // const pluginModule: PluginModule = await import(entryFile); const pluginModule = require(entryFile); if (!pluginModule.name) { index_1.logger.warn(`⚠️ Plugin must export 'name' constant: ${entryFile}`); this.bot.client.send_private_msg({ user_id: this.bot.master, message: [index_1.Structs.text(`⚠️ Plugin must export 'name' constant: ${entryFile}`)] }); } const pluginName = pluginModule.name; if (this.plugins.has(pluginName)) { await this.unloadPlugin(pluginName); } // 验证插件名与目录名的一致性 const expectedDirName = path.basename(pluginDir); if (path.basename(pluginDir) !== pluginName) { index_1.logger.warn(`⚠️ Plugin name '${pluginName}' does not match directory name '${expectedDirName}'`); } let handlers = {}; if (pluginModule.handlers) { handlers = pluginModule.handlers; } else if (pluginModule.setup) { handlers = pluginModule.setup(this.bot); } else { index_1.logger.error(`Plugin ${pluginName} must export "handlers" or "setup"`); this.bot.client.send_private_msg({ user_id: this.bot.master, message: [index_1.Structs.text(`Plugin ${pluginName} must export "handlers" or "setup"`)] }); } const registeredHandlers = []; for (const [event, eventHandlers] of Object.entries(handlers)) { for (const handler of eventHandlers) { this.bot.on(event, handler); registeredHandlers.push({ event, handler }); } } this.plugins.set(pluginName, { dirPath: pluginDir, entryFile, handlers: registeredHandlers }); index_1.logger.info(`✅ Plugin ${pluginName} loaded from ${path.relative(this.pluginDir, pluginDir)}`); } catch (error) { index_1.logger.error(`❌ Failed to load plugin from ${entryFile}:`, error); } } async unloadPlugin(pluginName) { const plugin = this.plugins.get(pluginName); if (plugin) { for (const { event, handler } of plugin.handlers) { this.bot.off(event, handler); } this.plugins.delete(pluginName); index_1.logger.info(`✅ Plugin ${pluginName} unloaded`); } } async enablePlugin(pluginName) { if (this.builtInPlugins.has(pluginName)) { index_1.logger.warn(`❌ Built-in plugin ${pluginName} cannot be enabled.`); return; } const pluginEntry = this.findPluginEntry(pluginName); if (!pluginEntry) { index_1.logger.error(`❌ Plugin ${pluginName} not found in ${this.pluginDir}`); this.bot.client.send_private_msg({ user_id: this.bot.master, message: [index_1.Structs.text(`❌ Plugin ${pluginName} not found in ${this.pluginDir}`)] }); return; } const wasEnabled = this.config.plugins.includes(pluginName); if (!wasEnabled) { this.config.plugins.push(pluginName); this.saveConfig(); } try { await this.loadPlugin(pluginEntry.dirPath, pluginEntry.entryFile); } catch (error) { if (!wasEnabled) { const index = this.config.plugins.indexOf(pluginName); if (index !== -1) { this.config.plugins.splice(index, 1); this.saveConfig(); } } index_1.logger.error(`❌ Failed to enable plugin ${pluginName}:`, error); this.bot.client.send_private_msg({ user_id: this.bot.master, message: [index_1.Structs.text(`❌ Failed to enable plugin ${pluginName}:${error}`)] }); } } async disablePlugin(pluginName) { if (this.builtInPlugins.has(pluginName)) { index_1.logger.warn(`❌ Built-in plugin ${pluginName} cannot be disabled.`); return; } const index = this.config.plugins.indexOf(pluginName); if (index !== -1) { this.config.plugins.splice(index, 1); this.saveConfig(); } await this.unloadPlugin(pluginName); } async reloadPlugin(pluginName) { if (this.builtInPlugins.has(pluginName)) { index_1.logger.warn(`❌ Built-in plugin ${pluginName} cannot be reloaded.`); return; } const plugin = this.plugins.get(pluginName); if (!plugin) { index_1.logger.error(`Plugin ${pluginName} not loaded`); this.bot.client.send_private_msg({ user_id: this.bot.master, message: [index_1.Structs.text(`Plugin ${pluginName} not loaded`)] }); return; } await this.unloadPlugin(pluginName); await this.loadPlugin(plugin.dirPath, plugin.entryFile); } startWatching() { const watcher = (0, chokidar_1.watch)(this.pluginDir, { ignored: /(^|[\/\\])\../, persistent: true, ignoreInitial: true, depth: 2 }); watcher.on('change', async (changedPath) => { // 从文件路径推导插件目录 const relativePath = path.relative(this.pluginDir, changedPath); const pluginDirName = relativePath.split(path.sep)[0]; const pluginEntry = this.findPluginEntry(pluginDirName); if (pluginEntry && this.config.plugins.includes(pluginDirName)) { index_1.logger.info(`🔄 Detected changes in ${pluginDirName}`); await this.reloadPlugin(pluginDirName); } }); } // 监听 config.json 文件变化 startWatchingConfig() { const configWatcher = (0, chokidar_1.watch)(this.configPath, { persistent: true, ignoreInitial: true, }); configWatcher.on('change', async () => { index_1.logger.info('🔄 Detected changes in bot.config.json'); const newConfig = this.loadConfig(); const oldPlugins = new Set(this.config.plugins); const newPlugins = new Set(newConfig.plugins); // 找出新增的插件 const addedPlugins = [...newPlugins].filter(plugin => !oldPlugins.has(plugin)); // 找出移除的插件 const removedPlugins = [...oldPlugins].filter(plugin => !newPlugins.has(plugin)); // 启用新增的插件 for (const plugin of addedPlugins) { try { await this.enablePlugin(plugin); index_1.logger.info(`✅ Enabled plugin: ${plugin}`); } catch (error) { index_1.logger.error(`❌ Failed to enable plugin ${plugin}:`, error); } } // 禁用移除的插件 for (const plugin of removedPlugins) { try { await this.disablePlugin(plugin); index_1.logger.info(`✅ Disabled plugin: ${plugin}`); } catch (error) { index_1.logger.error(`❌ Failed to disable plugin ${plugin}:`, error); } } // 更新当前配置 this.config = newConfig; }); } } exports.PluginManager = PluginManager;