diff --git a/packages/napcat-onebot/action/index.ts b/packages/napcat-onebot/action/index.ts index 22fc68b6..a8ba3485 100644 --- a/packages/napcat-onebot/action/index.ts +++ b/packages/napcat-onebot/action/index.ts @@ -67,6 +67,8 @@ import GoCQHTTPUploadPrivateFile from './go-cqhttp/UploadPrivateFile'; import { FetchEmojiLike } from './extends/FetchEmojiLike'; import { NapCatCore } from 'napcat-core'; import { NapCatOneBot11Adapter } from '@/napcat-onebot/index'; +import type { NetworkAdapterConfig } from '../config/config'; +import { OneBotAction } from './OneBotAction'; import { SetInputStatus } from './extends/SetInputStatus'; import { GetCSRF } from './system/GetCSRF'; import { DelGroupNotice } from './group/DelGroupNotice'; @@ -322,6 +324,30 @@ export function createActionMap (obContext: NapCatOneBot11Adapter, core: NapCatC function get (key: K): MapType[K] | undefined { return _map.get(key as keyof MapType) as MapType[K] | undefined; } - return { get }; + + /** + * 类型安全的 action 调用辅助函数 + * 根据 action 名称自动推导返回类型 + */ + async function call ( + actionName: K, + params: unknown, + adapter: string, + config: NetworkAdapterConfig + ): Promise ? R : never> { + const action = _map.get(actionName); + if (!action) { + throw new Error(`Action ${String(actionName)} not found`); + } + + const result = await (action as any).handle(params, adapter, config); + if (result.status !== 'ok' || !result.data) { + throw new Error(`Action ${String(actionName)} failed: ${result.message || 'No data returned'}`); + } + + return result.data; + } + + return { get, call }; } export type ActionMap = ReturnType; diff --git a/packages/napcat-plugin-builtin/index.ts b/packages/napcat-plugin-builtin/index.ts new file mode 100644 index 00000000..71a60562 --- /dev/null +++ b/packages/napcat-plugin-builtin/index.ts @@ -0,0 +1,84 @@ +import type { ActionMap } from 'napcat-onebot/action'; +import { EventType } from 'napcat-onebot/event/OneBotEvent'; +import type { PluginModule } from 'napcat-onebot/network/plugin'; +import type { OB11Message, OB11PostSendMsg } from 'napcat-onebot/types/message'; + +let actions: ActionMap | undefined = undefined; + +/** + * 插件初始化 + */ +const plugin_init: PluginModule['plugin_init'] = async (_core, _obContext, _actions, _instance) => { + console.log('[Plugin: builtin] NapCat 内置插件已初始化'); + actions = _actions; +}; + +/** + * 消息处理 + * 当收到包含 #napcat 的消息时,回复版本信息 + */ +const plugin_onmessage: PluginModule['plugin_onmessage'] = async (adapter, _core, _obCtx, event, _actions, instance) => { + if (event.post_type !== EventType.MESSAGE || !event.raw_message.startsWith('#napcat')) { + return; + } + + try { + const versionInfo = await getVersionInfo(adapter, instance.config); + if (!versionInfo) return; + + const message = formatVersionMessage(versionInfo); + await sendMessage(event, message, adapter, instance.config); + + console.log('[Plugin: builtin] 已回复版本信息'); + } catch (error) { + console.error('[Plugin: builtin] 处理消息时发生错误:', error); + } +}; + +/** + * 获取版本信息(完美的类型推导,无需 as 断言) + */ +async function getVersionInfo (adapter: string, config: any) { + if (!actions) return null; + + try { + const data = await actions.call('get_version_info', void 0, adapter, config); + return { + appName: data.app_name, + appVersion: data.app_version, + protocolVersion: data.protocol_version, + }; + } catch (error) { + console.error('[Plugin: builtin] 获取版本信息失败:', error); + return null; + } +} + +/** + * 格式化版本信息消息 + */ +function formatVersionMessage (info: { appName: string; appVersion: string; protocolVersion: string; }) { + return `NapCat 信息\n版本: ${info.appVersion}\n平台: ${process.platform}${process.arch === 'x64' ? ' (64-bit)' : ''}`; +} + +/** + * 发送消息(完美的类型推导) + */ +async function sendMessage (event: OB11Message, message: string, adapter: string, config: any) { + if (!actions) return; + + const params: OB11PostSendMsg = { + message, + message_type: event.message_type, + ...(event.message_type === 'group' && event.group_id ? { group_id: String(event.group_id) } : {}), + ...(event.message_type === 'private' && event.user_id ? { user_id: String(event.user_id) } : {}), + }; + + try { + await actions.call('send_msg', params, adapter, config); + } catch (error) { + console.error('[Plugin: builtin] 发送消息失败:', error); + } +} + +export { plugin_init, plugin_onmessage, actions }; diff --git a/packages/napcat-plugin-builtin/package.json b/packages/napcat-plugin-builtin/package.json new file mode 100644 index 00000000..68120f79 --- /dev/null +++ b/packages/napcat-plugin-builtin/package.json @@ -0,0 +1,16 @@ +{ + "name": "napcat-plugin-builtin", + "version": "1.0.0", + "type": "module", + "main": "index.mjs", + "description": "NapCat 内置插件", + "dependencies": { + "napcat-onebot": "workspace:*" + }, + "devDependencies": { + "@types/node": "^22.0.1" + }, + "scripts": { + "build": "vite build" + } +} \ No newline at end of file diff --git a/packages/napcat-plugin-builtin/tsconfig.json b/packages/napcat-plugin-builtin/tsconfig.json new file mode 100644 index 00000000..48d3a03b --- /dev/null +++ b/packages/napcat-plugin-builtin/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.base.json", + "include": [ + "*.ts", + "**/*.ts" + ], + "exclude": [ + "node_modules", + "dist" + ] +} \ No newline at end of file diff --git a/packages/napcat-plugin-builtin/vite.config.ts b/packages/napcat-plugin-builtin/vite.config.ts new file mode 100644 index 00000000..db6bed35 --- /dev/null +++ b/packages/napcat-plugin-builtin/vite.config.ts @@ -0,0 +1,77 @@ +import { defineConfig } from 'vite'; +import { resolve } from 'path'; +import nodeResolve from '@rollup/plugin-node-resolve'; +import { builtinModules } from 'module'; +import fs from 'fs'; + +const nodeModules = [...builtinModules, builtinModules.map((m) => `node:${m}`)].flat(); + +// 构建后拷贝插件 +function copyToShellPlugin () { + return { + name: 'copy-to-shell', + closeBundle () { + try { + const sourceDir = resolve(__dirname, 'dist'); + const targetDir = resolve(__dirname, '../napcat-shell/dist/plugins/builtin'); + const packageJsonSource = resolve(__dirname, 'package.json'); + + // 确保目标目录存在 + if (!fs.existsSync(targetDir)) { + fs.mkdirSync(targetDir, { recursive: true }); + console.log(`[copy-to-shell] Created directory: ${targetDir}`); + } + + // 拷贝 dist 目录下的所有文件 + const files = fs.readdirSync(sourceDir); + let copiedCount = 0; + + files.forEach(file => { + const sourcePath = resolve(sourceDir, file); + const targetPath = resolve(targetDir, file); + + if (fs.statSync(sourcePath).isFile()) { + fs.copyFileSync(sourcePath, targetPath); + copiedCount++; + } + }); + + // 拷贝 package.json + if (fs.existsSync(packageJsonSource)) { + const packageJsonTarget = resolve(targetDir, 'package.json'); + fs.copyFileSync(packageJsonSource, packageJsonTarget); + copiedCount++; + } + + console.log(`[copy-to-shell] Successfully copied ${copiedCount} file(s) to ${targetDir}`); + } catch (error) { + console.error('[copy-to-shell] Failed to copy files:', error); + throw error; + } + }, + }; +} + +export default defineConfig({ + resolve: { + conditions: ['node', 'default'], + alias: { + '@/napcat-core': resolve(__dirname, '../napcat-core'), + '@': resolve(__dirname, '../'), + }, + }, + build: { + sourcemap: false, + target: 'esnext', + minify: false, + lib: { + entry: 'index.ts', + formats: ['es'], + fileName: () => 'index.mjs', + }, + rollupOptions: { + external: [...nodeModules], + }, + }, + plugins: [nodeResolve(), copyToShellPlugin()], +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e3307f78..8b0ffbe4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -220,6 +220,16 @@ importers: specifier: ^22.0.1 version: 22.19.1 + packages/napcat-plugin-builtin: + dependencies: + napcat-onebot: + specifier: workspace:* + version: link:../napcat-onebot + devDependencies: + '@types/node': + specifier: ^22.0.1 + version: 22.19.1 + packages/napcat-protobuf: dependencies: '@protobuf-ts/runtime':