Plugins.ts 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
  1. import botlogger from "./logger.js";
  2. import { promises as fsPromises } from 'fs';
  3. import { HtmlImg } from "./Puppeteer.js";
  4. import type {
  5. GroupMessage,
  6. PrivateFriendMessage,
  7. PrivateGroupMessage
  8. } from 'node-napcat-ts/dist/Interfaces.js';
  9. import * as cron from 'node-cron';
  10. import 'reflect-metadata';
  11. import { Command, ParamMetadata, Plugin, } from '../interface/plugin.js';
  12. import * as fs from 'fs'
  13. import * as path from 'path'
  14. // 获取指令前缀
  15. import { Botconfig as config, economy, load, PermissionConfig, saveConfig } from './config.js'
  16. import { ImageSegment, Receive, ReplySegment, TextSegment } from "node-napcat-ts/dist/Structs.js";
  17. import { fileURLToPath } from 'node:url';
  18. import { qqBot } from "../app.js";
  19. import { IsPermission } from "./Permission.js";
  20. import { download } from "./download.js";
  21. import { commandList, economyCommands, paramMetadata } from "./decorators.js";
  22. import { addCoins, removeCoins } from "./economy.js";
  23. import { url } from "node:inspector";
  24. //WSSendParam
  25. const CMD_PREFIX = config?.cmd?.prefix ?? '#';
  26. // 导出装饰器
  27. // export { param, ParamType };
  28. // export const plugins = pluginsDecorator;
  29. export { config }; // 导出配置对象
  30. // 创建消息工厂函数
  31. function createTextMessage(text: string): TextSegment {
  32. return {
  33. type: "text",
  34. data: {
  35. text: text,
  36. }
  37. };
  38. }
  39. function createImageMessage(base64Data: string): ImageSegment {
  40. return {
  41. type: "image",
  42. data: {
  43. file: `base64://${base64Data}`,
  44. }
  45. };
  46. }
  47. function createReplyMessage(messageId: number | string): ReplySegment {
  48. return {
  49. type: "reply",
  50. data: {
  51. id: String(messageId)
  52. }
  53. };
  54. }
  55. // 修改插件查找函数
  56. function findPlugin(pluginId: string): Plugin | undefined {
  57. return commandList.find((p: Plugin) => p.id === pluginId);
  58. }
  59. function findeasycmdPlugin(commandId: string): Plugin | undefined {
  60. return commandList.find((p: Plugin) => p.config.easycmd === true && p.commands.find((c: Command) => c.cmd === commandId || c.aliases?.includes(commandId)));
  61. }
  62. // 修改命令查找函数
  63. function findCommand(plugin: Plugin, cmdName: string): Command | undefined {
  64. return plugin.commands.find((cmd: Command) => {
  65. // 从完整命令中提取命令名
  66. const cmdParts = cmd.cmd.split(/\s+/);
  67. const matchCmd = cmdParts[cmdParts.length - 1] === cmdName;
  68. // 检查别名
  69. const matchAlias = cmd.aliases?.some((alias: string) => {
  70. const aliasParts = alias.split(/\s+/);
  71. return aliasParts[aliasParts.length - 1] === cmdName;
  72. });
  73. return matchCmd || matchAlias;
  74. });
  75. }
  76. // 添加插件加载函数
  77. async function loadPlugins(): Promise<void> {
  78. try {
  79. const __dirname = path.dirname(fileURLToPath(import.meta.url));
  80. const pluginsDir = path.join(__dirname, '..', 'plugins');
  81. // 删除require缓存相关代码
  82. const files = await fsPromises.readdir(pluginsDir);
  83. for (const file of files) {
  84. if (file.endsWith('.ts') && file !== 'index.ts') {
  85. const filePath = path.join(pluginsDir, file);
  86. try {
  87. // 使用ESM动态导入并添加时间戳防止缓存
  88. const module = await import(`${filePath}?t=${Date.now()}`);
  89. const pluginClasses = Object.values(module).filter(
  90. value => typeof value === 'function' && value.prototype?.plugincfg
  91. );
  92. for (const PluginClass of pluginClasses) {
  93. const instance = new (PluginClass as any)();
  94. const pluginConfig = instance.constructor.prototype.plugincfg;
  95. if (pluginConfig) {
  96. const plugin: Plugin = {
  97. id: pluginConfig.id,
  98. name: pluginConfig.name,
  99. commands: [] as Command[],
  100. class: instance.constructor,
  101. version: pluginConfig.version,
  102. author: pluginConfig.author,
  103. describe: pluginConfig.describe,
  104. config: pluginConfig
  105. };
  106. // 触发装饰器
  107. await initializePluginCommands(instance);
  108. // 初始化定时任务
  109. await initializeScheduledTasks(instance);
  110. }
  111. }
  112. } catch (error: any) {
  113. botlogger.error(`加载插件文件失败 ${file}:`+ error.message);
  114. }
  115. }
  116. }
  117. } catch (error) {
  118. botlogger.error("加载插件目录失败:", error);
  119. }
  120. }
  121. // 初始化插件命令
  122. async function initializePluginCommands(instance: any): Promise<void> {
  123. const methods = Object.getOwnPropertyNames(instance.constructor.prototype)
  124. .filter(name => name !== 'constructor');
  125. for (const methodName of methods) {
  126. const method = instance.constructor.prototype[methodName];
  127. try {
  128. if (typeof method === 'function') {
  129. method.call(instance, '', '');
  130. }
  131. } catch (error) {
  132. if (error instanceof TypeError && error.message.includes('Cannot read properties of undefined')) {
  133. continue;
  134. }
  135. botlogger.error(`触发装饰器时出错: ${error instanceof Error ? error.message : '未知错误'}`);
  136. }
  137. }
  138. }
  139. // 初始化定时任务
  140. async function initializeScheduledTasks(instance: any): Promise<void> {
  141. const methods = Object.getOwnPropertyNames(instance.constructor.prototype)
  142. .filter(name => name !== 'constructor');
  143. for (const methodName of methods) {
  144. const method = instance.constructor.prototype[methodName];
  145. if (method.isScheduled) {
  146. try {
  147. // 创建定时任务
  148. cron.schedule(method.cron, async () => {
  149. try {
  150. await method.call(instance);
  151. } catch (error) {
  152. botlogger.error(`执行定时任务失败 [${methodName}]:`, error);
  153. }
  154. });
  155. botlogger.info(`注册定时任务 [${methodName}]: ${method.cron}`);
  156. } catch (error) {
  157. botlogger.error(`注册定时任务失败 [${methodName}]:`, error);
  158. }
  159. }
  160. }
  161. }
  162. // 修改 runplugins 函数
  163. export async function runplugins() {
  164. try {
  165. // 清理旧实例
  166. commandList.forEach(plugin => {
  167. plugin.commands.forEach(cmd => {
  168. // 检查 cmd.fn 是否存在,并且是否有 close 方法
  169. if (cmd.fn && typeof (cmd.fn as any).close === 'function') {
  170. (cmd.fn as any).close();
  171. }
  172. });
  173. });
  174. // 清空现有命令列表
  175. commandList.length = 0;
  176. // 注册插件
  177. botlogger.info("开始注册插件...");
  178. // 自动加载插件
  179. await loadPlugins();
  180. // 设置消息处理器
  181. qqBot.on('message', async (context) => {
  182. try {
  183. if (context.message[0].type !== 'text') {
  184. return;
  185. }
  186. const msg = context.message[0].data.text || '';
  187. if (msg == CMD_PREFIX) {
  188. context.quick_action([{
  189. type: 'text',
  190. data: {
  191. text: "可用的命令有:\n" +
  192. commandList.map(p => `${CMD_PREFIX}${p.id}: ${p.name}`).join('\n') +
  193. "\n\n" +
  194. `使用 “${CMD_PREFIX}插件名 help” 或 “${CMD_PREFIX}插件名 ?” 查询命令的详情 ^_^`
  195. }
  196. }]);
  197. return;
  198. }
  199. botlogger.info('收到消息:' + context.message[0].data.text);
  200. // 检查是否是命令
  201. if (!msg.startsWith(CMD_PREFIX)) {
  202. return;
  203. }
  204. // 解析命令
  205. const parts = msg.slice(CMD_PREFIX.length).trim().split(/\s+/);
  206. const pluginId = parts[0];
  207. const cmdName = parts[1];
  208. const args = parts.slice(2);
  209. botlogger.info('尝试匹配插件:', pluginId);
  210. // 显示可用插件
  211. botlogger.info('可用插件:');
  212. commandList.forEach(p => {
  213. botlogger.info(` [${p.id}]: ${p.name}`);
  214. });
  215. // 查找插件
  216. const plugin = findPlugin(pluginId);
  217. if (!plugin) {
  218. botlogger.info(`插件未找到: ${pluginId}`);
  219. //尝试easycmd开启的插件
  220. const easyplugin = findeasycmdPlugin(pluginId)
  221. if (!easyplugin) {
  222. botlogger.info(`插件未找到: ${pluginId}`);
  223. return;
  224. }
  225. let command: Command | undefined = undefined;
  226. command = findCommand(easyplugin, pluginId);
  227. if (!command) {
  228. botlogger.info(`命令未找到: ${pluginId}`);
  229. return;
  230. }
  231. //指令权限检查
  232. if (context.message_type === 'private') {
  233. if (!await IsPermission(context.user_id, easyplugin.id, command.fnName, false)) {
  234. botlogger.info(`[${context.user_id}]无权限执行命令: ${CMD_PREFIX}${easyplugin.id} ${command.cmd}`);
  235. context.quick_action([{
  236. type: 'text',
  237. data: { text: `你没有权限执行此命令` }
  238. }]);
  239. return;
  240. }
  241. }
  242. if (context.message_type === 'group') {
  243. if (!await IsPermission(context.sender.user_id, easyplugin.id, command.fnName,true)) {
  244. botlogger.info(`[${context.group_id}]无权限执行命令: ${CMD_PREFIX}${easyplugin.id} ${command.cmd}`);
  245. context.quick_action([{
  246. type: 'text',
  247. data: { text: `你没有权限执行此命令` }
  248. }])
  249. return;
  250. }
  251. }
  252. console.log(JSON.stringify(context))
  253. qqBot.set_msg_emoji_like({
  254. message_id: context.message_id,
  255. set: true,
  256. emoji_id: '124'
  257. })
  258. await handleCommand(context, easyplugin, command, parts.slice(1), true);
  259. return;
  260. }
  261. botlogger.info(`找到插件[${plugin.id}]: ${plugin.name}`);
  262. // 显示可用命令
  263. botlogger.info('可用命令:');
  264. plugin.commands.forEach(cmd => {
  265. botlogger.info(`${CMD_PREFIX}${plugin.id} ${cmd.cmd}`);
  266. });
  267. let command: Command | undefined = undefined;
  268. if (cmdName == undefined) {
  269. botlogger.info(`未检测到第二个参数,运行默认命令: ${plugin.config.defaultCommandId}`);
  270. const commandId = plugin.config.defaultCommandId ?? "help";
  271. command = findCommand(plugin, commandId);
  272. } else {
  273. command = findCommand(plugin, cmdName);
  274. }
  275. if (!command) {
  276. botlogger.info(`命令未找到: ${cmdName}`);
  277. return;
  278. }
  279. botlogger.info(`找到命令: ${CMD_PREFIX}${plugin.id} ${command.cmd}`);
  280. //指令权限检查
  281. if (context.message_type === 'private') {
  282. if (!await IsPermission(context.user_id, plugin.id, command.fnName,false)) {
  283. botlogger.info(`[${context.user_id}]无权限执行命令: ${CMD_PREFIX}${plugin.id} ${command.cmd}`);
  284. context.quick_action([{
  285. type: 'text',
  286. data: { text: `你没有权限执行此命令` }
  287. }]);
  288. return;
  289. }
  290. }
  291. if (context.message_type === 'group') {
  292. if (!await IsPermission(context.sender.user_id, plugin.id, command.fnName, true)) {
  293. botlogger.info(`[${context.group_id}]无权限执行命令: ${CMD_PREFIX}${plugin.id} ${command.cmd}`);
  294. context.quick_action([{
  295. type: 'text',
  296. data: { text: `你没有权限执行此命令` }
  297. }])
  298. return;
  299. }
  300. }
  301. // 执行命令
  302. qqBot.set_msg_emoji_like({
  303. message_id: context.message_id,
  304. set: true,
  305. emoji_id: '124'
  306. })
  307. await handleCommand(context, plugin, command, args);
  308. } catch (error) {
  309. botlogger.error("处理消息时出错:", error);
  310. await context.quick_action([{
  311. type: 'text',
  312. data: { text: `处理消息时出错: ${error instanceof Error ? error.message : '未知错误'}` }
  313. }]);
  314. }
  315. });
  316. botlogger.info("插件注册完成");
  317. botlogger.info("命令表:");
  318. for (const plugin of commandList) {
  319. botlogger.info(`[${plugin.id}]:`);
  320. for (const cmd of plugin.commands) {
  321. botlogger.info(` ${CMD_PREFIX}${plugin.id} ${cmd.cmd}`);
  322. }
  323. }
  324. } catch (error) {
  325. botlogger.error("注册插件时出错:", error);
  326. }
  327. }
  328. // 修改 handleCommand 函数
  329. async function handleCommand(context: PrivateFriendMessage | PrivateGroupMessage | GroupMessage, plugin: Plugin, command: Command, args: string[],easycmd:boolean=false): Promise<void> {
  330. try {
  331. // 解析参数 - 传入完整消息文本
  332. if (!context.message[0].type || context.message[0].type !== 'text') {
  333. throw new Error('消息内容为空');
  334. }
  335. const message = context.message[0].data.text || '';
  336. const parsedArgs = await parseCommandParams(message, context, command,easycmd);
  337. botlogger.info('命令参数解析完成:' + JSON.stringify({
  338. command: command.cmd,
  339. args: parsedArgs.slice(0, -1) // 不显示 context 对象
  340. }));
  341. await cost(context, command);
  342. // 执行命令
  343. const pluginInstance = new (command.class)();
  344. const result = await command.fn.apply(pluginInstance, parsedArgs);
  345. // 检查是否是群消息
  346. const isGroupMessage = context.message_type === 'group';
  347. const baseMessage = isGroupMessage && context.message_id
  348. ? [createReplyMessage(context.message_id)]
  349. : [];
  350. // 检查是否有模板配置
  351. if (result?.template?.enabled) {
  352. try {
  353. let templateIsPath: boolean = true;
  354. const templateHtml = result.template.html;
  355. const templatePath = result.template.path;
  356. const url= result.template.render.url;
  357. if (templateHtml) {
  358. templateIsPath = false;
  359. } else if (url) {
  360. templateIsPath = false;
  361. }else if (!templatePath || !fs.existsSync(templatePath)) {
  362. throw new Error(`Template not found: ${templatePath}`);
  363. }
  364. // 生成图片
  365. const htmlImg = new HtmlImg();
  366. try {
  367. const img = await htmlImg.render({
  368. template: templateIsPath ? templatePath : templateHtml,
  369. templateIsPath,
  370. data: result,
  371. url: url,
  372. width: result.template.render?.width || 800,
  373. height: result.template.render?.height || 600,
  374. type: result.template.render?.type || 'png',
  375. quality: result.template.render?.quality || 100,
  376. fullPage: result.template.render?.fullPage || false,
  377. background: result.template.render?.background || true
  378. });
  379. // 发送图片
  380. const base64Data = Buffer.from(img).toString('base64');
  381. const imageMessage = createImageMessage(base64Data);
  382. const message = [...baseMessage, imageMessage];
  383. if (isGroupMessage && context.group_id) {
  384. await qqBot.send_group_msg({
  385. group_id: Number(context.group_id),
  386. message: message as any[]
  387. });
  388. } else {
  389. await qqBot.send_private_msg({
  390. user_id: Number(context.user_id),
  391. message: message as any[]
  392. });
  393. }
  394. // 如果配置了同时发送文字
  395. if (result.template.sendText) {
  396. const text = result?.toString?.() || String(result);
  397. const textMessage = createTextMessage(text);
  398. const textOnlyMessage = [...baseMessage, textMessage];
  399. if (isGroupMessage && context.group_id) {
  400. await qqBot.send_group_msg({
  401. group_id: Number(context.group_id),
  402. message: textOnlyMessage as any[]
  403. });
  404. } else {
  405. await qqBot.send_private_msg({
  406. user_id: Number(context.user_id),
  407. message: textOnlyMessage as any[]
  408. });
  409. }
  410. }
  411. } finally {
  412. await htmlImg.close();
  413. }
  414. } catch (error) {
  415. botlogger.error('图片生成失败:', error);
  416. // 如果图片生成失败,发送文本
  417. const text = result?.toString?.() || String(result);
  418. const textMessage = createTextMessage(text);
  419. const message = [...baseMessage, textMessage];
  420. if (isGroupMessage && context.group_id) {
  421. await qqBot.send_group_msg({
  422. group_id: Number(context.group_id),
  423. message: message as any[]
  424. });
  425. } else {
  426. await qqBot.send_private_msg({
  427. user_id: Number(context.user_id),
  428. message: message as any[]
  429. });
  430. }
  431. }
  432. } else if (result?.picture?.enabled) {
  433. const messages: any[] = [createImageMessage(result.picture.base64)];
  434. if (typeof result.picture.supplement == "string") {
  435. messages.push(createTextMessage(result.picture.supplement));
  436. }
  437. if (isGroupMessage && context.group_id) {
  438. await qqBot.send_group_msg({
  439. group_id: Number(context.group_id),
  440. message: messages
  441. });
  442. } else {
  443. await qqBot.send_private_msg({
  444. user_id: Number(context.user_id),
  445. message: messages
  446. });
  447. }
  448. }
  449. else if (typeof result?.redirect == "string") {
  450. const targetCommand = findCommand(plugin, result.redirect);
  451. if (!targetCommand) {
  452. botlogger.info(`命令未找到: ${result.redirect}`);
  453. return;
  454. }
  455. return await handleCommand(context, plugin, targetCommand, args);
  456. }
  457. else {
  458. // 发送普通文本响应
  459. const message = [...baseMessage, createTextMessage(result)];
  460. if (isGroupMessage && context.group_id) {
  461. await qqBot.send_group_msg({
  462. group_id: Number(context.group_id),
  463. message: message
  464. });
  465. } else {
  466. await qqBot.send_private_msg({
  467. user_id: Number(context.user_id),
  468. message: message
  469. });
  470. }
  471. }
  472. } catch (error: unknown) {
  473. botlogger.error('执行命令出错:', error);
  474. const errorMessage = error instanceof Error ? error.message : '未知错误';
  475. await context.quick_action([{
  476. type: 'text',
  477. data: { text: `执行命令时出错: ${errorMessage}` }
  478. }]);
  479. }
  480. }
  481. // 导出加载插件函数
  482. export async function loadplugins() {
  483. await runplugins();
  484. }
  485. // 修改 runcod 装饰器
  486. export function runcod(cmd: string | string[], desc: string): MethodDecorator {
  487. return function decorator(target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor {
  488. // 获取插件配置
  489. const pluginConfig = target.constructor.prototype.plugincfg;
  490. if (!pluginConfig) {
  491. botlogger.error(`未找到插件配置: ${target.constructor.name}`);
  492. return descriptor;
  493. }
  494. const pluginId = pluginConfig.id;
  495. const pluginName = pluginConfig.name;
  496. // 获或创建插件的命令列表
  497. let plugin = commandList.find((p: Plugin) => p.class === target.constructor);
  498. if (!plugin) {
  499. plugin = {
  500. id: pluginId,
  501. name: pluginName,
  502. commands: [],
  503. class: target.constructor,
  504. config: pluginConfig
  505. };
  506. commandList.push(plugin);
  507. botlogger.info(`创建新插件[${pluginId}]: ${pluginName}`);
  508. }
  509. // 使用新的命令格式
  510. const cmdList = Array.isArray(cmd) ? cmd : [cmd];
  511. const [mainCmd, ...aliases] = cmdList;
  512. // 修改命令创建
  513. const command: Command = {
  514. cmd: mainCmd,
  515. desc,
  516. fn: descriptor.value,
  517. fnName: propertyKey.toString(),
  518. aliases,
  519. cmdPrefix: CMD_PREFIX,
  520. pluginId: pluginId,
  521. class: target.constructor,
  522. template: {
  523. enabled: false,
  524. sendText: true
  525. },
  526. };
  527. plugin.commands.push(command);
  528. botlogger.info(`注册命令[${pluginId}]: ${CMD_PREFIX}${pluginId} ${mainCmd}`);
  529. return descriptor;
  530. };
  531. }
  532. // 指令花费金币
  533. export async function cost( context: PrivateFriendMessage | PrivateGroupMessage | GroupMessage,command: Command){
  534. if (economy.enable == false) {
  535. return;
  536. }
  537. const ecocmd = economyCommands.get(command.pluginId + "." + command.fnName);
  538. if (ecocmd) {
  539. if (ecocmd.type ==='add') {
  540. addCoins(context.sender.user_id.toString(), ecocmd.amount, ecocmd.reason);
  541. }else{
  542. removeCoins(context.sender.user_id.toString(), ecocmd.amount, ecocmd.reason);
  543. }
  544. }
  545. }
  546. // 修改参数解析函数
  547. async function parseCommandParams(message: string, context: PrivateFriendMessage | PrivateGroupMessage | GroupMessage, command: Command,easycmd:boolean): Promise<any[]> {
  548. const cmdArgs = message.split(/\s+/).filter(Boolean);
  549. // 移除命令前缀和命令名
  550. const parts = message.split(/\s+/);
  551. let cmdIndex = 2;
  552. if (easycmd) {
  553. cmdIndex = 1;
  554. }
  555. const paramArgs: Receive[keyof Receive][] = [];
  556. parts.slice(cmdIndex).forEach((part) => {
  557. if (part !== '') {
  558. const Args: Receive["text"] ={
  559. type: 'text',
  560. data: {
  561. text: part
  562. }
  563. }
  564. paramArgs.push(Args)
  565. }
  566. });
  567. // 插入其他类别的消息
  568. for(let i = 0;i<context.message.length;i++){
  569. if(i !== 0){
  570. const msg= context.message[i]
  571. if (msg.type === 'text') {
  572. const parts = msg.data.text.split(/\s+/);
  573. parts.forEach((part) => {
  574. if (part!== '') {
  575. const Args: Receive["text"] ={
  576. type: 'text',
  577. data: {
  578. text: part
  579. }
  580. }
  581. paramArgs.push(Args)
  582. }
  583. });
  584. }else{
  585. paramArgs.push(context.message[i])
  586. }
  587. }
  588. }
  589. botlogger.info('DEBUG - 命令参数:' + JSON.stringify(paramArgs));
  590. const params: any[] = [];
  591. const param = paramMetadata.get(command.pluginId + "." + command.fnName);
  592. if (param) {
  593. let defaultValuei = 0
  594. param.forEach((paramData) => {
  595. if (paramData.defaultValue) {
  596. defaultValuei++
  597. }
  598. })
  599. for (const paramData of param) {
  600. const { name, type, index, optional } = paramData;
  601. const msg = `正确格式为: ${CMD_PREFIX}${command.pluginId} ${command.cmd} ${param.map(p => p.optional ? `[${p.name}]` : `<${p.name}>`).join(' ')}`
  602. if (paramArgs.length + defaultValuei < param?.length) {
  603. throw new Error(`参数不足,${msg}`);
  604. }
  605. if (!optional && !paramArgs[index]) {
  606. throw new Error(`参数 <${name}> 是必需的,${msg}`);
  607. }
  608. // 检查参数类型
  609. if (optional && paramArgs[index] === undefined) {
  610. params[index] = paramData.defaultValue;
  611. }
  612. let msgtype = paramArgs[index]?.type ?? paramData.defaultValue?.type
  613. if (type === msgtype) {
  614. params[index] = paramArgs[index];
  615. }else{
  616. throw new Error(`参数 <${name}> 类型错误,${msg}`);
  617. }
  618. }
  619. }
  620. // 添加 context 参数
  621. params.push(context);
  622. // 调试日志
  623. botlogger.info('DEBUG - 最终参数:' + JSON.stringify({
  624. params: params.slice(0, -1), // 不显示 context
  625. paramCount: params.length,
  626. paramArgs
  627. }));
  628. return params;
  629. }