decorators.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. import * as path from 'path';
  2. import botlogger from './logger.js';
  3. // 读取配置文件
  4. import { Botconfig as config } from './config.js'
  5. const CMD_PREFIX = config?.cmd?.prefix ?? '#';
  6. import { fileURLToPath } from 'node:url';
  7. import { Command, CommandConfig, ParamMetadata, ParamType, PluginConfig } from '../interface/plugin.js';
  8. import { Plugin } from '../interface/plugin.js';
  9. // 存储参数元数据
  10. export const paramMetadata = new Map<string, ParamMetadata[]>();
  11. // 存储命令的数组
  12. export const commandList: Plugin[] = [];
  13. // 修改参数装饰器
  14. export function param(name: string, type: ParamType = ParamType.String, optional: boolean = false): ParameterDecorator {
  15. return function (target: any, propertyKey: string | symbol | undefined, parameterIndex: number): void {
  16. const actualPropertyKey = propertyKey!;
  17. //获得fn所在的类名和方法名
  18. const fnName = `${target.constructor.name}.${actualPropertyKey.toString()}`;
  19. let metadata = paramMetadata.get(fnName);
  20. if (!metadata) {
  21. metadata = [];
  22. paramMetadata.set(fnName, metadata);
  23. }
  24. const paramData: ParamMetadata = {
  25. name,
  26. type,
  27. index: parameterIndex,
  28. optional: optional
  29. };
  30. metadata[parameterIndex] = paramData;
  31. };
  32. }
  33. // 修改插件装饰器
  34. export function plugins(config: PluginConfig): ClassDecorator {
  35. return function (target: any): void {
  36. // 保存插件配置
  37. target.prototype.plugincfg = config;
  38. // 确保插件名称正确
  39. const existingPlugin = commandList.find((x) => x.class === target);
  40. let plugin: Plugin;
  41. if (existingPlugin) {
  42. // 更新现有插件的配置
  43. existingPlugin.name = config.name;
  44. plugin = existingPlugin;
  45. } else {
  46. // 添加新插件
  47. plugin = {
  48. id: config.id,
  49. name: config.name,
  50. class: target,
  51. commands: [] as Command[],
  52. config
  53. };
  54. commandList.push(plugin);
  55. // 添加调试日志
  56. botlogger.info(`发现插件: ${config.name}[${config.version}],插件类: ${target.name}`);
  57. }
  58. // 添加帮助命令
  59. if (config.help?.enabled !== false) {
  60. const helpCommand: Command = {
  61. cmd: 'help',
  62. desc: config.help?.description || "显示帮助信息",
  63. aliases: ['帮助', '?'],
  64. template: {
  65. enabled: true,
  66. sendText: false
  67. },
  68. cmdPrefix: CMD_PREFIX,
  69. pluginId: config.id,
  70. class: target,
  71. fnName: 'help',
  72. fn: async function (): Promise<object> {
  73. const plugin = commandList.find(p => p.class === target);
  74. if (!plugin) {
  75. return {
  76. message: "错误: 找不到插件信息"
  77. };
  78. }
  79. // 过滤并格式化命令列表
  80. const commands = plugin.commands
  81. .filter(cmd => !cmd.cmd.endsWith('help'))
  82. .map(cmd => {
  83. let fullCmd = `${CMD_PREFIX}${plugin.id} ${cmd.cmd}`;
  84. const aliases = cmd.aliases?.map(alias =>
  85. `${CMD_PREFIX}${plugin.id} ${alias}`
  86. ) || [];
  87. paramMetadata.get(config.id + '.' + cmd.fnName)?.forEach((param: ParamMetadata) => {
  88. param.optional ? fullCmd += ` [${param.name}]` : fullCmd += ` <${param.name}>`;
  89. })
  90. return {
  91. name: cmd.cmd,
  92. fullCmd,
  93. desc: cmd.desc,
  94. aliases
  95. };
  96. });
  97. const __dirname = path.dirname(fileURLToPath(import.meta.url));
  98. // 返回支持模板的响应对象
  99. return {
  100. title: plugin.name,
  101. version: config.version || '',
  102. description: config.describe || '',
  103. author: config.author || '',
  104. commands,
  105. pluginId: plugin.id,
  106. cmdPrefix: CMD_PREFIX,
  107. template: {
  108. name: 'help',
  109. path: path.join(__dirname, '..', 'resources', 'help', 'help.html'),
  110. enabled: true,
  111. sendText: false,
  112. render: {
  113. width: 800,
  114. height: 1600,
  115. type: 'png',
  116. quality: 100,
  117. fullPage: false,
  118. background: true
  119. }
  120. },
  121. toString() {
  122. const commandsText = commands.map(cmd => {
  123. let text = `${cmd.fullCmd} - ${cmd.desc}`;
  124. if (cmd.aliases.length > 0) {
  125. text += `\n 别名: ${cmd.aliases.join(', ')}`;
  126. }
  127. return text;
  128. }).join('\n');
  129. return [
  130. `=== ${plugin.name} ===`,
  131. `版本: ${config.version}`,
  132. config.describe ? `描述: ${config.describe}` : '',
  133. config.author ? `作者: ${config.author}` : '',
  134. '',
  135. '可用命令:',
  136. commandsText
  137. ].filter(Boolean).join('\n');
  138. }
  139. };
  140. },
  141. };
  142. plugin.commands.push(helpCommand);
  143. botlogger.info(`成功注册[${plugin.id}]帮助命令: ${CMD_PREFIX}${plugin.id} help`);
  144. }
  145. // 检查并添加默认命令
  146. if (!config.defaultCommandId) {
  147. config.defaultCommandId = "help";
  148. }
  149. };
  150. }
  151. // 修改 runcod 装饰器
  152. /**
  153. * 运行命令装饰器
  154. * @param cmd - 命令名称或别名数组
  155. * @param desc - 命令描述
  156. * @param config - 命令配置
  157. */
  158. export function runcod(cmd: string | string[], desc: string, config: CommandConfig = {}, IsTest = false): MethodDecorator {
  159. return function (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) {
  160. // 延迟执行命令注册
  161. const originalMethod = descriptor.value;
  162. descriptor.value = function (...args: any[]) {
  163. // 获取插件配置
  164. const pluginConfig = target.constructor.prototype.plugincfg;
  165. if (!pluginConfig) {
  166. botlogger.error(`未找到插件配置: ${target.constructor.name}`);
  167. return originalMethod.apply(this, args);
  168. }
  169. const pluginName = pluginConfig.name;
  170. // 获取或创建插件的命令列表
  171. let plugin = commandList.find((p: Plugin) => p.class === target.constructor);
  172. if (!plugin) {
  173. plugin = {
  174. id: pluginConfig.id,
  175. name: pluginName,
  176. commands: [] as Command[],
  177. class: target.constructor,
  178. config: pluginConfig
  179. };
  180. commandList.push(plugin);
  181. botlogger.info(`创建新插件: ${pluginName}`);
  182. }
  183. // 如果命令还没有注册
  184. if (!descriptor.value.isCommand) {
  185. const cmdList = Array.isArray(cmd) ? cmd : [cmd];
  186. const allCmds = cmdList.map(c => c);
  187. // 第一个命令作为主命令,其他的作为别名
  188. const [mainCmd, ...aliases] = allCmds;
  189. // 添加命令
  190. const command: Command = {
  191. cmd: mainCmd,
  192. desc,
  193. fnName: propertyKey.toString(),
  194. fn: descriptor.value,
  195. aliases,
  196. cmdPrefix: CMD_PREFIX,
  197. pluginId: plugin.id,
  198. class: target.constructor,
  199. template: {
  200. enabled: !IsTest,
  201. sendText: IsTest, // 默认不发送文本,
  202. ...(config.template || {})
  203. },
  204. };
  205. plugin.commands.push(command);
  206. // 修改日志输出
  207. botlogger.info(`注册插件${plugin.id}命令: ${CMD_PREFIX}${plugin.id} ${mainCmd} 成功`);
  208. // 添加命令标记
  209. descriptor.value.isCommand = true;
  210. descriptor.value.cmd = Array.isArray(cmd) ? cmd[0] : cmd;
  211. descriptor.value.desc = desc;
  212. descriptor.value.aliases = Array.isArray(cmd) ? cmd.slice(1) : [];
  213. }
  214. return originalMethod.apply(this, args);
  215. };
  216. return descriptor;
  217. };
  218. }
  219. // 添加定时任务装饰器
  220. export function schedule(cron: string): MethodDecorator {
  221. return function (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) {
  222. // 保存原始方法
  223. const originalMethod = descriptor.value;
  224. // 添加定时任务标记
  225. descriptor.value.isScheduled = true;
  226. descriptor.value.cron = cron;
  227. descriptor.value.methodName = propertyKey;
  228. // 返回修改后的描述符
  229. return descriptor;
  230. };
  231. }