From 0aa0c446342afdd5fc16735cc676f8b97d25c5ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Wed, 28 Jan 2026 15:02:47 +0800 Subject: [PATCH] Refactor plugin identification to use package name and dirname Updated plugin manager and API to distinguish between plugin package name and directory name (dirname) for more robust plugin identification and path resolution. Adjusted context creation, status management, and API handlers to use package name for identification and dirname for filesystem operations. Also replaced console.error with console.log in builtin plugin for consistency. --- package/package.json | 24 ---------- .../napcat-onebot/network/plugin-manger.ts | 42 ++++++++++-------- packages/napcat-plugin-builtin/index.ts | 4 +- .../napcat-webui-backend/src/api/Plugin.ts | 44 +++++++++---------- 4 files changed, 46 insertions(+), 68 deletions(-) delete mode 100644 package/package.json diff --git a/package/package.json b/package/package.json deleted file mode 100644 index 3780b4c8..00000000 --- a/package/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "napcat-types", - "version": "0.0.1", - "type": "module", - "types": "./napcat-types/index.d.ts", - "files": [ - "**/*" - ], - "dependencies": { - "@types/node": "^22.10.7", - "@types/express": "^4.17.21", - "@types/ws": "^8.5.12", - "@types/cors": "^2.8.17", - "@types/multer": "^1.4.12", - "@types/winston": "^2.4.4", - "@types/yaml": "^1.9.7", - "@types/ip": "^1.1.3" - }, - "publishConfig": { - "registry": "https://registry.npmjs.org/", - "access": "public", - "tag": "latest" - } -} \ No newline at end of file diff --git a/packages/napcat-onebot/network/plugin-manger.ts b/packages/napcat-onebot/network/plugin-manger.ts index a83d4ee7..99a26ef3 100644 --- a/packages/napcat-onebot/network/plugin-manger.ts +++ b/packages/napcat-onebot/network/plugin-manger.ts @@ -88,6 +88,7 @@ export interface PluginModule { } const plugin: LoadedPlugin = { - name: pluginId, // Use Directory Name as Internal Plugin Name/ID + name: packageJson?.name || pluginId, // Use package.json name for API lookups, fallback to dir name + dirname: pluginId, // Keep track of actual directory name for path resolution version: packageJson?.version, pluginPath: pluginDir, entryPath, @@ -325,15 +327,15 @@ export class OB11PluginMangerAdapter extends IOB11NetworkAdapter { } // Create Context - const id = plugin.name; // directory name as ID - const dataPath = path.join(this.pluginPath, id, 'data'); + // Use dirname for path resolution, name for identification + const dataPath = path.join(this.pluginPath, plugin.dirname, 'data'); const configPath = path.join(dataPath, 'config.json'); const context: NapCatPluginContext = { core: this.core, oneBot: this.obContext, actions: this.actions, - pluginName: id, + pluginName: plugin.name, // Use package name for identification pluginPath: plugin.pluginPath, dataPath: dataPath, configPath: configPath, @@ -404,20 +406,18 @@ export class OB11PluginMangerAdapter extends IOB11NetworkAdapter { } public setPluginStatus (pluginName: string, enable: boolean): void { + // Try to find plugin by package name first + const plugin = this.loadedPlugins.get(pluginName); + // Use dirname for config storage (if plugin is loaded), otherwise assume pluginName is dirname + const configKey = plugin?.dirname || pluginName; + const config = this.loadPluginConfig(); - config[pluginName] = enable; + config[configKey] = enable; this.savePluginConfig(config); - if (!enable) { - for (const [_, loaded] of this.loadedPlugins.entries()) { - const dirOrFile = path.basename(loaded.pluginPath === this.pluginPath ? loaded.entryPath : loaded.pluginPath); - const ext = path.extname(dirOrFile); - const simpleName = ext ? path.parse(dirOrFile).name : dirOrFile; - - if (pluginName === simpleName) { - this.unloadPlugin(loaded.name).catch(e => this.logger.logError('Error unloading', e)); - } - } + if (!enable && plugin) { + // Unload by plugin.name (package name, which is the key in loadedPlugins) + this.unloadPlugin(plugin.name).catch(e => this.logger.logError('Error unloading', e)); } } @@ -539,13 +539,14 @@ export class OB11PluginMangerAdapter extends IOB11NetworkAdapter { return false; } + const dirname = plugin.dirname; + try { // 卸载插件 await this.unloadPlugin(pluginName); - // 重新加载插件 - // We assume pluginName IS the directory name (the ID) - await this.loadDirectoryPlugin(pluginName); + // 重新加载插件 - use dirname for directory loading + await this.loadDirectoryPlugin(dirname); this.logger.log( `[Plugin Adapter] Plugin ${pluginName} reloaded successfully` @@ -560,7 +561,10 @@ export class OB11PluginMangerAdapter extends IOB11NetworkAdapter { } } public getPluginDataPath (pluginName: string): string { - return path.join(this.pluginPath, pluginName, 'data'); + // Lookup plugin by name (package name) and use dirname for path + const plugin = this.loadedPlugins.get(pluginName); + const dirname = plugin?.dirname || pluginName; // fallback to pluginName if not found + return path.join(this.pluginPath, dirname, 'data'); } public getPluginConfigPath (pluginName: string): string { diff --git a/packages/napcat-plugin-builtin/index.ts b/packages/napcat-plugin-builtin/index.ts index 82e973de..35c21fd3 100644 --- a/packages/napcat-plugin-builtin/index.ts +++ b/packages/napcat-plugin-builtin/index.ts @@ -97,7 +97,7 @@ const plugin_onmessage: PluginModule['plugin_onmessage'] = async (_ctx, event) = console.log('[Plugin: builtin] 已回复版本信息'); } catch (error) { - console.error('[Plugin: builtin] 处理消息时发生错误:', error); + console.log('[Plugin: builtin] 处理消息时发生错误:', error); } }; @@ -150,7 +150,7 @@ async function sendMessage (actions: ActionMap, event: OB11Message, message: str try { await actions.call('send_msg', params, adapter, config); } catch (error) { - console.error('[Plugin: builtin] 发送消息失败:', error); + console.log('[Plugin: builtin] 发送消息失败:', error); } } diff --git a/packages/napcat-webui-backend/src/api/Plugin.ts b/packages/napcat-webui-backend/src/api/Plugin.ts index 221dd703..653855fe 100644 --- a/packages/napcat-webui-backend/src/api/Plugin.ts +++ b/packages/napcat-webui-backend/src/api/Plugin.ts @@ -33,21 +33,19 @@ export const GetPluginListHandler: RequestHandler = async (_req, res) => { // 1. 整理已加载的插件 for (const p of loadedPlugins) { - // 计算 ID:需要回溯到加载时的入口信息 - // 对于已加载的插件,我们通过判断 pluginPath 是否等于根 pluginPath 来判断它是单文件还是目录 - const isFilePlugin = p.pluginPath === pluginManager.getPluginPath(); - const fsName = isFilePlugin ? path.basename(p.entryPath) : path.basename(p.pluginPath); - const id = getPluginId(fsName, isFilePlugin); + // Use dirname for map key (matches filesystem scan) + const id = p.dirname; + const fsName = p.dirname; // dirname is the actual filesystem directory name loadedPluginMap.set(id, { - name: p.packageJson?.name || p.name, // 优先使用 package.json 的 name + name: p.name, // This is now package name (from packageJson.name || dirname) id: id, version: p.version || '0.0.0', description: p.packageJson?.description || '', author: p.packageJson?.author || '', status: 'active', filename: fsName, // 真实文件/目录名 - loadedName: p.name, // 运行时注册的名称,用于重载/卸载 + loadedName: p.name, // 运行时注册的名称,用于重载/卸载 (package name) hasConfig: !!(p.module.plugin_config_schema || p.module.plugin_config_ui) }); } @@ -121,40 +119,40 @@ export const GetPluginListHandler: RequestHandler = async (_req, res) => { // ReloadPluginHandler removed export const SetPluginStatusHandler: RequestHandler = async (req, res) => { - const { enable, filename } = req.body; - // We Use filename / id to control config - // Front-end should pass the 'filename' or 'id' as the key identifier + const { enable, filename, name } = req.body; + // filename is the directory name (used for fs checks) + // name is the package name (used for plugin manager API, if provided) + // We need to determine: which to use for setPluginStatus call - if (!filename) return sendError(res, 'Plugin Filename/ID is required'); + if (!filename && !name) return sendError(res, 'Plugin Filename or Name is required'); const pluginManager = getPluginManager(); if (!pluginManager) { return sendError(res, 'Plugin Manager not found'); } - // ID IS the filename (directory name) now - const id = filename; + // Determine which ID to use + // If 'name' (package name) is provided, use it for pluginManager calls + // But 'filename' (dirname) is needed for filesystem operations + const dirname = filename || name; // fallback + const pluginName = name || filename; // fallback try { - pluginManager.setPluginStatus(id, enable); + // setPluginStatus now handles both package name and dirname lookup internally + pluginManager.setPluginStatus(pluginName, enable); // If enabling, trigger load if (enable) { const pluginPath = pluginManager.getPluginPath(); - const fullPath = path.join(pluginPath, filename); + const fullPath = path.join(pluginPath, dirname); if (fs.existsSync(fullPath) && fs.statSync(fullPath).isDirectory()) { - await pluginManager.loadDirectoryPlugin(filename); + await pluginManager.loadDirectoryPlugin(dirname); } else { - return sendError(res, 'Plugin directory not found: ' + filename); + return sendError(res, 'Plugin directory not found: ' + dirname); } - } else { - // Disabling behavior is managed by setPluginStatus which updates config - // If we want to unload immediately? - // The config update will prevent load on next startup. - // Does pluginManager.setPluginStatus unload it? - // Inspecting pluginManager.setPluginStatus... it iterates and unloads if match found. } + // Disabling is handled by setPluginStatus return sendSuccess(res, { message: 'Status updated successfully' }); } catch (e: any) {