decorators.ts 12 KB

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