@dsh-plugin/dsh-synapse 0.0.0-stage → 0.1.0-dev.41044228975

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/LICENSE +661 -0
  2. package/NOTICE.md +18 -0
  3. package/README.md +136 -2
  4. package/cordis.patch.yml +3 -0
  5. package/lib/bot.cjs +8314 -0
  6. package/lib/bot.cjs.map +7 -0
  7. package/lib/build.json +1 -0
  8. package/lib/client.js +46 -0
  9. package/lib/host/agent.d.ts +21 -0
  10. package/lib/host/agent.js +151 -0
  11. package/lib/host/agent.js.map +1 -0
  12. package/lib/host/bot.d.ts +17 -0
  13. package/lib/host/bot.js +158 -0
  14. package/lib/host/bot.js.map +1 -0
  15. package/lib/host/client.d.ts +2 -0
  16. package/lib/host/client.js +19 -0
  17. package/lib/host/client.js.map +1 -0
  18. package/lib/host/config.d.ts +24 -0
  19. package/lib/host/config.js +192 -0
  20. package/lib/host/config.js.map +1 -0
  21. package/lib/host/files.d.ts +29 -0
  22. package/lib/host/files.js +188 -0
  23. package/lib/host/files.js.map +1 -0
  24. package/lib/host/gateway.d.ts +33 -0
  25. package/lib/host/gateway.js +237 -0
  26. package/lib/host/gateway.js.map +1 -0
  27. package/lib/host/github.d.ts +47 -0
  28. package/lib/host/github.js +219 -0
  29. package/lib/host/github.js.map +1 -0
  30. package/lib/host/http.d.ts +4 -0
  31. package/lib/host/http.js +41 -0
  32. package/lib/host/http.js.map +1 -0
  33. package/lib/host/index.d.ts +32 -0
  34. package/lib/host/index.js +192 -0
  35. package/lib/host/index.js.map +1 -0
  36. package/lib/host/inference-worker.d.ts +1 -0
  37. package/lib/host/inference-worker.js +72 -0
  38. package/lib/host/inference-worker.js.map +1 -0
  39. package/lib/host/mcp.d.ts +138 -0
  40. package/lib/host/mcp.js +104 -0
  41. package/lib/host/mcp.js.map +1 -0
  42. package/lib/host/memory.d.ts +26 -0
  43. package/lib/host/memory.js +155 -0
  44. package/lib/host/memory.js.map +1 -0
  45. package/lib/host/panel.d.ts +1 -0
  46. package/lib/host/panel.html +174 -0
  47. package/lib/host/panel.js +388 -0
  48. package/lib/host/panel.js.map +1 -0
  49. package/lib/host/runtime.d.ts +21 -0
  50. package/lib/host/runtime.js +145 -0
  51. package/lib/host/runtime.js.map +1 -0
  52. package/lib/host/types.d.ts +112 -0
  53. package/lib/host/types.js +2 -0
  54. package/lib/host/types.js.map +1 -0
  55. package/package.json +94 -4
  56. package/scripts/build.mjs +90 -0
  57. package/scripts/check.mjs +13 -0
  58. package/scripts/dump-npm-latest.mjs +30 -0
  59. package/scripts/model-smoke.mjs +25 -0
  60. package/src/agent.ts +203 -0
  61. package/src/bot.ts +173 -0
  62. package/src/client.ts +20 -0
  63. package/src/config.ts +217 -0
  64. package/src/files.ts +206 -0
  65. package/src/gateway.ts +276 -0
  66. package/src/github.ts +263 -0
  67. package/src/http.ts +41 -0
  68. package/src/index.ts +227 -0
  69. package/src/inference-worker.ts +89 -0
  70. package/src/mcp.ts +112 -0
  71. package/src/memory.ts +178 -0
  72. package/src/panel.html +174 -0
  73. package/src/panel.ts +440 -0
  74. package/src/runtime.ts +164 -0
  75. package/src/types.ts +106 -0
  76. package/test/core.test.mjs +279 -0
  77. package/test/fixtures/mcp-server.mjs +14 -0
  78. package/test/integration.test.mjs +253 -0
  79. package/test/panel.test.mjs +73 -0
  80. package/tsconfig.json +18 -0
  81. package/vendor/gugle-chat-bot/LICENSE +661 -0
  82. package/vendor/gugle-chat-bot/package.json +57 -0
  83. package/vendor/gugle-chat-bot/src/allow-rule.yml +9345 -0
  84. package/vendor/gugle-chat-bot/src/api.json +4285 -0
  85. package/vendor/gugle-chat-bot/src/command/index.ts +141 -0
  86. package/vendor/gugle-chat-bot/src/config/features.ts +548 -0
  87. package/vendor/gugle-chat-bot/src/config/index.ts +184 -0
  88. package/vendor/gugle-chat-bot/src/config/manager.ts +357 -0
  89. package/vendor/gugle-chat-bot/src/constants/index.ts +13 -0
  90. package/vendor/gugle-chat-bot/src/custom.ts +548 -0
  91. package/vendor/gugle-chat-bot/src/event/index.ts +118 -0
  92. package/vendor/gugle-chat-bot/src/features/admin/index.ts +414 -0
  93. package/vendor/gugle-chat-bot/src/features/bili/image/impl/video.ts +39 -0
  94. package/vendor/gugle-chat-bot/src/features/bili/image/index.ts +37 -0
  95. package/vendor/gugle-chat-bot/src/features/bili/index.ts +65 -0
  96. package/vendor/gugle-chat-bot/src/features/bili/template/style/video.css +97 -0
  97. package/vendor/gugle-chat-bot/src/features/bili/template/video.html +57 -0
  98. package/vendor/gugle-chat-bot/src/features/discord-bridge/index.ts +1128 -0
  99. package/vendor/gugle-chat-bot/src/features/discord-bridge/markdown.ts +105 -0
  100. package/vendor/gugle-chat-bot/src/features/discord-bridge/slash.ts +355 -0
  101. package/vendor/gugle-chat-bot/src/features/discord-bridge/source.ts +96 -0
  102. package/vendor/gugle-chat-bot/src/features/discord-bridge/template/message.html +14 -0
  103. package/vendor/gugle-chat-bot/src/features/discord-bridge/template/style/message.css +144 -0
  104. package/vendor/gugle-chat-bot/src/features/github/api.ts +93 -0
  105. package/vendor/gugle-chat-bot/src/features/github/binding.ts +161 -0
  106. package/vendor/gugle-chat-bot/src/features/github/image/impl/issue.ts +132 -0
  107. package/vendor/gugle-chat-bot/src/features/github/image/impl/pr.ts +144 -0
  108. package/vendor/gugle-chat-bot/src/features/github/image/impl/release.ts +160 -0
  109. package/vendor/gugle-chat-bot/src/features/github/image/index.ts +310 -0
  110. package/vendor/gugle-chat-bot/src/features/github/index.ts +302 -0
  111. package/vendor/gugle-chat-bot/src/features/github/template/issue.html +118 -0
  112. package/vendor/gugle-chat-bot/src/features/github/template/pull_request.html +125 -0
  113. package/vendor/gugle-chat-bot/src/features/github/template/release.html +53 -0
  114. package/vendor/gugle-chat-bot/src/features/github/template/style/issue.css +468 -0
  115. package/vendor/gugle-chat-bot/src/features/github/template/style/pull_request.css +469 -0
  116. package/vendor/gugle-chat-bot/src/features/github/template/style/release.css +412 -0
  117. package/vendor/gugle-chat-bot/src/features/hermes/client.ts +184 -0
  118. package/vendor/gugle-chat-bot/src/features/hermes/config.ts +210 -0
  119. package/vendor/gugle-chat-bot/src/features/hermes/context.ts +67 -0
  120. package/vendor/gugle-chat-bot/src/features/hermes/history.ts +86 -0
  121. package/vendor/gugle-chat-bot/src/features/hermes/index.ts +1533 -0
  122. package/vendor/gugle-chat-bot/src/features/hermes/message.ts +319 -0
  123. package/vendor/gugle-chat-bot/src/features/hermes/reaction.ts +181 -0
  124. package/vendor/gugle-chat-bot/src/features/hermes/render.ts +118 -0
  125. package/vendor/gugle-chat-bot/src/features/hermes/reply-units.ts +331 -0
  126. package/vendor/gugle-chat-bot/src/features/hermes/skills.ts +406 -0
  127. package/vendor/gugle-chat-bot/src/features/hermes/template/approval.html +30 -0
  128. package/vendor/gugle-chat-bot/src/features/hermes/template/progress.html +21 -0
  129. package/vendor/gugle-chat-bot/src/features/hermes/template/style/approval.css +81 -0
  130. package/vendor/gugle-chat-bot/src/features/hermes/template/style/progress.css +120 -0
  131. package/vendor/gugle-chat-bot/src/features/hermes/trigger.ts +86 -0
  132. package/vendor/gugle-chat-bot/src/features/hermes/types.ts +327 -0
  133. package/vendor/gugle-chat-bot/src/features/management/index.ts +156 -0
  134. package/vendor/gugle-chat-bot/src/features/minecraft/index.ts +127 -0
  135. package/vendor/gugle-chat-bot/src/features/modrinth/index.ts +71 -0
  136. package/vendor/gugle-chat-bot/src/features/parentheses/index.ts +117 -0
  137. package/vendor/gugle-chat-bot/src/features/peak-valley-timer/index.ts +452 -0
  138. package/vendor/gugle-chat-bot/src/features/poke/index.ts +24 -0
  139. package/vendor/gugle-chat-bot/src/features/version-tracker/index.ts +78 -0
  140. package/vendor/gugle-chat-bot/src/features/welcome/index.ts +58 -0
  141. package/vendor/gugle-chat-bot/src/github-ip.json +5814 -0
  142. package/vendor/gugle-chat-bot/src/image/index.ts +278 -0
  143. package/vendor/gugle-chat-bot/src/index.ts +695 -0
  144. package/vendor/gugle-chat-bot/src/logger/index.ts +54 -0
  145. package/vendor/gugle-chat-bot/src/result.json +359 -0
  146. package/vendor/gugle-chat-bot/src/type/bili.d.ts +825 -0
  147. package/vendor/gugle-chat-bot/src/type/event.d.ts +146 -0
  148. package/vendor/gugle-chat-bot/src/type/github.d.ts +446 -0
  149. package/vendor/gugle-chat-bot/src/type/index.d.ts +254 -0
  150. package/vendor/gugle-chat-bot/tsconfig.json +46 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../vendor/gugle-chat-bot/src/constants/index.ts", "../vendor/gugle-chat-bot/src/config/index.ts", "../vendor/gugle-chat-bot/src/config/manager.ts", "../vendor/gugle-chat-bot/src/config/features.ts", "../vendor/gugle-chat-bot/src/logger/index.ts", "../vendor/gugle-chat-bot/src/image/index.ts", "../vendor/gugle-chat-bot/src/features/github/image/impl/issue.ts", "../vendor/gugle-chat-bot/src/features/github/image/impl/pr.ts", "../vendor/gugle-chat-bot/src/features/github/image/impl/release.ts", "../vendor/gugle-chat-bot/src/features/github/api.ts", "../vendor/gugle-chat-bot/src/features/github/image/index.ts", "../vendor/gugle-chat-bot/src/features/github/binding.ts", "../vendor/gugle-chat-bot/src/features/github/index.ts", "../vendor/gugle-chat-bot/src/command/index.ts", "../vendor/gugle-chat-bot/src/features/admin/index.ts", "../vendor/gugle-chat-bot/src/features/discord-bridge/markdown.ts", "../vendor/gugle-chat-bot/src/features/discord-bridge/source.ts", "../vendor/gugle-chat-bot/src/features/hermes/config.ts", "../vendor/gugle-chat-bot/src/features/hermes/client.ts", "../vendor/gugle-chat-bot/src/features/bili/image/impl/video.ts", "../vendor/gugle-chat-bot/src/features/bili/image/index.ts", "../vendor/gugle-chat-bot/src/features/bili/index.ts", "../vendor/gugle-chat-bot/src/features/minecraft/index.ts", "../vendor/gugle-chat-bot/src/event/index.ts", "../vendor/gugle-chat-bot/src/features/version-tracker/index.ts", "../vendor/gugle-chat-bot/src/features/modrinth/index.ts", "../vendor/gugle-chat-bot/src/features/hermes/skills.ts", "../vendor/gugle-chat-bot/src/features/hermes/history.ts", "../vendor/gugle-chat-bot/src/features/hermes/context.ts", "../vendor/gugle-chat-bot/src/features/hermes/message.ts", "../vendor/gugle-chat-bot/src/features/hermes/trigger.ts", "../vendor/gugle-chat-bot/src/features/hermes/reply-units.ts", "../vendor/gugle-chat-bot/src/features/hermes/reaction.ts", "../vendor/gugle-chat-bot/src/features/hermes/render.ts", "../vendor/gugle-chat-bot/src/features/hermes/index.ts", "../vendor/gugle-chat-bot/src/features/discord-bridge/slash.ts", "../vendor/gugle-chat-bot/src/features/discord-bridge/index.ts", "../vendor/gugle-chat-bot/src/features/parentheses/index.ts", "../vendor/gugle-chat-bot/src/features/poke/index.ts", "../vendor/gugle-chat-bot/src/features/management/index.ts", "../vendor/gugle-chat-bot/src/features/welcome/index.ts", "../vendor/gugle-chat-bot/src/features/peak-valley-timer/index.ts", "../vendor/gugle-chat-bot/src/custom.ts", "../vendor/gugle-chat-bot/src/index.ts"],
4
+ "sourcesContent": ["export default class Constants {\n public static readonly USER_AGENT =\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36 Edg/140.0.0.0';\n public static readonly LOG_LEVEL = 'debug';\n public static readonly CHROME_PATH = 'C:\\\\Program Files (x86)\\\\Google\\\\Chrome\\\\Application\\\\chrome.exe';\n public static readonly HTTP_URL = 'http://127.0.0.1:33100';\n public static readonly WS_URL = 'ws://127.0.0.1:33101';\n public static readonly TOKEN_PARAMS = '?access_token=';\n public static readonly WS_TOKEN = '';\n public static readonly HTTP_TOKEN = '';\n public static readonly GITHUB_PORT = 8848;\n public static readonly GITHUB_ALLOWED_REPOSITORIES = ['Anvil-Dev/*', 'Gu-ZT/*'];\n}\n", "import fs from 'node:fs';\nimport path from 'node:path';\nimport Constants from '@/constants';\n\nexport type LogLevel = 'info' | 'error' | 'warn' | 'debug';\n\nexport interface BotConfig {\n userAgent: string;\n logLevel: LogLevel;\n chromePath: string;\n httpUrl: string;\n wsUrl: string;\n tokenParams: string;\n wsToken: string;\n httpToken: string;\n githubPort: number;\n githubAllowedRepositories: string[];\n}\n\ntype ConfigFile = Partial<BotConfig>;\n\nconst defaultBotConfig: BotConfig = {\n userAgent: Constants.USER_AGENT,\n logLevel: Constants.LOG_LEVEL,\n chromePath: Constants.CHROME_PATH,\n httpUrl: Constants.HTTP_URL,\n wsUrl: Constants.WS_URL,\n tokenParams: Constants.TOKEN_PARAMS,\n wsToken: Constants.WS_TOKEN,\n httpToken: Constants.HTTP_TOKEN,\n githubPort: Constants.GITHUB_PORT,\n githubAllowedRepositories: Constants.GITHUB_ALLOWED_REPOSITORIES\n};\n\nconst logLevels: readonly LogLevel[] = ['info', 'error', 'warn', 'debug'];\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\nfunction isLogLevel(value: unknown): value is LogLevel {\n return isString(value) && logLevels.includes(value as LogLevel);\n}\n\nfunction isPort(value: unknown): value is number {\n return typeof value === 'number' && Number.isInteger(value) && value > 0 && value <= 65535;\n}\n\nfunction isRepositoryPattern(value: unknown): value is string {\n return isString(value) && /^[A-Za-z0-9_.-]+\\/(?:[A-Za-z0-9_.-]+|\\*)$/.test(value);\n}\n\nfunction isRepositoryPatternArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every(isRepositoryPattern);\n}\n\ninterface LoadedConfigFile {\n config: ConfigFile;\n contents: string;\n}\n\ninterface NormalizedConfig {\n config: BotConfig;\n hasInvalidField: boolean;\n needsWrite: boolean;\n}\n\nfunction writeConfigFile(configPath: string, config: BotConfig): void {\n fs.mkdirSync(path.dirname(configPath), { recursive: true });\n fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\\n`, 'utf8');\n}\n\nfunction createDefaultConfigFile(configPath: string): void {\n writeConfigFile(configPath, defaultBotConfig);\n}\n\nfunction createBackupConfigFile(configPath: string, contents: string): void {\n const { dir, ext, name } = path.parse(configPath);\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n let backupPath = path.join(dir, `${name}.bak.${timestamp}${ext}`);\n let index = 1;\n\n while (fs.existsSync(backupPath)) {\n backupPath = path.join(dir, `${name}.bak.${timestamp}.${index}${ext}`);\n index++;\n }\n\n fs.writeFileSync(backupPath, contents, 'utf8');\n}\n\nfunction readConfigFile(configPath: string): LoadedConfigFile {\n if (!fs.existsSync(configPath)) createDefaultConfigFile(configPath);\n\n const contents = fs.readFileSync(configPath, 'utf8');\n try {\n const config = JSON.parse(contents) as unknown;\n if (!config || typeof config !== 'object' || Array.isArray(config)) {\n throw new Error('root must be an object');\n }\n return { config: config as ConfigFile, contents };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Failed to load bot configuration from ${configPath}: ${message}`);\n }\n}\n\nfunction normalizeConfig(config: ConfigFile): NormalizedConfig {\n let hasInvalidField = false;\n let needsWrite = false;\n\n function valueOrDefault<T>(value: unknown, validator: (value: unknown) => value is T, fallback: T): T {\n if (value === undefined) {\n needsWrite = true;\n return fallback;\n }\n if (!validator(value)) {\n hasInvalidField = true;\n needsWrite = true;\n return fallback;\n }\n return value;\n }\n\n return {\n config: {\n userAgent: valueOrDefault(config.userAgent, isString, defaultBotConfig.userAgent),\n logLevel: valueOrDefault(config.logLevel, isLogLevel, defaultBotConfig.logLevel),\n chromePath: valueOrDefault(config.chromePath, isString, defaultBotConfig.chromePath),\n httpUrl: valueOrDefault(config.httpUrl, isString, defaultBotConfig.httpUrl),\n wsUrl: valueOrDefault(config.wsUrl, isString, defaultBotConfig.wsUrl),\n tokenParams: valueOrDefault(config.tokenParams, isString, defaultBotConfig.tokenParams),\n wsToken: valueOrDefault(config.wsToken, isString, defaultBotConfig.wsToken),\n httpToken: valueOrDefault(config.httpToken, isString, defaultBotConfig.httpToken),\n githubPort: valueOrDefault(config.githubPort, isPort, defaultBotConfig.githubPort),\n githubAllowedRepositories: valueOrDefault(\n config.githubAllowedRepositories,\n isRepositoryPatternArray,\n defaultBotConfig.githubAllowedRepositories\n )\n },\n hasInvalidField,\n needsWrite\n };\n}\n\nexport function loadBotConfig(configPath: string = path.resolve(process.cwd(), 'configs', 'bot-config.json')): BotConfig {\n const loadedConfig = readConfigFile(configPath);\n const normalizedConfig = normalizeConfig(loadedConfig.config);\n\n if (normalizedConfig.hasInvalidField) createBackupConfigFile(configPath, loadedConfig.contents);\n if (normalizedConfig.needsWrite) writeConfigFile(configPath, normalizedConfig.config);\n\n loadedConfigPath = configPath;\n return normalizedConfig.config;\n}\n\n/** \u751F\u6548\u914D\u7F6E\u7684\u52A0\u8F7D\u8DEF\u5F84\uFF0C\u5199\u56DE\u914D\u7F6E\u65F6\u590D\u7528\uFF08\u7531 loadBotConfig \u8BB0\u5F55\uFF09 */\nlet loadedConfigPath: string = path.resolve(process.cwd(), 'configs', 'bot-config.json');\n\nexport const botConfig = loadBotConfig();\n\n/**\n * \u628A\u4E00\u4E2A GitHub \u4ED3\u5E93\u6216 owner \u52A0\u5165\u5141\u8BB8\u8BBF\u95EE\u7684\u4ED3\u5E93\u5217\u8868\uFF0C\u5E76\u6301\u4E45\u5316\u5230\u914D\u7F6E\u6587\u4EF6\u3002\n *\n * \u52A8\u673A\uFF1A\u7BA1\u7406\u5458\u901A\u8FC7 /github allow \u6216 /github subscribe \u6388\u6743\u4ED3\u5E93\u65F6\uFF0C\u65E0\u9700\u624B\u5DE5\u6539\u914D\u7F6E\u6587\u4EF6\u3002\n * \u4F20\u5165 owner \u65F6\u5199\u5165 `owner/*`\uFF08\u8986\u76D6\u8BE5 owner \u4E0B\u5168\u90E8\u4ED3\u5E93\uFF0C\u542B\u540E\u7EED\u65B0\u5EFA\u7684\uFF09\uFF1B\n * \u4F20\u5165 owner/repo \u65F6\u5199\u5165\u7CBE\u786E\u6761\u76EE\uFF0C\u53EA\u6388\u6743\u8BE5\u4ED3\u5E93\u3002\n *\n * @param target GitHub owner\uFF08\u5982 Anvil-Dev\uFF09\u6216\u4ED3\u5E93\u5168\u540D\uFF08\u5982 Anvil-Dev/AnvilCraft\uFF09\n * @returns \u5B9E\u9645\u5199\u5165\u7684\u5141\u8BB8\u5217\u8868\u6761\u76EE\n */\nexport function addGithubAllowedRepository(target: string): string {\n const pattern = target.includes('/') ? target : `${target}/*`;\n if (botConfig.githubAllowedRepositories.includes(pattern)) return pattern;\n botConfig.githubAllowedRepositories.push(pattern);\n writeConfigFile(loadedConfigPath, botConfig);\n return pattern;\n}\n\n/** \u5224\u65AD\u67D0\u6761\u76EE\u662F\u5426\u5DF2\u5728\u5141\u8BB8\u5217\u8868\u4E2D\uFF08\u4E0D\u505A\u4EFB\u4F55\u5199\u5165\uFF09 */\nexport function isGithubAllowedPattern(target: string): boolean {\n const pattern = target.includes('/') ? target : `${target}/*`;\n return botConfig.githubAllowedRepositories.includes(pattern);\n}\n", "import fs from 'node:fs';\nimport path from 'node:path';\n\nexport interface MigrationStep {\n /** \u6E90\u7248\u672C\u53F7\uFF08\u8FC1\u79FB\u524D\uFF09 */\n from: number;\n /** \u76EE\u6807\u7248\u672C\u53F7\uFF08\u8FC1\u79FB\u540E\uFF09 */\n to: number;\n /** \u8FC1\u79FB\u51FD\u6570\uFF1A\u63A5\u6536\u65E7\u6570\u636E\uFF0C\u8FD4\u56DE\u65B0\u6570\u636E */\n migrate: (data: Record<string, unknown>) => Record<string, unknown>;\n}\n\nexport interface ConfigFileOptions<T> {\n /** \u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84\uFF08\u76F8\u5BF9 process.cwd() \u6216\u7EDD\u5BF9\u8DEF\u5F84\uFF09 */\n path: string;\n /** \u9ED8\u8BA4\u503C\uFF1A\u6587\u4EF6\u4E0D\u5B58\u5728\u65F6\u5199\u5165\u7684\u5185\u5BB9\uFF1B\u4E5F\u53EF\u7528 factory \u52A8\u6001\u751F\u6210 */\n default?: T;\n /** \u52A8\u6001\u9ED8\u8BA4\u503C\u5DE5\u5382\uFF08\u4F18\u5148\u4E8E default\uFF09\uFF0C\u5982\u9700\u8981\u57FA\u4E8E\u8FD0\u884C\u65F6\u72B6\u6001\u751F\u6210\u9ED8\u8BA4\u914D\u7F6E */\n factory?: () => T;\n /** \u89E3\u6790\u540E\u7684\u6821\u9A8C/\u89C4\u8303\u5316\u94A9\u5B50\uFF1A\u8FD4\u56DE false \u8868\u793A\u5185\u5BB9\u975E\u6CD5\uFF08\u89E6\u53D1\u5907\u4EFD\u91CD\u5EFA\uFF09\uFF0C\u8FD4\u56DE\u89C4\u8303\u5316\u540E\u7684\u503C */\n normalize?: (raw: unknown) => T | null;\n /** \u671F\u671B\u7684\u914D\u7F6E\u6587\u4EF6\u7248\u672C\u53F7\uFF08\u9ED8\u8BA4 1\uFF09\u3002\u6587\u4EF6\u7248\u672C\u4F4E\u4E8E\u6B64\u503C\u65F6\u6309 migrations \u9010\u7EA7\u5347\u7EA7 */\n version?: number;\n /** \u7248\u672C\u8FC1\u79FB\u6B65\u9AA4\uFF1A\u6309 from \u5347\u5E8F\u6267\u884C\uFF0C\u628A\u65E7\u7248\u672C\u6570\u636E\u5347\u7EA7\u5230\u65B0\u7248\u672C */\n migrations?: MigrationStep[];\n}\n\nexport interface ReadOrCreateResult<T> {\n /** \u6700\u7EC8\u751F\u6548\u7684\u6570\u636E\uFF08\u53EF\u80FD\u662F\u9ED8\u8BA4\u503C\u6216\u89C4\u8303\u5316\u540E\u7684\u503C\uFF09 */\n data: T;\n /** \u662F\u5426\u53D1\u751F\u4E86\"\u65B0\u5EFA / \u5907\u4EFD\u91CD\u5EFA / \u89C4\u8303\u5316\u540E\u5199\u56DE\"\u7B49\u5199\u76D8\u52A8\u4F5C */\n wrote: boolean;\n /** \u6587\u4EF6\u539F\u672C\u7684\u5185\u5BB9\u635F\u574F\u88AB\u5907\u4EFD\u65F6\u7684\u5907\u4EFD\u8DEF\u5F84\uFF1B\u672A\u53D1\u751F\u5219\u4E3A undefined */\n backupPath?: string;\n}\n\nconst EMPTY_OBJECT = {};\n\n/**\n * \u8BFB\u53D6 JSON \u914D\u7F6E\u6587\u4EF6\uFF1B\u6587\u4EF6\u4E0D\u5B58\u5728\u65F6\u7528\u9ED8\u8BA4\u503C\u521B\u5EFA\uFF0C\u5185\u5BB9\u635F\u574F\u65F6\u5907\u4EFD\u5E76\u91CD\u5EFA\u3002\n *\n * \u7EDF\u4E00\u4E86\u5404\u533A\u6A21\u5757\u5404\u81EA\u4E3A\u653F\u7684\"\u8BFB\u53D6/\u521B\u5EFA/\u5907\u4EFD\"\u903B\u8F91\uFF1A\n * - \u76EE\u5F55\u4E0D\u5B58\u5728 \u2192 \u81EA\u52A8\u521B\u5EFA\n * - \u6587\u4EF6\u4E0D\u5B58\u5728 \u2192 \u5199\u5165 default / factory() \u751F\u6210\u7684\u5185\u5BB9\n * - JSON \u89E3\u6790\u5931\u8D25 \u2192 \u5907\u4EFD\u4E3A `<name>.bak.<timestamp>.json` \u540E\u91CD\u5EFA\u4E3A\u9ED8\u8BA4\u5185\u5BB9\n * - normalize \u8FD4\u56DE null\uFF08\u5185\u5BB9\u975E\u6CD5\uFF09\u2192 \u540C\u6837\u5907\u4EFD\u540E\u91CD\u5EFA\n * - normalize \u8FD4\u56DE\u5408\u6CD5\u503C \u2192 \u82E5\u4E0E\u539F\u6587\u4EF6\u4E0D\u540C\u5219\u5199\u56DE\uFF08\u89C4\u8303\u5316\u843D\u76D8\uFF09\n *\n * @example\n * const { data } = readOrCreate<BotConfig>({\n * path: 'configs/bot-config.json',\n * factory: () => defaultBotConfig,\n * normalize: raw => normalizeConfig(raw)\n * });\n */\nexport function readOrCreate<T>(options: ConfigFileOptions<T>): ReadOrCreateResult<T> {\n const configPath = path.resolve(process.cwd(), options.path);\n const dir = path.dirname(configPath);\n fs.mkdirSync(dir, { recursive: true });\n\n const factory = options.factory || (() => options.default as T);\n let wrote = false;\n let backupPath: string | undefined = undefined;\n\n // \u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A\u76F4\u63A5\u521B\u5EFA\u9ED8\u8BA4\u5185\u5BB9\n if (!fs.existsSync(configPath)) {\n const created = factory();\n writeConfigFile(configPath, created);\n return { data: created, wrote: true };\n }\n\n const contents = fs.readFileSync(configPath, 'utf8');\n let raw: unknown;\n let parsed: unknown;\n try {\n parsed = JSON.parse(contents);\n } catch (_) {\n // JSON \u635F\u574F\uFF1A\u5907\u4EFD\u539F\u6587\u4EF6\u540E\u91CD\u5EFA\u9ED8\u8BA4\n backupPath = createBackupConfigFile(configPath, contents);\n const rebuilt = factory();\n writeConfigFile(configPath, rebuilt);\n return { data: rebuilt, wrote: true, backupPath };\n }\n // \u7248\u672C\u8FC1\u79FB\uFF1A\u6587\u4EF6\u7248\u672C\u4F4E\u4E8E\u671F\u671B\u7248\u672C\u65F6\u9010\u7EA7\u5347\u7EA7\n const targetVersion = options.version ?? 1;\n let migrated = parsed;\n let migratedAny = false;\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n const currentVersion =\n typeof (parsed as Record<string, unknown>).version === 'number'\n ? ((parsed as Record<string, unknown>).version as number)\n : 1;\n if (currentVersion < targetVersion && options.migrations) {\n const ordered = [...options.migrations].sort((a, b) => a.from - b.from);\n let working = parsed as Record<string, unknown>;\n for (const step of ordered) {\n if (step.from >= currentVersion && step.to <= targetVersion) {\n working = step.migrate(working);\n migratedAny = true;\n }\n }\n if (migratedAny) {\n working = { ...working, version: targetVersion };\n writeConfigFile(configPath, working);\n return { data: working as T, wrote: true };\n }\n migrated = working;\n }\n }\n raw = migrated;\n\n if (options.normalize) {\n const normalized = options.normalize(raw);\n if (normalized === null) {\n // \u5185\u5BB9\u975E\u6CD5\uFF1A\u5907\u4EFD\u540E\u91CD\u5EFA\u9ED8\u8BA4\n backupPath = createBackupConfigFile(configPath, contents);\n const rebuilt = factory();\n writeConfigFile(configPath, rebuilt);\n return { data: rebuilt, wrote: true, backupPath };\n }\n // \u89C4\u8303\u5316\u7ED3\u679C\u4E0E\u6587\u4EF6\u4E0D\u4E00\u81F4\u65F6\u5199\u56DE\uFF08\u6BD4\u5982\u8865\u4E86\u7F3A\u5931\u7684\u9ED8\u8BA4\u5B57\u6BB5\uFF09\n const serialized = JSON.stringify(normalized, null, 2);\n if (serialized !== contents.trim()) {\n writeConfigFile(configPath, normalized);\n wrote = true;\n }\n return { data: normalized, wrote };\n }\n\n return { data: raw as T, wrote };\n}\n\n/**\n * \u8BFB\u53D6 JSON \u914D\u7F6E\u6587\u4EF6\uFF1B\u6587\u4EF6\u4E0D\u5B58\u5728\u8FD4\u56DE null\uFF0CJSON \u635F\u574F\u629B\u51FA\u9519\u8BEF\uFF08\u7531\u8C03\u7528\u65B9\u51B3\u5B9A\u5982\u4F55\u5904\u7406\uFF09\u3002\n */\nexport function readConfigFile<T>(configPath: string): T | null {\n const resolved = path.resolve(process.cwd(), configPath);\n if (!fs.existsSync(resolved)) return null;\n const contents = fs.readFileSync(resolved, 'utf8');\n try {\n return JSON.parse(contents) as T;\n } catch (error) {\n throw new Error(\n `\u65E0\u6CD5\u89E3\u6790\u914D\u7F6E\u6587\u4EF6 ${resolved}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\n/**\n * \u5199\u5165 JSON \u914D\u7F6E\u6587\u4EF6\uFF08\u539F\u5B50\u5199\uFF1A\u5148\u5199\u4E34\u65F6\u6587\u4EF6\u518D rename\uFF0C\u907F\u514D\u5199\u5165\u4E2D\u65AD\u635F\u574F\u6587\u4EF6\uFF09\u3002\n */\nexport function writeConfigFile<T>(configPath: string, data: T, options: { pretty?: boolean } = {}): void {\n const resolved = path.resolve(process.cwd(), configPath);\n fs.mkdirSync(path.dirname(resolved), { recursive: true });\n const serialized = options.pretty === false ? JSON.stringify(data) : `${JSON.stringify(data, null, 2)}\\n`;\n const tempPath = `${resolved}.tmp-${process.pid}`;\n fs.writeFileSync(tempPath, serialized, 'utf8');\n fs.renameSync(tempPath, resolved);\n}\n\n/**\n * \u5907\u4EFD\u6587\u4EF6\u4E3A `<name>.bak.<timestamp>.<n>.json`\uFF0C\u8FD4\u56DE\u5907\u4EFD\u8DEF\u5F84\u3002\n */\nexport function createBackupConfigFile(configPath: string, contents: string): string {\n const resolved = path.resolve(process.cwd(), configPath);\n const { dir, ext, name } = path.parse(resolved);\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n let backupPath = path.join(dir, `${name}.bak.${timestamp}${ext}`);\n let index = 1;\n while (fs.existsSync(backupPath)) {\n backupPath = path.join(dir, `${name}.bak.${timestamp}.${index}${ext}`);\n index++;\n }\n fs.writeFileSync(backupPath, contents, 'utf8');\n return backupPath;\n}\n\nexport { EMPTY_OBJECT };\n\n// ---------------------------------------------------------------------------\n// ConfigStore\uFF1A\u5E26\u70ED\u91CD\u8F7D\u7684\u914D\u7F6E\u4ED3\u5E93\n// ---------------------------------------------------------------------------\n\nexport type ConfigChangeSource = 'self' | 'file' | 'reload';\n\n/**\n * \u5168\u5C40 ConfigStore \u6CE8\u518C\u8868\uFF1A\u4F9B /reload \u547D\u4EE4\u7EDF\u4E00\u4ECE\u78C1\u76D8\u91CD\u8F7D\u5168\u90E8\u914D\u7F6E\u3002\n *\n * \u5404\u529F\u80FD\u6A21\u5757\u7684 store \u5728\u6A21\u5757\u52A0\u8F7D\u65F6\u521B\u5EFA\uFF08\u90E8\u5206\u4E3A\u61D2\u52A0\u8F7D\uFF09\uFF0C\u6CA1\u6709\u7EDF\u4E00\u5165\u53E3\uFF1B\n * \u8FD9\u91CC\u7531 ConfigStore \u6784\u9020\u51FD\u6570\u81EA\u52A8\u767B\u8BB0\uFF0C\u907F\u514D\u6BCF\u4E2A\u6A21\u5757\u5404\u81EA\u66B4\u9732 reload \u51FD\u6570\u3001\n * \u4E5F\u907F\u514D\u65B0\u589E\u914D\u7F6E\u65F6\u6F0F\u6389\u91CD\u8F7D\u3002\n */\nconst configStoreRegistry = new Set<ConfigStore<unknown>>();\n\n/** \u4ECE\u78C1\u76D8\u91CD\u8F7D\u5168\u90E8\u5DF2\u767B\u8BB0\u7684\u914D\u7F6E\u4ED3\u5E93\uFF0C\u8FD4\u56DE\u6210\u529F\u91CD\u8F7D\u7684\u6570\u91CF */\nexport function reloadAllConfigStores(): number {\n let reloaded = 0;\n for (const store of configStoreRegistry) {\n try {\n store.reload('reload');\n reloaded++;\n } catch {\n // \u5355\u4E2A\u914D\u7F6E\u6587\u4EF6\u635F\u574F\u65F6\u4E0D\u5F71\u54CD\u5176\u4F59\u914D\u7F6E\u91CD\u8F7D\n }\n }\n return reloaded;\n}\n\n/** \u5DF2\u767B\u8BB0\u7684\u914D\u7F6E\u4ED3\u5E93\u6570\u91CF\uFF08\u8BCA\u65AD/\u6D4B\u8BD5\u7528\uFF09 */\nexport function configStoreCount(): number {\n return configStoreRegistry.size;\n}\n\nexport interface ConfigStoreOptions<T> extends ConfigFileOptions<T> {\n /** \u662F\u5426\u76D1\u542C\u6587\u4EF6\u53D8\u5316\u81EA\u52A8\u70ED\u91CD\u8F7D\uFF08\u9ED8\u8BA4 true\uFF09 */\n watch?: boolean;\n /** \u6587\u4EF6\u53D8\u5316\u89E6\u53D1\u91CD\u8F7D\u7684\u9632\u6296\u6BEB\u79D2\u6570\uFF08\u9ED8\u8BA4 300\uFF09\uFF0C\u5E94\u5BF9\u7F16\u8F91\u5668\u591A\u6B21\u5199\u5165 */\n debounceMs?: number;\n /** \u901A\u8FC7 onChange \u901A\u77E5\u65F6\u662F\u5426\u4F20\u5165\uFF08\u65E7\u503C, \u65B0\u503C, \u6765\u6E90\uFF09 */\n logger?: (message: string) => void;\n}\n\nexport class ConfigStore<T> {\n private readonly options: ConfigStoreOptions<T>;\n private data: T;\n private readonly listeners: Array<(data: T, source: ConfigChangeSource) => void> = [];\n private watcher: { close(): void } | undefined = undefined;\n private debounceTimer: NodeJS.Timeout | undefined = undefined;\n private selfWrite = false;\n\n public constructor(options: ConfigStoreOptions<T>) {\n this.options = options;\n const result = readOrCreate<T>(options);\n this.data = result.data;\n if (options.watch !== false) this.watch();\n // \u767B\u8BB0\u5230\u5168\u5C40\u6CE8\u518C\u8868\uFF0C\u4F9B /reload \u7EDF\u4E00\u91CD\u8F7D\n configStoreRegistry.add(this as unknown as ConfigStore<unknown>);\n }\n\n /** \u5F53\u524D\u751F\u6548\u914D\u7F6E\uFF08\u5185\u5B58\u7F13\u5B58\uFF09 */\n public get(): T {\n return this.data;\n }\n\n /**\n * \u66F4\u65B0\u914D\u7F6E\uFF1A\u5199\u5165\u6587\u4EF6\uFF08\u539F\u5B50\u5199\uFF09\u2192 \u66F4\u65B0\u7F13\u5B58 \u2192 \u901A\u77E5\u8BA2\u9605\u8005\uFF08\u6765\u6E90 self\uFF09\u3002\n * \u901A\u8FC7 ConfigStore \u66F4\u65B0\u7684\u65E0\u9700\u4F9D\u8D56\u6587\u4EF6 watch \u4E8B\u4EF6\u3002\n */\n public update(next: T): T {\n const resolved = path.resolve(process.cwd(), this.options.path);\n this.selfWrite = true;\n try {\n writeConfigFile(resolved, next);\n } finally {\n this.selfWrite = false;\n }\n this.data = next;\n for (const listener of this.listeners) {\n try {\n listener(next, 'self');\n } catch (error) {\n this.options.logger?.(`[config-manager] onChange(self) \u56DE\u8C03\u5F02\u5E38: ${String(error)}`);\n }\n }\n return this.data;\n }\n\n /** \u4ECE\u78C1\u76D8\u91CD\u65B0\u8BFB\u53D6\u914D\u7F6E\uFF08\u4E22\u5F03\u7F13\u5B58\uFF09\u3002\u6587\u4EF6\u53D8\u5316\u65F6\u901A\u77E5\u8BA2\u9605\u8005\uFF08\u6765\u6E90 file / reload\uFF09\u3002 */\n public reload(source: ConfigChangeSource = 'reload'): T {\n const result = readOrCreate<T>(this.options);\n if (!isDeepEqual(result.data, this.data)) {\n this.data = result.data;\n for (const listener of this.listeners) {\n try {\n listener(result.data, source);\n } catch (error) {\n this.options.logger?.(`[config-manager] onChange(${source}) \u56DE\u8C03\u5F02\u5E38: ${String(error)}`);\n }\n }\n }\n return this.data;\n }\n\n /** \u8BA2\u9605\u914D\u7F6E\u53D8\u66F4\uFF08\u6570\u636E\u53D8\u5316\u65F6\u89E6\u53D1\uFF09\u3002\u8FD4\u56DE\u53D6\u6D88\u8BA2\u9605\u51FD\u6570\u3002 */\n public onChange(listener: (data: T, source: ConfigChangeSource) => void): () => void {\n this.listeners.push(listener);\n return () => {\n const index = this.listeners.indexOf(listener);\n if (index >= 0) this.listeners.splice(index, 1);\n };\n }\n\n /**\n * \u76D1\u542C\u6587\u4EF6\u53D8\u5316\u81EA\u52A8\u70ED\u91CD\u8F7D\u3002\n * - \u9632\u6296\uFF1A\u7F16\u8F91\u5668/\u6279\u91CF\u5DE5\u5177\u8FDE\u7EED\u5199\u5165\u65F6\u53EA\u5728\u505C\u987F\u540E\u91CD\u8F7D\u4E00\u6B21\n * - \u81EA\u5199\u6291\u5236\uFF1Aupdate() \u81EA\u5DF1\u5199\u5165\u65F6\u6807\u8BB0 selfWrite\uFF0C\u5FFD\u7565\u968F\u540E\u7684 change \u4E8B\u4EF6\n */\n public watch(): void {\n if (this.watcher) return;\n const resolved = path.resolve(process.cwd(), this.options.path);\n const dir = path.dirname(resolved);\n const filename = path.basename(resolved);\n fs.mkdirSync(dir, { recursive: true });\n\n let lastReported: string | undefined = undefined;\n const listener = () => { const changedFile = filename;\n if (changedFile !== filename) return;\n if (this.selfWrite) return;\n if (this.debounceTimer) clearTimeout(this.debounceTimer);\n this.debounceTimer = setTimeout(() => {\n try {\n const before = JSON.stringify(this.data);\n this.reload('file');\n const after = JSON.stringify(this.data);\n if (before !== after) {\n const reported = `${new Date().toISOString()} ${this.options.path} -> \u5DF2\u70ED\u91CD\u8F7D`;\n if (reported !== lastReported) {\n lastReported = reported;\n this.options.logger?.(reported);\n }\n }\n } catch (error) {\n this.options.logger?.(`[config-manager] \u70ED\u91CD\u8F7D\u5931\u8D25: ${String(error)}`);\n }\n }, this.options.debounceMs ?? 300);\n };\n fs.watchFile(resolved, { interval: 1000 }, listener);\n this.watcher = { close: () => fs.unwatchFile(resolved, listener) };\n }\n\n /** \u505C\u6B62\u6587\u4EF6\u76D1\u542C */\n public dispose(): void {\n if (this.debounceTimer) clearTimeout(this.debounceTimer);\n if (this.watcher) {\n this.watcher.close();\n this.watcher = undefined;\n }\n }\n}\n\n/** \u6DF1\u5EA6\u6BD4\u8F83\u4E24\u4E2A JSON \u503C\u662F\u5426\u76F8\u7B49\uFF08\u7528\u4E8E\u5224\u65AD\u53D8\u66F4\u662F\u5426\u9700\u8981\u901A\u77E5\uFF09\u3002 */\nfunction isDeepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (typeof a !== typeof b) return false;\n if (a === null || b === null) return a === b;\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false;\n return a.every((item, index) => isDeepEqual(item, b[index]));\n }\n if (typeof a === 'object' && typeof b === 'object') {\n const aObj = a as Record<string, unknown>;\n const bObj = b as Record<string, unknown>;\n const aKeys = Object.keys(aObj);\n const bKeys = Object.keys(bObj);\n if (aKeys.length !== bKeys.length) return false;\n return aKeys.every(key => isDeepEqual(aObj[key], bObj[key]));\n }\n return false;\n}", "import path from 'node:path';\nimport Constants from '@/constants';\nimport { readConfigFile, writeConfigFile, ConfigStore, MigrationStep } from '@/config/manager';\n\n// ---------------------------------------------------------------------------\n// \u529F\u80FD\u6A21\u5757\u72EC\u7ACB\u914D\u7F6E\u6587\u4EF6\u6CE8\u518C\u8868\n//\n// \u5386\u53F2\u80CC\u666F\uFF1A\u6240\u6709\u529F\u80FD\u7684\u7FA4\u804A/\u64CD\u4F5C\u4EBA\u767D\u540D\u5355\u539F\u5148\u6324\u5728 bot-config.json \u7684\n// `function*` \u5B57\u6BB5\u91CC\u3002\u73B0\u5DF2\u62C6\u5206\u4E3A\u5404\u529F\u80FD\u81EA\u5DF1\u7684\u914D\u7F6E\u6587\u4EF6\uFF08configs/features/*.json\uFF09\uFF0C\n// \u6BCF\u4E2A\u6587\u4EF6\u5E26 version \u5B57\u6BB5\uFF0C\u672A\u6765\u7684\u7ED3\u6784\u53D8\u66F4\u901A\u8FC7 versions/migrations \u5347\u7EA7\u3002\n//\n// \u672C\u6A21\u5757\u804C\u8D23\uFF1A\n// 1. \u58F0\u660E\u6BCF\u4E2A\u529F\u80FD\u7684\u72EC\u7ACB\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84\u3001\u9ED8\u8BA4\u503C\u3001\u7248\u672C\u4E0E\u8FC1\u79FB\u6B65\u9AA4\n// 2. \u63D0\u4F9B getFeatureConfig / getFeatureGroups \u8BBF\u95EE\u751F\u6548\u914D\u7F6E\uFF08ConfigStore \u70ED\u91CD\u8F7D\uFF09\n// 3. \u63D0\u4F9B migrateLegacyFunctionFields() \u628A bot-config.json \u91CC\u7684\u65E7 function*\n// \u5B57\u6BB5\u4E00\u6B21\u6027\u62C6\u8FDB\u5404\u529F\u80FD\u914D\u7F6E\u6587\u4EF6\uFF0C\u5E76\u4ECE bot-config.json \u79FB\u9664\n// ---------------------------------------------------------------------------\n\nexport interface FeatureConfig {\n version: number;\n /** \u751F\u6548\u7FA4\u804A */\n groups: number[];\n /** \u64CD\u4F5C\u4EBA\u767D\u540D\u5355\uFF08\u5982\u7BA1\u7406\u529F\u80FD\u7684\u7BA1\u7406\u5458 QQ\uFF09 */\n operators?: number[];\n /**\n * Discord \u7BA1\u7406\u5458\u767D\u540D\u5355\uFF08Discord \u7528\u6237 ID\uFF0C\u53EF\u5E26 `dc:` \u524D\u7F00\uFF09\u3002\n *\n * \u7528\u9014\uFF1A\u6865\u63A5\u9891\u9053\u91CC\u6267\u884C\u7BA1\u7406\u5458\u547D\u4EE4\u65F6\uFF0C\u9664\u300C\u670D\u52A1\u5668\u62E5\u6709\u8005/\u7BA1\u7406\u5458\u6743\u9650\u300D\u5916\uFF0C\n * \u989D\u5916\u5141\u8BB8\u8FD9\u4E9B\u7528\u6237\u3002Discord \u7528\u6237 ID \u4E3A snowflake \u5B57\u7B26\u4E32\uFF0C\u4E0D\u80FD\u585E\u8FDB operators\uFF08number[]\uFF09\u3002\n *\n * \u793A\u4F8B\uFF08configs/features/management.json\uFF09\uFF1A\n * ```jsonc\n * {\n * \"version\": 1,\n * \"groups\": [703453948],\n * \"operators\": [2308465862], // QQ \u7BA1\u7406\u5458\n * \"discordOperators\": [\"123456789012345678\"] // Discord \u7BA1\u7406\u5458\uFF08\u53EF\u5199 dc: \u524D\u7F00\uFF09\n * }\n * ```\n */\n discordOperators?: string[];\n /** \u5176\u4ED6\u81EA\u5B9A\u4E49\u5B57\u6BB5\uFF08\u5982 Bili \u5173\u6CE8\u5217\u8868\uFF09 */\n extra?: Record<string, unknown>;\n}\n\nexport interface FeatureDefinition {\n /** \u529F\u80FD id\uFF0C\u5BF9\u5E94 configs/features/<id>.json */\n id: string;\n /** \u9ED8\u8BA4\u7FA4\u804A */\n defaultGroups: number[];\n /** \u9ED8\u8BA4\u64CD\u4F5C\u4EBA\u767D\u540D\u5355 */\n defaultOperators?: number[];\n /** bot-config.json \u4E2D\u7684\u65E7\u5B57\u6BB5\u540D\uFF08\u8FC1\u79FB\u7528\uFF09 */\n legacyGroupsFields: string[];\n /** bot-config.json \u4E2D\u65E7\u64CD\u4F5C\u4EBA\u5B57\u6BB5\u540D\uFF08\u8FC1\u79FB\u7528\uFF09 */\n legacyOperatorsField?: string;\n /** \u5176\u4ED6\u65E7\u5B57\u6BB5\u540D \u2192 \u8FC1\u79FB\u5230 extra \u7684\u952E */\n\n legacyExtraFields?: Record<string, string>;\n /** \u5F53\u524D\u914D\u7F6E\u7248\u672C */\n version?: number;\n /** \u8FC1\u79FB\u6B65\u9AA4 */\n migrations?: import('@/config/manager').MigrationStep[];\n}\n\n/**\n * GitHub \u529F\u80FD\u914D\u7F6E\uFF08v2\uFF09\u3002\n *\n * groups \u4ECE v1 \u7684 `number[]`\uFF08\u5168\u4ED3\u5E93\u767D\u540D\u5355\u7FA4\uFF09\u5347\u7EA7\u4E3A\n * `Record<\u4ED3\u5E93 full_name, \u8BA2\u9605\u8BE5\u4ED3\u5E93\u7684\u7FA4\u53F7[]>`\u2014\u2014webhook \u4E8B\u4EF6\u53EA\u63A8\u9001\u5230\n * \u5BF9\u5E94\u4ED3\u5E93\u8BA2\u9605\u7684\u7FA4\uFF0C\u5B9E\u73B0\"\u4ED3\u5E93\u7EA7\u8BA2\u9605\"\u3002\n */\nexport interface GithubFeatureConfig {\n version: number;\n /** \u4ED3\u5E93 full_name\uFF08\u5982 Anvil-Dev/AnvilCraft\uFF09\u2192 \u8BA2\u9605\u7FA4\u53F7\u5217\u8868 */\n groups: Record<string, number[]>;\n}\n\n/**\n * \u65B0\u4EBA\u6B22\u8FCE\u914D\u7F6E\uFF08v1\uFF09\u3002\n *\n * configs/features/welcome.json\uFF1A\n * ```json\n * {\n * \"version\": 1,\n * \"welcomes\": [\n * {\n * \"group\": [123456, 234567],\n * \"msg\": [\"\u6B22\u8FCE ${at} \u52A0\u5165\u7FA4\u804A\", \"\u8BF7\u67E5\u770B\u7FA4\u516C\u544A\"]\n * }\n * ]\n * }\n * ```\n * `msg` \u53EF\u4EE5\u662F\u5B57\u7B26\u4E32\u6216\u5B57\u7B26\u4E32\u6570\u7EC4\uFF08\u6570\u7EC4\u6BCF\u9879\u4E00\u884C\uFF0C\u53D1\u9001\u65F6\u6309\u6362\u884C\u62FC\u63A5\uFF09\u3002\n */\nexport interface WelcomeFeatureConfig {\n version: number;\n welcomes: WelcomeEntry[];\n}\n\n/** \u5355\u6761\u6B22\u8FCE\u914D\u7F6E\uFF1A\u9002\u7528\u7684\u7FA4\u5217\u8868 + \u6B22\u8FCE\u8BED\u6A21\u677F\uFF08${at} \u66FF\u6362\u4E3A @\u65B0\u6210\u5458\uFF1B\u652F\u6301\u591A\u884C\u6570\u7EC4\uFF09 */\nexport interface WelcomeEntry {\n group: number[];\n msg: string | string[];\n}\n\nexport interface LegacyFunctionValues {\n featureId: string;\n groups?: number[];\n operators?: number[];\n extra?: Record<string, unknown>;\n}\n\nconst FEATURES_DIR = path.resolve(process.cwd(), 'configs', 'features');\n\n// ---------------------------------------------------------------------------\n// \u529F\u80FD\u5B9A\u4E49\u8868\n// ---------------------------------------------------------------------------\n\nexport const FEATURE_DEFINITIONS: FeatureDefinition[] = [\n {\n id: 'github',\n defaultGroups: [],\n legacyGroupsFields: ['functionGithubGroup']\n },\n {\n id: 'management',\n defaultGroups: [],\n defaultOperators: [],\n legacyGroupsFields: ['functionManagementGroup'],\n legacyOperatorsField: 'functionManagementOperator'\n },\n {\n id: 'parentheses',\n defaultGroups: [],\n legacyGroupsFields: ['functionParenthesesGroup']\n },\n {\n id: 'poke',\n defaultGroups: [],\n legacyGroupsFields: ['functionPokeGroup']\n },\n {\n id: 'minecraft',\n defaultGroups: [],\n legacyGroupsFields: ['functionMinecraftGroup']\n },\n {\n id: 'modrinth',\n defaultGroups: [],\n legacyGroupsFields: ['functionModrinthGroup']\n },\n {\n id: 'bili',\n defaultGroups: [],\n legacyGroupsFields: ['functionBiliGroup'],\n legacyExtraFields: { functionBiliFollow: 'follow' }\n },\n {\n id: 'command',\n defaultGroups: [],\n legacyGroupsFields: ['functionCommandGroup']\n }\n];\n\n// ---------------------------------------------------------------------------\n// \u914D\u7F6E\u4ED3\u5E93\uFF08ConfigStore\uFF1A\u72EC\u7ACB\u6587\u4EF6 + \u70ED\u91CD\u8F7D\uFF09\n// ---------------------------------------------------------------------------\n\nconst stores = new Map<string, ConfigStore<FeatureConfig>>();\n\nfunction isNumberArray(value: unknown): value is number[] {\n return Array.isArray(value) && value.every(item => typeof item === 'number' && Number.isSafeInteger(item));\n}\n\nfunction isGithubGroups(\n value: unknown\n): value is Record<string, number[]> {\n return (\n !!value &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n Object.entries(value as Record<string, unknown>).every(\n ([repo, groups]) =>\n typeof repo === 'string' && repo.includes('/') && isNumberArray(groups)\n )\n );\n}\n\n/** github.json \u7684\u9ED8\u8BA4\u5DE5\u5382\uFF1Av2 \u7A7A\u8BA2\u9605\uFF08\u65E0\u4ED3\u5E93\u8BA2\u9605\uFF09 */\nfunction githubFactory(): GithubFeatureConfig {\n return { version: 2, groups: {} };\n}\n\n/** github.json \u7684 v1 \u2192 v2 \u8FC1\u79FB\uFF1A\u65E7 groups \u6570\u7EC4\uFF08\u5168\u4ED3\u5E93\u767D\u540D\u5355\u7FA4\uFF09\u62C6\u7ED9\u9ED8\u8BA4\u4ED3\u5E93 */\nfunction migrateGithubV1ToV2(data: Record<string, unknown>): Record<string, unknown> {\n const legacyGroups = isNumberArray(data.groups) ? data.groups : [];\n return {\n groups: legacyGroups.length > 0 ? { 'Anvil-Dev/AnvilCraft': legacyGroups } : {}\n };\n}\n\n/** github.json \u89C4\u8303\u5316\uFF1A\u63A5\u53D7 v2 Record\uFF1B\u975E\u6CD5/\u635F\u574F\u8FD4\u56DE null \u89E6\u53D1\u5907\u4EFD\u91CD\u5EFA */\nfunction normalizeGithubConfig(raw: unknown): GithubFeatureConfig | null {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null;\n const record = raw as Record<string, unknown>;\n if (isGithubGroups(record.groups)) {\n return { version: 2, groups: record.groups };\n }\n // v1 \u9057\u7559\uFF08groups \u4E3A\u6570\u7EC4\uFF09\uFF1A\u8F6C\u4E3A v2 \u7ED3\u6784\uFF08\u4E0D\u843D\u76D8\uFF0C\u8BFB\u53D6\u65F6\u52A8\u6001\u8F6C\u6362\uFF09\n if (isNumberArray(record.groups)) {\n const groups: Record<string, number[]> =\n record.groups.length > 0 ? { 'Anvil-Dev/AnvilCraft': record.groups } : {};\n return { version: 2, groups };\n }\n return { version: 2, groups: {} };\n}\n\nfunction normalizeFeatureConfig(def: FeatureDefinition, raw: unknown): FeatureConfig | null {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null;\n const record = raw as Record<string, unknown>;\n const groups = record.groups !== undefined && isNumberArray(record.groups) ? record.groups : def.defaultGroups;\n const operators =\n record.operators !== undefined\n ? isNumberArray(record.operators)\n ? record.operators\n : def.defaultOperators\n : def.defaultOperators;\n const extra =\n record.extra !== undefined && record.extra && typeof record.extra === 'object' && !Array.isArray(record.extra)\n ? (record.extra as Record<string, unknown>)\n : undefined;\n // Discord \u7528\u6237 ID\uFF1Asnowflake \u5B57\u7B26\u4E32\uFF0C\u5141\u8BB8\u5E26 dc: \u524D\u7F00\uFF1B\u975E\u6CD5\u9879\u5254\u9664\n const discordOperators = Array.isArray(record.discordOperators)\n ? record.discordOperators\n .filter((item): item is string => typeof item === 'string' && item.trim().length > 0)\n .map(item => normalizeDiscordUserId(item))\n : undefined;\n const config: FeatureConfig = {\n version: def.version ?? 1,\n groups,\n ...(operators ? { operators } : {}),\n ...(discordOperators ? { discordOperators } : {}),\n ...(extra ? { extra } : {})\n };\n return config;\n}\n\n/** \u5F52\u4E00\u5316 Discord \u7528\u6237 ID\uFF1A\u53BB\u6389 dc: \u524D\u7F00\u4E0E\u9996\u5C3E\u7A7A\u767D\uFF0C\u7EDF\u4E00\u5B58\u88F8 snowflake */\nexport function normalizeDiscordUserId(raw: string): string {\n return raw.trim().replace(/^dc:/i, '');\n}\n\nfunction createStore(def: FeatureDefinition): ConfigStore<FeatureConfig> {\n const filePath = path.join('configs', 'features', `${def.id}.json`);\n return new ConfigStore<FeatureConfig>({\n path: filePath,\n factory: () => ({\n version: def.version ?? 1,\n groups: def.defaultGroups,\n ...(def.defaultOperators ? { operators: def.defaultOperators } : {}),\n ...(def.legacyExtraFields ? { extra: {} } : {})\n }),\n version: def.version ?? 1,\n ...(def.migrations ? { migrations: def.migrations } : {}),\n normalize: raw => normalizeFeatureConfig(def, raw)\n });\n}\n\nfunction getStore(def: FeatureDefinition): ConfigStore<FeatureConfig> {\n let store = stores.get(def.id);\n if (!store) {\n store = createStore(def);\n stores.set(def.id, store);\n }\n return store;\n}\n\n/** \u8BFB\u53D6\u529F\u80FD\u7684\u751F\u6548\u914D\u7F6E\uFF08\u81EA\u52A8\u521B\u5EFA\u9ED8\u8BA4\u6587\u4EF6 + \u70ED\u91CD\u8F7D\uFF09 */\nexport function getFeatureConfig(defId: string): FeatureConfig {\n const def = FEATURE_DEFINITIONS.find(item => item.id === defId);\n if (!def) throw new Error(`\u672A\u6CE8\u518C\u7684\u529F\u80FD\u914D\u7F6E: ${defId}`);\n return getStore(def).get();\n}\n\n/** \u8BFB\u53D6\u529F\u80FD\u7684\u751F\u6548\u7FA4\u804A\u767D\u540D\u5355 */\nexport function getFeatureGroups(defId: string): number[] {\n return getFeatureConfig(defId).groups;\n}\n\n/** \u8BA2\u9605\u529F\u80FD\u914D\u7F6E\u6587\u4EF6\u53D8\u5316\uFF08\u5916\u90E8\u6539\u6587\u4EF6\u81EA\u52A8\u70ED\u91CD\u8F7D\uFF09 */\nexport function watchFeatureConfig(defId: string, listener: (config: FeatureConfig, source: import('@/config/manager').ConfigChangeSource) => void): () => void {\n const def = FEATURE_DEFINITIONS.find(item => item.id === defId);\n if (!def) throw new Error(`\u672A\u6CE8\u518C\u7684\u529F\u80FD\u914D\u7F6E: ${defId}`);\n return getStore(def).onChange(listener);\n}\n\n// ---------------------------------------------------------------------------\n// GitHub \u914D\u7F6E\uFF08v2\uFF1A\u4ED3\u5E93\u7EA7\u8BA2\u9605\uFF09\n// \u4E0E\u901A\u7528 FeatureConfig \u4E0D\u540C\uFF1Agroups \u662F \u4ED3\u5E93 full_name \u2192 \u8BA2\u9605\u7FA4\u53F7[]\u3002\n// \u72EC\u7ACB store \u7BA1\u7406\uFF08configs/features/github.json\uFF0Cv1 \u2192 v2 \u81EA\u52A8\u8FC1\u79FB\uFF09\u3002\n// ---------------------------------------------------------------------------\n\nconst githubStore = new ConfigStore<GithubFeatureConfig>({\n path: 'configs/features/github.json',\n version: 2,\n migrations: [{ from: 1, to: 2, migrate: migrateGithubV1ToV2 }],\n factory: githubFactory,\n normalize: normalizeGithubConfig\n});\n\n/** \u8BFB\u53D6 github \u751F\u6548\u914D\u7F6E\uFF08v2\uFF1A\u4ED3\u5E93 \u2192 \u8BA2\u9605\u7FA4\u6620\u5C04\uFF09 */\nexport function getGithubConfig(): GithubFeatureConfig {\n return githubStore.get();\n}\n\n/** \u8BFB\u53D6\u67D0\u4ED3\u5E93\u7684\u8BA2\u9605\u7FA4\u5217\u8868\uFF08\u65E0\u8BA2\u9605\u8FD4\u56DE\u7A7A\u6570\u7EC4\uFF09 */\nexport function getGithubSubscribers(repository: string): number[] {\n return getGithubConfig().groups[repository] || [];\n}\n\n/** \u5224\u65AD\u67D0\u7FA4\u662F\u5426\u8BA2\u9605\u4E86\u4EFB\u610F\u4ED3\u5E93\uFF08\u7528\u4E8E processMessage \u7684\u542F\u7528\u5224\u65AD\uFF09 */\nexport function isGithubEnabledGroup(groupId: number): boolean {\n return Object.values(getGithubConfig().groups).some(list => list.includes(groupId));\n}\n\n/**\n * \u8BA2\u9605\uFF1A\u628A\u7FA4\u52A0\u5165\u4ED3\u5E93\u7684\u8BA2\u9605\u5217\u8868\uFF08\u5E42\u7B49\uFF1B\u5199\u5165\u540E\u70ED\u751F\u6548\uFF09\u3002\n * @returns \u8BE5\u4ED3\u5E93\u8BA2\u9605\u540E\u7684\u5B8C\u6574\u7FA4\u5217\u8868\n */\nexport function subscribeGithubRepository(repository: string, groupId: number): number[] {\n const current = getGithubConfig();\n const subscribers = Array.from(new Set([...(current.groups[repository] || []), groupId]));\n const next: GithubFeatureConfig = {\n version: 2,\n groups: { ...current.groups, [repository]: subscribers }\n };\n githubStore.update(next);\n return subscribers;\n}\n\n/**\n * \u53D6\u6D88\u8BA2\u9605\uFF1A\u628A\u7FA4\u4ECE\u4ED3\u5E93\u7684\u8BA2\u9605\u5217\u8868\u79FB\u9664\u3002\n * @returns \u8BE5\u4ED3\u5E93\u8BA2\u9605\u540E\u7684\u5B8C\u6574\u7FA4\u5217\u8868\uFF08\u7FA4\u53F7\u4E0D\u5B58\u5728\u5219\u4E0D\u53D8\uFF09\n */\nexport function unsubscribeGithubRepository(repository: string, groupId: number): number[] {\n const current = getGithubConfig();\n const subscribers = (current.groups[repository] || []).filter(id => id !== groupId);\n const nextGroups: Record<string, number[]> = { ...current.groups };\n if (subscribers.length > 0) nextGroups[repository] = subscribers;\n else delete nextGroups[repository];\n const next: GithubFeatureConfig = { version: 2, groups: nextGroups };\n githubStore.update(next);\n return subscribers;\n}\n\n/** \u4ED3\u5E93\u540D\u683C\u5F0F\u6821\u9A8C\uFF08owner/name\uFF0C\u5747\u5141\u8BB8\u5B57\u6BCD\u6570\u5B57 . _ -\uFF09 */\nexport function isValidRepositoryName(repository: string): boolean {\n return /^[A-Za-z0-9_.-]+\\/[A-Za-z0-9_.-]+$/.test(repository);\n}\n\n/** GitHub owner \u683C\u5F0F\u6821\u9A8C\uFF08\u5B57\u6BCD\u6570\u5B57 . _ -\uFF0C\u4E0D\u542B\u659C\u6760\uFF09 */\nexport function isValidRepositoryOwner(owner: string): boolean {\n return /^[A-Za-z0-9_.-]+$/.test(owner);\n}\n\n/**\n * \u5224\u65AD\u67D0 QQ \u7528\u6237\u662F\u5426\u4E3A\u7BA1\u7406\u5458\uFF08\u64CD\u4F5C\u4EBA\u767D\u540D\u5355\uFF09\u3002\n *\n * \u767D\u540D\u5355\u590D\u7528 management \u529F\u80FD\u7684 operators \u914D\u7F6E\uFF0C\u4F7F\u300C\u8C01\u662F\u7BA1\u7406\u5458\u300D\u5728\u5168\u9879\u76EE\u53EA\u6709\u4E00\u4E2A\u5B9A\u4E49\uFF0C\n * \u4E0E /pardon \u7B49\u7BA1\u7406\u547D\u4EE4\u4FDD\u6301\u4E00\u81F4\u3002\n *\n * @param qqUserId \u53D1\u9001\u8005 QQ \u53F7\n */\nexport function isOperator(qqUserId: number): boolean {\n return getFeatureConfig('management').operators?.includes(qqUserId) ?? false;\n}\n\n/**\n * \u5224\u65AD\u67D0 Discord \u7528\u6237\u662F\u5426\u4E3A\u7BA1\u7406\u5458\uFF08\u914D\u7F6E\u767D\u540D\u5355\uFF09\u3002\n *\n * \u4E0E\u300C\u670D\u52A1\u5668\u62E5\u6709\u8005/\u7BA1\u7406\u5458\u6743\u9650\u300D\u4E92\u8865\uFF1ADiscord \u4FA7\u547D\u4EE4\u6E90\u7528 isDiscordAdmin() \u5224\u5B9A\u670D\u52A1\u5668\u6743\u9650\uFF0C\n * \u8FD9\u91CC\u989D\u5916\u5141\u8BB8 management.json \u7684 `discordOperators` \u540D\u5355\u6210\u5458\uFF08\u4FBF\u4E8E\u4E0D\u52A0\u670D\u52A1\u5668\u6743\u9650\u4E5F\u80FD\u7BA1\u7406\uFF09\u3002\n *\n * @param discordUserId Discord \u7528\u6237 ID\uFF08\u88F8 snowflake \u6216\u5E26 `dc:` \u524D\u7F00\uFF09\n */\nexport function isDiscordOperator(discordUserId: string): boolean {\n const list = getFeatureConfig('management').discordOperators;\n if (!list || list.length === 0) return false;\n const target = normalizeDiscordUserId(discordUserId);\n return list.some(item => normalizeDiscordUserId(item) === target);\n}\n\n// ---------------------------------------------------------------------------\n// \u65B0\u4EBA\u6B22\u8FCE\u914D\u7F6E\uFF08v1\uFF1Awelcomes \u6570\u7EC4\uFF09\n// ---------------------------------------------------------------------------\n\nfunction welcomeFactory(): WelcomeFeatureConfig {\n return { version: 1, welcomes: [] };\n}\n\nfunction normalizeWelcomeConfig(raw: unknown): WelcomeFeatureConfig | null {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null;\n const record = raw as Record<string, unknown>;\n const welcomes = Array.isArray(record.welcomes) ? record.welcomes : [];\n const normalized: WelcomeEntry[] = [];\n for (const item of welcomes) {\n if (!item || typeof item !== 'object' || Array.isArray(item)) continue;\n const entry = item as Record<string, unknown>;\n if (!Array.isArray(entry.group)) continue;\n const msg = normalizeWelcomeMsg(entry.msg);\n if (msg === null) continue;\n const group = entry.group.filter(g => typeof g === 'number' && Number.isSafeInteger(g));\n normalized.push({ group, msg });\n }\n return { version: 1, welcomes: normalized };\n}\n\n/** \u89C4\u8303\u5316 msg \u5B57\u6BB5\uFF1A\u5B57\u7B26\u4E32\u6216\u5B57\u7B26\u4E32\u6570\u7EC4\uFF08\u6570\u7EC4\u5143\u7D20\u987B\u5168\u4E3A\u5B57\u7B26\u4E32\uFF09\uFF0C\u5426\u5219\u8FD4\u56DE null */\nfunction normalizeWelcomeMsg(raw: unknown): string | string[] | null {\n if (typeof raw === 'string') return raw;\n if (Array.isArray(raw)) {\n if (raw.every(item => typeof item === 'string')) return raw;\n return null;\n }\n return null;\n}\n\nconst welcomeStore = new ConfigStore<WelcomeFeatureConfig>({\n path: 'configs/features/welcome.json',\n version: 1,\n factory: welcomeFactory,\n normalize: normalizeWelcomeConfig\n});\n\n/** \u8BFB\u53D6\u65B0\u4EBA\u6B22\u8FCE\u751F\u6548\u914D\u7F6E\uFF08v1\uFF1Awelcomes \u6570\u7EC4\uFF09 */\nexport function getWelcomeConfig(): WelcomeFeatureConfig {\n return welcomeStore.get();\n}\n\n// ---------------------------------------------------------------------------\n// \u65E7\u5B57\u6BB5\u8FC1\u79FB\uFF1Abot-config.json \u7684 function* \u2192 \u5404\u529F\u80FD\u914D\u7F6E\u6587\u4EF6\n// ---------------------------------------------------------------------------\n\nconst BOT_CONFIG_PATH = path.resolve(process.cwd(), 'configs', 'bot-config.json');\n\nfunction readLegacyValues(def: FeatureDefinition, legacyRaw: Record<string, unknown>): LegacyFunctionValues | undefined {\n const groupsValue = def.legacyGroupsFields\n .map(field => legacyRaw[field])\n .find(value => value !== undefined);\n const groups = groupsValue !== undefined && isNumberArray(groupsValue) ? groupsValue : undefined;\n\n let operators: number[] | undefined = undefined;\n if (def.legacyOperatorsField) {\n const operatorValue = legacyRaw[def.legacyOperatorsField];\n if (operatorValue !== undefined && isNumberArray(operatorValue)) operators = operatorValue;\n }\n\n let extra: Record<string, unknown> | undefined = undefined;\n if (def.legacyExtraFields) {\n const extras: Record<string, unknown> = {};\n let extrasAny = false;\n for (const [legacyField, extraKey] of Object.entries(def.legacyExtraFields)) {\n const value = legacyRaw[legacyField];\n if (value !== undefined) {\n extras[extraKey] = value;\n extrasAny = true;\n }\n }\n if (extrasAny) extra = extras;\n }\n\n if (groups === undefined && operators === undefined && extra === undefined) return undefined;\n return {\n featureId: def.id,\n ...(groups !== undefined ? { groups } : {}),\n ...(operators !== undefined ? { operators } : {}),\n ...(extra !== undefined ? { extra } : {})\n };\n}\n\nfunction applyLegacyToStore(def: FeatureDefinition, legacy: LegacyFunctionValues): void {\n // github \u4F7F\u7528\u4E13\u7528 store\uFF08v2 \u4ED3\u5E93\u7EA7\u8BA2\u9605\uFF09\uFF0C\u65E7 groups \u6570\u7EC4\u62C6\u7ED9\u9ED8\u8BA4\u4ED3\u5E93\n if (def.id === 'github') {\n const legacyGroups = legacy.groups || [];\n const current = githubStore.get();\n const nextGroups: Record<string, number[]> = { ...current.groups };\n if (legacyGroups.length > 0 && Object.keys(nextGroups).length === 0) {\n nextGroups['Anvil-Dev/AnvilCraft'] = legacyGroups;\n }\n githubStore.update({ version: 2, groups: nextGroups });\n return;\n }\n const store = getStore(def);\n const current = store.get();\n const next: FeatureConfig = {\n version: def.version ?? 1,\n groups: legacy.groups ?? current.groups,\n ...(legacy.operators || current.operators ? { operators: legacy.operators ?? current.operators } : {}),\n ...(legacy.extra || current.extra ? { extra: legacy.extra ?? current.extra } : {})\n };\n store.update(next);\n}\n\n/**\n * \u628A bot-config.json \u91CC\u7684\u65E7 `function*` \u5B57\u6BB5\u4E00\u6B21\u6027\u62C6\u8FDB\u5404\u529F\u80FD\u914D\u7F6E\u6587\u4EF6\u3002\n * \u8FC1\u79FB\u5B8C\u6210\u540E\u4ECE bot-config.json \u5220\u9664\u8FD9\u4E9B\u5B57\u6BB5\u3002\u5E42\u7B49\uFF1A\u65E0\u65E7\u5B57\u6BB5\u65F6\u76F4\u63A5\u8FD4\u56DE\u3002\n */\nexport function migrateLegacyFunctionFields(): boolean {\n const legacyRaw = readConfigFile<Record<string, unknown>>(BOT_CONFIG_PATH);\n if (!legacyRaw) return false;\n\n let anyMigrated = false;\n for (const def of FEATURE_DEFINITIONS) {\n const legacy = readLegacyValues(def, legacyRaw);\n if (legacy) {\n applyLegacyToStore(def, legacy);\n anyMigrated = true;\n }\n }\n\n if (!anyMigrated) return false;\n\n // \u4ECE bot-config.json \u5220\u9664\u5DF2\u8FC1\u79FB\u7684 function* \u5B57\u6BB5\u5E76\u5199\u56DE\n const next = { ...legacyRaw };\n for (const def of FEATURE_DEFINITIONS) {\n for (const field of def.legacyGroupsFields) delete next[field];\n if (def.legacyOperatorsField) delete next[def.legacyOperatorsField];\n if (def.legacyExtraFields) {\n for (const legacyField of Object.keys(def.legacyExtraFields)) delete next[legacyField];\n }\n }\n writeConfigFile(BOT_CONFIG_PATH, next);\n return true;\n}\n\n/** \u4F9B ConfigStore \u89C2\u5BDF\u8005\u4F7F\u7528\u7684\u5185\u90E8\u5BFC\u51FA\uFF08\u6D4B\u8BD5\u7528\uFF09 */\nexport function __featureStores(): Map<string, ConfigStore<FeatureConfig>> {\n return stores;\n}\n\nexport type { MigrationStep };\nexport { FEATURES_DIR };\n\n// \u6A21\u5757\u52A0\u8F7D\u65F6\u7ACB\u5373\u6267\u884C\u4E00\u6B21\u65E7\u5B57\u6BB5\u8FC1\u79FB\uFF1A\n// \u4FDD\u8BC1\u4EFB\u4F55\u529F\u80FD\u6A21\u5757\u9996\u6B21 getFeatureConfig() \u4E4B\u524D\uFF0Cbot-config.json \u7684\n// function* \u65E7\u5B57\u6BB5\u5DF2\u62C6\u5165\u5404\u81EA\u7684 features/*.json \u914D\u7F6E\u6587\u4EF6\u3002\nmigrateLegacyFunctionFields();", "// \u5BFC\u5165winston\u5E93\u4E2D\u7684createLogger\u3001Logger\u3001transports\u548Cformat\u6A21\u5757\uFF0C\u4EE5\u53CAdayjs\u5E93\nimport { createLogger, format, Logger, transports } from 'winston';\nimport 'winston-daily-rotate-file';\nimport dayjs from 'dayjs';\n\n// \u5B9A\u4E49LoggerFactory\u7C7B\nexport class LoggerFactory {\n /**\n * \u521B\u5EFA\u4E00\u4E2ALogger\u5B9E\u4F8B\n *\n * @param name \u65E5\u5FD7\u7684\u540D\u79F0\uFF0C\u7528\u4E8E\u5728\u65E5\u5FD7\u6761\u76EE\u4E2D\u6807\u8BC6\uFF0C\u53EF\u9009\uFF0C\u9ED8\u8BA4\u4E3Aundefined\n * @param path \u65E5\u5FD7\u6587\u4EF6\u7684\u5B58\u50A8\u8DEF\u5F84\uFF0C\u53EF\u9009\uFF0C\u9ED8\u8BA4\u4E3A\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\n * @param logLevel \u65E5\u5FD7\u7EA7\u522B\uFF0C\u53EF\u9009\uFF0C\u9ED8\u8BA4\u4E3A'info'\uFF0C\u53EF\u9009\u503C\u5305\u62EC'debug'\u3001'info'\u3001'warn'\u548C'error'\n * @returns \u8FD4\u56DE\u4E00\u4E2A\u914D\u7F6E\u597D\u7684Logger\u5B9E\u4F8B\n */\n public static createLogger(\n name: string | undefined = undefined,\n path: string = process.cwd(),\n logLevel: 'debug' | 'info' | 'warn' | 'error' = 'info'\n ): Logger {\n // \u914D\u7F6E\u6587\u4EF6\u65E5\u5FD7\u8F93\u51FA\n const dailyRotateFile = new transports.DailyRotateFile({\n // \u6307\u5B9A\u65E5\u5FD7\u6587\u4EF6\u7684\u540D\u79F0\u548C\u8DEF\u5F84\n filename: `%DATE%.log`,\n dirname: path,\n datePattern: 'YYYY-MM-DD',\n // \u4F7F\u7528\u81EA\u5B9A\u4E49\u6253\u5370\u683C\u5F0F\n format: format.printf(\n info => `[${dayjs().format('YYYY-MM-DD HH:mm:ss')}]${name ? `[${name}]` : ''}[${info.level}] ${info.message}`\n ),\n maxSize: '16k',\n zippedArchive: true\n });\n\n // \u914D\u7F6E\u63A7\u5236\u53F0\u65E5\u5FD7\u8F93\u51FA\n const console = new transports.Console({\n // \u7ED3\u5408\u989C\u8272\u683C\u5F0F\u5316\u548C\u81EA\u5B9A\u4E49\u6253\u5370\u683C\u5F0F\n format: format.combine(\n format.colorize(),\n format.printf(\n info => `[${dayjs().format('YYYY-MM-DD HH:mm:ss')}]${name ? `[${name}]` : ''}[${info.level}] ${info.message}`\n )\n )\n });\n\n // \u4F7F\u7528winston\u7684createLogger\u65B9\u6CD5\u521B\u5EFA\u5E76\u8FD4\u56DE\u4E00\u4E2ALogger\u5B9E\u4F8B\n return createLogger({\n // \u8BBE\u7F6E\u65E5\u5FD7\u7EA7\u522B\n level: logLevel,\n // \u914D\u7F6E\u65E5\u5FD7\u4F20\u8F93\uFF08\u8F93\u51FA\uFF09\u7684\u65B9\u5F0F\uFF0C\u5305\u62EC\u63A7\u5236\u53F0\u548C\u6587\u4EF6\n transports: [console, dailyRotateFile]\n });\n }\n}\n", "import fs from 'node:fs';\nimport path from 'node:path';\nimport puppeteer, { Browser, Page } from 'puppeteer';\nimport { botConfig } from '@/config';\n\nexport class Template {\n private readonly templatePath: string;\n private readonly templateName: string;\n private processor: ((template: string) => string)[] = [];\n\n public constructor(templateName: string, templatePath: string) {\n this.templateName = templateName;\n this.templatePath = templatePath;\n }\n\n public static load(templateName: string, templatePath: string = 'src/template'): Template {\n return new Template(templateName, templatePath);\n }\n\n private async loadTemplate(templateName: string, templatePath: string): Promise<string> {\n return fs.readFileSync(`${templatePath}/${templateName}.html`, 'utf8');\n }\n\n public arg(param: string, object: any): Template {\n this.processor.push(template => {\n while (template.includes(`{{${param}}}`)) {\n template = template.replace(`{{${param}}}`, object.toString());\n }\n return template;\n });\n return this;\n }\n\n /**\n * \u8FD4\u56DE\u6A21\u677F\u6587\u4EF6\u7684\u7EDD\u5BF9\u8DEF\u5F84\uFF08\u7528\u4E8E\u4EE5\u6A21\u677F\u6240\u5728\u76EE\u5F55\u4E3A\u76F8\u5BF9\u8DEF\u5F84\u6839\u52A0\u8F7D CSS \u7B49\u8D44\u6E90\uFF09\u3002\n */\n public file(): string {\n return path.resolve(this.templatePath, `${this.templateName}.html`);\n }\n\n /**\n * \u6E32\u67D3\u6A21\u677F\uFF1A\u586B\u5145\u6240\u6709\u53C2\u6570\uFF0C\u8FD4\u56DE\u5904\u7406\u540E\u7684 HTML \u5B57\u7B26\u4E32\u3002\n * \u6CE8\u610F\uFF1ACSS \u4E0D\u518D\u5185\u8054\u8FDB HTML\uFF08\u6539\u7531 file:// \u76F8\u5BF9\u8DEF\u5F84\u52A0\u8F7D\uFF09\uFF0C\n * \u4F46\u4FDD\u7559 {{style sheet}} \u5360\u4F4D\u7B26\u517C\u5BB9\u65E7\u6A21\u677F\u3002\n */\n public handler(): Promise<string> {\n return new Promise((resolve, reject) => {\n this.loadTemplate(this.templateName, this.templatePath)\n .then(template => {\n try {\n this.processor.forEach(processor => {\n template = processor(template);\n });\n resolve(template);\n } catch (e) {\n reject(e);\n }\n })\n .catch(reject);\n });\n }\n}\n\nfunction imageToBase64(image: Buffer<ArrayBufferLike>) {\n return image.toString('base64');\n}\n\n/**\n * \u4E32\u884C\u56FE\u7247\u751F\u6210\u961F\u5217\uFF1A\u540C\u4E00\u65F6\u523B\u53EA\u5141\u8BB8\u4E00\u5F20\u56FE\u7247\u751F\u6210\u4EFB\u52A1\u6267\u884C\uFF0C\n * \u4E0A\u4E00\u5F20\u751F\u6210\u5B8C\u6BD5\uFF08\u6210\u529F\u6216\u5931\u8D25\uFF09\u540E\u518D\u5F00\u59CB\u4E0B\u4E00\u5F20\u3002\n */\nlet imageQueueTail: Promise<void> = Promise.resolve();\nlet imageQueueSize = 0;\n\nfunction enqueueImageTask<T>(task: () => Promise<T>): Promise<T> {\n const result = imageQueueTail.then(task);\n // \u65E0\u8BBA\u4EFB\u52A1\u6210\u529F\u8FD8\u662F\u5931\u8D25\uFF0C\u961F\u5217\u90FD\u5FC5\u987B\u7EE7\u7EED\u6D41\u8F6C\n imageQueueTail = result.then(\n () => undefined,\n () => undefined\n );\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// \u53EF\u590D\u7528 Chrome \u5B9E\u4F8B\u7BA1\u7406\n// ---------------------------------------------------------------------------\n\n/** \u7A7A\u95F2\u591A\u5C11\u6BEB\u79D2\u540E\u5173\u95ED Chrome \u5B9E\u4F8B\uFF08\u9ED8\u8BA4 5 \u5206\u949F\uFF0C\u53EF\u7528\u73AF\u5883\u53D8\u91CF\u8986\u76D6\u4EE5\u4FBF\u6D4B\u8BD5\uFF09 */\nconst BROWSER_IDLE_TIMEOUT_MS = Number(process.env.GUGLE_IMAGE_IDLE_TIMEOUT_MS) || 5 * 60 * 1000;\n\nlet sharedBrowser: Browser | undefined = undefined;\nlet browserIdleTimer: NodeJS.Timeout | undefined = undefined;\n/** \u8BB0\u5F55\u4E0A\u4E00\u6B21\u5B8C\u6210\u751F\u6210\u7684\u65F6\u95F4\uFF0C\u7528\u4E8E\u7A7A\u95F2\u5224\u5B9A */\nlet lastBrowserUseTime = 0;\n\nfunction browserLaunchArgs(): string[] {\n return [\n '--no-sandbox',\n '--disable-setuid-sandbox',\n '--disable-dev-shm-usage',\n '--disable-gpu',\n '--disable-software-rasterizer',\n '--disable-extensions'\n ];\n}\n\n/**\n * \u60F0\u6027\u542F\u52A8\uFF08\u6216\u590D\u7528\uFF09\u5171\u4EAB\u7684 Chrome \u5B9E\u4F8B\u3002\n * \u82E5\u5B9E\u4F8B\u5DF2\u5B58\u5728\u5219\u76F4\u63A5\u590D\u7528\uFF0C\u5E76\u91CD\u7F6E\u7A7A\u95F2\u8BA1\u65F6\u5668\u3002\n */\nasync function getSharedBrowser(): Promise<Browser> {\n if (sharedBrowser && sharedBrowser.isConnected()) {\n clearTimeout(browserIdleTimer);\n return sharedBrowser;\n }\n sharedBrowser = await puppeteer.launch({\n executablePath: botConfig.chromePath,\n headless: true,\n defaultViewport: { width: 820, height: 1 },\n args: browserLaunchArgs()\n });\n clearTimeout(browserIdleTimer);\n lastBrowserUseTime = Date.now();\n return sharedBrowser;\n}\n\n/** \u5173\u95ED\u5171\u4EAB\u7684 Chrome \u5B9E\u4F8B\uFF08\u5E42\u7B49\uFF09 */\nfunction closeSharedBrowser(): void {\n clearTimeout(browserIdleTimer);\n browserIdleTimer = undefined;\n const browser = sharedBrowser;\n sharedBrowser = undefined;\n if (browser && browser.isConnected()) {\n browser.close().catch(() => undefined);\n }\n}\n\n/**\n * \u7A7A\u95F2\u56DE\u6536\u8C03\u5EA6\uFF1A\u5F53\u6CA1\u6709\u65B0\u7684\u751F\u6210\u4EFB\u52A1\u65F6\uFF0C\u7B49\u5F85 BROWSER_IDLE_TIMEOUT_MS\n * \u540E\u81EA\u52A8\u5173\u95ED Chrome \u5B9E\u4F8B\uFF0C\u907F\u514D\u957F\u671F\u5360\u7528\u5185\u5B58\u3002\n */\nfunction scheduleBrowserIdleClose(): void {\n clearTimeout(browserIdleTimer);\n lastBrowserUseTime = Date.now();\n browserIdleTimer = setTimeout(() => {\n // \u82E5\u8FD9\u6BB5\u65F6\u95F4\u5185\u53C8\u6709\u65B0\u7684\u4EFB\u52A1\u5728\u8DD1\uFF0C\u5C31\u4E0D\u5173\u95ED\uFF08\u4EFB\u52A1\u7ED3\u675F\u540E\u4F1A\u91CD\u65B0\u8C03\u5EA6\uFF09\n if (imageQueueSize > 0 || Date.now() - lastBrowserUseTime < BROWSER_IDLE_TIMEOUT_MS) {\n scheduleBrowserIdleClose();\n return;\n }\n closeSharedBrowser();\n }, BROWSER_IDLE_TIMEOUT_MS);\n // \u7A7A\u95F2\u5B9A\u65F6\u5668\u4E0D\u5E94\u963B\u6B62\u8FDB\u7A0B\u9000\u51FA\uFF08\u5982\u6D4B\u8BD5/\u65E0\u4EFB\u52A1\u65F6\uFF09\n browserIdleTimer.unref?.();\n}\n\n// \u8FDB\u7A0B\u9000\u51FA\u65F6\u4F18\u96C5\u5173\u95ED\u5171\u4EAB Chrome \u5B9E\u4F8B\uFF0C\u907F\u514D\u6B8B\u7559\u5B50\u8FDB\u7A0B\u3002\n// SIGINT/SIGTERM \u4E0B\u7B49\u5F85\u5173\u95ED\u5B8C\u6210\u540E\u518D\u9000\u51FA\uFF1Bexit \u4E8B\u4EF6\u91CC\u505A\u540C\u6B65\u515C\u5E95\u3002\nprocess.once('SIGINT', () => {\n closeSharedBrowser();\n process.exit(0);\n});\nprocess.once('SIGTERM', () => {\n closeSharedBrowser();\n process.exit(0);\n});\nprocess.once('exit', () => {\n // exit \u4E8B\u4EF6\u4E2D\u65E0\u6CD5 await\uFF0C\u4EC5\u515C\u5E95\uFF08close \u7684 promise \u7531\u4E8B\u4EF6\u5FAA\u73AF\u7EE7\u7EED\u5B8C\u6210\uFF09\n const browser = sharedBrowser;\n sharedBrowser = undefined;\n if (browser && browser.isConnected()) {\n browser.close().catch(() => undefined);\n }\n});\n\n/**\n * \u7B49\u5F85\u9875\u9762\u4E2D\u6240\u6709 <img> \u5143\u7D20\u52A0\u8F7D\u5B8C\u6210\u3002\n * \u8F6E\u8BE2\u68C0\u67E5\uFF1A\u6240\u6709\u56FE\u7247 complete \u4E14 naturalWidth > 0\uFF08\u52A0\u8F7D\u6210\u529F\uFF09\uFF1B\n * \u6CA1\u6709\u56FE\u7247\u65F6\u7ACB\u5373\u8FD4\u56DE\u3002\u6700\u591A\u7B49\u5F85 timeoutMs\uFF0C\u8D85\u65F6\u4ECD\u7EE7\u7EED\uFF08\u4E0D\u963B\u585E\u622A\u56FE\uFF09\u3002\n */\nasync function waitForImagesLoaded(page: Page, timeoutMs: number = 30000): Promise<void> {\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n const pending = await page.evaluate(() => {\n const images = Array.from(document.querySelectorAll('img'));\n if (images.length === 0) return 0;\n return images.filter(img => !img.complete || img.naturalWidth === 0).length;\n });\n if (pending === 0) return;\n await new Promise(r => setTimeout(r, 200));\n }\n}\n\n/**\n * \u7528\u5171\u4EAB Chrome \u5B9E\u4F8B\u6E32\u67D3 HTML \u5E76\u622A\u56FE\u3002\n * \u82E5\u63D0\u4F9B templateFile\uFF0C\u5219\u7528 file:// \u52A0\u8F7D\u8BE5\u6587\u4EF6\uFF08\u4EE5\u6A21\u677F\u6240\u5728\u76EE\u5F55\u4E3A\u76F8\u5BF9\u8DEF\u5F84\u6839\uFF0C\n * \u76F8\u5BF9\u8DEF\u5F84\u7684 CSS/\u8D44\u6E90\u4F1A\u6309\u6B64\u89E3\u6790\uFF09\uFF0C\u5E76\u5728\u9875\u9762\u5185\u66FF\u6362 {{xxx}} \u5360\u4F4D\u7B26\uFF1B\n * \u5426\u5219\u9000\u56DE setContent\uFF08\u76F8\u5BF9\u8DEF\u5F84\u65E0\u6CD5\u89E3\u6790\uFF0C\u9700\u5185\u8054\u6837\u5F0F\uFF09\u3002\n */\nasync function renderWithSharedBrowser(\n html: string,\n width: number,\n templateFile?: string\n): Promise<Buffer> {\n const browser = await getSharedBrowser();\n const page = await browser.newPage();\n try {\n await page.setViewport({ width, height: 1 });\n if (templateFile) {\n const fileUrl = 'file:///' + path.resolve(templateFile).replace(/\\\\/g, '/');\n await page.goto(fileUrl, { waitUntil: 'networkidle0', timeout: 60000 });\n // \u5728\u9875\u9762\u5185\u66FF\u6362\u6240\u6709 {{xxx}} \u5360\u4F4D\u7B26\uFF08\u4E0E Template.arg \u7684\u66FF\u6362\u903B\u8F91\u4E00\u81F4\uFF09\n await page.evaluate(processedHtml => {\n document.documentElement.innerHTML = processedHtml;\n // \u79FB\u9664\u9057\u7559\u7684 {{style sheet}} \u5360\u4F4D\u7B26\n document.querySelectorAll('*').forEach(el => {\n if (el.children.length === 0) {\n el.textContent = el.textContent.replace(/\\{\\{style sheet\\}\\}/g, '');\n }\n });\n }, html);\n // \u7B49\u5F85\u66FF\u6362\u540E\u63D2\u5165\u7684\u56FE\u7247\u52A0\u8F7D\u5B8C\u6210\uFF08data URL / \u76F8\u5BF9\u8DEF\u5F84\uFF09\uFF0C\u907F\u514D\u622A\u56FE\u65F6\u56FE\u7247\u7F3A\u5931\n await waitForImagesLoaded(page);\n } else {\n await page.setContent(html, { waitUntil: 'networkidle0', timeout: 60000 });\n // setContent \u8DEF\u5F84\u540C\u6837\u7B49\u5F85\u56FE\u7247\u52A0\u8F7D\u5B8C\u6210\n await waitForImagesLoaded(page);\n }\n const element = await page.$('body');\n if (!element) {\n throw new Error('No element matches selector: body');\n }\n // \u9ED8\u8BA4 encoding \u4E3A binary\uFF0C\u8FD4\u56DE Uint8Array\uFF1B\u8F6C\u6210 Buffer \u540E\u518D base64\n const image = (await element.screenshot({ type: 'png' })) as Uint8Array;\n return Buffer.from(image);\n } finally {\n await page.close().catch(() => undefined);\n }\n}\n\nfunction generateImage(\n resolve: (value: string | PromiseLike<string>) => void,\n reject: (reason?: any) => void,\n html: string,\n width: number,\n templateFile?: string\n): Promise<void> {\n return new Promise<void>((taskResolve, taskReject) => {\n renderWithSharedBrowser(html, width, templateFile)\n .then(image => {\n resolve(imageToBase64(image));\n taskResolve();\n })\n .catch(e => {\n reject(e);\n taskReject(e);\n })\n .finally(() => {\n scheduleBrowserIdleClose();\n });\n });\n}\n\nexport function tryGenerateImage(\n resolve: (value: string | PromiseLike<string>) => void,\n reject: (reason?: any) => void,\n html: string,\n width: number = 820,\n templateFile?: string\n) {\n imageQueueSize++;\n enqueueImageTask(() => generateImage(resolve, reject, html, width, templateFile))\n .catch(() => undefined)\n .finally(() => {\n imageQueueSize--;\n });\n}\n", "import { Issue, IssueEvent, OpenedIssueEvent, ReopenedIssueEvent, User } from '@/type/github';\nimport { Logger } from 'winston';\nimport { Template, tryGenerateImage } from '@/image';\nimport dayjs from 'dayjs';\nimport { renderIssueBody } from '@/features/github/image';\n\nfunction getRepositoryParts(repositoryUrl?: string, htmlUrl?: string): { owner: string; name: string } {\n const url = repositoryUrl || htmlUrl;\n if (!url) return { owner: 'GitHub', name: '' };\n const parts = url.split('/').filter(Boolean);\n const githubIndex = parts.indexOf('github.com');\n const repositoryParts = githubIndex >= 0 ? parts.slice(githubIndex + 1, githubIndex + 3) : parts.slice(-2);\n return repositoryParts.length === 2\n ? { owner: repositoryParts[0]!, name: repositoryParts[1]! }\n : { owner: 'GitHub', name: '' };\n}\n\nfunction getIssueState(issue: Issue): string {\n if (issue.state == 'open') {\n return `\n <div class=\"state_label\" style=\"color:white; background-color: #347d39; box-shadow-color: #347d39;\">\n <div style=\"display: flex; align-items: center;\">\n <svg focusable=\"false\" aria-label=\"Issue\" \n class=\"octicon octicon-issue-opened prc-StateLabel-Icon-YICrR\" role=\"img\" viewBox=\"0 0 16 16\" \n width=\"16\" height=\"16\" fill=\"currentColor\" display=\"inline-block\" \n overflow=\"visible\" style=\"margin-right:4px; vertical-align:text-bottom\">\n <path d=\"M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z\"/>\n <path d=\"M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Z\"/>\n </svg>\n Open\n </div>\n </div>\\n\n `;\n } else {\n if (issue.state_reason == 'completed') {\n return `\n <div class=\"state_label\" style=\"color:white; background-color: #8256d0; box-shadow-color: #8256d0;\">\n <div style=\"display: flex; align-items: center;\">\n <svg focusable=\"false\" aria-label=\"Issue\" class=\"octicon octicon-issue-closed prc-StateLabel-Icon-YICrR\"\n role=\"img\" viewBox=\"0 0 16 16\" width=\"16\" height=\"16\" fill=\"currentColor\" display=\"inline-block\" \n overflow=\"visible\" style=\"margin-right:4px; vertical-align:text-bottom\">\n <path d=\"M11.28 6.78a.75.75 0 0 0-1.06-1.06L7.25 8.69 5.78 7.22a.75.75 0 0 0-1.06 1.06l2 2a.75.75 0 0 0 1.06 0l3.5-3.5Z\"/>\n <path d=\"M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0Zm-1.5 0a6.5 6.5 0 1 0-13 0 6.5 6.5 0 0 0 13 0Z\"></path>\n </svg>\n Closed\n </div>\n </div>\\n\n `;\n } else {\n return `\n <div class=\"state_label\" style=\"color:white; background-color: #656c76; box-shadow-color: #656c76;\">\n <div style=\"display: flex; align-items: center;\">\n <svg focusable=\"false\" aria-label=\"Issue, not planned\" class=\"octicon octicon-skip prc-StateLabel-Icon-YICrR\"\n role=\"img\" viewBox=\"0 0 16 16\" width=\"16\" height=\"16\" fill=\"currentColor\" display=\"inline-block\" \n overflow=\"visible\" style=\"margin-right:4px; vertical-align: text-bottom;\">\n <path d=\"M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 \n 0Zm9.78-2.22-5.5 5.5a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734l5.5-5.5a.751.751 0 0 1 \n 1.042.018.751.751 0 0 1 .018 1.042Z\"/>\n </svg>\n Closed\n </div>\n </div>\\n\n `;\n }\n }\n}\n\nexport function issuesHandler(issue: Issue, logger?: Logger, operation?: string, sender?: User, extra?: string) {\n let labelsHtml = '';\n for (let label of issue.labels) {\n labelsHtml += `<div class=\"sidebar-label\" style=\"color:#${label.color}ff; background-color: #${label.color}55; border:2px solid #${label.color}99\">${label.name}</div>\\n`;\n }\n let headerExtra: string | undefined = undefined;\n if (sender) {\n headerExtra = `<div class=\"message\">\u7528\u6237<div class=\"user\">${sender.login}</div>${operation}\u4E86 </div>`;\n }\n return new Promise<string>((resolve, reject) => {\n const repository = getRepositoryParts(issue.repository_url, issue.html_url);\n const repoFullName = `${repository.owner}/${repository.name}`;\n renderIssueBody(issue.body, repoFullName, logger)\n .then(bodyHtml => {\n const template = Template.load('issue', 'src/features/github/template')\n .arg('header extra', headerExtra || '')\n .arg('repository owner', repository.owner)\n .arg('repository name', repository.name)\n .arg('issue number', issue.number)\n .arg('state label', getIssueState(issue))\n .arg('issue title', issue.title)\n .arg('issue author', issue.user.login)\n .arg('issue body', bodyHtml)\n .arg('issue created at', dayjs(issue.created_at).format('YYYY-MM-DD HH:mm:ss'))\n .arg('issue labels', labelsHtml || 'No labels')\n .arg('extra', extra || '');\n const templateFile = template.file();\n template\n .handler()\n .then(issue => {\n logger?.debug(`Start process issue message...`);\n tryGenerateImage(resolve, reject, issue, 820, templateFile);\n })\n .catch(reject);\n })\n .catch(reject);\n });\n}\n\nexport function issuesClosed(issue: IssueEvent, logger?: Logger): Promise<string> {\n let extra: string | undefined;\n if (issue.issue.state_reason) {\n extra = \"<div class='h1'>\u5173\u95ED\u539F\u56E0\uFF1A</div>\\n\";\n }\n if (issue.issue.state_reason == 'completed') {\n extra += \"<div class='body'>\u5DF2\u5B8C\u6210</div>\\n\";\n } else if (issue.issue.state_reason == 'not_planned') {\n extra += \"<div class='body'>\u672A\u8BA1\u5212</div>\\n\";\n } else if (issue.issue.state_reason == 'duplicate') {\n extra += \"<div class='body'>\u91CD\u590D</div>\\n\";\n } else {\n extra += `<div class='body'>${issue.issue.state_reason}</div>\\n`;\n }\n return issuesHandler(issue.issue, logger, '\u5173\u95ED', issue.sender, extra);\n}\n\nexport function issuesOpened(issue: OpenedIssueEvent | ReopenedIssueEvent, logger?: Logger): Promise<string> {\n let operation: string;\n if (issue.action == 'reopened') {\n operation = '\u91CD\u65B0\u6253\u5F00';\n } else {\n operation = '\u63D0\u4EA4';\n }\n return issuesHandler(issue.issue, logger, operation, issue.sender);\n}\n", "import { OpenedPullRequestEvent, PullRequest, PullRequestEvent, ReopenedPullRequestEvent, User } from '@/type/github';\nimport { Logger } from 'winston';\nimport { Template, tryGenerateImage } from '@/image';\nimport { renderIssueBody } from '@/features/github/image';\nimport dayjs from 'dayjs';\n\nfunction getRepositoryParts(repositoryUrl?: string, htmlUrl?: string): { owner: string; name: string } {\n const url = repositoryUrl || htmlUrl;\n if (!url) return { owner: 'GitHub', name: '' };\n const parts = url.split('/').filter(Boolean);\n const githubIndex = parts.indexOf('github.com');\n const repositoryParts = githubIndex >= 0 ? parts.slice(githubIndex + 1, githubIndex + 3) : parts.slice(-2);\n return repositoryParts.length === 2\n ? { owner: repositoryParts[0]!, name: repositoryParts[1]! }\n : { owner: 'GitHub', name: '' };\n}\n\nexport function getPullRequestState(pr: PullRequest): string {\n if (pr.state == 'open') {\n if (pr.draft) {\n return `\n <div class=\"state_label\" style=\"color:white; background-color: #656c76; box-shadow-color: #656c76;\">\n <div style=\"display: flex; align-items: center;\">\n <svg class=\"octicon\" focusable=\"false\" height=\"16\" fill=\"currentColor\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" aria-hidden=\"true\">\n <path d=\"M3.25 1A2.25 2.25 0 0 1 4 5.372v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.251 2.251 \n 0 0 1 3.25 1Zm9.5 14a2.25 2.25 0 1 1 0-4.5 2.25 2.25 0 0 1 0 4.5ZM2.5 3.25a.75.75 0 1 0 1.5 0 \n .75.75 0 0 0-1.5 0ZM3.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm9.5 0a.75.75 0 1 0 0 1.5.75.75 \n 0 0 0 0-1.5ZM14 7.5a1.25 1.25 0 1 1-2.5 0 1.25 1.25 0 0 1 2.5 0Zm0-4.25a1.25 1.25 0 1 1-2.5 0 \n 1.25 1.25 0 0 1 2.5 0Z\"/>\n </svg>\n Draft \n </div>\n </div>\\n\n `;\n } else {\n return `\n <div class=\"state_label\" style=\"color:white; background-color: #347d39; box-shadow-color: #347d39;\">\n <div style=\"display: flex; align-items: center;\">\n <svg class=\"octicon\" focusable=\"false\" height=\"16\" fill=\"currentColor\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" aria-hidden=\"true\">\n <path d=\"M1.5 3.25a2.25 2.25 0 1 1 3 2.122v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.25 2.25 \n 0 0 1 1.5 3.25Zm5.677-.177L9.573.677A.25.25 0 0 1 10 .854V2.5h1A2.5 2.5 0 0 1 13.5 5v5.628a2.251 \n 2.251 0 1 1-1.5 0V5a1 1 0 0 0-1-1h-1v1.646a.25.25 0 0 1-.427.177L7.177 3.427a.25.25 0 0 1 \n 0-.354ZM3.75 2.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm0 9.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 \n 0-1.5Zm8.25.75a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Z\"/>\n </svg>\n Open \n </div>\n </div>\\n\n `;\n }\n } else {\n if (pr.merged) {\n return `\n <div class=\"state_label\" style=\"color:white; background-color: #8256d0; box-shadow-color: #8256d0;\">\n <div style=\"display: flex; align-items: center;\">\n <svg class=\"octicon\" focusable=\"false\" height=\"16\" fill=\"currentColor\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" aria-hidden=\"true\">\n <path d=\"M5.45 5.154A4.25 4.25 0 0 0 9.25 7.5h1.378a2.251 2.251 0 1 1 0 1.5H9.25A5.734 5.734\n 0 0 1 5 7.123v3.505a2.25 2.25 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.95-.218ZM4.25 13.5a.75.75 \n 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm8.5-4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5ZM5 3.25a.75.75 \n 0 1 0 0 .005V3.25Z\"/>\n </svg>\n Merged \n </div>\n </div>\\n\n `;\n } else {\n return `\n <div class=\"state_label\" style=\"color:white; background-color: #c93c37; box-shadow-color: #c93c37;\">\n <div style=\"display: flex; align-items: center;\">\n <svg class=\"octicon\" focusable=\"false\" height=\"16\" fill=\"currentColor\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" aria-hidden=\"true\">\n <path d=\"M3.25 1A2.25 2.25 0 0 1 4 5.372v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.251 2.251\n 0 0 1 3.25 1Zm9.5 5.5a.75.75 0 0 1 .75.75v3.378a2.251 2.251 0 1 1-1.5 0V7.25a.75.75\n 0 0 1 .75-.75Zm-2.03-5.273a.75.75 0 0 1 1.06 0l.97.97.97-.97a.748.748 0 0 1 1.265.332.75.75\n 0 0 1-.205.729l-.97.97.97.97a.751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018l-.97-.97-.97.97a.749.749\n 0 0 1-1.275-.326.749.749 0 0 1 .215-.734l.97-.97-.97-.97a.75.75 0 0 1 0-1.06ZM2.5 3.25a.75.75\n 0 1 0 1.5 0 .75.75 0 0 0-1.5 0ZM3.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm9.5 0a.75.75 0 1\n 0 0 1.5.75.75 0 0 0 0-1.5Z\"/>\n </svg>\n Closed\n </div>\n </div>\\n\n `;\n }\n }\n}\n\nexport function prHandler(pr: PullRequest, logger?: Logger, operation?: string, sender?: User, extra?: string) {\n let labelsHtml = '';\n for (let label of pr.labels) {\n labelsHtml += `<div class=\"sidebar-label\" style=\"color:#${label.color}ff; background-color: #${label.color}55; border:2px solid #${label.color}99\">${label.name}</div>\\n`;\n }\n let headerExtra: string | undefined = undefined;\n if (sender) {\n headerExtra = `<div class=\"message\">\u7528\u6237<div class=\"user\">${sender.login}</div>${operation}\u4E86 </div>`;\n }\n return new Promise<string>((resolve, reject) => {\n const repository = getRepositoryParts(pr.repository_url, pr.html_url);\n const repoFullName = `${repository.owner}/${repository.name}`;\n renderIssueBody(pr.body, repoFullName, logger)\n .then(bodyHtml => {\n const template = Template.load('pull_request', 'src/features/github/template')\n .arg('header extra', headerExtra || '')\n .arg('repository owner', repository.owner)\n .arg('repository name', repository.name)\n .arg('pr number', pr.number)\n .arg('pr title', pr.title)\n .arg('pr body', bodyHtml)\n .arg('pr labels', labelsHtml || 'No labels')\n .arg('pr author', pr.user.login)\n .arg('pr created at', dayjs(pr.created_at).format('YYYY-MM-DD HH:mm:ss'))\n .arg('state label', getPullRequestState(pr))\n .arg('extra', extra || '');\n const templateFile = template.file();\n template\n .handler()\n .then(pr => {\n logger?.debug(`Start process pull request message...`);\n tryGenerateImage(resolve, reject, pr, 820, templateFile);\n })\n .catch(reject);\n })\n .catch(reject);\n });\n}\n\nexport function prClosed(pr: PullRequestEvent, logger?: Logger): Promise<string> {\n let operation: string;\n if (pr.pull_request.merged) {\n operation = '\u5408\u5E76';\n } else {\n operation = '\u5173\u95ED';\n }\n return prHandler(pr.pull_request, logger, operation, pr.sender);\n}\n\nexport function prOpened(pr: OpenedPullRequestEvent | ReopenedPullRequestEvent, logger?: Logger): Promise<string> {\n let operation: string;\n if (pr.action == 'reopened') {\n operation = '\u91CD\u65B0\u6253\u5F00';\n } else {\n operation = '\u63D0\u4EA4';\n }\n return prHandler(pr.pull_request, logger, operation, pr.sender);\n}\n", "import { Release, ReleaseAsset, ReleaseEvent, User } from '@/type/github';\nimport { Logger } from 'winston';\nimport { Template, tryGenerateImage } from '@/image';\nimport dayjs from 'dayjs';\nimport { renderIssueBody } from '@/features/github/image';\n\n/**\n * \u89E3\u6790\u4ED3\u5E93 owner/name\u3002\n * \u4F18\u5148\u4F7F\u7528 webhook \u8F7D\u8377\u4E2D\u7684 repository.full_name\uFF08\u6700\u53EF\u9760\uFF09\uFF0C\n * \u5176\u6B21\u4ECE html_url\uFF08https://github.com/owner/repo/releases/tag/x\uFF09\u89E3\u6790\u3002\n * \u6CE8\u610F\uFF1A\u4E0D\u80FD\u7528 release.url\u2014\u2014\u5B83\u662F API \u5730\u5740\n * (https://api.github.com/repos/owner/repo/releases/123)\uFF0C\u672B\u4E24\u6BB5\u662F releases/123\u3002\n */\nfunction getRepositoryParts(repositoryFullName?: string, htmlUrl?: string): { owner: string; name: string } {\n if (repositoryFullName) {\n const parts = repositoryFullName.split('/').filter(Boolean);\n if (parts.length === 2) return { owner: parts[0]!, name: parts[1]! };\n }\n if (htmlUrl) {\n const parts = htmlUrl.split('/').filter(Boolean);\n const githubIndex = parts.indexOf('github.com');\n const repositoryParts = githubIndex >= 0 ? parts.slice(githubIndex + 1, githubIndex + 3) : [];\n if (repositoryParts.length === 2) return { owner: repositoryParts[0]!, name: repositoryParts[1]! };\n }\n return { owner: 'GitHub', name: '' };\n}\n\n/** \u683C\u5F0F\u5316\u4E3A GitHub \u98CE\u683C\u7684\u4F53\u79EF\u6587\u672C\uFF08\u5982 23.8 MB\uFF09 */\nexport function formatBytes(bytes: number): string {\n if (!Number.isFinite(bytes) || bytes < 0) return '0 Bytes';\n const units = ['Bytes', 'KB', 'MB', 'GB', 'TB'];\n if (bytes === 0) return '0 Bytes';\n const exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);\n const value = bytes / Math.pow(1024, exponent);\n // GitHub \u5C55\u793A\u4FDD\u7559 1 \u4F4D\u5C0F\u6570\uFF08\u6574\u6570\u4F53\u79EF\u4E0D\u663E\u793A\u5C0F\u6570\uFF09\n const text = exponent === 0 || Number.isInteger(value) ? String(value) : value.toFixed(1);\n return `${text} ${units[exponent]}`;\n}\n\n/** \u8F6C\u4E49 HTML \u7279\u6B8A\u5B57\u7B26\uFF0C\u907F\u514D\u6807\u9898/\u6587\u4EF6\u540D\u7834\u574F\u6A21\u677F */\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n\n/** \u300CLatest\u300D/\u300CPre-release\u300D\u5FBD\u7AE0 */\nfunction getReleaseBadge(release: Release): string {\n if (release.prerelease) {\n return `<span class=\"release_badge release_badge_prerelease\"><svg viewBox=\"0 0 16 16\" width=\"12\" height=\"12\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm4.25-3.25v6.5a.75.75 0 0 1-1.5 0v-6.5a.75.75 0 0 1 1.5 0Zm3.5 0v6.5a.75.75 0 0 1-1.5 0v-6.5a.75.75 0 0 1 1.5 0Z\"/></svg>Pre-release</span>`;\n }\n if (release.draft) {\n return `<span class=\"release_badge release_badge_prerelease\">Draft</span>`;\n }\n return `<span class=\"release_badge\"><svg viewBox=\"0 0 16 16\" width=\"12\" height=\"12\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16Zm3.78-9.72a.751.751 0 0 0-.018-1.042.751.751 0 0 0-1.042-.018L6.75 9.19 5.28 7.72a.751.751 0 0 0-1.042.018.751.751 0 0 0-.018 1.042l2 2a.75.75 0 0 0 1.06 0Z\"/></svg>Latest</span>`;\n}\n\n/** \u6E32\u67D3 Assets \u5217\u8868\uFF08GitHub release \u9875\u9762\u7684\u9644\u4EF6\u884C\uFF09 */\nfunction renderAssets(assets: ReleaseAsset[]): string {\n if (!assets || assets.length === 0) {\n return '<div class=\"empty_assets\">No assets uploaded.</div>';\n }\n return assets\n .map(asset => {\n const downloadCount = asset.download_count > 0 ? `${asset.download_count} downloads` : '0 downloads';\n return (\n `<div class=\"asset_item\">` +\n `<svg class=\"asset_icon\" viewBox=\"0 0 16 16\" aria-hidden=\"true\"><path d=\"M2.75 1A1.75 1.75 0 0 0 1 2.75v10.5c0 .966.784 1.75 1.75 1.75h10.5c.966 0 1.75-.784 1.75-1.75V2.75A1.75 1.75 0 0 0 13.25 1Zm-.25 1.75a.25.25 0 0 1 .25-.25h10.5a.25.25 0 0 1 .25.25v10.5a.25.25 0 0 1-.25.25H2.75a.25.25 0 0 1-.25-.25Z\"/><path d=\"M9.5 4.5v3.293l1.146-1.147a.75.75 0 1 1 1.061 1.061l-2.5 2.5a.75.75 0 0 1-1.06 0l-2.5-2.5a.75.75 0 0 1 1.06-1.06L8 7.792V4.5a.75.75 0 0 1 1.5 0Z\"/></svg>` +\n `<a class=\"asset_name\" href=\"${asset.browser_download_url}\">${escapeHtml(asset.name)}</a>` +\n `<span class=\"asset_spacer\"></span>` +\n `<span class=\"asset_size\">${formatBytes(asset.size)}</span>` +\n `<span class=\"asset_downloads\">${downloadCount}</span>` +\n `</div>`\n );\n })\n .join('\\n');\n}\n\nexport function releaseHandler(release: Release, repositoryFullName?: string, logger?: Logger, sender?: User) {\n let headerExtra: string | undefined = undefined;\n if (sender) {\n headerExtra = `<div class=\"message\">\u7528\u6237<div class=\"user\">${sender.login}</div>\u53D1\u5E03\u4E86 </div>`;\n }\n const repository = getRepositoryParts(repositoryFullName, release.html_url);\n const repoFullName = `${repository.owner}/${repository.name}`;\n // release \u6807\u9898\u4E3A\u7A7A\u65F6\u56DE\u9000\u4E3A tag \u540D\n const title = release.name || release.tag_name;\n return new Promise<string>((resolve, reject) => {\n renderIssueBody(release.body || '', repoFullName, logger)\n .then(bodyHtml => {\n const publishedAt = release.published_at || release.created_at;\n const template = Template.load('release', 'src/features/github/template')\n .arg('header extra', headerExtra || '')\n .arg('repository owner', repository.owner)\n .arg('repository name', repository.name)\n .arg('release badge', getReleaseBadge(release))\n .arg('release title', title)\n .arg('release tag', release.tag_name)\n .arg('release commitish', release.target_commitish)\n .arg('release author', release.author?.login || 'unknown')\n .arg('release published at', dayjs(publishedAt).format('YYYY-MM-DD HH:mm:ss'))\n .arg('release body', bodyHtml || '<p><em>No description provided.</em></p>')\n .arg('release asset count', release.assets?.length || 0)\n .arg('release assets', renderAssets(release.assets || []));\n const templateFile = template.file();\n template\n .handler()\n .then(html => {\n logger?.debug(`Start process release message...`);\n tryGenerateImage(resolve, reject, html, 820, templateFile);\n })\n .catch(reject);\n })\n .catch(reject);\n });\n}\n\nexport function releasePublished(event: ReleaseEvent, logger?: Logger): Promise<string> {\n return releaseHandler(event.release, event.repository?.full_name, logger, event.sender);\n}\n\n/** \u89C6\u4E3A\u300C\u53D1\u5E03\u300D\u7684 release action\uFF08\u5176\u4F59\u5982 edited / deleted / unpublished \u4E0D\u63A8\u9001\uFF09 */\nconst RELEASE_PUBLISH_ACTIONS = new Set(['created', 'published', 'released', 'prereleased']);\n/** \u540C\u4E00\u6B21\u53D1\u5E03\u7684\u591A\u6B21\u6295\u9012\u4F1A\u96C6\u4E2D\u5728\u6570\u79D2\u5185\u5230\u8FBE\uFF0C\u7528\u8BE5\u7A97\u53E3\u6298\u53E0 */\nexport const RELEASE_DEDUPE_WINDOW_MS = 60 * 1000;\n/** release id -> \u6700\u8FD1\u4E00\u6B21\u63A8\u9001\u65F6\u95F4 */\nconst handledReleases = new Map<number, number>();\n\n/**\n * \u5224\u65AD\u4E00\u4E2A release webhook \u4E8B\u4EF6\u662F\u5426\u5E94\u5F53\u63A8\u9001\u3002\n *\n * GitHub \u5BF9\u540C\u4E00\u6B21\u53D1\u5E03\u4F1A\u6295\u9012\u591A\u4E2A action\uFF08\u5B9E\u6D4B\u540C\u4E00 release id \u4F1A\u5728\u6570\u79D2\u5185\u5148\u540E\u6536\u5230\n * created + released\u3001published + created + prereleased \u7B49\u7EC4\u5408\uFF0C\u4E14\u7EC4\u5408\u4E0D\u56FA\u5B9A\uFF09\uFF0C\n * \u56E0\u6B64\u4E0D\u80FD\u53EA\u6309 action \u8FC7\u6EE4\uFF1A\u8FD9\u91CC\u4EE5 release id \u5728\u65F6\u95F4\u7A97\u53E3\u5185\u53BB\u91CD\uFF0C\u4FDD\u8BC1\u4E00\u6B21\u53D1\u5E03\u53EA\u63A8\u4E00\u5F20\u3002\n * \u540C\u65F6\u8DF3\u8FC7\u4ECD\u662F\u8349\u7A3F\u7684 release\uFF08\u4FDD\u5B58\u8349\u7A3F\u540C\u6837\u4F1A\u89E6\u53D1 created\uFF09\u3002\n *\n * @param event release webhook \u4E8B\u4EF6\n * @param now \u5F53\u524D\u65F6\u95F4\u6233\uFF08\u4FBF\u4E8E\u9A8C\u8BC1\uFF09\n * @returns \u662F\u5426\u63A8\u9001\uFF0C\u4EE5\u53CA\u8DF3\u8FC7\u539F\u56E0\n */\nexport function shouldPushRelease(event: ReleaseEvent, now: number = Date.now()): { push: boolean; reason: string } {\n if (!RELEASE_PUBLISH_ACTIONS.has(event.action)) {\n return { push: false, reason: `action ${event.action} \u4E0D\u5C5E\u4E8E\u53D1\u5E03` };\n }\n if (event.release.draft) {\n return { push: false, reason: 'release \u4ECD\u4E3A\u8349\u7A3F' };\n }\n const last = handledReleases.get(event.release.id);\n if (last !== undefined && now - last < RELEASE_DEDUPE_WINDOW_MS) {\n return { push: false, reason: `\u540C\u4E00 release \u5DF2\u4E8E ${now - last}ms \u524D\u63A8\u9001` };\n }\n // \u6E05\u7406\u7A97\u53E3\u5916\u7684\u8BB0\u5F55\uFF0C\u907F\u514D\u957F\u671F\u8FD0\u884C\u540E Map \u6301\u7EED\u589E\u957F\n for (const [id, timestamp] of handledReleases) {\n if (now - timestamp >= RELEASE_DEDUPE_WINDOW_MS) handledReleases.delete(id);\n }\n handledReleases.set(event.release.id, now);\n return { push: true, reason: '\u9996\u6B21\u6536\u5230\u8BE5 release \u7684\u53D1\u5E03\u4E8B\u4EF6' };\n}\n", "import axios, { AxiosInstance } from 'axios';\nimport { Logger } from 'winston';\nimport { botConfig } from '@/config';\nimport { Issue, IssueStateReason, State } from '@/type/github';\n\n/**\n * GitHub API \u53CD\u4EE3\u5730\u5740\uFF1AURL \u7ED3\u6784\u4E3A ghapi.anvilcraft.dev + apiPath\u3002\n * \u4E0D\u518D\u7ECF\u5176\u4ED6\u7B2C\u4E09\u65B9\u4EE3\u7406\u3002\n */\nconst GITHUB_API_BASE = 'https://ghapi.anvilcraft.dev';\n\nconst axiosInstance: AxiosInstance = axios.create({\n timeout: 10000,\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': botConfig.userAgent\n }\n});\n\n/**\n * \u8BF7\u6C42 GitHub API\uFF08\u7ECF ghapi.anvilcraft.dev \u53CD\u4EE3\uFF09\u3002\n * @param apiPath GitHub API \u8DEF\u5F84\uFF08\u4E0D\u542B\u57DF\u540D\uFF09\uFF0C\u5982 /repos/owner/repo/issues/123\n * @param logger \u53EF\u9009\u65E5\u5FD7\u5668\n */\nexport function fetchGithubApi<T>(apiPath: string, logger?: Logger): Promise<T> {\n const url = `${GITHUB_API_BASE}${apiPath}`;\n logger?.debug(`GitHub API: ${url}`);\n return axiosInstance.get(url).then(response => response.data as T);\n}\n\n/** issue/PR \u72B6\u6001\u7EC6\u5206\uFF08\u7528\u4E8E pill \u5FBD\u7AE0\u6E32\u67D3\uFF09 */\nexport type LinkedIssueStatus =\n | 'open' // issue \u8FDB\u884C\u4E2D\uFF08\u7EFF\u8272\u5706\u70B9\uFF09\n | 'completed' // issue \u5DF2\u5B8C\u6210\u5173\u95ED\uFF08\u7D2B\u8272\u5BF9\u52FE\uFF09\n | 'not_planned' // issue \u672A\u8BA1\u5212\u7B49\u5173\u95ED\uFF08\u7070\u8272\u659C\u6760\uFF09\n | 'pr_open' // PR \u8FDB\u884C\u4E2D\uFF08\u7EFF\u8272 PR \u56FE\u6807\uFF09\n | 'pr_merged' // PR \u5DF2\u5408\u5E76\uFF08\u7D2B\u8272\u5408\u5E76\u56FE\u6807\uFF09\n | 'pr_closed' // PR \u5173\u95ED\u672A\u5408\u5E76\uFF08\u7EA2\u8272 PR \u5173\u95ED\u56FE\u6807\uFF09\n | 'unknown'; // \u72B6\u6001\u672A\u77E5\uFF08\u67E5\u8BE2\u5931\u8D25\u56DE\u9000\uFF0C\u7070\u8272\u95EE\u53F7\uFF09\n\nexport interface LinkedIssueInfo {\n state: State;\n state_reason?: IssueStateReason;\n /** issue / PR \u6807\u9898 */\n title?: string | number;\n /** issue API \u5BF9 PR \u4F1A\u5E26\u6B64\u5B57\u6BB5\uFF1Bmerged_at \u975E\u7A7A\u8868\u793A\u5DF2\u5408\u5E76 */\n pull_request?: { merged_at?: string | null };\n}\n\n/** \u628A API \u8FD4\u56DE\u7684 state/state_reason/pull_request \u5F52\u4E00\u5316\u4E3A pill \u72B6\u6001 */\nfunction toStatus(info: LinkedIssueInfo): LinkedIssueStatus {\n // PR \u4F18\u5148\uFF1Aissue API \u5BF9 PR \u4F1A\u5E26 pull_request \u5B57\u6BB5\uFF0Cmerged_at \u533A\u5206\u5408\u5E76/\u5173\u95ED\u672A\u5408\u5E76\n if (info.pull_request) {\n if (info.state === 'open') return 'pr_open';\n return info.pull_request.merged_at ? 'pr_merged' : 'pr_closed';\n }\n if (info.state === 'open') return 'open';\n if (info.state === 'closed') {\n return info.state_reason === 'completed' ? 'completed' : 'not_planned';\n }\n return 'unknown';\n}\n\n/** \u67E5\u8BE2\u7ED3\u679C\uFF1A\u72B6\u6001 + \u6807\u9898\uFF08\u5931\u8D25\u65F6\u6807\u9898\u7F3A\u5931\uFF0C\u8C03\u7528\u65B9\u56DE\u9000\u4E3A\u7F16\u53F7\u663E\u793A\uFF09 */\nexport interface LinkedIssueDetail {\n status: LinkedIssueStatus;\n title?: string;\n}\n\n/**\n * \u67E5\u8BE2 issue/PR \u72B6\u6001\u4E0E\u6807\u9898\u3002\u5931\u8D25\u56DE\u9000\uFF1A\u4EFB\u4F55\u9519\u8BEF\u90FD\u8FD4\u56DE { status: 'unknown' }\uFF0C\n * \u4E0D\u629B\u5F02\u5E38\u3001\u4E0D\u963B\u585E\u8C03\u7528\u65B9\u3002\n * @param repository owner/repo\n * @param number issue \u7F16\u53F7\n * @param logger \u53EF\u9009\u65E5\u5FD7\u5668\n */\nexport async function fetchIssueDetail(\n repository: string,\n number: number,\n logger?: Logger\n): Promise<LinkedIssueDetail> {\n try {\n const info = await fetchGithubApi<LinkedIssueInfo>(`/repos/${repository}/issues/${number}`, logger);\n const status = toStatus(info);\n if (info.title == null) {\n return { status };\n }\n return { status, title: String(info.title) };\n } catch (e) {\n logger?.warn(`\u67E5\u8BE2 issue \u72B6\u6001\u5931\u8D25 ${repository}#${number}\uFF1A${(e as Error)?.message ?? e}`);\n return { status: 'unknown' };\n }\n}\n", "import {\n AllReleaseEvent,\n ClosedIssueEvent,\n ClosedPullRequestEvent,\n Issue,\n OpenedIssueEvent,\n OpenedPullRequestEvent,\n PullRequest,\n Release,\n ReopenedIssueEvent,\n ReopenedPullRequestEvent,\n User\n} from '@/type/github';\nimport { Logger } from 'winston';\nimport markdownit from 'markdown-it';\nimport hljs from 'highlight.js';\nimport { full as emoji } from 'markdown-it-emoji';\nimport { issuesClosed, issuesHandler, issuesOpened } from '@/features/github/image/impl/issue';\nimport { prClosed, prHandler, prOpened } from '@/features/github/image/impl/pr';\nimport { releaseHandler, releasePublished } from '@/features/github/image/impl/release';\nimport { fetchIssueDetail, LinkedIssueDetail, LinkedIssueStatus } from '@/features/github/api';\n\n/**\n * \u89E3\u6790 PR/Issue \u6B63\u6587\u5217\u8868\u884C\u4E2D\u7684 issue/PR \u5F15\u7528\uFF08\u5982 \"- resolved #4851\"\u3001\n * \"- fixed owner/repo#4858\"\u3001\u81EA\u5B9A\u4E49\u6587\u5B57 \"- \u89E3\u51B3\u4E86 #4851\"\uFF09\uFF0C\n * \u53EA\u66FF\u6362\u5176\u4E2D\u7684 \"#\u7F16\u53F7\" \u90E8\u5206\u4E3A GitHub \u98CE\u683C\u7684\u884C\u5185\u5F15\u7528\uFF1A\n * \u72B6\u6001\u56FE\u6807 + \u6807\u9898 + \u94FE\u63A5\u8272\u7F16\u53F7\uFF1B\u884C\u5185\u5176\u5B83\u6587\u5B57\u539F\u6837\u4FDD\u7559\u3002\n *\n * \u4E0E GitHub \u5B98\u65B9 markdown \u6E32\u67D3\u884C\u4E3A\u4E00\u81F4\uFF08\u5217\u8868\u4FDD\u7559\u3001\u6587\u5B57\u4FDD\u7559\u3001#\u7F16\u53F7\u53D8\u94FE\u63A5\uFF09\uFF0C\n * \u53EA\u662F\u989D\u5916\u628A\u6807\u9898\u4E0E\u72B6\u6001\u56FE\u6807\u4E00\u5E76\u6E32\u67D3\u51FA\u6765\u3002\u975E\u5217\u8868\u884C\u4E2D\u7684 #\u7F16\u53F7 \u4E0D\u5904\u7406\u3002\n */\n/** \u5339\u914D\u5217\u8868\u884C\uFF08\u65E0\u5E8F\u6807\u8BB0 -\u3001*\u3001+ \u6216\u6709\u5E8F\u6807\u8BB0 1. / 1) \uFF09 */\nconst LIST_LINE = /^\\s*(?:[-*+]|\\d+[.)])\\s+.*$/gm;\n/** \u5339\u914D\u884C\u5185\u7684\u5F15\u7528\uFF1A#1234 \u6216 owner/repo#1234\uFF08\u4E0D\u5339\u914D\u5355\u8BCD/\u8DEF\u5F84\u4E2D\u95F4\uFF09 */\nconst REF_IN_LINE = /(?<![\\w/])((?:[\\w.-]+\\/[\\w.-]+)?#\\d+)(?!\\w)/g;\n/** \u5339\u914D GitHub issue/PR \u5B8C\u6574\u5730\u5740\uFF1Ahttps://github.com/owner/repo/(pull|issues)/1234 */\nconst ISSUE_URL = /(?:https?:\\/\\/)?(?:www\\.)?github\\.com\\/([\\w.-]+)\\/([\\w.-]+)\\/(issues|pull)\\/(\\d+)/g;\n/** \u4EE3\u7801\u6BB5\uFF08\u56F4\u680F\u4EE3\u7801\u5757\u6216\u884C\u5185\u4EE3\u7801\uFF09\uFF1A\u7F29\u77ED URL \u65F6\u8DF3\u8FC7\uFF0C\u907F\u514D\u7834\u574F\u4EE3\u7801\u5185\u5BB9 */\nconst CODE_SEGMENT = /(```[\\s\\S]*?```|`[^`\\n]*`)/g;\n\n/** \u5360\u4F4D\u7B26\u524D\u7F00/\u540E\u7F00\uFF1A\u907F\u5F00 markdown \u7279\u6B8A\u5B57\u7B26\uFF0C\u6E32\u67D3\u540E\u518D\u6362\u56DE\u5F15\u7528 HTML */\nconst PLACEHOLDER_PREFIX = 'GHILINKISSUEPILLZ';\nconst PLACEHOLDER_SUFFIX = 'ZLLIPISSUEKNILHG';\n/** \u7EAF\u94FE\u63A5\uFF08URL \u7F29\u77ED\u800C\u6765\uFF09\u7684\u5360\u4F4D\u7B26\uFF0C\u4E0E\u5F15\u7528\u5206\u5F00\u7F16\u53F7\u907F\u514D\u51B2\u7A81 */\nconst LINK_PLACEHOLDER_PREFIX = 'GHILINKSHORTURLZ';\nconst LINK_PLACEHOLDER_SUFFIX = 'ZLRUHTROHSKNILIHG';\n\ninterface LinkedIssueReference {\n /** \u5B8C\u6574\u4ED3\u5E93\uFF08owner/repo\uFF09\uFF0C\u77ED\u5F15\u7528\u7528\u5F53\u524D\u4ED3\u5E93\u5C55\u5F00 */\n repository: string;\n /** issue \u7F16\u53F7 */\n number: number;\n /** \u67E5\u8BE2\u5931\u8D25\u65F6\u56DE\u9000\u663E\u793A\u7684\u6587\u672C\uFF08owner/repo#\u7F16\u53F7\uFF09 */\n label: string;\n /** \u662F\u5426\u8DE8\u4ED3\u5E93\u5F15\u7528\uFF08\u663E\u5F0F\u5199\u4E86 owner/repo#\u7F16\u53F7\uFF09 */\n crossRepo: boolean;\n}\n\n/** \u7531\u5B8C\u6574 GitHub \u5730\u5740\u7F29\u77ED\u800C\u6765\u7684\u5F15\u7528\uFF08\u53EA\u6E32\u67D3\u4E3A\u84DD\u8272 #\u7F16\u53F7\uFF0C\u4E0D\u67E5\u8BE2 API\uFF09 */\ninterface ShortUrl {\n url: string;\n number: number;\n label: string;\n}\n\n/** \u8F6C\u4E49\u6807\u9898\u4E2D\u7684 HTML \u7279\u6B8A\u5B57\u7B26\uFF0C\u907F\u514D\u6CE8\u5165/\u7834\u574F\u6A21\u677F */\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n\n/** \u6309\u72B6\u6001\u8FD4\u56DE\u5BF9\u5E94\u7684 SVG \u56FE\u6807\uFF08GitHub octicon \u539F\u7248\uFF1A\u5F69\u8272\u5706\u73AF+\u540C\u8272\u56FE\u6807\uFF0C\u989C\u8272\u4E0E\u5361\u7247\u72B6\u6001\u6807\u7B7E\u4E00\u81F4\uFF09 */\nfunction iconForStatus(status: LinkedIssueStatus): string {\n switch (status) {\n case 'open':\n // issue \u8FDB\u884C\u4E2D\uFF1A\u7EFF\u8272\u5706\u73AF+\u7EFF\u70B9\uFF08issue-opened\uFF09\n return `<svg class=\"issue-ref-icon\" viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\" fill=\"#347d39\"><path d=\"M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z\"/><path d=\"M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Z\"/></svg>`;\n case 'completed':\n // issue \u5DF2\u5B8C\u6210\u5173\u95ED\uFF1A\u7D2B\u8272\u5706\u73AF+\u7D2B\u8272\u5BF9\u52FE\uFF08issue-closed\uFF09\n return `<svg class=\"issue-ref-icon\" viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\" fill=\"#8256d0\"><path d=\"M11.28 6.78a.75.75 0 0 0-1.06-1.06L7.25 8.69 5.78 7.22a.75.75 0 0 0-1.06 1.06l2 2a.75.75 0 0 0 1.06 0l3.5-3.5Z\"/><path d=\"M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0Zm-1.5 0a6.5 6.5 0 1 0-13 0 6.5 6.5 0 0 0 13 0Z\"/></svg>`;\n case 'not_planned':\n // issue \u672A\u8BA1\u5212\u7B49\u5173\u95ED\uFF1A\u7070\u8272\u5706\u73AF+\u7070\u8272\u659C\u6760\uFF08skip\uFF09\n return `<svg class=\"issue-ref-icon\" viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\" fill=\"#656c76\"><path d=\"M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm9.78-2.22-5.5 5.5a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734l5.5-5.5a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z\"/></svg>`;\n case 'pr_open':\n // PR \u8FDB\u884C\u4E2D\uFF1A\u7EFF\u8272 PR \u56FE\u6807\uFF08git-pull-request\uFF0C\u539F\u751F\u65E0\u5706\u73AF\uFF09\n return `<svg class=\"issue-ref-icon\" viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\" fill=\"#347d39\"><path d=\"M1.5 3.25a2.25 2.25 0 1 1 3 2.122v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.25 2.25 0 0 1 1.5 3.25Zm5.677-.177L9.573.677A.25.25 0 0 1 10 .854V2.5h1A2.5 2.5 0 0 1 13.5 5v5.628a2.251 2.251 0 1 1-1.5 0V5a1 1 0 0 0-1-1h-1v1.646a.25.25 0 0 1-.427.177L7.177 3.427a.25.25 0 0 1 0-.354ZM3.75 2.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm0 9.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm8.25.75a.75.75 0 1 1 1.5 0 .75.75 0 0 1-1.5 0Z\"/></svg>`;\n case 'pr_merged':\n // PR \u5DF2\u5408\u5E76\uFF1A\u7D2B\u8272\u5408\u5E76\u56FE\u6807\uFF08git-merge\uFF0C\u539F\u751F\u65E0\u5706\u73AF\uFF09\n return `<svg class=\"issue-ref-icon\" viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\" fill=\"#8256d0\"><path d=\"M5.45 5.154A4.25 4.25 0 0 0 9.25 7.5h1.378a2.251 2.251 0 1 1 0 1.5H9.25A5.734 5.734 0 0 1 5 7.123v3.505a2.25 2.25 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.95-.218ZM4.25 13.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm8.5-4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5ZM5 3.25a.75.75 0 1 0 0 .005V3.25Z\"/></svg>`;\n case 'pr_closed':\n // PR \u5173\u95ED\u672A\u5408\u5E76\uFF1A\u7EA2\u8272 PR \u5173\u95ED\u56FE\u6807\uFF08git-pull-request-closed\uFF0C\u539F\u751F\u65E0\u5706\u73AF\uFF09\n return `<svg class=\"issue-ref-icon\" viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\" fill=\"#cf222e\"><path d=\"M3.25 1A2.25 2.25 0 0 1 4 5.372v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.251 2.251 0 0 1 3.25 1Zm9.5 5.5a.75.75 0 0 1 .75.75v3.378a2.251 2.251 0 1 1-1.5 0V7.25a.75.75 0 0 1 .75-.75Zm-2.03-5.273a.75.75 0 0 1 1.06 0l.97.97.97-.97a.748.748 0 0 1 1.265.332.75.75 0 0 1-.205.729l-.97.97.97.97a.75.75 0 0 1-1.06 1.061l-.97-.97-.97.97a.75.75 0 0 1-1.06-1.06l.97-.97-.97-.97a.75.75 0 0 1 0-1.06ZM2.5 3.25a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm0 9.5a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm9.5 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Z\"/></svg>`;\n default:\n // \u72B6\u6001\u672A\u77E5\uFF08\u67E5\u8BE2\u5931\u8D25\u56DE\u9000\uFF0C\u901A\u5E38\u4E0D\u663E\u793A\uFF09\uFF1A\u7070\u8272\u5706\u73AF+\u95EE\u53F7\n return `<svg class=\"issue-ref-icon\" viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" aria-hidden=\"true\" fill=\"#8b949e\"><circle cx=\"8\" cy=\"8\" r=\"6.5\" fill=\"none\" stroke=\"#8b949e\" stroke-width=\"1.5\"/><text x=\"8\" y=\"11\" text-anchor=\"middle\" font-size=\"9\" font-family=\"sans-serif\">?</text></svg>`;\n }\n}\n\n/**\n * \u628A\u5355\u4E2A\u5F15\u7528\u6E32\u67D3\u4E3A GitHub \u98CE\u683C\u7684\u884C\u5185\u5F15\u7528\uFF1A\n * \u67E5\u5230\u6807\u9898\u65F6\u663E\u793A\u300C\u72B6\u6001\u56FE\u6807 + \u6807\u9898 + \u94FE\u63A5\u8272\u7F16\u53F7\u300D\uFF1B\u67E5\u8BE2\u5931\u8D25\u65F6\u56DE\u9000\u4E3A\u7EAF\u94FE\u63A5\u8272\u7F16\u53F7\u3002\n */\nfunction renderInlineRef(ref: LinkedIssueReference, detail: LinkedIssueDetail): string {\n const numberLabel = ref.crossRepo ? `${ref.repository}#${ref.number}` : `#${ref.number}`;\n const url = ref.repository ? `https://github.com/${ref.repository}/issues/${ref.number}` : '';\n if (!detail.title) {\n return `<a class=\"issue-ref-number\" href=\"${url}\">${numberLabel}</a>`;\n }\n return (\n `<span class=\"issue-ref issue-ref-${detail.status}\">${iconForStatus(detail.status)}` +\n `<a class=\"issue-ref-title\" href=\"${url}\">${escapeHtml(detail.title)}</a>` +\n `<a class=\"issue-ref-number\" href=\"${url}\">${numberLabel}</a></span>`\n );\n}\n\n/** \u5360\u4F4D\u7B26 -> \u5F15\u7528\u7D22\u5F15 */\nfunction placeholder(index: number): string {\n return `${PLACEHOLDER_PREFIX}${index}${PLACEHOLDER_SUFFIX}`;\n}\n\n/** \u5360\u4F4D\u7B26 -> \u7F29\u77ED\u94FE\u63A5\u7D22\u5F15 */\nfunction linkPlaceholder(index: number): string {\n return `${LINK_PLACEHOLDER_PREFIX}${index}${LINK_PLACEHOLDER_SUFFIX}`;\n}\n\n/**\n * \u628A\u6B63\u6587\u4E2D\u7684 issue/PR \u5B8C\u6574\u5730\u5740\u7F29\u77ED\u4E3A \"#\u7F16\u53F7\"\uFF08\u6E32\u67D3\u540E\u4E3A\u84DD\u8272\u94FE\u63A5\uFF09\u3002\n * \u4F8B\u5982 https://github.com/owner/repo/pull/1234 -> #1234\uFF0C\n * \u4E0D\u5904\u7406 compare/commit \u7B49\u5176\u4ED6 GitHub \u5730\u5740\uFF0C\u4EE3\u7801\u6BB5\u5185\u7684 URL \u4E5F\u8DF3\u8FC7\u3002\n * @param body \u539F\u59CB markdown\n * @param repository \u5F53\u524D\u4ED3\u5E93\uFF08owner/repo\uFF09\uFF0C\u540C\u4ED3\u5E93\u65F6\u53EA\u663E\u793A #\u7F16\u53F7\n * @param links \u6536\u96C6\u7F29\u77ED\u540E\u7684\u94FE\u63A5\u4FE1\u606F\n */\nfunction shortenIssueUrls(body: string, repository: string, links: ShortUrl[]): string {\n // \u5148\u6309\u4EE3\u7801\u6BB5\u5207\u5206\uFF0C\u53EA\u5904\u7406\u975E\u4EE3\u7801\u6BB5\uFF0C\u907F\u514D\u7834\u574F\u4EE3\u7801\u5757\u4E2D\u7684 URL\n return body\n .split(CODE_SEGMENT)\n .map((segment, index) => {\n // \u5947\u6570\u7D22\u5F15\u662F\u6355\u83B7\u5230\u7684\u4EE3\u7801\u6BB5\uFF0C\u539F\u6837\u4FDD\u7559\n if (index % 2 === 1) return segment;\n return segment.replace(ISSUE_URL, (match, owner: string, repo: string, kind: string, number: string) => {\n const fullName = `${owner}/${repo}`;\n const url = `https://github.com/${fullName}/${kind}/${number}`;\n const sameRepo = fullName.toLowerCase() === repository.toLowerCase();\n const index = links.length;\n links.push({ url, number: Number.parseInt(number, 10), label: sameRepo ? `#${number}` : `${fullName}#${number}` });\n return linkPlaceholder(index);\n });\n })\n .join('');\n}\n\n/**\n * \u4ECE\u6B63\u6587\u7684\u5217\u8868\u884C\u4E2D\u63D0\u53D6 issue/PR \u5F15\u7528\uFF0C\u66FF\u6362\u4E3A\u5360\u4F4D\u7B26\uFF08\u884C\u5185\u66FF\u6362\uFF0C\u4FDD\u7559\u5176\u5B83\u6587\u5B57\uFF09\u3002\n */\nfunction extractReferences(\n body: string,\n repository: string,\n references: LinkedIssueReference[]\n): string {\n const toRef = (reference: string): string => {\n // reference \u5F62\u5982 \"#4851\" \u6216 \"owner/repo#4851\"\n const hasRepo = reference.includes('/');\n const repo = (hasRepo ? reference.split('#')[0] : repository) ?? '';\n const numberText = reference.slice(reference.indexOf('#') + 1);\n const number = Number.parseInt(numberText, 10);\n if (!Number.isFinite(number)) return reference;\n const label = repo ? `${repo}#${number}` : `#${number}`;\n const index = references.length;\n references.push({ repository: repo, number, label, crossRepo: hasRepo });\n return placeholder(index);\n };\n // \u53EA\u5904\u7406\u5217\u8868\u884C\uFF1B\u884C\u5185\u6BCF\u4E2A #\u7F16\u53F7 \u72EC\u7ACB\u66FF\u6362\u4E3A\u5360\u4F4D\u7B26\uFF0C\u5176\u4F59\u6587\u5B57\u4E0D\u52A8\n return body.replace(LIST_LINE, line => line.replace(REF_IN_LINE, (m: string) => toRef(m)));\n}\n\n/**\n * \u6E32\u67D3 PR/Issue \u6B63\u6587 markdown\u3002\u5217\u8868\u884C\u4E2D\u7684 \"#\u7F16\u53F7\" \u5F15\u7528\u66FF\u6362\u4E3A\n * \u968F issue/PR \u72B6\u6001\u53D8\u5316\u7684\u884C\u5185\u5F15\u7528\uFF08\u56FE\u6807 + \u6807\u9898 + \u94FE\u63A5\u8272\u7F16\u53F7\uFF09\uFF0C\n * \u884C\u5185\u5176\u5B83\u6587\u5B57\uFF08resolved / fixed / \u81EA\u5B9A\u4E49\u6587\u5B57\uFF09\u539F\u6837\u4FDD\u7559\uFF1B\n * \u5F02\u6B65\u67E5\u8BE2\u72B6\u6001\u4E0E\u6807\u9898\uFF0C\u5931\u8D25\u56DE\u9000\u4E3A\u7EAF\u94FE\u63A5\u8272\u7F16\u53F7\u3002\n * @param body \u539F\u59CB markdown \u6B63\u6587\n * @param repository \u5F53\u524D\u4ED3\u5E93\uFF08owner/repo\uFF09\uFF0C\u7528\u4E8E\u5C55\u5F00 \"#1234\" \u5F62\u5F0F\u7684\u77ED\u5F15\u7528\n * @param logger \u53EF\u9009\u65E5\u5FD7\u5668\n */\nexport async function renderIssueBody(body?: string, repository: string = '', logger?: Logger): Promise<any> {\n const references: LinkedIssueReference[] = [];\n const links: ShortUrl[] = [];\n // \u5148\u628A\u5B8C\u6574 issue/PR \u5730\u5740\u7F29\u77ED\u4E3A\u5360\u4F4D\u7B26\uFF08\u4E0D\u67E5 API\uFF09\uFF0C\u518D\u5904\u7406 #\u7F16\u53F7 \u5F15\u7528\n const shortened = shortenIssueUrls(body || '', repository, links);\n const withPlaceholders = extractReferences(shortened, repository, references);\n let html = renderMarkdown(withPlaceholders);\n\n // \u7F29\u77ED\u94FE\u63A5 -> \u84DD\u8272 #\u7F16\u53F7\uFF08\u7EAF\u94FE\u63A5\uFF0C\u4E0D\u67E5\u8BE2\u72B6\u6001\u4E0E\u6807\u9898\uFF09\n links.forEach((link, i) => {\n const shortHtml = `<a class=\"issue-ref-number\" href=\"${link.url}\">${link.label}</a>`;\n html = html.split(linkPlaceholder(i)).join(shortHtml);\n });\n\n if (references.length === 0) {\n return html;\n }\n\n // \u5E76\u53D1\u67E5\u8BE2\u6240\u6709\u5F15\u7528\u7684 issue \u72B6\u6001\u4E0E\u6807\u9898\uFF1B\u5355\u4E2A\u5931\u8D25\u56DE\u9000\u4E3A unknown\uFF08\u65E0\u6807\u9898\uFF09\uFF0C\u4E0D\u963B\u585E\u5176\u4ED6\n const details = await Promise.all(\n references.map(ref => fetchIssueDetail(ref.repository, ref.number, logger))\n );\n\n // \u5360\u4F4D\u7B26 -> \u884C\u5185\u5F15\u7528 HTML\uFF08\u5217\u8868\u7ED3\u6784\u4E0E\u5176\u4F59\u6587\u5B57\u5DF2\u7531 markdown \u6E32\u67D3\u4FDD\u7559\uFF09\n let replaced = html;\n references.forEach((ref, i) => {\n const refHtml = renderInlineRef(ref, details[i] ?? { status: 'unknown' });\n replaced = replaced.split(placeholder(i)).join(refHtml);\n });\n return replaced;\n}\n\nexport function renderMarkdown(body?: string): any {\n const taskLists = require('markdown-it-task-lists');\n const abbr = require('markdown-it-abbr');\n const container = require('markdown-it-container');\n const footnote = require('markdown-it-footnote');\n const ins = require('markdown-it-ins');\n const mark = require('markdown-it-mark');\n const sup = require('markdown-it-sup');\n const sub = require('markdown-it-sub');\n const renderer = markdownit({\n html: true,\n linkify: true,\n breaks: true,\n xhtmlOut: true,\n highlight: function (str, lang): any {\n if (lang && hljs.getLanguage(lang)) {\n try {\n return (\n '<pre><code class=\"hljs\">' +\n hljs.highlight(str, { language: lang, ignoreIllegals: true }).value +\n '</code></pre>'\n );\n } catch (__) {}\n }\n\n return '<pre><code class=\"hljs\">' + renderer.utils.escapeHtml(str) + '</code></pre>';\n }\n })\n .use(taskLists)\n .use(abbr)\n .use(container)\n .use(emoji)\n .use(footnote)\n .use(ins)\n .use(mark)\n .use(sup)\n .use(sub);\n return renderer.render(body || '');\n}\n\nexport class GitHubImage {\n public static issuesHandler(\n issue: Issue,\n logger?: Logger,\n operation?: string,\n sender?: User,\n extra?: string\n ): Promise<string> {\n return issuesHandler(issue, logger, operation, sender, extra);\n }\n\n public static prHandler(\n pr: PullRequest,\n logger?: Logger,\n operation?: string,\n sender?: User,\n extra?: string\n ): Promise<string> {\n return prHandler(pr, logger, operation, sender, extra);\n }\n\n public static issuesOpened(issue: OpenedIssueEvent | ReopenedIssueEvent, logger?: Logger): Promise<string> {\n return issuesOpened(issue, logger);\n }\n\n public static issuesClosed(issue: ClosedIssueEvent, logger?: Logger): Promise<string> {\n return issuesClosed(issue, logger);\n }\n\n public static prOpened(pr: OpenedPullRequestEvent | ReopenedPullRequestEvent, logger?: Logger): Promise<string> {\n return prOpened(pr, logger);\n }\n\n public static prClosed(pr: ClosedPullRequestEvent, logger?: Logger): Promise<string> {\n return prClosed(pr, logger);\n }\n\n public static releaseHandler(\n release: Release,\n repository?: string,\n logger?: Logger,\n sender?: User\n ): Promise<string> {\n return releaseHandler(release, repository, logger, sender);\n }\n\n public static releasePublished(event: AllReleaseEvent, logger?: Logger): Promise<string> {\n return releasePublished(event, logger);\n }\n}\n", "import { randomInt } from 'node:crypto';\nimport path from 'node:path';\nimport axios from 'axios';\nimport { readOrCreate, writeConfigFile } from '@/config/manager';\n\ninterface PendingBinding {\n code: string;\n username: string;\n}\n\ninterface GitHubBindings {\n bindings: Record<string, string[]>;\n githubToQq: Record<string, string>;\n pending: Record<string, PendingBinding>;\n}\n\ninterface GitHubUserProfile {\n login: string;\n bio: string | null;\n}\n\nexport class GitHubBindingManager {\n private static readonly FILE = path.resolve(process.cwd(), 'configs', 'github-bind.json');\n private static readonly CODE_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n private static bindings: GitHubBindings | undefined;\n\n /**\n * \u7ED1\u5B9A GitHub \u7528\u6237\u540D\u3002\n * @param qqUserId \u5E73\u53F0\u5185\u7528\u6237\u6807\u8BC6\uFF1AQQ \u53F7\u4E3A number\uFF1BDiscord \u7528\u6237\u4E3A `dc:<\u7528\u6237ID>` \u5B57\u7B26\u4E32\n * \uFF08`dc:` \u524D\u7F00\u4FDD\u8BC1\u4E0E\u7EAF\u6570\u5B57 QQ \u53F7\u4E0D\u51B2\u7A81\uFF0C\u5171\u7528\u540C\u4E00\u5957\u7ED1\u5B9A\u5B58\u50A8\uFF09\n */\n public static async bind(qqUserId: number | string, username: string): Promise<{ state: 'pending' | 'bound'; code?: string }> {\n const normalizedUsername = GitHubBindingManager.normalizeUsername(username);\n const bindings = GitHubBindingManager.load();\n const qqUserIdText = String(qqUserId);\n\n const boundQqUserId = bindings.githubToQq[normalizedUsername.toLowerCase()];\n if (boundQqUserId && boundQqUserId !== qqUserIdText) {\n throw new Error(`GitHub \u7528\u6237 ${normalizedUsername} \u5DF2\u88AB\u5176\u4ED6\u7528\u6237\u7ED1\u5B9A`);\n }\n if (GitHubBindingManager.hasBinding(qqUserIdText, normalizedUsername)) {\n return { state: 'bound' };\n }\n\n const pendingKey = `${qqUserIdText}:${normalizedUsername.toLowerCase()}`;\n const pending = bindings.pending[pendingKey];\n if (!pending) {\n const code = GitHubBindingManager.generateCode();\n bindings.pending[pendingKey] = { code, username: normalizedUsername };\n GitHubBindingManager.save();\n return { state: 'pending', code };\n }\n\n const profile = await GitHubBindingManager.getUserProfile(pending.username);\n if (!profile.bio?.includes(pending.code)) {\n return { state: 'pending', code: pending.code };\n }\n\n const boundUsernames = bindings.bindings[qqUserIdText] || [];\n const normalizedProfileUsername = profile.login.toLowerCase();\n const profileBoundQqUserId = bindings.githubToQq[normalizedProfileUsername];\n if (profileBoundQqUserId && profileBoundQqUserId !== qqUserIdText) {\n throw new Error(`GitHub \u7528\u6237 ${profile.login} \u5DF2\u88AB\u5176\u4ED6\u7528\u6237\u7ED1\u5B9A`);\n }\n if (!boundUsernames.some(boundUsername => boundUsername.toLowerCase() === normalizedProfileUsername)) {\n boundUsernames.push(profile.login);\n bindings.bindings[qqUserIdText] = boundUsernames;\n }\n bindings.githubToQq[normalizedProfileUsername] = qqUserIdText;\n delete bindings.pending[pendingKey];\n GitHubBindingManager.save();\n return { state: 'bound' };\n }\n\n public static isBoundUsername(username: string): boolean {\n const normalizedUsername = username.toLowerCase();\n return Boolean(GitHubBindingManager.load().githubToQq[normalizedUsername]);\n }\n\n private static normalizeUsername(username: string): string {\n if (!/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/.test(username)) {\n throw new Error('GitHub \u7528\u6237\u540D\u683C\u5F0F\u65E0\u6548');\n }\n return username;\n }\n\n private static generateCode(): string {\n let code = '';\n for (let index = 0; index < 12; index++) {\n code += GitHubBindingManager.CODE_ALPHABET[randomInt(GitHubBindingManager.CODE_ALPHABET.length)];\n }\n return code;\n }\n\n private static async getUserProfile(username: string): Promise<GitHubUserProfile> {\n try {\n const response = await axios.get<GitHubUserProfile>(`https://api.github.com/users/${username}`, {\n timeout: 10000,\n headers: { Accept: 'application/vnd.github+json' }\n });\n return response.data;\n } catch (error) {\n if (axios.isAxiosError(error) && error.response?.status === 404) {\n throw new Error(`\u672A\u627E\u5230 GitHub \u7528\u6237 ${username}`);\n }\n throw new Error(`\u65E0\u6CD5\u67E5\u8BE2 GitHub \u7528\u6237 ${username}`);\n }\n }\n\n private static hasBinding(qqUserId: string, username: string): boolean {\n return (GitHubBindingManager.load().bindings[qqUserId] || []).some(\n boundUsername => boundUsername.toLowerCase() === username.toLowerCase()\n );\n }\n\n /** \u91CD\u65B0\u4ECE\u78C1\u76D8\u52A0\u8F7D\u7ED1\u5B9A\u6570\u636E\uFF08/reload \u7528\uFF1B\u6E05\u7A7A\u5185\u5B58\u7F13\u5B58\u540E\u61D2\u52A0\u8F7D\uFF09 */\n public static reload(): void {\n GitHubBindingManager.bindings = undefined;\n }\n\n private static load(): GitHubBindings {\n if (GitHubBindingManager.bindings) return GitHubBindingManager.bindings;\n\n const { data } = readOrCreate<GitHubBindings>({\n path: GitHubBindingManager.FILE,\n factory: () => ({ bindings: {}, githubToQq: {}, pending: {} }),\n normalize: raw => {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null;\n const parsed = raw as Partial<GitHubBindings>;\n const bindings: Record<string, string[]> =\n parsed.bindings && typeof parsed.bindings === 'object' ? (parsed.bindings as Record<string, string[]>) : {};\n const githubToQq: Record<string, string> =\n parsed.githubToQq && typeof parsed.githubToQq === 'object' ? (parsed.githubToQq as Record<string, string>) : {};\n\n for (const [qqUserId, usernames] of Object.entries(bindings)) {\n if (!Array.isArray(usernames)) continue;\n for (const username of usernames) {\n if (typeof username !== 'string') continue;\n const normalizedUsername = username.toLowerCase();\n if (!githubToQq[normalizedUsername]) {\n githubToQq[normalizedUsername] = qqUserId;\n }\n }\n }\n\n return {\n bindings,\n githubToQq,\n pending: parsed.pending && typeof parsed.pending === 'object' ? (parsed.pending as Record<string, PendingBinding>) : {}\n };\n }\n });\n GitHubBindingManager.bindings = data;\n return GitHubBindingManager.bindings;\n }\n\n private static save(): void {\n if (!GitHubBindingManager.bindings) return;\n writeConfigFile(GitHubBindingManager.FILE, GitHubBindingManager.bindings);\n }\n}\n", "import { EventManager } from 'gugle-event';\nimport { Logger } from 'winston';\nimport http from 'node:http';\nimport { QQBot } from '@/index';\nimport { AllIssueEvent, AllPullRequestEvent, AllReleaseEvent, Issue, PullRequest } from '@/type/github';\nimport { GroupMessageWSMSG, Message, SentMessage, TextMessage } from '@/type';\nimport { GitHubImage } from '@/features/github/image';\nimport { botConfig } from '@/config';\nimport { getGithubSubscribers, isGithubEnabledGroup } from '@/config/features';\nimport { GitHubBindingManager } from '@/features/github/binding';\nimport { fetchGithubApi } from '@/features/github/api';\nimport { shouldPushRelease } from '@/features/github/image/impl/release';\nimport axios, { AxiosInstance } from 'axios';\n\n/** \u4E00\u6761 Issue/PR \u5F15\u7528\uFF08`owner/repo#123`\uFF0Crepo \u7701\u7565\u65F6\u4E3A\u9ED8\u8BA4\u4ED3\u5E93\uFF09 */\nexport interface IssueReference {\n repository: string;\n number: number;\n}\n\nexport class Github {\n private readonly bot: QQBot;\n private readonly logger: Logger;\n private readonly eventManager: EventManager;\n private httpServer: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;\n\n public static readonly axiosInstance: AxiosInstance = axios.create({\n timeout: 15000,\n baseURL: botConfig.httpUrl,\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': botConfig.userAgent\n }\n });\n\n /**\n * \u8BF7\u6C42 GitHub API\uFF08\u7ECF ghapi.anvilcraft.dev \u53CD\u4EE3\uFF09\uFF0C\u4F9B processMessage \u4F7F\u7528\u3002\n * @param apiPath GitHub API \u8DEF\u5F84\uFF08\u4E0D\u542B\u57DF\u540D\uFF09\uFF0C\u5982 /repos/owner/repo/issues/123\n */\n public static fetchGithubApi<T>(bot: QQBot, apiPath: string): Promise<T> {\n return fetchGithubApi<T>(apiPath, bot.logger);\n }\n\n public constructor(bot: QQBot) {\n this.bot = bot;\n this.logger = bot.logger!;\n this.eventManager = new EventManager();\n this.eventManager.listen('github-issues', this.listenIssueEvent.bind(this));\n this.eventManager.listen('github-pull_request', this.listenPullRequestEvent.bind(this));\n this.eventManager.listen('github-release', this.listenReleaseEvent.bind(this));\n this.httpServer = http.createServer((req, res) => {\n if (req.method === 'POST') {\n let body = '';\n req.on('data', chunk => {\n body += chunk.toString(); // \u5C06\u6570\u636E\u5757\u62FC\u63A5\u6210\u5B57\u7B26\u4E32\n });\n req.on('end', () => {\n try {\n const event = (req.headers['x-github-event'] as string) || '';\n body = JSON.parse(body);\n bot.logger?.debug(`Receive Github Event: github-${event}`);\n this.post(`github-${event}`, bot, body);\n res.statusCode = 202;\n res.setHeader('Content-Type', 'text/plain');\n res.end('Accepted');\n } catch (e) {\n bot.logger?.error(e);\n }\n });\n return;\n }\n res.statusCode = 202;\n res.setHeader('Content-Type', 'text/plain');\n res.end('Accepted');\n });\n }\n\n private listenIssueEvent(bot: QQBot, msg: AllIssueEvent) {\n let promise: Promise<string> | undefined = undefined;\n if (msg.action === 'opened' || msg.action === 'reopened') {\n promise = GitHubImage.issuesOpened(msg, this.logger);\n } else if (msg.action === 'closed') {\n promise = GitHubImage.issuesClosed(msg, this.logger);\n }\n const repository = msg.repository.full_name;\n this.sendGeneratedImage(bot, repository, 'issue', promise);\n }\n\n private listenPullRequestEvent(bot: QQBot, msg: AllPullRequestEvent) {\n let promise: Promise<string> | undefined = undefined;\n if (msg.action === 'opened' || msg.action === 'reopened') {\n promise = GitHubImage.prOpened(msg, this.logger);\n } else if (msg.action === 'closed') {\n promise = GitHubImage.prClosed(msg, this.logger);\n }\n const repository = msg.repository.full_name;\n this.sendGeneratedImage(bot, repository, 'pull request', promise);\n }\n\n /**\n * \u5904\u7406\u4ED3\u5E93\u53D1\u5E03 release \u7684 webhook \u4E8B\u4EF6\u3002\n *\n * GitHub \u5BF9\u540C\u4E00\u6B21\u53D1\u5E03\u4F1A\u6295\u9012\u591A\u4E2A action\uFF08\u5B9E\u6D4B\u540C\u4E00 release id \u4F1A\u5728\u6570\u79D2\u5185\u5148\u540E\u6536\u5230\n * created + released\u3001published + created + prereleased \u7B49\u7EC4\u5408\uFF0C\u4E14\u7EC4\u5408\u4E0D\u56FA\u5B9A\uFF09\uFF0C\n * \u56E0\u6B64\u6309 action \u8FC7\u6EE4\u5E76\u4E0D\u80FD\u907F\u514D\u91CD\u590D\uFF0C\u8FD9\u91CC\u4EE5 release id \u5728\u65F6\u95F4\u7A97\u53E3\u5185\u53BB\u91CD\u3002\n */\n private listenReleaseEvent(bot: QQBot, msg: AllReleaseEvent) {\n const decision = shouldPushRelease(msg);\n if (!decision.push) {\n this.logger?.debug(`Skip release event ${msg.release?.id} (${msg.action}): ${decision.reason}`);\n return;\n }\n const repository = msg.repository.full_name;\n this.sendGeneratedImage(bot, repository, 'release', GitHubImage.releasePublished(msg, this.logger));\n }\n\n /**\n * \u628A\u751F\u6210\u7684\u56FE\u7247\u63A8\u9001\u7ED9\u8BA2\u9605\u4E86\u8BE5\u4ED3\u5E93\u7684\u7FA4\uFF08\u4ED3\u5E93\u7EA7\u8DEF\u7531\uFF09\u3002\n * \u53EA\u53D1\u7ED9 github.json \u4E2D repository \u2192 \u8BA2\u9605\u7FA4\u5217\u8868\u5BF9\u5E94\u7684\u7FA4\u3002\n */\n private sendGeneratedImage(bot: QQBot, repository: string, type: string, result: Promise<string> | undefined = undefined) {\n const subscribers = getGithubSubscribers(repository);\n if (subscribers.length === 0) {\n this.logger?.debug(`No subscribers for repository ${repository}, skip ${type} message...`);\n return;\n }\n result\n ?.then(base64 => {\n const msg: SentMessage = [\n {\n type: 'image',\n data: {\n file: `data:image/png;base64, ${base64}`\n }\n }\n ];\n subscribers.forEach(group => {\n // fromWebhook\uFF1A\u6807\u8BB0\u4E3A webhook \u63A8\u9001\uFF0C\u4E92\u901A\u6865\u636E\u6B64\u4E0D\u8F6C\u53D1\u5230 need_cmd/need_reply \u9891\u9053\n bot.sendGroupMsg(group, msg, { fromWebhook: true });\n });\n this.logger?.debug(`Sent process ${type} message of ${repository} to ${subscribers.length} group(s)...`);\n })\n .catch(e => {\n this.logger?.error(e);\n });\n }\n\n public post(event: string, ...args: any): Promise<any[]> {\n return this.eventManager.post(event, ...args);\n }\n\n public start(port: number): void {\n const self = this;\n this.httpServer.listen(port, () => {\n self.logger?.info(`http server listen on port ${port}`);\n });\n }\n\n /**\n * \u5224\u65AD\u4ED3\u5E93\u662F\u5426\u5141\u8BB8\u88AB\u8BBF\u95EE\uFF1Aowner \u5DF2\u7ED1\u5B9A GitHub \u8D26\u53F7\uFF0C\u6216\u547D\u4E2D\u5141\u8BB8\u5217\u8868\u914D\u7F6E\u3002\n * \u6D88\u606F\u67E5\u8BE2\u4E0E\u8BA2\u9605\u5171\u7528\u8BE5\u5224\u5B9A\uFF0C\u4FDD\u8BC1\u300C\u80FD\u67E5\u300D\u4E0E\u300C\u80FD\u8BA2\u9605\u300D\u53E3\u5F84\u4E00\u81F4\u3002\n */\n public static isAllowedRepository(repository: string): boolean {\n const [owner] = repository.split('/');\n if (owner && GitHubBindingManager.isBoundUsername(owner)) return true;\n\n return botConfig.githubAllowedRepositories.some(pattern => {\n if (pattern.endsWith('/*')) return repository.startsWith(`${pattern.slice(0, -2)}/`);\n return repository === pattern;\n });\n }\n\n /** \u5355\u6761\u6D88\u606F\u6700\u591A\u89E3\u6790\u7684 Issue/PR \u5F15\u7528\u6570\uFF08\u9632\u6B62\u5237\u5C4F\uFF0C\u8D85\u51FA\u90E8\u5206\u5FFD\u7565\uFF09 */\n public static readonly MAX_REFERENCES_PER_MESSAGE = 10;\n\n /**\n * \u89E3\u6790\u6587\u672C\u4E2D\u7684\u5168\u90E8 Issue/PR \u5F15\u7528\uFF08`owner/repo#123` \u6216 `#123`\uFF09\u3002\n * \u6309\u51FA\u73B0\u987A\u5E8F\u53BB\u91CD\uFF08`repository#number`\uFF09\uFF0C\u6700\u591A\u8FD4\u56DE MAX_REFERENCES_PER_MESSAGE \u6761\u3002\n */\n public static parseReferences(text: string, defaultRepository: string = 'Anvil-Dev/AnvilCraft'): IssueReference[] {\n const pattern = /(?:([A-Za-z0-9_.-]+\\/[A-Za-z0-9_.-]+))?#(\\d+)/g;\n const references: IssueReference[] = [];\n const seen = new Set<string>();\n let match: RegExpExecArray | null;\n while ((match = pattern.exec(text)) !== null) {\n const repository = match[1] || defaultRepository;\n const number = Number.parseInt(match[2]!, 10);\n const key = `${repository}#${number}`;\n if (seen.has(key)) continue;\n seen.add(key);\n references.push({ repository, number });\n if (references.length >= Github.MAX_REFERENCES_PER_MESSAGE) break;\n }\n return references;\n }\n\n /**\n * \u6E32\u67D3\u5355\u4E2A Issue/PR \u5F15\u7528\u4E3A QQ \u6D88\u606F\u6BB5\uFF1A\u6210\u529F\u4E3A\u56FE\u7247\u6BB5\uFF0C\u5931\u8D25\u4E3A\u6587\u672C\u8BF4\u660E\u6BB5\u3002\n * \u4ED3\u5E93\u4E0D\u5728\u5141\u8BB8\u5217\u8868\u65F6\u8FD4\u56DE\u63D0\u793A\u6587\u672C\uFF08\u4E0D\u53D1\u8BF7\u6C42\uFF09\u3002\n */\n public static async renderReference(bot: QQBot, reference: IssueReference): Promise<Message> {\n const { repository, number } = reference;\n if (!this.isAllowedRepository(repository)) {\n return {\n type: 'text',\n data: {\n text: `\u4ED3\u5E93 ${repository} \u4E0D\u5728\u5141\u8BB8\u8BBF\u95EE\u7684\u4ED3\u5E93\u5217\u8868\u4E2D`\n }\n };\n }\n\n const issueApiPath = `/repos/${repository}/issues/${number}`;\n const pullApiPath = `/repos/${repository}/pulls/${number}`;\n\n // \u7ECF ghapi \u53CD\u4EE3\u83B7\u53D6 issue \u6570\u636E\n let issueData: Issue & { pull_request?: unknown };\n try {\n issueData = await Github.fetchGithubApi<Issue & { pull_request?: unknown }>(bot, issueApiPath);\n } catch (e) {\n bot.logger?.error(e);\n return {\n type: 'text',\n data: {\n text: `\u8BF7\u6C42\u5931\u8D25\uFF0C\u539F\u56E0\uFF1A${(e as Error)?.message ?? e}`\n }\n };\n }\n\n try {\n if (issueData.pull_request) {\n // \u5982\u679C\u662F PR\uFF0C\u7ECF ghapi \u53CD\u4EE3\u83B7\u53D6 PR \u6570\u636E\n const prData = await Github.fetchGithubApi<PullRequest>(bot, pullApiPath);\n const imageData = await GitHubImage.prHandler(prData, bot.logger);\n return {\n type: 'image',\n data: {\n file: `data:image/png;base64, ${imageData}`\n }\n };\n }\n // \u5904\u7406 issue\n const imageData = await GitHubImage.issuesHandler(issueData as Issue, bot.logger);\n return {\n type: 'image',\n data: {\n file: `data:image/png;base64, ${imageData}`\n }\n };\n } catch (e) {\n bot.logger?.error(e);\n return {\n type: 'text',\n data: {\n text: `\u56FE\u7247\u5904\u7406\u5931\u8D25\uFF0C\u539F\u56E0\uFF1A${(e as Error)?.message ?? e}`\n }\n };\n }\n }\n\n /**\n * QQ \u7FA4\u6D88\u606F\u5165\u53E3\uFF1A\u89E3\u6790\u6D88\u606F\u4E2D\u7684\u5168\u90E8 Issue/PR \u5F15\u7528\u5E76\u9010\u4E2A\u6E32\u67D3\u3002\n * \u7B2C\u4E00\u4E2A\u5F15\u7528\u7684\u7ED3\u679C\u5E76\u5165 sentMessage\uFF08\u968F\u8C03\u7528\u65B9\u7684\u56DE\u590D\u6D88\u606F\u4E00\u8D77\u53D1\u51FA\uFF09\uFF1B\n * \u5176\u4F59\u5F15\u7528\u5404\u81EA\u4F5C\u4E3A\u72EC\u7ACB\u7FA4\u6D88\u606F\u53D1\u9001\uFF08\u7528\u6237\u8981\u6C42\u591A\u7F16\u53F7\u65F6\u5206\u591A\u6761\u6D88\u606F\u53D1\u9001\uFF09\u3002\n * \u6E32\u67D3\u4E32\u884C\u6267\u884C\u4EE5\u4FDD\u8BC1\u6D88\u606F\u987A\u5E8F\u4E0E\u7F16\u53F7\u987A\u5E8F\u4E00\u81F4\u3002\n */\n public static async processMessage(bot: QQBot, msg: GroupMessageWSMSG, sentMessage: Message[]): Promise<void> {\n if (!isGithubEnabledGroup(msg.group_id)) return;\n\n const receivedMessage: TextMessage[] = [];\n msg.message.forEach(message => {\n if (message.type != 'text') return;\n receivedMessage.push(message);\n });\n\n const strMsg = receivedMessage.map(msg => msg.data.text).join(' ');\n const references = Github.parseReferences(strMsg);\n if (references.length === 0) return;\n\n for (let index = 0; index < references.length; index++) {\n const rendered = await Github.renderReference(bot, references[index]!);\n if (index === 0) {\n sentMessage.push(rendered);\n } else {\n await bot.sendGroupMsg(msg.group_id, [rendered]);\n }\n }\n }\n\n /**\n * Discord \u4FA7\u5165\u53E3\uFF1A\u89E3\u6790\u6587\u672C\u4E2D\u7684\u5168\u90E8 Issue/PR \u5F15\u7528\u5E76\u6E32\u67D3\u4E3A\u6D88\u606F\u6BB5\u5217\u8868\uFF08\u7531\u8C03\u7528\u65B9\u53D1\u9001\uFF09\u3002\n * groupId \u4E3A Discord \u9891\u9053\u6865\u63A5\u7684 QQ \u7FA4\u53F7\uFF0C\u7528\u4E8E\u590D\u7528\u300C\u8BE5\u7FA4\u662F\u5426\u542F\u7528 github \u529F\u80FD\u300D\u7684\u5224\u5B9A\u3002\n */\n public static async processDiscordMessage(bot: QQBot, text: string, groupId?: number): Promise<Message[]> {\n if (groupId !== undefined && !isGithubEnabledGroup(groupId)) return [];\n const references = Github.parseReferences(text);\n const results: Message[] = [];\n for (const reference of references) {\n results.push(await Github.renderReference(bot, reference));\n }\n return results;\n }\n}\n", "import { CommandManager, CommandSource } from 'gugle-command';\n\n/**\n * \u5E73\u53F0\u65E0\u5173\u7684\u547D\u4EE4\u6E90\u62BD\u8C61\u3002\n *\n * \u80CC\u666F\uFF1A`gugle-command` \u7684 `CommandSource` \u53EA\u6709 success/fail/getName/hasPermission \u56DB\u4E2A\u65B9\u6CD5\uFF0C\n * \u547D\u4EE4\u5B9E\u73B0\u82E5\u76F4\u63A5 `instanceof GroupMsgCommandSource` \u5224\u5B9A\uFF0C\u5C31\u53EA\u80FD\u670D\u52A1 QQ \u7FA4\u6D88\u606F\u3002\n * \u8FD9\u91CC\u6269\u5C55\u51FA\u8DE8\u5E73\u53F0\u9700\u8981\u7684\u6700\u5C0F\u80FD\u529B\u96C6\uFF0C\u8BA9\u540C\u4E00\u5957\u547D\u4EE4\u5B9E\u73B0\u65E2\u80FD\u5728 QQ \u7FA4\u3001\u4E5F\u80FD\u5728 Discord \u9891\u9053\u6267\u884C\u3002\n */\nexport interface BotCommandSource extends CommandSource {\n /**\n * \u8C03\u7528\u8005\u5E73\u53F0\u5185\u552F\u4E00\u6807\u8BC6\uFF1A\n * - QQ \u7FA4\u6D88\u606F\uFF1A\u7EAF\u6570\u5B57 QQ \u53F7\u5B57\u7B26\u4E32\uFF08\u5982 `2308465862`\uFF09\n * - Discord \u6D88\u606F\uFF1A`dc:<\u7528\u6237ID>`\uFF08\u5982 `dc:123456789012345678`\uFF09\n *\n * \u7528\u4E8E GitHub \u7ED1\u5B9A\u7B49\u9700\u8981\u6301\u4E45\u5316\u7528\u6237\u8EAB\u4EFD\u7684\u573A\u666F\uFF1B`dc:` \u524D\u7F00\u4FDD\u8BC1\u4E0E\u7EAF\u6570\u5B57 QQ \u53F7\u4E0D\u4F1A\u51B2\u7A81\u3002\n */\n getUserId(): string;\n\n /**\n * \u6D88\u606F\u6240\u5728\u7FA4/\u9891\u9053\u5728\u5F53\u524D\u5E73\u53F0\u4E0A\u5BF9\u5E94\u7684\u4E92\u901A QQ \u7FA4\u53F7\u3002\n * QQ \u7FA4\u6D88\u606F\u8FD4\u56DE\u81EA\u8EAB\u7FA4\u53F7\uFF1BDiscord \u9891\u9053\u8FD4\u56DE\u8BE5\u9891\u9053\u6865\u63A5\u914D\u7F6E\u91CC\u7684 QQ \u7FA4\u53F7\uFF1B\n * \u65E0\u5F52\u5C5E\u7FA4\uFF08\u672A\u914D\u7F6E\u4E92\u901A\u7684 Discord \u9891\u9053\u7B49\uFF09\u8FD4\u56DE undefined\u3002\n */\n getGroupId(): number | undefined;\n\n /**\n * \u662F\u5426\u4E3A\u7BA1\u7406\u5458\uFF1AQQ \u4FA7\u590D\u7528 management operators \u767D\u540D\u5355\uFF1B\n * Discord \u4FA7\u4E3A\u670D\u52A1\u5668\u62E5\u6709\u8005\u6216\u62E5\u6709\u7BA1\u7406\u670D\u52A1\u5668/\u7BA1\u7406\u5458\u6743\u9650\u8005\u3002\n */\n isAdmin(): boolean;\n}\n\n/** \u5224\u65AD\u547D\u4EE4\u6E90\u662F\u5426\u5177\u5907 BotCommandSource \u80FD\u529B\uFF08\u9E2D\u5B50\u7C7B\u578B\uFF0C\u8DE8\u6A21\u5757\u5B9E\u4F8B\u4E5F\u6210\u7ACB\uFF09 */\nexport function isBotCommandSource(source: CommandSource | undefined): source is BotCommandSource {\n if (!source) return false;\n const candidate = source as Partial<BotCommandSource>;\n return (\n typeof candidate.getUserId === 'function' &&\n typeof candidate.getGroupId === 'function' &&\n typeof candidate.isAdmin === 'function'\n );\n}\n\n/**\n * \u7531\u547D\u4EE4\u6E90\u63A8\u5BFC\u300C\u64CD\u4F5C\u8005\u6807\u8BC6\u300D\uFF0C\u4F9B\u9700\u8981\u533A\u5206\u5E73\u53F0\u7684\u7BA1\u7406\u903B\u8F91\u4F7F\u7528\u3002\n *\n * \u80CC\u666F\uFF1A`executeCommand` \u4F1A\u628A\u547D\u4EE4\u6E90\u5305\u8FDB `FailOnceCommandSource` \u4EE3\u7406\uFF0C\n * \u547D\u4EE4\u5B9E\u73B0\u91CC `source instanceof GroupMsgCommandSource` \u6052\u4E3A false\u2014\u2014\u66FE\u56E0\u6B64\u8BA9\n * QQ \u7BA1\u7406\u5458\u6267\u884C /pardon \u88AB\u8BEF\u5224\u6210 Discord \u7528\u6237\u800C\u5931\u8D25\u3002\u8FD9\u91CC\u6539\u4E3A\u6309 BotCommandSource\n * \u7684 id \u7EA6\u5B9A\u5224\u5B9A\uFF0C\u4E0D\u4F9D\u8D56\u5B9E\u4F8B\u7C7B\u578B\uFF1A\n * - \u7EAF\u6570\u5B57 \u2192 QQ \u53F7\uFF0C\u8FD4\u56DE number\uFF08Management \u5185\u90E8\u518D\u6821\u9A8C operators \u767D\u540D\u5355\uFF09\uFF1B\n * - `dc:<\u7528\u6237ID>` \u2192 Discord \u7528\u6237\uFF0C\u539F\u6837\u8FD4\u56DE\u5B57\u7B26\u4E32\uFF08\u670D\u52A1\u5668\u7BA1\u7406\u5458\u8EAB\u4EFD\u5DF2\u5728\u547D\u4EE4\u6E90\u5C42\u6821\u9A8C\uFF09\u3002\n */\nexport function resolveOperatorId(source: BotCommandSource): number | string {\n const userId = source.getUserId();\n return /^\\d+$/.test(userId) ? Number(userId) : userId;\n}\n\n/**\n * \u628A\u547D\u4EE4\u6587\u672C\u89C4\u8303\u5316\u4E3A gugle-command \u53EF\u6267\u884C\u7684\u5F62\u5F0F\u3002\n *\n * gugle-command \u56FA\u5B9A\u4F7F\u7528 `/` \u524D\u7F00\uFF1BDiscord \u5BA2\u6237\u7AEF\u4F1A\u628A\u4EE5 `/` \u5F00\u5934\u7684\u5185\u5BB9\u5F53\u4F5C\u659C\u6760\u547D\u4EE4\u8F93\u5165\uFF0C\n * \u56E0\u6B64 Discord \u4FA7\u989D\u5916\u652F\u6301 `!` \u524D\u7F00\u4F5C\u4E3A\u515C\u5E95\uFF0C\u7EDF\u4E00\u5728\u8FD9\u91CC\u8F6C\u6362\u3002\n */\nexport function normalizeCommandText(text: string): string {\n return text.startsWith('!') ? `/${text.slice(1)}` : text;\n}\n\n/**\n * \u5931\u8D25\u53BB\u91CD\u4EE3\u7406\uFF1A\u540C\u4E00\u547D\u4EE4\u6267\u884C\u5468\u671F\u5185\uFF0C`fail` \u53EA\u5411\u7528\u6237\u4E0A\u62A5\u7B2C\u4E00\u6B21\u3002\n *\n * \u80CC\u666F\uFF1Agugle-command \u7684 `CommandNode.parse()` \u5728\u6DF1\u5C42\u5339\u914D\u5931\u8D25\u65F6\u5DF2\u8C03\u7528\u4E00\u6B21\n * `source.fail('Invalid command')`\uFF0C`CommandManager.execute()` \u904D\u5386\u5B8C\u6240\u6709\u547D\u540D\u7A7A\u95F4\u540E\n * \u53C8\u515C\u5E95\u8C03\u7528\u4E00\u6B21\u2014\u2014`/help abc` \u8FD9\u7C7B\u300C\u547D\u4EE4\u5B58\u5728\u4F46\u53C2\u6570\u591A\u4F59\u300D\u7684\u8F93\u5165\u4F1A\u5411\u7528\u6237\u56DE\u590D\u4E24\u6761\n * Invalid command\u3002\u4EE3\u7406\u53EA\u653E\u884C\u7B2C\u4E00\u6B21 fail\uFF08success \u4E0D\u53D7\u5F71\u54CD\uFF09\uFF0C\u4FEE\u590D\u91CD\u590D\u56DE\u590D\u3002\n *\n * \u6CE8\u610F\uFF1AgetUserId/getGroupId/isAdmin \u6309\u5185\u90E8\u6E90\u662F\u5426\u6709\u8BE5\u65B9\u6CD5\u6761\u4EF6\u7ED1\u5B9A\uFF0C\n * \u4FDD\u8BC1 isBotCommandSource \u9E2D\u5B50\u7C7B\u578B\u68C0\u67E5\u5BF9\u4EE3\u7406\u524D\u540E\u7ED3\u679C\u4E00\u81F4\u3002\n */\nclass FailOnceCommandSource implements CommandSource {\n private readonly inner: CommandSource;\n private failed = false;\n\n public getUserId?: () => string;\n public getGroupId?: () => number | undefined;\n public isAdmin?: () => boolean;\n\n public constructor(inner: CommandSource) {\n this.inner = inner;\n if (isBotCommandSource(inner)) {\n this.getUserId = () => inner.getUserId();\n this.getGroupId = () => inner.getGroupId();\n this.isAdmin = () => inner.isAdmin();\n }\n }\n\n public success(message: string): void {\n this.inner.success(message);\n }\n\n public fail(message: string): void {\n if (this.failed) return;\n this.failed = true;\n this.inner.fail(message);\n }\n\n public getName(): string {\n return this.inner.getName();\n }\n\n public hasPermission(permission: string): boolean {\n return this.inner.hasPermission(permission);\n }\n}\n\n/**\n * \u6267\u884C\u547D\u4EE4\u6587\u672C\uFF08\u7ECF FailOnce \u4EE3\u7406\uFF0C\u91CD\u590D\u5931\u8D25\u63D0\u793A\u53EA\u4E0A\u62A5\u4E00\u6B21\uFF09\u3002\n * \u6240\u6709\u547D\u4EE4\u5206\u53D1\u5165\u53E3\uFF08QQ \u7FA4\u3001Discord \u6587\u672C\u3001Discord \u4EA4\u4E92\uFF09\u7EDF\u4E00\u8D70\u8FD9\u91CC\u3002\n */\nexport function executeCommand(manager: CommandManager, source: CommandSource, command: string): void {\n manager.execute(new FailOnceCommandSource(source), command);\n}\n\n/**\n * \u4EE5\u7CBE\u786E token \u5E8F\u5217\u6267\u884C\u547D\u4EE4\uFF08\u4E0D\u7ECF\u5B57\u7B26\u4E32\u5207\u5206\uFF09\u3002\n *\n * \u7528\u9014\uFF1ADiscord \u539F\u751F\u659C\u6760\u547D\u4EE4\u4EA4\u4E92\u2014\u2014\u53C2\u6570\u503C\u7531 Discord \u5BA2\u6237\u7AEF\u6309 option \u7ED9\u51FA\uFF0C\n * \u53EF\u80FD\u5305\u542B\u7A7A\u683C\uFF08\u5982 /wiki \u7684\u67E5\u8BE2\u8BCD\uFF09\uFF0C\u82E5\u62FC\u56DE\u5B57\u7B26\u4E32\u518D\u7ECF gugle-command \u6309\u7A7A\u683C\u5207\u5206\n * \u4F1A\u88AB\u6253\u6563\u3002\u8FD9\u91CC\u628A token \u9006\u5E8F\u540E\u76F4\u63A5\u5582\u7ED9\u5404\u547D\u540D\u7A7A\u95F4\u6839\u8282\u70B9\u7684 parse\uFF1A\n * parse \u9010 token \u5F39\u51FA\u5339\u914D\uFF0C\u5B57\u7B26\u4E32\u53C2\u6570\u4FDD\u6301\u5B8C\u6574\u3002\n *\n * @param tokens \u5B8C\u6574 token \u5E8F\u5217\uFF0C\u5982 ['github', 'bind', 'Gugle'] \u6216 ['wiki', '\u4E0B\u754C \u5408\u91D1']\n */\nexport function executeCommandTokens(manager: CommandManager, source: CommandSource, tokens: string[]): void {\n const wrapped = new FailOnceCommandSource(source);\n for (const root of manager.roots.values()) {\n if (root.parse([...tokens].reverse(), wrapped)) return;\n }\n wrapped.fail('Invalid command');\n}\n", "import { spawn } from 'node:child_process';\nimport type { ChildProcess } from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { configStoreCount, reloadAllConfigStores } from '@/config/manager';\n\n/**\n * \u7BA1\u7406\u5458\u8FD0\u7EF4\u547D\u4EE4\uFF1A/reboot\u3001/reload\u3001/shutdown\u3002\n *\n * \u8BBE\u8BA1\u8981\u70B9\uFF1A\n * - \u4EC5\u7BA1\u7406\u5458\u53EF\u7528\uFF08QQ \u4FA7 = management operators\uFF0CDiscord \u4FA7 = \u670D\u52A1\u5668\u7BA1\u7406\u5458\uFF09\uFF0C\n * \u6743\u9650\u6821\u9A8C\u7531\u8C03\u7528\u65B9\uFF08\u547D\u4EE4\u5B9E\u73B0\uFF09\u5728\u8FDB\u5165\u672C\u6A21\u5757\u524D\u5B8C\u6210\uFF1B\n * - `/reboot` \u91C7\u7528\u300C\u8FDB\u7A0B\u5185\u81EA\u91CD\u542F\u300D\uFF1A\u5148\u62C9\u8D77\u4E00\u4E2A\u8131\u79BB\u7236\u8FDB\u7A0B\u7684\u65B0\u5B9E\u4F8B\uFF0C\u786E\u8BA4\u5176\u5B58\u6D3B\u540E\u518D\u9000\u51FA\u5F53\u524D\u8FDB\u7A0B\u3002\n * \u56E0\u6B64\u4E0D\u4F9D\u8D56\u5916\u90E8\u5B88\u62A4\u8FDB\u7A0B\uFF08\u672C\u4ED3\u5E93\u65E0 pm2/Docker/systemd \u914D\u7F6E\uFF09\uFF1B\n * \u82E5\u5916\u90E8\u540C\u65F6\u6709\u5B88\u62A4\u8FDB\u7A0B\u62C9\u8D77\uFF0C\u4F1A\u77ED\u6682\u51FA\u73B0\u4E24\u4E2A\u5B9E\u4F8B\u62A2\u540C\u4E00 QQ \u53F7\uFF0C\u9700\u907F\u514D\u6DF7\u7528\uFF1B\n * - \u91CD\u542F/\u5173\u505C\u524D\u5148\u53D1\u4E00\u6761\u786E\u8BA4\u6D88\u606F\uFF0C\u5E76\u7B49\u5F85\u5176\u53D1\u9001\u5B8C\u6210\uFF0C\u907F\u514D\u300C\u521A\u56DE\u590D\u5C31\u88AB\u6740\u300D\u5BFC\u81F4\u7528\u6237\u6536\u4E0D\u5230\u56DE\u6267\uFF1B\n * - \u9000\u51FA\u524D\u7559\u4E00\u5C0F\u6BB5\u7F13\u51B2\uFF0C\u4FDD\u8BC1 QQ \u4FA7\u6D88\u606F\u5DF2\u771F\u6B63\u53D1\u51FA\u3001\u4E14\u65E5\u5FD7\u5DF2\u843D\u76D8\u3002\n *\n * \u5173\u4E8E\u542F\u52A8\u53C2\u6570\uFF082026-10-22 \u751F\u4EA7\u4E8B\u6545\u4FEE\u590D\uFF09\uFF1A\n * \u751F\u4EA7\u7528 `tsx src/index.ts` \u8FD0\u884C\uFF0Ctsx \u7684\u52A0\u8F7D\u5668\u53C2\u6570\u4F4D\u4E8E **process.execArgv**\n * \uFF08`--require .../preflight.cjs --import .../loader.mjs`\uFF09\uFF0C\u800C\u672A\u5728 process.argv \u4E2D\u3002\n * \u65E9\u671F\u5B9E\u73B0\u53EA\u8F6C\u53D1 process.argv\uFF0C\u5BFC\u81F4\u65B0\u8FDB\u7A0B\u53D8\u6210\u88F8 `node /data/src/index.ts`\uFF0C\n * `@/` \u8DEF\u5F84\u522B\u540D\u65E0\u6CD5\u89E3\u6790\uFF08ERR_MODULE_NOT_FOUND: Cannot find package '@/config'\uFF09\uFF0C\n * \u4E14\u7236\u8FDB\u7A0B\u65E0\u6761\u4EF6\u9000\u51FA \u2192 \u673A\u5668\u4EBA\u76F4\u63A5\u79BB\u7EBF\u3002\u73B0\u5728\uFF1A\n * 1. buildRestartCommand() \u91CD\u5EFA\u547D\u4EE4\u65F6\u5FC5\u987B\u5E26\u4E0A execArgv\uFF1B\n * 2. restartApplication() \u5728\u9000\u51FA\u7236\u8FDB\u7A0B\u524D\u505A\u5065\u5EB7\u68C0\u67E5\uFF0C\u5B50\u8FDB\u7A0B\u79D2\u9000\u5219\u56DE\u6EDA\uFF08\u7236\u8FDB\u7A0B\u7EE7\u7EED\u8FD0\u884C\uFF09\uFF1B\n * 3. \u5B50\u8FDB\u7A0B\u901A\u8FC7 QQBOT_START_DELAY_MS \u5EF6\u8FDF\u542F\u52A8\uFF0C\u907F\u514D\u4E0E\u65E7\u8FDB\u7A0B\u7684 QQ \u8FDE\u63A5\u91CD\u53E0\u4E92\u8E22\u3002\n *\n * \u73AF\u5883\u53D8\u91CF\u9003\u751F\u53E3\uFF1A\u82E5\u90E8\u7F72\u7531\u5916\u90E8\u6258\u7BA1\uFF08\u5BB9\u5668 restart policy / pm2 / systemd / shell \u811A\u672C\uFF09\uFF0C\n * \u53EF\u7528 QQBOT_RESTART_COMMAND \u6307\u5B9A\u91CD\u542F\u547D\u4EE4\uFF08shell \u6267\u884C\uFF09\uFF0C\u4F8B\u5982 `pm2 restart gugle-qq-bot`\uFF1B\n * \u672A\u8BBE\u7F6E\u65F6\u6309 buildRestartCommand() \u91CD\u5EFA argv\uFF08\u9ED8\u8BA4\u8DEF\u5F84\uFF0C\u5DF2\u5E26 tsx \u52A0\u8F7D\u5668\uFF09\u3002\n */\n\n/** \u91CD\u542F/\u5173\u505C\u524D\u7684\u7F13\u51B2\u65F6\u95F4\uFF1A\u7ED9\u53D1\u9001\u961F\u5217\u7559\u51FA\u53D1\u9001 + \u65E5\u5FD7\u5237\u76D8\u7684\u65F6\u95F4 */\nconst SHUTDOWN_GRACE_MS = 800;\n\n/** \u91CD\u542F\u65F6\u7528\u4E8E\u6807\u8BB0\u300C\u672C\u8FDB\u7A0B\u7531 /reboot \u62C9\u8D77\u300D\uFF0C\u907F\u514D\u65B0\u8FDB\u7A0B\u7EE7\u627F\u540C\u540D\u73AF\u5883\u53D8\u91CF\u9020\u6210\u8BEF\u5224 */\nconst RESTART_ENV_FLAG = 'QQBOT_RESTARTED_BY_COMMAND';\n\n/**\n * \u81EA\u5B9A\u4E49\u91CD\u542F\u547D\u4EE4\u7684\u73AF\u5883\u53D8\u91CF\u540D\uFF08\u9003\u751F\u53E3\uFF09\u3002\n *\n * \u7528\u6CD5\uFF1A\u67D0\u4E9B\u90E8\u7F72\u7531\u5916\u90E8\u6258\u7BA1\uFF08\u5BB9\u5668 restart policy\u3001pm2\u3001systemd\u3001\u4E00\u6BB5 shell \u811A\u672C\uFF09\uFF0C\n * \u76F4\u63A5\u91CD\u5EFA argv \u53EF\u80FD\u4E0E\u5BBF\u4E3B\u62C9\u8D77\u65B9\u5F0F\u4E0D\u4E00\u81F4\u3002\u8BBE\u7F6E\u8BE5\u53D8\u91CF\u540E\u6539\u4E3A\u7528 shell \u6267\u884C\u6B64\u547D\u4EE4\u4E32\uFF0C\u4F8B\u5982\uFF1A\n * ```\n * QQBOT_RESTART_COMMAND=\"pm2 restart gugle-qq-bot\"\n * QQBOT_RESTART_COMMAND=\"docker restart qq_bot\"\n * ```\n * \u672A\u8BBE\u7F6E\u65F6\u8D70 buildRestartCommand() \u91CD\u5EFA argv\uFF08\u9ED8\u8BA4\u8DEF\u5F84\uFF09\u3002\n */\nconst RESTART_COMMAND_ENV = 'QQBOT_RESTART_COMMAND';\n\n/** \u662F\u5426\u7528\u4E86\u81EA\u5B9A\u4E49\u91CD\u542F\u547D\u4EE4\uFF08\u4F9B\u65E5\u5FD7/\u56DE\u6267\u8BF4\u660E\uFF09 */\nexport function customRestartCommand(): string | undefined {\n const raw = process.env[RESTART_COMMAND_ENV];\n const trimmed = raw?.trim();\n return trimmed ? trimmed : undefined;\n}\n\n/** \u5B50\u8FDB\u7A0B\u5EF6\u8FDF\u542F\u52A8\u65F6\u95F4\u7684\u73AF\u5883\u53D8\u91CF\u540D\uFF08\u907F\u514D\u65B0\u65E7\u8FDB\u7A0B QQ \u8FDE\u63A5\u91CD\u53E0\uFF09 */\nconst START_DELAY_ENV = 'QQBOT_START_DELAY_MS';\n\n/** \u5B50\u8FDB\u7A0B\u5EF6\u8FDF\u542F\u52A8\u9ED8\u8BA4\u503C\uFF08\u6BEB\u79D2\uFF09 */\nconst DEFAULT_START_DELAY_MS = 2500;\n\n/** \u65B0\u8FDB\u7A0B\u5B58\u6D3B\u89C2\u5BDF\u7A97\u53E3\u9ED8\u8BA4\u503C\uFF08\u6BEB\u79D2\uFF09 */\nconst DEFAULT_HEALTH_CHECK_MS = 2000;\n\n/** \u5065\u5EB7\u68C0\u67E5\u901A\u8FC7\u540E\uFF0C\u7236\u8FDB\u7A0B\u9000\u51FA\u524D\u7684\u5EF6\u8FDF\uFF08\u6BEB\u79D2\uFF09 */\nconst DEFAULT_EXIT_DELAY_MS = 300;\n\n/** \u5B50\u8FDB\u7A0B\u65E5\u5FD7\u4FDD\u7559\u7684\u6587\u4EF6\u540D\u524D\u7F00 */\nconst CHILD_LOG_PREFIX = 'reboot-child';\n\n/** \u8BFB\u65E5\u5FD7\u5C3E\u90E8\u7684\u6700\u5927\u5B57\u8282\u6570 */\nconst CHILD_LOG_TAIL_BYTES = 4096;\n\n/** \u91CD\u542F\u8FDB\u884C\u4E2D\u6807\u8BB0\uFF1A\u62D2\u7EDD\u5E76\u53D1 /reboot */\nlet restartInProgress = false;\n\nexport interface ReloadReport {\n /** \u4ECE\u78C1\u76D8\u91CD\u8F7D\u7684 ConfigStore \u6570\u91CF */\n configStores: number;\n /** \u662F\u5426\u6210\u529F\u91CD\u8F7D\u4E8B\u4EF6\u6570\u636E\uFF08ban_list \u7B49\uFF09 */\n eventData: boolean;\n /** \u662F\u5426\u6210\u529F\u91CD\u8F7D GitHub \u7ED1\u5B9A\u6570\u636E */\n githubBindings: boolean;\n /** \u989D\u5916\u91CD\u8F7D\u7684\u6A21\u5757\uFF08\u6309\u9700\u7531\u8C03\u7528\u65B9\u8FFD\u52A0\u8BF4\u660E\uFF09 */\n extras: string[];\n}\n\n/**\n * \u91CD\u8F7D\u914D\u7F6E\u4E0E\u6570\u636E\u6587\u4EF6\uFF08/reload\uFF09\u3002\n *\n * \u8986\u76D6\u8303\u56F4\uFF1A\n * - \u5168\u90E8 ConfigStore\uFF08features/*.json\u3001hermes.json\u3001discord-bridge.json \u7B49\uFF09\u2192 \u78C1\u76D8\u91CD\u8BFB\uFF1B\n * - \u4E8B\u4EF6\u6570\u636E\uFF08configs/event-data.json\uFF0C\u542B ban_list \u4E0E\u7248\u672C\u8DDF\u8E2A\u8BB0\u5F55\uFF09\uFF1B\n * - GitHub \u7ED1\u5B9A\u6570\u636E\uFF08configs/github-bind.json\uFF09\uFF1B\n * - \u7531\u8C03\u7528\u65B9\u4F20\u5165\u7684\u989D\u5916\u91CD\u8F7D\u52A8\u4F5C\uFF08\u5982 PeakValleyTimer \u8FD9\u7C7B\u6784\u9020\u671F\u8BFB\u914D\u7F6E\u7684\u6A21\u5757\uFF09\u3002\n *\n * \u4E0D\u8986\u76D6\uFF1Abot-config.json\uFF08\u8FDB\u7A0B\u542F\u52A8\u65F6\u8BFB\u53D6\uFF0C\u6539\u7AEF\u53E3/token \u9700\u91CD\u542F\uFF09\u3002\n */\nexport async function reloadRuntime(options: {\n eventData?: () => Promise<void>;\n githubBindings?: () => void;\n extra?: Array<{ name: string; run: () => void }>;\n}): Promise<ReloadReport> {\n const extras: string[] = [];\n const configStores = reloadAllConfigStores();\n\n let eventData = false;\n try {\n await options.eventData?.();\n eventData = options.eventData !== undefined;\n } catch {\n eventData = false;\n }\n\n let githubBindings = false;\n try {\n options.githubBindings?.();\n githubBindings = options.githubBindings !== undefined;\n } catch {\n githubBindings = false;\n }\n\n for (const item of options.extra ?? []) {\n try {\n item.run();\n extras.push(item.name);\n } catch {\n // \u5355\u4E2A\u6A21\u5757\u91CD\u8F7D\u5931\u8D25\u4E0D\u5F71\u54CD\u5176\u4F59\u90E8\u5206\n }\n }\n\n return { configStores, eventData, githubBindings, extras };\n}\n\n/** \u5DF2\u767B\u8BB0\u7684\u914D\u7F6E\u4ED3\u5E93\u6570\u91CF\uFF08\u7528\u4E8E /reload \u56DE\u6267\u5C55\u793A\uFF09 */\nexport function registeredConfigStoreCount(): number {\n return configStoreCount();\n}\n\n/** \u91CD\u5EFA\u51FA\u7684\u542F\u52A8\u547D\u4EE4 */\nexport interface RestartCommand {\n /** \u53EF\u6267\u884C\u6587\u4EF6\uFF08\u901A\u5E38\u662F node\uFF09 */\n command: string;\n /** \u4F20\u7ED9\u53EF\u6267\u884C\u6587\u4EF6\u7684\u53C2\u6570\uFF08\u542B tsx \u52A0\u8F7D\u5668\u7B49 node \u9009\u9879\uFF09 */\n args: string[];\n}\n\n/** \u9700\u8981\u4ECE execArgv \u4E2D\u5254\u9664\u7684\u300C\u4E00\u6B21\u6027\u7684 eval \u6A21\u5F0F\u300D\u9009\u9879\uFF1A\u4E0D\u80FD\u4F20\u9012\u7ED9\u5B50\u8FDB\u7A0B */\nconst EVAL_ARG_FLAGS = new Set(['--eval', '-e', '--print', '-p']);\n/** \u8FD9\u4E9B\u9009\u9879\u5E26\u503C\uFF0C\u82E5\u4EE5 `=` \u5F62\u5F0F\u51FA\u73B0\u4E5F\u8981\u8BC6\u522B\uFF08\u5982 --eval=xxx\uFF09 */\nconst EVAL_ARG_PREFIXES = ['--eval=', '-e=', '--print=', '-p='];\n\n/**\n * \u8FC7\u6EE4 execArgv \u4E2D\u4E0E\u300C\u5F53\u524D\u8FDB\u7A0B\u4EE5 eval \u6A21\u5F0F\u8FD0\u884C\u300D\u76F8\u5173\u7684\u53C2\u6570\u3002\n * \u8FD9\u7C7B\u53C2\u6570\uFF08`-e` / `--eval` / `-p` / `--print`\uFF09\u4E0D\u53EF\u4F20\u9012\u7ED9\u5B50\u8FDB\u7A0B\uFF0C\n * \u5426\u5219\u5B50\u8FDB\u7A0B\u4F1A\u628A\u5165\u53E3\u8DEF\u5F84\u5F53\u6210 eval \u7684\u8868\u8FBE\u5F0F\u3002\n */\nfunction filterEvalArgs(execArgv: string[]): string[] {\n const result: string[] = [];\n for (let index = 0; index < execArgv.length; index++) {\n const arg = execArgv[index]!;\n if (EVAL_ARG_FLAGS.has(arg)) {\n // \u8DF3\u8FC7\u8BE5 flag \u53CA\u5176\u503C\n index++;\n continue;\n }\n if (EVAL_ARG_PREFIXES.some(prefix => arg.startsWith(prefix))) continue;\n result.push(arg);\n }\n return result;\n}\n\n/**\n * \u91CD\u5EFA\u300C\u91CD\u542F\u81EA\u8EAB\u300D\u6240\u9700\u7684\u542F\u52A8\u547D\u4EE4\uFF08\u7EAF\u51FD\u6570\uFF0C\u4FBF\u4E8E\u6D4B\u8BD5\uFF09\u3002\n *\n * \u5173\u952E\u70B9\uFF1A\u5FC5\u987B\u4FDD\u7559 process.execArgv \u2014\u2014 \u751F\u4EA7\u7528 tsx \u8FD0\u884C TS \u5165\u53E3\u65F6\uFF0C\n * tsx \u7684\u52A0\u8F7D\u5668\uFF08`--require preflight.cjs` / `--import loader.mjs`\uFF09\u53EA\u5B58\u5728\u4E8E\n * execArgv\uFF1B\u9057\u6F0F\u4F1A\u5BFC\u81F4\u5B50\u8FDB\u7A0B\u4EE5\u88F8 node \u6267\u884C .ts \u6587\u4EF6\u3001`@/` \u522B\u540D\u89E3\u6790\u5931\u8D25\u3002\n *\n * @param execPath \u5F53\u524D node \u53EF\u6267\u884C\u6587\u4EF6\u8DEF\u5F84\uFF08\u9ED8\u8BA4 process.execPath\uFF09\n * @param execArgv \u5F53\u524D node \u9009\u9879\uFF08\u9ED8\u8BA4 process.execArgv\uFF09\n * @param argv \u5F53\u524D\u8FDB\u7A0B\u53C2\u6570\uFF08\u9ED8\u8BA4 process.argv\uFF1B\u7B2C 0 \u9879\u4E3A\u53EF\u6267\u884C\u6587\u4EF6\u3001\u7B2C 1 \u9879\u4E3A\u5165\u53E3\uFF09\n * @returns \u53EF\u76F4\u63A5\u4EA4\u7ED9 spawn \u7684\u547D\u4EE4\u4E0E\u53C2\u6570\n */\nexport function buildRestartCommand(\n execPath: string = process.execPath,\n execArgv: string[] = process.execArgv,\n argv: string[] = process.argv\n): RestartCommand {\n const scriptArgs = argv.slice(1);\n if (scriptArgs.length === 0) throw new Error('\u65E0\u6CD5\u83B7\u53D6\u5F53\u524D\u542F\u52A8\u547D\u4EE4\uFF0C\u6682\u4E0D\u80FD\u81EA\u91CD\u542F');\n return {\n command: execPath,\n args: [...filterEvalArgs(execArgv), ...scriptArgs]\n };\n}\n\n/** \u91CD\u542F\u7ED3\u679C */\nexport interface RestartResult {\n /** \u65B0\u8FDB\u7A0B pid */\n pid: number;\n /** \u5065\u5EB7\u68C0\u67E5\u662F\u5426\u901A\u8FC7\uFF08false \u8868\u793A\u5B50\u8FDB\u7A0B\u5728\u89C2\u5BDF\u7A97\u53E3\u5185\u9000\u51FA\uFF0C\u7236\u8FDB\u7A0B\u672A\u9000\u51FA\uFF09 */\n alive: boolean;\n /** \u5B50\u8FDB\u7A0B\u65E5\u5FD7\u5C3E\u90E8\uFF08\u5931\u8D25\u65F6\u7528\u4E8E\u56DE\u62A5\u539F\u56E0\uFF09 */\n logTail?: string;\n}\n\n/** restartApplication \u7684\u53EF\u6CE8\u5165\u9009\u9879\uFF08\u6D4B\u8BD5\u7528\uFF09 */\nexport interface RestartOptions {\n /** \u81EA\u5B9A\u4E49\u542F\u52A8\u547D\u4EE4\uFF1B\u5B57\u7B26\u4E32\u8868\u793A\u8D70 shell\uFF0C\u672A\u6307\u5B9A\u65F6\u6309 buildRestartCommand \u91CD\u5EFA */\n restartCommand?: RestartCommand | { shell: string };\n /** \u5B50\u8FDB\u7A0B\u5B58\u6D3B\u89C2\u5BDF\u7A97\u53E3\uFF08\u6BEB\u79D2\uFF09 */\n healthCheckMs?: number;\n /** \u5065\u5EB7\u68C0\u67E5\u901A\u8FC7\u540E\u7236\u8FDB\u7A0B\u9000\u51FA\u5EF6\u8FDF\uFF08\u6BEB\u79D2\uFF09 */\n exitDelayMs?: number;\n /** \u5B50\u8FDB\u7A0B\u542F\u52A8\u5EF6\u8FDF\uFF08\u6BEB\u79D2\uFF09\uFF0C\u901A\u8FC7 env \u4F20\u7ED9\u5B50\u8FDB\u7A0B */\n startDelayMs?: number;\n /** \u6CE8\u5165\u9000\u51FA\u51FD\u6570\uFF08\u9ED8\u8BA4 process.exit\uFF09 */\n exit?: (code: number) => void;\n /** \u6CE8\u5165 spawn\uFF08\u9ED8\u8BA4 node:child_process.spawn\uFF09 */\n spawnImpl?: typeof spawn;\n /** \u6CE8\u5165\u65E5\u5FD7\u76EE\u5F55\uFF08\u9ED8\u8BA4 <cwd>/logs\uFF09 */\n logDir?: string;\n /** \u5B50\u8FDB\u7A0B\u5DE5\u4F5C\u76EE\u5F55\uFF08\u9ED8\u8BA4 process.cwd()\uFF1Btsx \u4F9D\u8D56 cwd \u4E0B\u7684 tsconfig \u89E3\u6790 @/ \u522B\u540D\uFF09 */\n cwd?: string;\n}\n\n/** \u91CD\u542F\u8FDB\u884C\u4E2D\uFF08\u62D2\u7EDD\u5E76\u53D1\u89E6\u53D1\uFF09 */\nexport function isRestartInProgress(): boolean {\n return restartInProgress;\n}\n\n/** \u8BFB\u53D6\u6587\u4EF6\u5C3E\u90E8\u6587\u672C\uFF08\u5931\u8D25\u8FD4\u56DE\u7A7A\u4E32\uFF09 */\nfunction readTail(filePath: string, maxBytes: number = CHILD_LOG_TAIL_BYTES): string {\n try {\n const stat = fs.statSync(filePath);\n const start = Math.max(0, stat.size - maxBytes);\n const fd = fs.openSync(filePath, 'r');\n try {\n const buffer = Buffer.alloc(stat.size - start);\n fs.readSync(fd, buffer, 0, buffer.length, start);\n return buffer.toString('utf-8').trim();\n } finally {\n fs.closeSync(fd);\n }\n } catch {\n return '';\n }\n}\n\n/**\n * \u8FDB\u7A0B\u5185\u81EA\u91CD\u542F\uFF1A\u62C9\u8D77\u65B0\u5B9E\u4F8B \u2192 \u5065\u5EB7\u68C0\u67E5 \u2192 \u901A\u8FC7\u540E\u624D\u9000\u51FA\u5F53\u524D\u8FDB\u7A0B\u3002\n *\n * \u5931\u8D25\u8BED\u4E49\uFF08\u91CD\u8981\uFF09\uFF1A\u53EA\u8981\u5B50\u8FDB\u7A0B\u5728\u89C2\u5BDF\u7A97\u53E3\u5185\u9000\u51FA / spawn \u629B\u9519\uFF0C\u5C31**\u4E0D\u9000\u51FA\u7236\u8FDB\u7A0B**\uFF0C\n * \u8FD4\u56DE alive=false \u4E0E\u65E5\u5FD7\u5C3E\u90E8\uFF0C\u7531\u8C03\u7528\u65B9\u56DE\u62A5\u7BA1\u7406\u5458\u3002\u8FD9\u6837\u5355\u6B21\u91CD\u542F\u5931\u8D25\u4E0D\u4F1A\u8BA9\u673A\u5668\u4EBA\u79BB\u7EBF\u3002\n *\n * @returns \u65B0\u8FDB\u7A0B pid \u4E0E\u5065\u5EB7\u68C0\u67E5\u7ED3\u679C\n */\nexport async function restartApplication(options: RestartOptions = {}): Promise<RestartResult> {\n if (restartInProgress) throw new Error('\u5DF2\u6709\u91CD\u542F\u6B63\u5728\u8FDB\u884C\u4E2D\uFF0C\u8BF7\u7A0D\u5019');\n restartInProgress = true;\n\n const {\n restartCommand: explicitRestartCommand,\n healthCheckMs = DEFAULT_HEALTH_CHECK_MS,\n exitDelayMs = DEFAULT_EXIT_DELAY_MS,\n startDelayMs = DEFAULT_START_DELAY_MS,\n exit = (code: number) => process.exit(code),\n spawnImpl = spawn,\n logDir = path.resolve(process.cwd(), 'logs'),\n cwd = process.cwd()\n } = options;\n\n // \u4F18\u5148\u7EA7\uFF1A\u663E\u5F0F\u4F20\u5165 > QQBOT_RESTART_COMMAND \u73AF\u5883\u53D8\u91CF > \u6309 execArgv \u91CD\u5EFA\n const shellCommand = customRestartCommand();\n const restartCommand: RestartCommand | { shell: string } | undefined =\n explicitRestartCommand ?? (shellCommand !== undefined ? { shell: shellCommand } : undefined);\n\n // \u5B50\u8FDB\u7A0B\u65E5\u5FD7\uFF1Adetached + inherit \u65F6\u7236\u8FDB\u7A0B\u9000\u51FA\u4F1A\u5173\u95ED\u7BA1\u9053\uFF0C\u5B50\u8FDB\u7A0B\u540E\u7EED\u5199\u5165\u53EF\u80FD EPIPE\uFF0C\n // \u56E0\u6B64\u6539\u4E3A\u5199\u5165\u72EC\u7ACB\u65E5\u5FD7\u6587\u4EF6\uFF0C\u540C\u65F6\u4FDD\u8BC1\u542F\u52A8\u5931\u8D25\u7684\u539F\u56E0\u53EF\u67E5\u3002\n let logPath = '';\n let logFd: number | undefined;\n try {\n fs.mkdirSync(logDir, { recursive: true });\n logPath = path.join(logDir, `${CHILD_LOG_PREFIX}-${new Date().toISOString().replace(/[:.]/g, '-')}.log`);\n logFd = fs.openSync(logPath, 'a');\n } catch {\n logFd = undefined; // \u65E5\u5FD7\u76EE\u5F55\u4E0D\u53EF\u5199\u65F6\u9000\u5316\u4E3A\u5FFD\u7565\u8F93\u51FA\uFF0C\u5065\u5EB7\u68C0\u67E5\u4ECD\u751F\u6548\n }\n\n const env: NodeJS.ProcessEnv = {\n ...process.env,\n [RESTART_ENV_FLAG]: '1',\n [START_DELAY_ENV]: String(Math.max(0, startDelayMs))\n };\n\n /** \u6E05\u7406\u65E5\u5FD7 fd */\n const closeLog = (): void => {\n if (logFd !== undefined) {\n try {\n fs.closeSync(logFd);\n } catch {\n // \u5FFD\u7565\n }\n logFd = undefined;\n }\n };\n\n let child: ChildProcess;\n try {\n const stdio: Array<'ignore' | number> =\n logFd !== undefined ? ['ignore', logFd, logFd] : ['ignore', 'ignore', 'ignore'];\n if (typeof restartCommand === 'string') {\n child = spawnImpl(restartCommand, { cwd, env, detached: true, stdio, shell: true } as never);\n } else if (restartCommand !== undefined && 'shell' in restartCommand) {\n child = spawnImpl(restartCommand.shell, { cwd, env, detached: true, stdio, shell: true } as never);\n } else {\n const command = restartCommand ?? buildRestartCommand();\n child = spawnImpl(command.command, command.args, {\n cwd,\n env,\n detached: true,\n stdio\n });\n }\n } catch (error) {\n closeLog();\n restartInProgress = false;\n throw error;\n }\n\n const pid = child.pid;\n if (pid === undefined) {\n closeLog();\n restartInProgress = false;\n throw new Error('\u65B0\u8FDB\u7A0B\u542F\u52A8\u5931\u8D25\uFF08\u672A\u83B7\u5F97 pid\uFF09\uFF0C\u5DF2\u53D6\u6D88\u91CD\u542F');\n }\n\n // \u2500\u2500 \u5065\u5EB7\u68C0\u67E5\uFF1A\u89C2\u5BDF\u7A97\u53E3\u5185\u9000\u51FA = \u542F\u52A8\u5931\u8D25 \u2500\u2500\n const alive = await new Promise<boolean>(resolve => {\n let settled = false;\n const finish = (value: boolean): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n child.removeListener('exit', onExit);\n child.removeListener('error', onError);\n resolve(value);\n };\n const onExit = (): void => finish(false);\n const onError = (): void => finish(false);\n const timer = setTimeout(() => finish(true), Math.max(0, healthCheckMs));\n child.once('exit', onExit);\n child.once('error', onError);\n });\n\n if (!alive) {\n const logTail = logPath ? readTail(logPath) : '';\n closeLog();\n restartInProgress = false;\n return { pid, alive: false, ...(logTail ? { logTail } : {}) };\n }\n\n // \u5B58\u6D3B\uFF1A\u8131\u79BB\u7236\u8FDB\u7A0B\u540E\u5B89\u6392\u9000\u51FA\uFF08\u5173\u95ED\u65E5\u5FD7 fd \u4EA4\u7ED9\u5B50\u8FDB\u7A0B\u7EE7\u7EED\u6301\u6709\u5199\u5165\uFF09\n try {\n child.unref();\n } catch {\n // \u5FFD\u7565\n }\n setTimeout(() => {\n // \u751F\u4EA7\u8BED\u4E49\uFF1A\u8FDB\u7A0B\u5373\u5C06\u9000\u51FA\uFF0C\u6807\u8BB0\u65E0\u9700\u91CA\u653E\uFF1B\u4F46\u82E5 exit \u88AB\u6CE8\u5165\uFF08\u6D4B\u8BD5/\u81EA\u5B9A\u4E49\u9000\u51FA\uFF09\uFF0C\n // \u8FDB\u7A0B\u4E0D\u4F1A\u771F\u7684\u9000\u51FA\uFF0C\u5FC5\u987B\u91CA\u653E\u6807\u8BB0\uFF0C\u5426\u5219\u540E\u7EED /reboot \u4F1A\u88AB\u6C38\u4E45\u62D2\u7EDD\u3002\n restartInProgress = false;\n exit(0);\n }, Math.max(0, exitDelayMs));\n // \u6CE8\u610F\uFF1A\u4E0D\u5173\u95ED logFd \u2014\u2014 \u5B50\u8FDB\u7A0B\u4ECD\u5728\u5199\u540C\u4E00\u4E2A fd\n return { pid, alive: true };\n}\n\n/**\n * \u5173\u95ED\u5E94\u7528\uFF1A\u4F18\u96C5\u505C\u670D\u540E\u9000\u51FA\u8FDB\u7A0B\u3002\n *\n * @param stopBot \u505C\u6B62\u673A\u5668\u4EBA\uFF08\u65AD\u5F00 WebSocket\u3001\u89E6\u53D1 before-stop/after-stop\uFF09\n * @param delayMs \u9000\u51FA\u524D\u7684\u7F13\u51B2\u65F6\u95F4\n */\nexport function shutdownApplication(stopBot: () => void, delayMs: number = SHUTDOWN_GRACE_MS): void {\n try {\n stopBot();\n } finally {\n // \u65E0\u8BBA\u505C\u670D\u662F\u5426\u629B\u9519\u90FD\u8981\u9000\u51FA\uFF0C\u5426\u5219 /shutdown \u5F62\u540C\u865A\u8BBE\n setTimeout(() => process.exit(0), delayMs);\n }\n}\n\n/** \u672C\u8FDB\u7A0B\u662F\u5426\u7531 /reboot \u62C9\u8D77\uFF08\u7528\u4E8E\u542F\u52A8\u65E5\u5FD7\u6807\u6CE8\uFF09 */\nexport function isRestartedByCommand(): boolean {\n return process.env[RESTART_ENV_FLAG] === '1';\n}\n\n/**\n * \u672C\u8FDB\u7A0B\u88AB /reboot \u62C9\u8D77\u65F6\u8981\u6C42\u7684\u542F\u52A8\u5EF6\u8FDF\uFF08\u6BEB\u79D2\uFF09\u3002\n * \u7528\u4E8E\u907F\u514D\u65B0\u65E7\u8FDB\u7A0B\u540C\u65F6\u8FDE\u63A5 NapCat \u9020\u6210\u540C\u53F7\u4E92\u8E22\uFF1B\u975E\u91CD\u542F\u542F\u52A8\u8FD4\u56DE 0\u3002\n */\nexport function restartStartDelayMs(): number {\n if (!isRestartedByCommand()) return 0;\n const raw = process.env[START_DELAY_ENV];\n if (!raw) return 0;\n const value = Number(raw);\n return Number.isFinite(value) && value > 0 ? value : 0;\n}\n", "import MarkdownIt from 'markdown-it';\n// @ts-ignore markdown-it-emoji \u672A\u63D0\u4F9B\u9ED8\u8BA4\u5BFC\u51FA\u7C7B\u578B\u58F0\u660E\uFF08full \u9884\u8BBE\uFF0C\u540C\u65F6\u652F\u6301 :name: \u4E0E Unicode emoji\uFF09\nimport emoji from 'markdown-it-emoji';\nimport { Message } from 'discord.js';\nimport { Template, tryGenerateImage } from '@/image';\n\n/**\n * Discord markdown \u5DE5\u5177\uFF1A\n * - isDiscordMarkdown\uFF1A\u542F\u53D1\u5F0F\u5224\u65AD\u4E00\u6BB5\u6587\u672C\u662F\u5426\u4F7F\u7528\u4E86 Discord markdown \u8BED\u6CD5\n * - escapeDiscord\uFF1A\u53D1\u9001\u7EAF\u6587\u672C\u524D\u8F6C\u4E49 markdown \u7279\u6B8A\u5B57\u7B26\uFF0C\u907F\u514D\u88AB Discord \u8BEF\u6E32\u67D3\n * - renderDiscordMessageToImage\uFF1A\u628A Discord \u6D88\u606F\u5185\u5BB9\u6E32\u67D3\u4E3A\u56FE\u7247\uFF08base64\uFF09\uFF0C\u8F6C\u53D1\u5230 QQ \u7528\n */\n\n/** \u542F\u53D1\u5F0F\u5224\u5B9A\u6587\u672C\u662F\u5426\u5305\u542B Discord markdown \u8BED\u6CD5 */\nconst DISCORD_MARKDOWN_PATTERNS: RegExp[] = [\n /\\*\\*[^*\\n]+\\*\\*/, // **\u7C97\u4F53**\n /(^|[\\s\uFF08(>])\\*[^*\\s][^*\\n]*\\*(?![*\\w])/, // *\u659C\u4F53*\uFF08\u907F\u514D\u628A a*b*c \u8BEF\u5224\u4E3A markdown\uFF09\n /__[^_\\n]+__/, // __\u4E0B\u5212\u7EBF__\uFF08CommonMark \u4F1A\u6E32\u67D3\u4E3A\u7C97\u4F53\uFF0C\u5C5E\u5DF2\u77E5\u504F\u5DEE\uFF09\n /(^|[\\s\uFF08(>])_[^_\\s][^_\\n]*_(?![_\\w])/, // _\u659C\u4F53_\n /~~[^~\\n]+~~/, // ~~\u5220\u9664\u7EBF~~\n /\\|\\|[^|\\n]+\\|\\|/, // ||\u5267\u900F||\n /```[\\s\\S]*?```/, // \u4EE3\u7801\u5757\n /`[^`\\n]+`/, // \u884C\u5185\u4EE3\u7801\n /^#{1,3}\\s+\\S/m, // # \u6807\u9898\uFF08\u884C\u9996\uFF09\n /^>\\s+\\S/m, // > \u5F15\u7528\uFF08\u884C\u9996\uFF09\n /^\\s*[-*+]\\s+\\S/m, // \u65E0\u5E8F\u5217\u8868\n /^\\s*\\d+\\.\\s+\\S/m, // \u6709\u5E8F\u5217\u8868\n /\\[[^\\]\\n]+\\]\\([^)\\n]+\\)/, // [\u6587\u5B57](\u94FE\u63A5)\n /<[@#][!&]?\\d+>/, // <@id> / <@!id> / <#id>\n /<a?:\\w{2,32}:\\d+>/, // <a?:name:id> \u81EA\u5B9A\u4E49\u8868\u60C5\n /^-{3,}\\s*$/m // \u5206\u5272\u7EBF\n];\n\nexport function isDiscordMarkdown(text: string): boolean {\n return DISCORD_MARKDOWN_PATTERNS.some(pattern => pattern.test(text));\n}\n\n/** \u8F6C\u4E49 Discord markdown \u7279\u6B8A\u5B57\u7B26\uFF0C\u4F7F\u6587\u672C\u6309\u539F\u6837\uFF08plain text\uFF09\u663E\u793A */\nexport function escapeDiscord(text: string): string {\n return text.replace(/([\\\\*_~`|>#-])/g, '\\\\$1');\n}\n\n/** HTML \u8F6C\u4E49\uFF08\u6CE8\u5165\u6A21\u677F/\u6E32\u67D3\u524D\u5904\u7406\u7528\u6237\u6587\u672C\uFF09 */\nexport function escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n\nconst md: MarkdownIt = new MarkdownIt({\n html: false,\n linkify: true,\n breaks: true\n}).use(emoji.full ?? emoji);\n\n/**\n * \u628A Discord \u6D88\u606F\u6587\u672C\u6E32\u67D3\u4E3A HTML \u7247\u6BB5\u3002\n * \u5148 HTML \u8F6C\u4E49\uFF0C\u518D\u628A Discord \u7279\u6709\u8BED\u6CD5\uFF08\u63D0\u53CA/\u9891\u9053/\u81EA\u5B9A\u4E49\u8868\u60C5/\u5267\u900F\uFF09\u66FF\u6362\u4E3A\u7B49\u4EF7 HTML\uFF0C\n * \u6700\u540E\u4EA4\u7ED9 markdown-it \u6E32\u67D3\u516C\u5171 markdown \u90E8\u5206\u3002\n */\nexport function renderDiscordMarkdownToHtml(content: string, message?: Message): string {\n let text = escapeHtml(content);\n // \u5267\u900F ||x|| \u2192 \u9ED1\u5757\uFF08markdown-it \u4E0D\u8BA4\u8BC6\u8BE5\u8BED\u6CD5\uFF0C\u5148\u66FF\u6362\u4E3A HTML\uFF09\n text = text.replace(/\\|\\|([\\s\\S]+?)\\|\\|/g, '<span class=\"spoiler\">$1</span>');\n // \u81EA\u5B9A\u4E49\u8868\u60C5 <a?:name:id>\n text = text.replace(/&lt;a?:(\\w{2,32}):\\d+&gt;/g, ':$1:');\n // \u9891\u9053\u63D0\u53CA <#id> \u2192 #\u9891\u9053\u540D\n text = text.replace(/&lt;#(\\d+)&gt;/g, (_match, id: string) => {\n const name = message?.guild?.channels.cache.get(id)?.name;\n return `<span class=\"mention\">#${escapeHtml(name ?? id)}</span>`;\n });\n // \u7528\u6237\u63D0\u53CA <@id> / <@!id> \u2192 @\u6635\u79F0\n text = text.replace(/&lt;@!?(\\d+)&gt;/g, (_match, id: string) => {\n const name =\n message?.mentions.members?.get(id)?.displayName ?? message?.mentions.users.get(id)?.username;\n return `<span class=\"mention\">@${escapeHtml(name ?? id)}</span>`;\n });\n // \u89D2\u8272\u63D0\u53CA <@&id> \u2192 @\u89D2\u8272\u540D\n text = text.replace(/&lt;@&amp;(\\d+)&gt;/g, (_match, id: string) => {\n const name = message?.guild?.roles.cache.get(id)?.name;\n return `<span class=\"mention\">@${escapeHtml(name ?? id)}</span>`;\n });\n return md.render(text);\n}\n\n/**\n * \u628A Discord markdown \u6D88\u606F\u6E32\u67D3\u6210 PNG \u56FE\u7247\uFF08base64\uFF09\u3002\n * header \u4E3A\u56FE\u7247\u9876\u90E8\u6765\u6E90\u884C\uFF08\u3010\u670D\u52A1\u5668|\u9891\u9053\u3011\u6635\u79F0(\u7528\u6237\u540D)\uFF09\uFF0Ccontent \u4E3A\u6D88\u606F\u6B63\u6587\u3002\n * \u590D\u7528 src/image/index.ts \u7684\u5171\u4EAB Chrome \u622A\u56FE\u961F\u5217\u3002\n */\nexport function renderDiscordMessageToImage(header: string, content: string, message?: Message): Promise<string> {\n return new Promise<string>((resolve, reject) => {\n const htmlFragment = renderDiscordMarkdownToHtml(content, message);\n const template = Template.load('message', 'src/features/discord-bridge/template')\n .arg('header', escapeHtml(header))\n .arg('content', htmlFragment);\n const templateFile = template.file();\n template\n .handler()\n .then(html => tryGenerateImage(resolve, reject, html, 640, templateFile))\n .catch(reject);\n });\n}\n", "import { Message as DiscordMessage, PermissionFlagsBits } from 'discord.js';\nimport { BotCommandSource } from '@/command';\nimport { isDiscordOperator } from '@/config/features';\nimport { QQBot } from '@/index';\n\nconst DISCORD_MESSAGE_LIMIT = 2000;\n\n/**\n * Discord \u4FA7\u7BA1\u7406\u5458\u5224\u5B9A\uFF1A\u670D\u52A1\u5668\u62E5\u6709\u8005\u3001\u62E5\u6709\u300C\u7BA1\u7406\u670D\u52A1\u5668\u300D/\u300C\u7BA1\u7406\u5458\u300D\u6743\u9650\u7684\u6210\u5458\uFF0C\n * \u6216 management.json \u91CC `discordOperators` \u767D\u540D\u5355\u4E2D\u7684\u7528\u6237\u3002\n * \u547D\u4EE4\u6E90\uFF08DiscordMsgCommandSource\uFF09\u4E0E Hermes AI\uFF08\u5BA1\u6279/\u6280\u80FD\u95E8\u63A7\uFF09\u5171\u7528\u540C\u4E00\u53E3\u5F84\u3002\n */\nexport function isDiscordAdmin(message: DiscordMessage): boolean {\n if (isDiscordOperator(message.author.id)) return true;\n if (message.guild?.ownerId === message.author.id) return true;\n const permissions = message.member?.permissions;\n if (!permissions) return false;\n return permissions.has(PermissionFlagsBits.Administrator) || permissions.has(PermissionFlagsBits.ManageGuild);\n}\n\n/**\n * Discord \u9891\u9053\u547D\u4EE4\u6E90\uFF1A\u8BA9 gugle-command \u5DF2\u6CE8\u518C\u7684\u5168\u90E8\u547D\u4EE4\u90FD\u80FD\u5728 Discord \u9891\u9053\u4E2D\u6267\u884C\uFF0C\n * \u547D\u4EE4\u56DE\u590D\uFF08success/fail\uFF09\u4EE5 Discord \u56DE\u590D\u7684\u5F62\u5F0F\u53D1\u56DE\u539F\u9891\u9053\u3002\n *\n * \u8EAB\u4EFD\u4E0E\u6743\u9650\u6620\u5C04\uFF1A\n * - getUserId() \u8FD4\u56DE `dc:<Discord\u7528\u6237ID>`\uFF08`dc:` \u524D\u7F00\u4FDD\u8BC1\u4E0E\u7EAF\u6570\u5B57 QQ \u53F7\u4E0D\u51B2\u7A81\uFF0C\n * GitHub \u7ED1\u5B9A\u7B49\u6309\u7528\u6237\u6301\u4E45\u5316\u7684\u6570\u636E\u53EF\u5B89\u5168\u5171\u7528\u540C\u4E00\u5957\u5B58\u50A8\uFF09\uFF1B\n * - getGroupId() \u8FD4\u56DE\u8BE5\u9891\u9053\u6865\u63A5\u914D\u7F6E\u91CC\u7684 QQ \u7FA4\u53F7\uFF08\u8BA2\u9605\u7C7B\u547D\u4EE4\u4F5C\u7528\u4E8E\u5BF9\u5E94 QQ \u7FA4\uFF09\uFF1B\n * - isAdmin() / hasPermission()\uFF1A\u670D\u52A1\u5668\u62E5\u6709\u8005\u89C6\u4E3A owner\uFF082\uFF09\uFF0C\n * \u62E5\u6709\u300C\u7BA1\u7406\u670D\u52A1\u5668\u300D\u6216\u300C\u7BA1\u7406\u5458\u300D\u6743\u9650\u7684\u6210\u5458\u89C6\u4E3A admin\uFF081\uFF09\uFF0C\u5176\u4F59\u4E3A 0\u3002\n */\nexport class DiscordMsgCommandSource implements BotCommandSource {\n private readonly bot: QQBot;\n private readonly message: DiscordMessage;\n private readonly groupId: number | undefined;\n\n public constructor(bot: QQBot, message: DiscordMessage, groupId?: number) {\n this.bot = bot;\n this.message = message;\n this.groupId = groupId;\n }\n\n public getUserId(): string {\n return `dc:${this.message.author.id}`;\n }\n\n public getGroupId(): number | undefined {\n return this.groupId;\n }\n\n public isAdmin(): boolean {\n return isDiscordAdmin(this.message);\n }\n\n public success(message: string): void {\n this.reply(message);\n }\n\n public fail(message: string): void {\n this.reply(message);\n this.bot.logger?.error(`[Discord:${this.message.author.username}] ${message}: ${this.message.content}`);\n }\n\n public getName(): string {\n return this.message.member?.displayName ?? this.message.author.username;\n }\n\n public hasPermission(permission: string): boolean {\n if (!permission) return true;\n const getPermissionLevel = (text: string): number => {\n const level = Number.parseInt(text);\n if (Number.isNaN(level)) {\n return text === 'owner' ? 2 : text === 'admin' ? 1 : 0;\n }\n return level;\n };\n const isOwner = this.message.guild?.ownerId === this.message.author.id;\n const permissionLevel = isOwner ? 2 : this.isAdmin() ? 1 : 0;\n return permissionLevel >= getPermissionLevel(permission);\n }\n\n /** \u547D\u4EE4\u56DE\u590D\u53D1\u5728 Discord \u9891\u9053\uFF08\u56DE\u590D\u539F\u6D88\u606F\uFF0C\u4E0D @ \u4EBA\uFF0C\u8D85\u957F\u622A\u65AD\uFF09 */\n private reply(content: string): void {\n const text =\n content.length <= DISCORD_MESSAGE_LIMIT\n ? content\n : `${content.slice(0, DISCORD_MESSAGE_LIMIT - 16)}\\n\u2026\uFF08\u6D88\u606F\u8FC7\u957F\u5DF2\u622A\u65AD\uFF09`;\n this.message\n .reply({ content: text, allowedMentions: { parse: [], repliedUser: false } })\n .catch(error => {\n this.bot.logger?.error(\n `[DiscordBridge] Discord \u547D\u4EE4\u56DE\u590D\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`\n );\n });\n }\n}\n", "import { ConfigStore } from '@/config/manager';\nimport { isOperator } from '@/config/features';\n\n/**\n * Hermes Agent \u6865\u63A5\u914D\u7F6E\uFF08\u79FB\u690D\u81EA qq-hermes-bridge \u7684 .env \u914D\u7F6E\uFF0CMIT \u534F\u8BAE\uFF0C\u539F\u4F5C\u8005 Amorter\uFF09\u3002\n *\n * \u914D\u7F6E\u6587\u4EF6\uFF1Aconfigs/features/hermes.json\uFF08v1\uFF0CConfigStore \u70ED\u91CD\u8F7D\uFF09\uFF1A\n * ```json\n * {\n * \"version\": 1,\n * \"apiUrl\": \"http://127.0.0.1:8642\",\n * \"apiKey\": \"\",\n * \"botName\": \"\u5C0F\u55B5\",\n * \"groups\": [659356928],\n * \"admins\": [],\n * \"requireMention\": true,\n * \"keywordTriggers\": [\"\u5C0F\u55B5\"],\n * \"replyPerSentence\": true,\n * \"compactMaxChars\": 300,\n * \"compactMaxLines\": 5,\n * \"privateReactionFallbackText\": true,\n * \"privateReceiptText\": \"\uD83E\uDD14 \u6536\u5230\uFF0C\u6B63\u5728\u601D\u8003\u2026\"\n * }\n * ```\n * - `groups`\uFF1A\u542F\u7528 AI \u5BF9\u8BDD\u7684 QQ \u7FA4\uFF08\u7A7A=\u7FA4\u804A\u4E0D\u542F\u7528\uFF09\uFF1B\u4E92\u901A\u7684 Discord \u9891\u9053\u80FD\u5426\u89E6\u53D1 AI\n * \u4E5F\u7531\u5176\u6865\u63A5\u7684 QQ \u7FA4\u662F\u5426\u5728\u6B64\u5217\u8868\u51B3\u5B9A\uFF1B\u79C1\u804A\u59CB\u7EC8\u542F\u7528\uFF08\u53D7\u7528\u6237\u9ED1\u767D\u540D\u5355\u7EA6\u675F\uFF09\uFF1B\n * - `replyPerSentence`\uFF1AQQ \u7FA4\u804A AI \u56DE\u590D\u9010\u53E5\u53D1\u9001\uFF1B\u5355\u6761\u8D85\u8FC7 compactMaxChars \u5B57\u6216\n * compactMaxLines \u884C\u65F6\u6539\u7528\u804A\u5929\u8BB0\u5F55\uFF08\u5408\u5E76\u8F6C\u53D1\uFF09\u53D1\u9001\uFF0C\u4EFB\u4E00\u9879\u586B 0 \u5173\u95ED\u8BE5\u7EF4\u5EA6\uFF1B\n * - `privateReactionFallbackText`\uFF1A\u79C1\u804A\u8D34\u8868\u60C5\u5931\u8D25\u65F6\u6539\u53D1\u6587\u5B57\u56DE\u6267\uFF08NapCat \u5BF9\u79C1\u804A\n * \u6D88\u606F\u901A\u5E38\u4E0D\u652F\u6301 set_msg_emoji_like\uFF09\uFF0CAI \u9996\u6761\u56DE\u590D\u53D1\u51FA\u540E\u81EA\u52A8\u64A4\u56DE\uFF1B\n * - Bot QQ \u53F7\u65E0\u9700\u914D\u7F6E\uFF0C\u53D6 bot.getLoginInfoSync().user_id\uFF08@\u63D0\u53CA\u5224\u5B9A\uFF09\uFF1B\n * - \u7CFB\u7EDF\u63D0\u793A\u8BCD\u4F18\u5148\u7EA7\uFF1Aconfigs/SOUL.md > systemPrompt > \u7A7A\u3002\n */\n\nexport interface HermesBridgeConfig {\n version: number;\n /** Hermes API Server \u5730\u5740 */\n apiUrl: string;\n /** API Key\uFF08Hermes \u914D\u7F6E\u4E86\u8BA4\u8BC1\u65F6\u586B\uFF09 */\n apiKey: string;\n /** Bot \u540D\u79F0\uFF08\u5361\u7247\u843D\u6B3E\u3001\u5408\u5E76\u8F6C\u53D1\u7F72\u540D\u7B49\u5C55\u793A\u7528\uFF09 */\n botName: string;\n /** \u542F\u7528 AI \u5BF9\u8BDD\u7684 QQ \u7FA4\uFF08\u7A7A\u6570\u7EC4=\u7FA4\u804A\u4E0D\u542F\u7528\uFF09 */\n groups: number[];\n /** \u7BA1\u7406\u5458 QQ \u53F7\uFF08\u4E0E management operators \u5408\u5E76\u751F\u6548\uFF09 */\n admins: number[];\n /** \u5141\u8BB8\u79C1\u804A/\u5BF9\u8BDD\u7684\u7528\u6237\uFF08\u7A7A=\u5168\u90E8\u5141\u8BB8\uFF09 */\n allowedUsers: number[];\n /** \u5C4F\u853D\u7684\u7528\u6237 */\n blockedUsers: number[];\n /** \u7FA4\u804A\u4E2D\u662F\u5426\u9700\u8981 @bot \u624D\u89E6\u53D1 */\n requireMention: boolean;\n /** \u5173\u952E\u8BCD\u89E6\u53D1\u5217\u8868\uFF08\u5C0F\u5199\u5339\u914D\uFF09 */\n keywordTriggers: string[];\n /** \u8FDB\u5EA6\u5361\u7247\u53D1\u9001\u6700\u5C0F\u95F4\u9694\uFF08\u79D2\uFF09 */\n progressRateLimitSec: number;\n /** \u662F\u5426\u4EE5\u56FE\u7247\u5F62\u5F0F\u53D1\u9001\u8FDB\u5EA6 */\n progressAsImage: boolean;\n /** \u8FDB\u5EA6\u5361\u7247\u6700\u591A\u663E\u793A\u7684\u5DE5\u5177\u6570\u91CF */\n progressMaxTools: number;\n /** \u5355\u6761\u6D88\u606F\u6700\u5927\u957F\u5EA6\uFF08\u8D85\u51FA\u5207\u5206\u53D1\u9001\uFF09 */\n maxMessageLength: number;\n /** \u662F\u5426\u5C06\u6D88\u606F\u4E2D\u7684\u56FE\u7247\u539F\u56FE\u8F6C\u53D1\u7ED9 Hermes\uFF08\u591A\u6A21\u6001\u8BC6\u522B\uFF09 */\n forwardImages: boolean;\n /** \u662F\u5426\u6309\u53E5\u62C6\u5206 AI \u56DE\u590D\u9010\u6761\u53D1\u9001\uFF08\u5173\u95ED\u5219\u6574\u6BB5\u4E00\u6B21\u53D1\u51FA\uFF09 */\n replyPerSentence: boolean;\n /** \u5355\u6761\u56DE\u590D\u8D85\u8FC7\u8BE5\u5B57\u6570\u6539\u7528\u804A\u5929\u8BB0\u5F55\uFF08\u5408\u5E76\u8F6C\u53D1\uFF09\u53D1\u9001\uFF1B0=\u5173\u95ED\u8BE5\u7EF4\u5EA6 */\n compactMaxChars: number;\n /** \u5355\u6761\u56DE\u590D\u8D85\u8FC7\u8BE5\u884C\u6570\u6539\u7528\u804A\u5929\u8BB0\u5F55\uFF08\u5408\u5E76\u8F6C\u53D1\uFF09\u53D1\u9001\uFF1B0=\u5173\u95ED\u8BE5\u7EF4\u5EA6 */\n compactMaxLines: number;\n /** \u7CFB\u7EDF\u63D0\u793A\u8BCD\uFF08\u88AB configs/SOUL.md \u8986\u76D6\uFF09 */\n systemPrompt: string;\n /** \u4FDD\u7559\u7684\u5386\u53F2\u6D88\u606F\u8F6E\u6570 */\n localHistoryMaxMessages: number;\n /** \u662F\u5426\u542F\u7528\u5BF9\u8BDD\u5386\u53F2\u6301\u4E45\u5316\u5B58\u50A8 */\n persistHistoryEnabled: boolean;\n /** \u6301\u4E45\u5316\u4FDD\u7559\u7684\u6700\u5927\u6D88\u606F\u6570 */\n persistHistoryMax: number;\n /** \u662F\u5426\u542F\u7528\u547D\u4EE4\u5BA1\u6279 */\n approvalEnabled: boolean;\n /** \u5BA1\u6279\u8D85\u65F6\u81EA\u52A8\u62D2\u7EDD\uFF08\u79D2\uFF09\uFF0C0=\u4E0D\u8D85\u65F6 */\n approvalTimeoutSec: number;\n /** \u662F\u5426\u542F\u7528\u300CAI \u5904\u7406\u4E2D\u300D\u8868\u60C5\u56DE\u5E94\uFF08QQ \u8D34\u8868\u60C5 / Discord reaction\uFF0C\u5B8C\u6210\u540E\u6458\u9664\uFF09 */\n reactionEnabled: boolean;\n /** QQ \u8868\u60C5\u56DE\u5E94\u7684 emoji_id\uFF08NapCat set_msg_emoji_like\uFF1B76=\uD83D\uDC4D \u5F3A\uFF09 */\n reactionEmojiQq: string;\n /** Discord \u8868\u60C5\u56DE\u5E94\uFF08unicode emoji\uFF09 */\n reactionEmojiDiscord: string;\n /**\n * \u79C1\u804A\u8D34\u8868\u60C5\u5931\u8D25\u65F6\u6539\u7528\u6587\u5B57\u56DE\u6267\uFF08\u9ED8\u8BA4 true\uFF09\u3002\n * NapCat \u5BF9\u79C1\u804A\u6D88\u606F\u901A\u5E38\u4E0D\u652F\u6301 set_msg_emoji_like\uFF0C\u8D34\u8868\u60C5\u4F1A\u5931\u8D25\uFF1B\n * \u5F00\u542F\u540E\u6539\u53D1\u4E00\u6761\u7B80\u77ED\u63D0\u793A\uFF0C\u8BA9\u7528\u6237\u77E5\u9053 AI \u5DF2\u6536\u5230\uFF08\u7FA4\u804A\u4E0D\u53D7\u5F71\u54CD\uFF09\u3002\n */\n privateReactionFallbackText: boolean;\n /** \u79C1\u804A\u6587\u5B57\u56DE\u6267\u5185\u5BB9\uFF08privateReactionFallbackText \u5F00\u542F\u4E14\u8D34\u8868\u60C5\u5931\u8D25\u65F6\u53D1\u9001\uFF09 */\n privateReceiptText: string;\n}\n\nexport function hermesConfigFactory(): HermesBridgeConfig {\n return {\n version: 1,\n apiUrl: 'http://127.0.0.1:8642',\n apiKey: '',\n botName: '\u5C0F\u55B5',\n groups: [],\n admins: [],\n allowedUsers: [],\n blockedUsers: [],\n requireMention: true,\n keywordTriggers: [],\n progressRateLimitSec: 15,\n progressAsImage: true,\n progressMaxTools: 12,\n maxMessageLength: 1200,\n forwardImages: true,\n replyPerSentence: true,\n compactMaxChars: 300,\n compactMaxLines: 5,\n systemPrompt: '',\n localHistoryMaxMessages: 24,\n persistHistoryEnabled: true,\n persistHistoryMax: 100,\n approvalEnabled: true,\n approvalTimeoutSec: 300,\n reactionEnabled: true,\n reactionEmojiQq: '76',\n reactionEmojiDiscord: '\uD83D\uDC40',\n privateReactionFallbackText: true,\n privateReceiptText: '\uD83E\uDD14 \u6536\u5230\uFF0C\u6B63\u5728\u601D\u8003\u2026'\n };\n}\n\nfunction asString(raw: unknown, fallback: string): string {\n return typeof raw === 'string' ? raw : fallback;\n}\n\nfunction asBool(raw: unknown, fallback: boolean): boolean {\n return typeof raw === 'boolean' ? raw : fallback;\n}\n\nfunction asPositiveNumber(raw: unknown, fallback: number): number {\n return typeof raw === 'number' && Number.isFinite(raw) && raw >= 0 ? raw : fallback;\n}\n\nfunction asQqList(raw: unknown): number[] {\n if (!Array.isArray(raw)) return [];\n return raw.filter((item): item is number => typeof item === 'number' && Number.isSafeInteger(item) && item > 0);\n}\n\nfunction asStringList(raw: unknown): string[] {\n if (!Array.isArray(raw)) return [];\n return raw.filter((item): item is string => typeof item === 'string' && item.length > 0);\n}\n\nexport function normalizeHermesConfig(raw: unknown): HermesBridgeConfig | null {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null;\n const record = raw as Record<string, unknown>;\n const fallback = hermesConfigFactory();\n return {\n version: 1,\n apiUrl: asString(record.apiUrl, fallback.apiUrl),\n apiKey: asString(record.apiKey, fallback.apiKey),\n botName: asString(record.botName, fallback.botName),\n groups: asQqList(record.groups),\n admins: asQqList(record.admins),\n allowedUsers: asQqList(record.allowedUsers),\n blockedUsers: asQqList(record.blockedUsers),\n requireMention: asBool(record.requireMention, fallback.requireMention),\n keywordTriggers: asStringList(record.keywordTriggers).map(keyword => keyword.toLowerCase()),\n progressRateLimitSec: asPositiveNumber(record.progressRateLimitSec, fallback.progressRateLimitSec),\n progressAsImage: asBool(record.progressAsImage, fallback.progressAsImage),\n progressMaxTools: asPositiveNumber(record.progressMaxTools, fallback.progressMaxTools),\n maxMessageLength: asPositiveNumber(record.maxMessageLength, fallback.maxMessageLength),\n forwardImages: asBool(record.forwardImages, fallback.forwardImages),\n replyPerSentence: asBool(record.replyPerSentence, fallback.replyPerSentence),\n compactMaxChars: asPositiveNumber(record.compactMaxChars, fallback.compactMaxChars),\n compactMaxLines: asPositiveNumber(record.compactMaxLines, fallback.compactMaxLines),\n systemPrompt: asString(record.systemPrompt, fallback.systemPrompt),\n localHistoryMaxMessages: asPositiveNumber(record.localHistoryMaxMessages, fallback.localHistoryMaxMessages),\n persistHistoryEnabled: asBool(record.persistHistoryEnabled, fallback.persistHistoryEnabled),\n persistHistoryMax: asPositiveNumber(record.persistHistoryMax, fallback.persistHistoryMax),\n approvalEnabled: asBool(record.approvalEnabled, fallback.approvalEnabled),\n approvalTimeoutSec: asPositiveNumber(record.approvalTimeoutSec, fallback.approvalTimeoutSec),\n reactionEnabled: asBool(record.reactionEnabled, fallback.reactionEnabled),\n reactionEmojiQq: asString(record.reactionEmojiQq, fallback.reactionEmojiQq),\n reactionEmojiDiscord: asString(record.reactionEmojiDiscord, fallback.reactionEmojiDiscord),\n privateReactionFallbackText: asBool(record.privateReactionFallbackText, fallback.privateReactionFallbackText),\n privateReceiptText: asString(record.privateReceiptText, fallback.privateReceiptText)\n };\n}\n\nconst hermesStore = new ConfigStore<HermesBridgeConfig>({\n path: 'configs/features/hermes.json',\n version: 1,\n factory: hermesConfigFactory,\n normalize: normalizeHermesConfig\n});\n\n/** \u8BFB\u53D6 Hermes \u6865\u63A5\u751F\u6548\u914D\u7F6E\uFF08\u70ED\u91CD\u8F7D\uFF09 */\nexport function getHermesConfig(): HermesBridgeConfig {\n return hermesStore.get();\n}\n\n/**\n * \u6709\u6548\u7BA1\u7406\u5458\u5224\u5B9A\uFF1Ahermes \u81EA\u6709 admins \u222A management operators\u3002\n * \u4E0E /pardon\u3001/github allow \u7B49\u7BA1\u7406\u547D\u4EE4\u4FDD\u6301\u300C\u8C01\u662F\u7BA1\u7406\u5458\u300D\u53E3\u5F84\u4E00\u81F4\u3002\n */\nexport function isHermesAdmin(qqUserId: number): boolean {\n return getHermesConfig().admins.includes(qqUserId) || isOperator(qqUserId);\n}\n", "import { getHermesConfig } from '@/features/hermes/config';\nimport { ApprovalChoice, HermesEventCallbacks, MessageContentPart } from '@/features/hermes/types';\n\n/** \u63D0\u4EA4\u8FD0\u884C\u53C2\u6570 */\ninterface SubmitRunParams {\n userMessage: string | MessageContentPart[];\n sessionId?: string;\n systemPrompt?: string;\n conversationHistory?: Array<{ role: string; content: string }>;\n}\n\n/**\n * Hermes API \u5BA2\u6237\u7AEF\uFF08\u79FB\u690D\u81EA qq-hermes-bridge src/hermes.ts\uFF0CMIT \u534F\u8BAE\uFF0C\u539F\u4F5C\u8005 Amorter\uFF09\u3002\n * \u8D1F\u8D23\u5F02\u6B65\u63D0\u4EA4\u4EFB\u52A1\u3001\u76D1\u542C SSE \u4E8B\u4EF6\u6D41\u3001\u5BA1\u6279\u7BA1\u7406\u548C\u4EFB\u52A1\u505C\u6B62\u3002\n * \u914D\u7F6E\u6BCF\u6B21\u8C03\u7528\u65F6\u8BFB\u53D6\uFF08ConfigStore \u70ED\u91CD\u8F7D\u751F\u6548\uFF09\u3002\n */\nexport class HermesClient {\n private get baseUrl(): string {\n return getHermesConfig().apiUrl;\n }\n\n /**\n * \u63D0\u4EA4\u5F02\u6B65\u8FD0\u884C\u4EFB\u52A1\u3002\n * @returns \u5305\u542B runId \u548C status \u7684\u7ED3\u679C\n */\n public async submitRun({ userMessage, sessionId, systemPrompt, conversationHistory }: SubmitRunParams): Promise<{\n runId: string;\n status: string;\n }> {\n // \u591A\u6A21\u6001\u6D88\u606F\uFF1Ainput \u9700\u4F20\u6D88\u606F\u6570\u7EC4\uFF0C\u6700\u540E\u4E00\u6761\u643A\u5E26\u56FE\u7247\u5185\u5BB9\u6BB5\uFF1B\u7EAF\u6587\u672C\u4FDD\u6301\u539F\u6837\n const body: Record<string, unknown> = {\n input: Array.isArray(userMessage) ? [{ role: 'user', content: userMessage }] : userMessage\n };\n if (systemPrompt) body.instructions = systemPrompt;\n if (sessionId) body.session_id = sessionId;\n if (conversationHistory?.length) body.conversation_history = conversationHistory;\n\n const resp = await fetch(`${this.baseUrl}/v1/runs`, {\n method: 'POST',\n headers: this.headers(),\n body: JSON.stringify(body)\n });\n\n if (!resp.ok) {\n const text = await resp.text().catch(() => '');\n throw new Error(`submitRun \u5931\u8D25 ${resp.status}: ${text.slice(0, 200)}`);\n }\n\n const data = (await resp.json()) as Record<string, string>;\n return {\n runId: data.run_id || data.id || '',\n status: data.status || ''\n };\n }\n\n /**\n * \u8FDE\u63A5 SSE \u4E8B\u4EF6\u6D41\uFF0C\u76D1\u542C\u8FD0\u884C\u8FC7\u7A0B\u4E2D\u7684\u5404\u7C7B\u4E8B\u4EF6\u3002\n * @param runId \u8FD0\u884C ID\n * @param callbacks \u4E8B\u4EF6\u56DE\u8C03\u6620\u5C04\n * @returns \u5305\u542B abort \u65B9\u6CD5\u7684\u63A7\u5236\u5668\n */\n public streamEvents(runId: string, callbacks: HermesEventCallbacks): { abort(): void } {\n const url = `${this.baseUrl}/v1/runs/${runId}/events`;\n const headers = this.headers({ Accept: 'text/event-stream' });\n\n let aborted = false;\n const controller = new AbortController();\n\n const doConnect = async () => {\n try {\n const resp = await fetch(url, { headers, signal: controller.signal });\n if (!resp.ok) {\n const text = await resp.text().catch(() => '');\n callbacks._error?.(new Error(`SSE ${resp.status}: ${text.slice(0, 200)}`));\n return;\n }\n\n const reader = resp.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let currentEvent = '';\n\n while (!aborted) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n // SSE event \u884C\uFF1A\u8BB0\u5F55\u5F53\u524D\u4E8B\u4EF6\u7C7B\u578B\n if (line.startsWith('event: ')) {\n currentEvent = line.slice(7).trim();\n continue;\n }\n if (line.startsWith('data: ')) {\n const jsonStr = line.slice(6).trim();\n if (!jsonStr) continue;\n try {\n const data = JSON.parse(jsonStr) as { event?: string } & Record<string, unknown>;\n // \u4F18\u5148\u7528 SSE event \u884C\u7684\u7C7B\u578B\uFF0C\u5176\u6B21\u7528 data.event\n const eventType =\n currentEvent || (data.event as string) || (data as Record<string, unknown>).type as string || 'unknown';\n\n if (eventType) {\n const handler = (callbacks as Record<string, ((ev: unknown) => void) | undefined>)[eventType];\n if (handler) {\n handler(data);\n } else {\n callbacks._any?.(data as never);\n }\n // run.completed / run.failed \u4E5F\u89E6\u53D1 _end\n if (eventType === 'run.completed' || eventType === 'run.failed') {\n callbacks._end?.();\n }\n }\n\n currentEvent = ''; // \u91CD\u7F6E\uFF0C\u7B49\u5F85\u4E0B\u4E00\u4E2A\u4E8B\u4EF6\n } catch {\n // \u975E JSON \u7684 SSE \u6570\u636E\uFF08\u5FC3\u8DF3\u6CE8\u91CA\uFF09\n }\n } else if (line.startsWith(': stream closed')) {\n callbacks._end?.();\n return;\n }\n }\n }\n callbacks._end?.();\n } catch (err) {\n if (!aborted) callbacks._error?.(err as Error);\n }\n };\n\n void doConnect();\n\n return {\n /** \u4E2D\u6B62 SSE \u8FDE\u63A5 */\n abort() {\n aborted = true;\n controller.abort();\n }\n };\n }\n\n /**\n * \u6279\u51C6\u6216\u62D2\u7EDD\u5F85\u5BA1\u6279\u547D\u4EE4\u3002\n * @param runId \u8FD0\u884C ID\n * @param choice \u5BA1\u6279\u9009\u62E9\uFF1Aonce | deny | always | session\n */\n public async resolveApproval(runId: string, choice: ApprovalChoice): Promise<unknown> {\n const resp = await fetch(`${this.baseUrl}/v1/runs/${runId}/approval`, {\n method: 'POST',\n headers: this.headers(),\n body: JSON.stringify({ choice })\n });\n if (!resp.ok) {\n const text = await resp.text().catch(() => '');\n throw new Error(`\u5BA1\u6279\u5904\u7406\u5931\u8D25 ${resp.status}: ${text.slice(0, 200)}`);\n }\n return resp.json();\n }\n\n /**\n * \u505C\u6B62\u6B63\u5728\u8FD0\u884C\u7684 Agent \u4EFB\u52A1\u3002\n * @param runId \u8FD0\u884C ID\n * @returns \u662F\u5426\u6210\u529F\u505C\u6B62\n */\n public async stopRun(runId: string): Promise<boolean> {\n const resp = await fetch(`${this.baseUrl}/v1/runs/${runId}/stop`, {\n method: 'POST',\n headers: this.headers()\n });\n return resp.ok;\n }\n\n /** \u6784\u5EFA\u8BF7\u6C42\u5934 */\n private headers(extra: Record<string, string> = {}): Record<string, string> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json', ...extra };\n const apiKey = getHermesConfig().apiKey;\n if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`;\n return headers;\n }\n}\n", "import { ViewData } from '@/type/bili';\nimport { Template, tryGenerateImage } from '@/image';\nimport { Logger } from 'winston';\nimport { convertDuration, convertStat, getPicB64 } from '@/features/bili/image';\nimport { QQBot } from '@/index';\nimport dayjs from 'dayjs';\n\nexport function videoHandler(bot: QQBot, viewData: ViewData, logger?: Logger): Promise<string> {\n return new Promise<string>((resolve, reject) => {\n Promise.all([\n getPicB64(bot, viewData.pic).then(cover => {\n viewData.pic = cover;\n }),\n getPicB64(bot, viewData.owner.face).then(cover => {\n viewData.owner.face = cover;\n })\n ]).then(() => {\n const description = `<p>${viewData.desc.split('\\n').join('</p>\\n<p>')}</p>`;\n const template = Template.load('video', 'src/features/bili/template')\n .arg('title', viewData.title)\n .arg('description', description)\n .arg('cover', viewData.pic)\n .arg('avatar', viewData.owner.face)\n .arg('name', viewData.owner.name)\n .arg('date', dayjs(viewData.ctime * 1000).format('YYYY-MM-DD HH:mm'))\n .arg('duration', convertDuration(viewData.duration))\n .arg('view', convertStat(viewData.stat.view))\n .arg('like', convertStat(viewData.stat.like))\n .arg('reply', convertStat(viewData.stat.reply));\n const templateFile = template.file();\n template\n .handler()\n .then(video => {\n logger?.debug(`Start process video message...`);\n tryGenerateImage(resolve, reject, video, 820, templateFile);\n });\n });\n });\n}\n", "import { QQBot } from '@/index';\nimport { ViewData } from '@/type/bili';\nimport { Logger } from 'winston';\nimport { videoHandler } from '@/features/bili/image/impl/video';\n\nconst B64_IMG_PREFIX = 'data:image/jpg;base64,';\n\nexport class BiliImage {\n public static videoHandler(bot: QQBot, viewData: ViewData, logger?: Logger): Promise<string> {\n return videoHandler(bot, viewData, logger);\n }\n}\n\nexport function getPicB64(bot: QQBot, url: string): Promise<string> {\n return new Promise<string>((resolve, reject) => {\n bot.axiosInstance\n .get(url, { responseType: 'arraybuffer' })\n .then(res => resolve(B64_IMG_PREFIX + Buffer.from(res.data, 'binary').toString('base64')))\n .catch(reject);\n });\n}\n\nexport function convertDuration(duration: number): string {\n const hour = Math.floor(duration / 3600);\n const min = Math.floor((duration % 3600) / 60);\n const sec = Math.floor(duration % 60);\n if (hour > 0) return `${hour}:${min}:${sec}`;\n if (min > 0) return `${min}:${sec}`;\n return `${sec}\u79D2`;\n}\n\nexport function convertStat(stat: number) {\n if (Math.floor(stat / 10000) > 0) {\n return `${Math.floor(stat / 10000)}.${Math.floor((stat % 10000) / 1000)}\u4E07`;\n }\n return `${stat}`;\n}\n", "import { QQBot } from '@/index';\nimport { GroupMessageWSMSG, Message } from '@/type';\nimport { ViewData } from '@/type/bili';\nimport { BiliImage } from '@/features/bili/image';\n\nconst BV_REG_EXP = /^BV[a-zA-Z0-9]{10}$/;\nconst AV_REG_EXP = /^av(\\d+)$/;\nconst B23_REG_EXP = /^(https:\\/\\/)?(b23.tv\\/[a-zA-Z0-9]{7})$/;\n\nexport class Bili {\n public static processMessage(bot: QQBot, msg: GroupMessageWSMSG, sentMessage: Message[]): Promise<void> {\n const bv = msg.raw_message.match(BV_REG_EXP);\n if (bv) {\n return Bili.getVideoInfo(bot, msg, bv[0], sentMessage);\n }\n const av = msg.raw_message.match(AV_REG_EXP);\n if (av) {\n return Bili.getVideoInfo(bot, msg, av[0], sentMessage);\n }\n return Promise.resolve();\n }\n\n public static getVideoInfo(bot: QQBot, msg: GroupMessageWSMSG, vid: string, sentMessage: Message[]): Promise<void> {\n return Bili.renderVideoCard(bot, vid).then(result => {\n sentMessage.push({\n type: 'image',\n data: {\n file: `data:image/png;base64, ${result}`\n }\n });\n });\n }\n\n /**\n * \u6E32\u67D3\u89C6\u9891\u4FE1\u606F\u5361\u7247\uFF08\u4F9B AI \u6280\u80FD\u7B49\u7A0B\u5E8F\u5316\u8C03\u7528\uFF09\uFF1A\u652F\u6301 BV \u53F7 / av \u53F7 / b23.tv \u77ED\u94FE\uFF0C\n * \u8FD4\u56DE base64 \u56FE\u7247\uFF1B\u5931\u8D25\u629B\u9519\u7531\u8C03\u7528\u65B9\u7EDF\u4E00\u5904\u7406\u3002\n */\n public static async renderVideoCard(bot: QQBot, input: string): Promise<string> {\n let vid = input.trim();\n const shortLink = vid.match(B23_REG_EXP);\n if (shortLink) {\n // b23 \u77ED\u94FE\uFF1A\u8DDF\u968F 302 \u8DF3\u8F6C\uFF0C\u4ECE\u6700\u7EC8 URL \u63D0\u53D6 BV/av \u53F7\n const url = `https://${shortLink[2]}`;\n const res = await bot.axiosInstance.get(url);\n const finalUrl: string = res.request?.res?.responseUrl ?? '';\n const resolved = finalUrl.match(/BV[a-zA-Z0-9]{10}/) ?? finalUrl.match(/av\\d+/);\n if (!resolved) throw new Error(`\u65E0\u6CD5\u4ECE\u77ED\u94FE ${shortLink[2]} \u89E3\u6790\u51FA\u89C6\u9891`);\n vid = resolved[0];\n }\n if (!BV_REG_EXP.test(vid) && !AV_REG_EXP.test(vid)) {\n throw new Error(`\u65E0\u6CD5\u8BC6\u522B\u7684\u89C6\u9891\u53F7\uFF1A${input}\uFF08\u652F\u6301 BV \u53F7 / av \u53F7 / b23.tv \u77ED\u94FE\uFF09`);\n }\n const params = vid.startsWith('BV') ? { bvid: vid } : { aid: vid.slice(2) };\n const res = await bot.axiosInstance.get<{ code: number; message: string; data: ViewData }>(\n 'https://api.bilibili.com/x/web-interface/view',\n { params }\n );\n const { code, message, data } = res.data;\n // B \u7AD9\u4E1A\u52A1\u9519\u8BEF\uFF08\u5982 -412 \u8BF7\u6C42\u88AB\u62E6\u622A\u3001-404 \u89C6\u9891\u4E0D\u5B58\u5728\uFF09\u629B\u9519\uFF0C\u7531\u8C03\u7528\u65B9\u7EDF\u4E00\u5904\u7406\n if (code !== 0) {\n throw new Error(`B \u7AD9\u63A5\u53E3\u8FD4\u56DE\u9519\u8BEF\uFF08${code}\uFF09\uFF1A${message || '\u672A\u77E5\u9519\u8BEF'}`);\n }\n return BiliImage.videoHandler(bot, data, bot.logger);\n }\n}\n", "import axios, { AxiosResponse } from 'axios';\nimport { JSDOM } from 'jsdom';\n\nexport declare type Version = {\n success: boolean;\n latest: {\n release: string;\n snapshot: string;\n };\n versions: {\n id: string;\n type: string;\n url: string;\n time: string;\n releaseTime: string;\n sha1: string;\n complianceLevel: number;\n }[];\n};\n\nexport declare type MinecraftServerStatus = {\n online: boolean;\n version: string;\n motd: string;\n players: { online: number; max: number; list: string[] };\n};\n\nexport declare type WikiResult = {\n success: boolean;\n url: string;\n title: string;\n desc: string;\n};\n\nexport class MinecraftAPI {\n private static readonly VERSION_MANIFEST_URL: string =\n 'https://piston-meta.mojang.com/mc/game/version_manifest_v2.json';\n\n public static async getVersion(): Promise<Version> {\n try {\n const res: AxiosResponse<{\n latest: {\n release: '';\n snapshot: '';\n };\n versions: [];\n }> = await axios.get(MinecraftAPI.VERSION_MANIFEST_URL);\n return {\n success: true,\n ...res.data\n };\n } catch (_) {\n return {\n success: false,\n latest: {\n release: '',\n snapshot: ''\n },\n versions: []\n };\n }\n }\n\n public static async getMinecraftServerStatus(ip: string, port?: number): Promise<MinecraftServerStatus> {\n try {\n if (ip.includes(':')) port = undefined;\n const res: AxiosResponse = await axios.get(\n `https://api.mcstatus.io/v2/status/java/${ip}${port ? ':' + port : ''}`\n );\n const result: MinecraftServerStatus = {\n online: res.data.online,\n version: res.data.version.name_clean,\n motd: res.data.motd.clean,\n players: {\n online: res.data.players.online,\n max: res.data.players.max,\n list: []\n }\n };\n for (const player of res.data.players.list as { name_clean: string }[]) {\n result.players.list.push(player.name_clean);\n }\n return result;\n } catch (e) {\n return {\n online: false,\n version: '',\n motd: '',\n players: {\n online: 0,\n max: 0,\n list: []\n }\n };\n }\n }\n\n public static async getMinecraftWiki(keyword: string): Promise<WikiResult> {\n try {\n const res: AxiosResponse = await axios.get(`https://zh.minecraft.wiki/w/?search=${keyword}`);\n const dom = new JSDOM(res.data);\n const document = dom.window.document;\n const content = document.getElementById('mw-content-text');\n const result: WikiResult = {\n success: !content?.getElementsByClassName('mw-search-nonefound').length || false,\n url: res.request.res.responseUrl || '',\n title: document.getElementById('firstHeading')?.textContent || '',\n desc: ''\n };\n for (let child of content?.getElementsByClassName('mw-parser-output').item(0)?.children || []) {\n if (child.id == 'toc') break;\n if (child.tagName != 'P') continue;\n if (!child.textContent) continue;\n if (child.textContent == '\\n' || child.textContent == '\\\\n') continue;\n result.desc += '\\r\\n' + child.textContent.replace('\\n', '').replace('\\\\n', '');\n }\n return result;\n } catch (_) {\n return {\n success: false,\n url: '',\n title: '',\n desc: ''\n };\n }\n }\n}\n", "import * as fs from 'node:fs';\nimport { readOrCreate } from '@/config/manager';\n\nexport class EventDataManager {\n private static readonly FILE: string = './configs/event-data.json';\n private static readonly LEGACY_FILE: string = './config/event-data.json';\n private static readonly LEGACY_DIRECTORY: string = './config';\n private static listeners: {\n [key: string]: { roomId: string; channelId: string }[];\n } = {};\n\n private static storage: {\n [key: string]: {\n [key: string]: any;\n };\n } = {};\n\n static {\n EventDataManager.load().then();\n }\n\n public static async addListener(event: string, roomId: string, channelId: string): Promise<void> {\n if (!EventDataManager.listeners[event]) EventDataManager.listeners[event] = [];\n EventDataManager.listeners[event].push({ roomId, channelId });\n await EventDataManager.save();\n }\n\n public static async containListener(event: string, roomId: string, channelId: string): Promise<boolean> {\n if (!EventDataManager.listeners[event]) return false;\n return EventDataManager.listeners[event].some(\n listener => listener.roomId === roomId && listener.channelId === channelId\n );\n }\n\n public static async removeListener(event: string, roomId: string, channelId: string): Promise<void> {\n if (!EventDataManager.listeners[event]) return;\n EventDataManager.listeners[event] = EventDataManager.listeners[event].filter(\n listener => listener.roomId !== roomId && listener.channelId !== channelId\n );\n await EventDataManager.save();\n }\n\n public static async getListeners(event: string): Promise<{ roomId: string; channelId: string }[]> {\n return EventDataManager.listeners[event] || [];\n }\n\n public static async setStorage(event: string, key: string, value: any): Promise<void> {\n if (!EventDataManager.storage[event]) EventDataManager.storage[event] = {};\n EventDataManager.storage[event][key] = value;\n await EventDataManager.save();\n }\n\n public static async getStorage(event: string, key: string): Promise<any> {\n if (!EventDataManager.storage[event]) return undefined;\n return EventDataManager.storage[event][key];\n }\n\n public static async clearStorage(event: string): Promise<void> {\n delete EventDataManager.storage[event];\n await EventDataManager.save();\n }\n\n /**\n * \u4ECE\u78C1\u76D8\u91CD\u65B0\u52A0\u8F7D\u4E8B\u4EF6\u6570\u636E\uFF08/reload \u7528\uFF09\u3002\n * \u73B0\u6709 storage \u4F1A\u88AB\u78C1\u76D8\u5185\u5BB9\u8986\u76D6\uFF0C\u56E0\u6B64\u540C\u65F6\u6E05\u7A7A\u5DF2\u5220\u9664\u7684\u952E\u3002\n */\n public static async reload(): Promise<void> {\n EventDataManager.listeners = {};\n EventDataManager.storage = {};\n await EventDataManager.load();\n }\n\n private static migrateLegacyFile(): void {\n if (!fs.existsSync(EventDataManager.LEGACY_DIRECTORY)) return;\n\n fs.mkdirSync('./configs', { recursive: true });\n if (fs.existsSync(EventDataManager.LEGACY_FILE) && !fs.existsSync(EventDataManager.FILE)) {\n fs.renameSync(EventDataManager.LEGACY_FILE, EventDataManager.FILE);\n }\n fs.rmSync(EventDataManager.LEGACY_DIRECTORY, { recursive: true, force: true });\n }\n\n public static async checkAndCreateFile(): Promise<void> {\n EventDataManager.migrateLegacyFile();\n readOrCreate<{ listeners: Record<string, never>; storage: Record<string, never> }>({\n path: EventDataManager.FILE,\n factory: () => ({ listeners: {}, storage: {} })\n });\n }\n\n public static async load(): Promise<void> {\n await EventDataManager.checkAndCreateFile();\n const data = readOrCreate<{\n listeners?: Record<string, { roomId: string; channelId: string }[]>;\n storage?: Record<string, Record<string, unknown>>;\n }>({\n path: EventDataManager.FILE,\n factory: () => ({ listeners: {}, storage: {} })\n }).data;\n EventDataManager.listeners = {\n ...EventDataManager.listeners,\n ...(data.listeners || {})\n };\n EventDataManager.storage = {\n ...EventDataManager.storage,\n ...(data.storage || {})\n };\n }\n\n public static async save(): Promise<void> {\n await EventDataManager.checkAndCreateFile();\n const { writeConfigFile } = await import('@/config/manager');\n writeConfigFile(EventDataManager.FILE, {\n listeners: EventDataManager.listeners,\n storage: EventDataManager.storage\n });\n }\n}\n", "import { EventDataManager } from '@/event';\n\n/**\n * \u7248\u672C\u53D1\u5E03\u7684\u65F6\u95F4\u6233\u5355\u8C03\u6027\u8DDF\u8E2A\u5668\u3002\n *\n * \u80CC\u666F\uFF1AMinecraft / Modrinth \u7684\u7248\u672C\u6E05\u5355\u63A5\u53E3\u7ECF\u8FC7 CDN \u7F13\u5B58\uFF0C\u7F13\u5B58\u5237\u65B0\u4E0D\u662F\n * \u5168\u91CF\u539F\u5B50\u7684\uFF0C\u540C\u4E00\"\u6700\u65B0\u7248\u672C\"\u53EF\u80FD\u5728\u51E0\u6B21\u8BF7\u6C42\u95F4\u53CD\u590D\u51FA\u73B0/\u6D88\u5931\uFF08\u4F8B\u5982\n * 1.21.10 \u2192 1.21.9 \u2192 1.21.10\uFF09\u3002\u82E5\u53EA\u6309\"\u503C\u53D8\u5316\u5373\u53D1\u9001\"\u4F1A\u91CD\u590D\u901A\u77E5\u3002\n *\n * \u65B9\u6848\uFF1A\u5229\u7528\u5404\u6570\u636E\u6E90\u81EA\u5E26\u7684\u6743\u5A01\u65F6\u95F4\u6233\u5224\u65AD\u65B0\u7248\u672C\uFF0C\u800C\u4E0D\u662F\u89C2\u5BDF\u7A97\u53E3\u2014\u2014\n * - Minecraft version_manifest_v2.json \u7684 versions[].releaseTime\uFF1A\u5404\u7248\u672C\n * \u53D1\u5E03\u65F6\u95F4\u6052\u5B9A\uFF0CCDN \u53EA\u7F13\u5B58\u771F\u5B9E\u6570\u636E\uFF0C\u540C\u4E00\u7248\u672C\u53F7\u7684\u65F6\u95F4\u6233\u6C38\u8FDC\u4E0D\u53D8\u3002\n * - Modrinth Maven maven-metadata.xml \u7684 <lastUpdated>\uFF1Ametadata \u6587\u4EF6\u66F4\u65B0\n * \u65F6\u95F4\uFF0C\u5355\u8C03\u9012\u589E\uFF08\u53EA\u6709\u53D1\u5E03\u65B0\u7248\u672C\u624D\u66F4\u65B0\uFF09\uFF1BCDN \u56DE\u9000\u8FD4\u56DE\u7684\u65E7\u7F13\u5B58\u5176\n * lastUpdated \u5FC5\u7136\u66F4\u5C0F\u3002\n *\n * \u56E0\u6B64\u8BB0\u5F55\"\u5DF2\u786E\u8BA4\u7248\u672C + \u65F6\u95F4\u6233\"\uFF1A\u4EC5\u5F53\u89C2\u5BDF\u5230\u7684\u65F6\u95F4\u6233\u4E25\u683C\u665A\u4E8E\u5DF2\u8BB0\u5F55\u503C\n * \u624D\u5224\u5B9A\u4E3A\u65B0\u53D1\u5E03\u5E76\u901A\u77E5\u3002CDN \u6296\u52A8\u671F\u95F4\u7248\u672C\u53F7\u6765\u56DE\u8DF3\u52A8\uFF0C\u4F46\u65F6\u95F4\u6233\u4E0D\u4F1A\u5F80\u524D\n * \u8DF3\u2014\u2014\u65E7\u7F13\u5B58\u65F6\u95F4\u6233\u66F4\u5C0F\uFF0C\u81EA\u7136\u4E0D\u89E6\u53D1\u901A\u77E5\u3002\n */\n\nexport interface ReleasedVersionState {\n version: string;\n /** \u6743\u5A01\u53D1\u5E03\u65F6\u95F4\uFF08\u6BEB\u79D2\u65F6\u95F4\u6233\uFF09\uFF1AMinecraft \u7528 releaseTime\uFF0CModrinth \u7528 lastUpdated */\n timestamp: number;\n}\n\n/**\n * \u68C0\u67E5\u4E00\u4E2A\u7248\u672C\u662F\u5426\u4E3A\u65B0\u53D1\u5E03\u3001\u9700\u8981\u901A\u77E5\u3002\n *\n * @param storageKey EventDataManager storage \u952E\uFF08\u5404\u8D44\u6E90\u72EC\u7ACB\uFF0C\u5982 mcupdate:release / modrinth:sodium\uFF09\n * @param version \u672C\u6B21\u89C2\u5BDF\u5230\u7684\u7248\u672C\u53F7\uFF08\u7A7A\u4E32\u5FFD\u7565\uFF09\n * @param timestamp \u7248\u672C\u6743\u5A01\u53D1\u5E03\u65F6\u95F4\uFF08\u6BEB\u79D2\uFF09\uFF1B0 \u6216\u7F3A\u5931\u65F6\u4FDD\u5B88\u5FFD\u7565\uFF08\u4E0D\u8BEF\u62A5\uFF09\n * @returns true = \u8BE5\u7248\u672C\u65F6\u95F4\u6233\u4E25\u683C\u665A\u4E8E\u5DF2\u8BB0\u5F55\u7684\u7248\u672C\uFF0C\u786E\u8BA4\u4E3A\u65B0\u53D1\u5E03\n */\nexport async function checkReleasedVersion(storageKey: string, version: string, timestamp: number): Promise<boolean> {\n if (!version || !timestamp) return false;\n\n const last = (await EventDataManager.getStorage(storageKey, 'released')) as ReleasedVersionState | undefined;\n\n // \u9996\u6B21\u4F7F\u7528\uFF08\u51B7\u542F\u52A8 / \u5347\u7EA7\u5F15\u5165\u65B0\u952E\uFF09\uFF1A\u53EA\u8BB0\u5F55\u5F53\u524D\u7248\u672C\uFF0C\u4E0D\u901A\u77E5\uFF0C\n // \u907F\u514D\u90E8\u7F72/\u91CD\u542F\u540E\u7ACB\u5373\u5BF9\u65E2\u6709\u7248\u672C\u8F70\u70B8\uFF1B\u6B64\u540E\u7248\u672C\u53D8\u5316\u624D\u6B63\u5E38\u901A\u77E5\u3002\n if (!last || !last.version) {\n await EventDataManager.setStorage(storageKey, 'released', { version, timestamp });\n return false;\n }\n\n // \u5DF2\u786E\u8BA4\u8FC7\u7684\u7248\u672C\uFF1A\u5FFD\u7565\uFF08\u6C38\u4E0D\u91CD\u590D\u901A\u77E5\uFF09\n if (version === last.version) return false;\n\n // \u65F6\u95F4\u6233\u4E25\u683C\u66F4\u665A\uFF1A\u771F\u5B9E\u65B0\u53D1\u5E03 \u2192 \u8BB0\u5F55\u5E76\u901A\u77E5\n if (timestamp > last.timestamp) {\n await EventDataManager.setStorage(storageKey, 'released', { version, timestamp });\n return true;\n }\n\n // CDN \u56DE\u9000\u5230\u66F4\u65E7\u7684\u6570\u636E\uFF08\u65E7\u7F13\u5B58\u65F6\u95F4\u6233\u66F4\u5C0F\uFF09\uFF1A\u5FFD\u7565\uFF0C\u4E0D\u66F4\u65B0\u8BB0\u5F55\n return false;\n}\n\n/**\n * \u89E3\u6790 Maven <lastUpdated> \u4E3A\u6BEB\u79D2\u65F6\u95F4\u6233\u3002\n * \u517C\u5BB9\u4E24\u79CD\u683C\u5F0F\uFF1A\u6807\u51C6 Maven 14 \u4F4D YYYYMMDDHHmmss\uFF0C\u4EE5\u53CA 13 \u4F4D\u6BEB\u79D2\u65F6\u95F4\u6233\u3002\n * \u65E0\u6CD5\u89E3\u6790\u65F6\u8FD4\u56DE 0\uFF08\u8C03\u7528\u65B9\u6309\"\u65E0\u65F6\u95F4\u6233\"\u4FDD\u5B88\u5904\u7406\uFF0C\u4E0D\u8BEF\u62A5\uFF09\u3002\n */\nexport function parseMavenLastUpdated(raw: string): number {\n const trimmed = (raw || '').trim();\n if (/^\\d{14}$/.test(trimmed)) {\n const t = Date.parse(\n `${trimmed.slice(0, 4)}-${trimmed.slice(4, 6)}-${trimmed.slice(6, 8)}T` +\n `${trimmed.slice(8, 10)}:${trimmed.slice(10, 12)}:${trimmed.slice(12, 14)}Z`\n );\n return Number.isNaN(t) ? 0 : t;\n }\n if (/^\\d{13}$/.test(trimmed)) return parseInt(trimmed, 10);\n const t = Date.parse(trimmed);\n return Number.isNaN(t) ? 0 : t;\n}", "import axios, { AxiosResponse } from 'axios';\nimport { Message } from '@/type';\nimport { getFeatureGroups } from '@/config/features';\nimport { checkReleasedVersion, parseMavenLastUpdated } from '@/features/version-tracker';\nimport { QQBot } from '@/index';\n\nexport declare type ModrinthVersion = {\n success: boolean;\n latest: string;\n /** Maven <lastUpdated>\uFF0814 \u4F4D YYYYMMDDHHmmss\uFF09/ 13 \u4F4D\u6BEB\u79D2\u65F6\u95F4\u6233 / ISO\uFF0C\u89E3\u6790\u4E3A\u6BEB\u79D2\uFF1B\u65E0\u5219 0 */\n lastUpdated: number;\n};\n\nexport class ModrinthAPI {\n public static async getModVersion(slug: string): Promise<ModrinthVersion> {\n try {\n const res: AxiosResponse<string> = await axios.get(\n `https://api.modrinth.com/maven/maven/modrinth/${slug}/maven-metadata.xml`\n );\n const xml = res.data;\n\n // Parse XML to extract latest version\n const latestMatch = xml.match(/<latest>(.*?)<\\/latest>/);\n if (!latestMatch || !latestMatch[1]) {\n return {\n success: false,\n latest: '',\n lastUpdated: 0\n };\n }\n\n // Parse <lastUpdated>\uFF08\u6807\u51C6 Maven \u5143\u6570\u636E\u65F6\u95F4\u6233\uFF0C\u5355\u8C03\u9012\u589E\uFF09\n const lastUpdatedMatch = xml.match(/<lastUpdated>(.*?)<\\/lastUpdated>/);\n\n return {\n success: true,\n latest: latestMatch[1],\n lastUpdated: parseMavenLastUpdated(lastUpdatedMatch?.[1] || '')\n };\n } catch (_) {\n return {\n success: false,\n latest: '',\n lastUpdated: 0\n };\n }\n }\n\n public static checkVersion(bot: QQBot, slug: string, name: string) {\n ModrinthAPI.getModVersion(slug).then(version => {\n if (!version.success) return;\n // \u7528 Maven lastUpdated \u65F6\u95F4\u6233\u5355\u8C03\u6027\u5224\u65AD\u65B0\u53D1\u5E03\uFF0C\u907F\u514D CDN \u7F13\u5B58\u6296\u52A8\u91CD\u590D\u53D1\u9001\n checkReleasedVersion(`modrinth:${slug}`, version.latest, version.lastUpdated).then((shouldNotify: boolean) => {\n if (!shouldNotify) return;\n const msg: Message[] = [\n {\n type: 'text',\n data: {\n text: `${name}\u66F4\u65B0\u4E86\uFF01\n\u00B7 \u6700\u65B0\u7248\u672C\uFF1A${version.latest}\n\u00B7 \u4E0B\u8F7D\u76F4\u94FE\uFF1Ahttps://api.modrinth.com/maven/maven/modrinth/${slug}/${version.latest}/${slug}-${version.latest}.jar`\n }\n }\n ];\n for (const listener of getFeatureGroups('modrinth')) {\n bot.sendGroupMsg(listener, msg);\n }\n });\n });\n }\n}\n", "import {\n GroupAdminApi,\n ProcessedSkillOutput,\n RouteInfo,\n Skill,\n SkillExecuteContext,\n SkillFeedback,\n SkillResult\n} from '@/features/hermes/types';\nimport type { QQBot } from '@/index';\nimport { Github } from '@/features/github';\nimport { Bili } from '@/features/bili';\nimport { MinecraftAPI } from '@/features/minecraft';\nimport { ModrinthAPI } from '@/features/modrinth';\nimport { isGithubEnabledGroup } from '@/config/features';\n\n/**\n * \u6280\u80FD\u7BA1\u7406\u5668\uFF08\u79FB\u690D\u81EA qq-hermes-bridge src/skills.ts\uFF0CMIT \u534F\u8BAE\uFF0C\u539F\u4F5C\u8005 Amorter\uFF09\u3002\n * \u5B9A\u4E49 AI \u53EF\u8C03\u7528\u7684\u6280\u80FD\uFF0C\u751F\u6210\u6280\u80FD\u63D0\u793A\u8BCD\uFF0C\u89E3\u6790\u5E76\u6267\u884C AI \u8F93\u51FA\u4E2D\u7684\u6280\u80FD\u6807\u7B7E\u3002\n * \u7BA1\u7406\u6280\u80FD\u4EC5\u9650\u6709\u6548\u7BA1\u7406\u5458\uFF08hermes admins \u222A management operators\uFF09\u4F7F\u7528\uFF1B\n * \u5361\u7247\u7C7B\u516C\u5171\u6280\u80FD\uFF08\u67E5Issue / B\u7AD9\u89C6\u9891\uFF09\u6240\u6709\u4EBA\u53EF\u7528\uFF0C\u4EA7\u51FA\u56FE\u7247\u7531\u8C03\u7528\u65B9\u9010\u5F20\u53D1\u9001\u3002\n */\nexport class SkillManager {\n /** \u6280\u80FD\u6807\u7B7E\u5339\u914D\u6B63\u5219\uFF1A[SKILL:\u540D\u79F0 \u53C2\u6570...] */\n private static readonly SKILL_TAG_RE = /\\[SKILL:([^\\]]+)\\]/g;\n /** \u5DF2\u6CE8\u518C\u7684\u6280\u80FD\u5217\u8868 */\n private readonly skills: Skill[];\n /** \u6280\u80FD\u540D \u2192 \u6280\u80FD\u5B9A\u4E49\u7684\u5FEB\u901F\u7D22\u5F15 */\n private readonly skillIndex: Map<string, Skill>;\n\n public constructor() {\n this.skills = [\n {\n name: '\u7981\u8A00',\n usage: '\u7981\u8A00 <QQ\u53F7> <\u65F6\u957F(\u5206\u949F)>',\n description: '\u7981\u8A00\u6307\u5B9A\u7FA4\u6210\u5458\uFF0C\u6700\u957F 30 \u5929\uFF0843200 \u5206\u949F\uFF09',\n adminOnly: true,\n execute: this.executeMute.bind(this)\n },\n {\n name: '\u89E3\u9664\u7981\u8A00',\n usage: '\u89E3\u9664\u7981\u8A00 <QQ\u53F7>',\n description: '\u89E3\u9664\u6307\u5B9A\u6210\u5458\u7684\u7981\u8A00',\n adminOnly: true,\n execute: this.executeUnmute.bind(this)\n },\n {\n name: '\u8E22\u51FA',\n usage: '\u8E22\u51FA <QQ\u53F7>',\n description: '\u5C06\u6307\u5B9A\u6210\u5458\u8E22\u51FA\u7FA4\u804A',\n adminOnly: true,\n execute: this.executeKick.bind(this)\n },\n {\n name: '\u5168\u5458\u7981\u8A00',\n usage: '\u5168\u5458\u7981\u8A00 <\u5F00/\u5173>',\n description: '\u5F00\u542F\u6216\u5173\u95ED\u5168\u5458\u7981\u8A00',\n adminOnly: true,\n execute: this.executeWholeBan.bind(this)\n },\n {\n name: '\u67E5Issue',\n usage: '\u67E5Issue <owner/repo#\u7F16\u53F7 \u6216 #\u7F16\u53F7>',\n description: '\u67E5\u8BE2 GitHub Issue/PR \u5E76\u751F\u6210\u4FE1\u606F\u5361\u7247\uFF08PR \u81EA\u52A8\u8BC6\u522B\uFF1B\u7701\u7565\u4ED3\u5E93\u65F6\u9ED8\u8BA4 Anvil-Dev/AnvilCraft\uFF1B\u4E00\u6761\u6700\u591A 3 \u4E2A\u7F16\u53F7\uFF09',\n adminOnly: false,\n execute: this.executeIssueCard.bind(this)\n },\n {\n name: 'B\u7AD9\u89C6\u9891',\n usage: 'B\u7AD9\u89C6\u9891 <BV\u53F7/av\u53F7/b23.tv\u77ED\u94FE>',\n description: '\u83B7\u53D6 B \u7AD9\u89C6\u9891\u4FE1\u606F\u5E76\u751F\u6210\u5361\u7247\uFF08\u6807\u9898/UP\u4E3B/\u65F6\u957F/\u64AD\u653E\u70B9\u8D5E\u8BC4\u8BBA\u6570\uFF09',\n adminOnly: false,\n execute: this.executeBiliCard.bind(this)\n },\n {\n name: 'MC\u7248\u672C',\n usage: 'MC\u7248\u672C',\n description: '\u67E5\u8BE2 Minecraft \u6700\u65B0\u6B63\u5F0F\u7248\u4E0E\u5FEB\u7167\u7248',\n adminOnly: false,\n feedback: true,\n execute: this.executeMcVersion.bind(this)\n },\n {\n name: 'MC\u670D\u52A1\u5668',\n usage: 'MC\u670D\u52A1\u5668 <ip> [\u7AEF\u53E3]',\n description: '\u67E5\u8BE2 Minecraft Java \u670D\u52A1\u5668\u72B6\u6001\uFF08MOTD/\u7248\u672C/\u5728\u7EBF\u73A9\u5BB6\uFF09',\n adminOnly: false,\n feedback: true,\n execute: this.executeMcServer.bind(this)\n },\n {\n name: 'MC\u7EF4\u57FA',\n usage: 'MC\u7EF4\u57FA <\u5173\u952E\u8BCD>',\n description: '\u641C\u7D22\u4E2D\u6587 Minecraft \u7EF4\u57FA\uFF0C\u8FD4\u56DE\u6761\u76EE\u6458\u8981\u4E0E\u94FE\u63A5',\n adminOnly: false,\n feedback: true,\n execute: this.executeMcWiki.bind(this)\n },\n {\n name: 'Modrinth\u7248\u672C',\n usage: 'Modrinth\u7248\u672C <slug>',\n description: '\u67E5\u8BE2 Modrinth \u6A21\u7EC4\u6700\u65B0\u7248\u672C\u4E0E\u4E0B\u8F7D\u76F4\u94FE\uFF08slug \u5982 create-aeronautics\uFF09',\n adminOnly: false,\n feedback: true,\n execute: this.executeModrinthVersion.bind(this)\n }\n ];\n\n this.skillIndex = new Map(this.skills.map(skill => [skill.name, skill]));\n }\n\n /**\n * \u6784\u5EFA\u6280\u80FD\u5217\u8868\u63D0\u793A\u8BCD\uFF0C\u6CE8\u5165\u5230\u7CFB\u7EDF\u63D0\u793A\u8BCD\u4E2D\u3002\n * \u6309\u300C\u516C\u5171\u6280\u80FD\u300D\u548C\u300C\u7BA1\u7406\u6280\u80FD\uFF08\u4EC5\u7BA1\u7406\u5458\uFF09\u300D\u5206\u7EC4\u5C55\u793A\u3002\n */\n public buildPrompt(): string {\n if (this.skills.length === 0) return '';\n\n const adminSkills = this.skills.filter(skill => skill.adminOnly);\n const publicSkills = this.skills.filter(skill => !skill.adminOnly && !skill.feedback);\n const querySkills = this.skills.filter(skill => !skill.adminOnly && skill.feedback);\n\n const lines: string[] = ['## \u53EF\u7528\u6280\u80FD'];\n\n if (publicSkills.length > 0) {\n lines.push('');\n for (const skill of publicSkills) {\n lines.push(`- \\`${skill.usage}\\` \u2014 ${skill.description}`);\n }\n }\n\n if (querySkills.length > 0) {\n lines.push('');\n lines.push('### \u67E5\u8BE2\u6280\u80FD\uFF08\u8C03\u7528\u540E\u7ED3\u679C\u4F1A\u8FD4\u56DE\u7ED9\u4F60\uFF0C\u8BF7\u6574\u7406\u6210\u53E3\u8BED\u5316\u56DE\u7B54\u540E\u518D\u56DE\u590D\uFF0C\u4E0D\u8981\u590D\u8FF0\u539F\u59CB\u6570\u636E\uFF09');\n for (const skill of querySkills) {\n lines.push(`- \\`${skill.usage}\\` \u2014 ${skill.description}`);\n }\n }\n\n if (adminSkills.length > 0) {\n lines.push('');\n lines.push('### \u7BA1\u7406\u6280\u80FD\uFF08\u4EC5\u7BA1\u7406\u5458\u53EF\u7528\uFF09');\n for (const skill of adminSkills) {\n lines.push(`- \\`${skill.usage}\\` \u2014 ${skill.description}`);\n }\n }\n\n lines.push('');\n lines.push('\u8C03\u7528\u683C\u5F0F\uFF1A\u5728\u56DE\u590D\u4E2D\u63D2\u5165 `[SKILL:\u6280\u80FD\u540D \u53C2\u6570...]`\uFF0C\u6807\u7B7E\u4F1A\u5728\u53D1\u9001\u524D\u88AB\u5904\u7406\u5E76\u79FB\u9664\u3002');\n lines.push('\u6CE8\u610F\uFF1A\u53C2\u6570\u4E2D\u7684 QQ \u53F7\u4F7F\u7528\u7EAF\u6570\u5B57\u683C\u5F0F\uFF0C\u591A\u4E2A\u6280\u80FD\u53EF\u5728\u4E00\u6BB5\u8BDD\u4E2D\u540C\u65F6\u8C03\u7528\u3002');\n lines.push('\u5361\u7247\u7C7B\u6280\u80FD\uFF08\u67E5Issue / B\u7AD9\u89C6\u9891\uFF09\u4F1A\u4EE5\u56FE\u7247\u5F62\u5F0F\u968F\u56DE\u590D\u4E00\u8D77\u53D1\u51FA\uFF0C\u65E0\u9700\u5728\u6587\u672C\u4E2D\u590D\u8FF0\u5361\u7247\u5185\u5BB9\u3002');\n lines.push('\u67E5\u8BE2\u7C7B\u6280\u80FD\u8C03\u7528\u540E\u8BF7\u7B49\u5F85\u6570\u636E\u8FD4\u56DE\uFF0C\u4E0D\u8981\u5047\u88C5\u5DF2\u7ECF\u77E5\u9053\u7ED3\u679C\u3002');\n\n return lines.join('\\n');\n }\n\n /**\n * \u89E3\u6790\u5E76\u6267\u884C AI \u8F93\u51FA\u4E2D\u7684 [SKILL:...] \u6807\u7B7E\u3002\n * \u5168\u90E8\u6807\u7B7E\u6267\u884C\u5B8C\u6BD5\u540E\u4ECE\u6587\u672C\u4E2D\u79FB\u9664\uFF0C\u5E76\u5728\u672B\u5C3E\u9644\u52A0\u6267\u884C\u6458\u8981\u3002\n *\n * @param output AI \u751F\u6210\u7684\u8F93\u51FA\u6587\u672C\n * @param route \u6D88\u606F\u6765\u6E90\u8DEF\u7531\n * @param deps \u4F9D\u8D56\u9879\uFF08QQBot\u3001\u7FA4\u7BA1\u7406 API \u95E8\u9762\u548C\u7BA1\u7406\u5458\u68C0\u67E5\u51FD\u6570\uFF09\n * @returns \u6E05\u7406\u540E\u7684\u6587\u672C\uFF08\u542B\u6267\u884C\u6458\u8981\uFF09\u4E0E\u5168\u90E8\u6280\u80FD\u4EA7\u51FA\u7684\u56FE\u7247\uFF08base64\uFF09\n */\n public async processTags(\n output: string,\n route: RouteInfo,\n deps: { bot: QQBot; api: GroupAdminApi; isAdmin: (userId: string) => boolean }\n ): Promise<ProcessedSkillOutput> {\n const { bot, api, isAdmin } = deps;\n\n // \u6536\u96C6\u6240\u6709\u6280\u80FD\u6807\u7B7E\n const tags: Array<{ raw: string; content: string }> = [];\n let match: RegExpExecArray | null;\n SkillManager.SKILL_TAG_RE.lastIndex = 0;\n while ((match = SkillManager.SKILL_TAG_RE.exec(output)) !== null) {\n tags.push({ raw: match[0], content: match[1]! });\n }\n\n if (tags.length === 0) return { text: output, images: [], feedback: [] };\n\n // \u9010\u4E2A\u89E3\u6790\u5E76\u6267\u884C\n const results: SkillResult[] = [];\n const feedback: SkillFeedback[] = [];\n for (const tag of tags) {\n const parts = tag.content.trim().split(/\\s+/);\n const skillName = parts[0]!;\n const args = parts.slice(1);\n\n const skill = this.skillIndex.get(skillName);\n if (!skill) {\n results.push({ ok: false, skill: skillName, error: `\u672A\u77E5\u6280\u80FD: ${skillName}` });\n continue;\n }\n\n // \u7BA1\u7406\u5458\u6743\u9650\u68C0\u67E5\n if (skill.adminOnly && !isAdmin(route.userId)) {\n results.push({ ok: false, skill: skillName, error: '\u4EC5\u7BA1\u7406\u5458\u53EF\u7528' });\n continue;\n }\n\n try {\n const execution = await skill.execute({ bot, api, route, args });\n const data = typeof execution === 'string' ? execution : execution.message;\n if (skill.feedback) {\n // \u67E5\u8BE2\u7C7B\uFF1A\u6570\u636E\u56DE\u5582\u7ED9 AI \u6574\u7406\u56DE\u7B54\uFF0C\u4E0D\u9644\u52A0\u6458\u8981\n feedback.push({ skill: skillName, data });\n continue;\n }\n if (typeof execution === 'string') {\n results.push({ ok: true, skill: skillName, message: execution });\n } else {\n results.push({\n ok: true,\n skill: skillName,\n message: execution.message,\n ...(execution.images?.length ? { images: execution.images } : {})\n });\n }\n } catch (error) {\n // \u67E5\u8BE2\u7C7B\u5931\u8D25\u540C\u6837\u56DE\u5582\uFF08\u8BA9 AI \u544A\u77E5\u7528\u6237\u6CA1\u67E5\u5230\uFF09\uFF0C\u52A8\u4F5C\u7C7B\u8BB0\u5165\u9519\u8BEF\u6458\u8981\n if (skill.feedback) {\n feedback.push({ skill: skillName, data: `\u6267\u884C\u5931\u8D25\uFF1A${(error as Error).message}` });\n } else {\n results.push({ ok: false, skill: skillName, error: (error as Error).message });\n }\n }\n }\n\n // \u4ECE\u6587\u672C\u4E2D\u79FB\u9664\u6240\u6709\u6280\u80FD\u6807\u7B7E\n let cleaned = output;\n for (const tag of tags) {\n cleaned = cleaned.replace(tag.raw, '');\n }\n cleaned = cleaned.replace(/ {2,}/g, ' ').replace(/\\n{3,}/g, '\\n\\n').trim();\n\n // \u9644\u52A0\u6267\u884C\u6458\u8981\uFF08\u6574\u4F53 trim\uFF1A\u6574\u6761\u6D88\u606F\u53EA\u6709\u6280\u80FD\u6807\u7B7E\u65F6\u907F\u514D\u6458\u8981\u524D\u51FA\u73B0\u7A7A\u884C\u2014\u2014\u5BF9\u4E0A\u6E38\u7684\u5FAE\u8C03\uFF09\n const summaryLines: string[] = [];\n const images: string[] = [];\n for (const result of results) {\n if (result.ok) {\n summaryLines.push(`\u2705 ${result.message}`);\n if (result.images) images.push(...result.images);\n } else {\n summaryLines.push(`\u274C ${result.skill}: ${result.error}`);\n }\n }\n if (summaryLines.length > 0) {\n cleaned = cleaned + '\\n\\n' + summaryLines.join('\\n');\n }\n\n return { text: cleaned.trim(), images, feedback };\n }\n\n /** \u4ECE\u53C2\u6570\u4E2D\u63D0\u53D6\u7EAF\u6570\u5B57 QQ \u53F7 */\n private extractQq(arg: string | undefined): string | null {\n if (!arg) return null;\n const match = String(arg).match(/\\((\\d+)\\)$/);\n return match ? match[1]! : String(arg).replace(/\\D/g, '') || null;\n }\n\n /** \u89E3\u6790\u6B63\u6574\u6570 */\n private parsePositiveInt(text: string | undefined, max = Infinity): number | null {\n const value = Number.parseInt(text ?? '', 10);\n if (!Number.isFinite(value) || value <= 0) return null;\n return Math.min(value, max);\n }\n\n /** \u6267\u884C\u7981\u8A00\u6280\u80FD */\n private async executeMute({ api, route, args }: SkillExecuteContext): Promise<string> {\n const qq = this.extractQq(args[0]);\n if (!qq) throw new Error('\u7F3A\u5C11 QQ \u53F7\uFF0C\u683C\u5F0F: \u7981\u8A00 <QQ\u53F7> <\u65F6\u957F(\u5206\u949F)>');\n const minutes = this.parsePositiveInt(args[1], 43200);\n if (!minutes) throw new Error('\u65F6\u957F\u65E0\u6548\uFF0C\u683C\u5F0F: \u7981\u8A00 <QQ\u53F7> <\u65F6\u957F(\u5206\u949F)>');\n await api.setGroupBan(route.groupId!, qq, minutes * 60);\n return `\u5DF2\u7981\u8A00 ${qq} ${minutes} \u5206\u949F`;\n }\n\n /** \u6267\u884C\u89E3\u9664\u7981\u8A00\u6280\u80FD */\n private async executeUnmute({ api, route, args }: SkillExecuteContext): Promise<string> {\n const qq = this.extractQq(args[0]);\n if (!qq) throw new Error('\u7F3A\u5C11 QQ \u53F7\uFF0C\u683C\u5F0F: \u89E3\u9664\u7981\u8A00 <QQ\u53F7>');\n await api.setGroupBan(route.groupId!, qq, 0);\n return `\u5DF2\u89E3\u9664 ${qq} \u7684\u7981\u8A00`;\n }\n\n /** \u6267\u884C\u8E22\u51FA\u6280\u80FD */\n private async executeKick({ api, route, args }: SkillExecuteContext): Promise<string> {\n const qq = this.extractQq(args[0]);\n if (!qq) throw new Error('\u7F3A\u5C11 QQ \u53F7\uFF0C\u683C\u5F0F: \u8E22\u51FA <QQ\u53F7>');\n await api.setGroupKick(route.groupId!, qq);\n return `\u5DF2\u5C06 ${qq} \u8E22\u51FA\u7FA4\u804A`;\n }\n\n /** \u6267\u884C\u5168\u5458\u7981\u8A00\u6280\u80FD */\n private async executeWholeBan({ api, route, args }: SkillExecuteContext): Promise<string> {\n const arg = String(args[0] || '').trim();\n if (['\u5F00', '\u5F00\u542F', '\u542F\u7528', 'on', 'true', '1'].includes(arg)) {\n await api.setGroupWholeBan(route.groupId!, true);\n return '\u5DF2\u5F00\u542F\u5168\u5458\u7981\u8A00';\n }\n if (['\u5173', '\u5173\u95ED', '\u7981\u7528', 'off', 'false', '0'].includes(arg)) {\n await api.setGroupWholeBan(route.groupId!, false);\n return '\u5DF2\u5173\u95ED\u5168\u5458\u7981\u8A00';\n }\n throw new Error('\u53C2\u6570\u65E0\u6548\uFF0C\u8BF7\u4F7F\u7528 \u5F00 \u6216 \u5173');\n }\n\n // \u2500\u2500 \u5361\u7247\u7C7B\u516C\u5171\u6280\u80FD\uFF08\u590D\u7528 github / bili \u529F\u80FD\u7684\u6E32\u67D3\u7BA1\u7EBF\uFF09 \u2500\u2500\n\n /** \u5361\u7247\u6280\u80FD\u4E00\u6B21\u6700\u591A\u751F\u6210\u7684\u56FE\u7247\u6570\uFF08\u9632\u5237\u5C4F\uFF09 */\n private static readonly MAX_CARD_IMAGES = 3;\n\n /**\n * \u67E5Issue\uFF1A\u89E3\u6790\u7F16\u53F7\u5F15\u7528\u5E76\u590D\u7528 Github.renderReference \u6E32\u67D3\u5361\u7247\uFF08PR \u81EA\u52A8\u8BC6\u522B\uFF09\u3002\n * \u7FA4/\u9891\u9053\u8DEF\u7531\u8981\u6C42\u8BE5\u7FA4\u542F\u7528 github \u529F\u80FD\uFF1B\u4ED3\u5E93\u5141\u8BB8\u5217\u8868\u5224\u5B9A\u5728 renderReference \u5185\u5B8C\u6210\u3002\n */\n private async executeIssueCard({ bot, route, args }: SkillExecuteContext): Promise<{ message: string; images?: string[] }> {\n const input = args.join(' ').trim();\n if (!input) throw new Error('\u7F3A\u5C11\u7F16\u53F7\uFF0C\u683C\u5F0F: \u67E5Issue <owner/repo#\u7F16\u53F7 \u6216 #\u7F16\u53F7>');\n if (route.type !== 'user' && route.groupId && !isGithubEnabledGroup(Number(route.groupId))) {\n throw new Error('\u672C\u7FA4\u672A\u542F\u7528 GitHub \u529F\u80FD');\n }\n const references = Github.parseReferences(input).slice(0, SkillManager.MAX_CARD_IMAGES);\n if (references.length === 0) {\n throw new Error('\u672A\u8BC6\u522B\u5230 Issue/PR \u7F16\u53F7\uFF0C\u683C\u5F0F: \u67E5Issue <owner/repo#\u7F16\u53F7 \u6216 #\u7F16\u53F7>');\n }\n const images: string[] = [];\n const notes: string[] = [];\n for (const reference of references) {\n const rendered = await Github.renderReference(bot, reference);\n // Message \u8054\u5408\u7C7B\u578B\u4E2D\u90E8\u5206\u6210\u5458\uFF08\u5982 DiceMessage\uFF09\u65E0 data \u5B57\u6BB5\uFF0C\u6574\u4F53\u65AD\u8A00\u540E\u53D6\u503C\n const data = (rendered as { data?: { file?: string; text?: string } }).data ?? {};\n if (rendered.type === 'image') {\n const file = data.file ?? '';\n const comma = file.indexOf(',');\n const base64 = (comma >= 0 ? file.slice(comma + 1) : file).trim();\n images.push(base64);\n notes.push(`${reference.repository}#${reference.number}`);\n } else {\n // \u6E32\u67D3\u5931\u8D25/\u4ED3\u5E93\u4E0D\u5141\u8BB8\uFF1A\u5C1A\u65E0\u6210\u529F\u5361\u7247\u65F6\u6574\u4F53\u5931\u8D25\uFF1B\u5DF2\u6709\u6210\u529F\u5361\u7247\u65F6\u5E76\u5165\u6458\u8981\u8BF4\u660E\n const reason = data.text ?? '\u6E32\u67D3\u5931\u8D25';\n if (images.length === 0) throw new Error(reason);\n notes.push(`${reference.repository}#${reference.number}\uFF08${reason}\uFF09`);\n }\n }\n return { message: `\u5DF2\u751F\u6210\u5361\u7247\uFF1A${notes.join('\u3001')}`, images };\n }\n\n /** B\u7AD9\u89C6\u9891\uFF1ABV\u53F7/av\u53F7/b23.tv \u77ED\u94FE \u2192 Bili.renderVideoCard \u6E32\u67D3\u4FE1\u606F\u5361\u7247 */\n private async executeBiliCard({ bot, args }: SkillExecuteContext): Promise<{ message: string; images: string[] }> {\n const input = args.join(' ').trim();\n if (!input) throw new Error('\u7F3A\u5C11\u89C6\u9891\u53F7\uFF0C\u683C\u5F0F: B\u7AD9\u89C6\u9891 <BV\u53F7/av\u53F7/b23.tv\u77ED\u94FE>');\n const base64 = await Bili.renderVideoCard(bot, input);\n return { message: '\u5DF2\u751F\u6210 B \u7AD9\u89C6\u9891\u5361\u7247', images: [base64] };\n }\n\n // \u2500\u2500 \u67E5\u8BE2\u7C7B\u516C\u5171\u6280\u80FD\uFF08feedback\uFF1A\u7ED3\u679C\u56DE\u5582\u7ED9 AI \u6574\u7406\u56DE\u7B54\uFF0C\u4E0D\u76F4\u63A5\u53D1\u6458\u8981\uFF09 \u2500\u2500\n\n /** MC\u7248\u672C\uFF1AMinecraft \u6700\u65B0\u6B63\u5F0F\u7248/\u5FEB\u7167\u7248 */\n private async executeMcVersion(): Promise<string> {\n const version = await MinecraftAPI.getVersion();\n if (!version.success) throw new Error('\u83B7\u53D6 Minecraft \u7248\u672C\u4FE1\u606F\u5931\u8D25');\n return `\u6700\u65B0\u6B63\u5F0F\u7248\uFF1A${version.latest.release}\\n\u6700\u65B0\u5FEB\u7167\u7248\uFF1A${version.latest.snapshot}`;\n }\n\n /** MC\u670D\u52A1\u5668\uFF1AJava \u7248\u670D\u52A1\u5668\u72B6\u6001\uFF08\u5730\u5740\u53EF\u5E26\u7AEF\u53E3\uFF0C\u4E5F\u53EF\u5206\u5F00\u4F20\uFF09 */\n private async executeMcServer({ args }: SkillExecuteContext): Promise<string> {\n const ip = args[0]?.trim();\n if (!ip) throw new Error('\u7F3A\u5C11\u670D\u52A1\u5668\u5730\u5740\uFF0C\u683C\u5F0F: MC\u670D\u52A1\u5668 <ip> [\u7AEF\u53E3]');\n const parsed = args[1] ? Number.parseInt(args[1], 10) : NaN;\n const status = await MinecraftAPI.getMinecraftServerStatus(ip, Number.isSafeInteger(parsed) ? parsed : undefined);\n if (!status.online) return `\u670D\u52A1\u5668 ${ip} \u5F53\u524D\u79BB\u7EBF\u6216\u65E0\u6CD5\u8FDE\u63A5`;\n return [\n `\u670D\u52A1\u5668 ${ip} \u5728\u7EBF`,\n `MOTD\uFF1A${status.motd}`,\n `\u6E38\u620F\u7248\u672C\uFF1A${status.version}`,\n `\u5728\u7EBF\u73A9\u5BB6\uFF1A${status.players.online}/${status.players.max}`,\n status.players.list.length > 0 ? `\u5728\u7EBF\u5217\u8868\uFF1A${status.players.list.join('\u3001')}` : ''\n ]\n .filter(Boolean)\n .join('\\n');\n }\n\n /** MC\u7EF4\u57FA\uFF1A\u4E2D\u6587 Minecraft \u7EF4\u57FA\u641C\u7D22\uFF08\u5173\u952E\u8BCD\u53EF\u542B\u7A7A\u683C\uFF09 */\n private async executeMcWiki({ args }: SkillExecuteContext): Promise<string> {\n const query = args.join(' ').trim();\n if (!query) throw new Error('\u7F3A\u5C11\u5173\u952E\u8BCD\uFF0C\u683C\u5F0F: MC\u7EF4\u57FA <\u5173\u952E\u8BCD>');\n const wiki = await MinecraftAPI.getMinecraftWiki(query);\n if (!wiki.success) return `\u672A\u627E\u5230\u300C${query}\u300D\u76F8\u5173\u7684\u7EF4\u57FA\u6761\u76EE`;\n return `\u6761\u76EE\uFF1A${wiki.title}\\n\u6458\u8981\uFF1A${wiki.desc.trim()}\\n\u94FE\u63A5\uFF1A${wiki.url}`;\n }\n\n /** Modrinth\u7248\u672C\uFF1A\u6A21\u7EC4\u6700\u65B0\u7248\u672C\u4E0E\u4E0B\u8F7D\u76F4\u94FE */\n private async executeModrinthVersion({ args }: SkillExecuteContext): Promise<string> {\n const slug = args[0]?.trim();\n if (!slug) throw new Error('\u7F3A\u5C11\u6A21\u7EC4 slug\uFF0C\u683C\u5F0F: Modrinth\u7248\u672C <slug>');\n const version = await ModrinthAPI.getModVersion(slug);\n if (!version.success) throw new Error(`\u83B7\u53D6\u6A21\u7EC4 ${slug} \u7684\u7248\u672C\u4FE1\u606F\u5931\u8D25\uFF08slug \u662F\u5426\u6B63\u786E\uFF1F\uFF09`);\n return (\n `\u6A21\u7EC4 ${slug} \u6700\u65B0\u7248\u672C\uFF1A${version.latest}\\n` +\n `\u4E0B\u8F7D\u76F4\u94FE\uFF1Ahttps://api.modrinth.com/maven/maven/modrinth/${slug}/${version.latest}/${slug}-${version.latest}.jar`\n );\n }\n}\n", "import fs from 'node:fs';\nimport path from 'node:path';\nimport { HistoryEntry } from '@/features/hermes/types';\n\n/**\n * \u804A\u5929\u8BB0\u5F55\u6301\u4E45\u5316\u5B58\u50A8\uFF08\u79FB\u690D\u81EA qq-hermes-bridge src/history.ts\uFF0CMIT \u534F\u8BAE\uFF0C\u539F\u4F5C\u8005 Amorter\uFF09\u3002\n * \u6BCF\u4E2A\u4F1A\u8BDD\u4EE5 JSON \u6587\u4EF6\u5F62\u5F0F\u5B58\u50A8\u5728 data/hermes-history/\uFF0C\u6700\u591A\u4FDD\u7559 N \u6761\u6D88\u606F\uFF0C\n * \u8FDB\u7A0B\u91CD\u542F\u540E\u81EA\u52A8\u6062\u590D\u3002\n */\nexport class ChatHistoryStore {\n private readonly dir: string;\n private readonly maxMessages: number;\n private readonly enabled: boolean;\n\n /** \u5185\u5B58\u7F13\u5B58\uFF1Akey \u2192 \u5386\u53F2\u6D88\u606F\u6570\u7EC4 */\n private readonly cache = new Map<string, HistoryEntry[]>();\n\n /**\n * @param maxMessages \u6BCF\u4E2A\u4F1A\u8BDD\u6700\u591A\u4FDD\u7559\u7684\u6D88\u606F\u6761\u6570\n * @param enabled \u662F\u5426\u542F\u7528\u6301\u4E45\u5316\n */\n public constructor(maxMessages: number, enabled: boolean) {\n this.dir = path.resolve(process.cwd(), 'data', 'hermes-history');\n this.maxMessages = maxMessages;\n this.enabled = enabled;\n if (enabled) {\n fs.mkdirSync(this.dir, { recursive: true });\n }\n }\n\n /**\n * \u52A0\u8F7D\u4F1A\u8BDD\u5386\u53F2\u3002\n * \u4F18\u5148\u4ECE\u5185\u5B58\u7F13\u5B58\u8BFB\u53D6\uFF0C\u7F13\u5B58\u672A\u547D\u4E2D\u65F6\u4ECE\u78C1\u76D8\u52A0\u8F7D\u3002\n */\n public load(key: string): HistoryEntry[] {\n if (!this.enabled) return [];\n if (this.cache.has(key)) return this.cache.get(key)!;\n\n try {\n const filePath = this.filePath(key);\n if (fs.existsSync(filePath)) {\n const data = JSON.parse(fs.readFileSync(filePath, 'utf-8')) as HistoryEntry[];\n if (Array.isArray(data)) {\n this.cache.set(key, data);\n return data;\n }\n }\n } catch {\n // \u6587\u4EF6\u635F\u574F\u6216\u683C\u5F0F\u9519\u8BEF\uFF0C\u5FFD\u7565\n }\n return [];\n }\n\n /**\n * \u4FDD\u5B58\u4F1A\u8BDD\u5386\u53F2\u3002\n * \u81EA\u52A8\u88C1\u526A\u5230 maxMessages \u6761\uFF0C\u5148\u5199\u5185\u5B58\u7F13\u5B58\u518D\u843D\u76D8\u3002\n */\n public save(key: string, history: HistoryEntry[]): void {\n if (!this.enabled) return;\n const trimmed = history.slice(-this.maxMessages);\n this.cache.set(key, trimmed);\n try {\n fs.writeFileSync(this.filePath(key), JSON.stringify(trimmed, null, 2), 'utf-8');\n } catch {\n // \u5199\u5165\u5931\u8D25\u4E0D\u963B\u585E\u4E3B\u6D41\u7A0B\uFF08\u78C1\u76D8\u6EE1\u7B49\u6781\u7AEF\u60C5\u51B5\uFF09\n }\n }\n\n /** \u6E05\u9664\u4F1A\u8BDD\u7684\u6301\u4E45\u5316\u5386\u53F2 */\n public clear(key: string): void {\n if (!this.enabled) return;\n this.cache.delete(key);\n try {\n const filePath = this.filePath(key);\n if (fs.existsSync(filePath)) fs.unlinkSync(filePath);\n } catch {\n // \u5FFD\u7565\n }\n }\n\n /** \u6839\u636E\u4F1A\u8BDD\u952E\u751F\u6210\u5B89\u5168\u7684\u6587\u4EF6\u540D\uFF08group:123456 \u2192 group_123456.json\uFF09 */\n private filePath(key: string): string {\n const safe = key.replace(/[<>:\"/\\\\|?*]/g, '_');\n return path.join(this.dir, `${safe}.json`);\n }\n}\n", "import { HistoryEntry, RouteInfo } from '@/features/hermes/types';\n\n/**\n * \u4F1A\u8BDD\u4E0A\u4E0B\u6587\u7EC4\u88C5\uFF08\u7EAF\u51FD\u6570\uFF09\u3002\n *\n * \u7FA4\u804A/\u4E92\u901A\u9891\u9053\u7684\u4F1A\u8BDD\u5386\u53F2\u662F\u591A\u4EBA\u5171\u4EAB\u7684\uFF0C\u82E5\u539F\u6837\u5E73\u94FA\u63D0\u4EA4\uFF0C\u6A21\u578B\u5206\u4E0D\u6E05\u300C\u8C01\u5728\u8DDF\u6211\n * \u8BF4\u8BDD\u3001\u54EA\u4E9B\u662F\u80CC\u666F\u95F2\u804A\u3001\u6211\u4E4B\u524D\u56DE\u590D\u7684\u662F\u8C01\u300D\uFF0C\u5BB9\u6613\u4E32\u53F0\u8BA4\u9519\u4EBA\u3002\u8FD9\u91CC\u5728\u63D0\u4EA4\u65F6\u6309\n * \u5F53\u524D\u5BF9\u8BDD\u8005\u52A8\u6001\u6807\u6CE8\u5F52\u5C5E\uFF08\u5B58\u50A8\u5C42\u4FDD\u6301\u539F\u6837\uFF09\uFF1A\n * - \u5F53\u524D\u5BF9\u8BDD\u8005\u7684\u6D88\u606F\uFF1A\u4FDD\u6301 `\u53D1\u9001\u8005: \u5185\u5BB9` \u539F\u6837\uFF08\u5BF9\u8BDD\u4E3B\u7EBF\uFF09\uFF1B\n * - \u5176\u4ED6\u6210\u5458\u7684\u6D88\u606F\uFF1A\u52A0 [\u7FA4\u804A\u80CC\u666F] \u524D\u7F00\uFF08\u65C1\u542C\u5185\u5BB9\uFF0C\u4E0D\u662F\u5BF9 AI \u8BF4\u7684\u8BDD\uFF09\uFF1B\n * - AI \u56DE\u590D\u8FC7\u5176\u4ED6\u6210\u5458\u7684\u8BDD\uFF1A\u52A0 [\u4F60\u56DE\u590D xxx \u7684\u8BDD] \u524D\u7F00\uFF08\u907F\u514D\u8BEF\u8BA4\u4E3A\u662F\u5BF9\u5F53\u524D\n * \u5BF9\u8BDD\u8005\u8BF4\u7684\uFF09\u3002\n */\n\n/** \u80CC\u666F\u6D88\u606F\u524D\u7F00\uFF1A\u5176\u4ED6\u6210\u5458\u7684\u95F2\u804A */\nexport const BG_PREFIX = '[\u7FA4\u804A\u80CC\u666F]';\n\n/** \u7EC4\u88C5\u63D0\u4EA4\u7ED9 Hermes \u7684 conversation_history\uFF08\u6309\u5F53\u524D\u5BF9\u8BDD\u8005\u6807\u6CE8\u5F52\u5C5E\uFF09 */\nexport function buildConversationHistory(\n history: HistoryEntry[],\n currentUserId: string\n): Array<{ role: string; content: string }> {\n return history.map(entry => {\n if (entry.role === 'user') {\n if (entry.userId !== undefined && entry.userId !== currentUserId) {\n return { role: 'user', content: `${BG_PREFIX} ${entry.content}` };\n }\n return { role: 'user', content: entry.content };\n }\n if (entry.role === 'assistant') {\n if (entry.userId !== undefined && entry.userId !== currentUserId) {\n return { role: 'assistant', content: `[\u4F60\u56DE\u590D ${entry.label ?? entry.userId} \u7684\u8BDD] ${entry.content}` };\n }\n return { role: 'assistant', content: entry.content };\n }\n return { role: entry.role, content: entry.content };\n });\n}\n\n/**\n * \u7FA4\u804A/\u4E92\u901A\u9891\u9053\u7684\u4E0A\u4E0B\u6587\u7CFB\u7EDF\u63D0\u793A\uFF08QQ \u7FA4\u4E0E Discord \u8DEF\u7531\u7EDF\u4E00\u53E3\u5F84\uFF09\u3002\n * \u663E\u5F0F\u58F0\u660E\u5F53\u524D\u5BF9\u8BDD\u8005\u4E0E\u5386\u53F2\u6807\u6CE8\u7EA6\u5B9A\uFF0C\u914D\u5408 buildConversationHistory \u4F7F\u7528\u3002\n */\nexport function buildGroupContext(route: RouteInfo, senderLabel: string): string {\n const where =\n route.type === 'group'\n ? `\u4F60\u6B63\u5728 QQ \u7FA4 ${route.groupId} \u4E2D\uFF08\u8BE5\u7FA4\u4E0E Discord \u9891\u9053\u4E92\u901A\uFF0C\u6D88\u606F\u4E5F\u53EF\u80FD\u6765\u81EA Discord \u7528\u6237\uFF09\u3002`\n : `\u4F60\u6B63\u5728 QQ \u7FA4 ${route.groupId} \u4E92\u901A\u7684 Discord \u9891\u9053 #${route.discord?.channelName ?? ''} \u4E2D\uFF08\u9891\u9053\u4E0E QQ \u7FA4\u6D88\u606F\u4E92\u901A\uFF09\u3002`;\n return [\n where,\n '\u7FA4\u804A\u6709\u591A\u4E2A\u6210\u5458\uFF0C\u53D1\u9001\u8005\u6807\u8BC6\u683C\u5F0F\u4E3A\u300C\u6635\u79F0 (ID)\u300D\uFF1A\u7EAF\u6570\u5B57 ID \u662F QQ \u53F7\uFF0Cdc: \u524D\u7F00\u7684\u662F Discord \u7528\u6237\uFF0C\u8BF7\u636E\u6B64\u533A\u5206\u4E0D\u540C\u7684\u4EBA\u3002',\n `\u5386\u53F2\u6D88\u606F\u4E2D\u5E26 ${BG_PREFIX} \u524D\u7F00\u7684\u662F\u5176\u4ED6\u6210\u5458\u7684\u95F2\u804A\uFF0C\u4E0D\u662F\u5BF9\u4F60\u8BF4\u7684\u8BDD\uFF0C\u4EC5\u4F9B\u4F60\u4E86\u89E3\u4E0A\u4E0B\u6587\uFF0C\u4E0D\u8981\u56DE\u5E94\u3001\u4E5F\u4E0D\u8981\u5F53\u6210\u4F60\u81EA\u5DF1\u7684\u8BB0\u5FC6\uFF1B\u5E26 [\u4F60\u56DE\u590D xxx \u7684\u8BDD] \u524D\u7F00\u7684\u662F\u4F60\u4E4B\u524D\u56DE\u590D\u5176\u4ED6\u6210\u5458\u7684\u5185\u5BB9\u3002`,\n `\u5F53\u524D\u4E0E\u4F60\u5BF9\u8BDD\u7684\u662F ${senderLabel}\uFF0C\u53EA\u9700\u56DE\u5E94 TA \u7684\u6D88\u606F\u3002\u56DE\u590D\u7B80\u77ED\u53E3\u8BED\u5316\uFF0C\u7B26\u5408\u804A\u5929\u98CE\u683C\u3002`\n ].join('\\n');\n}\n\n/**\n * \u6D88\u606F\u4E2D\u7684 Issue/PR \u7F16\u53F7\u4F1A\u88AB\u6D88\u606F\u7BA1\u9053\u81EA\u52A8\u6E32\u67D3\u5361\u7247\u65F6\uFF0C\u7ED9 AI \u8F93\u5165\u8FFD\u52A0\u6CE8\u8BB0\uFF0C\n * \u907F\u514D AI \u518D\u8C03\u7528\u67E5Issue\u6280\u80FD\u91CD\u590D\u53D1\u540C\u4E00\u5F20\u5361\u7247\uFF08autoCardCount=0 \u65F6\u539F\u6837\u8FD4\u56DE\uFF09\u3002\n */\nexport function appendAutoCardNote(text: string, autoCardCount: number): string {\n if (autoCardCount <= 0) return text;\n return (\n `${text}\\n\uFF08\u7CFB\u7EDF\u63D0\u793A\uFF1A\u672C\u6761\u6D88\u606F\u4E2D\u7684 ${autoCardCount} \u4E2A Issue/PR \u7F16\u53F7\u5DF2\u7531\u7CFB\u7EDF\u81EA\u52A8\u751F\u6210\u4FE1\u606F\u5361\u7247\u5E76\u53D1\u51FA\uFF0C` +\n `\u8BF7\u76F4\u63A5\u636E\u6B64\u56DE\u7B54\uFF0C\u4E0D\u8981\u518D\u8C03\u7528\u67E5Issue\u6280\u80FD\u91CD\u590D\u53D1\u9001\u5361\u7247\uFF09`\n );\n}\n", "/**\n * QQ \u6D88\u606F\u6BB5\u7684\u6DF1\u5EA6\u89E3\u6790\uFF08\u7EAF\u51FD\u6570 + \u6CE8\u5165\u5F0F resolver\uFF0C\u62BD\u79BB\u81EA HermesBridge \u4EE5\u4FBF\u5192\u70DF\u6D4B\u8BD5\uFF09\u3002\n *\n * \u6DF1\u5EA6\u8868\uFF08depth = \u6B63\u5728\u89E3\u6790\u5185\u5BB9\u7684\u5C42\u7EA7\uFF0C\u89E6\u53D1\u6D88\u606F\u672C\u8EAB\u4E3A 0\uFF09\uFF1A\n * | \u6BB5\u7C7B\u578B | depth 0 | depth 1 | depth 2 | depth \u22653 |\n * | \u56FE\u7247 | `[\u56FE\u7247]` + \u539F\u56FE\u5165 images | \u540C\u5DE6 | `[\u56FE\u7247]`\uFF0C\u4E0D\u4E0B\u8F7D | `[\u56FE\u7247]`\uFF0C\u4E0D\u4E0B\u8F7D |\n * | reply / forward | \u62C9\u53D6\u5E76\u89E3\u6790\uFF08depth+1\uFF09 | \u540C\u5DE6 | \u62C9\u53D6\u4F46\u4EC5\u6587\u5B57\uFF08\u4E0D\u4E0B\u8F7D\u56FE\u7247\uFF09 | \u4E0D\u62C9\u53D6\uFF0C\u8F93\u51FA `[\u804A\u5929\u8BB0\u5F55]` |\n * | text / at / video / record | \u539F\u6837\uFF08at\uFF1A\u7FA4\u540D\u7247\u4F18\u5148\uFF0C\u7F3A\u5931\u56DE\u9000 `@QQ\u53F7`\uFF09 | \u540C\u5DE6 | \u540C\u5DE6 | \u540C\u5DE6 |\n *\n * \u6709\u610F\u7684\u53D6\u820D\uFF1A\u4EA4\u4ED8\u7269 5 \u4E2D\u300C\u5F15\u7528\u91CC\u542B\u804A\u5929\u8BB0\u5F55 \u2192 \u8282\u70B9\u5185\u56FE\u7247\u5165 images\u300D\u6309\u6DF1\u5EA6\u8868\u89E3\u91CA\u4E3A\n * forward \u5904\u4E8E depth 0 \u65F6\u5176\u8282\u70B9\u56FE\u7247\u5165 images\uFF1B\u82E5 forward \u843D\u5728 depth 1\uFF0C\u5219\u8282\u70B9\u6587\u5B57\u5C55\u5F00\u3001\n * \u8282\u70B9\u56FE\u7247\u5728 depth 2 \u4E0D\u4E0B\u8F7D\uFF08\u6DF1\u5EA6\u8868\u4E3A\u51C6\uFF09\u3002\n *\n * \u672C\u6A21\u5757\u4E0D\u4EA7\u751F\u4EFB\u4F55 IO\uFF1A\u62C9\u53D6\u3001\u56FE\u7247\u4E0B\u8F7D\u3001\u6635\u79F0\u4E0E\u65F6\u95F4\u683C\u5F0F\u5316\u5168\u90E8\u7531\u8C03\u7528\u65B9\u7ECF MessageResolvers \u6CE8\u5165\u3002\n */\n\n/** \u804A\u5929\u8BB0\u5F55\uFF08\u5408\u5E76\u8F6C\u53D1\uFF09\u5728\u6587\u672C\u4E2D\u7684\u5360\u4F4D\u7B26 */\nexport const FORWARD_PLACEHOLDER = '[\u804A\u5929\u8BB0\u5F55]';\n\n/** \u89E3\u6790\u62A4\u680F\uFF1A\u5355\u6B21\u89E3\u6790\u7684\u6BB5/\u8282\u70B9/\u56FE\u7247\u603B\u91CF\u4E0E\u6DF1\u5EA6\u4E0A\u9650\uFF0C\u9632\u8D85\u957F\u6D88\u606F\u4E0E\u81EA\u73AF\u62D6\u57AE\u8C03\u7528\u65B9 */\nexport const PARSE_LIMITS = {\n /** \u5355\u6B21\u89E3\u6790\u5904\u7406\u7684\u6BB5\u603B\u91CF\u4E0A\u9650\uFF08\u8DE8\u5C42\u7EA7\u7D2F\u8BA1\uFF09 */\n maxSegments: 500,\n /** \u5355\u4E2A\u804A\u5929\u8BB0\u5F55\u5C55\u5F00\u7684\u8282\u70B9\u4E0A\u9650 */\n maxNodes: 50,\n /** \u5355\u6B21\u89E3\u6790\u6536\u96C6\u7684\u56FE\u7247\u4E0A\u9650 */\n maxImages: 10,\n /** \u6DF1\u5EA6\u4E0A\u9650\uFF1A\u5904\u4E8E\u8BE5\u6DF1\u5EA6\u7684 reply/forward \u4E0D\u518D\u62C9\u53D6\uFF0C\u53EA\u7559 `[\u804A\u5929\u8BB0\u5F55]` \u5360\u4F4D */\n maxDepth: 3,\n /** \u4E0B\u8F7D\u56FE\u7247\u7684\u6700\u5927\u6DF1\u5EA6\uFF08depth 2 \u8D77\u53EA\u4FDD\u7559 `[\u56FE\u7247]` \u5360\u4F4D\uFF09 */\n maxImageDepth: 1\n} as const;\n\nexport interface MessageResolvers {\n /** get_msg\uFF0C\u5931\u8D25\u629B\u9519 */\n getMsg(\n messageId: number\n ): Promise<{ sender?: { nickname?: string; user_id?: number }; time?: number; message: unknown }>;\n /** get_forward_msg\uFF0C\u5931\u8D25\u629B\u9519 */\n getForward(forwardId: string): Promise<{ messages: unknown[] }>;\n /** \u56FE\u7247\u6BB5 \u2192 base64 data URL\uFF1B\u5931\u8D25/\u4E0D\u9700\u8981\u65F6\u8FD4\u56DE null */\n resolveImage(segment: { data: { file: string; url?: string } }): Promise<string | null>;\n /** @\u63D0\u53CA\u6635\u79F0\u89E3\u6790\uFF08\u53EF\u9009\uFF1B\u7F3A\u7701\u7528 QQ \u53F7\uFF09 */\n resolveName?(qq: string): Promise<string | null>;\n /** \u65F6\u95F4\u6233\u683C\u5F0F\u5316\uFF08\u6CE8\u5165\u4EE5\u4FDD\u6301\u7EAF\u51C0\uFF09 */\n formatTime(unixTs: number): string;\n /** \u662F\u5426\u4E0B\u8F7D/\u8F6C\u53D1\u56FE\u7247\uFF08config.forwardImages\uFF09 */\n forwardImages: boolean;\n}\n\nexport interface ParsedMessage {\n text: string;\n images: string[];\n}\n\n/**\n * \u5355\u6B21\u89E3\u6790\u7684\u5171\u4EAB\u72B6\u6001\uFF1A\u62A4\u680F\u8BA1\u6570\u3001\u53BB\u91CD\u96C6\u5408\u4E0E @\u63D0\u53CA\u6635\u79F0\u7F13\u5B58\u3002\n * \u7531 parseMessage \u5EFA\u7ACB\u5E76\u4F20\u7ED9\u5404\u5C42 parseSegments\uFF1B\u76F4\u63A5\u8C03\u7528 parseSegments \u65F6\u53EF\u7701\u7565\u3002\n */\nexport interface ParseState {\n /** \u5DF2\u5C55\u5F00\u7684\u5F15\u7528/\u8F6C\u53D1 id\uFF0C\u9632\u81EA\u73AF\u4E0E\u91CD\u590D\u5C55\u5F00 */\n visitedIds: Set<string>;\n /** \u8DE8\u5C42\u7EA7\u7D2F\u8BA1\u7684\u6BB5\u8BA1\u6570\uFF08\u62A4\u680F\uFF09 */\n segmentCount: number;\n /** \u5DF2\u5C55\u5F00\u7684\u8F6C\u53D1\u8282\u70B9\u8BA1\u6570\uFF08\u62A4\u680F\uFF09 */\n nodeCount: number;\n /** \u662F\u5426\u89E3\u6790 @\u63D0\u53CA\u6635\u79F0\uFF08\u7FA4\u804A\u4E0A\u4E0B\u6587\u624D\u89E3\u6790\uFF0C\u79C1\u804A\u76F4\u63A5\u56DE\u9000 QQ \u53F7\uFF09 */\n resolveNames: boolean;\n /** @\u63D0\u53CA\u6635\u79F0\u7F13\u5B58\uFF08qq \u2192 \u6635\u79F0\uFF0C\u7A7A\u4E32\u8868\u793A\u89E3\u6790\u5931\u8D25\uFF09 */\n nameCache: Map<string, string>;\n}\n\n/** \u5EFA\u7ACB\u89E3\u6790\u72B6\u6001\uFF1BresolveNames=false \u65F6 @\u63D0\u53CA \u4E00\u5F8B\u56DE\u9000 QQ \u53F7 */\nexport function createParseState(resolveNames: boolean): ParseState {\n return { visitedIds: new Set<string>(), segmentCount: 0, nodeCount: 0, resolveNames, nameCache: new Map() };\n}\n\n/** \u53D6\u6D88\u606F\u6BB5\u7684\u6570\u636E\u4F53\uFF08\u975E\u5BF9\u8C61\u6309\u7A7A\u5BF9\u8C61\u5904\u7406\uFF09 */\nfunction segmentData(segment: unknown): Record<string, unknown> {\n if (!segment || typeof segment !== 'object') return {};\n const data = (segment as { data?: unknown }).data;\n return data && typeof data === 'object' ? (data as Record<string, unknown>) : {};\n}\n\n/** \u53D6\u6D88\u606F\u6BB5\u7C7B\u578B\uFF08\u7F3A\u5931/\u975E\u5B57\u7B26\u4E32\u8FD4\u56DE\u7A7A\u4E32\uFF09 */\nfunction segmentType(segment: unknown): string {\n if (!segment || typeof segment !== 'object') return '';\n const type = (segment as { type?: unknown }).type;\n return typeof type === 'string' ? type : '';\n}\n\n/** id \u5B57\u6BB5\u5F52\u4E00\u5316\u4E3A\u5B57\u7B26\u4E32\uFF08number/string \u7686\u53EF\uFF0C0 \u4E0E\u7A7A\u4E32\u89C6\u4E3A\u65E0 id\uFF09 */\nfunction readId(raw: unknown): string {\n if (typeof raw === 'number' && Number.isFinite(raw) && raw !== 0) return String(raw);\n if (typeof raw === 'string' && raw.length > 0) return raw;\n return '';\n}\n\n/** \u53D6\u9996\u4E2A\u975E\u7A7A\u5B57\u7B26\u4E32 */\nfunction pickString(values: unknown[]): string {\n for (const value of values) {\n if (typeof value === 'string' && value.length > 0) return value;\n if (typeof value === 'number' && Number.isFinite(value)) return String(value);\n }\n return '';\n}\n\n/** \u5F15\u7528\u5757 / \u8F6C\u53D1\u8282\u70B9\u884C\u7684\u7EDF\u4E00\u683C\u5F0F\uFF1A`> {\u6635\u79F0}({id}) {\u65F6\u95F4}\\n> {\u5185\u5BB9}\\n` */\nfunction quoteBlock(name: string, id: string, time: string, content: string): string {\n return `${`> ${name}(${id}) ${time}`.trimEnd()}\\n> ${content}\\n`;\n}\n\n/** \u89E3\u6790 @\u63D0\u53CA \u6635\u79F0\uFF1A\u7FA4\u804A\u4E0A\u4E0B\u6587\u7ECF resolver \u67E5\u8BE2\u5E76\u7F13\u5B58\uFF0C\u5931\u8D25\u56DE\u9000 null\uFF08\u8C03\u7528\u65B9\u7528 QQ \u53F7\uFF09 */\nasync function resolveAtName(qq: string, resolvers: MessageResolvers, state: ParseState): Promise<string | null> {\n if (!state.resolveNames || !resolvers.resolveName) return null;\n const cached = state.nameCache.get(qq);\n if (cached !== undefined) return cached || null;\n try {\n const name = await resolvers.resolveName(qq);\n state.nameCache.set(qq, name ?? '');\n return name ?? null;\n } catch {\n state.nameCache.set(qq, '');\n return null;\n }\n}\n\n/** \u89E3\u6790\u56FE\u7247\u6BB5\u4E3A base64 data URL\uFF1B\u5931\u8D25/\u4E0D\u9700\u8981\u65F6\u8FD4\u56DE null\uFF08\u8C03\u7528\u65B9\u4FDD\u7559 `[\u56FE\u7247]` \u5360\u4F4D\uFF09 */\nasync function resolveImageSafe(segment: unknown, resolvers: MessageResolvers): Promise<string | null> {\n const data = segmentData(segment);\n const file = typeof data.file === 'string' ? data.file : '';\n const url = typeof data.url === 'string' ? data.url : '';\n if (!file && !url) return null;\n const target: { data: { file: string; url?: string } } = { data: { file } };\n if (url) target.data.url = url;\n try {\n return await resolvers.resolveImage(target);\n } catch {\n return null;\n }\n}\n\n/** \u8F6C\u53D1\u8282\u70B9\u5F52\u4E00\u5316\u7ED3\u679C\uFF08\u517C\u5BB9 NapCat node \u683C\u5F0F\u4E0E go-cqhttp \u65E7\u683C\u5F0F\uFF09 */\ninterface ForwardNodeContent {\n segments: unknown;\n name: string;\n id: string;\n time: string;\n}\n\n/** \u8282\u70B9\u6B63\u6587\uFF1Amessage \u975E\u7A7A\u53D6 message\uFF0C\u5426\u5219\u53D6 content\uFF08content \u53EF\u80FD\u662F\u5B57\u7B26\u4E32\uFF09 */\nfunction pickNodeContent(data: Record<string, unknown>): unknown {\n const message = data.message;\n if (Array.isArray(message) && message.length > 0) return message;\n if (typeof message === 'string' && message.length > 0) return message;\n return data.content ?? [];\n}\n\n/**\n * \u5F52\u4E00\u5316\u8F6C\u53D1\u8282\u70B9\uFF1A\n * - NapCat\uFF1A`{ type: 'node', data: { user_id, nickname, card, time, message, content } }`\n * - go-cqhttp \u65E7\u683C\u5F0F\uFF1A`{ sender, time, content }`\n */\nfunction normalizeForwardNode(raw: unknown, resolvers: MessageResolvers): ForwardNodeContent {\n const record = raw && typeof raw === 'object' ? (raw as Record<string, unknown>) : {};\n const data =\n record.type === 'node' && record.data && typeof record.data === 'object'\n ? (record.data as Record<string, unknown>)\n : record;\n const sender = data.sender && typeof data.sender === 'object' ? (data.sender as Record<string, unknown>) : {};\n const rawTime = data.time ?? record.time;\n return {\n segments: pickNodeContent(data),\n name: pickString([data.nickname, data.card, sender.nickname, sender.card]) || '\u672A\u77E5',\n id: pickString([data.user_id, sender.user_id]),\n time: typeof rawTime === 'number' && rawTime > 0 ? resolvers.formatTime(rawTime) : ''\n };\n}\n\n/** \u5C55\u5F00\u8F6C\u53D1\uFF08\u804A\u5929\u8BB0\u5F55\uFF09\uFF1A\u9010\u8282\u70B9\u8F93\u51FA\u5F15\u7528\u5757\uFF0C\u8282\u70B9\u6B63\u6587\u6309 depth+1 \u7EE7\u7EED\u89E3\u6790 */\nasync function parseForward(\n forwardId: string,\n depth: number,\n resolvers: MessageResolvers,\n images: string[],\n state: ParseState\n): Promise<string> {\n try {\n const forward = await resolvers.getForward(forwardId);\n const parts: string[] = [];\n for (const raw of forward?.messages ?? []) {\n if (state.nodeCount >= PARSE_LIMITS.maxNodes) break;\n state.nodeCount++;\n const node = normalizeForwardNode(raw, resolvers);\n const content = await parseSegments(node.segments, depth + 1, resolvers, images, state);\n parts.push(quoteBlock(node.name, node.id, node.time, content));\n }\n return parts.join('');\n } catch {\n return '> [\u8F6C\u53D1\u6D88\u606F\u83B7\u53D6\u5931\u8D25]\\n';\n }\n}\n\n/** \u5C55\u5F00\u5F15\u7528\uFF1A\u8F93\u51FA\u5F15\u7528\u5757\uFF0C\u88AB\u5F15\u7528\u6D88\u606F\u6309 depth+1 \u7EE7\u7EED\u89E3\u6790 */\nasync function parseReply(\n repliedId: string,\n depth: number,\n resolvers: MessageResolvers,\n images: string[],\n state: ParseState\n): Promise<string> {\n try {\n const numericId = Number(repliedId);\n const original = await resolvers.getMsg(Number.isFinite(numericId) ? numericId : 0);\n const name = pickString([original?.sender?.nickname]) || '\u672A\u77E5';\n const id = pickString([original?.sender?.user_id]);\n const time = typeof original?.time === 'number' && original.time > 0 ? resolvers.formatTime(original.time) : '';\n const content = await parseSegments(original?.message, depth + 1, resolvers, images, state);\n return quoteBlock(name, id, time, content);\n } catch {\n return '> [\u5F15\u7528\u6D88\u606F\u83B7\u53D6\u5931\u8D25]\\n';\n }\n}\n\n/**\n * \u89E3\u6790\u6D88\u606F\u6BB5\u6570\u7EC4\u4E3A\u53EF\u8BFB\u6587\u672C\uFF1A\n * \u6587\u672C \u2192 \u539F\u6587\uFF1B@\u63D0\u53CA \u2192 `@\u6635\u79F0`\uFF08\u7FA4\u804A\u4E0A\u4E0B\u6587\uFF0C\u7F3A\u5931\u56DE\u9000 `@QQ\u53F7`\uFF0C`all` \u2192 `@\u5168\u4F53\u6210\u5458`\uFF09\uFF1B\n * \u56FE\u7247 \u2192 `[\u56FE\u7247]`\uFF08depth \u2264 1 \u4E14\u5F00\u542F forwardImages \u65F6\u539F\u56FE\u5165 images\uFF09\uFF1B\n * \u89C6\u9891/\u8BED\u97F3 \u2192 `[\u89C6\u9891]` / `[\u8BED\u97F3]`\uFF1B\u5F15\u7528/\u8F6C\u53D1 \u2192 \u62C9\u53D6\u5E76\u9010\u5757\u5C55\u5F00\uFF08depth+1\uFF0C\u89C1\u6587\u4EF6\u5934\u6DF1\u5EA6\u8868\uFF09\u3002\n *\n * `images` \u4E3A\u8C03\u7528\u65B9\u6301\u6709\u7684\u6536\u96C6\u6570\u7EC4\uFF08\u51FD\u6570\u53EA\u8FFD\u52A0\uFF09\uFF1B`state` \u7701\u7565\u65F6\u6309\u5355\u6B21\u89E3\u6790\u65B0\u5EFA\uFF08\u6635\u79F0\u89E3\u6790\u5F00\u542F\uFF09\u3002\n */\nexport async function parseSegments(\n segments: unknown,\n depth: number,\n resolvers: MessageResolvers,\n images: string[],\n state?: ParseState\n): Promise<string> {\n if (typeof segments === 'string') return segments;\n if (!Array.isArray(segments)) return '';\n const current = state ?? createParseState(true);\n const parts: string[] = [];\n\n for (const segment of segments) {\n if (current.segmentCount >= PARSE_LIMITS.maxSegments) break;\n current.segmentCount++;\n const data = segmentData(segment);\n\n switch (segmentType(segment)) {\n case 'text': {\n const text = data.text;\n if (typeof text === 'string') parts.push(text);\n break;\n }\n case 'at': {\n const qq = pickString([data.qq]);\n if (qq === 'all') parts.push('@\u5168\u4F53\u6210\u5458');\n else if (qq) parts.push(`@${(await resolveAtName(qq, resolvers, current)) ?? qq}`);\n break;\n }\n case 'image': {\n parts.push('[\u56FE\u7247]');\n if (!resolvers.forwardImages || depth > PARSE_LIMITS.maxImageDepth) break;\n if (images.length >= PARSE_LIMITS.maxImages) break;\n const url = await resolveImageSafe(segment, resolvers);\n if (url) images.push(url);\n break;\n }\n case 'video':\n parts.push('[\u89C6\u9891]');\n break;\n case 'record':\n parts.push('[\u8BED\u97F3]');\n break;\n case 'reply':\n case 'forward': {\n const id = readId(data.id);\n if (!id) break;\n const key = `${segmentType(segment)}:${id}`;\n // depth \u2265 3 \u4E0D\u62C9\u53D6\uFF1B\u5DF2\u5C55\u5F00\u8FC7\u7684 id\uFF08\u542B\u81EA\u73AF\uFF09\u4E0D\u518D\u8FDB\u5165\uFF0C\u7EDF\u4E00\u7559\u5360\u4F4D\n if (depth >= PARSE_LIMITS.maxDepth || current.visitedIds.has(key)) {\n parts.push(FORWARD_PLACEHOLDER);\n break;\n }\n current.visitedIds.add(key);\n parts.push(\n segmentType(segment) === 'reply'\n ? await parseReply(id, depth, resolvers, images, current)\n : await parseForward(id, depth, resolvers, images, current)\n );\n break;\n }\n default:\n break;\n }\n }\n\n return parts.join('').trim();\n}\n\n/**\n * \u89E3\u6790\u6574\u6761\u6D88\u606F\uFF08\u89E6\u53D1\u6D88\u606F\u672C\u8EAB\u4E3A depth 0\uFF09\uFF1A\n * - \u975E\u6570\u7EC4\u8F93\u5165\uFF08string/undefined \u7B49\uFF09\u2192 text \u4E3A\u8BE5\u5B57\u7B26\u4E32\u6216 ''\uFF0Cimages \u4E3A []\uFF1B\n * - `groupId` \u4E3A undefined\uFF08\u79C1\u804A\uFF09\u65F6 @\u63D0\u53CA \u4E0D\u67E5\u6635\u79F0\uFF0C\u76F4\u63A5\u56DE\u9000 `@QQ\u53F7`\uFF1B\n * - \u56FE\u7247\u6536\u96C6\u4E0A\u9650\u3001\u6BB5/\u8282\u70B9\u4E0A\u9650\u4E0E visitedIds \u53BB\u91CD\u89C1 PARSE_LIMITS\u3002\n */\nexport async function parseMessage(\n message: unknown,\n groupId: number | undefined,\n resolvers: MessageResolvers\n): Promise<ParsedMessage> {\n const images: string[] = [];\n if (typeof message === 'string') return { text: message, images };\n if (!Array.isArray(message)) return { text: '', images };\n const state = createParseState(groupId !== undefined);\n const text = await parseSegments(message, 0, resolvers, images, state);\n return { text, images };\n}\n", "import { ApprovalChoice } from '@/features/hermes/types';\n\n/**\n * Hermes \u89E6\u53D1\u5224\u5B9A\u4E0E\u6587\u672C\u5904\u7406\u7684\u7EAF\u51FD\u6570\uFF08\u62BD\u79BB\u81EA HermesBridge \u4EE5\u4FBF\u5192\u70DF\u6D4B\u8BD5\uFF09\u3002\n */\n\nexport interface GroupTriggerOptions {\n /** \u6D88\u606F\u7EAF\u6587\u672C */\n text: string;\n /** \u662F\u5426 @\u4E86 bot */\n mentioned: boolean;\n /** \u7FA4\u804A\u4E2D\u662F\u5426\u9700\u8981 @bot \u624D\u89E6\u53D1 */\n requireMention: boolean;\n /** \u5173\u952E\u8BCD\u89E6\u53D1\u5217\u8868\uFF08\u9700\u5DF2\u8F6C\u5C0F\u5199\uFF09 */\n keywordTriggers: string[];\n}\n\nexport interface TriggerDecision {\n triggered: boolean;\n reason?: string;\n}\n\n/**\n * \u7FA4\u804A\u89E6\u53D1\u5224\u5B9A\u3002\n * \u76F8\u5BF9\u4E0A\u6E38\u65B0\u589E\u7684\u5B88\u536B\uFF1A\u4EE5 / \u6216 ! \u5F00\u5934\u7684\u6D88\u606F\u89C6\u4E3A\u547D\u4EE4\uFF08\u4EA4\u7ED9 gugle-command\uFF09\uFF0C\n * \u9664\u975E\u663E\u5F0F @bot\uFF0C\u5426\u5219\u4E0D\u89E6\u53D1 AI\uFF0C\u907F\u514D\u4E0E\u547D\u4EE4\u7CFB\u7EDF\u53CC\u91CD\u54CD\u5E94\u3002\n */\nexport function decideGroupTrigger(options: GroupTriggerOptions): TriggerDecision {\n const text = options.text.trim();\n if (options.mentioned) return { triggered: true, reason: 'mention' };\n if (text.startsWith('/') || text.startsWith('!')) return { triggered: false };\n const lower = text.toLowerCase();\n for (const keyword of options.keywordTriggers) {\n if (lower.includes(keyword)) return { triggered: true, reason: `keyword:${keyword}` };\n }\n if (!options.requireMention) return { triggered: true, reason: 'bare' };\n return { triggered: false };\n}\n\n/**\n * \u89E3\u6790\u5BA1\u6279\u56DE\u590D\u6587\u672C\u4E3A\u5BA1\u6279\u9009\u62E9\u3002\n * \u6CE8\u610F\u5339\u914D\u987A\u5E8F\uFF1A\u59CB\u7EC8\u5141\u8BB8 > \u672C\u6B21\u5141\u8BB8 > \u62D2\u7EDD > \u6279\u51C6\u2014\u2014\n * \u300C\u4E0D\u5141\u8BB8/\u4E0D\u6279\u51C6\u300D\u5305\u542B\u300C\u5141\u8BB8/\u6279\u51C6\u300D\uFF0C\u4E0A\u6E38\u5148\u5339\u914D\u6279\u51C6\u5BFC\u81F4\u5426\u5B9A\u8BCD\u4E0D\u53EF\u8FBE\uFF0C\u6B64\u5904\u4FEE\u6B63\u3002\n */\nexport function parseApprovalChoice(text: string): ApprovalChoice | null {\n const lower = text.toLowerCase().trim();\n if (['\u59CB\u7EC8\u5141\u8BB8', 'always', '\u59CB\u7EC8\u6279\u51C6', '\u5168\u90E8\u5141\u8BB8'].some(keyword => lower.includes(keyword))) return 'always';\n if (['\u672C\u6B21\u5141\u8BB8', 'session'].some(keyword => lower.includes(keyword))) return 'session';\n if (['\u62D2\u7EDD', 'deny', '\u4E0D\u6279\u51C6', '\u4E0D\u5141\u8BB8'].some(keyword => lower.includes(keyword))) return 'deny';\n if (['\u6279\u51C6', '\u901A\u8FC7', 'approve', 'ok', '\u5141\u8BB8'].some(keyword => lower.includes(keyword))) return 'once';\n return null;\n}\n\n/** \u6309\u6700\u5927\u957F\u5EA6\u5207\u5206\u6D88\u606F\uFF08\u4F18\u5148\u5728\u6362\u884C/\u7A7A\u683C\u5904\u65AD\u5F00\uFF09 */\nexport function splitMessageText(text: string, maxLength: number): string[] {\n if (maxLength <= 0 || text.length <= maxLength) return [text];\n const chunks: string[] = [];\n let remaining = text;\n while (remaining.length > 0) {\n if (remaining.length <= maxLength) {\n chunks.push(remaining);\n break;\n }\n let splitIndex = remaining.lastIndexOf('\\n', maxLength);\n if (splitIndex < maxLength * 0.3) {\n splitIndex = remaining.lastIndexOf(' ', maxLength);\n }\n if (splitIndex < maxLength * 0.3) {\n splitIndex = maxLength;\n }\n chunks.push(remaining.slice(0, splitIndex));\n remaining = remaining.slice(splitIndex).trimStart();\n }\n return chunks;\n}\n\n/** \u5224\u65AD\u6587\u672C\u662F\u5426\u4E3A\u505C\u6B62\u6307\u4EE4 */\nexport function isStopCommand(text: string): boolean {\n return text === '\u505C\u6B62' || text.toLowerCase() === 'stop';\n}\n\n/** \u5224\u65AD\u6587\u672C\u662F\u5426\u4E3A\u6E05\u9664\u4E0A\u4E0B\u6587\u6307\u4EE4 */\nexport function isResetCommand(text: string): boolean {\n const lower = text.toLowerCase();\n return text === '\u6E05\u9664\u4E0A\u4E0B\u6587' || text === '\u65B0\u5BF9\u8BDD' || lower === 'new' || lower === 'reset';\n}\n", "import { splitMessageText } from '@/features/hermes/trigger';\n\n/**\n * QQ \u4FA7 AI \u56DE\u590D\u7684\u5206\u53E5\u4E0E\u300C\u662F\u5426\u6539\u7528\u804A\u5929\u8BB0\u5F55\uFF08\u5408\u5E76\u8F6C\u53D1\uFF09\u53D1\u9001\u300D\u5224\u5B9A\uFF08\u7EAF\u51FD\u6570\uFF0C\u62BD\u79BB\u81EA HermesBridge \u4EE5\u4FBF\u5192\u70DF\u6D4B\u8BD5\uFF09\u3002\n *\n * - `splitReplyIntoUnits`\uFF1A\u4E00\u6B21\u6027\u628A\u5B8C\u6574\u56DE\u590D\u5207\u6210\u8BED\u4E49\u5355\u5143\uFF08\u8FD0\u884C\u7ED3\u675F\u65F6\u4F7F\u7528\uFF09\uFF1B\n * - `takeCompleteUnits`\uFF1A\u6D41\u5F0F\u573A\u666F\u4E0B\u53EA\u53D6\u300C\u5DF2\u5B8C\u6574\u300D\u7684\u5355\u5143\uFF0C\u672A\u6210\u5F62\u7684\u5185\u5BB9\u7559\u5728 rest\uFF0C\n * \u5C24\u5176\u8981\u4FDD\u4F4F `[SKILL:` / `MEDIA:` \u6807\u7B7E\u4E0E\u672A\u95ED\u5408\u7684\u4EE3\u7801\u56F4\u680F\uFF0C\u9632\u6B62\u534A\u622A\u6807\u7B7E\u88AB\u539F\u6837\u53D1\u5230\u7FA4\u91CC\u3002\n *\n * \u672C\u6A21\u5757\u4E0D\u4EA7\u751F\u4EFB\u4F55 IO\u3002\n */\n\nexport interface ReplyUnitOptions {\n /** \u5355\u5143\u6700\u5927\u957F\u5EA6\uFF1A\u8D85\u8FC7\u5219\u7528\u65E2\u6709 splitMessageText \u4E8C\u6B21\u5207\u5206\uFF08\u901A\u5E38\u4F20 config.maxMessageLength\uFF09 */\n maxUnitLength: number;\n /** \u5355\u6761\u8D85\u8FC7\u8BE5\u5B57\u6570\u6539\u7528\u804A\u5929\u8BB0\u5F55\u53D1\u9001\uFF1B0 = \u5173\u95ED\u8BE5\u7EF4\u5EA6 */\n compactMaxChars: number;\n /** \u5355\u6761\u8D85\u8FC7\u8BE5\u884C\u6570\u6539\u7528\u804A\u5929\u8BB0\u5F55\u53D1\u9001\uFF1B0 = \u5173\u95ED\u8BE5\u7EF4\u5EA6 */\n compactMaxLines: number;\n}\n\nexport interface ReplyUnit {\n text: string;\n asForward: boolean;\n}\n\n/** \u53E5\u5B50\u7ED3\u675F\u6807\u70B9\uFF1A\u547D\u4E2D\u540E\u8FDE\u540C\u5176\u540E\u7684\u6536\u5C3E\u5F15\u53F7/\u62EC\u53F7\u4E00\u8D77\u5207\u8D70 */\nconst SENTENCE_ENDINGS = '\u3002\uFF01\uFF1F!?\uFF1B;\u2026';\n/** \u53E5\u672B\u6807\u70B9\u540E\u53EF\u4E00\u5E76\u5207\u8D70\u7684\u6536\u5C3E\u5F15\u53F7\u4E0E\u62EC\u53F7 */\nconst CLOSING_MARKS = '\u201D\u2019\"\\'\u300D\u300F\uFF09)\u3011]\u300B\u3009';\n/** \u6280\u80FD/\u5A92\u4F53\u6807\u7B7E\uFF1A\u547D\u4E2D\u5373\u628A\u8BE5\u5355\u5143\u53CA\u5176\u540E\u7684\u5185\u5BB9\u7559\u5728 rest\uFF0C\u4EA4\u7531\u8C03\u7528\u65B9\u5728\u8FD0\u884C\u7ED3\u675F\u65F6\u7EDF\u4E00\u5904\u7406 */\nconst RESERVED_LABEL_PATTERN = /\\[SKILL:|MEDIA:/;\n\n/** \u5355\u5143\u5728\u7F13\u51B2\u533A\u4E2D\u7684\u4F4D\u7F6E\uFF08start/end \u6307\u5411\u539F\u59CB\u7F13\u51B2\u533A\u4E0B\u6807\uFF0Ctext \u5DF2\u88C1\u526A\u9996\u5C3E\u7A7A\u767D\uFF09 */\ninterface ScannedUnit {\n text: string;\n start: number;\n end: number;\n}\n\n/** \u662F\u5426\u4E3A\u7A7A\u767D\u5B57\u7B26\uFF08\\s \u8BED\u4E49\uFF0C\u542B\u5236\u8868\u7B26/\u6362\u884C/\u4E0D\u6362\u884C\u7A7A\u683C\uFF09 */\nfunction isWhitespace(char: string): boolean {\n return char !== '' && /\\s/.test(char);\n}\n\n/** \u662F\u5426\u4E3A\u884C\u5185\u7F29\u8FDB\u5B57\u7B26\uFF08\u5217\u8868\u7EED\u884C\u3001\u56F4\u680F\u7F29\u8FDB\u5224\u5B9A\u7528\uFF09 */\nfunction isIndent(char: string): boolean {\n return char === ' ' || char === '\\t' || char === '\\r';\n}\n\n/** \u4ECE from \u8D77\u8DF3\u8FC7\u5168\u90E8\u7A7A\u767D\uFF0C\u8FD4\u56DE\u9996\u4E2A\u975E\u7A7A\u767D\u5B57\u7B26\u4E0B\u6807 */\nfunction skipWhitespace(source: string, from: number): number {\n let index = from;\n while (index < source.length && isWhitespace(source[index] ?? '')) index++;\n return index;\n}\n\n/** index \u662F\u5426\u4E3A\u884C\u9996\u5185\u5BB9\u4F4D\uFF08\u5176\u524D\u7F00\u53EA\u6709\u7F29\u8FDB\uFF0C\u5373\u5217\u8868\u9879/\u56F4\u680F\u53EF\u4EE5\u51FA\u73B0\u7684\u4F4D\u7F6E\uFF09 */\nfunction isLineHead(source: string, index: number): boolean {\n let cursor = index - 1;\n while (cursor >= 0 && isIndent(source[cursor] ?? '')) cursor--;\n return cursor < 0 || source[cursor] === '\\n';\n}\n\n/** \u5217\u8868\u9879\u524D\u7F00\u957F\u5EA6\uFF08`- ` / `* ` / `+ ` / `1. ` / `1\u3001` \u7B49\uFF09\uFF1B\u975E\u5217\u8868\u9879\u8FD4\u56DE 0 */\nfunction listPrefixLength(source: string, index: number): number {\n const char = source[index] ?? '';\n if (char === '-' || char === '*' || char === '+') {\n const next = source[index + 1] ?? '';\n return next === ' ' || next === '\\t' ? 2 : 0;\n }\n if (char >= '0' && char <= '9') {\n let cursor = index;\n while (cursor < source.length && (source[cursor] ?? '') >= '0' && (source[cursor] ?? '') <= '9') cursor++;\n const separator = source[cursor] ?? '';\n if (separator !== '.' && separator !== '\u3001' && separator !== ')' && separator !== '\uFF09') return 0;\n const after = source[cursor + 1] ?? '';\n return cursor - index + 1 + (after === ' ' || after === '\\t' ? 1 : 0);\n }\n return 0;\n}\n\n/** \u7EDF\u8BA1 index \u8D77\u8FDE\u7EED\u76F8\u540C\u5B57\u7B26\u7684\u957F\u5EA6 */\nfunction countRun(source: string, index: number, char: string): number {\n let cursor = index;\n while (cursor < source.length && source[cursor] === char) cursor++;\n return cursor - index;\n}\n\n/**\n * ASCII \u53E5\u70B9\u662F\u5426\u53EF\u4F5C\u4E3A\u53E5\u672B\u8FB9\u754C\u3002\n *\n * \u53EA\u5728\u300C\u5176\u540E\u662F\u7A7A\u767D\u6216\u7F13\u51B2\u533A\u672B\u5C3E\u300D\u4E14\u300C\u524D\u4E00\u5B57\u7B26\u4E0D\u662F\u6570\u5B57\u300D\u65F6\u6210\u7ACB\uFF0C\u907F\u514D\u628A\u5C0F\u6570\uFF083.14\uFF09\u3001\n * \u7248\u672C\u53F7\uFF08v1.0\uFF09\u3001IP\uFF08192.168.1.1\uFF09\u4E0E\u57DF\u540D\uFF08example.com\uFF09\u5207\u788E\u2014\u2014\u4E2D\u6587\u573A\u666F\u4E0B\u8FD9\u4E9B\u6BD4\n * \u82F1\u6587\u53E5\u672B\u51FA\u73B0\u5F97\u66F4\u9891\u7E41\u3002\u7F13\u51B2\u533A\u672B\u5C3E\u7684\u53E5\u70B9\u89C6\u4E3A\u5B8C\u6574\u53E5\uFF08\u4E0B\u4E00\u6BB5\u6587\u672C\u4F1A\u91CD\u65B0\u62FC\u63A5\uFF09\u3002\n */\nfunction isAsciiPeriodBoundary(source: string, index: number): boolean {\n const previous = index > 0 ? (source[index - 1] ?? '') : '';\n if (previous >= '0' && previous <= '9') return false;\n const next = source[index + 1];\n if (next === undefined) return true;\n return isWhitespace(next);\n}\n\n/** index \u662F\u5426\u4E3A\u56F4\u680F\u884C\u9996\uFF08Markdown \u5141\u8BB8\u81F3\u591A 3 \u4E2A\u7A7A\u683C\u7F29\u8FDB\uFF09 */\nfunction isFenceHead(source: string, index: number): boolean {\n let cursor = index - 1;\n let spaces = 0;\n while (cursor >= 0 && isIndent(source[cursor] ?? '')) {\n if (source[cursor] !== '\\r') spaces++;\n cursor--;\n }\n if (spaces > 3) return false;\n return cursor < 0 || source[cursor] === '\\n';\n}\n\n/** start~stop \u8FD9\u4E00\u884C\u662F\u5426\u4E3A\u95ED\u5408\u56F4\u680F\uFF08\u540C\u7C7B\u6807\u8BB0\u4E14\u4E0D\u5C11\u4E8E\u5F00\u542F\u957F\u5EA6\uFF0C\u884C\u5185\u65E0\u5176\u4ED6\u5185\u5BB9\uFF09 */\nfunction isClosingFence(source: string, start: number, stop: number, marker: string, minLength: number): boolean {\n let cursor = start;\n while (cursor < stop && isIndent(source[cursor] ?? '')) cursor++;\n let count = 0;\n while (cursor < stop && source[cursor] === marker) {\n count++;\n cursor++;\n }\n if (count < minLength) return false;\n while (cursor < stop && isIndent(source[cursor] ?? '')) cursor++;\n return cursor >= stop;\n}\n\n/**\n * \u67E5\u627E\u56F4\u680F\u5757\u7ED3\u675F\u4E0B\u6807\uFF08\u4E0D\u542B\uFF09\uFF0C\u672A\u95ED\u5408\u6216\u56F4\u680F\u884C\u5C1A\u672A\u7ED3\u675F\u65F6\u8FD4\u56DE -1\u3002\n * \u8FD4\u56DE\u4F4D\u7F6E\u5DF2\u5305\u542B\u95ED\u5408\u56F4\u680F\u884C\u53CA\u5176\u6362\u884C\u7B26\u3002\n */\nfunction findFenceEnd(source: string, index: number, marker: string, markerLength: number): number {\n const lineEnd = source.indexOf('\\n', index);\n if (lineEnd < 0) return -1;\n let cursor = lineEnd + 1;\n while (cursor < source.length) {\n const stop = source.indexOf('\\n', cursor);\n const lineStop = stop < 0 ? source.length : stop;\n if (isClosingFence(source, cursor, lineStop, marker, markerLength)) {\n return stop < 0 ? source.length : stop + 1;\n }\n if (stop < 0) return -1;\n cursor = stop + 1;\n }\n return -1;\n}\n\n/**\n * \u67E5\u627E\u8FDE\u7EED\u5217\u8868\u5757\u7684\u7ED3\u675F\u4E0B\u6807\uFF08\u542B\u6700\u540E\u4E00\u884C\u7684\u6362\u884C\u7B26\uFF09\u3002\n * complete=false \u8868\u793A\u5217\u8868\u5757\u4E00\u76F4\u5EF6\u4F38\u5230\u7F13\u51B2\u533A\u672B\u5C3E\u3001\u4ECD\u53EF\u80FD\u7EE7\u7EED\u589E\u957F\uFF08\u6D41\u5F0F\u573A\u666F\u9700\u7559\u5728 rest\uFF09\u3002\n */\nfunction findListBlockEnd(source: string, start: number): { end: number; complete: boolean } {\n const length = source.length;\n let cursor = start;\n while (cursor < length) {\n const lineEnd = source.indexOf('\\n', cursor);\n if (lineEnd < 0) return { end: length, complete: false };\n const end = lineEnd + 1;\n const next = end;\n if (next >= length) return { end, complete: false };\n if ((source[next] ?? '') === '\\n') return { end, complete: true };\n // \u7F29\u8FDB\u7EED\u884C\u6216\u540C\u7C7B\u5217\u8868\u9879 \u2192 \u5E76\u5165\u540C\u4E00\u5355\u5143\uFF1B\u666E\u901A\u65B0\u6BB5\u843D \u2192 \u5217\u8868\u5757\u7ED3\u675F\n if (!isLineHead(source, next) || listPrefixLength(source, next) > 0) {\n cursor = next;\n continue;\n }\n return { end, complete: true };\n }\n return { end: length, complete: false };\n}\n\n/**\n * \u626B\u63CF\u7F13\u51B2\u533A\u5E76\u5207\u51FA\u8BED\u4E49\u5355\u5143\u3002\n *\n * \u8FB9\u754C\u89C4\u5219\uFF1A\u53E5\u672B\u6807\u70B9\uFF08\u542B\u5176\u540E\u6536\u5C3E\u5F15\u53F7/\u62EC\u53F7\uFF09\u3001\u7A7A\u884C\u3001\u6362\u884C\u3001\u4EE3\u7801\u56F4\u680F\u3001\u8FDE\u7EED\u5217\u8868\u5757\u3002\n * final=true \u65F6\u672B\u6BB5\u5373\u4F7F\u6CA1\u6709\u8FB9\u754C\u4E5F\u4F5C\u4E3A\u6700\u540E\u4E00\u4E2A\u5355\u5143\uFF1Bfinal=false \u65F6\u672A\u6210\u5F62\u5185\u5BB9\u5168\u90E8\u7559\u5728\n * consumed \u4E4B\u540E\uFF08\u7531 takeCompleteUnits \u4F5C\u4E3A rest \u8FD4\u56DE\uFF09\uFF0C\u672A\u95ED\u5408\u7684\u56F4\u680F\u4EA6\u4E0D\u4E0B\u53D1\u3002\n */\nfunction scanUnits(source: string, final: boolean): { units: ScannedUnit[]; consumed: number } {\n const units: ScannedUnit[] = [];\n const length = source.length;\n let consumed = 0;\n let unitStart = skipWhitespace(source, 0);\n let index = unitStart;\n\n /** \u628A [unitStart, end) \u4F5C\u4E3A\u4E00\u4E2A\u5355\u5143\u6536\u4E0B\uFF0C\u5E76\u8DF3\u5230\u5176\u540E\u7684\u9996\u4E2A\u975E\u7A7A\u767D\u5B57\u7B26 */\n const emit = (end: number): void => {\n const text = source.slice(unitStart, end).trim();\n if (text) units.push({ text, start: unitStart, end });\n index = skipWhitespace(source, end);\n unitStart = index;\n if (units.length > 0) consumed = index;\n };\n\n while (index < length) {\n const char = source[index] ?? '';\n\n // \u4EE3\u7801\u56F4\u680F\uFF08``` / ~~~\uFF09\uFF1A\u6574\u5757\u539F\u5B50\uFF0C\u672A\u95ED\u5408\u65F6\u505C\u6B62\u626B\u63CF\uFF08\u975E\u6700\u7EC8\u6A21\u5F0F\u7559\u7ED9 rest\uFF09\n if ((char === '`' || char === '~') && isFenceHead(source, index)) {\n const runLength = countRun(source, index, char);\n if (runLength >= 3) {\n const fenceEnd = findFenceEnd(source, index, char, runLength);\n if (fenceEnd >= 0) {\n emit(fenceEnd);\n continue;\n }\n if (final) emit(length);\n break;\n }\n }\n\n // \u5217\u8868\u5757\uFF1A\u8FDE\u7EED\u5217\u8868\u9879\u884C\u5408\u5E76\u4E3A\u4E00\u4E2A\u5355\u5143\uFF0C\u907F\u514D\u9010\u6761\u5237\u5C4F\n if (\n (char === '-' || char === '*' || char === '+' || (char >= '0' && char <= '9')) &&\n isLineHead(source, index) &&\n listPrefixLength(source, index) > 0\n ) {\n const block = findListBlockEnd(source, index);\n if (block.complete || final) {\n emit(block.end);\n continue;\n }\n break;\n }\n\n // \u53E5\u672B\u6807\u70B9\uFF1A\u8FDE\u540C\u540E\u7EED\u7684\u6536\u5C3E\u5F15\u53F7/\u62EC\u53F7\u4E00\u8D77\u5207\u8D70\n if (SENTENCE_ENDINGS.includes(char) || (char === '.' && isAsciiPeriodBoundary(source, index))) {\n let end = index + 1;\n while (end < length && SENTENCE_ENDINGS.includes(source[end] ?? '')) end++;\n while (end < length && CLOSING_MARKS.includes(source[end] ?? '')) end++;\n emit(end);\n continue;\n }\n\n // \u6362\u884C\uFF1A\u6BB5\u843D\u8FB9\u754C\uFF1B\u7F13\u51B2\u533A\u4EE5\u6362\u884C\u7ED3\u5C3E\u65F6\u89C6\u4E3A\u300C\u5C1A\u672A\u6210\u5F62\u300D\uFF0C\u7B49\u540E\u7EED\u5185\u5BB9\u4E00\u5E76\u5224\u5B9A\n if (char === '\\n') {\n if (index + 1 < length || final) {\n emit(index);\n continue;\n }\n break;\n }\n\n index++;\n }\n\n // \u6700\u7EC8\u6A21\u5F0F\uFF1A\u672B\u6BB5\u6CA1\u6709\u8FB9\u754C\u4E5F\u4F5C\u4E3A\u6700\u540E\u4E00\u4E2A\u5355\u5143\uFF08\u5426\u5219\u8C03\u7528\u65B9\u4F1A\u4E22\u5B57\uFF09\n if (final && unitStart < length) {\n const text = source.slice(unitStart).trim();\n if (text) units.push({ text, start: unitStart, end: length });\n consumed = length;\n }\n\n return { units, consumed };\n}\n\n/** \u5355\u6761\u662F\u5426\u5E94\u6539\u7528\u804A\u5929\u8BB0\u5F55\uFF08\u5408\u5E76\u8F6C\u53D1\uFF09\u53D1\u9001\uFF1A\u4EFB\u4E00\u7EF4\u5EA6\u8D85\u9650\u5373\u547D\u4E2D\uFF0C0 \u8868\u793A\u5173\u95ED\u8BE5\u7EF4\u5EA6 */\nexport function shouldCompact(text: string, options: ReplyUnitOptions): boolean {\n if (options.compactMaxChars > 0 && text.length > options.compactMaxChars) return true;\n if (options.compactMaxLines > 0 && text.split('\\n').length > options.compactMaxLines) return true;\n return false;\n}\n\n/** \u8D85\u957F\u5355\u5143\u5148\u7528\u65E2\u6709 splitMessageText \u4E8C\u6B21\u5207\u5206\uFF08maxUnitLength<=0 \u65F6\u4E0D\u5207\uFF09 */\nfunction splitByLength(text: string, maxUnitLength: number): string[] {\n if (maxUnitLength <= 0) return [text];\n return splitMessageText(text, maxUnitLength);\n}\n\n/** \u6587\u672C\u5217\u8868 \u2192 \u56DE\u590D\u5355\u5143\uFF1A\u9010\u5757\u72EC\u7ACB\u5224\u5B9A asForward\uFF0C\u7A7A\u5757\u4E0D\u4EA7\u51FA\u5355\u5143 */\nfunction toReplyUnits(texts: string[], options: ReplyUnitOptions): ReplyUnit[] {\n const units: ReplyUnit[] = [];\n for (const text of texts) {\n for (const piece of splitByLength(text, options.maxUnitLength)) {\n const trimmed = piece.trim();\n if (!trimmed) continue;\n units.push({ text: trimmed, asForward: shouldCompact(trimmed, options) });\n }\n }\n return units;\n}\n\n/**\n * \u628A\u5B8C\u6574\u56DE\u590D\u5207\u6210\u56DE\u590D\u5355\u5143\uFF08\u6700\u7EC8\u6A21\u5F0F\uFF1A\u8FD0\u884C\u7ED3\u675F\u3001\u6807\u7B7E\u5DF2\u5904\u7406\u5B8C\u65F6\u4F7F\u7528\uFF09\u3002\n *\n * \u4E0E takeCompleteUnits \u7684\u533A\u522B\u662F\u300C\u672B\u6BB5\u4E00\u5F8B\u6210\u5F62\u300D\uFF1A\u7F13\u51B2\u533A\u4EE5\u672A\u95ED\u5408\u56F4\u680F\u7ED3\u5C3E\u65F6\uFF0C\u6574\u5757\uFF08\u542B\u56F4\u680F\u884C\uFF09\n * \u4F5C\u4E3A\u4E00\u4E2A\u5355\u5143\u53D1\u51FA\uFF1B\u672B\u5C3E\u6CA1\u6709\u8FB9\u754C\u7684\u6B8B\u53E5\u4E5F\u7167\u53D1\u3002\u8C03\u7528\u65B9\u7528\u5B83\u5BF9 rest \u505A\u6536\u5C3E flush \u65F6\u4E0D\u4F1A\u4E22\u5B57\uFF0C\n * \u9664\u975E\u8F93\u5165\u672C\u8EAB\u4E3A\u7A7A/\u7EAF\u7A7A\u767D\uFF08\u6B64\u65F6\u8FD4\u56DE []\uFF09\u3002\n * \u5355\u5143\u957F\u5EA6\u8D85\u8FC7 maxUnitLength \u65F6\u6309 splitMessageText \u4E8C\u6B21\u5207\u5206\uFF0C\u62FC\u63A5\u7ED3\u679C\u4E0E\u8F93\u5165\u4E00\u81F4\uFF08\u4EC5\u88C1\u526A\u7A7A\u767D\uFF09\u3002\n */\nexport function splitReplyIntoUnits(text: string, options: ReplyUnitOptions): ReplyUnit[] {\n if (!text) return [];\n return toReplyUnits(\n scanUnits(text, true).units.map(unit => unit.text),\n options\n );\n}\n\n/**\n * \u6D41\u5F0F\u53D6\u300C\u5DF2\u5B8C\u6574\u300D\u7684\u56DE\u590D\u5355\u5143\uFF1A\u7F13\u51B2\u533A\u672B\u5C3E\u672A\u6210\u5F62\u7684\u5185\u5BB9\uFF08\u542B\u672A\u95ED\u5408\u56F4\u680F\uFF09\u7559\u5728 rest\uFF0C\n * \u7531\u8C03\u7528\u65B9\u5728\u8FD0\u884C\u7ED3\u675F\u65F6\u7EDF\u4E00\u5904\u7406\u3002\n *\n * \u6807\u7B7E\u4FDD\u7559\uFF1A\u4E00\u65E6\u67D0\u4E2A\u5355\u5143\u547D\u4E2D `[SKILL:` \u6216 `MEDIA:`\uFF08\u5B50\u4E32\u5339\u914D\uFF09\uFF0C\u4ECE\u8BE5\u5355\u5143\u8D77\uFF08\u542B\u8BE5\u5355\u5143\uFF09\n * \u53CA\u5176\u540E\u7684\u5168\u90E8\u5185\u5BB9\u90FD\u7559\u5728 rest\u2014\u2014\u5B81\u53EF\u591A\u7559\uFF0C\u4E5F\u7EDD\u4E0D\u8BA9\u6807\u7B7E\u7ECF\u6D41\u5F0F\u8DEF\u5F84\u88AB\u539F\u6837\u53D1\u5230\u7FA4\u91CC\u3002\n * \u6CE8\u610F rest \u53EA\u4FDD\u8BC1\u4E0D\u4E22\u5B57\uFF0C\u4E0D\u7BA1\u6807\u7B7E\u662F\u5426\u95ED\u5408\uFF1B\u6807\u7B7E\u7684\u5904\u7406\u4E0E\u6700\u7EC8\u4E0B\u53D1\u7531\u8C03\u7528\u65B9\u8D1F\u8D23\u3002\n */\nexport function takeCompleteUnits(buffer: string, options: ReplyUnitOptions): { units: ReplyUnit[]; rest: string } {\n if (!buffer) return { units: [], rest: '' };\n const scanned = scanUnits(buffer, false);\n\n let cut = scanned.units.length;\n let restStart = scanned.consumed;\n for (let index = 0; index < scanned.units.length; index++) {\n const unit = scanned.units[index]!;\n if (!RESERVED_LABEL_PATTERN.test(unit.text)) continue;\n cut = index;\n restStart = unit.start;\n break;\n }\n\n return {\n units: toReplyUnits(\n scanned.units.slice(0, cut).map(unit => unit.text),\n options\n ),\n rest: buffer.slice(restStart)\n };\n}\n", "import fs from 'node:fs';\nimport path from 'node:path';\nimport { QQBot } from '@/index';\nimport { getHermesConfig } from '@/features/hermes/config';\nimport { writeConfigFile } from '@/config/manager';\n\n/**\n * AI\u300C\u5904\u7406\u4E2D\u300D\u8868\u60C5\u56DE\u5E94\u7BA1\u7406\u3002\n *\n * \u7528\u6237\u6D88\u606F\u89E6\u53D1 Hermes \u8FD0\u884C\u65F6\uFF0C\u7ED9\u89E6\u53D1\u6D88\u606F\u8D34\u4E0A\u8868\u60C5\u56DE\u5E94\uFF08QQ \u8D70 NapCat\n * set_msg_emoji_like\uFF0CDiscord \u8D70 message.react\uFF09\uFF1B\u5B8C\u6574\u56DE\u590D\u5B8C\u6210\uFF08\u542B\u67E5\u8BE2\u6280\u80FD\u7684\n * \u56DE\u5582\u8F6E\uFF09\u540E\u6458\u9664\u3002\n *\n * \u8BB0\u5F55\u7ACB\u5373\u6301\u4E45\u5316\u5230 data/hermes-reactions.json\uFF1A\u82E5\u56DE\u5E94\u671F\u95F4\u8FDB\u7A0B\u91CD\u542F/\u5B95\u673A\uFF0C\n * \u4E0B\u6B21\u542F\u52A8\u65F6\u6309\u6B8B\u7559\u8BB0\u5F55\u9010\u6761\u6458\u9664\u5E76\u6E05\u7A7A\uFF08cleanupQQ \u7531 after-start \u94A9\u5B50\u8C03\u7528\uFF0C\n * Discord \u6B8B\u7559\u7531 DiscordBridge \u5728 clientReady \u540E\u8C03\u7528 listDiscord/drop \u5904\u7406\uFF09\uFF0C\n * \u907F\u514D\u300C\u5904\u7406\u4E2D\u300D\u8868\u60C5\u6C38\u4E45\u6B8B\u7559\u3002\n */\n\n/** \u4E00\u6761\u5F85\u6458\u9664\u7684\u8868\u60C5\u56DE\u5E94\u8BB0\u5F55 */\nexport interface PendingReaction {\n /** \u552F\u4E00\u952E\uFF1A`qq:{messageId}` / `discord:{channelId}:{messageId}` */\n key: string;\n platform: 'qq' | 'discord';\n /** QQ: OneBot message_id \u5B57\u7B26\u4E32\uFF1BDiscord: \u6D88\u606F snowflake */\n messageId: string;\n /** Discord \u9891\u9053 snowflake\uFF08QQ \u8BB0\u5F55\u65E0\u6B64\u5B57\u6BB5\uFF09 */\n channelId?: string;\n /** QQ: emoji_id\uFF1BDiscord: unicode emoji */\n emoji: string;\n createdAt: number;\n}\n\ninterface ReactionStoreFile {\n version: number;\n reactions: PendingReaction[];\n}\n\nexport class ReactionManager {\n private static instance?: ReactionManager;\n\n public static getInstance(): ReactionManager {\n if (!ReactionManager.instance) ReactionManager.instance = new ReactionManager();\n return ReactionManager.instance;\n }\n\n private readonly file = path.resolve(process.cwd(), 'data', 'hermes-reactions.json');\n private readonly records = new Map<string, PendingReaction>();\n private loaded = false;\n\n private constructor() {}\n\n /** \u61D2\u52A0\u8F7D\u6301\u4E45\u5316\u8BB0\u5F55\uFF08\u635F\u574F\u6309\u7A7A\u5904\u7406\uFF09 */\n private load(): void {\n if (this.loaded) return;\n this.loaded = true;\n try {\n if (!fs.existsSync(this.file)) return;\n const raw = JSON.parse(fs.readFileSync(this.file, 'utf-8')) as ReactionStoreFile;\n if (!Array.isArray(raw?.reactions)) return;\n for (const record of raw.reactions) {\n if (record && typeof record.key === 'string' && typeof record.messageId === 'string') {\n this.records.set(record.key, record);\n }\n }\n } catch {\n // \u6587\u4EF6\u635F\u574F\u6216\u683C\u5F0F\u9519\u8BEF\uFF0C\u6309\u7A7A\u8BB0\u5F55\u5904\u7406\n }\n }\n\n /** \u7ACB\u5373\u843D\u76D8\uFF08\u539F\u5B50\u5199\uFF0C\u590D\u7528 config/manager \u7684 writeConfigFile\uFF09 */\n private persist(): void {\n try {\n writeConfigFile(this.file, { version: 1, reactions: [...this.records.values()] } satisfies ReactionStoreFile);\n } catch {\n // \u6301\u4E45\u5316\u5931\u8D25\u4E0D\u963B\u585E\u4E3B\u6D41\u7A0B\n }\n }\n\n /**\n * QQ\uFF1A\u7ED9\u6D88\u606F\u8D34\u300C\u5904\u7406\u4E2D\u300D\u8868\u60C5\uFF0C\u6210\u529F\u540E\u7ACB\u5373\u6301\u4E45\u5316\u8BB0\u5F55\u5E76\u8FD4\u56DE\u8BB0\u5F55\u952E\u3002\n * \u8FD4\u56DE null \u8868\u793A\u6CA1\u8D34\u4E0A\uFF08\u529F\u80FD\u5173\u95ED\u6216 NapCat \u62D2\u7EDD\uFF0C\u4F8B\u5982\u79C1\u804A\u6D88\u606F\u901A\u5E38\u4E0D\u652F\u6301\u8868\u60C5\u56DE\u5E94\uFF09\uFF0C\n * \u8C03\u7528\u65B9\u636E\u6B64\u51B3\u5B9A\u662F\u5426\u6539\u7528\u6587\u5B57\u56DE\u6267\uFF1B\u5177\u4F53\u539F\u56E0\u5199\u5165\u65E5\u5FD7\u4FBF\u4E8E\u6392\u67E5\u3002\n */\n public async addQQ(bot: QQBot, messageId: number): Promise<string | null> {\n const config = getHermesConfig();\n if (!config.reactionEnabled) {\n bot.logger?.debug(`[Hermes] \u8868\u60C5\u56DE\u5E94\u5DF2\u5173\u95ED\uFF0C\u8DF3\u8FC7\uFF08message ${messageId}\uFF09`);\n return null;\n }\n try {\n await bot.axiosInstance.post('/set_msg_emoji_like', {\n message_id: messageId,\n emoji_id: config.reactionEmojiQq,\n set: true\n });\n } catch (error) {\n bot.logger?.warn(\n `[Hermes] \u8868\u60C5\u56DE\u5E94\u5931\u8D25\uFF08message ${messageId}\uFF09: ${(error as Error).message}` +\n `\uFF1BNapCat \u5BF9\u79C1\u804A\u6D88\u606F\u901A\u5E38\u4E0D\u652F\u6301 set_msg_emoji_like`\n );\n return null;\n }\n const key = `qq:${messageId}`;\n this.load();\n this.records.set(key, {\n key,\n platform: 'qq',\n messageId: String(messageId),\n emoji: config.reactionEmojiQq,\n createdAt: Date.now()\n });\n this.persist();\n return key;\n }\n\n /**\n * Discord\uFF1Amessage.react \u6210\u529F\u540E\u8C03\u7528\uFF0C\u7ACB\u5373\u6301\u4E45\u5316\u8BB0\u5F55\u5E76\u8FD4\u56DE\u8BB0\u5F55\u952E\u3002\n */\n public persistDiscord(channelId: string, messageId: string, emoji: string): string {\n const key = `discord:${channelId}:${messageId}`;\n this.load();\n this.records.set(key, { key, platform: 'discord', messageId, channelId, emoji, createdAt: Date.now() });\n this.persist();\n return key;\n }\n\n /**\n * QQ\uFF1A\u6458\u9664\u8868\u60C5\uFF08set=false\uFF09\u3002\u65E0\u8BBA\u6458\u9664\u6210\u8D25\u90FD\u79FB\u9664\u6301\u4E45\u5316\u8BB0\u5F55\n * \uFF08\u5931\u8D25\u591A\u4E3A\u6D88\u606F\u5DF2\u88AB\u64A4\u56DE\u7B49\u4E0D\u53EF\u91CD\u8BD5\u539F\u56E0\uFF0C\u6B8B\u7559\u8BB0\u5F55\u53EA\u4F1A\u5728\u4E0B\u6B21\u542F\u52A8\u65F6\u518D\u6B21\u5931\u8D25\uFF09\u3002\n */\n public async removeQQ(bot: QQBot | undefined, key: string): Promise<void> {\n this.load();\n const record = this.records.get(key);\n if (this.records.delete(key)) this.persist();\n if (!record || !bot) return;\n try {\n await bot.axiosInstance.post('/set_msg_emoji_like', {\n message_id: Number(record.messageId),\n emoji_id: record.emoji,\n set: false\n });\n } catch (error) {\n bot.logger?.warn(`[Hermes] \u6458\u9664\u8868\u60C5\u56DE\u5E94\u5931\u8D25\uFF08${key}\uFF09: ${(error as Error).message}`);\n }\n }\n\n /** \u79FB\u9664\u4E00\u6761\u8BB0\u5F55\uFF08Discord \u6458\u9664\u52A8\u4F5C\u7531\u8C03\u7528\u65B9\u5B8C\u6210\u540E\u8C03\u7528\uFF09 */\n public drop(key: string): void {\n this.load();\n if (this.records.delete(key)) this.persist();\n }\n\n /** \u5168\u90E8\u6B8B\u7559\u7684 Discord \u8BB0\u5F55\uFF08\u542F\u52A8\u6E05\u7406\u7528\uFF0C\u6458\u9664\u540E\u9010\u6761 drop\uFF09 */\n public listDiscord(): PendingReaction[] {\n this.load();\n return [...this.records.values()].filter(record => record.platform === 'discord');\n }\n\n /** \u542F\u52A8\u6E05\u7406\uFF1A\u6458\u9664\u5168\u90E8\u6B8B\u7559\u7684 QQ \u8868\u60C5\u56DE\u5E94\u5E76\u6E05\u7A7A\u5BF9\u5E94\u8BB0\u5F55 */\n public async cleanupQQ(bot: QQBot): Promise<void> {\n this.load();\n const stale = [...this.records.values()].filter(record => record.platform === 'qq');\n for (const record of stale) {\n try {\n await bot.axiosInstance.post('/set_msg_emoji_like', {\n message_id: Number(record.messageId),\n emoji_id: record.emoji,\n set: false\n });\n } catch {\n // \u6D88\u606F\u5DF2\u64A4\u56DE/\u4E0D\u5B58\u5728\u7B49\uFF1A\u653E\u5F03\u6458\u9664\uFF0C\u4EC5\u6E05\u7406\u8BB0\u5F55\n }\n this.records.delete(record.key);\n }\n if (stale.length > 0) {\n this.persist();\n bot.logger?.info(`[Hermes] \u5DF2\u6E05\u7406 ${stale.length} \u6761\u6B8B\u7559\u7684 QQ \u8868\u60C5\u56DE\u5E94`);\n }\n }\n}\n", "import { Template, tryGenerateImage } from '@/image';\nimport { getHermesConfig } from '@/features/hermes/config';\nimport { RunCurrentTool, RunToolRecord } from '@/features/hermes/types';\n\n/**\n * Hermes \u8FDB\u5EA6/\u5BA1\u6279\u5361\u7247\u6E32\u67D3\uFF08\u79FB\u690D\u81EA qq-hermes-bridge src/renderer.ts\uFF0CMIT \u534F\u8BAE\uFF0C\u539F\u4F5C\u8005 Amorter\uFF09\u3002\n * HTML/CSS \u62C6\u4E3A\u6A21\u677F\u6587\u4EF6\uFF0C\u6E32\u67D3\u6539\u7528 src/image \u7684\u5171\u4EAB Chrome + \u4E32\u884C\u622A\u56FE\u961F\u5217\u3002\n * \u6E32\u67D3\u5931\u8D25\u8FD4\u56DE null\uFF0C\u8C03\u7528\u65B9\u964D\u7EA7\u4E3A\u7EAF\u6587\u672C\u3002\n */\n\n/** \u8FDB\u5EA6\u5361\u7247\u6E32\u67D3\u6570\u636E */\nexport interface ProgressCardData {\n tools: RunToolRecord[];\n currentTool: RunCurrentTool | null;\n messageDelta?: string;\n elapsed: string;\n}\n\n/** \u5BA1\u6279\u5361\u7247\u6E32\u67D3\u6570\u636E */\nexport interface ApprovalCardData {\n command: string;\n riskLevel: string;\n toolName?: string;\n runId?: string;\n preview?: string;\n}\n\nfunction escapeHtml(text: unknown): string {\n return String(text || '')\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n\nfunction formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;\n return `${Math.floor(ms / 60000)}m${Math.round((ms % 60000) / 1000)}s`;\n}\n\nfunction render(templateName: string, template: Template): Promise<string> {\n return new Promise<string>((resolve, reject) => {\n const templateFile = template.file();\n template\n .handler()\n .then(html => tryGenerateImage(resolve, reject, html, 500, templateFile))\n .catch(reject);\n });\n}\n\n/** \u6E32\u67D3\u8FDB\u5EA6\u5361\u7247\u4E3A base64 \u56FE\u7247\uFF1BprogressAsImage=false \u6216\u6E32\u67D3\u5931\u8D25\u65F6\u8FD4\u56DE null */\nexport async function renderProgressImage(data: ProgressCardData): Promise<string | null> {\n const config = getHermesConfig();\n if (!config.progressAsImage) return null;\n try {\n const toolRows = data.tools\n .slice(-config.progressMaxTools)\n .map(tool => {\n const icon = tool.error ? '\u274C' : '\u2705';\n const duration = tool.duration ? ` (${formatDuration(tool.duration)})` : '';\n return `<div class=\"tool-row\">\n <span class=\"icon\">${icon}</span>\n <span class=\"name\">${escapeHtml(tool.name)}</span>\n <span class=\"dur\">${duration}</span>\n </div>\n ${tool.preview ? `<div class=\"preview\">${escapeHtml(tool.preview).slice(0, 120)}</div>` : ''}`;\n })\n .join('\\n');\n\n const currentHtml = data.currentTool\n ? `<div class=\"current-tool\">\n <span class=\"spinner\">\u23F3</span>\n <span class=\"name\">${escapeHtml(data.currentTool.name)}</span>\n ${data.currentTool.preview ? `<div class=\"preview\">${escapeHtml(data.currentTool.preview).slice(0, 120)}</div>` : ''}\n </div>`\n : '';\n\n const previewHtml = data.messageDelta\n ? `<div class=\"response-preview\">${escapeHtml(data.messageDelta.slice(-300))}</div>`\n : '';\n\n const template = Template.load('progress', 'src/features/hermes/template')\n .arg('elapsed', escapeHtml(data.elapsed))\n .arg('tools', toolRows)\n .arg('current', currentHtml)\n .arg('preview', previewHtml)\n .arg('botName', escapeHtml(config.botName));\n return await render('progress', template);\n } catch {\n return null;\n }\n}\n\n/** \u6E32\u67D3\u5BA1\u6279\u5361\u7247\u4E3A base64 \u56FE\u7247\uFF1B\u6E32\u67D3\u5931\u8D25\u65F6\u8FD4\u56DE null */\nexport async function renderApprovalImage(data: ApprovalCardData): Promise<string | null> {\n try {\n const riskColors: Record<string, string> = { high: '#E94560', medium: '#F5C842', low: '#53D8FB' };\n const riskColor = riskColors[data.riskLevel] || riskColors.high!;\n\n const toolSection = data.toolName\n ? `<div class=\"section\"><div class=\"label\">\u5DE5\u5177</div><div class=\"value\">${escapeHtml(data.toolName)}</div></div>`\n : '';\n const previewSection = data.preview\n ? `<div class=\"section\"><div class=\"label\">\u8BF4\u660E</div><div class=\"value\">${escapeHtml(data.preview).slice(0, 200)}</div></div>`\n : '';\n\n const template = Template.load('approval', 'src/features/hermes/template')\n .arg('riskColor', riskColor)\n .arg('toolSection', toolSection)\n .arg('command', escapeHtml(data.command))\n .arg('previewSection', previewSection)\n .arg('runId', escapeHtml(data.runId?.slice(-8) ?? ''));\n return await render('approval', template);\n } catch {\n return null;\n }\n}\n", "import fs from 'node:fs';\nimport path from 'node:path';\nimport { Message as DiscordMessage } from 'discord.js';\nimport { QQBot } from '@/index';\nimport { GroupMessageWSMSG, PrivateMessageWSMSG, ReceiveMessage, SentMessage } from '@/type';\nimport { getHermesConfig, isHermesAdmin } from '@/features/hermes/config';\nimport { HermesClient } from '@/features/hermes/client';\nimport { SkillManager } from '@/features/hermes/skills';\nimport { ChatHistoryStore } from '@/features/hermes/history';\nimport { buildConversationHistory, buildGroupContext, appendAutoCardNote } from '@/features/hermes/context';\nimport { MessageResolvers, parseMessage } from '@/features/hermes/message';\nimport { ReplyUnit, ReplyUnitOptions, splitReplyIntoUnits, takeCompleteUnits } from '@/features/hermes/reply-units';\nimport { ReactionManager } from '@/features/hermes/reaction';\nimport { Github } from '@/features/github';\nimport { isGithubEnabledGroup } from '@/config/features';\nimport { renderApprovalImage, renderProgressImage } from '@/features/hermes/render';\nimport {\n decideGroupTrigger,\n isResetCommand,\n isStopCommand,\n parseApprovalChoice,\n splitMessageText\n} from '@/features/hermes/trigger';\nimport {\n Approval,\n ApprovalChoice,\n DiscordChatParams,\n FormattedMessage,\n GroupAdminApi,\n HermesApprovalEvent,\n MessageContentPart,\n OneBotGetForwardMsgResponse,\n OneBotGetMsgResponse,\n OneBotGroupMemberInfo,\n RouteInfo,\n RunState,\n Session\n} from '@/features/hermes/types';\n\n/**\n * QQ \u21C4 Hermes Agent \u6865\u63A5\uFF08\u79FB\u690D\u81EA qq-hermes-bridge src/index.ts\uFF0CMIT \u534F\u8BAE\uFF0C\u539F\u4F5C\u8005 Amorter\uFF1A\n * https://github.com/Amorter/qq-hermes-bridge\uFF09\u3002\n *\n * \u4E0E\u4E0A\u6E38\u7684\u5DEE\u5F02\uFF1A\n * - OneBot \u8FDE\u63A5/\u4E8B\u4EF6/\u53D1\u9001\u5168\u90E8\u590D\u7528\u672C\u4ED3\u5E93 QQBot\uFF08\u4E0D\u518D\u81EA\u5EFA WebSocket \u5BA2\u6237\u7AEF\uFF09\uFF1B\n * - \u914D\u7F6E\u6539\u4E3A configs/features/hermes.json\uFF08ConfigStore \u70ED\u91CD\u8F7D\uFF09\uFF0CSOUL.md \u8BFB configs/SOUL.md\uFF1B\n * - \u7BA1\u7406\u5458 = hermes admins \u222A management operators\uFF1B\n * - \u7FA4\u804A\u547D\u4EE4\u5B88\u536B\uFF1A/ \u6216 ! \u5F00\u5934\u7684\u6D88\u606F\u4E0D\u89E6\u53D1\uFF08\u9664\u975E\u663E\u5F0F @bot\uFF09\uFF0C\u907F\u514D\u4E0E gugle-command \u53CC\u91CD\u54CD\u5E94\uFF1B\n * - \u5BA1\u6279\u5426\u5B9A\u8BCD\uFF08\u4E0D\u5141\u8BB8/\u4E0D\u6279\u51C6\uFF09\u5339\u914D\u987A\u5E8F\u4FEE\u6B63\uFF08\u4E0A\u6E38\u5148\u5339\u914D\u300C\u6279\u51C6\u300D\u5BFC\u81F4\u5426\u5B9A\u8BCD\u4E0D\u53EF\u8FBE\uFF09\uFF1B\n * - \u4E0D\u5B9E\u73B0 COMPACT_LINES \u5408\u5E76\u8F6C\u53D1\uFF08\u4E0A\u6E38\u9ED8\u8BA4\u5173\u95ED\uFF09\uFF0C\u957F\u56DE\u590D\u6309\u957F\u5EA6\u5207\u5206\u53D1\u9001\uFF1B\n * - QQ \u7FA4\u804A\u7684 AI \u56DE\u590D\u6539\u4E3A\u300C\u9010\u53E5\u53D1\u9001\u300D\uFF1A\u6D41\u5F0F\u589E\u91CF\u91CC\u5DF2\u5B8C\u6574\u7684\u53E5\u5B50\u7ACB\u5373\u4F5C\u4E3A\u72EC\u7ACB\u6D88\u606F\u53D1\u51FA\uFF0C\n * \u5355\u6761\u8D85\u8FC7 compactMaxChars \u5B57\u6216 compactMaxLines \u884C\u65F6\u6539\u7528\u804A\u5929\u8BB0\u5F55\uFF08\u5408\u5E76\u8F6C\u53D1\uFF09\u53D1\u9001\uFF1B\n * \u5F15\u7528\u53EA\u8D34\u5728\u672C\u8F6E\u9996\u6761\u5355\u5143\u4E0A\uFF1BQQ \u79C1\u804A\u4E0E Discord \u7EF4\u6301\u539F\u6709\u6574\u6BB5\u5207\u5206\u884C\u4E3A\uFF1B\n * - \u89E6\u53D1\u6D88\u606F\u7684\u5F15\u7528/\u804A\u5929\u8BB0\u5F55\u6309\u6DF1\u5EA6\u89E3\u6790\uFF08depth \u2264 1 \u8F6C\u53D1\u56FE\u7247\u539F\u56FE\u3001depth 2 \u4EC5\u6587\u5B57\u3001\n * depth \u2265 3 \u8BB0\u4F5C [\u804A\u5929\u8BB0\u5F55]\uFF09\uFF0C\u5F15\u7528\u4E0E\u804A\u5929\u8BB0\u5F55\u5185\u7684\u56FE\u7247\u90FD\u4F1A\u4F5C\u4E3A\u591A\u6A21\u6001\u8F93\u5165\u8F6C\u53D1\u7ED9 AI\n * \uFF08\u89E3\u6790\u903B\u8F91\u89C1 message.ts\uFF1Bget_msg \u53EA\u56DE file \u65F6\u515C\u5E95\u8C03 NapCat get_image \u6362\u76F4\u94FE\uFF09\uFF1B\n * - \u591A\u4EBA\u5171\u4EAB\u5386\u53F2\u5728\u63D0\u4EA4\u65F6\u6309\u5F53\u524D\u5BF9\u8BDD\u8005\u6807\u6CE8\u5F52\u5C5E\uFF08[\u7FA4\u804A\u80CC\u666F] / [\u4F60\u56DE\u590D xxx \u7684\u8BDD]\uFF09\uFF0C\n * \u7CFB\u7EDF\u63D0\u793A\u663E\u5F0F\u58F0\u660E\u5F53\u524D\u5BF9\u8BDD\u8005\u4E0E\u6807\u6CE8\u7EA6\u5B9A\uFF0C\u907F\u514D\u4E32\u53F0\u8BA4\u9519\u4EBA\uFF08context.ts\uFF09\uFF1B\n * - \u6280\u80FD\u7CFB\u7EDF\u5728\u7FA4\u7BA1\u7406\u6280\u80FD\u4E4B\u5916\u6269\u5C55\u5361\u7247\u7C7B\u516C\u5171\u6280\u80FD\uFF08\u67E5Issue / B\u7AD9\u89C6\u9891\uFF09\uFF0C\u6280\u80FD\u53EF\u4EA7\u51FA\n * base64 \u56FE\u7247\uFF0C\u7531\u5B8C\u6210\u5904\u7406\u5668\u9010\u5F20\u53D1\u9001\uFF08\u590D\u7528 github / bili \u529F\u80FD\u7684\u6E32\u67D3\u7BA1\u7EBF\uFF09\uFF1B\n * - \u67E5\u8BE2\u7C7B\u6280\u80FD\uFF08MC\u7248\u672C / MC\u670D\u52A1\u5668 / MC\u7EF4\u57FA / Modrinth\u7248\u672C\uFF0Cfeedback \u6807\u8BB0\uFF09\u7684\u6267\u884C\u7ED3\u679C\n * \u4E0D\u76F4\u63A5\u53D1\u6458\u8981\uFF0C\u800C\u662F\u4F5C\u4E3A [\u5DE5\u5177\u7ED3\u679C] \u8F93\u5165\u518D\u63D0\u4EA4\u4E00\u8F6E run \u7531 AI \u6574\u7406\u56DE\u7B54\uFF08\u6700\u591A\u56DE\u5582\n * MAX_FEEDBACK_DEPTH \u8F6E\u9632\u5FAA\u73AF\uFF0C\u8FBE\u4E0A\u9650\u540E\u6570\u636E\u9644\u5728\u56DE\u590D\u91CC\u515C\u5E95\uFF09\uFF1B\n * - \u89E6\u53D1 AI \u65F6\u7ED9\u89E6\u53D1\u6D88\u606F\u8D34\u300C\u5904\u7406\u4E2D\u300D\u8868\u60C5\u56DE\u5E94\uFF08QQ \u8D34\u8868\u60C5 / Discord reaction\uFF0C\n * reaction.ts\uFF09\uFF0C\u5B8C\u6574\u56DE\u590D\u5B8C\u6210\uFF08\u542B\u56DE\u5582\u8F6E\uFF09\u540E\u6458\u9664\uFF1B\u8BB0\u5F55\u7ACB\u5373\u6301\u4E45\u5316\u5230\n * data/hermes-reactions.json\uFF0C\u8FDB\u7A0B\u5728\u56DE\u5E94\u671F\u95F4\u9000\u51FA\u65F6\u4E0B\u6B21\u542F\u52A8\u81EA\u52A8\u6E05\u7406\u6B8B\u7559\uFF1B\n * QQ \u79C1\u804A\u6D88\u606F NapCat \u901A\u5E38\u4E0D\u652F\u6301\u8868\u60C5\u56DE\u5E94\uFF0C\u8D34\u5931\u8D25\u65F6\u9000\u5316\u4E3A\u4E00\u6761\u6587\u5B57\u56DE\u6267\n * \uFF08privateReceiptText\uFF09\uFF0CAI \u9996\u6761\u56DE\u590D\u53D1\u51FA\u540E\u64A4\u56DE\uFF0C\u8BA9\u7528\u6237\u786E\u8BA4\u300CAI \u6536\u5230\u4E86\u300D\uFF1B\n * - AI \u7684\u7FA4\u56DE\u590D\u7ECF bot.sendGroupMsg \u53D1\u51FA\uFF0C\u4F1A\u6309\u65E2\u5B9A\u884C\u4E3A\u540C\u6B65\u8F6C\u53D1\u5230\u4E92\u901A\u7684 Discord \u9891\u9053\u3002\n *\n * Discord \u9891\u9053\u89E6\u53D1\uFF08\u5BF9\u4E0A\u6E38\u7684\u6269\u5C55\uFF09\uFF1A\n * - \u4E92\u901A\u7684 Discord \u9891\u9053\u6D88\u606F\u540C\u6837\u53EF\u89E6\u53D1 AI\uFF08@\u63D0\u53CA/\u5173\u952E\u8BCD/requireMention \u540C\u4E00\u5957\u89C4\u5219\uFF09\uFF0C\n * \u9891\u9053\u662F\u5426\u542F\u7528\u7531\u5176\u6865\u63A5\u7684 QQ \u7FA4\u662F\u5426\u5728 hermes.json groups \u4E2D\u51B3\u5B9A\uFF1B\n * - Discord \u4E0E QQ \u7FA4\u5171\u4EAB\u540C\u4E00\u4EFD\u4F1A\u8BDD\u4E0A\u4E0B\u6587\uFF08group:{\u7FA4\u53F7}\uFF09\uFF0CAI \u80FD\u770B\u5230\u4E24\u4FA7\u7684\u5BF9\u8BDD\uFF1B\n * - AI \u56DE\u590D\u53D1\u5728\u63D0\u95EE\u4FA7\uFF08Discord \u63D0\u95EE\u56DE Discord\uFF0CQQ \u63D0\u95EE\u56DE QQ\uFF09\uFF0C\u53D1\u9001\u7ECF DiscordBridge\n * \u6CE8\u5165\u7684\u59D4\u6258\u5B8C\u6210\uFF0C\u672C\u6A21\u5757\u4E0D\u6301\u6709 discord.js Client\uFF1B\n * - Discord \u4FA7\u7BA1\u7406\u5458\uFF08\u670D\u52A1\u5668\u62E5\u6709\u8005/\u7BA1\u7406\u670D\u52A1\u5668/\u7BA1\u7406\u5458\u6743\u9650\uFF09\u53EF\u5BA1\u6279\u4E0E\u8C03\u7528\u7BA1\u7406\u6280\u80FD\uFF1B\n * QQ \u7528\u6237\u9ED1\u767D\u540D\u5355\uFF08allowedUsers/blockedUsers\uFF09\u4E0D\u7EA6\u675F Discord \u7528\u6237\u3002\n */\n\n/** \u56FE\u7247\u6269\u5C55\u540D \u2192 MIME \u7C7B\u578B\u6620\u5C04\uFF08\u672C\u5730\u56FE\u7247\u8F6C base64 data URL \u7528\uFF09 */\nconst IMAGE_MIME_BY_EXT: Record<string, string> = {\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n png: 'image/png',\n gif: 'image/gif',\n webp: 'image/webp',\n bmp: 'image/bmp'\n};\n\n/** \u4ECE\u8DEF\u5F84/URL \u6269\u5C55\u540D\u63A8\u65AD MIME\uFF0C\u672A\u77E5\u9ED8\u8BA4 image/jpeg */\nfunction mimeFromPath(filePath: string): string {\n const ext = filePath.split('.').pop()?.split(/[?#]/)[0]?.toLowerCase() || '';\n return IMAGE_MIME_BY_EXT[ext] || 'image/jpeg';\n}\n\n/** \u4FDD\u7559\u6807\u7B7E\uFF1A\u547D\u4E2D\u5373\u4E0D\u5F97\u7ECF\u6D41\u5F0F/\u5DE5\u5177\u8FB9\u754C\u4E0B\u53D1\uFF0C\u7EDF\u4E00\u7559\u7ED9 finishRun \u5904\u7406 */\nconst RESERVED_LABEL_RE = /\\[SKILL:|MEDIA:/;\n\nexport class HermesBridge {\n private static instance?: HermesBridge;\n\n public static getInstance(): HermesBridge {\n if (!HermesBridge.instance) HermesBridge.instance = new HermesBridge();\n return HermesBridge.instance;\n }\n\n private bot?: QQBot;\n private readonly hermes = new HermesClient();\n private readonly skillManager = new SkillManager();\n /** \u300C\u5904\u7406\u4E2D\u300D\u8868\u60C5\u56DE\u5E94\uFF08\u89E6\u53D1\u65F6\u8D34\u4E0A\u3001\u5B8C\u6574\u56DE\u590D\u540E\u6458\u9664\u3001\u6B8B\u7559\u968F\u91CD\u542F\u6E05\u7406\uFF09 */\n private readonly reactions = ReactionManager.getInstance();\n\n /** \u67E5\u8BE2\u6280\u80FD\u7ED3\u679C\u56DE\u5582\u7684\u6700\u5927\u8F6E\u6570\uFF08\u9632\u6B62 AI \u53CD\u590D\u8C03\u7528\u67E5\u8BE2\u6280\u80FD\u6B7B\u5FAA\u73AF\uFF09 */\n private static readonly MAX_FEEDBACK_DEPTH = 2;\n\n /** \u5BF9\u8BDD\u4F1A\u8BDD\uFF1AsessionKey \u2192 Session */\n private readonly sessions = new Map<string, Session>();\n /** \u6D3B\u8DC3\u8FD0\u884C\uFF1ArunId \u2192 RunState */\n private readonly activeRuns = new Map<string, RunState>();\n /** \u5F85\u5BA1\u6279\u8BB0\u5F55\uFF1ArunId \u2192 Approval */\n private readonly pendingApprovals = new Map<string, Approval>();\n /** \u5DF2\u53D1\u9001\u5BA1\u6279\u6D88\u606F\u8DDF\u8E2A\uFF1ArunId \u2192 true\uFF08\u9632\u6B62\u91CD\u590D\u53D1\u9001\uFF09 */\n private readonly approvalMessageSent = new Map<string, boolean>();\n /** \u7FA4\u6210\u5458\u6635\u79F0\u7F13\u5B58\uFF1AgroupId:userId \u2192 \u7FA4\u540D\u7247\u6216\u6635\u79F0 */\n private readonly memberCache = new Map<string, string>();\n\n /** \u5386\u53F2\u5B58\u50A8\uFF08\u968F\u6301\u4E45\u5316\u914D\u7F6E\u53D8\u66F4\u91CD\u5EFA\uFF09 */\n private historyStore?: ChatHistoryStore;\n private historyStoreKey = '';\n\n private constructor() {}\n\n // ===================================================================\n // \u4E8B\u4EF6\u5165\u53E3\n // ===================================================================\n\n /** message-event-group \u94A9\u5B50 */\n public handleGroupMessage(bot: QQBot, msg: GroupMessageWSMSG): void {\n this.handleMessageSafe(bot, msg).catch(error => {\n bot.logger?.error(`[Hermes] \u7FA4\u6D88\u606F\u5904\u7406\u5931\u8D25: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}`);\n });\n }\n\n /** message-event-private \u94A9\u5B50 */\n public handlePrivateMessage(bot: QQBot, msg: PrivateMessageWSMSG): void {\n this.handleMessageSafe(bot, msg).catch(error => {\n bot.logger?.error(`[Hermes] \u79C1\u804A\u6D88\u606F\u5904\u7406\u5931\u8D25: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}`);\n });\n }\n\n /** Discord \u9891\u9053\u6D88\u606F\u94A9\u5B50\uFF08\u7531 DiscordBridge \u8C03\u7528\uFF0C\u53D1\u9001\u7ECF\u6CE8\u5165\u7684\u59D4\u6258\u5B8C\u6210\uFF09 */\n public handleDiscordMessage(bot: QQBot, message: DiscordMessage, params: DiscordChatParams): void {\n this.handleDiscordMessageSafe(bot, message, params).catch(error => {\n bot.logger?.error(\n `[Hermes] Discord \u6D88\u606F\u5904\u7406\u5931\u8D25: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}`\n );\n });\n }\n\n // ===================================================================\n // \u8BBF\u95EE\u63A7\u5236\u4E0E\u89E6\u53D1\n // ===================================================================\n\n /** \u68C0\u67E5\u7528\u6237\u662F\u5426\u6709\u6743\u4E0E Bot \u5BF9\u8BDD */\n private canChat(route: RouteInfo): boolean {\n const config = getHermesConfig();\n if (route.type === 'discord') {\n // Discord \u9891\u9053\u7531\u5176\u6865\u63A5\u7684 QQ \u7FA4\u51B3\u5B9A\u662F\u5426\u542F\u7528\uFF1BQQ \u7528\u6237\u9ED1\u767D\u540D\u5355\u4E0D\u7EA6\u675F Discord \u7528\u6237\n return config.groups.includes(Number(route.groupId));\n }\n const uid = Number(route.userId);\n if (config.blockedUsers.includes(uid)) return false;\n if (config.allowedUsers.length > 0 && !config.allowedUsers.includes(uid) && !isHermesAdmin(uid)) return false;\n if (route.type === 'group' && !config.groups.includes(Number(route.groupId))) return false;\n return true;\n }\n\n /** \u8DEF\u7531\u662F\u5426\u6307\u5411\u540C\u4E00\u4F1A\u8BDD\u57DF\uFF08QQ \u7FA4\u4E0E\u4E92\u901A\u7684 Discord \u9891\u9053\u6309\u6865\u63A5\u7FA4\u53F7\u5BF9\u9F50\uFF09 */\n private routeMatches(a: RouteInfo, b: RouteInfo): boolean {\n if (a.type === 'user' || b.type === 'user') return a.type === b.type && a.userId === b.userId;\n return a.groupId !== undefined && a.groupId === b.groupId;\n }\n\n /** \u8DEF\u7531\u53D1\u9001\u8005\u662F\u5426\u4E3A\u7BA1\u7406\u5458\uFF08QQ \u4FA7\u53D6 hermes admins \u222A operators\uFF0CDiscord \u4FA7\u53D6\u670D\u52A1\u5668\u6743\u9650\uFF09 */\n private isRouteAdmin(route: RouteInfo): boolean {\n if (route.type === 'discord') return route.discord?.isAdmin === true;\n return isHermesAdmin(Number(route.userId));\n }\n\n /** \u68C0\u67E5\u662F\u5426 @\u4E86 Bot */\n private hasAtSelf(message: ReceiveMessage): boolean {\n if (!Array.isArray(message)) return false;\n const selfId = this.bot?.getLoginInfoSync()?.user_id;\n if (selfId === undefined) return false;\n return message.some(segment => segment.type === 'at' && String(segment.data.qq) === String(selfId));\n }\n\n /** \u63D0\u53D6\u6D88\u606F\u4E2D\u7684\u7EAF\u6587\u672C */\n private extractText(message: ReceiveMessage | string): string {\n if (typeof message === 'string') return message;\n if (!Array.isArray(message)) return '';\n return message\n .filter(segment => segment.type === 'text')\n .map(segment => (segment.type === 'text' ? segment.data.text : ''))\n .join('')\n .trim();\n }\n\n /**\n * \u7EDF\u8BA1\u6D88\u606F\u4E2D\u4F1A\u88AB\u6D88\u606F\u7BA1\u9053\u81EA\u52A8\u6E32\u67D3\u5361\u7247\u7684 Issue/PR \u5F15\u7528\u6570\n * \uFF08\u8BE5\u7FA4\u542F\u7528 github \u529F\u80FD\u4E14\u4ED3\u5E93\u5728\u5141\u8BB8\u5217\u8868\u5185\uFF1B\u79C1\u804A\u65E0\u81EA\u52A8\u5361\u7247\uFF0C\u8FD4\u56DE 0\uFF09\u3002\n * \u4E0E Github.processMessage / processDiscordMessage \u7684\u81EA\u52A8\u53D1\u5361\u53E3\u5F84\u4E00\u81F4\u3002\n */\n private countAutoCardReferences(text: string, route: RouteInfo): number {\n if (route.type === 'user' || !route.groupId) return 0;\n const groupId = Number(route.groupId);\n if (!Number.isSafeInteger(groupId) || !isGithubEnabledGroup(groupId)) return 0;\n return Github.parseReferences(text).filter(reference => Github.isAllowedRepository(reference.repository)).length;\n }\n\n /** \u53D1\u9001\u8005\u5C55\u793A\u540D\uFF1A\u7FA4\u540D\u7247\uFF08\u6635\u79F0\uFF09\u6216\u6635\u79F0\u6216 QQ \u53F7 */\n private senderLabel(sender: { card?: string; nickname?: string }, userId: string): string {\n const card = sender.card?.trim();\n const nick = sender.nickname?.trim();\n const name = card && nick && card !== nick ? `${card}\uFF08${nick}\uFF09` : card || nick || userId;\n return `${name} (${userId})`;\n }\n\n // ===================================================================\n // \u4F1A\u8BDD\u7BA1\u7406\n // ===================================================================\n\n private get store(): ChatHistoryStore {\n const config = getHermesConfig();\n const key = `${config.persistHistoryMax}:${config.persistHistoryEnabled}`;\n if (!this.historyStore || this.historyStoreKey !== key) {\n this.historyStore = new ChatHistoryStore(config.persistHistoryMax, config.persistHistoryEnabled);\n this.historyStoreKey = key;\n }\n return this.historyStore;\n }\n\n /** \u83B7\u53D6\u4F1A\u8BDD\u952E\uFF08QQ \u7FA4\u4E0E\u4E92\u901A\u7684 Discord \u9891\u9053\u6309\u7FA4\u5171\u4EAB\uFF0C\u79C1\u804A\u6309\u4EBA\u9694\u79BB\uFF09 */\n private getSessionKey(route: RouteInfo): string {\n return route.type === 'user' ? `user:${route.userId}` : `group:${route.groupId}`;\n }\n\n /** \u83B7\u53D6\u6216\u521B\u5EFA\u4F1A\u8BDD\uFF08\u4F18\u5148\u4ECE\u6301\u4E45\u5316\u5B58\u50A8\u6062\u590D\uFF09 */\n private getSession(key: string): Session {\n if (!this.sessions.has(key)) {\n this.sessions.set(key, { history: this.store.load(key), sessionVersion: 0 });\n }\n return this.sessions.get(key)!;\n }\n\n /** \u6E05\u9664\u4F1A\u8BDD\u4E0A\u4E0B\u6587\uFF08\u5185\u5B58 + \u6301\u4E45\u5316\uFF09 */\n private clearSession(route: RouteInfo): number {\n const baseKey = this.getSessionKey(route);\n const session = this.sessions.get(baseKey);\n if (session) {\n session.sessionVersion = (session.sessionVersion || 0) + 1;\n session.history = [];\n }\n this.store.clear(baseKey);\n return session?.sessionVersion || 0;\n }\n\n /** \u8FFD\u52A0\u5386\u53F2\u6D88\u606F\uFF08\u5185\u5B58 + \u6301\u4E45\u5316\uFF09\uFF0C\u8DF3\u8FC7\u7A7A\u5185\u5BB9\uFF1Blabel \u7528\u4E8E assistant \u6D88\u606F\u6807\u6CE8\u56DE\u590D\u5BF9\u8C61 */\n private appendHistory(key: string, role: string, content: string, userId?: string, label?: string): void {\n if (!content.trim()) return;\n const config = getHermesConfig();\n const session = this.getSession(key);\n session.history.push({\n role,\n content,\n ...(userId !== undefined ? { userId } : {}),\n ...(label !== undefined ? { label } : {})\n });\n const max = config.localHistoryMaxMessages * 2;\n if (session.history.length > max) {\n session.history = session.history.slice(-max);\n }\n this.store.save(key, session.history);\n }\n\n // ===================================================================\n // OneBot API\uFF08QQBot \u672A\u5C01\u88C5\u7684\u5C11\u91CF\u63A5\u53E3\uFF0C\u7ECF HTTP API \u76F4\u63A5\u8C03\u7528\uFF09\n // ===================================================================\n\n private async callApi<T>(action: string, params: Record<string, unknown>): Promise<T> {\n if (!this.bot) throw new Error('bot \u672A\u5C31\u7EEA');\n const res = await this.bot.axiosInstance.post(`/${action}`, params);\n const payload = res.data as { retcode?: number; msg?: string; wording?: string; data?: T };\n if (payload?.retcode !== 0) {\n throw new Error(`API \u9519\u8BEF ${payload?.retcode}: ${payload?.msg || payload?.wording || ''}`);\n }\n return payload?.data as T;\n }\n\n /** \u6280\u80FD\u6267\u884C\u7528\u7684\u7FA4\u7BA1\u7406 API \u95E8\u9762 */\n private groupAdminApi(): GroupAdminApi {\n return {\n setGroupBan: (groupId, userId, durationSec) =>\n this.callApi('set_group_ban', { group_id: Number(groupId), user_id: Number(userId), duration: durationSec }),\n setGroupKick: (groupId, userId) =>\n this.callApi('set_group_kick', { group_id: Number(groupId), user_id: Number(userId), reject_add_request: false }),\n setGroupWholeBan: (groupId, enable) =>\n this.callApi('set_group_whole_ban', { group_id: Number(groupId), enable })\n };\n }\n\n /** \u89E3\u6790\u7FA4\u6210\u5458\u540D\u79F0\uFF08\u7FA4\u540D\u7247 \u2192 QQ \u6635\u79F0 \u2192 QQ \u53F7\uFF09\uFF0C\u5E26\u7F13\u5B58 */\n private async resolveMemberName(groupId: string | number, userId: string | number): Promise<string | null> {\n const key = `${groupId}:${userId}`;\n if (this.memberCache.has(key)) return this.memberCache.get(key)!;\n try {\n const info = await this.callApi<OneBotGroupMemberInfo>('get_group_member_info', {\n group_id: Number(groupId),\n user_id: Number(userId)\n });\n const name = info.card || info.nickname || String(userId);\n this.memberCache.set(key, name);\n return name;\n } catch {\n return null;\n }\n }\n\n // ===================================================================\n // \u6D88\u606F\u53D1\u9001\n // ===================================================================\n\n /** \u53D1\u9001\u6587\u672C\u56DE\u590D\uFF08\u8D85\u957F\u6309 maxMessageLength \u5207\u5206\uFF1Bdiscord \u8DEF\u7531\u7ECF\u59D4\u6258\u53D1\u9001\uFF09 */\n private async sendReply(route: RouteInfo, text: string): Promise<void> {\n if (route.type === 'discord') {\n await route.discord?.send(text, false);\n return;\n }\n if (!this.bot) return;\n const chunks = splitMessageText(text, getHermesConfig().maxMessageLength);\n for (const chunk of chunks) {\n if (route.type === 'group') {\n await this.bot.sendGroupMsg(route.groupId!, [{ type: 'text', data: { text: chunk } }]);\n } else {\n await this.bot.sendPrivateMsg(route.userId, [{ type: 'text', data: { text: chunk } }]);\n }\n }\n }\n\n /** \u53D1\u9001\u5E26\u5F15\u7528\u7684\u6587\u672C\u56DE\u590D\uFF08\u7FA4\u804A\u56DE\u590D\u539F\u6D88\u606F\uFF0C\u79C1\u804A\u9000\u5316\u4E3A\u666E\u901A\u53D1\u9001\uFF1Bdiscord \u8DEF\u7531\u5F15\u7528\u539F\u6D88\u606F\uFF09 */\n private async sendReplyWithMention(route: RouteInfo, text: string, userMsgId: number): Promise<void> {\n if (route.type === 'discord') {\n await route.discord?.send(text, true);\n return;\n }\n if (!this.bot) return;\n if (route.type === 'group' && userMsgId) {\n const chunks = splitMessageText(text, getHermesConfig().maxMessageLength);\n for (const chunk of chunks) {\n await this.bot.sendGroupMsg(route.groupId!, [\n { type: 'reply', data: { id: userMsgId } },\n { type: 'text', data: { text: chunk } }\n ]);\n }\n return;\n }\n await this.sendReply(route, text);\n }\n\n /** \u53D1\u9001\u56FE\u7247\uFF08base64\uFF0C\u5141\u8BB8\u5DF2\u5E26 base64:// \u524D\u7F00\uFF1Bdiscord \u8DEF\u7531\u4EE5\u9644\u4EF6\u53D1\u9001\uFF09 */\n private async sendReplyImage(route: RouteInfo, imageData: string): Promise<void> {\n if (route.type === 'discord') {\n const base64 = imageData.startsWith('base64://') ? imageData.slice('base64://'.length) : imageData;\n await route.discord?.sendImage(base64);\n return;\n }\n if (!this.bot) return;\n const file = imageData.startsWith('base64://') ? imageData : `base64://${imageData}`;\n try {\n if (route.type === 'group') {\n await this.bot.sendGroupMsg(route.groupId!, [{ type: 'image', data: { file } }]);\n } else {\n await this.bot.sendPrivateMsg(route.userId, [{ type: 'image', data: { file } }]);\n }\n } catch (error) {\n this.bot.logger?.error(`[Hermes] \u56FE\u7247\u53D1\u9001\u5931\u8D25: ${(error as Error).message}\uFF0C\u56DE\u9000\u5230\u6587\u5B57`);\n }\n }\n\n // ===================================================================\n // QQ \u7FA4\u804A AI \u56DE\u590D\u53D1\u9001\uFF08\u9010\u53E5 + \u8D85\u957F\u8F6C\u804A\u5929\u8BB0\u5F55\uFF09\n // ===================================================================\n\n /** \u5206\u53E5\u4E0E\u5408\u5E76\u8F6C\u53D1\u9608\u503C\uFF08\u6BCF\u6B21\u53D1\u9001\u65F6\u8BFB\u53D6\u914D\u7F6E\uFF0C\u70ED\u91CD\u8F7D\u751F\u6548\uFF09 */\n private replyUnitOptions(): ReplyUnitOptions {\n const config = getHermesConfig();\n return {\n maxUnitLength: config.maxMessageLength,\n compactMaxChars: config.compactMaxChars,\n compactMaxLines: config.compactMaxLines\n };\n }\n\n /** \u7FA4\u804A AI \u6587\u672C\u662F\u5426\u8D70\u300C\u9010\u53E5 + \u804A\u5929\u8BB0\u5F55\u300D\u65B0\u8DEF\u5F84\uFF08\u79C1\u804A\u4E0E Discord \u7EF4\u6301\u73B0\u72B6\uFF09 */\n private useReplyUnits(route: RouteInfo): boolean {\n return route.type === 'group' && getHermesConfig().replyPerSentence;\n }\n\n /**\n * \u4EE5\u804A\u5929\u8BB0\u5F55\uFF08\u5408\u5E76\u8F6C\u53D1\uFF09\u5F62\u5F0F\u53D1\u9001\u4E00\u6BB5\u7FA4\u804A\u6587\u672C\u3002\n * \u5355\u8282\u70B9\u7F72\u540D\u53D6 bot \u6635\u79F0\u4E0E QQ \u53F7\uFF1B\u673A\u5668\u4EBA\u4FE1\u606F\u7F3A\u5931\u6216\u53D1\u9001\u5931\u8D25\u65F6\u56DE\u9000\u666E\u901A\u6587\u672C\uFF08\u4E0D\u4E22\u5185\u5BB9\uFF09\u3002\n */\n private async sendGroupForward(route: RouteInfo, text: string): Promise<void> {\n const bot = this.bot;\n const login = bot?.getLoginInfoSync();\n if (!bot || route.type !== 'group' || !login?.user_id) {\n await this.sendReply(route, text);\n return;\n }\n const messageId = await bot.sendGroupForwardMsg(route.groupId!, [\n { name: getHermesConfig().botName, uin: login.user_id, content: text }\n ]);\n if (messageId === undefined) {\n bot.logger?.warn(`[Hermes] \u804A\u5929\u8BB0\u5F55\u53D1\u9001\u5931\u8D25\uFF0C\u56DE\u9000\u4E3A\u666E\u901A\u6587\u672C\uFF08${text.length} \u5B57\uFF09`);\n await this.sendReply(route, text);\n }\n }\n\n /**\n * \u628A\u4E00\u6BB5\u53D1\u9001\u4EFB\u52A1\u6302\u5230\u4F1A\u8BDD\u7684\u4E32\u884C\u94FE\u4E0A\uFF0C\u4FDD\u8BC1\u6309\u8C03\u7528\u987A\u5E8F\u6267\u884C\u3002\n *\n * message.delta \u7684\u9010\u53E5\u4E0B\u53D1\u5C55\u5F00\u662F fire-and-forget\uFF08\u4E0D\u80FD\u963B\u585E SSE \u4E8B\u4EF6\u6D41\uFF09\uFF0C\n * \u82E5\u5404\u81EA\u5E76\u53D1 await \u53D1\u9001\uFF0C\u540E\u4E00\u6B21 flush \u7684\u53E5\u5B50\u4F1A\u63D2\u5230\u524D\u4E00\u6B21\u672A\u53D1\u5B8C\u7684\u53E5\u5B50\u4E4B\u524D\uFF0C\n * \u7FA4\u804A\u91CC\u5C31\u51FA\u73B0\u300C\u4E00\u3001\u5C3E\u53E5\u3001\u4E8C\u3001\u4E09\u300D\u8FD9\u79CD\u9519\u5E8F\u3002\u56E0\u6B64\u6240\u6709 AI \u6587\u672C\u53D1\u9001\u7EDF\u4E00\u7ECF\u6B64\u4E32\u884C\u5316\u3002\n * \u8FD4\u56DE\u7684 Promise \u5728\u8BE5\u6BB5\u53D1\u9001\u5B8C\u6210\u540E resolve\uFF08\u8C03\u7528\u65B9\u53EF\u6309\u9700 await\uFF09\u3002\n */\n private enqueueSend(run: RunState | undefined, task: () => Promise<void>): Promise<void> {\n if (!run) return task();\n const previous = run.sendChain ?? Promise.resolve();\n // \u524D\u4E00\u6BB5\u5931\u8D25\u4E0D\u5F71\u54CD\u540E\u7EED\u53D1\u9001\uFF1A\u94FE\u4E0A\u53EA\u4F20\u9012\u5B8C\u6210\u4FE1\u53F7\n const next = previous.then(task, task);\n run.sendChain = next.catch(() => {});\n return next;\n }\n\n /** \u7B49\u5F85\u4E32\u884C\u94FE\u4E0A\u5168\u90E8\u53D1\u9001\u5B8C\u6210\uFF08finishRun \u6536\u5C3E\u4E0E\u6D4B\u8BD5\u7528\uFF09 */\n private async drainSends(run: RunState): Promise<void> {\n // \u94FE\u5C3E\u53EF\u80FD\u4ECD\u5728\u8FFD\u52A0\uFF0C\u5FAA\u73AF\u7B49\u5230\u7A33\u5B9A\n let current = run.sendChain;\n while (current) {\n await current.catch(() => {});\n if (current === run.sendChain) return;\n current = run.sendChain;\n }\n }\n\n /**\n * \u53D1\u9001\u4E00\u6761\u7FA4\u804A AI \u56DE\u590D\u5355\u5143\uFF1A\u8D85\u9608\u503C\u8D70\u804A\u5929\u8BB0\u5F55\uFF0C\u5426\u5219\u666E\u901A\u6587\u672C\u3002\n * \u5F15\u7528\u6BB5\u4EC5\u8D34\u5728\u672C\u8F6E\u9996\u4E2A\u5355\u5143\u4E0A\uFF08\u9636\u68AF\u5F0F\u56DE\u590D\uFF0C\u907F\u514D\u6BCF\u6761\u90FD\u91CD\u590D\u5F15\u7528\uFF09\u3002\n */\n private async sendReplyUnit(route: RouteInfo, unit: ReplyUnit, replyMsgId: number, run?: RunState): Promise<void> {\n await this.sendReplyUnits(route, [unit], replyMsgId, run);\n }\n\n /**\n * \u6279\u91CF\u53D1\u9001\u56DE\u590D\u5355\u5143\uFF1A\u6574\u6279\u4F5C\u4E3A\u4E32\u884C\u94FE\u4E0A\u7684**\u4E00\u4E2A\u4EFB\u52A1**\u6267\u884C\u3002\n *\n * \u5FC5\u987B\u662F\u6574\u6279\u5165\u94FE\u800C\u4E0D\u662F\u9010\u6761\u5165\u94FE\uFF1Amessage.delta \u7684 flush \u662F fire-and-forget\uFF0C\n * \u82E5\u9010\u6761\u5165\u94FE\uFF0C\u4E0B\u4E00\u6279\u4F1A\u5728\u300C\u4E0A\u4E00\u6279\u8FD8\u6CA1\u53D1\u5B8C\u300D\u65F6\u628A\u81EA\u5DF1\u7684\u4EFB\u52A1\u8FFD\u52A0\u5230\u94FE\u4E0A\uFF0C\n * \u4E8E\u662F\u4E0A\u4E00\u6279\u5269\u4F59\u7684\u53E5\u5B50\u88AB\u6392\u5230\u4E0B\u4E00\u6279\u4E4B\u540E\uFF0C\u7FA4\u804A\u91CC\u51FA\u73B0\n * \u300C\u4E00\u4E00\u5F97\u4E00 / \u5C3E\u53E5 / \u4E00\u4E8C\u5F97\u4E8C / \u4E00\u4E09\u5F97\u4E09\u300D\u8FD9\u79CD\u9519\u5E8F\u3002\n */\n private async sendReplyUnits(\n route: RouteInfo,\n units: ReplyUnit[],\n replyMsgId: number,\n run?: RunState\n ): Promise<void> {\n if (units.length === 0) return;\n await this.enqueueSend(run, async () => {\n for (const unit of units) {\n await this.sendSingleUnit(route, unit, replyMsgId, run);\n }\n });\n }\n\n /** \u5355\u6761\u5355\u5143\u7684\u5B9E\u9645\u53D1\u9001\uFF08\u4E0D\u505A\u6392\u961F\uFF0C\u4EC5\u4F9B\u4E32\u884C\u94FE\u5185\u90E8\u8C03\u7528\uFF09 */\n private async sendSingleUnit(\n route: RouteInfo,\n unit: ReplyUnit,\n replyMsgId: number,\n run?: RunState\n ): Promise<void> {\n if (unit.asForward) {\n await this.sendGroupForward(route, unit.text);\n if (run) await this.recallReceiptOnce(run);\n return;\n }\n if (!this.bot) return;\n const attachReply = route.type === 'group' && replyMsgId > 0 && !(run?.replySent ?? false);\n const segments: SentMessage = attachReply\n ? [\n { type: 'reply', data: { id: replyMsgId } },\n { type: 'text', data: { text: unit.text } }\n ]\n : [{ type: 'text', data: { text: unit.text } }];\n if (route.type === 'group') {\n await this.bot.sendGroupMsg(route.groupId!, segments);\n } else {\n await this.bot.sendPrivateMsg(route.userId, segments);\n }\n // \u65E0\u8BBA\u53D1\u9001\u6210\u529F\u4E0E\u5426\u90FD\u7F6E\u4F4D\uFF1A\u907F\u514D\u5931\u8D25\u65F6\u6BCF\u6761\u5355\u5143\u90FD\u91CD\u590D\u8D34\u5F15\u7528\n if (run && attachReply) run.replySent = true;\n // \u79C1\u804A\u56DE\u6267\uFF1A\u9996\u6761\u771F\u5B9E\u56DE\u590D\u5DF2\u53D1\u51FA\uFF0C\u64A4\u56DE\u300C\u6536\u5230\u300D\u63D0\u793A\n if (run) await this.recallReceiptOnce(run);\n }\n\n /**\n * \u9996\u6761\u771F\u5B9E\u56DE\u590D\u53D1\u51FA\u540E\u64A4\u56DE\u79C1\u804A\u56DE\u6267\uFF1A\u7528\u6237\u5DF2\u7ECF\u770B\u5230 AI \u7684\u5185\u5BB9\uFF0C\u56DE\u6267\u4E0D\u5FC5\u5E38\u9A7B\u3002\n * \u53EA\u6267\u884C\u4E00\u6B21\uFF08\u64A4\u56DE\u540E\u6E05\u7A7A run.receiptId\uFF09\u3002\n */\n private async recallReceiptOnce(run: RunState): Promise<void> {\n if (run.receiptId === undefined) return;\n const receiptId = run.receiptId;\n // exactOptionalPropertyTypes \u4E0B\u4E0D\u80FD\u663E\u5F0F\u8D4B undefined\uFF0C\u7528 delete \u6E05\u9664\n delete run.receiptId;\n await this.recallReceipt(run.route, receiptId);\n }\n\n /**\n * QQ \u7FA4\u804A AI \u6587\u672C\u51FA\u53E3\uFF1A\u6309\u53E5\u62C6\u6210\u591A\u6761\u6D88\u606F\u9010\u6761\u53D1\u51FA\uFF1B\u5355\u6761\u8D85\u8FC7 compactMaxChars \u5B57\u6216\n * compactMaxLines \u884C\u65F6\u6539\u7528\u804A\u5929\u8BB0\u5F55\u53D1\u9001\u3002\u975E\u7FA4\u804A\u8DEF\u7531\u6216 replyPerSentence=false \u65F6\u9000\u56DE\n * sendReply / sendReplyWithMention \u7684\u539F\u6709\u6574\u6BB5\u53D1\u9001\u884C\u4E3A\u3002\n *\n * \u547D\u4E2D `[SKILL:` / `MEDIA:` \u7684\u6587\u672C\u4E0D\u4E0B\u53D1\uFF08\u7559\u7ED9 finishRun \u7EDF\u4E00\u5904\u7406\uFF09\u3002\n */\n private async sendAiReplyUnits(route: RouteInfo, text: string, replyMsgId: number, run?: RunState): Promise<void> {\n if (!text.trim()) return;\n if (!this.useReplyUnits(route)) {\n await this.sendReplyWithMention(route, text, replyMsgId);\n return;\n }\n if (RESERVED_LABEL_RE.test(text)) return;\n const units = splitReplyIntoUnits(text, this.replyUnitOptions());\n // \u6574\u6279\u5165\u94FE\uFF1A\u4FDD\u8BC1\u672C\u6279\u53E5\u5B50\u8FDE\u7EED\u53D1\u51FA\uFF0C\u4E0D\u88AB\u540E\u7EED flush \u63D2\u961F\n await this.sendReplyUnits(route, units, replyMsgId, run);\n }\n\n /**\n * \u6D41\u5F0F\u9010\u53E5\u4E0B\u53D1\uFF1A\u628A pendingText \u91CC\u5DF2\u5B8C\u6574\u7684\u5355\u5143\u7ACB\u5373\u53D1\u51FA\uFF0C\u672A\u6210\u5F62\u7684\u90E8\u5206\u5199\u56DE pendingText\u3002\n * \u9010\u53E5\u5355\u5143\u4E0D\u8D34\u5F15\u7528\uFF08\u7531 finishRun \u7684\u6536\u5C3E\u6587\u672C\u8D1F\u8D23\u8D34\uFF09\uFF0C\u4FDD\u8BC1\u300C\u5F15\u7528 + \u9996\u53E5\u300D\u4E0D\u4F1A\u62C6\u6210\u4E24\u6761\u3002\n */\n private async flushStreamUnits(run: RunState): Promise<void> {\n if (!this.useReplyUnits(run.route)) return;\n if (!run.pendingText) return;\n // \u5DF2\u51FA\u73B0\u4FDD\u7559\u6807\u7B7E\uFF1A\u6574\u6BB5\u7559\u7ED9 finishRun\uFF08\u6280\u80FD\u6807\u7B7E/\u56FE\u7247\u8DEF\u5F84\u7EDD\u4E0D\u80FD\u539F\u6837\u53D1\u5230\u7FA4\u91CC\uFF09\n if (RESERVED_LABEL_RE.test(run.pendingText)) return;\n const { units, rest } = takeCompleteUnits(run.pendingText, this.replyUnitOptions());\n run.pendingText = rest;\n if (units.length === 0) return;\n run.sentTextLength = run.messageDelta.length;\n run.lastTextSent = Date.now();\n // \u6574\u6279\u5165\u94FE\uFF08\u5185\u90E8\u6309\u5E8F\u53D1\u5B8C\uFF09\uFF0C\u907F\u514D\u4E0E\u540E\u7EED flush \u4EA4\u9519\n await this.sendReplyUnits(run.route, units, run.userMsgId, run);\n }\n\n // ===================================================================\n // \u56FE\u7247\u5904\u7406\uFF08Hermes \u591A\u6A21\u6001\u8F93\u5165 / MEDIA \u6807\u7B7E\u8F93\u51FA\uFF09\n // ===================================================================\n\n /** \u4E0B\u8F7D\u8FDC\u7A0B\u56FE\u7247\u5E76\u7F16\u7801\u4E3A base64:// */\n private async downloadImageToBase64(imageUrl: string): Promise<string> {\n const resp = await fetch(imageUrl);\n if (!resp.ok) throw new Error(`\u4E0B\u8F7D\u5931\u8D25: ${resp.status}`);\n return `base64://${Buffer.from(await resp.arrayBuffer()).toString('base64')}`;\n }\n\n /** \u4E0B\u8F7D\u8FDC\u7A0B\u56FE\u7247\u5E76\u8F6C\u6210 base64 data URL\uFF08Hermes \u591A\u6A21\u6001\u8F93\u5165\u7528\uFF09 */\n private async downloadImageToDataUrl(imageUrl: string): Promise<string> {\n const resp = await fetch(imageUrl);\n if (!resp.ok) throw new Error(`\u4E0B\u8F7D\u5931\u8D25: ${resp.status}`);\n const buffer = Buffer.from(await resp.arrayBuffer());\n const contentType = (resp.headers.get('content-type') || '').split(';')[0]!.trim();\n const mime = /^image\\//i.test(contentType) ? contentType : mimeFromPath(imageUrl);\n return `data:${mime};base64,${buffer.toString('base64')}`;\n }\n\n /**\n * \u89E3\u6790\u56FE\u7247\u6BB5\u4E3A base64 data URL\uFF0C\u5168\u90E8\u8F6C\u4E3A base64 \u540E\u4F20\u7ED9 Hermes\u3002\n * \u4F18\u5148\u4E0B\u8F7D CDN url / http(s) file\uFF0C\u5176\u6B21\u8BFB\u672C\u5730\u6587\u4EF6\uFF0C\u6700\u540E\u515C\u5E95\u95EE\u4E00\u6B21 NapCat get_image\n * \uFF08get_msg \u8FD4\u56DE\u7684\u56FE\u7247\u6BB5\u5E38\u53EA\u6709 file \u6CA1\u6709 url\uFF0C\u4F8B\u5982\u88AB\u5F15\u7528\u6D88\u606F\u91CC\u7684\u56FE\u7247\uFF09\u3002\n * \u5931\u8D25\u65F6\u8FD4\u56DE null\uFF08\u4EC5\u4FDD\u7559 [\u56FE\u7247] \u5360\u4F4D\u7B26\uFF09\uFF0C\u4E0D\u629B\u9519\u4E2D\u65AD\u6D88\u606F\u5904\u7406\u3002\n */\n private async resolveImageDataUrl(segment: { data: { file: string; url?: string } }): Promise<string | null> {\n const { url, file } = segment.data || {};\n const remote = url || (file && /^https?:\\/\\//i.test(file) ? file : '');\n if (remote) {\n try {\n return await this.downloadImageToDataUrl(remote);\n } catch (error) {\n this.bot?.logger?.error(`[Hermes] \u56FE\u7247\u4E0B\u8F7D\u5931\u8D25 ${remote}: ${(error as Error).message}`);\n }\n }\n if (file && !/^https?:\\/\\//i.test(file)) {\n try {\n const buffer = fs.readFileSync(file);\n return `data:${mimeFromPath(file)};base64,${buffer.toString('base64')}`;\n } catch (error) {\n this.bot?.logger?.error(`[Hermes] \u56FE\u7247\u672C\u5730\u6587\u4EF6\u8BFB\u53D6\u5931\u8D25 ${file}: ${(error as Error).message}`);\n }\n }\n return await this.resolveImageViaApi(file);\n }\n\n /**\n * \u515C\u5E95\uFF1A\u8BF7 NapCat \u6362\u53D6\u56FE\u7247\u76F4\u94FE\uFF08get_image\uFF09\uFF0C\u518D\u6309\u76F4\u94FE\u4E0B\u8F7D\u4E3A data URL\u3002\n * \u5F15\u7528\u6D88\u606F / \u804A\u5929\u8BB0\u5F55\u91CC\u7684\u56FE\u7247\u6BB5\u5E38\u5E38\u53EA\u6709 file \u5B57\u6BB5\uFF0C\u8FD9\u4E00\u6B65\u80FD\u628A\u539F\u56FE\u62FF\u56DE\u6765\u7ED9 AI \u8BC6\u522B\u3002\n */\n private async resolveImageViaApi(file: string | undefined): Promise<string | null> {\n if (!file) return null;\n try {\n const result = await this.callApi<{ url?: string; file?: string }>('get_image', { file });\n const target = result?.url || result?.file;\n if (!target) return null;\n if (/^https?:\\/\\//i.test(target)) return await this.downloadImageToDataUrl(target);\n const buffer = fs.readFileSync(target);\n return `data:${mimeFromPath(target)};base64,${buffer.toString('base64')}`;\n } catch (error) {\n this.bot?.logger?.warn(`[Hermes] get_image \u515C\u5E95\u5931\u8D25 ${file}: ${(error as Error).message}`);\n return null;\n }\n }\n\n /**\n * \u7EC4\u88C5\u6D88\u606F\u89E3\u6790\u7528\u7684 resolver\uFF08IO \u7531\u672C\u7C7B\u63D0\u4F9B\uFF0C\u6DF1\u5EA6/\u5360\u4F4D/\u53BB\u91CD\u903B\u8F91\u5728 message.ts \u4E2D\uFF09\u3002\n * \u89E3\u6790\u5931\u8D25\u4E00\u5F8B\u964D\u7EA7\u4E3A null / \u629B\u9519\u7531 message.ts \u8F6C\u6210\u5360\u4F4D\u7B26\uFF0C\u4E0D\u963B\u585E\u89E6\u53D1\u6D41\u7A0B\u3002\n */\n private messageResolvers(groupId: number | undefined): MessageResolvers {\n return {\n getMsg: messageId =>\n this.callApi<OneBotGetMsgResponse>('get_msg', { message_id: messageId }).then(original => ({\n sender: original?.sender,\n time: original?.time,\n message: original?.message\n })),\n getForward: forwardId => this.callApi<OneBotGetForwardMsgResponse>('get_forward_msg', { message_id: forwardId }),\n resolveImage: segment => this.resolveImageDataUrl(segment),\n resolveName: qq => (groupId === undefined ? Promise.resolve(null) : this.resolveMemberName(groupId, qq)),\n formatTime: unixTs => this.formatTime(unixTs),\n forwardImages: getHermesConfig().forwardImages\n };\n }\n\n // ===================================================================\n // \u8FDB\u5EA6\u8DDF\u8E2A\n // ===================================================================\n\n /** \u683C\u5F0F\u5316\u6BEB\u79D2\u4E3A\u53EF\u8BFB\u65F6\u957F */\n private formatElapsed(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60000) return `${(ms / 1000).toFixed(0)}s`;\n return `${Math.floor(ms / 60000)}m${Math.round((ms % 60000) / 1000)}s`;\n }\n\n /** \u68C0\u67E5\u662F\u5426\u5E94\u53D1\u9001\u8FDB\u5EA6\u66F4\u65B0 */\n private shouldSendProgress(run: RunState): boolean {\n if (run.sendingProgress) return false;\n const elapsed = (Date.now() - run.lastProgressSent) / 1000;\n return elapsed >= getHermesConfig().progressRateLimitSec;\n }\n\n /** \u53D1\u9001\u8FDB\u5EA6\u5361\u7247\uFF08\u6E32\u67D3\u5931\u8D25\u964D\u7EA7\u7EAF\u6587\u672C\uFF09 */\n private async sendProgressCard(runId: string): Promise<void> {\n const run = this.activeRuns.get(runId);\n if (!run || run.sendingProgress) return;\n run.sendingProgress = true;\n\n const now = Date.now();\n const elapsed = this.formatElapsed(now - run.startedAt);\n\n try {\n const image = await renderProgressImage({\n tools: run.tools,\n currentTool: run.currentTool,\n // \u9010\u53E5\u4E0B\u53D1\u540E pendingText \u53EA\u5269\u672A\u6210\u5F62\u7684\u5C3E\u5DF4\uFF0C\u8FDB\u5EA6\u9884\u89C8\u7528\u5B8C\u6574\u589E\u91CF\u66F4\u8D34\u8FD1\u5B9E\u9645\u8F93\u51FA\n messageDelta: run.messageDelta,\n elapsed\n });\n if (image) {\n await this.sendReplyImage(run.route, image);\n run.lastProgressSent = now;\n return;\n }\n\n // \u6587\u5B57\u56DE\u9000\n const lines: string[] = [`\u23F3 Hermes \u6267\u884C\u4E2D (${elapsed})`];\n for (const tool of run.tools.slice(-8)) {\n const icon = tool.error ? '\u274C' : '\u2705';\n const duration = tool.duration ? ` (${this.formatElapsed(tool.duration)})` : '';\n const preview = tool.preview ? ` \u2192 ${tool.preview.slice(0, 80)}` : '';\n lines.push(`${icon} ${tool.name}${duration}${preview}`);\n }\n if (run.currentTool) {\n const preview = run.currentTool.preview ? ` \u2192 ${run.currentTool.preview.slice(0, 80)}` : '';\n lines.push(`\u23F3 ${run.currentTool.name}...${preview}`);\n }\n await this.sendReply(run.route, lines.join('\\n'));\n run.lastProgressSent = now;\n } finally {\n run.sendingProgress = false;\n }\n }\n\n // ===================================================================\n // \u6D88\u606F\u683C\u5F0F\u5316\uFF08\u5BCC\u6587\u672C \u2192 AI \u53EF\u7406\u89E3\u7684\u6587\u672C + \u56FE\u7247\uFF09\n // ===================================================================\n\n /** \u683C\u5F0F\u5316 Unix \u65F6\u95F4\u6233 */\n private formatTime(unixTs: number): string {\n const date = new Date(unixTs * 1000);\n const pad = (value: number) => String(value).padStart(2, '0');\n return `${date.getFullYear()}/${pad(date.getMonth() + 1)}/${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;\n }\n\n /**\n * \u5C06 OneBot \u6D88\u606F\u8F6C\u6362\u4E3A AI \u53EF\u7406\u89E3\u7684\u5BCC\u6587\u672C\u683C\u5F0F\uFF08\u59D4\u6258 message.ts \u7684\u6DF1\u5EA6\u89E3\u6790\uFF09\u3002\n * \u6DF1\u5EA6\u89C4\u5219\uFF1A\u5F15\u7528/\u804A\u5929\u8BB0\u5F55\u9010\u5C42\u5C55\u5F00\uFF08depth+1\uFF09\uFF0Cdepth \u2264 1 \u7684\u56FE\u7247\u8F6C\u53D1\u539F\u56FE\u7ED9 Hermes\uFF0C\n * depth 2 \u53EA\u89E3\u6790\u6587\u5B57\uFF0Cdepth \u2265 3 \u4E00\u5F8B\u8BB0\u4F5C [\u804A\u5929\u8BB0\u5F55]\uFF1B\u8BE6\u89C1 message.ts \u6587\u4EF6\u5934\u6DF1\u5EA6\u8868\u3002\n */\n private async formatMessage(\n message: ReceiveMessage | string,\n groupId: number | undefined\n ): Promise<FormattedMessage> {\n const parsed = await parseMessage(message, groupId, this.messageResolvers(groupId));\n return { text: parsed.text, images: parsed.images };\n }\n\n // ===================================================================\n // \u6D88\u606F\u5904\u7406\u4E3B\u5165\u53E3\n // ===================================================================\n\n private async handleMessageSafe(bot: QQBot, msg: GroupMessageWSMSG | PrivateMessageWSMSG): Promise<void> {\n // \u9632\u81EA\u73AF\uFF1ANapCat \u914D\u7F6E\u56DE\u62A5\u81EA\u8EAB\u6D88\u606F\u65F6\u76F4\u63A5\u5FFD\u7565\n if (msg.user_id === msg.self_id) return;\n this.bot = bot;\n\n const route: RouteInfo =\n msg.message_type === 'group'\n ? { type: 'group', groupId: String(msg.group_id), userId: String(msg.sender.user_id) }\n : { type: 'user', userId: String(msg.sender.user_id) };\n\n if (!this.canChat(route)) return;\n\n const config = getHermesConfig();\n const rawText = this.extractText(msg.message);\n\n // \u5BA1\u6279\u56DE\u590D\u4F18\u5148\u4E8E\u89E6\u53D1\u5224\u5B9A\uFF08\u4EC5\u7BA1\u7406\u5458\u7684\u9009\u62E9\u7C7B\u6587\u672C\u4F1A\u88AB\u62E6\u622A\uFF0C\u907F\u514D\u8BEF\u4F24\u6B63\u5E38\u804A\u5929\uFF09\n if (rawText && (await this.handleApprovalReply(route, rawText, msg.message_id))) return;\n\n // \u89E6\u53D1\u5224\u5B9A\uFF1A\u79C1\u804A\u59CB\u7EC8\u89E6\u53D1\uFF1B\u7FA4\u804A\u6309 @\u63D0\u53CA/\u5173\u952E\u8BCD/requireMention\uFF08\u542B\u547D\u4EE4\u5B88\u536B\uFF09\n let triggered = true;\n let reason = 'private';\n if (route.type === 'group') {\n const decision = decideGroupTrigger({\n text: rawText,\n mentioned: this.hasAtSelf(msg.message),\n requireMention: config.requireMention,\n keywordTriggers: config.keywordTriggers\n });\n triggered = decision.triggered;\n reason = decision.reason ?? '';\n\n if (!triggered) {\n // \u672A\u89E6\u53D1\u4F46\u7FA4\u804A\u6D88\u606F\u4ECD\u9700\u8BB0\u5F55\u5230\u80CC\u666F\u4E0A\u4E0B\u6587\uFF08\u4F9B AI \u611F\u77E5\u7FA4\u804A\u6C1B\u56F4\uFF09\n if (rawText) {\n this.appendHistory(this.getSessionKey(route), 'user', `${this.senderLabel(msg.sender, route.userId)}: ${rawText}`, route.userId);\n }\n return;\n }\n }\n\n bot.logger?.info(`[Hermes] \u89E6\u53D1: ${reason} from ${route.userId} in ${route.type}:${route.groupId || route.userId}`);\n\n const formatted = await this.formatMessage(msg.message, msg.message_type === 'group' ? msg.group_id : undefined);\n // \u6D88\u606F\u4E2D\u7684 #\u7F16\u53F7 \u4F1A\u88AB\u6D88\u606F\u7BA1\u9053\u81EA\u52A8\u6E32\u67D3\u5361\u7247\u65F6\u52A0\u6CE8\u8BB0\uFF0C\u907F\u514D AI \u518D\u8C03\u67E5Issue\u6280\u80FD\u91CD\u590D\u53D1\u5361\n const text = appendAutoCardNote(formatted.text, this.countAutoCardReferences(formatted.text, route));\n if (!text) return;\n\n // \u505C\u6B62\u547D\u4EE4\n if (isStopCommand(text)) {\n await this.handleStopCommand(route);\n return;\n }\n\n // \u6E05\u9664\u4E0A\u4E0B\u6587\u547D\u4EE4\n if (isResetCommand(text)) {\n const newVersion = this.clearSession(route);\n await this.sendReplyWithMention(route, `\u2705 \u4E0A\u4E0B\u6587\u5DF2\u6E05\u9664\uFF0C\u5F00\u59CB\u65B0\u5BF9\u8BDD (v${newVersion})`, msg.message_id);\n return;\n }\n\n // \u8D34\u4E0A\u300C\u5904\u7406\u4E2D\u300D\u8868\u60C5\u56DE\u5E94\uFF08\u7ACB\u5373\u6301\u4E45\u5316\uFF0C\u8FDB\u7A0B\u5D29\u6E83\u65F6\u7531\u4E0B\u6B21\u542F\u52A8\u6E05\u7406\u6B8B\u7559\uFF09\u3002\n // \u79C1\u804A\u6D88\u606F NapCat \u901A\u5E38\u4E0D\u652F\u6301\u8868\u60C5\u56DE\u5E94\uFF1A\u5931\u8D25\u65F6\u9000\u5316\u4E3A\u4E00\u6761\u6587\u5B57\u56DE\u6267\uFF0C\n // \u8BA9\u7528\u6237\u77E5\u9053 AI \u5DF2\u6536\u5230\uFF08reactionKey \u4E3A null \u8868\u793A\u6CA1\u8D34\u4E0A\uFF09\u3002\n const reactionKey = await this.reactions.addQQ(bot, msg.message_id);\n const receiptId =\n reactionKey === null && route.type === 'user' ? await this.sendPrivateReceipt(route) : undefined;\n\n await this.startHermesRun(\n bot,\n route,\n text,\n formatted.images,\n this.senderLabel(msg.sender, route.userId),\n msg.message_id,\n 0,\n reactionKey ?? undefined,\n receiptId\n );\n }\n\n /**\n * \u79C1\u804A\u6587\u5B57\u56DE\u6267\uFF1A\u8D34\u8868\u60C5\u5931\u8D25\u65F6\u53D1\u4E00\u6761\u7B80\u77ED\u63D0\u793A\uFF0C\u8BA9\u7528\u6237\u77E5\u9053 AI \u5DF2\u6536\u5230\u3002\n * \u8FD4\u56DE\u56DE\u6267\u6D88\u606F id\uFF08\u4F9B AI \u56DE\u590D\u65F6\u64A4\u56DE\uFF1B\u64A4\u56DE\u5931\u8D25\u5219\u7559\u5728\u804A\u5929\u91CC\uFF0C\u4E0D\u5F71\u54CD\u5BF9\u8BDD\uFF09\u3002\n */\n private async sendPrivateReceipt(route: RouteInfo): Promise<number | undefined> {\n const config = getHermesConfig();\n if (!config.privateReactionFallbackText || !config.privateReceiptText.trim()) return undefined;\n if (!this.bot || route.type !== 'user') return undefined;\n try {\n return await this.bot.sendPrivateMsg(route.userId, [\n { type: 'text', data: { text: config.privateReceiptText } }\n ]);\n } catch (error) {\n this.bot.logger?.warn(`[Hermes] \u79C1\u804A\u6587\u5B57\u56DE\u6267\u53D1\u9001\u5931\u8D25: ${(error as Error).message}`);\n return undefined;\n }\n }\n\n /** \u64A4\u56DE\u79C1\u804A\u6587\u5B57\u56DE\u6267\uFF08AI \u5F00\u59CB\u56DE\u590D\u65F6\u6E05\u6389\uFF0C\u907F\u514D\u56DE\u6267\u5E38\u9A7B\u804A\u5929\uFF1B\u5931\u8D25\u53EA\u8BB0\u65E5\u5FD7\uFF09 */\n private async recallReceipt(route: RouteInfo, receiptId: number | undefined): Promise<void> {\n if (receiptId === undefined || !this.bot) return;\n try {\n await this.bot.axiosInstance.post('/delete_msg', { message_id: receiptId });\n } catch (error) {\n this.bot.logger?.debug(`[Hermes] \u64A4\u56DE\u79C1\u804A\u56DE\u6267\u5931\u8D25\uFF08${receiptId}\uFF09: ${(error as Error).message}`);\n }\n }\n\n // ===================================================================\n // Discord \u6D88\u606F\u5904\u7406\n // ===================================================================\n\n /** Discord \u6D88\u606F\u5904\u7406\u4E3B\u5165\u53E3\uFF08\u4E0E QQ \u5171\u7528\u89E6\u53D1/\u5BA1\u6279/\u4F1A\u8BDD\u903B\u8F91\uFF09 */\n private async handleDiscordMessageSafe(bot: QQBot, message: DiscordMessage, params: DiscordChatParams): Promise<void> {\n if (message.author.bot || message.webhookId) return;\n this.bot = bot;\n\n const route: RouteInfo = {\n type: 'discord',\n groupId: params.groupId,\n userId: `dc:${message.author.id}`,\n channelId: message.channelId,\n discord: params\n };\n if (!this.canChat(route)) return;\n\n const config = getHermesConfig();\n const rawText = this.plainDiscordText(message, params.botUserId);\n\n // \u5BA1\u6279\u56DE\u590D\u4F18\u5148\u4E8E\u89E6\u53D1\u5224\u5B9A\uFF08\u4EC5\u7BA1\u7406\u5458\u7684\u9009\u62E9\u7C7B\u6587\u672C\u4F1A\u88AB\u62E6\u622A\uFF09\n if (rawText && (await this.handleApprovalReply(route, rawText, 0))) return;\n\n const mentioned = params.botUserId ? message.mentions.has(params.botUserId) : false;\n const decision = decideGroupTrigger({\n text: rawText,\n mentioned,\n requireMention: config.requireMention,\n keywordTriggers: config.keywordTriggers\n });\n\n const displayName = message.member?.displayName ?? message.author.username;\n const label = this.senderLabel({ nickname: displayName }, route.userId);\n\n if (!decision.triggered) {\n // \u672A\u89E6\u53D1\u7684\u9891\u9053\u6D88\u606F\u8BB0\u5F55\u5230\u5171\u4EAB\u4F1A\u8BDD\u7684\u80CC\u666F\u4E0A\u4E0B\u6587\uFF08AI \u53EF\u611F\u77E5\u4E24\u4FA7\u5BF9\u8BDD\uFF09\n if (rawText) {\n this.appendHistory(this.getSessionKey(route), 'user', `${label}: ${rawText}`, route.userId);\n }\n return;\n }\n\n bot.logger?.info(\n `[Hermes] \u89E6\u53D1: ${decision.reason} from ${route.userId} in discord:${params.guildName}#${params.channelName}`\n );\n\n const formatted = await this.formatDiscordMessage(message, params.botUserId);\n // \u4E0E QQ \u4FA7\u540C\u7406\uFF1A#\u7F16\u53F7 \u5DF2\u7531 DiscordBridge \u81EA\u52A8\u6E32\u67D3\u5361\u7247\u65F6\u52A0\u6CE8\u8BB0\uFF0C\u907F\u514D\u6280\u80FD\u91CD\u590D\u53D1\u5361\n const text = appendAutoCardNote(formatted.text, this.countAutoCardReferences(formatted.text, route));\n if (!text) return;\n\n if (isStopCommand(text)) {\n await this.handleStopCommand(route);\n return;\n }\n if (isResetCommand(text)) {\n const newVersion = this.clearSession(route);\n await this.sendReplyWithMention(route, `\u2705 \u4E0A\u4E0B\u6587\u5DF2\u6E05\u9664\uFF0C\u5F00\u59CB\u65B0\u5BF9\u8BDD (v${newVersion})`, 0);\n return;\n }\n\n // \u8D34\u4E0A\u300C\u5904\u7406\u4E2D\u300D\u8868\u60C5\u56DE\u5E94\uFF08\u7ACB\u5373\u6301\u4E45\u5316\uFF0C\u6458\u9664\u52A8\u4F5C\u7531\u59D4\u6258\u5B8C\u6210\uFF09\n let reactionKey: string | undefined;\n if (config.reactionEnabled) {\n try {\n await message.react(config.reactionEmojiDiscord);\n reactionKey = this.reactions.persistDiscord(message.channelId, message.id, config.reactionEmojiDiscord);\n } catch (error) {\n bot.logger?.warn(`[Hermes] Discord \u8868\u60C5\u56DE\u5E94\u5931\u8D25: ${(error as Error).message}`);\n }\n }\n\n await this.startHermesRun(bot, route, text, formatted.images, label, 0, 0, reactionKey);\n }\n\n /** Discord \u6D88\u606F\u6B63\u6587\u7EAF\u6587\u672C\u5316\uFF1A\u63D0\u53CA/\u89D2\u8272/\u9891\u9053/\u81EA\u5B9A\u4E49\u8868\u60C5 \u2192 \u53EF\u8BFB\u6587\u672C\uFF0C\u5E76\u5254\u9664\u5BF9 bot \u7684\u63D0\u53CA */\n private plainDiscordText(message: DiscordMessage, botUserId?: string): string {\n let text = message.content ?? '';\n if (botUserId) text = text.replace(new RegExp(`<@!?${botUserId}>`, 'g'), '');\n text = text.replace(/<@!?(\\d+)>/g, (_match, id: string) => {\n const name = message.mentions.members?.get(id)?.displayName ?? message.mentions.users.get(id)?.username;\n return `@${name ?? id}(${id})`;\n });\n text = text.replace(/<@&(\\d+)>/g, (_match, id: string) => {\n return `@${message.guild?.roles.cache.get(id)?.name ?? id}`;\n });\n text = text.replace(/<#(\\d+)>/g, (_match, id: string) => {\n return `#${message.guild?.channels.cache.get(id)?.name ?? id}`;\n });\n text = text.replace(/<a?:(\\w{2,32}):\\d+>/g, ':$1:');\n return text.trim();\n }\n\n /**\n * \u5C06 Discord \u6D88\u606F\u8F6C\u6362\u4E3A AI \u53EF\u7406\u89E3\u7684\u5BCC\u6587\u672C\u683C\u5F0F\uFF1A\n * \u6B63\u6587 \u2192 \u53EF\u8BFB\u6587\u672C\uFF1B\u56DE\u590D\u5F15\u7528 \u2192 \u5F15\u7528\u5757\uFF08\u542B\u5176\u4E2D\u56FE\u7247\uFF09\uFF1B\u56FE\u7247\u9644\u4EF6 \u2192 [\u56FE\u7247] + \u539F\u56FE\n * \uFF08forwardImages \u53EF\u5173\uFF09\uFF1B\u5176\u4F59\u9644\u4EF6/\u8D34\u7EB8 \u2192 \u5360\u4F4D\u7B26\u3002\n */\n private async formatDiscordMessage(message: DiscordMessage, botUserId?: string): Promise<FormattedMessage> {\n const config = getHermesConfig();\n const parts: string[] = [];\n const images: string[] = [];\n\n const text = this.plainDiscordText(message, botUserId);\n if (text) parts.push(text);\n\n // \u56DE\u590D\u5F15\u7528\uFF1A\u62C9\u53D6\u88AB\u5F15\u7528\u6D88\u606F\uFF0C\u8F6C\u4E3A\u5F15\u7528\u5757\uFF08\u542B\u5176\u4E2D\u56FE\u7247\uFF09\n const referenceId = message.reference?.messageId;\n if (referenceId) {\n try {\n const original = await message.channel.messages.fetch(referenceId);\n const name = original.member?.displayName ?? original.author.username;\n const time = this.formatTime(Math.floor(original.createdTimestamp / 1000));\n const content = this.plainDiscordText(original, botUserId) || '[\u975E\u6587\u672C\u6D88\u606F]';\n parts.push(`> ${name}(dc:${original.author.id}) ${time}\\n> ${content}\\n`);\n if (config.forwardImages) {\n for (const attachment of original.attachments.values()) {\n if (attachment.contentType?.startsWith('image/')) {\n const dataUrl = await this.downloadImageToDataUrlSafe(attachment.url);\n if (dataUrl) images.push(dataUrl);\n }\n }\n }\n } catch {\n parts.push('> [\u5F15\u7528\u6D88\u606F\u83B7\u53D6\u5931\u8D25]\\n');\n }\n }\n\n // \u9644\u4EF6\uFF1A\u56FE\u7247\u8F6C\u591A\u6A21\u6001\u8F93\u5165\uFF0C\u5176\u4F59\u7C7B\u578B\u5360\u4F4D\n for (const attachment of message.attachments.values()) {\n if (attachment.contentType?.startsWith('image/')) {\n parts.push('[\u56FE\u7247]');\n if (config.forwardImages) {\n const dataUrl = await this.downloadImageToDataUrlSafe(attachment.url);\n if (dataUrl) images.push(dataUrl);\n }\n } else {\n parts.push(`[\u9644\u4EF6 ${attachment.name}]`);\n }\n }\n if (message.stickers.size > 0) parts.push('[\u8D34\u7EB8]');\n\n return { text: parts.join('\\n').trim(), images };\n }\n\n /** \u4E0B\u8F7D\u56FE\u7247\u4E3A data URL\uFF0C\u5931\u8D25\u8FD4\u56DE null\uFF08\u4EC5\u4FDD\u7559 [\u56FE\u7247] \u5360\u4F4D\u7B26\uFF09 */\n private async downloadImageToDataUrlSafe(url: string): Promise<string | null> {\n try {\n return await this.downloadImageToDataUrl(url);\n } catch (error) {\n this.bot?.logger?.error(`[Hermes] Discord \u56FE\u7247\u4E0B\u8F7D\u5931\u8D25 ${url}: ${(error as Error).message}`);\n return null;\n }\n }\n\n // ===================================================================\n // Hermes \u8FD0\u884C\u63D0\u4EA4\uFF08QQ \u4E0E Discord \u5171\u7528\uFF09\n // ===================================================================\n\n /**\n * \u7EC4\u88C5\u63D0\u793A\u8BCD\u3001\u63D0\u4EA4 Hermes \u8FD0\u884C\u5E76\u6302\u63A5 SSE \u4E8B\u4EF6\u6D41\u3002\n * feedbackDepth > 0 \u8868\u793A\u8FD9\u662F\u67E5\u8BE2\u6280\u80FD\u7684\u56DE\u5582\u8F6E\uFF1Atext \u662F\u5DE5\u5177\u6570\u636E\u800C\u975E\u7528\u6237\u53D1\u8A00\uFF0C\n * \u4E0D\u5E26\u53D1\u8A00\u4EBA\u524D\u7F00\uFF08\u5386\u53F2\u4E2D\u4FDD\u6301\u5728\u5F53\u524D\u5BF9\u8BDD\u4E3B\u7EBF\u4E0A\uFF09\u3002\n * reactionKey \u4E3A\u300C\u5904\u7406\u4E2D\u300D\u8868\u60C5\u56DE\u5E94\u7684\u6301\u4E45\u5316\u8BB0\u5F55\u952E\uFF1A\u9996\u8F6E\u7531\u89E6\u53D1\u5904\u8D34\u4E0A\u540E\u4F20\u5165\uFF0C\n * \u56DE\u5582\u8F6E\u539F\u6837\u8F6C\u79FB\uFF0C\u5BF9\u8BDD\u94FE\u5B8C\u5168\u7ED3\u675F\u65F6\u7531 handleRunComplete \u6458\u9664\u3002\n */\n private async startHermesRun(\n bot: QQBot,\n route: RouteInfo,\n text: string,\n images: string[],\n senderLabel: string,\n replyMsgId: number,\n feedbackDepth: number = 0,\n reactionKey?: string,\n receiptId?: number\n ): Promise<void> {\n const config = getHermesConfig();\n\n // \u6784\u5EFA\u7528\u6237\u63D0\u793A\u8BCD\uFF08\u79C1\u804A\u4E0E\u56DE\u5582\u8F6E\u4E0D\u5E26\u53D1\u8A00\u4EBA\u524D\u7F00\uFF09\n const isFeedback = feedbackDepth > 0;\n const userPrompt = isFeedback || route.type === 'user' ? text : `${senderLabel}: ${text}`;\n // \u5E26\u56FE\u6D88\u606F\uFF1A\u7EC4\u88C5 OpenAI \u591A\u6A21\u6001\u5185\u5BB9\u6BB5\uFF0C\u5C06\u539F\u56FE\u4F20\u7ED9 Hermes\n const userMessage: string | MessageContentPart[] =\n images.length > 0\n ? [\n { type: 'text', text: userPrompt },\n ...images.map((url): MessageContentPart => ({ type: 'image_url', image_url: { url } }))\n ]\n : userPrompt;\n\n const sessionKey = this.getSessionKey(route);\n const session = this.getSession(sessionKey);\n const sessionVersion = session.sessionVersion || 0;\n // Hermes sessionId \u6309\u4EBA\u533A\u5206\uFF1B\u7FA4\u804A/\u9891\u9053\u65E5\u5FD7\u4ECD\u6309\u7FA4\u5171\u4EAB\n const hermesSessionBase = route.type === 'user' ? `user_${route.userId}` : `group_${route.groupId}_user_${route.userId}`;\n const hermesSessionId = sessionVersion > 0 ? `${hermesSessionBase}:v${sessionVersion}` : hermesSessionBase;\n\n // \u56DE\u5582\u8F6E\uFF08\u5DE5\u5177\u6570\u636E\uFF09\u4E0E\u79C1\u804A\u4E0D\u5E26\u53D1\u8A00\u4EBA\u524D\u7F00\uFF1B\u5DE5\u5177\u6570\u636E\u6807\u8BB0 userId=\u5F53\u524D\u5BF9\u8BDD\u8005\uFF0C\n // \u5728\u5386\u53F2\u91CD\u7EC4\u65F6\u4FDD\u6301\u5728\u5BF9\u8BDD\u4E3B\u7EBF\u4E0A\uFF08\u4E0D\u4F1A\u88AB\u6807\u6CE8\u4E3A [\u7FA4\u804A\u80CC\u666F]\uFF09\n const historyContent = isFeedback || route.type === 'user' ? text : `${senderLabel}: ${text}`;\n this.appendHistory(sessionKey, 'user', historyContent, route.userId);\n\n // \u7EC4\u88C5\u7CFB\u7EDF\u63D0\u793A\u8BCD\uFF1Aconfigs/SOUL.md > systemPrompt > \u7A7A + \u7FA4\u804A\u4E0A\u4E0B\u6587 + \u6280\u80FD\u5217\u8868\u3002\n // \u7FA4\u804A\u4E0A\u4E0B\u6587\u663E\u5F0F\u58F0\u660E\u5F53\u524D\u5BF9\u8BDD\u8005\u4E0E\u5386\u53F2\u6807\u6CE8\u7EA6\u5B9A\uFF08\u914D\u5408 buildConversationHistory \u9632\u4E32\u53F0\uFF09\n const baseSystem = this.loadSoulPrompt() || config.systemPrompt || '';\n const groupContext = route.type === 'user' ? '' : buildGroupContext(route, senderLabel);\n const skillsPrompt = this.skillManager.buildPrompt();\n const systemPrompt = [baseSystem, groupContext, skillsPrompt].filter(Boolean).join('\\n\\n') || undefined;\n\n try {\n const { runId } = await this.hermes.submitRun({\n userMessage,\n sessionId: hermesSessionId,\n ...(systemPrompt !== undefined ? { systemPrompt } : {}),\n conversationHistory: buildConversationHistory(session.history.slice(0, -1), route.userId)\n });\n\n bot.logger?.info(`[Hermes] run \u5DF2\u63D0\u4EA4: ${runId}`);\n\n const runState: RunState = {\n route,\n tools: [],\n currentTool: null,\n startedAt: Date.now(),\n lastProgressSent: 0,\n sendingProgress: false,\n messageDelta: '',\n pendingText: '',\n sentTextLength: 0,\n lastTextSent: 0,\n finalOutput: '',\n userMsgId: replyMsgId,\n senderLabel,\n feedbackDepth,\n ...(reactionKey !== undefined ? { reactionKey } : {}),\n ...(receiptId !== undefined ? { receiptId } : {})\n };\n this.activeRuns.set(runId, runState);\n\n // \u6D41\u5F0F\u8F93\u51FA\uFF1A\u5DE5\u5177\u8C03\u7528\u524D\u540E\u5F3A\u5236\u4E0B\u53D1\u4E2D\u95F4\u6587\u672C\uFF1B\u7FA4\u804A\u53E6\u6709\u9010\u53E5\u5373\u65F6\u4E0B\u53D1\uFF08\u89C1 message.delta\uFF09\n const flushPendingText = async () => {\n const pending = runState.pendingText;\n if (!pending.trim()) return;\n // \u547D\u4E2D [SKILL: / MEDIA: \u7684\u6587\u672C\u7559\u5728\u7F13\u51B2\u533A\uFF0C\u4EA4\u7ED9 finishRun \u7EDF\u4E00\u5904\u7406\n if (RESERVED_LABEL_RE.test(pending)) return;\n runState.pendingText = '';\n runState.sentTextLength = runState.messageDelta.length;\n runState.lastTextSent = Date.now();\n try {\n if (this.useReplyUnits(runState.route)) {\n // \u6574\u6279\u5165\u94FE\uFF0C\u907F\u514D\u88AB\u968F\u540E\u5230\u8FBE\u7684 delta flush \u63D2\u961F\n await this.sendReplyUnits(\n runState.route,\n splitReplyIntoUnits(pending, this.replyUnitOptions()),\n runState.userMsgId,\n runState\n );\n } else {\n await this.sendReply(runState.route, pending.trim());\n }\n } catch (error) {\n this.bot?.logger?.error(`[Hermes] \u6587\u672C\u53D1\u9001\u9519\u8BEF: ${(error as Error).message}`);\n }\n };\n\n const stream = this.hermes.streamEvents(runId, {\n 'tool.started': ev => {\n void flushPendingText();\n runState.currentTool = {\n name: ev.tool,\n ...(ev.preview !== undefined ? { preview: ev.preview } : {}),\n startedAt: ev.timestamp * 1000\n };\n },\n\n 'tool.completed': ev => {\n runState.tools.push({\n name: ev.tool,\n duration: (ev.duration || 0) * 1000,\n error: ev.error || false,\n ...(runState.currentTool?.preview !== undefined ? { preview: runState.currentTool.preview } : {})\n });\n runState.currentTool = null;\n void flushPendingText();\n },\n\n 'message.delta': ev => {\n runState.messageDelta += ev.delta || '';\n runState.pendingText += ev.delta || '';\n\n // \u7FA4\u804A\u9010\u53E5\u5373\u65F6\u4E0B\u53D1\uFF1A\u5B8C\u6574\u53E5\u5B50\u7ACB\u523B\u53D1\u51FA\uFF0C\u672A\u6210\u5F62\u7684\u90E8\u5206\u7559\u5728 pendingText\n if (this.useReplyUnits(runState.route)) {\n void this.flushStreamUnits(runState).catch(error =>\n this.bot?.logger?.error(`[Hermes] \u9010\u53E5\u53D1\u9001\u9519\u8BEF: ${(error as Error).message}`)\n );\n }\n\n if (this.shouldSendProgress(runState) && runState.tools.length > 0) {\n this.sendProgressCard(runId).catch(error =>\n this.bot?.logger?.error(`[Hermes] \u8FDB\u5EA6\u53D1\u9001\u9519\u8BEF: ${(error as Error).message}`)\n );\n }\n },\n\n 'approval.request': ev => {\n this.handleApprovalRequest(runId, ev);\n },\n\n 'run.completed': ev => {\n runState.finalOutput = ev.output || '';\n },\n\n 'run.failed': ev => {\n runState.finalOutput = `\u274C \u6267\u884C\u5931\u8D25: ${ev.error || '\u672A\u77E5\u9519\u8BEF'}`;\n runState.failed = true;\n },\n\n _end: () => {\n void this.handleRunComplete(runId);\n },\n\n _error: () => {\n void this.handleRunComplete(runId);\n }\n });\n\n runState.stream = stream;\n } catch (error) {\n bot.logger?.error(`[Hermes] \u63D0\u4EA4\u9519\u8BEF: ${(error as Error).message}`);\n // \u63D0\u4EA4\u5373\u5931\u8D25\uFF1A\u6458\u9664\u300C\u5904\u7406\u4E2D\u300D\u8868\u60C5\u56DE\u5E94\uFF0C\u5E76\u64A4\u56DE\u79C1\u804A\u56DE\u6267\n if (reactionKey) {\n await this.removeReaction(reactionKey, route).catch(() => {});\n }\n await this.recallReceipt(route, receiptId);\n await this.sendReplyWithMention(route, `\u274C \u8C03\u7528 Hermes \u5931\u8D25: ${(error as Error).message}`, replyMsgId);\n }\n }\n\n /** \u6458\u9664\u300C\u5904\u7406\u4E2D\u300D\u8868\u60C5\u56DE\u5E94\u5E76\u79FB\u9664\u6301\u4E45\u5316\u8BB0\u5F55\uFF08QQ \u8D70 NapCat API\uFF1BDiscord \u8D70\u8DEF\u7531\u59D4\u6258\uFF09 */\n private async removeReaction(reactionKey: string, route: RouteInfo): Promise<void> {\n if (route.type === 'discord') {\n try {\n await route.discord?.removeReaction?.();\n } catch (error) {\n this.bot?.logger?.warn(`[Hermes] Discord \u6458\u9664\u8868\u60C5\u56DE\u5E94\u5931\u8D25: ${(error as Error).message}`);\n }\n this.reactions.drop(reactionKey);\n return;\n }\n await this.reactions.removeQQ(this.bot, reactionKey);\n }\n\n /** \u542F\u52A8\u65F6\u6E05\u7406\u6B8B\u7559\u7684 QQ \u8868\u60C5\u56DE\u5E94\uFF08\u4E0A\u6B21\u8FDB\u7A0B\u5728 AI \u5904\u7406\u4E2D\u9000\u51FA\u5BFC\u81F4\uFF09\uFF1BDiscord \u6B8B\u7559\u7531 DiscordBridge \u6E05\u7406 */\n public async cleanupStaleQQReactions(bot: QQBot): Promise<void> {\n await this.reactions.cleanupQQ(bot);\n }\n\n /** \u8BFB\u53D6 configs/SOUL.md \u4F5C\u4E3A\u7CFB\u7EDF\u63D0\u793A\u8BCD\uFF08\u6BCF\u6B21\u89E6\u53D1\u65F6\u8BFB\u53D6\uFF0C\u7F16\u8F91\u514D\u91CD\u542F\uFF09 */\n private loadSoulPrompt(): string {\n try {\n const soulPath = [process.env.SYNAPSE_PROFILE, process.env.SYNAPSE_HOME].filter(Boolean).map(dir => path.join(dir!, 'SOUL.md')).find(file => fs.existsSync(file)) || path.resolve(process.cwd(), 'configs', 'SOUL.md');\n if (fs.existsSync(soulPath)) return fs.readFileSync(soulPath, 'utf-8').trim();\n } catch {\n // \u8BFB\u53D6\u5931\u8D25\u6309\u65E0 SOUL.md \u5904\u7406\n }\n return '';\n }\n\n // ===================================================================\n // \u8FD0\u884C\u5B8C\u6210\u5904\u7406\n // ===================================================================\n\n /** \u5904\u7406\u8FD0\u884C\u5B8C\u6210\uFF1A\u53D1\u9001\u6700\u7EC8\u56DE\u590D\uFF0C\u6267\u884C\u6280\u80FD\u6807\u7B7E\uFF0C\u5904\u7406 MEDIA \u6807\u7B7E */\n private async handleRunComplete(runId: string): Promise<void> {\n const run = this.activeRuns.get(runId);\n if (!run) return;\n this.activeRuns.delete(runId);\n this.pendingApprovals.delete(runId);\n this.approvalMessageSent.delete(runId);\n\n // \u300C\u5904\u7406\u4E2D\u300D\u8868\u60C5\u56DE\u5E94\u9ED8\u8BA4\u968F\u672C\u8F6E\u7ED3\u675F\u6458\u9664\uFF1B\u67E5\u8BE2\u6280\u80FD\u56DE\u5582\u4F1A\u628A\u8BB0\u5F55\u952E\u8F6C\u79FB\u7ED9\n // \u4E0B\u4E00\u8F6E run\uFF08keepReaction\uFF09\uFF0C\u5BF9\u8BDD\u94FE\u5B8C\u5168\u7ED3\u675F\u65F6\u624D\u6458\u9664\n let keepReaction = false;\n try {\n await this.finishRun(run, () => {\n keepReaction = true;\n });\n } finally {\n if (!keepReaction && run.reactionKey) {\n await this.removeReaction(run.reactionKey, run.route).catch(() => {});\n }\n }\n }\n\n /** handleRunComplete \u4E3B\u4F53\uFF08\u62C6\u51FA\u4EE5\u4FBF\u7EDF\u4E00\u5728 finally \u4E2D\u6458\u9664\u8868\u60C5\u56DE\u5E94\uFF1BmarkKeepReaction \u7528\u4E8E\u56DE\u5582\u8F6C\u79FB\uFF09 */\n private async finishRun(run: RunState, markKeepReaction: () => void): Promise<void> {\n try {\n await this.finishRunBody(run, markKeepReaction);\n } finally {\n // \u6536\u5C3E\u515C\u5E95\uFF1A\u672C\u8F6E\u82E5\u5DF2\u628A\u56DE\u6267\u8F6C\u79FB\u7ED9\u67E5\u8BE2\u6280\u80FD\u7684\u56DE\u5582\u8F6E\uFF08receiptTransferred\uFF09\uFF0C\n // \u7531\u4E0B\u4E00\u8F6E\u8D1F\u8D23\u64A4\u56DE\uFF1B\u5426\u5219\u65E0\u8BBA\u8D70\u54EA\u6761\u5206\u652F\uFF08\u542B\u63D0\u524D return\uFF09\u90FD\u5728\u6B64\u64A4\u56DE\uFF0C\n // \u907F\u514D\u300C\u274C \u6267\u884C\u5931\u8D25\u300D\u8FD9\u7C7B\u8D70 sendReplyWithMention \u7684\u8DEF\u5F84\u628A\u56DE\u6267\u7559\u5728\u804A\u5929\u91CC\u3002\n if (run.receiptTransferred !== true) {\n await this.recallReceiptOnce(run);\n }\n }\n }\n\n /** finishRun \u4E3B\u4F53\uFF08\u62C6\u51FA\u4EE5\u4FBF\u7EDF\u4E00\u5728 finally \u4E2D\u64A4\u56DE\u79C1\u804A\u56DE\u6267\uFF09 */\n private async finishRunBody(run: RunState, markKeepReaction: () => void): Promise<void> {\n // \u5148\u7B49\u5728\u9014\u7684\u9010\u53E5\u4E0B\u53D1\u5B8C\u6210\uFF0C\u518D\u8BA1\u7B97/\u53D1\u9001\u5C3E\u6BB5\uFF0C\u5426\u5219\u5C3E\u6BB5\u4F1A\u63D2\u5230\u672A\u53D1\u5B8C\u7684\u53E5\u5B50\u4E4B\u524D\n await this.drainSends(run);\n let output = run.finalOutput || run.messageDelta;\n\n // \u9010\u53E5\u4E0B\u53D1\u8FC7\u65F6\uFF0CremainingText \u7531\u6D41\u5F0F\u8DEF\u5F84\u8D1F\u8D23\uFF1B\u8FD9\u91CC\u53EA\u5904\u7406\u300C\u5C1A\u672A\u4E0B\u53D1\u7684\u5C3E\u6BB5\u300D\u3002\n // \u8BF4\u660E\uFF1A\u6D41\u5F0F\u4E0B\u53D1\u7684\u5355\u5143\u5DF2\u88C1\u526A\u7A7A\u767D\uFF0C\u65E0\u6CD5\u4E0E\u6E90\u4E32\u6309\u4E0B\u6807\u5BF9\u9F50\uFF0C\u56E0\u6B64\u6539\u7528\u7F13\u51B2\u533A\u5224\u65AD\u2014\u2014\n // pendingText \u4E3A\u7A7A\u8868\u793A\u5168\u90E8\u5DF2\u53D1\u51FA\uFF0C\u975E\u7A7A\u8868\u793A\u8FD8\u6709\u672A\u6210\u5F62\u7684\u5C3E\u6BB5\uFF08\u53EF\u80FD\u540C\u65F6\u542B run.completed \u7684\u5B8C\u6574\u8F93\u51FA\u5DEE\u5F02\uFF09\u3002\n if (this.useReplyUnits(run.route)) {\n const pending = run.pendingText.trim();\n run.pendingText = '';\n // run.completed \u7684\u5B8C\u6574\u8F93\u51FA\u6BD4\u6D41\u5F0F\u589E\u91CF\u66F4\u957F\uFF08SSE \u4E22\u5E27\u7B49\uFF09\uFF1A\u8865\u53D1\u5DEE\u989D\uFF0C\u907F\u514D\u6F0F\u5B57\u3002\n // \u4EC5\u5728 messageDelta \u786E\u5B9E\u662F finalOutput \u524D\u7F00\u65F6\u8865\u53D1\uFF0C\u5426\u5219\u5B81\u53EF\u6F0F\u53D1\u4E5F\u4E0D\u91CD\u590D\u53D1\u3002\n const tailFromFinal =\n run.finalOutput && run.messageDelta && run.finalOutput.startsWith(run.messageDelta)\n ? run.finalOutput.slice(run.messageDelta.length).trim()\n : '';\n if (run.failed) {\n // \u8FD0\u884C\u5931\u8D25\uFF1A\u5931\u8D25\u6587\u6848\u4F18\u5148\u53D1\u51FA\uFF08\u5DF2\u9010\u53E5\u4E0B\u53D1\u7684\u90E8\u5206\u4E0D\u91CD\u590D\u53D1\uFF09\n output = run.finalOutput || output;\n } else if (pending) {\n output = pending;\n } else if (tailFromFinal) {\n output = tailFromFinal;\n } else if (run.sentTextLength > 0) {\n return; // \u5168\u90E8\u5DF2\u53D1\u9001\n }\n // \u5426\u5219\u4FDD\u6301 output = finalOutput || messageDelta\uFF08\u4F8B\u5982\u65E0\u6D41\u5F0F\u589E\u91CF\u3001\u4EC5 run.completed \u7ED9\u5168\u6587\uFF09\n } else if (run.sentTextLength > 0 && run.messageDelta && output) {\n const unsentFromDelta = run.messageDelta.slice(run.sentTextLength).trim();\n if (unsentFromDelta) {\n output = unsentFromDelta;\n } else if (output.length > run.sentTextLength) {\n output = output.slice(run.sentTextLength).trim();\n } else {\n return; // \u5168\u90E8\u5DF2\u53D1\u9001\n }\n }\n\n // \u8FD0\u884C\u6CA1\u6709\u4EFB\u4F55\u8F93\u51FA\uFF08SSE \u8FDE\u63A5\u5931\u8D25 / \u6A21\u578B\u9519\u8BEF\u7B49\uFF09\uFF0C\u901A\u77E5\u7528\u6237\u5E76\u8BB0\u5F55\u7A7A\u56DE\u590D\u5360\u4F4D\n if (!output?.trim()) {\n const errorMsg = '\u274C \u6267\u884C\u5931\u8D25\uFF1A\u672A\u6536\u5230 Hermes \u54CD\u5E94\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5';\n this.appendHistory(this.getSessionKey(run.route), 'assistant', errorMsg, run.route.userId, run.senderLabel);\n await this.sendReplyWithMention(run.route, errorMsg, run.userMsgId).catch(() => {});\n return;\n }\n\n // \u6267\u884C\u6280\u80FD\u6807\u7B7E\uFF08\u7BA1\u7406\u6280\u80FD\u7684\u6743\u9650\u6309\u8DEF\u7531\u53D1\u9001\u8005\u5224\u5B9A\uFF1AQQ \u53D6 operators\uFF0CDiscord \u53D6\u670D\u52A1\u5668\u6743\u9650\uFF09\uFF1B\n // \u5361\u7247\u7C7B\u6280\u80FD\u4EA7\u51FA\u7684\u56FE\u7247\u5728\u6587\u672C\u53D1\u9001\u524D\u9010\u5F20\u53D1\u51FA\n if (this.bot) {\n const skillOutput = await this.skillManager.processTags(output, run.route, {\n bot: this.bot,\n api: this.groupAdminApi(),\n isAdmin: () => this.isRouteAdmin(run.route)\n });\n output = skillOutput.text;\n for (const image of skillOutput.images) {\n await this.sendReplyImage(run.route, image).catch(error => {\n this.bot?.logger?.error(`[Hermes] \u6280\u80FD\u5361\u7247\u53D1\u9001\u5931\u8D25: ${(error as Error).message}`);\n });\n }\n\n // \u67E5\u8BE2\u7C7B\u6280\u80FD\uFF1A\u628A\u5DE5\u5177\u6570\u636E\u56DE\u5582\u7ED9 AI \u6574\u7406\u56DE\u7B54\uFF08\u518D\u63D0\u4EA4\u4E00\u8F6E run\uFF0C\u6709\u9632\u5FAA\u73AF\u4E0A\u9650\uFF09\n if (skillOutput.feedback.length > 0) {\n const depth = run.feedbackDepth ?? 0;\n if (depth < HermesBridge.MAX_FEEDBACK_DEPTH) {\n // \u4E2D\u95F4\u6587\u672C\uFF08\u5982\u300C\u6211\u67E5\u4E00\u4E0B\u300D\uFF09\u7167\u5E38\u53D1\u51FA\uFF0C\u5DE5\u5177\u6570\u636E\u8BB0\u5165\u5386\u53F2\u540E\u63D0\u4EA4\u56DE\u5582\u8F6E\n if (output) {\n await this.sendAiReplyUnits(run.route, output, run.userMsgId, run).catch(error => {\n this.bot?.logger?.error(`[Hermes] \u4E2D\u95F4\u56DE\u590D\u53D1\u9001\u5931\u8D25: ${(error as Error).message}`);\n });\n }\n this.appendHistory(this.getSessionKey(run.route), 'assistant', output, run.route.userId, run.senderLabel);\n const feedbackText = skillOutput.feedback.map(item => `[\u5DE5\u5177\u7ED3\u679C ${item.skill}]\\n${item.data}`).join('\\n\\n');\n // \u8868\u60C5\u56DE\u5E94\u8BB0\u5F55\u952E\u968F\u56DE\u5582\u8F6C\u79FB\u7ED9\u4E0B\u4E00\u8F6E\uFF0C\u94FE\u5C3E\u624D\u6458\u9664\uFF1B\u79C1\u804A\u56DE\u6267\u540C\u7406\u8F6C\u79FB\n markKeepReaction();\n const transferredReceipt = run.receiptId;\n if (transferredReceipt !== undefined) run.receiptTransferred = true;\n await this.startHermesRun(\n this.bot,\n run.route,\n `${feedbackText}\\n\\n\uFF08\u4EE5\u4E0A\u662F\u5DE5\u5177\u8FD4\u56DE\u7684\u6570\u636E\uFF0C\u8BF7\u636E\u6B64\u7528\u7B80\u77ED\u53E3\u8BED\u5316\u7684\u8BDD\u56DE\u590D\u5BF9\u65B9\uFF0C\u4E0D\u8981\u91CD\u590D\u8C03\u7528\u76F8\u540C\u6280\u80FD\uFF09`,\n [],\n run.senderLabel,\n run.userMsgId,\n depth + 1,\n run.reactionKey,\n transferredReceipt\n );\n return;\n }\n // \u5DF2\u8FBE\u56DE\u5582\u4E0A\u9650\uFF1A\u6570\u636E\u76F4\u63A5\u9644\u5728\u56DE\u590D\u91CC\u515C\u5E95\u53D1\u51FA\uFF0C\u4E0D\u518D\u63D0\u4EA4\u65B0 run\n output = `${output}\\n\\n${skillOutput.feedback.map(item => `${item.skill}\uFF1A${item.data}`).join('\\n')}`.trim();\n }\n }\n // \u8BB0\u5F55\u56DE\u590D\u5BF9\u8C61\uFF08userId + \u5C55\u793A\u6807\u7B7E\uFF09\uFF1A\u5386\u53F2\u63D0\u4EA4\u65F6\u636E\u5176\u6807\u6CE8\u300C\u4F60\u56DE\u590D xxx \u7684\u8BDD\u300D\uFF0C\u9632\u4E32\u53F0\n this.appendHistory(this.getSessionKey(run.route), 'assistant', output, run.route.userId, run.senderLabel);\n\n // \u89E3\u6790 MEDIA: \u6807\u7B7E\uFF0C\u53D1\u9001\u56FE\u7247\n const mediaRegex = /MEDIA:((?:\\/|https?:\\/\\/)[^\\s\\n]+)/g;\n const mediaPaths: string[] = [];\n let match: RegExpExecArray | null;\n while ((match = mediaRegex.exec(output)) !== null) {\n mediaPaths.push(match[1]!);\n }\n const remainingText = output.replace(/MEDIA:(?:\\/|https?:\\/\\/)[^\\s\\n]+/g, '').trim();\n\n for (const mediaPath of mediaPaths) {\n try {\n const imageData =\n mediaPath.startsWith('http://') || mediaPath.startsWith('https://')\n ? await this.downloadImageToBase64(mediaPath)\n : `base64://${fs.readFileSync(mediaPath).toString('base64')}`;\n await this.sendReplyImage(run.route, imageData);\n } catch (error) {\n this.bot?.logger?.error(`[Hermes] \u56FE\u7247\u53D1\u9001\u5931\u8D25 ${mediaPath}: ${(error as Error).message}`);\n }\n }\n\n if (remainingText) {\n // \u7FA4\u804A\u9010\u53E5\u53D1\u9001\uFF08\u5355\u6761\u8D85\u9608\u503C\u8F6C\u804A\u5929\u8BB0\u5F55\uFF09\uFF1B\u79C1\u804A\u4E0E Discord \u8D70\u539F\u6709\u6574\u6BB5\u53D1\u9001\n await this.sendAiReplyUnits(run.route, remainingText, run.userMsgId, run);\n }\n // \u515C\u5E95\uFF1A\u56DE\u590D\u5DF2\u53D1\u51FA\u4F46\u56DE\u6267\u8FD8\u6CA1\u64A4\uFF08\u4F8B\u5982\u8D70\u7684\u662F\u975E sendReplyUnit \u8DEF\u5F84\uFF09\uFF0C\u8FD9\u91CC\u8865\u4E00\u6B21\n await this.recallReceiptOnce(run);\n }\n\n // ===================================================================\n // \u505C\u6B62\u547D\u4EE4\n // ===================================================================\n\n /** \u5904\u7406\u505C\u6B62\u547D\u4EE4\uFF08QQ \u7FA4\u4E0E\u4E92\u901A\u7684 Discord \u9891\u9053\u6309\u4F1A\u8BDD\u57DF\u5339\u914D\uFF0C\u53EF\u4E92\u76F8\u505C\u6B62\uFF09 */\n private async handleStopCommand(route: RouteInfo): Promise<void> {\n for (const [runId, run] of this.activeRuns) {\n if (this.routeMatches(route, run.route)) {\n await this.hermes.stopRun(runId);\n await this.sendReply(route, '\u5DF2\u505C\u6B62\u5F53\u524D\u4EFB\u52A1 \u270B');\n return;\n }\n }\n await this.sendReply(route, '\u5F53\u524D\u6CA1\u6709\u6B63\u5728\u8FD0\u884C\u7684\u4EFB\u52A1');\n }\n\n // ===================================================================\n // \u5BA1\u6279\u5904\u7406\n // ===================================================================\n\n /** \u5904\u7406\u5BA1\u6279\u8BF7\u6C42 */\n private handleApprovalRequest(runId: string, ev: HermesApprovalEvent): void {\n const config = getHermesConfig();\n if (!config.approvalEnabled) return;\n\n const run = this.activeRuns.get(runId);\n if (!run) return;\n\n // \u9632\u6B62\u540C\u4E00 run \u91CD\u590D\u53D1\u9001\u5BA1\u6279\u6D88\u606F\uFF08\u4ECD\u66F4\u65B0\u5F85\u5BA1\u6279\u6570\u636E\uFF0C\u5DE5\u5177\u53EF\u80FD\u88AB\u591A\u6B21\u8C03\u7528\uFF09\n if (this.approvalMessageSent.has(runId)) {\n this.pendingApprovals.set(runId, { runId, route: run.route, data: ev, createdAt: Date.now() });\n return;\n }\n\n const route = run.route;\n const command = ev.command || '\u672A\u77E5\u547D\u4EE4';\n const patternKey = ev.pattern_key || '';\n\n const riskLevel = /rm|delete|sudo|chmod|chown|kill|reboot|shutdown/.test(patternKey)\n ? 'high'\n : /curl|wget|pip|npm|apt|docker/.test(patternKey)\n ? 'medium'\n : 'low';\n\n const approval: Approval = { runId, route, data: ev, createdAt: Date.now() };\n this.pendingApprovals.set(runId, approval);\n this.approvalMessageSent.set(runId, true);\n\n if (config.approvalTimeoutSec > 0) {\n approval.timeoutTimer = setTimeout(() => {\n void (async () => {\n if (this.pendingApprovals.has(runId)) {\n this.pendingApprovals.delete(runId);\n this.approvalMessageSent.delete(runId);\n try {\n await this.hermes.resolveApproval(runId, 'deny');\n await this.sendReply(route, `\u23F1\uFE0F \u5BA1\u6279\u8D85\u65F6\uFF0C\u5DF2\u81EA\u52A8\u62D2\u7EDD: ${command.slice(0, 100)}`);\n } catch {\n // \u5FFD\u7565\n }\n }\n })();\n }, config.approvalTimeoutSec * 1000);\n }\n\n this.sendApprovalCard(runId, command, riskLevel).catch(error =>\n this.bot?.logger?.error(`[Hermes] \u5BA1\u6279\u5361\u7247\u9519\u8BEF: ${(error as Error).message}`)\n );\n }\n\n /** \u53D1\u9001\u5BA1\u6279\u5361\u7247\uFF08\u6E32\u67D3\u5931\u8D25\u964D\u7EA7\u7EAF\u6587\u672C\uFF09 */\n private async sendApprovalCard(runId: string, command: string, riskLevel: string): Promise<void> {\n const approval = this.pendingApprovals.get(runId);\n if (!approval) return;\n\n const route = approval.route;\n const ev = approval.data;\n\n const image = await renderApprovalImage({\n command,\n riskLevel,\n toolName: ev.pattern_key || '',\n runId,\n preview: ev.description || ''\n });\n\n if (image) {\n await this.sendReplyImage(route, image);\n await this.sendReply(route, `\u26A0\uFE0F \u4E0A\u65B9\u547D\u4EE4\u9700\u8981\u5BA1\u6279\u3002\u56DE\u590D \"\u6279\u51C6\" / \"\u62D2\u7EDD\" / \"\u672C\u6B21\u5141\u8BB8\" / \"\u59CB\u7EC8\u5141\u8BB8\" \u6765\u5904\u7406\u3002`);\n return;\n }\n\n const toolName = ev.pattern_key || '';\n const preview = ev.description || '';\n const lines = [\n `\u26A0\uFE0F \u9700\u8981\u5BA1\u6279`,\n toolName ? `\u6A21\u5F0F: ${toolName}` : '',\n `\u547D\u4EE4: ${command.slice(0, 300)}`,\n preview ? `\u8BF4\u660E: ${preview.slice(0, 200)}` : '',\n riskLevel === 'high' ? `\u98CE\u9669: \uD83D\uDD34 \u9AD8` : riskLevel === 'medium' ? `\u98CE\u9669: \uD83D\uDFE1 \u4E2D` : `\u98CE\u9669: \uD83D\uDD35 \u4F4E`,\n '',\n `\u56DE\u590D \"\u6279\u51C6\" / \"\u62D2\u7EDD\" / \"\u672C\u6B21\u5141\u8BB8\" / \"\u59CB\u7EC8\u5141\u8BB8\" \u6765\u5904\u7406`,\n `(run: ${runId.slice(-8)})`\n ].filter(Boolean);\n await this.sendReply(route, lines.join('\\n'));\n }\n\n /**\n * \u5904\u7406\u5BA1\u6279\u56DE\u590D\uFF08\u5728\u89E6\u53D1\u5224\u5B9A\u4E4B\u524D\u8C03\u7528\uFF1B\u547D\u4E2D\u8FD4\u56DE true \u8868\u793A\u8BE5\u6D88\u606F\u5DF2\u4F5C\u4E3A\u5BA1\u6279\u5904\u7406\uFF09\u3002\n * \u4EC5\u62E6\u622A\u300C\u540C\u4E00\u4F1A\u8BDD\u57DF\u5B58\u5728\u5F85\u5BA1\u6279 + \u6587\u672C\u53EF\u89E3\u6790\u4E3A\u5BA1\u6279\u9009\u62E9 + \u53D1\u9001\u8005\u662F\u7BA1\u7406\u5458\u300D\u7684\u6D88\u606F\uFF1B\n * \u975E\u7BA1\u7406\u5458\u7684\u9009\u62E9\u7C7B\u6587\u672C\uFF08\u5982 \"ok\"\uFF09\u4E0D\u62E6\u622A\uFF0C\u907F\u514D\u5BA1\u6279\u7B49\u5F85\u671F\u95F4\u8BEF\u4F24\u6B63\u5E38\u804A\u5929\u3002\n * QQ \u7FA4\u4E0E\u4E92\u901A\u7684 Discord \u9891\u9053\u6309\u4F1A\u8BDD\u57DF\u5339\u914D\uFF0C\u4E24\u4FA7\u7BA1\u7406\u5458\u53EF\u4E92\u76F8\u5BA1\u6279\u3002\n */\n private async handleApprovalReply(route: RouteInfo, text: string, msgId: number): Promise<boolean> {\n for (const [runId, approval] of this.pendingApprovals) {\n if (!this.routeMatches(route, approval.route)) continue;\n\n const choice: ApprovalChoice | null = parseApprovalChoice(text);\n if (!choice) return false;\n\n // \u4EC5\u7BA1\u7406\u5458\u53EF\u5BA1\u6279\uFF08\u975E\u7BA1\u7406\u5458\u4E0D\u62E6\u622A\uFF0C\u6309\u666E\u901A\u6D88\u606F\u7EE7\u7EED\u8D70\u89E6\u53D1/\u8F6C\u53D1\u6D41\u7A0B\uFF09\n if (!this.isRouteAdmin(route)) return false;\n\n if (approval.timeoutTimer) clearTimeout(approval.timeoutTimer);\n this.pendingApprovals.delete(runId);\n this.approvalMessageSent.delete(runId);\n\n try {\n await this.hermes.resolveApproval(runId, choice);\n const labels: Record<ApprovalChoice, string> = {\n once: '\u5DF2\u6279\u51C6\uFF08\u4E00\u6B21\uFF09\u2705',\n deny: '\u5DF2\u62D2\u7EDD \u274C',\n always: '\u5DF2\u8BBE\u7F6E\u59CB\u7EC8\u5141\u8BB8 \u267E\uFE0F',\n session: '\u5DF2\u5141\u8BB8\u672C\u6B21\u4F1A\u8BDD \u2705'\n };\n await this.sendReplyWithMention(route, labels[choice], msgId);\n } catch (error) {\n await this.sendReply(route, `\u5BA1\u6279\u5904\u7406\u5931\u8D25: ${(error as Error).message}`);\n }\n\n return true;\n }\n return false;\n }\n}\n", "import {\n ApplicationCommandOptionType,\n ChatInputCommandInteraction,\n Client,\n PermissionFlagsBits,\n SlashCommandBuilder,\n SlashCommandSubcommandBuilder\n} from 'discord.js';\nimport { Arguments, CommandManager, CommandNode } from 'gugle-command';\nimport { BotCommandSource, executeCommandTokens } from '@/command';\nimport { isDiscordOperator } from '@/config/features';\nimport { QQBot } from '@/index';\n\n/**\n * Discord \u539F\u751F\u659C\u6760\u547D\u4EE4\uFF1A\u628A gugle-command \u5DF2\u6CE8\u518C\u7684\u547D\u4EE4\u6811\u8F6C\u6362\u4E3A Discord application\n * commands\uFF08guild \u7EA7\u6CE8\u518C\uFF0C\u5373\u65F6\u751F\u6548\uFF09\uFF0C\u4EA4\u4E92\u7ECF token \u7CBE\u786E\u56DE\u653E\u4EA4\u7ED9\u540C\u4E00\u5957\u547D\u4EE4\u5B9E\u73B0\u3002\n *\n * \u6620\u5C04\u89C4\u5219\uFF1A\n * - \u6839\u5B57\u9762\u91CF\uFF08help/mcv/server/...\uFF09\u2192 \u9876\u547D\u4EE4\uFF1B\u5176\u4E8C\u5C42\u5B57\u9762\u91CF\uFF08github\u2192bind \u7B49\uFF09\u2192 \u5B50\u547D\u4EE4\uFF1B\n * - \u53C2\u6570\u8282\u70B9 \u2192 options\uFF0C\u7C7B\u578B\u6309 decode \u8BC6\u522B\uFF08NUMBER\u2192Number\u3001BOOLEAN\u2192Boolean\u3001\u5176\u4F59 String\uFF09\uFF1B\n * - \u5FC5\u586B\u89C4\u5219\uFF1A\u53C2\u6570\u8282\u70B9\u7684\u7956\u5148\uFF08\u5230\u7236\u8282\u70B9\u4E3A\u6B62\uFF09\u90FD\u6CA1\u6709 exec \u5219\u5FC5\u586B\uFF0C\u5426\u5219\u9009\u586B\n * \uFF08/server <ip> [port]\uFF1Aip \u5FC5\u586B\u3001port \u9009\u586B\uFF0C\u5929\u7136\u4FDD\u8BC1\u5FC5\u586B\u5728\u9009\u586B\u4E4B\u524D\uFF09\uFF1B\n * - \u5B57\u9762\u91CF\u4E0E\u53C2\u6570\u6DF7\u6392\u3001\u4E09\u5C42\u4EE5\u4E0A\u5B57\u9762\u91CF\u5D4C\u5957\u4E0D\u652F\u6301\uFF08\u5F53\u524D\u547D\u4EE4\u6811\u65E0\u6B64\u60C5\u51B5\uFF0C\u51FA\u73B0\u65F6\u544A\u8B66\u8DF3\u8FC7\uFF09\u3002\n *\n * \u524D\u7F6E\u6761\u4EF6\uFF1Abot \u9080\u8BF7\u94FE\u63A5\u9700\u5305\u542B applications.commands \u6743\u9650\u8303\u56F4\uFF08\u5426\u5219\u6CE8\u518C\u62A5\u9519\u5E76\u544A\u8B66\uFF09\u3002\n */\n\n/** option \u89C4\u683C\uFF08\u4EA4\u4E92\u65F6\u6309\u58F0\u660E\u987A\u5E8F\u56DE\u653E token \u7528\uFF09 */\nexport interface SlashOptionSpec {\n /** \u5C0F\u5199 option \u540D */\n name: string;\n type: ApplicationCommandOptionType.String | ApplicationCommandOptionType.Number | ApplicationCommandOptionType.Boolean;\n required: boolean;\n}\n\n/** \u547D\u4EE4\u89C4\u683C\uFF1A\u4E00\u4E2A\u9876\u547D\u4EE4\u6216\u4E00\u4E2A\u5B50\u547D\u4EE4 */\nexport interface SlashCommandSpec {\n name: string;\n subcommand?: string;\n options: SlashOptionSpec[];\n}\n\nexport interface BuiltSlashCommands {\n builders: SlashCommandBuilder[];\n specs: SlashCommandSpec[];\n}\n\n/** \u547D\u4EE4/\u53C2\u6570\u63CF\u8FF0\uFF08Discord \u5FC5\u586B\u9879\uFF1B\u672A\u6536\u5F55\u65F6\u56DE\u9000\u901A\u7528\u6587\u6848\uFF09 */\nconst DESCRIPTIONS: Record<string, string> = {\n help: '\u5E2E\u52A9',\n mcv: '\u83B7\u53D6 Minecraft \u7248\u672C\u4FE1\u606F',\n server: '\u83B7\u53D6 Minecraft \u670D\u52A1\u5668\u72B6\u6001',\n wiki: '\u641C\u7D22 Minecraft \u7EF4\u57FA',\n github: 'GitHub \u7ED1\u5B9A/\u8BA2\u9605/\u6388\u6743',\n pardon: '\u628A\u7528\u6237\u79FB\u51FA\u9ED1\u540D\u5355\uFF08\u7BA1\u7406\u5458\uFF09',\n reload: '\u91CD\u8F7D\u914D\u7F6E\u4E0E\u6570\u636E\u6587\u4EF6\uFF08\u7BA1\u7406\u5458\uFF09',\n reboot: '\u91CD\u542F\u5E94\u7528\uFF08\u7BA1\u7406\u5458\uFF09',\n shutdown: '\u5173\u95ED\u5E94\u7528\uFF08\u7BA1\u7406\u5458\uFF09',\n pvtime: '\u67E5\u8BE2\u5F53\u524D\u662F\u6881\u6587\u5CF0\u65F6\u95F4\u8FD8\u662F\u6881\u6587\u8C37\u65F6\u95F4',\n bind: '\u7ED1\u5B9A GitHub \u7528\u6237\u540D',\n subscribe: '\u8BA2\u9605\u4ED3\u5E93\u6D88\u606F\u63A8\u9001',\n allow: '\u6388\u6743\u4ED3\u5E93\u8BBF\u95EE\uFF08\u7BA1\u7406\u5458\uFF09',\n ip: '\u670D\u52A1\u5668\u5730\u5740',\n port: '\u7AEF\u53E3',\n query: '\u641C\u7D22\u8BCD',\n username: 'GitHub \u7528\u6237\u540D',\n repository: 'owner/repo',\n target: 'owner \u6216 owner/repo',\n userid: 'QQ \u53F7'\n};\n\nfunction describe(name: string): string {\n return DESCRIPTIONS[name] ?? `\u53C2\u6570 ${name}`;\n}\n\n/** Discord chat-input \u540D\u79F0\u7EA6\u675F\uFF1A\u5C0F\u5199\u5B57\u6BCD\u6570\u5B57 _ -\uFF0C1-32 \u5B57\u7B26 */\nconst DISCORD_NAME_RE = /^[a-z0-9_-]{1,32}$/;\n\n/** \u53C2\u6570\u8282\u70B9 \u2192 option \u540D\uFF08`<userId>` \u2192 `userid`\uFF09 */\nfunction optionName(node: CommandNode): string {\n const raw = node.toString().replace(/^<(.+)>$/, '$1');\n return raw.toLowerCase();\n}\n\n/** \u53C2\u6570\u8282\u70B9 \u2192 option \u7C7B\u578B\uFF08\u6309\u6784\u9020\u65F6\u5B58\u5165\u7684 decorator \u8EAB\u4EFD\u8BC6\u522B\uFF09 */\nfunction optionType(node: CommandNode): SlashOptionSpec['type'] {\n const decorator = (node as unknown as { decorator?: unknown }).decorator;\n if (decorator === Arguments.NUMBER) return ApplicationCommandOptionType.Number;\n if (decorator === Arguments.BOOLEAN) return ApplicationCommandOptionType.Boolean;\n return ApplicationCommandOptionType.String;\n}\n\n/**\n * \u6536\u96C6\u8282\u70B9\u7684\u53C2\u6570\u94FE\uFF08\u6CBF\u7B2C\u4E00\u4E2A\u53C2\u6570\u5B50\u8282\u70B9\u4E0B\u884C\uFF09\u3002\n * \u5FC5\u586B\u89C4\u5219\uFF1A\u7956\u5148\uFF08rootLiteral \u5230\u7236\u8282\u70B9\uFF09\u90FD\u6CA1\u6709 exec \u5219\u5FC5\u586B\u3002\n */\nfunction collectOptions(rootLiteral: CommandNode, warn: (msg: string) => void): SlashOptionSpec[] {\n const options: SlashOptionSpec[] = [];\n let ancestorHasExec = rootLiteral.exec !== undefined;\n let current: CommandNode = rootLiteral;\n for (;;) {\n const literals = current.children.filter(child => child.isLiteral());\n const args = current.children.filter(child => !child.isLiteral());\n if (args.length === 0) return options;\n if (literals.length > 0) {\n warn(`\u53C2\u6570\u94FE\u4E2D\u51FA\u73B0\u5B57\u9762\u91CF\u5206\u652F\uFF08${rootLiteral.toString()}\uFF09\uFF0C\u659C\u6760\u547D\u4EE4\u4E0D\u652F\u6301\uFF0C\u5DF2\u622A\u65AD`);\n return options;\n }\n if (args.length > 1) {\n warn(`\u540C\u4E00\u5C42\u7EA7\u6709\u591A\u4E2A\u53C2\u6570\u8282\u70B9\uFF08${rootLiteral.toString()}\uFF09\uFF0C\u53EA\u53D6\u7B2C\u4E00\u4E2A`);\n }\n const arg = args[0]!;\n const required = !ancestorHasExec;\n options.push({ name: optionName(arg), type: optionType(arg), required });\n ancestorHasExec = ancestorHasExec || arg.exec !== undefined;\n current = arg;\n }\n}\n\nfunction applyOptions(\n target: SlashCommandBuilder | SlashCommandSubcommandBuilder,\n options: SlashOptionSpec[]\n): void {\n for (const option of options) {\n if (option.type === ApplicationCommandOptionType.Number) {\n target.addNumberOption(builder =>\n builder.setName(option.name).setDescription(describe(option.name)).setRequired(option.required)\n );\n } else if (option.type === ApplicationCommandOptionType.Boolean) {\n target.addBooleanOption(builder =>\n builder.setName(option.name).setDescription(describe(option.name)).setRequired(option.required)\n );\n } else {\n target.addStringOption(builder =>\n builder.setName(option.name).setDescription(describe(option.name)).setRequired(option.required)\n );\n }\n }\n}\n\nlet cachedSpecs: SlashCommandSpec[] = [];\n\n/** \u628A gugle-command \u547D\u4EE4\u6811\u8F6C\u6362\u4E3A Discord \u659C\u6760\u547D\u4EE4\u6784\u5EFA\u5668\u4E0E\u56DE\u653E\u89C4\u683C */\nexport function buildSlashCommands(manager: CommandManager, warn: (msg: string) => void = () => undefined): BuiltSlashCommands {\n const builders: SlashCommandBuilder[] = [];\n const specs: SlashCommandSpec[] = [];\n\n for (const root of manager.roots.values()) {\n for (const literal of root.children) {\n if (!literal.isLiteral()) continue;\n const name = literal.toString();\n if (!DISCORD_NAME_RE.test(name)) {\n warn(`\u547D\u4EE4\u540D ${name} \u4E0D\u7B26\u5408 Discord \u547D\u540D\u7EA6\u675F\uFF0C\u5DF2\u8DF3\u8FC7`);\n continue;\n }\n\n const literalChildren = literal.children.filter(child => child.isLiteral());\n const argumentChildren = literal.children.filter(child => !child.isLiteral());\n const builder = new SlashCommandBuilder().setName(name).setDescription(describe(name));\n\n if (literalChildren.length > 0 && argumentChildren.length > 0) {\n warn(`\u547D\u4EE4 ${name} \u5B57\u9762\u91CF\u4E0E\u53C2\u6570\u6DF7\u6392\uFF0C\u659C\u6760\u547D\u4EE4\u4E0D\u652F\u6301\uFF0C\u5DF2\u8DF3\u8FC7`);\n continue;\n }\n\n if (literalChildren.length > 0) {\n // \u4E8C\u5C42\u5B57\u9762\u91CF \u2192 \u5B50\u547D\u4EE4\n for (const sub of literalChildren) {\n const subName = sub.toString();\n if (!DISCORD_NAME_RE.test(subName)) {\n warn(`\u5B50\u547D\u4EE4\u540D ${name} ${subName} \u4E0D\u7B26\u5408 Discord \u547D\u540D\u7EA6\u675F\uFF0C\u5DF2\u8DF3\u8FC7`);\n continue;\n }\n if (sub.children.some(child => child.isLiteral())) {\n warn(`\u547D\u4EE4 ${name} ${subName} \u5B58\u5728\u4E09\u5C42\u5D4C\u5957\uFF0C\u659C\u6760\u547D\u4EE4\u4E0D\u652F\u6301\uFF0C\u5DF2\u8DF3\u8FC7`);\n continue;\n }\n const options = collectOptions(sub, warn);\n builder.addSubcommand(subBuilder => {\n subBuilder.setName(subName).setDescription(describe(subName));\n applyOptions(subBuilder, options);\n return subBuilder;\n });\n specs.push({ name, subcommand: subName, options });\n }\n } else {\n const options = collectOptions(literal, warn);\n applyOptions(builder, options);\n specs.push({ name, options });\n }\n\n builders.push(builder);\n }\n }\n\n cachedSpecs = specs;\n return { builders, specs };\n}\n\n/**\n * \u5BF9 bridges \u914D\u7F6E\u53BB\u91CD\u540E\u7684\u6BCF\u4E2A guild \u6CE8\u518C\u659C\u6760\u547D\u4EE4\uFF08guild \u7EA7\uFF0C\u5373\u65F6\u751F\u6548\uFF09\u3002\n * \u914D\u7F6E\u70ED\u91CD\u8F7D\u65B0\u589E\u7684 guild \u9700\u91CD\u542F\u540E\u624D\u4F1A\u6CE8\u518C\u3002\n */\nexport async function registerSlashCommands(client: Client<true>, guildIds: string[], bot: QQBot): Promise<void> {\n const { builders } = buildSlashCommands(bot.getCommandManager(), msg => bot.logger?.warn(`[DiscordBridge] ${msg}`));\n if (builders.length === 0) return;\n for (const guildId of guildIds) {\n const guild = client.guilds.cache.get(guildId);\n if (!guild) {\n bot.logger?.warn(`[DiscordBridge] \u6CE8\u518C\u659C\u6760\u547D\u4EE4\uFF1A\u672A\u627E\u5230\u670D\u52A1\u5668 ${guildId}`);\n continue;\n }\n try {\n await guild.commands.set(builders);\n bot.logger?.info(`[DiscordBridge] \u5DF2\u5728 ${guild.name} \u6CE8\u518C ${builders.length} \u4E2A\u659C\u6760\u547D\u4EE4`);\n } catch (error) {\n bot.logger?.error(\n `[DiscordBridge] \u6CE8\u518C\u659C\u6760\u547D\u4EE4\u5931\u8D25\uFF08${guild.name}\uFF09: ${error instanceof Error ? error.message : String(error)}` +\n `\u3002\u8BF7\u786E\u8BA4 bot \u9080\u8BF7\u94FE\u63A5\u5305\u542B applications.commands \u6743\u9650\u8303\u56F4\uFF08\u9700\u5728\u5F00\u53D1\u8005\u540E\u53F0\u91CD\u65B0\u751F\u6210\u9080\u8BF7\u5E76\u6388\u6743\uFF09`\n );\n }\n }\n}\n\n/**\n * Discord \u4EA4\u4E92\u547D\u4EE4\u6E90\uFF1A\u56DE\u590D\u7ECF interaction \u7684 deferReply \u2192 editReply/followUp \u5B8C\u6210\u3002\n * \u8EAB\u4EFD\u4E0E\u6743\u9650\u6620\u5C04\u4E0E DiscordMsgCommandSource \u4E00\u81F4\uFF08dc: \u524D\u7F00\u7528\u6237\u6807\u8BC6\u3001\u670D\u52A1\u5668\u6743\u9650\u5373\u7BA1\u7406\u5458\uFF09\u3002\n */\nexport class DiscordInteractionCommandSource implements BotCommandSource {\n private readonly bot: QQBot;\n private readonly interaction: ChatInputCommandInteraction;\n private readonly groupId: number | undefined;\n private responded = false;\n\n public constructor(bot: QQBot, interaction: ChatInputCommandInteraction, groupId?: number) {\n this.bot = bot;\n this.interaction = interaction;\n this.groupId = groupId;\n }\n\n public getUserId(): string {\n return `dc:${this.interaction.user.id}`;\n }\n\n public getGroupId(): number | undefined {\n return this.groupId;\n }\n\n public isAdmin(): boolean {\n if (isDiscordOperator(this.interaction.user.id)) return true;\n if (this.interaction.guild?.ownerId === this.interaction.user.id) return true;\n const permissions = this.interaction.memberPermissions;\n if (!permissions) return false;\n return permissions.has(PermissionFlagsBits.Administrator) || permissions.has(PermissionFlagsBits.ManageGuild);\n }\n\n /** \u7ACB\u5373 deferReply\uFF08\u4EA4\u4E92 3 \u79D2\u65F6\u9650\uFF09\uFF0C\u540E\u7EED success/fail \u7528 editReply/followUp */\n public async defer(): Promise<void> {\n await this.interaction.deferReply().catch(error => {\n this.bot.logger?.error(\n `[DiscordBridge] \u4EA4\u4E92 deferReply \u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`\n );\n });\n }\n\n public success(message: string): void {\n this.reply(message);\n }\n\n public fail(message: string): void {\n this.reply(message);\n this.bot.logger?.error(\n `[Discord:${this.interaction.user.username}] ${message}: /${this.interaction.commandName}`\n );\n }\n\n public getName(): string {\n return this.interaction.user.displayName ?? this.interaction.user.username;\n }\n\n public hasPermission(permission: string): boolean {\n if (!permission) return true;\n const getPermissionLevel = (text: string): number => {\n const level = Number.parseInt(text);\n if (Number.isNaN(level)) {\n return text === 'owner' ? 2 : text === 'admin' ? 1 : 0;\n }\n return level;\n };\n const isOwner = this.interaction.guild?.ownerId === this.interaction.user.id;\n const permissionLevel = isOwner ? 2 : this.isAdmin() ? 1 : 0;\n return permissionLevel >= getPermissionLevel(permission);\n }\n\n /** \u9996\u6B21\u56DE\u590D editReply\uFF0C\u5176\u540E followUp\uFF1B2000 \u5B57\u622A\u65AD */\n private reply(message: string): void {\n const content = message.length <= 2000 ? message : `${message.slice(0, 1984)}\\n\u2026\uFF08\u6D88\u606F\u8FC7\u957F\u5DF2\u622A\u65AD\uFF09`;\n if (!this.responded) {\n this.responded = true;\n this.interaction.editReply({ content }).catch(error => {\n this.bot.logger?.error(\n `[DiscordBridge] \u4EA4\u4E92\u56DE\u590D\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`\n );\n });\n return;\n }\n this.interaction.followUp({ content }).catch(error => {\n this.bot.logger?.error(\n `[DiscordBridge] \u4EA4\u4E92 followUp \u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`\n );\n });\n }\n}\n\n/**\n * \u659C\u6760\u547D\u4EE4\u4EA4\u4E92\u5206\u53D1\uFF1A\u628A option \u503C\u6309\u58F0\u660E\u987A\u5E8F\u56DE\u653E\u4E3A\u7CBE\u786E token\uFF08\u4E0D\u7ECF\u5B57\u7B26\u4E32\u5207\u5206\uFF0C\n * \u5E26\u7A7A\u683C\u7684\u5B57\u7B26\u4E32\u53C2\u6570\u4FDD\u6301\u5B8C\u6574\uFF09\uFF0C\u4EA4\u7ED9 gugle-command \u6267\u884C\u3002\n *\n * @param resolveGroupId \u7531 DiscordBridge \u6CE8\u5165\u7684\u9891\u9053 \u2192 \u6865\u63A5 QQ \u7FA4\u89E3\u6790\n */\nexport async function handleSlashInteraction(\n bot: QQBot,\n interaction: ChatInputCommandInteraction,\n resolveGroupId: (guildId: string, channelName: string | undefined) => number | undefined\n): Promise<void> {\n if (!interaction.guildId) return;\n const specs = cachedSpecs.length > 0 ? cachedSpecs : buildSlashCommands(bot.getCommandManager()).specs;\n const subcommand = interaction.options.getSubcommand(false) ?? undefined;\n const spec = specs.find(item => item.name === interaction.commandName && item.subcommand === subcommand);\n if (!spec) {\n await interaction.reply({ content: '\u672A\u77E5\u547D\u4EE4\uFF08\u53EF\u80FD\u521A\u66F4\u65B0\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\uFF09', ephemeral: true }).catch(() => undefined);\n return;\n }\n\n // \u6309\u58F0\u660E\u987A\u5E8F\u56DE\u653E token\uFF1B\u53EF\u9009\u53C2\u6570\u7F3A\u5931\u5373\u622A\u65AD\uFF08\u4F4D\u7F6E\u53C2\u6570\u5FC5\u987B\u8FDE\u7EED\uFF09\n const tokens = [spec.name];\n if (spec.subcommand) tokens.push(spec.subcommand);\n for (const option of spec.options) {\n const value = interaction.options.get(option.name)?.value;\n if (value === undefined || value === null) break;\n tokens.push(String(value));\n }\n\n const channel = interaction.channel;\n const channelName = channel && 'name' in channel ? (channel.name ?? undefined) : undefined;\n const source = new DiscordInteractionCommandSource(bot, interaction, resolveGroupId(interaction.guildId, channelName));\n await source.defer();\n try {\n executeCommandTokens(bot.getCommandManager(), source, tokens);\n } catch (error) {\n bot.logger?.error(\n `[DiscordBridge] \u659C\u6760\u547D\u4EE4\u6267\u884C\u5931\u8D25: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}`\n );\n }\n}\n", "import { ChannelType, Client, GatewayIntentBits, Guild, Message as DiscordMessage, TextChannel } from 'discord.js';\nimport { GroupMessageSentEvent, QQBot } from '@/index';\nimport { GroupMessageWSMSG, Message, SentMessage } from '@/type';\nimport { ConfigStore } from '@/config/manager';\nimport { escapeDiscord, isDiscordMarkdown, renderDiscordMessageToImage } from '@/features/discord-bridge/markdown';\nimport { DiscordMsgCommandSource, isDiscordAdmin } from '@/features/discord-bridge/source';\nimport { executeCommand, normalizeCommandText } from '@/command';\nimport { Github } from '@/features/github';\nimport { HermesBridge } from '@/features/hermes';\nimport { getHermesConfig } from '@/features/hermes/config';\nimport { ReactionManager } from '@/features/hermes/reaction';\nimport { handleSlashInteraction, registerSlashCommands } from '@/features/discord-bridge/slash';\n\n/**\n * QQ \u7FA4 \u21C4 Discord \u9891\u9053\u4E92\u901A\u6A21\u5757\u3002\n *\n * \u914D\u7F6E\uFF08configs/features/discord-bridge.json\uFF0Cv1\uFF0CConfigStore \u70ED\u91CD\u8F7D\uFF09\uFF1A\n * ```json\n * {\n * \"version\": 1,\n * \"token\": \"DISCORD_BOT_TOKEN\",\n * \"bridges\": {\n * \"940551045929639949#general\": {\n * \"group\": \"659356928\",\n * \"need_reply\": \"false\",\n * \"need_cmd\": \"false\"\n * }\n * }\n * }\n * ```\n * - key \u4E3A `\"\u670D\u52A1\u5668ID#\u9891\u9053\u540D\u79F0\"`\uFF08\u9891\u9053\u540D\u4EE5 Discord \u5F53\u524D\u540D\u4E3A\u51C6\uFF0C\u6539\u540D\u9700\u540C\u6B65\u6539\u914D\u7F6E\uFF09\uFF1B\n * - `group` \u4E3A\u4E92\u901A\u7684 QQ \u7FA4\u53F7\uFF1B\n * - `need_reply=true`\uFF1ADiscord\u2192QQ \u5168\u91CF\u8F6C\u53D1\uFF1BQQ\u2192Discord \u4EC5\u8F6C\u53D1\u300C\u56DE\u590D\u6865\u6D88\u606F\u300D\u7684\u56DE\u590D\u5185\u5BB9\uFF1B\n * - `need_cmd=true`\uFF1A\u53CC\u5411\u666E\u901A\u6D88\u606F\u5747\u4E0D\u8F6C\u53D1\uFF0C\u4EC5 `/send <msg> [group|channel]` \u4E0E\n * \u300C\u56DE\u590D\u6865\u6D88\u606F\u300D\u4E92\u901A\uFF08\u56DE\u590D\u4F1A\u4EE5\u56DE\u590D\u5F62\u5F0F\u56DE\u4F20\u5230\u5BF9\u4FA7\uFF09\uFF1B\n * - `/send` \u4E0D\u586B\u76EE\u6807\u65F6\u9ED8\u8BA4\u53D1\u9001\u5230\u914D\u7F6E\u6587\u4EF6\u4E2D\u7B2C\u4E00\u4E2A\u4E92\u901A\u6761\u76EE\u7684\u5BF9\u7AEF\u3002\n *\n * \u9644\u52A0\u80FD\u529B\uFF1A\n * - \u673A\u5668\u4EBA\u81EA\u8EAB\u53D1\u51FA\u7684 QQ \u7FA4\u6D88\u606F\u8F6C\u53D1\u5230\u8BE5\u7FA4\u7ED1\u5B9A\u7684\u9891\u9053\uFF08\u7ECF QQBot.onGroupMessageSent\n * \u94A9\u5B50\uFF0CfromBridge \u6807\u8BB0\u9632\u6B62\u56DE\u73AF\uFF09\uFF1A\u4E0E\u666E\u901A QQ \u6D88\u606F\u540C\u4E00\u5957\u95E8\u63A7\u2014\u2014need_cmd / need_reply\n * \u9891\u9053\u4EC5\u653E\u884C\u300C\u5BF9\u8BE5\u9891\u9053\u6865\u6D88\u606F\u7684\u56DE\u590D\u300D\uFF0Cwebhook \u63A8\u9001\uFF08GitHub \u8BA2\u9605\u901A\u77E5\uFF09\u4E00\u5F8B\u4E0D\u8FDB\uFF1B\n * - \u673A\u5668\u4EBA\u81EA\u5DF1\u5728\u4E92\u901A\u9891\u9053\u53D1\u51FA\u7684 Discord \u6D88\u606F\uFF08\u547D\u4EE4\u56DE\u590D\u3001AI \u56DE\u590D\u3001GitHub \u5361\u7247\u3001\n * \u659C\u6760\u547D\u4EE4\u56DE\u590D\u7B49\uFF09\u7ECF messageCreate \u56DE\u63A8\u540C\u6B65\u5230 QQ\uFF08\u6865\u81EA\u8EAB\u8F6C\u53D1\u8DF3\u8FC7\u9632\u56DE\u73AF\uFF0C\n * \u95E8\u63A7\u4E0E\u666E\u901A Discord \u6D88\u606F\u4E00\u81F4\uFF09\uFF0CQQ \u4FA7\u53EF\u770B\u5230\u5B8C\u6574\u5BF9\u8BDD\uFF1B\n * - Discord \u9891\u9053\u4E2D\u53EF\u76F4\u63A5\u4F7F\u7528\u5168\u90E8\u5DF2\u6CE8\u518C\u547D\u4EE4\uFF08/ \u6216 ! \u524D\u7F00\uFF09\uFF0C\u56DE\u590D\u53D1\u5728 Discord \u9891\u9053\uFF1B\n * \u672A\u6CE8\u518C\u547D\u4EE4\u7684 /xxx \u6587\u672C\u6309\u666E\u901A\u6D88\u606F\u8F6C\u53D1\uFF0C\u4E0D\u4F1A\u56DE\u53D1 Invalid command\uFF1B\n * - \u540C\u65F6\u628A\u547D\u4EE4\u6811\u6CE8\u518C\u4E3A Discord \u539F\u751F\u659C\u6760\u547D\u4EE4\uFF08guild \u7EA7\u3001\u81EA\u52A8\u8865\u5168\uFF09\uFF0C\u4EA4\u4E92\u7ECF token\n * \u7CBE\u786E\u56DE\u653E\u6267\u884C\uFF0C\u56DE\u590D\u53D1\u5728 Discord \u9891\u9053\uFF08\u9700\u9080\u8BF7\u94FE\u63A5\u542B applications.commands \u6743\u9650\uFF09\uFF1B\n * - Discord \u6D88\u606F\u4E2D\u7684 `#\u7F16\u53F7` / `owner/repo#\u7F16\u53F7` \u4F1A\u67E5\u8BE2 GitHub Issue/PR \u5E76\u4EE5\u56FE\u7247\u5361\u7247\n * \u56DE\u590D\u5728 Discord \u9891\u9053\uFF08\u4E00\u6761\u6D88\u606F\u591A\u4E2A\u7F16\u53F7\u5168\u90E8\u89E3\u6790\uFF0C\u4E0E QQ \u4FA7\u540C\u4E00\u5957\u6E32\u67D3\uFF09\uFF1B\n * - \u4E92\u901A\u9891\u9053\u6D88\u606F\u53EF\u89E6\u53D1 Hermes AI\uFF08\u5BA1\u6279\u56DE\u590D /@\u63D0\u53CA / \u5173\u952E\u8BCD\uFF09\uFF0C\u9891\u9053\u662F\u5426\u542F\u7528\u7531\u5176\u6865\u63A5\u7684\n * QQ \u7FA4\u5728 hermes.json groups \u4E2D\u51B3\u5B9A\uFF0CAI \u56DE\u590D\u53D1\u5728 Discord \u9891\u9053\u3002\n *\n * \u524D\u7F6E\u6761\u4EF6\uFF1ADiscord \u5F00\u53D1\u8005\u540E\u53F0\u4E3A bot \u5F00\u542F Message Content Intent\uFF0C\u5E76\u9080\u8BF7\u5165\u5BF9\u5E94\u670D\u52A1\u5668\u3002\n */\n\n// ---------------------------------------------------------------------------\n// \u914D\u7F6E\n// ---------------------------------------------------------------------------\n\nexport interface BridgeEntry {\n /** QQ \u7FA4\u53F7\uFF08\u5B57\u7B26\u4E32\u5F62\u5F0F\uFF0C\u517C\u5BB9\u914D\u7F6E\u91CC\u5199\u6210\u6570\u5B57\uFF09 */\n group: string;\n /** true \u65F6 QQ\u2192Discord \u4EC5\u8F6C\u53D1\u5BF9\u6865\u6D88\u606F\u7684\u56DE\u590D */\n need_reply: string;\n /** true \u65F6\u53CC\u5411\u4EC5 /send \u4E0E\u5BF9\u6865\u6D88\u606F\u7684\u56DE\u590D\u4E92\u901A */\n need_cmd: string;\n}\n\nexport interface DiscordBridgeConfig {\n version: number;\n token: string;\n /** key: \"\u670D\u52A1\u5668ID#\u9891\u9053\u540D\u79F0\" */\n bridges: Record<string, BridgeEntry>;\n}\n\nfunction normalizeBridgeFlag(raw: unknown): string | null {\n if (typeof raw === 'boolean') return raw ? 'true' : 'false';\n if (raw === 'true' || raw === 'false') return raw;\n return null;\n}\n\nfunction normalizeDiscordBridgeConfig(raw: unknown): DiscordBridgeConfig | null {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null;\n const record = raw as Record<string, unknown>;\n const token = typeof record.token === 'string' ? record.token : '';\n const bridges: Record<string, BridgeEntry> = {};\n if (record.bridges && typeof record.bridges === 'object' && !Array.isArray(record.bridges)) {\n for (const [key, value] of Object.entries(record.bridges as Record<string, unknown>)) {\n if (!/^\\d+#.+$/.test(key)) continue;\n if (!value || typeof value !== 'object' || Array.isArray(value)) continue;\n const entry = value as Record<string, unknown>;\n let group: string;\n if (typeof entry.group === 'string' && /^\\d+$/.test(entry.group)) group = entry.group;\n else if (typeof entry.group === 'number' && Number.isSafeInteger(entry.group)) group = String(entry.group);\n else continue;\n const needReply = normalizeBridgeFlag(entry.need_reply) ?? 'false';\n const needCmd = normalizeBridgeFlag(entry.need_cmd) ?? 'false';\n bridges[key] = { group, need_reply: needReply, need_cmd: needCmd };\n }\n }\n return { version: 1, token, bridges };\n}\n\nconst bridgeStore = new ConfigStore<DiscordBridgeConfig>({\n path: 'configs/features/discord-bridge.json',\n version: 1,\n factory: () => ({ version: 1, token: '', bridges: {} }),\n normalize: normalizeDiscordBridgeConfig\n});\n\n/** \u8BFB\u53D6\u4E92\u901A\u751F\u6548\u914D\u7F6E\uFF08\u70ED\u91CD\u8F7D\uFF09 */\nexport function getDiscordBridgeConfig(): DiscordBridgeConfig {\n return bridgeStore.get();\n}\n\n// ---------------------------------------------------------------------------\n// \u5185\u5B58\u8DEF\u7531\u8868\uFF08\u56DE\u590D\u94FE\u6620\u5C04\uFF09\n// ---------------------------------------------------------------------------\n\n/** \u6709\u754C FIFO \u7F13\u5B58\uFF1A\u8F6C\u53D1\u6D88\u606F\u7684 id \u6620\u5C04\uFF0C\u5BB9\u91CF\u5C01\u9876\u9632\u6B62\u957F\u671F\u8FD0\u884C\u5185\u5B58\u81A8\u80C0 */\nclass BoundedCache<V> {\n private readonly map = new Map<number, V>();\n\n public constructor(private readonly capacity: number = 2000) {}\n\n public get(key: number): V | undefined {\n return this.map.get(key);\n }\n\n public set(key: number, value: V): void {\n if (this.map.has(key)) this.map.delete(key);\n this.map.set(key, value);\n if (this.map.size > this.capacity) {\n const oldest = this.map.keys().next().value;\n if (oldest !== undefined) this.map.delete(oldest);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// DiscordBridge\n// ---------------------------------------------------------------------------\n\nconst DISCORD_MESSAGE_LIMIT = 2000;\nconst SEND_COMMAND_PREFIX = '/send';\n\n/** \u5DF2\u89E3\u6790\u7684\u4E92\u901A\u6761\u76EE\uFF08\u914D\u7F6E key \u62C6\u5206\u540E\u7684\u89C6\u56FE\uFF09 */\ninterface ResolvedBridge {\n key: string;\n guildId: string;\n channelName: string;\n entry: BridgeEntry;\n}\n\nexport class DiscordBridge {\n private static instance?: DiscordBridge;\n\n public static getInstance(): DiscordBridge {\n if (!DiscordBridge.instance) DiscordBridge.instance = new DiscordBridge();\n return DiscordBridge.instance;\n }\n\n private client?: Client;\n private bot?: QQBot;\n private started = false;\n\n /** QQ \u6D88\u606F id \u2192\uFF08bridge key \u2192 Discord \u6D88\u606F id\uFF09\uFF1A\u540C\u7FA4\u591A\u9891\u9053\u65F6\u6BCF\u4E2A\u9891\u9053\u5404\u6709\u81EA\u5DF1\u7684\u8F6C\u53D1\uFF0C\u56DE\u590D\u5F15\u7528\u5FC5\u987B\u6309\u9891\u9053\u53D6 */\n private readonly discordByQQ = new BoundedCache<Record<string, string>>();\n /** Discord \u6D88\u606F id \u2192 QQ \u6D88\u606F id */\n private readonly qqByDiscord = new BoundedCache<number>();\n /** \u672C\u6865\u8F6C\u53D1\u5230 QQ \u7684\u6D88\u606F id\uFF08\u56DE\u590D\u94FE & need_reply/need_cmd \u5224\u5B9A\uFF09 */\n private readonly qqFromBridge = new BoundedCache<true>();\n /** \u672C\u6865\u8F6C\u53D1\u5230 Discord \u7684\u6D88\u606F id\uFF08\u56DE\u590D\u94FE & need_cmd \u5224\u5B9A\uFF09 */\n private readonly dcFromBridge = new BoundedCache<true>();\n /** \u8F6C\u53D1\u5230 QQ \u7684\u6D88\u606F id \u2192 \u6765\u6E90 bridge key\uFF08\u540C\u7FA4\u591A\u9891\u9053\u65F6\u56DE\u590D\u8981\u56DE\u5230\u539F\u9891\u9053\uFF09 */\n private readonly bridgeByQQForward = new BoundedCache<string>();\n /**\n * \u6865\u53D1\u5F80 Discord \u7684\u5F85\u53D1\u6D88\u606F\u8BA1\u6570\uFF08`${channelId}\\n${content}` \u2192 \u6761\u6570\uFF09\u3002\n * messageCreate \u56DE\u63A8\u53EF\u80FD\u5148\u4E8E send() \u8FD4\u56DE\uFF08\u6B64\u65F6 dcFromBridge \u5C1A\u672A\u767B\u8BB0\uFF09\uFF0C\n * \u53D1\u9001\u524D\u5148\u767B\u8BB0\u3001\u56DE\u63A8\u65F6\u6838\u9500\uFF0C\u4FDD\u8BC1\u4E71\u5E8F\u4E0B\u4E5F\u4E0D\u4F1A\u628A\u6865\u8F6C\u53D1\u56DE\u4F20 QQ \u5F62\u6210\u56DE\u73AF\u3002\n */\n private readonly pendingBridgeSends = new Map<string, number>();\n\n private constructor() {}\n\n /** \u8BE5\u7FA4\u662F\u5426\u914D\u7F6E\u4E86\u4E92\u901A\u6761\u76EE\uFF08QQ \u4FA7 /send \u5206\u53D1\u4E0E\u6865\u63A5\u5171\u7528\u540C\u4E00\u5224\u5B9A\uFF09 */\n public static isSendTargetGroup(groupId: number | string): boolean {\n const group = String(groupId);\n return Object.values(getDiscordBridgeConfig().bridges).some(entry => entry.group === group);\n }\n\n /** after-start \u94A9\u5B50\uFF1A\u542F\u52A8 Discord \u5BA2\u6237\u7AEF\uFF08token \u4E3A\u7A7A\u65F6\u7981\u7528\u672C\u529F\u80FD\uFF09 */\n public start(bot: QQBot): void {\n if (this.started) return;\n this.started = true;\n this.bot = bot;\n // \u673A\u5668\u4EBA\u81EA\u8EAB\u53D1\u51FA\u7684\u7FA4\u6D88\u606F\uFF08GitHub \u901A\u77E5\u3001\u547D\u4EE4\u56DE\u590D\u7B49\uFF09\u4E5F\u8F6C\u53D1\u5230 Discord\u3002\n // \u6CE8\u610F\uFF1AOneBot \u4E0D\u4F1A\u628A bot \u81EA\u5DF1\u7684\u6D88\u606F\u56DE\u63A8\u4E3A message \u4E8B\u4EF6\uFF0C\u53EA\u80FD\u6302\u53D1\u9001\u51FA\u53E3\u94A9\u5B50\u3002\n bot.onGroupMessageSent(event => {\n this.handleQQOutgoingMessage(event).catch(error => {\n bot.logger?.error(\n `[DiscordBridge] \u673A\u5668\u4EBA\u6D88\u606F\u8F6C\u53D1\u5931\u8D25: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}`\n );\n });\n });\n const token = getDiscordBridgeConfig().token;\n if (!token) {\n bot.logger?.warn('[DiscordBridge] token \u672A\u914D\u7F6E\uFF08configs/features/discord-bridge.json\uFF09\uFF0C\u4E92\u901A\u529F\u80FD\u5DF2\u7981\u7528');\n return;\n }\n this.client = new Client({\n intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent]\n });\n this.client.on('messageCreate', message => {\n this.handleDiscordMessage(message).catch(error => {\n this.bot?.logger?.error(\n `[DiscordBridge] \u5904\u7406 Discord \u6D88\u606F\u5931\u8D25: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}`\n );\n });\n });\n this.client.on('error', error => {\n this.bot?.logger?.error(`[DiscordBridge] Discord \u5BA2\u6237\u7AEF\u9519\u8BEF: ${error.message}`);\n });\n this.client.once('clientReady', client => {\n this.bot?.logger?.info(`[DiscordBridge] \u5DF2\u767B\u5F55 Discord\uFF1A${client.user.tag}`);\n // \u6E05\u7406\u4E0A\u6B21\u8FDB\u7A0B\u9000\u51FA\u65F6\u6B8B\u7559\u7684\u300CAI \u5904\u7406\u4E2D\u300D\u8868\u60C5\u56DE\u5E94\uFF08Discord \u4FA7\uFF09\n this.cleanupStaleHermesReactions(client).catch(error => {\n this.bot?.logger?.error(\n `[DiscordBridge] \u6B8B\u7559\u8868\u60C5\u56DE\u5E94\u6E05\u7406\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`\n );\n });\n // \u6CE8\u518C\u539F\u751F\u659C\u6760\u547D\u4EE4\uFF08guild \u7EA7\u5373\u65F6\u751F\u6548\uFF1Bbridges \u914D\u7F6E\u53BB\u91CD\u7684\u5168\u90E8\u670D\u52A1\u5668\uFF09\n const guildIds = [...new Set(Object.keys(getDiscordBridgeConfig().bridges).map(key => key.split('#')[0]!))];\n registerSlashCommands(client, guildIds, bot).catch(error => {\n this.bot?.logger?.error(\n `[DiscordBridge] \u6CE8\u518C\u659C\u6760\u547D\u4EE4\u5F02\u5E38: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}`\n );\n });\n });\n // \u539F\u751F\u659C\u6760\u547D\u4EE4\u4EA4\u4E92\uFF1A\u7ECF token \u7CBE\u786E\u56DE\u653E\u4EA4\u7ED9 gugle-command \u6267\u884C\uFF0C\u56DE\u590D\u53D1\u5728 Discord\n this.client.on('interactionCreate', interaction => {\n if (!interaction.isChatInputCommand() || !this.bot) return;\n const bot = this.bot;\n handleSlashInteraction(bot, interaction, (guildId, channelName) => {\n const bridge = channelName ? this.resolveByChannel(guildId, channelName) : undefined;\n const group = bridge ? Number(bridge.entry.group) : NaN;\n return Number.isSafeInteger(group) ? group : undefined;\n }).catch(error => {\n bot.logger?.error(\n `[DiscordBridge] \u659C\u6760\u547D\u4EE4\u4EA4\u4E92\u5931\u8D25: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}`\n );\n });\n });\n this.client.login(token).catch(error => {\n this.bot?.logger?.error(\n `[DiscordBridge] Discord \u767B\u5F55\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`\n );\n });\n }\n\n /**\n * \u542F\u52A8\u6E05\u7406\uFF1A\u6458\u9664\u4E0A\u6B21\u8FDB\u7A0B\u5728 AI \u5904\u7406\u4E2D\u9000\u51FA\u65F6\u6B8B\u7559\u7684 Discord \u8868\u60C5\u56DE\u5E94\u3002\n * \u8BB0\u5F55\u7531 HermesBridge \u5728\u8D34\u8868\u60C5\u65F6\u7ACB\u5373\u6301\u4E45\u5316\uFF08data/hermes-reactions.json\uFF09\uFF1B\n * \u6D88\u606F\u5DF2\u5220\u9664/\u9891\u9053\u4E0D\u53EF\u8FBE\u7B49\u5931\u8D25\u4EC5\u6E05\u7406\u8BB0\u5F55\uFF0C\u4E0D\u505A\u91CD\u8BD5\u3002\n */\n private async cleanupStaleHermesReactions(client: Client): Promise<void> {\n const reactions = ReactionManager.getInstance();\n const stale = reactions.listDiscord();\n for (const record of stale) {\n try {\n const channel = record.channelId ? await client.channels.fetch(record.channelId) : null;\n if (channel?.isTextBased()) {\n const message = await channel.messages.fetch(record.messageId);\n const reaction =\n message.reactions.resolve(record.emoji) ??\n message.reactions.cache.find(item => item.emoji.name === record.emoji);\n await reaction?.users.remove(client.user?.id);\n }\n } catch (error) {\n this.bot?.logger?.warn(\n `[DiscordBridge] \u6458\u9664\u6B8B\u7559\u8868\u60C5\u56DE\u5E94\u5931\u8D25\uFF08${record.key}\uFF09: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n reactions.drop(record.key);\n }\n if (stale.length > 0) {\n this.bot?.logger?.info(`[DiscordBridge] \u5DF2\u6E05\u7406 ${stale.length} \u6761\u6B8B\u7559\u7684 Discord \u8868\u60C5\u56DE\u5E94`);\n }\n }\n\n // -------------------------------------------------------------------------\n // QQ \u2192 Discord\n // -------------------------------------------------------------------------\n\n /** message-event-group \u94A9\u5B50\uFF1A\u628A QQ \u7FA4\u6D88\u606F\u8F6C\u53D1\u5230 Discord\uFF08\u6216\u6267\u884C /send\uFF09 */\n public handleQQMessage(bot: QQBot, msg: GroupMessageWSMSG): void {\n try {\n // \u540C\u4E00 QQ \u7FA4\u53EF\u7ED1\u5B9A\u591A\u4E2A\u9891\u9053\uFF1A\u666E\u901A\u6D88\u606F\u9010\u6761\u95E8\u63A7\u5E76\u8F6C\u53D1\u5230\u6240\u6709\u653E\u884C\u7684\u9891\u9053\n const bridges = this.resolveAllByGroup(msg.group_id);\n if (bridges.length === 0) return;\n\n // /send \u547D\u4EE4\uFF08\u4E24\u4E2A need_cmd \u72B6\u6001\u90FD\u53EF\u7528\uFF1B\u4E0E\u5168\u5C40\u547D\u4EE4\u5206\u53D1\u4E92\u65A5\uFF0C\u6B64\u5904\u72EC\u7ACB\u5904\u7406\uFF09\n // \u4EC5\u62E6\u622A\u300C/send + \u7A7A\u767D/\u7ED3\u5C3E\u300D\uFF0C/sendxxx \u4E4B\u7C7B\u7684\u6D88\u606F\u4ECD\u6309\u666E\u901A\u6D88\u606F\u8F6C\u53D1\n const raw = msg.raw_message ?? '';\n if (this.isSendCommand(raw)) {\n this.handleSendFromQQ(bot, msg, raw).catch(error => {\n bot.logger?.error(\n `[DiscordBridge] QQ /send \u6267\u884C\u5931\u8D25: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}`\n );\n });\n return;\n }\n\n const replyId = this.extractQQReplyId(msg);\n // \u300C\u5BF9\u6865\u6D88\u606F\u7684\u56DE\u590D\u300D\u9700\u5B9A\u4F4D\u5230\u8BE5\u6865\u6D88\u606F\u6765\u81EA\u7684\u9891\u9053\uFF08\u56DE\u590D\u8981\u56DE\u5230\u539F\u9891\u9053\uFF09\n const replyBridge = replyId !== undefined ? this.bridgeByQQForward.get(replyId) : undefined;\n const isReplyToBridge = replyBridge !== undefined;\n\n for (const bridge of bridges) {\n // \u95E8\u63A7\uFF1Aneed_cmd / need_reply \u4EFB\u4E00\u5F00\u542F\u65F6\uFF0C\u4EC5\u653E\u884C\u300C\u5BF9\uFF08\u8BE5\u9891\u9053\uFF09\u6865\u6D88\u606F\u7684\u56DE\u590D\u300D\n if (bridge.entry.need_cmd === 'true' || bridge.entry.need_reply === 'true') {\n if (!isReplyToBridge || replyBridge !== bridge.key) continue;\n }\n this.forwardQQToDiscord(bot, msg, bridge, replyId).catch(error => {\n bot.logger?.error(\n `[DiscordBridge] QQ\u2192Discord \u8F6C\u53D1\u5931\u8D25: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}`\n );\n });\n }\n } catch (error) {\n bot.logger?.error(\n `[DiscordBridge] QQ \u6D88\u606F\u5904\u7406\u5F02\u5E38: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}`\n );\n }\n }\n\n /** \u63D0\u53D6 QQ \u6D88\u606F\u4E2D\u7684\u56DE\u590D\u76EE\u6807 id\uFF08reply \u6BB5\u4E0D\u8BA1\u5165\u6B63\u6587\uFF09 */\n private extractQQReplyId(msg: GroupMessageWSMSG): number | undefined {\n if (!Array.isArray(msg.message)) return undefined;\n const reply = msg.message.find(segment => segment.type === 'reply');\n if (!reply) return undefined;\n const id = Number(reply.data.id);\n return Number.isSafeInteger(id) ? id : undefined;\n }\n\n /** QQ \u6D88\u606F\u6BB5\u62C6\u5206\uFF1A\u6587\u672C/\u9644\u4EF6\u5206\u79BB\uFF08\u9644\u4EF6\u4EE5 Discord \u9644\u4EF6\u4E0A\u4F20\uFF0C\u6587\u672C\u4E0D\u6DF7\u5165 URL\uFF09 */\n private splitQQMessage(msg: GroupMessageWSMSG): { text: string; attachments: { url: string; name: string }[] } {\n const textParts: string[] = [];\n const attachments: { url: string; name: string }[] = [];\n if (Array.isArray(msg.message)) {\n for (const segment of msg.message) {\n switch (segment.type) {\n case 'text':\n textParts.push(segment.data.text);\n break;\n case 'at':\n textParts.push(`@${segment.data.qq}`);\n break;\n case 'face':\n textParts.push('[\u8868\u60C5]');\n break;\n case 'image':\n case 'record':\n case 'video':\n case 'file': {\n const url = segment.data.url || segment.data.file;\n if (url && /^https?:\\/\\//.test(url)) {\n const fallback =\n segment.type === 'image'\n ? 'image.png'\n : segment.type === 'record'\n ? 'audio.amr'\n : segment.type === 'video'\n ? 'video.mp4'\n : 'file';\n attachments.push({ url, name: fallback });\n }\n break;\n }\n default:\n break;\n }\n }\n }\n return { text: textParts.join('').trim(), attachments };\n }\n\n /** \u6309\u5B57\u8282\u5185\u5BB9\u8BC6\u522B\u5E38\u89C1\u56FE\u7247\u683C\u5F0F\uFF0C\u8FD4\u56DE\u5BF9\u5E94\u6269\u5C55\u540D\uFF08\u8BC6\u522B\u4E0D\u4E86\u8FD4\u56DE null\uFF09 */\n private detectImageExt(buf: Buffer): string | null {\n if (buf.length < 12) return null;\n if (buf[0] === 0xff && buf[1] === 0xd8) return 'jpg';\n if (buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4e && buf[3] === 0x47) return 'png';\n if (buf[0] === 0x47 && buf[1] === 0x49 && buf[2] === 0x46) return 'gif';\n if (buf[0] === 0x52 && buf[1] === 0x49 && buf[2] === 0x46 && buf[3] === 0x46 && buf.toString('ascii', 8, 12) === 'WEBP')\n return 'webp';\n if (buf[0] === 0x42 && buf[1] === 0x4d) return 'bmp';\n return null;\n }\n\n /**\n * \u4E0B\u8F7D\u9644\u4EF6\u4E3A Buffer \u5E76\u4FEE\u6B63\u6269\u5C55\u540D\u3002\n * NapCat \u591A\u5A92\u4F53\u76F4\u94FE\u5E26 rkey \u9632\u76D7\u94FE\uFF0C\u7531 bot \u4FA7\u4E0B\u8F7D\u518D\u4E0A\u4F20\u6BD4\u8BA9 Discord \u670D\u52A1\u5668\u6293\u53D6\u66F4\u53EF\u9760\uFF1B\n * \u6587\u4EF6\u540D\u5FC5\u987B\u5E26\u771F\u5B9E\u56FE\u7247\u6269\u5C55\u540D\uFF0CDiscord \u624D\u4F1A\u5185\u5D4C\u6E32\u67D3\u800C\u4E0D\u662F\u663E\u793A\u4E3A\u9644\u4EF6\u5361\u7247\u3002\n */\n private async fetchAttachment(\n bot: QQBot,\n item: { url: string; name: string }\n ): Promise<{ attachment: Buffer; name: string } | undefined> {\n try {\n const res = await bot.axiosInstance.get<ArrayBuffer>(item.url, { responseType: 'arraybuffer', timeout: 30000 });\n const buf = Buffer.from(res.data);\n const ext = this.detectImageExt(buf);\n let name = item.name;\n if (ext) {\n // \u56FE\u7247\uFF1A\u786E\u4FDD\u6269\u5C55\u540D\u4E0E\u771F\u5B9E\u683C\u5F0F\u4E00\u81F4\uFF08URL \u91CC\u5E38\u662F download?fileid=... \u6CA1\u6709\u6269\u5C55\u540D\uFF09\n name = name.replace(/\\.[a-z0-9]+$/i, '') + '.' + ext;\n }\n return { attachment: buf, name };\n } catch (error) {\n bot.logger?.error(\n `[DiscordBridge] \u9644\u4EF6\u4E0B\u8F7D\u5931\u8D25\uFF08${item.url.slice(0, 80)}\u2026\uFF09: ${error instanceof Error ? error.message : String(error)}`\n );\n return undefined;\n }\n }\n\n /** QQ\u2192Discord \u8F6C\u53D1\u4E3B\u6D41\u7A0B\uFF1BbodyOverride \u7528\u4E8E /send\uFF08\u5265\u79BB\u547D\u4EE4\u524D\u7F00\u540E\u7684\u6B63\u6587\uFF09 */\n private async forwardQQToDiscord(\n bot: QQBot,\n msg: GroupMessageWSMSG,\n bridge: ResolvedBridge,\n replyId?: number,\n viaCommand: boolean = false,\n bodyOverride?: string\n ): Promise<void> {\n if (!this.client) return;\n const channel = await this.resolveDiscordChannel(bridge);\n if (!channel) return;\n\n const senderName = msg.sender.card || msg.sender.nickname;\n const header = `\u3010${msg.group_name}|${msg.group_id}\u3011${senderName}(${msg.sender.user_id})\uFF1A`;\n const { text, attachments } =\n bodyOverride !== undefined ? { text: bodyOverride, attachments: [] as { url: string; name: string }[] } : this.splitQQMessage(msg);\n if (!text && attachments.length === 0) return;\n\n // QQ \u6D88\u606F\u7B26\u5408 Discord markdown \u2192 \u539F\u6587\u53D1\u9001\uFF08Discord \u539F\u751F\u6E32\u67D3\uFF09\uFF1B\u5426\u5219\u8F6C\u4E49\u4E3A plain text\n // \u6CE8\u610F\uFF1A\u4EC5\u5728\u6587\u672C\u975E\u7A7A\u65F6\u62FC\u63A5\u6362\u884C\uFF0C\u907F\u514D\u7EAF\u56FE\u7247\u6D88\u606F\u51FA\u73B0\u5B64\u7ACB\u7684\u6765\u6E90\u884C\n const rendered = isDiscordMarkdown(text) ? text : escapeDiscord(text);\n const content = this.truncateDiscord(text ? `${header}\\n${rendered}` : header);\n\n const referenceId = replyId !== undefined ? this.discordByQQ.get(replyId)?.[bridge.key] : undefined;\n // \u9644\u4EF6\u7531 bot \u4E0B\u8F7D\u4E3A Buffer \u4E0A\u4F20\uFF08\u907F\u514D Discord \u670D\u52A1\u5668\u6293\u53D6 NapCat \u9632\u76D7\u94FE\u76F4\u94FE\u5931\u8D25/\u62FF\u5230\u975E\u56FE\u7247\u54CD\u5E94\uFF09\uFF0C\n // \u5E76\u6309\u5B57\u8282\u5185\u5BB9\u4FEE\u6B63\u6269\u5C55\u540D\uFF0C\u4FDD\u8BC1\u56FE\u7247\u5728 Discord \u5185\u5D4C\u6E32\u67D3\n const files = (\n await Promise.all(attachments.map(item => this.fetchAttachment(bot, item)))\n ).filter((item): item is { attachment: Buffer; name: string } => item !== undefined);\n const sent = await this.sendWithReference(channel, { content, files }, referenceId);\n this.registerDiscordMessage(sent.id, msg.message_id, bridge.key, viaCommand);\n bot.logger?.debug(`[DiscordBridge] QQ\u2192Discord\uFF1A${msg.group_id} \u2192 ${bridge.key}\uFF08${sent.id}\uFF09`);\n }\n\n /**\n * \u673A\u5668\u4EBA\u81EA\u8EAB\u53D1\u51FA\u7684\u7FA4\u6D88\u606F \u2192 Discord\uFF08GitHub \u901A\u77E5\u3001\u547D\u4EE4\u56DE\u590D\u3001AI \u5BF9\u8BDD\u7B49\uFF09\u3002\n *\n * \u7531 QQBot.onGroupMessageSent \u94A9\u5B50\u9A71\u52A8\uFF08OneBot \u4E0D\u56DE\u63A8 bot \u81EA\u5DF1\u7684 message \u4E8B\u4EF6\uFF09\u3002\n * \u95E8\u63A7\u4E0E\u666E\u901A QQ \u6D88\u606F\u4E00\u81F4\uFF1Aneed_cmd / need_reply \u9891\u9053\u4EC5\u653E\u884C\u300C\u5BF9\u8BE5\u9891\u9053\u6865\u6D88\u606F\u7684\u56DE\u590D\u300D\n * \uFF08\u5982 AI \u56DE\u590D /send \u8FC7\u6765\u7684\u6D88\u606F\uFF09\uFF0Cwebhook \u63A8\u9001\uFF08GitHub \u8BA2\u9605\u901A\u77E5\uFF09\u4E00\u5F8B\u4E0D\u8FDB\u3002\n */\n private async handleQQOutgoingMessage(event: GroupMessageSentEvent): Promise<void> {\n // \u6865\u81EA\u8EAB\u8F6C\u53D1\u5230 QQ \u7684\u6D88\u606F\u5FC5\u987B\u8DF3\u8FC7\uFF0C\u5426\u5219\u300CDiscord\u2192QQ\u2192Discord\u300D\u5F62\u6210\u56DE\u73AF\n if (event.fromBridge) return;\n // \u53D1\u9001\u5931\u8D25\uFF08\u65E0 messageId\uFF09\u8BF4\u660E\u6D88\u606F\u6839\u672C\u6CA1\u53D1\u51FA\u53BB\uFF0C\u65E0\u9700\u8F6C\u53D1\n if (event.messageId === undefined) return;\n if (!this.client || !this.bot) return;\n const bot = this.bot;\n\n const bridges = this.resolveAllByGroup(event.groupId);\n if (bridges.length === 0) return;\n\n const loginInfo = bot.getLoginInfoSync();\n const senderName = loginInfo?.nickname ?? 'QQ Bot';\n const senderId = loginInfo?.user_id ?? 'unknown';\n const header = `\u3010QQ\u7FA4 ${event.groupId}\u3011${senderName}(${senderId})\uFF1A`;\n\n // \u62C6\u5206\u53D1\u9001\u8F7D\u8377\uFF1A\u6587\u672C / \u9644\u4EF6\uFF08base64 \u6216\u76F4\u94FE\uFF09/ \u56DE\u590D\u5F15\u7528\n const textParts: string[] = [];\n const downloads: { url: string; name: string }[] = [];\n const files: { attachment: Buffer; name: string }[] = [];\n let replyQQId: number | undefined;\n // QQ \u4FA7 AI \u957F\u56DE\u590D\u73B0\u5728\u4EE5\u804A\u5929\u8BB0\u5F55\uFF08node \u6BB5\uFF09\u5F62\u5F0F\u53D1\u51FA\uFF0C\u82E5\u76F4\u63A5\u8DF3\u8FC7\u4F1A\u5BFC\u81F4\u8FD9\u7C7B\u6D88\u606F\u5B8C\u5168\u4E0D\u955C\u50CF\u5230 Discord\u3002\n // \u56E0\u6B64\u8FD9\u91CC\u628A node \u5185\u7684\u6587\u672C\u62CD\u5E73\u540E\u8FFD\u52A0\u5230 textParts\uFF0C\u518D\u8D70\u4E0B\u65B9\u65E2\u6709\u95E8\u63A7\u6B63\u5E38\u8F6C\u53D1\u3002\n // \u5DF2\u77E5\u9650\u5236\uFF1Anode \u5185\u7684\u56FE\u7247/\u8BED\u97F3/\u89C6\u9891/\u6587\u4EF6\u7B49\u9644\u4EF6\u4E0D\u955C\u50CF\uFF08\u4EC5\u62BD\u53D6 text/at/face \u6587\u672C\uFF09\u3002\n const nodeTexts: string[] = [];\n for (const segment of event.message) {\n if (segment.type === 'node') {\n const nodeText = this.flattenForwardNodeText(segment.data.content);\n if (nodeText) nodeTexts.push(nodeText);\n continue;\n }\n switch (segment.type) {\n case 'text':\n textParts.push(segment.data.text);\n break;\n case 'at':\n textParts.push(`@${segment.data.qq}`);\n break;\n case 'face':\n textParts.push('[\u8868\u60C5]');\n break;\n case 'reply': {\n const id = Number(segment.data.id);\n if (Number.isSafeInteger(id)) replyQQId = id;\n break;\n }\n case 'image':\n case 'record':\n case 'video':\n case 'file': {\n const fallback =\n segment.type === 'image'\n ? 'image.png'\n : segment.type === 'record'\n ? 'audio.amr'\n : segment.type === 'video'\n ? 'video.mp4'\n : 'file';\n // bot \u53D1\u9001\u7684\u56FE\u7247\u591A\u4E3A base64 \u8F7D\u8377\uFF08GitHub \u5361\u7247\u3001markdown \u6E32\u67D3\u56FE\uFF09\uFF0C\u76F4\u63A5\u89E3\u7801\u4E3A\u9644\u4EF6\n const base64 = this.extractBase64Payload(segment.data.file);\n if (base64) {\n files.push({ attachment: Buffer.from(base64, 'base64'), name: fallback });\n break;\n }\n const url = segment.data.url || segment.data.file;\n if (url && /^https?:\\/\\//.test(url)) downloads.push({ url, name: fallback });\n break;\n }\n default:\n break;\n }\n }\n for (const item of downloads) {\n const fetched = await this.fetchAttachment(bot, item);\n if (fetched) files.push(fetched);\n }\n\n // \u804A\u5929\u8BB0\u5F55\u6309\u8282\u70B9\u5206\u6BB5\u5C55\u793A\uFF1A\u8282\u70B9\u6587\u672C\u4E4B\u95F4\u8865\u6362\u884C\uFF0C\u907F\u514D\u524D\u4E00\u6BB5\u672B\u5C3E\u4E0E\u540E\u4E00\u6BB5\u5F00\u5934\u7C98\u8FDE\n if (nodeTexts.length > 0) {\n if (textParts.length > 0) textParts.push('\\n');\n textParts.push(nodeTexts.join('\\n'));\n }\n\n const text = textParts.join('').trim();\n if (!text && files.length === 0) return;\n\n const rendered = isDiscordMarkdown(text) ? text : escapeDiscord(text);\n const content = this.truncateDiscord(text ? `${header}\\n${rendered}` : header);\n\n // \u300C\u5BF9\u6865\u6D88\u606F\u7684\u56DE\u590D\u300D\u6309\u6865\u5B9A\u4F4D\u6765\u6E90\u9891\u9053\uFF08\u4E0E\u666E\u901A QQ \u6D88\u606F\u95E8\u63A7\u540C\u4E00\u5224\u5B9A\uFF09\uFF1A\n // AI \u56DE\u590D /send \u5230 QQ \u7684\u6D88\u606F\u7B49\u573A\u666F\u53EF\u56DE\u5230\u539F\u9891\u9053\uFF0C\u5176\u4F59\u673A\u5668\u4EBA\u6D88\u606F\u4E0D\u8FDB need_cmd / need_reply \u9891\u9053\n const replyBridgeKey = replyQQId !== undefined ? this.bridgeByQQForward.get(replyQQId) : undefined;\n\n for (const bridge of bridges) {\n if (!this.shouldForwardBotMessage(bridge, event.fromWebhook, replyBridgeKey)) continue;\n const channel = await this.resolveDiscordChannel(bridge);\n if (!channel) continue;\n try {\n // \u56DE\u590D\u5F15\u7528\u6309\u9891\u9053\u5404\u81EA\u7684\u8F6C\u53D1\u6620\u5C04\u53D6\uFF08\u540C\u7FA4\u591A\u9891\u9053\u65F6\u540C\u4E00 QQ \u6D88\u606F\u5728\u6BCF\u4E2A\u9891\u9053\u5404\u6709\u8F6C\u53D1\uFF09\n const referenceId = replyQQId !== undefined ? this.discordByQQ.get(replyQQId)?.[bridge.key] : undefined;\n const sent = await this.sendWithReference(channel, { content, files }, referenceId);\n // \u767B\u8BB0\u6620\u5C04\uFF1ADiscord \u7528\u6237\u56DE\u590D\u673A\u5668\u4EBA\u6D88\u606F\u65F6\u53EF\u6309\u56DE\u590D\u94FE\u56DE\u4F20\u5230 QQ\n this.registerDiscordMessage(sent.id, event.messageId, bridge.key);\n bot.logger?.debug(`[DiscordBridge] \u673A\u5668\u4EBA\u6D88\u606F\u2192Discord\uFF1A${event.groupId} \u2192 ${bridge.key}\uFF08${sent.id}\uFF09`);\n } catch (error) {\n bot.logger?.error(\n `[DiscordBridge] \u673A\u5668\u4EBA\u6D88\u606F\u8F6C\u53D1\u5931\u8D25\uFF08${bridge.key}\uFF09: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n }\n\n /** \u804A\u5929\u8BB0\u5F55\uFF08node \u6BB5\uFF09\u5185\u6587\u672C\u7684\u9012\u5F52\u6DF1\u5EA6\u4E0A\u9650\uFF1A\u9632\u5FA1\u5F02\u5E38\u6570\u636E\u7ED3\u6784\u5BFC\u81F4\u7684\u65E0\u9650\u9012\u5F52 */\n private static readonly FORWARD_NODE_MAX_DEPTH = 4;\n\n /**\n * \u62BD\u53D6\u5408\u5E76\u8F6C\u53D1\uFF08\u804A\u5929\u8BB0\u5F55\uFF09\u8282\u70B9\u5185\u5BB9\u91CC\u7684\u6587\u672C\u3002\n *\n * QQ \u4FA7\u8D85\u957F AI \u56DE\u590D\u73B0\u5728\u4EE5\u804A\u5929\u8BB0\u5F55\uFF08node\uFF09\u5F62\u5F0F\u53D1\u51FA\uFF0C\u8FD9\u91CC\u628A\u8282\u70B9\u5185\u5BB9\u9012\u5F52\u62CD\u5E73\uFF1A\n * text \u2192 \u539F\u6587\u3001at \u2192 `@QQ\u53F7`\u3001face \u2192 `[\u8868\u60C5]`\uFF0C\u5D4C\u5957\u7684 node \u7EE7\u7EED\u4E0B\u94BB\uFF1B\n * \u8FBE\u5230\u6DF1\u5EA6\u4E0A\u9650\u3001\u6216\u9047\u5230\u975E\u6570\u7EC4/\u672A\u77E5\u7C7B\u578B\u65F6\u5B89\u5168\u8DF3\u8FC7\uFF08\u4E0D\u629B\u9519\uFF09\u3002\n * \u5DF2\u77E5\u9650\u5236\uFF1A\u8282\u70B9\u5185\u7684\u56FE\u7247/\u8BED\u97F3/\u89C6\u9891/\u6587\u4EF6\u7B49\u9644\u4EF6\u4E0D\u955C\u50CF\uFF08\u4EC5\u56DE\u4F20\u6587\u672C\uFF09\u3002\n *\n * @param content \u8282\u70B9\u5185\u5BB9\uFF08Message[]\u3001\u7EAF\u6587\u672C\u5B57\u7B26\u4E32\uFF0C\u6216\u7ED3\u6784\u5F02\u5E38\u7684\u672A\u77E5\u503C\uFF09\n * @param depth \u5F53\u524D\u9012\u5F52\u6DF1\u5EA6\uFF0C\u8D85\u8FC7\u4E0A\u9650\u5373\u505C\u6B62\u4E0B\u94BB\n */\n private flattenForwardNodeText(content: unknown, depth: number = 0): string {\n if (depth >= DiscordBridge.FORWARD_NODE_MAX_DEPTH) return '';\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n const parts: string[] = [];\n for (const raw of content) {\n if (!raw || typeof raw !== 'object') continue;\n const segment = raw as { type?: unknown; data?: unknown };\n const data = (segment.data ?? {}) as { text?: unknown; qq?: unknown; content?: unknown };\n switch (segment.type) {\n case 'text':\n if (typeof data.text === 'string') parts.push(data.text);\n break;\n case 'at':\n if (data.qq !== undefined && data.qq !== null) parts.push(`@${String(data.qq)}`);\n break;\n case 'face':\n parts.push('[\u8868\u60C5]');\n break;\n case 'node':\n parts.push(this.flattenForwardNodeText(data.content, depth + 1));\n break;\n default:\n break;\n }\n }\n return parts.join('');\n }\n\n /**\n * \u673A\u5668\u4EBA\u6D88\u606F\u95E8\u63A7\uFF08\u4E0E\u666E\u901A QQ \u7528\u6237\u6D88\u606F\u540C\u4E00\u5957\u8BED\u4E49\uFF09\uFF1A\n * - \u672A\u5F00\u542F\u95E8\u63A7\u7684\u9891\u9053\uFF1A\u5168\u90E8\u653E\u884C\uFF1B\n * - need_cmd / need_reply \u9891\u9053\uFF1A\u4EC5\u653E\u884C\u300C\u5BF9\u8BE5\u9891\u9053\u6865\u6D88\u606F\u7684\u56DE\u590D\u300D\n * \uFF08replyBridgeKey \u4E3A\u88AB\u56DE\u590D\u7684 QQ \u6865\u6D88\u606F\u6765\u6E90\u9891\u9053\uFF09\uFF1Bwebhook \u63A8\u9001\u4E00\u5F8B\u4E0D\u8FDB\u3002\n */\n private shouldForwardBotMessage(bridge: ResolvedBridge, fromWebhook: boolean, replyBridgeKey?: string): boolean {\n const gated = bridge.entry.need_cmd === 'true' || bridge.entry.need_reply === 'true';\n if (!gated) return true;\n if (fromWebhook) return false;\n return replyBridgeKey === bridge.key;\n }\n\n /**\n * \u5E26\u56DE\u590D\u5F15\u7528\u53D1\u9001\uFF1B\u88AB\u5F15\u7528\u6D88\u606F\u5DF2\u5220\u9664\u6216\u4E0D\u53EF\u89C1\uFF08MESSAGE_REFERENCE_UNKNOWN\uFF09\u65F6\n * \u964D\u7EA7\u4E3A\u65E0\u5F15\u7528\u53D1\u9001\uFF0C\u4FDD\u8BC1\u6D88\u606F\u4E0D\u4E22\u3002\n */\n private async sendWithReference(\n channel: TextChannel,\n payload: { content: string; files: { attachment: Buffer; name: string }[] },\n referenceId?: string\n ): Promise<DiscordMessage> {\n // \u53D1\u9001\u524D\u767B\u8BB0\u5F85\u53D1\u6807\u8BB0\uFF1AmessageCreate \u56DE\u63A8\u53EF\u80FD\u5148\u4E8E\u672C\u65B9\u6CD5\u8FD4\u56DE\uFF0C\u63A5\u6536\u4FA7\u636E\u6B64\u8BC6\u522B\u6865\u8F6C\u53D1\n const pendingKey = `${channel.id}\\n${payload.content}`;\n if (this.pendingBridgeSends.size > 500) this.pendingBridgeSends.clear();\n this.pendingBridgeSends.set(pendingKey, (this.pendingBridgeSends.get(pendingKey) ?? 0) + 1);\n try {\n return await channel.send({\n ...payload,\n ...(referenceId ? { reply: { messageReference: referenceId } } : {}),\n allowedMentions: { parse: [], repliedUser: false }\n });\n } catch (error) {\n if (referenceId && error instanceof Error && error.message.includes('MESSAGE_REFERENCE_UNKNOWN')) {\n return await channel.send({ ...payload, allowedMentions: { parse: [], repliedUser: false } });\n }\n throw error;\n }\n }\n\n /** \u4ECE OneBot \u6587\u4EF6\u5B57\u6BB5\u63D0\u53D6 base64 \u8F7D\u8377\uFF08`base64://\u2026` \u6216 `data:image/\u2026;base64,\u2026`\uFF09\uFF0C\u975E base64 \u8FD4\u56DE undefined */\n private extractBase64Payload(file: string): string | undefined {\n if (file.startsWith('base64://')) return file.slice('base64://'.length).trim();\n const match = /^data:image\\/[a-z0-9.+-]+;base64,\\s*(.+)$/i.exec(file);\n return match?.[1]?.trim();\n }\n\n // -------------------------------------------------------------------------\n // Discord \u2192 QQ\n // -------------------------------------------------------------------------\n\n /** messageCreate \u56DE\u8C03\uFF1A\u628A Discord \u9891\u9053\u6D88\u606F\u8F6C\u53D1\u5230 QQ\uFF08\u6216\u6267\u884C /send\uFF09 */\n private async handleDiscordMessage(message: DiscordMessage): Promise<void> {\n if (message.webhookId) return;\n if (message.channel.type !== ChannelType.GuildText) return;\n const guildId = message.guildId;\n if (!guildId) return;\n\n const bridge = this.resolveByChannel(guildId, message.channel.name);\n if (!bridge) return;\n\n // messageCreate \u540C\u6837\u56DE\u63A8 bot \u81EA\u5DF1\u7684\u6D88\u606F\uFF1A\u673A\u5668\u4EBA\u81EA\u5DF1\u5728\u4E92\u901A\u9891\u9053\u53D1\u7684\u6D88\u606F\uFF08\u547D\u4EE4\u56DE\u590D\u3001\n // AI \u56DE\u590D\u3001GitHub \u5361\u7247\u3001\u659C\u6760\u547D\u4EE4\u56DE\u590D\u7B49\uFF09\u540C\u6B65\u5230 QQ\uFF0C\u8BA9 QQ \u4FA7\u770B\u5230\u5B8C\u6574\u5BF9\u8BDD\uFF1B\n // \u6865\u81EA\u8EAB QQ\u2192Discord \u7684\u8F6C\u53D1\u5DF2\u5728 handleOwnDiscordMessage \u5185\u8DF3\u8FC7\uFF0C\u4E0D\u4F1A\u56DE\u73AF\n if (message.author.bot) {\n if (message.author.id === this.client?.user?.id) {\n await this.handleOwnDiscordMessage(message, bridge);\n }\n return;\n }\n\n const raw = message.content ?? '';\n if (this.isSendCommand(raw)) {\n await this.handleSendFromDiscord(message, raw);\n return;\n }\n\n // \u547D\u4EE4\u5206\u53D1\uFF1A/ \u6216 ! \u524D\u7F00\u4E14\u9996\u6BB5\u547D\u4E2D\u5DF2\u6CE8\u518C\u547D\u4EE4\u65F6\uFF0C\u5728 Discord \u4FA7\u76F4\u63A5\u6267\u884C\u5E76\u628A\u56DE\u590D\u53D1\u5728\n // Discord \u9891\u9053\uFF1B\u547D\u4EE4\u6D88\u606F\u672C\u8EAB\u4ECD\u6309\u540E\u7EED\u6D41\u7A0B\u8F6C\u53D1\u5230 QQ\uFF08QQ \u4FA7\u770B\u5230\u7684\u662F bot \u4EE3\u53D1\u7684\u6587\u672C\uFF0C\n // \u4E0D\u4F1A\u4E8C\u6B21\u6267\u884C\uFF09\u3002\u672A\u547D\u4E2D\u6CE8\u518C\u7684 /xxx \u6587\u672C\u540C\u6837\u6309\u666E\u901A\u6D88\u606F\u7EE7\u7EED\u8D70\u8F6C\u53D1\u6D41\u7A0B\n await this.handleDiscordCommand(message, bridge, raw);\n\n const referenceId = message.reference?.messageId;\n const referenceKey = referenceId ? this.snowflakeToKey(referenceId) : undefined;\n const isReplyToBridge = referenceKey !== undefined && this.dcFromBridge.get(referenceKey) === true;\n\n // Discord \u4FA7 #\u7F16\u53F7 \u67E5\u8BE2 GitHub Issue/PR\uFF08\u4E0E QQ \u4FA7\u5171\u7528\u540C\u4E00\u5957\u89E3\u6790/\u6E32\u67D3\uFF0C\u56DE\u590D\u53D1\u5728 Discord\n // \u9891\u9053\uFF09\uFF1B\u4E0D\u53D7 need_cmd \u95E8\u63A7\u5F71\u54CD\uFF0C\u6D88\u606F\u672C\u8EAB\u4ECD\u6309\u95E8\u63A7\u51B3\u5B9A\u662F\u5426\u8F6C\u53D1\u5230 QQ\n if (this.bot) {\n const groupId = Number(bridge.entry.group);\n const results = await Github.processDiscordMessage(\n this.bot,\n raw,\n Number.isSafeInteger(groupId) ? groupId : undefined\n );\n for (const result of results) {\n await this.sendGithubResultToDiscord(message, result);\n }\n }\n\n // Hermes AI\uFF1A\u4E92\u901A\u9891\u9053\u6D88\u606F\u53EF\u89E6\u53D1 AI\uFF08\u5BA1\u6279\u56DE\u590D / @\u63D0\u53CA / \u5173\u952E\u8BCD\uFF0C\u9891\u9053\u662F\u5426\u542F\u7528\u7531\u5176\u6865\u63A5\u7684\n // QQ \u7FA4\u5728 hermes.json groups \u4E2D\u51B3\u5B9A\uFF09\u3002AI \u56DE\u590D\u7ECF\u6CE8\u5165\u7684\u59D4\u6258\u53D1\u5728 Discord \u9891\u9053\uFF1B\n // \u6D88\u606F\u672C\u8EAB\u4ECD\u6309\u95E8\u63A7\u51B3\u5B9A\u662F\u5426\u8F6C\u53D1\u5230 QQ\n if (this.bot) {\n const bot = this.bot;\n const channel = message.channel as TextChannel;\n HermesBridge.getInstance().handleDiscordMessage(bot, message, {\n groupId: bridge.entry.group,\n ...(this.client?.user?.id ? { botUserId: this.client.user.id } : {}),\n guildName: message.guild?.name ?? bridge.guildId,\n channelName: channel.name,\n isAdmin: isDiscordAdmin(message),\n send: async (text, quote) => {\n // \u59D4\u6258\u4FA7\u515C\u5E95 2000 \u5B57\u4E0A\u9650\uFF08Hermes \u5DF2\u6309 maxMessageLength \u5207\u5206\uFF0C\u901A\u5E38\u4E0D\u4F1A\u89E6\u53D1\uFF09\n const chunks: string[] = [];\n let rest = text;\n while (rest.length > DISCORD_MESSAGE_LIMIT) {\n chunks.push(rest.slice(0, DISCORD_MESSAGE_LIMIT));\n rest = rest.slice(DISCORD_MESSAGE_LIMIT);\n }\n if (rest) chunks.push(rest);\n for (let index = 0; index < chunks.length; index++) {\n const content = chunks[index]!;\n try {\n if (quote && index === 0) {\n await message.reply({ content, allowedMentions: { parse: [], repliedUser: false } });\n } else {\n await channel.send({ content, allowedMentions: { parse: [] } });\n }\n } catch (error) {\n bot.logger?.error(\n `[DiscordBridge] Hermes \u56DE\u590D\u53D1\u9001\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n },\n sendImage: async base64 => {\n await channel\n .send({ files: [{ attachment: Buffer.from(base64, 'base64'), name: 'image.png' }] })\n .catch(error => {\n bot.logger?.error(\n `[DiscordBridge] Hermes \u56FE\u7247\u53D1\u9001\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`\n );\n });\n },\n // \u6458\u9664\u673A\u5668\u4EBA\u5BF9\u89E6\u53D1\u6D88\u606F\u7684\u300CAI \u5904\u7406\u4E2D\u300D\u8868\u60C5\u56DE\u5E94\uFF08\u5B8C\u6210/\u5931\u8D25/\u505C\u6B62\u65F6\u7531 HermesBridge \u8C03\u7528\uFF09\n removeReaction: async () => {\n const emoji = getHermesConfig().reactionEmojiDiscord;\n const reaction =\n message.reactions.resolve(emoji) ?? message.reactions.cache.find(item => item.emoji.name === emoji);\n await reaction?.users.remove();\n }\n });\n }\n\n // \u95E8\u63A7\uFF1Aneed_cmd \u5F00\u542F\u65F6\u4EC5\u653E\u884C\u300C\u5BF9\u6865\u6D88\u606F\u7684\u56DE\u590D\u300D\uFF08need_reply \u4E0D\u9650\u5236\u6B64\u65B9\u5411\uFF09\n if (bridge.entry.need_cmd === 'true' && !isReplyToBridge) return;\n\n await this.forwardDiscordToQQ(message, bridge, referenceId);\n }\n\n /**\n * \u673A\u5668\u4EBA\u81EA\u5DF1\u5728 Discord \u4E92\u901A\u9891\u9053\u53D1\u51FA\u7684\u6D88\u606F \u2192 QQ\uFF08\u547D\u4EE4\u56DE\u590D\u3001AI \u56DE\u590D\u3001GitHub \u5361\u7247\u3001\n * \u659C\u6760\u547D\u4EE4\u56DE\u590D\u7B49\uFF0C\u590D\u7528 forwardDiscordToQQ \u7684\u6E32\u67D3/\u56DE\u590D\u94FE/\u9644\u4EF6\u5904\u7406\uFF09\u3002\n *\n * \u9632\u56DE\u73AF\uFF1A\u6865\u81EA\u8EAB QQ\u2192Discord \u7684\u8F6C\u53D1\u5728\u53D1\u9001\u524D\u767B\u8BB0\u5F85\u53D1\u6807\u8BB0\uFF08pendingBridgeSends\uFF0C\n * \u517C\u5BB9 messageCreate \u5148\u4E8E send \u8FD4\u56DE\u7684\u4E71\u5E8F\uFF09\u3001\u53D1\u9001\u540E\u767B\u8BB0 dcFromBridge\uFF0C\u53CC\u91CD\u8BC6\u522B\u540E\u8DF3\u8FC7\uFF1B\n * \u95E8\u63A7\u4E0E\u666E\u901A Discord \u6D88\u606F\u4E00\u81F4\uFF08need_cmd \u9891\u9053\u4EC5\u653E\u884C\u300C\u5BF9\u6865\u6D88\u606F\u7684\u56DE\u590D\u300D\uFF09\u3002\n * \u8F6C\u53D1\u5230 QQ \u7684\u6D88\u606F\u5E26 fromBridge \u6807\u8BB0\u4E14\u767B\u8BB0\u4E3A\u6865\u6D88\u606F\uFF0CQQ \u4FA7\u56DE\u590D\u5B83\u53EF\u8DEF\u7531\u56DE\u672C\u9891\u9053\u3002\n */\n private async handleOwnDiscordMessage(message: DiscordMessage, bridge: ResolvedBridge): Promise<void> {\n // \u4E71\u5E8F\u9632\u56DE\u73AF\uFF1AmessageCreate \u5148\u4E8E registerDiscordMessage \u5230\u8FBE\u65F6 dcFromBridge \u67E5\u4E0D\u5230\uFF0C\n // \u6309\u300C\u9891\u9053+\u6B63\u6587\u300D\u6838\u9500\u53D1\u9001\u524D\u767B\u8BB0\u7684\u5F85\u53D1\u6807\u8BB0\uFF08\u4EC5\u6865\u8F6C\u53D1\u8D70 sendWithReference\uFF0C\u5FC5\u7136\u767B\u8BB0\uFF09\n const pendingKey = `${message.channelId}\\n${message.content ?? ''}`;\n const pending = this.pendingBridgeSends.get(pendingKey) ?? 0;\n if (pending > 0) {\n if (pending === 1) this.pendingBridgeSends.delete(pendingKey);\n else this.pendingBridgeSends.set(pendingKey, pending - 1);\n return;\n }\n if (this.dcFromBridge.get(this.snowflakeToKey(message.id)) === true) return;\n // \u4EA4\u4E92\u5EF6\u8FDF\u5360\u4F4D\u7B49\u65E0\u5185\u5BB9\u6D88\u606F\u4E0D\u540C\u6B65\n if (!message.content && message.attachments.size === 0) return;\n const referenceId = message.reference?.messageId;\n const referenceKey = referenceId ? this.snowflakeToKey(referenceId) : undefined;\n const isReplyToBridge = referenceKey !== undefined && this.dcFromBridge.get(referenceKey) === true;\n if (bridge.entry.need_cmd === 'true' && !isReplyToBridge) return;\n await this.forwardDiscordToQQ(message, bridge, referenceId);\n }\n\n /**\n * Discord \u547D\u4EE4\u5206\u53D1\uFF1A\u547D\u4E2D\u5DF2\u6CE8\u518C\u547D\u4EE4\u6839\u8282\u70B9\u5219\u6267\u884C\u5E76\u8FD4\u56DE true\uFF08\u6D88\u606F\u4E0D\u518D\u8F6C\u53D1\uFF09\uFF0C\n * \u5426\u5219\u8FD4\u56DE false \u4EA4\u56DE\u666E\u901A\u8F6C\u53D1\u6D41\u7A0B\u3002\n */\n private async handleDiscordCommand(message: DiscordMessage, bridge: ResolvedBridge, raw: string): Promise<boolean> {\n if (!this.bot) return false;\n const trimmed = raw.trim();\n if (!trimmed.startsWith('/') && !trimmed.startsWith('!')) return false;\n // Discord \u5BA2\u6237\u7AEF\u4F1A\u628A / \u5F00\u5934\u5185\u5BB9\u5F53\u4F5C\u659C\u6760\u547D\u4EE4\u8F93\u5165\uFF0C\u989D\u5916\u652F\u6301 ! \u524D\u7F00\u515C\u5E95\uFF0C\u7EDF\u4E00\u5F52\u4E00\u5316\u4E3A /\n const normalized = normalizeCommandText(trimmed);\n const rootName = normalized.slice(1).split(/\\s/)[0];\n if (!rootName || !this.isRegisteredCommand(rootName)) return false;\n const groupId = Number(bridge.entry.group);\n const source = new DiscordMsgCommandSource(this.bot, message, Number.isSafeInteger(groupId) ? groupId : undefined);\n try {\n executeCommand(this.bot.getCommandManager(), source, normalized);\n } catch (error) {\n this.bot.logger?.error(\n `[DiscordBridge] Discord \u547D\u4EE4\u6267\u884C\u5931\u8D25: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}`\n );\n }\n return true;\n }\n\n /** \u9996\u6BB5\u662F\u5426\u4E3A\u5DF2\u6CE8\u518C\u547D\u4EE4\uFF08\u904D\u5386\u5168\u90E8\u547D\u540D\u7A7A\u95F4\u6839\u8282\u70B9\u7684\u5B57\u9762\u91CF\u5B50\u8282\u70B9\uFF09 */\n private isRegisteredCommand(name: string): boolean {\n const manager = this.bot?.getCommandManager();\n if (!manager) return false;\n for (const root of manager.roots.values()) {\n if (root.children.some(child => child.isLiteral() && child.toString() === name)) return true;\n }\n return false;\n }\n\n /** \u628A GitHub Issue/PR \u67E5\u8BE2\u7ED3\u679C\u4EE5 Discord \u56DE\u590D\u53D1\u56DE\u539F\u9891\u9053\uFF08\u56FE\u7247\u4E3A\u9644\u4EF6\uFF0C\u5931\u8D25\u63D0\u793A\u4E3A\u6587\u672C\uFF09 */\n private async sendGithubResultToDiscord(message: DiscordMessage, result: Message): Promise<void> {\n try {\n if (result.type === 'image') {\n const base64 = this.extractBase64Payload(result.data.file);\n if (!base64) return;\n await message.reply({\n files: [{ attachment: Buffer.from(base64, 'base64'), name: 'github.png' }],\n allowedMentions: { parse: [], repliedUser: false }\n });\n return;\n }\n if (result.type === 'text') {\n await message.reply({\n content: this.truncateDiscord(result.data.text),\n allowedMentions: { parse: [], repliedUser: false }\n });\n }\n } catch (error) {\n this.bot?.logger?.error(\n `[DiscordBridge] GitHub \u5361\u7247\u56DE\u590D\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n /** Discord\u2192QQ \u8F6C\u53D1\u4E3B\u6D41\u7A0B\uFF1BcontentOverride \u7528\u4E8E /send\uFF08\u5265\u79BB\u547D\u4EE4\u524D\u7F00\u540E\u7684\u6B63\u6587\uFF09 */\n private async forwardDiscordToQQ(\n message: DiscordMessage,\n bridge: ResolvedBridge,\n referenceId?: string | null,\n viaCommand: boolean = false,\n contentOverride?: string\n ): Promise<void> {\n if (!this.bot) return;\n const bot = this.bot;\n const channel = message.channel as TextChannel;\n const guildName = message.guild?.name ?? bridge.guildId;\n const authorName = message.member?.displayName ?? message.author.username;\n const header = `\u3010${guildName}|${channel.name}\u3011${authorName}(${message.author.username})\uFF1A`;\n\n const content = contentOverride ?? message.content ?? '';\n const segments: Message[] = [];\n\n // \u56DE\u590D\u94FE\uFF1A\u8F6C\u53D1\u5230 QQ \u65F6\u5E26 reply \u5F15\u7528\n const replyTarget = referenceId ? this.qqByDiscord.get(this.snowflakeToKey(referenceId)) : undefined;\n if (replyTarget !== undefined) {\n segments.push({ type: 'reply', data: { id: replyTarget } });\n }\n\n if (content) {\n if (isDiscordMarkdown(content)) {\n // \u542B Discord markdown \u2192 \u6E32\u67D3\u4E3A\u56FE\u7247\u8F6C\u53D1\uFF08\u5934\u90E8\u5E76\u5165\u56FE\u7247\uFF09\n try {\n const base64 = await renderDiscordMessageToImage(header, content, message);\n segments.push({ type: 'image', data: { file: `base64://${base64}` } });\n } catch (error) {\n // \u6E32\u67D3\u5931\u8D25\u964D\u7EA7\u4E3A\u7EAF\u6587\u672C\u8F6C\u53D1\uFF0C\u4FDD\u8BC1\u6D88\u606F\u4E0D\u4E22\n bot.logger?.error(\n `[DiscordBridge] markdown \u6E32\u67D3\u5931\u8D25\uFF0C\u964D\u7EA7\u7EAF\u6587\u672C: ${error instanceof Error ? error.message : String(error)}`\n );\n segments.push({ type: 'text', data: { text: `${header}\\n${this.plainDiscordContent(message, content)}` } });\n }\n } else {\n segments.push({ type: 'text', data: { text: `${header}\\n${this.plainDiscordContent(message, content)}` } });\n }\n } else if (segments.length === 0 || message.attachments.size > 0) {\n // \u65E0\u6587\u5B57\u4F46\u6709\u9644\u4EF6/\u7EAF embed\uFF1A\u8865\u5934\u90E8\u884C\n segments.push({ type: 'text', data: { text: header } });\n }\n\n // \u9644\u4EF6\uFF1A\u56FE\u7247\u76F4\u63A5\u53D1\u56FE\u7247\u6BB5\uFF0C\u5176\u4F59\u7C7B\u578B\u4EE5\u94FE\u63A5\u6587\u672C\u884C\u8F6C\u53D1\n for (const attachment of message.attachments.values()) {\n if (attachment.contentType?.startsWith('image/')) {\n segments.push({ type: 'image', data: { file: attachment.url } });\n } else {\n segments.push({ type: 'text', data: { text: `\\n[\u9644\u4EF6] ${attachment.name}: ${attachment.url}` } });\n }\n }\n\n // \u7EAF embed \u6D88\u606F\u515C\u5E95\n if (!content && message.attachments.size === 0 && message.embeds.length > 0) {\n const embed = message.embeds[0];\n segments.push({ type: 'text', data: { text: `\\n[Embed] ${embed?.title ?? ''} ${embed?.url ?? ''}`.trim() } });\n }\n\n if (segments.length === 0) return;\n // fromBridge\uFF1A\u6807\u8BB0\u4E3A\u6865\u81EA\u8EAB\u53D1\u51FA\u7684\u6D88\u606F\uFF0C\u53D1\u9001\u51FA\u53E3\u94A9\u5B50\u636E\u6B64\u8DF3\u8FC7\uFF0C\u907F\u514D\u56DE\u73AF\n const qqMessageId = await bot.sendGroupMsg(bridge.entry.group, segments, { fromBridge: true });\n if (qqMessageId !== undefined) {\n this.registerQQMessage(qqMessageId, message.id, bridge.key, viaCommand);\n this.bridgeByQQForward.set(qqMessageId, bridge.key);\n bot.logger?.debug(`[DiscordBridge] Discord\u2192QQ\uFF1A${bridge.key} \u2192 ${bridge.entry.group}\uFF08${qqMessageId}\uFF09`);\n }\n }\n\n /** Discord \u6D88\u606F\u6B63\u6587\u7EAF\u6587\u672C\u5316\uFF08\u63D0\u53CA/\u9891\u9053/\u8868\u60C5 \u2192 \u53EF\u8BFB\u6587\u672C\uFF09\uFF1BcontentOverride \u7528\u4E8E /send \u5265\u79BB\u524D\u7F00\u540E\u7684\u6B63\u6587 */\n private plainDiscordContent(message: DiscordMessage, contentOverride?: string): string {\n let text = contentOverride ?? message.content;\n text = text.replace(/<@!?(\\d+)>/g, (_match, id: string) => {\n const name = message.mentions.members?.get(id)?.displayName ?? message.mentions.users.get(id)?.username;\n return `@${name ?? id}`;\n });\n text = text.replace(/<@&(\\d+)>/g, (_match, id: string) => {\n return `@${message.guild?.roles.cache.get(id)?.name ?? id}`;\n });\n text = text.replace(/<#(\\d+)>/g, (_match, id: string) => {\n return `#${message.guild?.channels.cache.get(id)?.name ?? id}`;\n });\n text = text.replace(/<a?:(\\w{2,32}):\\d+>/g, ':$1:');\n return text.trim();\n }\n\n // -------------------------------------------------------------------------\n // /send <msg> [group|channel]\n // -------------------------------------------------------------------------\n\n /** \u5224\u65AD\u6587\u672C\u662F\u5426\u4E3A /send \u547D\u4EE4\uFF08/send \u540E\u5FC5\u987B\u4E3A\u7A7A\u767D\u6216\u7ED3\u5C3E\uFF0C\u907F\u514D\u8BEF\u541E /sendxxx \u6D88\u606F\uFF09 */\n private isSendCommand(raw: string): boolean {\n return raw.startsWith(SEND_COMMAND_PREFIX) && (raw.length === SEND_COMMAND_PREFIX.length || /\\s/.test(raw[SEND_COMMAND_PREFIX.length]!));\n }\n\n /**\n * \u89E3\u6790 /send \u53C2\u6570\uFF1A\u5C3E\u90E8 token \u547D\u4E2D\u67D0\u4E2A bridge \u7684 QQ \u7FA4\u53F7\u6216\u9891\u9053 key\n * \uFF08`guildId#channel`\uFF0C\u6216 `#channel` \u540E\u7F00\u552F\u4E00\u5339\u914D\uFF09\u65F6\u4F5C\u4E3A\u76EE\u6807\uFF0C\u5176\u4F59\u4E3A\u6D88\u606F\u6B63\u6587\u3002\n * \u4E0D\u586B\u76EE\u6807\u65F6\u9ED8\u8BA4\u53D1\u9001\u5230\u914D\u7F6E\u6587\u4EF6\u4E2D\u7B2C\u4E00\u4E2A\u4E92\u901A\u6761\u76EE\u7684\u5BF9\u7AEF\u3002\n */\n private parseSendArgs(text: string): { message: string; target?: string } | null {\n const body = text.slice(SEND_COMMAND_PREFIX.length).trim();\n if (!body) return null;\n const tokens = body.split(/\\s+/);\n const bridges = getDiscordBridgeConfig().bridges;\n const keys = Object.keys(bridges);\n if (tokens.length >= 2) {\n const last = tokens[tokens.length - 1]!;\n // QQ \u7FA4\u53F7\n const byGroup = keys.find(key => bridges[key]!.group === last);\n if (byGroup) return { message: tokens.slice(0, -1).join(' ').trim(), target: byGroup };\n // \u5B8C\u6574 key\n if (keys.includes(last)) return { message: tokens.slice(0, -1).join(' ').trim(), target: last };\n // #\u9891\u9053\u540D \u540E\u7F00\uFF08\u552F\u4E00\u5339\u914D\uFF09\n if (last.startsWith('#')) {\n const channelName = last.slice(1);\n const matched = keys.filter(key => key.split('#')[1] === channelName);\n const only = matched[0];\n if (matched.length === 1 && only) return { message: tokens.slice(0, -1).join(' ').trim(), target: only };\n }\n }\n return { message: body };\n }\n\n /** QQ \u4FA7 /send\uFF1A\u53D1\u9001\u5230\u76EE\u6807 Discord \u9891\u9053\uFF08\u6216\u76EE\u6807 QQ \u7FA4\uFF09 */\n private async handleSendFromQQ(bot: QQBot, msg: GroupMessageWSMSG, raw: string): Promise<void> {\n const parsed = this.parseSendArgs(raw);\n if (!parsed || !parsed.message) {\n bot.sendGroupMsg(msg.group_id, [\n { type: 'reply', data: { id: msg.message_id } },\n { type: 'text', data: { text: `\u7528\u6CD5\uFF1A${SEND_COMMAND_PREFIX} <\u6D88\u606F> [QQ\u7FA4\u53F7|\u670D\u52A1\u5668ID#\u9891\u9053\u540D]` } }\n ]);\n return;\n }\n const targetKey = parsed.target ?? Object.keys(getDiscordBridgeConfig().bridges)[0];\n if (!targetKey) {\n bot.sendGroupMsg(msg.group_id, [{ type: 'text', data: { text: '[DiscordBridge] \u5C1A\u672A\u914D\u7F6E\u4EFB\u4F55\u4E92\u901A\u9891\u9053' } }]);\n return;\n }\n const bridge = this.resolveByKey(targetKey);\n if (!bridge) return;\n await this.forwardQQToDiscord(bot, msg, bridge, undefined, true, parsed.message);\n }\n\n /** Discord \u4FA7 /send\uFF1A\u53D1\u9001\u5230\u76EE\u6807 QQ \u7FA4\uFF08\u6216\u76EE\u6807 Discord \u9891\u9053\uFF09 */\n private async handleSendFromDiscord(message: DiscordMessage, raw: string): Promise<void> {\n const parsed = this.parseSendArgs(raw);\n if (!parsed || !parsed.message) {\n await message.reply(`\u7528\u6CD5\uFF1A${SEND_COMMAND_PREFIX} <\u6D88\u606F> [QQ\u7FA4\u53F7|\u670D\u52A1\u5668ID#\u9891\u9053\u540D]`).catch(() => undefined);\n return;\n }\n const targetKey = parsed.target ?? Object.keys(getDiscordBridgeConfig().bridges)[0];\n if (!targetKey) {\n await message.reply('[DiscordBridge] \u5C1A\u672A\u914D\u7F6E\u4EFB\u4F55\u4E92\u901A\u9891\u9053').catch(() => undefined);\n return;\n }\n const bridge = this.resolveByKey(targetKey);\n if (!bridge) return;\n // \u6765\u6E90\u9891\u9053\u5373\u76EE\u6807\u9891\u9053\u65F6\u6309\u666E\u901A\u6D88\u606F\u5904\u7406\uFF0C\u907F\u514D\u540C\u9891\u9053\u590D\u8BFB\n await this.forwardDiscordToQQ(message, bridge, null, true, parsed.message);\n }\n\n // -------------------------------------------------------------------------\n // \u8DEF\u7531\u8868\u4E0E\u9891\u9053\u89E3\u6790\n // -------------------------------------------------------------------------\n\n /** \u767B\u8BB0\u4E00\u6B21 QQ\u2192Discord \u8F6C\u53D1\u7684 id \u6620\u5C04\uFF08\u56DE\u590D\u94FE\u7528\uFF1B\u6309 bridge key \u5206\u522B\u8BB0\u5F55\uFF0C\u540C\u7FA4\u591A\u9891\u9053\u4E92\u4E0D\u8986\u76D6\uFF09 */\n private registerDiscordMessage(discordId: string, qqId: number, bridgeKey: string, viaCommand: boolean = false): void {\n const key = this.snowflakeToKey(discordId);\n const entry = this.discordByQQ.get(qqId) ?? {};\n entry[bridgeKey] = discordId;\n this.discordByQQ.set(qqId, entry);\n this.qqByDiscord.set(key, qqId);\n this.dcFromBridge.set(key, true);\n }\n\n /** \u767B\u8BB0\u4E00\u6B21 Discord\u2192QQ \u8F6C\u53D1\u7684 id \u6620\u5C04\uFF08\u56DE\u590D\u94FE\u7528\uFF1B\u6309 bridge key \u5206\u522B\u8BB0\u5F55\uFF0C\u540C\u7FA4\u591A\u9891\u9053\u4E92\u4E0D\u8986\u76D6\uFF09 */\n private registerQQMessage(qqId: number, discordId: string, bridgeKey: string, viaCommand: boolean = false): void {\n this.qqByDiscord.set(this.snowflakeToKey(discordId), qqId);\n const entry = this.discordByQQ.get(qqId) ?? {};\n entry[bridgeKey] = discordId;\n this.discordByQQ.set(qqId, entry);\n this.qqFromBridge.set(qqId, true);\n }\n\n /**\n * Discord snowflake \u662F 19 \u4F4D\u5341\u8FDB\u5236\u6570\uFF0C\u8D85\u51FA JS Number \u5B89\u5168\u6574\u6570\u8303\u56F4\u3002\n * \u56DE\u590D\u94FE\u6620\u5C04\u7528\u5341\u8FDB\u5236\u5B57\u7B26\u4E32\u6570\u503C\u5316\uFF08\u6309\u6A21 2^42 \u6298\u53E0\uFF09\u3002\u6A21\u6570\u53D6 2^42 \u800C\u975E 2^53\uFF1A\n * \u6298\u53E0\u7684\u4E2D\u95F4\u503C value*10+d \u5FC5\u987B\u5C0F\u4E8E 2^53 \u624D\u4E0D\u635F\u5931\u7CBE\u5EA6\uFF082^53 \u6A21\u6570\u4F1A\u8BA9\u4E2D\u95F4\u503C\n * \u8FBE\u5230 ~9e16 > 2^53\uFF0C\u76F8\u90BB id \u6298\u53E0\u540E\u76F8\u649E\uFF09\uFF1B2^42 \u4E0B\u4E2D\u95F4\u503C ~4.4e13\uFF0C\u5168\u7A0B\u7CBE\u786E\u3002\n * \u4E0D\u4F9D\u8D56 BigInt\uFF08target < ES2020 \u65E0 BigInt \u7C7B\u578B\uFF09\u3002\n */\n private snowflakeToKey(id: string): number {\n let value = 0;\n const MODULUS = 2 ** 42;\n for (const char of id) {\n value = (value * 10 + (char.charCodeAt(0) - 48)) % MODULUS;\n }\n return value;\n }\n\n private resolveByKey(key: string): ResolvedBridge | undefined {\n const entry = getDiscordBridgeConfig().bridges[key];\n if (!entry) return undefined;\n const [guildId, channelName] = key.split('#');\n if (!guildId || !channelName) return undefined;\n return { key, guildId, channelName, entry };\n }\n\n /** \u8BE5 QQ \u7FA4\u7ED1\u5B9A\u7684\u5168\u90E8\u4E92\u901A\u6761\u76EE\uFF08\u540C\u7FA4\u53EF\u7ED1\u591A\u4E2A\u9891\u9053\uFF0C\u666E\u901A\u6D88\u606F\u9010\u6761\u95E8\u63A7\uFF09 */\n private resolveAllByGroup(groupId: number | string): ResolvedBridge[] {\n const group = String(groupId);\n const bridges = getDiscordBridgeConfig().bridges;\n return Object.keys(bridges)\n .filter(key => bridges[key]!.group === group)\n .map(key => this.resolveByKey(key)!);\n }\n\n private resolveByChannel(guildId: string, channelName: string): ResolvedBridge | undefined {\n return this.resolveByKey(`${guildId}#${channelName}`);\n }\n\n /** \u89E3\u6790\u914D\u7F6E\u6761\u76EE\u5BF9\u5E94\u7684 Discord \u6587\u672C\u9891\u9053\uFF08\u7F13\u5B58\u4F18\u5148\uFF0C\u672A\u7F13\u5B58\u65F6\u6309\u9891\u9053\u540D\u6293\u53D6\uFF09 */\n private async resolveDiscordChannel(bridge: ResolvedBridge): Promise<TextChannel | undefined> {\n if (!this.client) return undefined;\n const guild: Guild | undefined = this.client.guilds.cache.get(bridge.guildId);\n if (!guild) {\n this.bot?.logger?.warn(`[DiscordBridge] \u672A\u627E\u5230 Discord \u670D\u52A1\u5668 ${bridge.guildId}\uFF08${bridge.key}\uFF09`);\n return undefined;\n }\n const channel = guild.channels.cache.find(\n item => item.type === ChannelType.GuildText && item.name === bridge.channelName\n );\n if (channel) return channel as TextChannel;\n try {\n const fetched = await guild.channels.fetch();\n const target = fetched?.find(\n item => item?.type === ChannelType.GuildText && item.name === bridge.channelName\n );\n if (target) return target as TextChannel;\n } catch (error) {\n this.bot?.logger?.error(\n `[DiscordBridge] \u62C9\u53D6\u9891\u9053\u5217\u8868\u5931\u8D25\uFF08${bridge.key}\uFF09: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n this.bot?.logger?.warn(`[DiscordBridge] \u672A\u627E\u5230 Discord \u9891\u9053 ${bridge.key}\uFF08\u9891\u9053\u6539\u540D\u9700\u540C\u6B65\u914D\u7F6E\uFF09`);\n return undefined;\n }\n\n private truncateDiscord(text: string): string {\n if (text.length <= DISCORD_MESSAGE_LIMIT) return text;\n return `${text.slice(0, DISCORD_MESSAGE_LIMIT - 16)}\\n\u2026\uFF08\u6D88\u606F\u8FC7\u957F\u5DF2\u622A\u65AD\uFF09`;\n }\n}\n", "import { GroupMessageWSMSG, Message, TextMessage } from '@/type';\nimport { getFeatureGroups } from '@/config/features';\n\nexport class ParenthesesMatching {\n static readonly bracketPairs: Record<string, string> = {};\n static readonly closingBrackets: Set<string> = new Set();\n static readonly bracketTypeMap: Record<string, string> = {};\n static readonly reversePairs: Record<string, string> = {};\n\n static {\n ParenthesesMatching.add('()', 'paren');\n ParenthesesMatching.add('\uFF08\uFF09', 'full width paren');\n ParenthesesMatching.add('\u207D\u207E', 'superscript paren');\n ParenthesesMatching.add('\u208D\u208E', 'subscript paren');\n ParenthesesMatching.add('\uFE35\uFE36', 'vertical paren');\n ParenthesesMatching.add('[]', 'square');\n ParenthesesMatching.add('\uFF3B\uFF3D', 'full width square');\n ParenthesesMatching.add('\u3010\u3011', 'solid square');\n ParenthesesMatching.add('\u3016\u3017', 'hollow square');\n ParenthesesMatching.add('\uFE17\uFE18', 'vertical hollow square');\n ParenthesesMatching.add('\uFE3B\uFE3C', 'vertical solid square');\n ParenthesesMatching.add('{}', 'brace');\n ParenthesesMatching.add('\uFE5B\uFE5C', 'full width brace');\n ParenthesesMatching.add('\uFE37\uFE38', 'vertical brace');\n ParenthesesMatching.add('<>', 'angle');\n ParenthesesMatching.add('\u2039\u203A', 'angle');\n ParenthesesMatching.add('\uFE3F\uFE40', 'vertical angle');\n ParenthesesMatching.add('\u201C\u201D', 'full width quote');\n ParenthesesMatching.add('\u2018\u2019', 'full width single quote');\n ParenthesesMatching.add('\u300C\u300D', 'traditional quote');\n ParenthesesMatching.add('\u300E\u300F', 'hollow traditional quote');\n ParenthesesMatching.add('\u23A1\u23A6', 'long traditional quote');\n ParenthesesMatching.add('\u23A3\u23A4', 'reverse traditional quote');\n ParenthesesMatching.add('\uFE41\uFE42', 'vertical traditional quote');\n ParenthesesMatching.add('\uFE43\uFE44', 'vertical hollow traditional quote');\n ParenthesesMatching.add('\u300A\u300B', 'book');\n ParenthesesMatching.add('\uFE3D\uFE3E', 'vertical book');\n ParenthesesMatching.add('\u3014\u3015', 'hexagonal');\n ParenthesesMatching.add('\uFE39\uFE3A', 'vertical hexagonal');\n }\n\n static add(bracket: string, type: string) {\n if (bracket.length !== 2) return;\n const left = bracket[0];\n const right = bracket[1];\n if (!left || !right) return;\n ParenthesesMatching.bracketPairs[left] = right;\n ParenthesesMatching.closingBrackets.add(right);\n ParenthesesMatching.bracketTypeMap[left] = type;\n ParenthesesMatching.bracketTypeMap[right] = type;\n ParenthesesMatching.reversePairs[right] = left;\n }\n\n public static parenthesesMatching(msg: GroupMessageWSMSG, sentMessage: Message[]) {\n if (!getFeatureGroups('parentheses').includes(msg.group_id)) return;\n const receivedMessage: TextMessage[] = [];\n msg.message.forEach(message => {\n if (message.type != 'text') return;\n receivedMessage.push(message);\n });\n const stack: { char: string; position: number }[] = [];\n const strMsg = receivedMessage.map(msg => msg.data.text).join(' ');\n for (let i = 0; i < strMsg.length; i++) {\n const char: string = strMsg[i]!;\n if (ParenthesesMatching.bracketPairs[char]) {\n stack.push({ char, position: i });\n } else if (ParenthesesMatching.closingBrackets.has(char)) {\n const expected = ParenthesesMatching.reversePairs[char];\n const charType = ParenthesesMatching.bracketTypeMap[char];\n if (stack.length > 0 && stack[stack.length - 1]!.char === expected) {\n stack.pop();\n } else {\n let errorMsg = `\u62EC\u53F7\u5339\u914D\u9519\u8BEF\uFF1A\u7B2C ${i + 1} \u4E2A\u5B57\u7B26 '${char}' `;\n if (stack.length === 0) {\n let errorType = '\u62EC\u53F7';\n let errorDirection = '\u5DE6';\n if (charType && charType.endsWith('quote')) errorType = '\u5F15\u53F7';\n if (charType && charType.startsWith('vertical')) errorDirection = '\u4E0A';\n errorMsg += `\u6CA1\u6709\u5BF9\u5E94\u7684${errorDirection}${errorType}`;\n } else {\n const topBracket = stack[stack.length - 1]!;\n const expectedClosing = ParenthesesMatching.bracketPairs[topBracket.char];\n const topType = ParenthesesMatching.bracketTypeMap[topBracket.char];\n const currentType = ParenthesesMatching.bracketTypeMap[char];\n\n if (topType !== currentType) {\n errorMsg += `\u4E0E\u7B2C ${topBracket.position + 1} \u4E2A\u5B57\u7B26 '${topBracket.char}' \u7C7B\u578B\u4E0D\u5339\u914D\uFF0C\u671F\u671B '${expectedClosing}'`;\n } else {\n errorMsg += `\u4F4D\u7F6E\u9519\u8BEF`;\n }\n }\n\n sentMessage.push({\n type: 'text',\n data: {\n text: errorMsg\n }\n });\n return;\n }\n }\n }\n\n let result = '';\n while (stack.length > 0) {\n const leftBracket = stack.pop()!;\n result += ParenthesesMatching.bracketPairs[leftBracket.char];\n }\n if (result.trim().length == 0) return;\n sentMessage.push({\n type: 'text',\n data: {\n text: result\n }\n });\n }\n}\n", "import { Message, NotifyNoticeWSMSG } from '@/type';\nimport { QQBot } from '@/index';\nimport { getFeatureGroups } from '@/config/features';\nimport dayjs from 'dayjs';\n\nexport class Poke {\n public static processPokeMsg(bot: QQBot, msg: NotifyNoticeWSMSG, sentMessage: Message[]) {\n const hour = dayjs().hour();\n if (hour > 7 && hour < 21) return;\n if (!msg.group_id || !getFeatureGroups('poke').includes(msg.group_id)) return;\n const loginInfo = bot.getLoginInfoSync();\n if (!loginInfo || msg.target_id !== loginInfo.user_id) return;\n const msgPool = ['\u4E0B\u4E00\u4E2A', '\u96F6\u5206', '\u8D1F\u5206', '\u7ED9\u6211\u6EDA'];\n const banPool = [60, 120, 240, 300];\n const rand = Math.round(Math.random() * msgPool.length);\n sentMessage.push({\n type: 'text',\n data: {\n text: msgPool[rand] || '\u96F6\u5206'\n }\n });\n bot.ban(msg.group_id, msg.user_id, banPool[rand] || 120);\n }\n}\n", "import { QQBot } from '@/index';\nimport { GroupDecreaseNoticeWSMSG, GroupIncreaseNoticeWSMSG, GroupRequestWSMSG } from '@/type';\nimport { EventDataManager } from '@/event';\nimport { getFeatureConfig } from '@/config/features';\n\nfunction handleGroupDecreaseNotice(bot: QQBot, msg: GroupDecreaseNoticeWSMSG) {\n if (!getFeatureConfig('management').groups.includes(msg.group_id)) return;\n EventDataManager.getStorage('management', 'ban_list').then(raw => {\n // \u5F52\u4E00\u5316\u540E\u518D\u5224\u91CD\uFF1A\u517C\u5BB9\u5386\u53F2\u5B57\u7B26\u4E32\u6761\u76EE\uFF0C\u907F\u514D\u540C\u4E00\u7528\u6237\u88AB\u5199\u6210\u4E24\u4EFD\uFF08\u6570\u5B57 + \u5B57\u7B26\u4E32\uFF09\n const banList = Management.normalizeBanList(raw);\n const userId = Number(msg.user_id);\n if (!Number.isSafeInteger(userId) || userId <= 0) return;\n // \u5DF2\u5F52\u4E00\u5316\u96C6\u5408\u91CC\u5B58\u5728\u5373\u89C6\u4E3A\u5DF2\u62C9\u9ED1\uFF1B\u987A\u5E26\u628A\u5F52\u4E00\u5316\u7ED3\u679C\u5199\u56DE\uFF0C\u6E05\u7406\u5386\u53F2\u810F\u6570\u636E\n const alreadyBanned = banList.includes(userId);\n if (alreadyBanned && JSON.stringify(banList) === JSON.stringify(raw)) return;\n if (!alreadyBanned) banList.push(userId);\n EventDataManager.setStorage('management', 'ban_list', banList).then(() => {\n if (alreadyBanned) return; // \u4EC5\u505A\u6570\u636E\u6E05\u7406\uFF0C\u4E0D\u518D\u91CD\u590D\u64AD\u62A5\n const reason = msg.sub_type === 'kick' ? '\u88AB\u7BA1\u7406\u5458\u79FB\u51FA\u7FA4\u804A' : '\u9000\u51FA\u7FA4\u804A';\n bot.getUserInfo(msg.user_id).then(userInfo => {\n bot.sendGroupMsg(msg.group_id, [\n {\n type: 'text',\n data: {\n text: `\u7528\u6237 ${userInfo.nickname}(${userInfo.user_id}) \u5DF2${reason}\uFF0C\u73B0\u5DF2\u52A0\u5165\u9ED1\u540D\u5355\uFF0C\u5C06\u81EA\u52A8\u62D2\u7EDD\u540E\u7EED\u52A0\u7FA4\u7533\u8BF7\uFF01`\n }\n }\n ]);\n });\n });\n });\n}\n\nfunction handleGroupIncreaseNotice(bot: QQBot, msg: GroupIncreaseNoticeWSMSG) {\n if (!getFeatureConfig('management').groups.includes(msg.group_id)) return;\n EventDataManager.getStorage('management', 'ban_list').then(raw => {\n const banList = Management.normalizeBanList(raw);\n const userId = Number(msg.user_id);\n if (!banList.includes(userId)) return;\n if (msg.sub_type === 'invite' && getFeatureConfig('management').operators?.includes(msg.operator_id)) {\n // \u7BA1\u7406\u5458\u4E3B\u52A8\u9080\u8BF7 = \u89C6\u4E3A\u8D66\u514D\uFF1A\u79FB\u51FA\u9ED1\u540D\u5355\n EventDataManager.setStorage(\n 'management',\n 'ban_list',\n banList.filter(id => id !== userId)\n ).then();\n return;\n }\n bot.kick(msg.group_id, msg.user_id);\n bot.getUserInfo(msg.user_id).then(userInfo => {\n bot.sendGroupMsg(msg.group_id, [\n {\n type: 'text',\n data: {\n text: `\u9ED1\u540D\u5355\u7528\u6237 ${userInfo.nickname}(${userInfo.user_id}) \u52A0\u7FA4\uFF0C\u5DF2\u81EA\u52A8\u79FB\u51FA\uFF01`\n }\n }\n ]);\n });\n });\n}\n\nfunction handleGroupRequest(bot: QQBot, msg: GroupRequestWSMSG) {\n if (!getFeatureConfig('management').groups.includes(msg.group_id)) return;\n if (msg.sub_type !== 'add') return;\n EventDataManager.getStorage('management', 'ban_list').then(raw => {\n const banList = Management.normalizeBanList(raw);\n if (!banList.includes(Number(msg.user_id))) return;\n bot.denyGroupAddRequest(msg.flag, '\u4F60\u5DF2\u88AB\u62C9\u5165\u9ED1\u540D\u5355\uFF0C\u7CFB\u7EDF\u81EA\u52A8\u62D2\u7EDD\uFF01');\n bot.getUserInfo(msg.user_id).then(userInfo => {\n bot.sendGroupMsg(msg.group_id, [\n {\n type: 'text',\n data: {\n text: `\u9ED1\u540D\u5355\u7528\u6237 ${userInfo.nickname}(${userInfo.user_id}) \u7533\u8BF7\u52A0\u7FA4\uFF0C\u5DF2\u81EA\u52A8\u62D2\u7EDD\uFF01`\n }\n }\n ]);\n });\n });\n}\n\nexport class Management {\n public static handleGroupDecreaseNotice(bot: QQBot, msg: GroupDecreaseNoticeWSMSG) {\n handleGroupDecreaseNotice(bot, msg);\n }\n\n public static handleGroupIncreaseNotice(bot: QQBot, msg: GroupIncreaseNoticeWSMSG) {\n handleGroupIncreaseNotice(bot, msg);\n }\n\n public static handleGroupRequest(bot: QQBot, msg: GroupRequestWSMSG) {\n handleGroupRequest(bot, msg);\n }\n\n /**\n * /pardon <QQ\u53F7>\uFF1A\u628A\u7528\u6237\u4ECE\u9ED1\u540D\u5355\u79FB\u9664\uFF08\u8D66\u514D\uFF09\u3002\n * \u4EC5\u9650 management \u529F\u80FD\u542F\u7528\u7684\u7FA4 + \u7BA1\u7406\u5458\u4F7F\u7528\u3002\n *\n * \u6743\u9650\u6821\u9A8C\uFF08**\u5168\u90E8\u5728\u6B64\u5904\u5B8C\u6210**\uFF0C\u4E0D\u4F9D\u8D56\u8C03\u7528\u65B9\uFF09\uFF1A\n * - QQ \u4FA7 operatorId \u4E3A number \u2192 \u5FC5\u987B\u5728 management.json `operators` \u767D\u540D\u5355\u5185\uFF1B\n * - Discord \u4FA7 operatorId \u4E3A `dc:<\u7528\u6237ID>` \u2192 \u5FC5\u987B\u662F management.json `discordOperators`\n * \u767D\u540D\u5355\u6210\u5458\u3002\u6CE8\u610F\uFF1A\u6B64\u524D\u4EC5\u6821\u9A8C `dc:` \u524D\u7F00\u5373\u653E\u884C\uFF0C\u5BFC\u81F4\u88AB\u6865\u63A5\u9891\u9053\u91CC\u4EFB\u610F\u6210\u5458\u90FD\u80FD\u8D66\u514D\u9ED1\u540D\u5355\u7528\u6237\u3002\n *\n * @returns true=\u5DF2\u79FB\u9664\uFF1Bfalse=\u4E0D\u5728\u9ED1\u540D\u5355/\u65E0\u6743\u9650\n */\n public static async pardon(groupId: number, operatorId: number | string, userId: number): Promise<boolean> {\n const config = getFeatureConfig('management');\n if (!config.groups.includes(groupId)) return false;\n if (!this.isOperatorAllowed(operatorId)) return false;\n\n // \u9ED1\u540D\u5355\u5386\u53F2\u6570\u636E/\u4E0D\u540C OneBot \u5B9E\u73B0\u53EF\u80FD\u5199\u5165\u5B57\u7B26\u4E32\u5F62\u6001\u7684 QQ \u53F7\uFF0C\n // \u7EDF\u4E00\u6309\u6570\u503C\u6BD4\u8F83\uFF0C\u907F\u514D\u300C\u660E\u660E\u5728\u9ED1\u540D\u5355\u91CC\u5374\u5220\u4E0D\u6389\u300D\u3002\n const banList = this.normalizeBanList(await EventDataManager.getStorage('management', 'ban_list'));\n if (!banList.includes(userId)) return false;\n\n const next = banList.filter(id => id !== userId);\n await EventDataManager.setStorage('management', 'ban_list', next);\n return true;\n }\n\n /**\n * \u64CD\u4F5C\u8005\u662F\u5426\u6709\u6743\u6267\u884C\u7BA1\u7406\u64CD\u4F5C\u3002\n *\n * - number\uFF08QQ \u53F7\uFF09\u2192 operators \u767D\u540D\u5355\uFF1B\n * - `dc:` \u524D\u7F00 \u2192 discordOperators \u767D\u540D\u5355\uFF08\u5FC5\u987B\u662F\u663E\u5F0F\u914D\u7F6E\u7684\u7528\u6237\uFF09\uFF1B\n * - \u5176\u4ED6\u5F62\u6001 \u2192 \u62D2\u7EDD\u3002\n */\n public static isOperatorAllowed(operatorId: number | string): boolean {\n const config = getFeatureConfig('management');\n if (typeof operatorId === 'number') {\n return config.operators?.includes(operatorId) ?? false;\n }\n if (typeof operatorId === 'string' && /^dc:/i.test(operatorId)) {\n const target = operatorId.slice(3).trim();\n if (!target) return false;\n return (config.discordOperators ?? []).some(item => item.replace(/^dc:/i, '').trim() === target);\n }\n return false;\n }\n\n /**\n * \u5F52\u4E00\u5316\u9ED1\u540D\u5355\uFF1A\u5B57\u7B26\u4E32\u6570\u5B57\u8F6C number\u3001\u5254\u9664\u975E\u6CD5\u9879\u3001\u53BB\u91CD\u3002\n * \u517C\u5BB9\u5386\u53F2\u810F\u6570\u636E\uFF08\u5982\u88AB\u5199\u6210 `\"2017065007\"` \u7684\u6761\u76EE\uFF09\uFF0C\u907F\u514D\u300C\u660E\u660E\u5728\u9ED1\u540D\u5355\u91CC\u5374\u5220\u4E0D\u6389\u300D\u3002\n */\n public static normalizeBanList(raw: unknown): number[] {\n if (!Array.isArray(raw)) return [];\n const result: number[] = [];\n for (const item of raw) {\n const value = typeof item === 'string' ? Number(item.trim()) : item;\n if (typeof value !== 'number' || !Number.isSafeInteger(value) || value <= 0) continue;\n if (!result.includes(value)) result.push(value);\n }\n return result;\n }\n}\n", "import { QQBot } from '@/index';\nimport { GroupIncreaseNoticeWSMSG } from '@/type';\nimport { getWelcomeConfig } from '@/config/features';\n\n/**\n * \u65B0\u4EBA\u6B22\u8FCE\u6A21\u5757\u3002\n *\n * \u914D\u7F6E\uFF08configs/features/welcome.json\uFF0Cv1\uFF09\uFF1A\n * ```json\n * {\n * \"version\": 1,\n * \"welcomes\": [\n * { \"group\": [123456, 234567], \"msg\": \"\u6B22\u8FCE ${at} \u52A0\u5165\u7FA4\u804A\" },\n * { \"group\": [888888], \"msg\": [\"${at} \u6B22\u8FCE\u4F60\uFF01\", \"\u8BF7\u67E5\u770B\u7FA4\u516C\u544A\", \"\u6709\u95EE\u9898\u627E\u7BA1\u7406\u5458\"] }\n * ]\n * }\n * ```\n * - `group`\uFF1A\u9002\u7528\u7FA4\u53F7\u5217\u8868\uFF08\u53EF\u591A\u4E2A\u7FA4\u5171\u7528\u540C\u4E00\u6761\u6B22\u8FCE\u8BED\uFF09\n * - `msg`\uFF1A\u6B22\u8FCE\u8BED\u6A21\u677F\uFF08\u5B57\u7B26\u4E32\u6216\u5B57\u7B26\u4E32\u6570\u7EC4\uFF0C\u6570\u7EC4\u6BCF\u9879\u4E00\u884C\uFF0C\\n \u62FC\u63A5\uFF09\uFF0C\n * `${at}` \u4F1A\u88AB\u66FF\u6362\u4E3A @\u65B0\u6210\u5458 \u6D88\u606F\u6BB5\n */\nexport class Welcome {\n /**\n * \u5904\u7406 group_increase \u901A\u77E5\uFF0C\u5339\u914D\u5230\u8BE5\u7FA4\u7684\u6B22\u8FCE\u914D\u7F6E\u540E\u53D1\u9001\u6B22\u8FCE\u6D88\u606F\u3002\n */\n public static handleGroupIncreaseNotice(bot: QQBot, msg: GroupIncreaseNoticeWSMSG): void {\n const config = getWelcomeConfig();\n const entry = config.welcomes.find(item => item.group.includes(msg.group_id));\n if (!entry) return;\n const message = buildWelcomeMessage(msg.user_id, entry.msg);\n bot.sendGroupMsg(msg.group_id, message);\n }\n}\n\n/**\n * \u5C06\u6B22\u8FCE\u6A21\u677F\u6E32\u67D3\u6210\u6D88\u606F\u6BB5\u6570\u7EC4\uFF1A`${at}` \u62C6\u5206\u4E3A @\u6210\u5458 \u6D88\u606F\u6BB5\uFF0C\n * \u5176\u4F59\u6587\u672C\u4FDD\u6301\u6587\u672C\u6BB5\uFF08\u907F\u514D\u4EE5\u5B57\u7B26\u4E32\u62FC\u63A5 CQ \u7801\uFF09\u3002\n * `template` \u53EF\u4E3A\u5B57\u7B26\u4E32\u6216\u5B57\u7B26\u4E32\u6570\u7EC4\uFF08\u6570\u7EC4\u6309\u6362\u884C\u62FC\u63A5\u4E3A\u591A\u884C\u6587\u672C\uFF09\u3002\n */\nexport function buildWelcomeMessage(qq: number, template: string | string[]): import('@/type').Message[] {\n const text = Array.isArray(template) ? template.join('\\n') : template;\n const parts = text.split('${at}');\n const messages: import('@/type').Message[] = [];\n parts.forEach((part, index) => {\n if (part) {\n messages.push({ type: 'text', data: { text: part } });\n }\n // \u5728\u6BCF\u6BB5\u6587\u672C\u4E4B\u540E\u63D2\u5165 @\uFF08\u9664\u6700\u540E\u4E00\u6BB5\uFF09\n if (index < parts.length - 1) {\n messages.push({ type: 'at', data: { qq } });\n }\n });\n // \u6A21\u677F\u4E2D\u65E0 ${at} \u65F6\u7684\u515C\u5E95\uFF1A\u628A QQ \u53F7\u9644\u5728\u672B\u5C3E\n if (messages.length === 0) {\n messages.push({ type: 'text', data: { text: `\u6B22\u8FCE\u65B0\u6210\u5458 ${qq} \u52A0\u5165\u7FA4\u804A` } });\n }\n return messages;\n}", "import dayjs from 'dayjs';\nimport { QQBot } from '@/index';\nimport { getFeatureGroups } from '@/config/features';\nimport { readOrCreate } from '@/config/manager';\nimport { Message } from '@/type';\n\nexport type PeakValleyMode = 'peak' | 'valley';\n\nexport type DayToken = 'Mon' | 'Tue' | 'Wed' | 'Thu' | 'Fri' | 'Sat' | 'Sun';\n\nexport interface PeakValleyTimerTimeRange {\n start: string;\n end: string;\n days: DayToken[];\n mode?: PeakValleyMode;\n msg?: string;\n}\n\nexport interface PeakValleyTimerConfig {\n groups?: number[];\n mode?: PeakValleyMode;\n time: Array<string | PeakValleyTimerTimeRange>;\n peak_msg?: string;\n valley_msg?: string;\n cmd_peak_msg?: string;\n cmd_valley_msg?: string;\n}\n\ninterface NormalizedTimeRange {\n startMinutes: number;\n endMinutes: number;\n days: number[];\n msg: string;\n mode?: PeakValleyMode;\n key: string;\n}\n\nconst DAY_MAP: Record<DayToken, number> = {\n Mon: 1,\n Tue: 2,\n Wed: 3,\n Thu: 4,\n Fri: 5,\n Sat: 6,\n Sun: 0\n};\n\nconst DAY_ORDER: DayToken[] = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];\n\nconst TIME_PATTERN: RegExp = /^([01]?\\d|2[0-3]):([0-5]\\d)$/;\n\nconst CHECK_INTERVAL_MS = 30 * 1000;\nconst CATCH_UP_WINDOW_SECONDS = 90;\n\nconst defaultConfig: PeakValleyTimerConfig = {\n groups: getFeatureGroups('command'),\n mode: 'peak',\n time: ['9:00-12:00 Mon-Fri', '14:00-18:00 Mon-Fri'],\n peak_msg: '\u6881\u6587\u5CF0\u65F6\u95F4\u5230\uFF01\\n\u5F53\u524D\u65F6\u95F4\u662F${time}',\n valley_msg: '\u6881\u6587\u8C37\u65F6\u95F4\u5230\uFF01\\n\u5F53\u524D\u65F6\u95F4\u662F${time}',\n cmd_peak_msg: '\u5F53\u524D\u65F6\u95F4\u662F${time}\uFF0C\u6881\u6587\u5CF0\u65F6\u95F4\uFF01',\n cmd_valley_msg: '\u5F53\u524D\u65F6\u95F4\u662F${time}\uFF0C\u6881\u6587\u8C37\u65F6\u95F4\uFF01'\n};\n\nfunction isPeakValleyMode(value: unknown): value is PeakValleyMode {\n return value === 'peak' || value === 'valley';\n}\n\nfunction isNumberArray(value: unknown): value is number[] {\n return Array.isArray(value) && value.every(item => typeof item === 'number' && Number.isSafeInteger(item));\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\nfunction parseTimeToMinutes(time: string): number | null {\n if (!TIME_PATTERN.test(time)) return null;\n const [hour, minute] = time.split(':');\n const hourValue = Number.parseInt(hour || '0', 10);\n const minuteValue = Number.parseInt(minute || '0', 10);\n if (Number.isNaN(hourValue) || Number.isNaN(minuteValue)) return null;\n return hourValue * 60 + minuteValue;\n}\n\nfunction expandDayToken(token: string): DayToken[] | null {\n const trimmed = token.trim();\n if (!trimmed) return null;\n const hyphenIndex = trimmed.indexOf('-');\n if (hyphenIndex === -1) {\n if (!(trimmed in DAY_MAP)) return null;\n return [trimmed as DayToken];\n }\n const from = trimmed.slice(0, hyphenIndex) as DayToken;\n const to = trimmed.slice(hyphenIndex + 1) as DayToken;\n if (!(from in DAY_MAP) || !(to in DAY_MAP)) return null;\n const fromIndex = DAY_ORDER.indexOf(from);\n const toIndex = DAY_ORDER.indexOf(to);\n const days: DayToken[] = [];\n let index = fromIndex;\n while (true) {\n const current = DAY_ORDER[index % DAY_ORDER.length];\n if (!current) break;\n days.push(current);\n if (current === to) break;\n index = (index + 1) % DAY_ORDER.length;\n }\n return days;\n}\n\nfunction parseDaysToken(daysToken: string): DayToken[] | null {\n if (!daysToken.trim()) return [];\n const tokens = daysToken.split(',').map(token => token.trim()).filter(token => token.length > 0);\n const result: DayToken[] = [];\n for (const token of tokens) {\n const expanded = expandDayToken(token);\n if (!expanded) return null;\n result.push(...expanded);\n }\n return Array.from(new Set(result));\n}\n\nfunction parseTimeRangeToken(token: string): PeakValleyTimerTimeRange | null {\n const parts = token.trim().split(/\\s+/);\n const timePart = parts[0];\n if (!timePart) return null;\n const dashIndex = timePart.indexOf('-');\n if (dashIndex === -1) return null;\n const start = timePart.slice(0, dashIndex).trim();\n const end = timePart.slice(dashIndex + 1).trim();\n if (parseTimeToMinutes(start) === null || parseTimeToMinutes(end) === null) return null;\n const daysToken = parts.slice(1).join(' ');\n const days = parseDaysToken(daysToken);\n if (days === null) return null;\n return { start, end, days };\n}\n\nfunction normalizeTimeRange(time: unknown): NormalizedTimeRange | null {\n let resolved: PeakValleyTimerTimeRange | null = null;\n if (typeof time === 'string') {\n resolved = parseTimeRangeToken(time);\n } else if (time && typeof time === 'object' && !Array.isArray(time)) {\n const candidate = time as Record<string, unknown>;\n if (isNonEmptyString(candidate.start) && isNonEmptyString(candidate.end)) {\n const daysValue = candidate.days;\n let days: DayToken[] = [];\n if (daysValue !== undefined) {\n if (!Array.isArray(daysValue) || !daysValue.every(isNonEmptyString)) return null;\n const parsedDays = parseDaysToken(daysValue.join(','));\n if (parsedDays === null) return null;\n days = parsedDays;\n }\n const parsedMode = isPeakValleyMode(candidate.mode) ? candidate.mode : undefined;\n const parsedMsg = isNonEmptyString(candidate.msg) ? candidate.msg : undefined;\n if (parsedMode || parsedMsg) {\n resolved = {\n start: candidate.start,\n end: candidate.end,\n days,\n ...(parsedMode ? { mode: parsedMode } : {}),\n ...(parsedMsg ? { msg: parsedMsg } : {})\n };\n } else {\n resolved = {\n start: candidate.start,\n end: candidate.end,\n days\n };\n }\n }\n }\n if (!resolved) return null;\n\n const startMinutes = parseTimeToMinutes(resolved.start);\n const endMinutes = parseTimeToMinutes(resolved.end);\n if (startMinutes === null || endMinutes === null) return null;\n\n const mode = resolved.mode;\n const msg = resolved.msg;\n const key = `${resolved.start}-${resolved.end}-${resolved.days.join(',')}-${mode || ''}-${msg || ''}`;\n return {\n startMinutes: startMinutes,\n endMinutes: endMinutes,\n days: resolved.days.map(day => DAY_MAP[day]),\n msg: msg || '',\n ...(mode ? { mode: mode } : {}),\n key\n };\n}\n\nfunction normalizeConfig(raw: PeakValleyTimerConfig | null): {\n groups: number[];\n mode: PeakValleyMode;\n time: NormalizedTimeRange[];\n peak_msg: string;\n valley_msg: string;\n cmd_peak_msg: string;\n cmd_valley_msg: string;\n} {\n const groups =\n raw && raw.groups !== undefined ? (isNumberArray(raw.groups) ? raw.groups : defaultConfig.groups!) : defaultConfig.groups!;\n const mode = raw && raw.mode !== undefined ? (isPeakValleyMode(raw.mode) ? raw.mode : 'peak') : 'peak';\n\n let time: NormalizedTimeRange[] = [];\n if (raw && Array.isArray(raw.time)) {\n time = raw.time.map(normalizeTimeRange).filter((range): range is NormalizedTimeRange => range !== null);\n }\n if (time.length === 0) {\n time = defaultConfig.time.map(normalizeTimeRange).filter((range): range is NormalizedTimeRange => range !== null);\n }\n\n const peak_msg =\n raw && raw.peak_msg !== undefined\n ? isNonEmptyString(raw.peak_msg)\n ? raw.peak_msg\n : defaultConfig.peak_msg!\n : defaultConfig.peak_msg!;\n const valley_msg =\n raw && raw.valley_msg !== undefined\n ? isNonEmptyString(raw.valley_msg)\n ? raw.valley_msg\n : defaultConfig.valley_msg!\n : defaultConfig.valley_msg!;\n const cmd_peak_msg =\n raw && raw.cmd_peak_msg !== undefined\n ? isNonEmptyString(raw.cmd_peak_msg)\n ? raw.cmd_peak_msg\n : defaultConfig.cmd_peak_msg!\n : defaultConfig.cmd_peak_msg!;\n const cmd_valley_msg =\n raw && raw.cmd_valley_msg !== undefined\n ? isNonEmptyString(raw.cmd_valley_msg)\n ? raw.cmd_valley_msg\n : defaultConfig.cmd_valley_msg!\n : defaultConfig.cmd_valley_msg!;\n return { groups, mode, time, peak_msg, valley_msg, cmd_peak_msg, cmd_valley_msg };\n}\n\nfunction normalizeRawConfig(raw: unknown): PeakValleyTimerConfig | null {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null;\n const record = raw as Record<string, unknown>;\n const time = record.time;\n if (time !== undefined && !Array.isArray(time)) return null;\n return raw as PeakValleyTimerConfig;\n}\n\nfunction loadConfig(): ReturnType<typeof normalizeConfig> {\n const { data } = readOrCreate<PeakValleyTimerConfig>({\n path: 'configs/peak-valley-timer.json',\n factory: () => defaultConfig,\n version: 1,\n normalize: normalizeRawConfig\n });\n return normalizeConfig(data);\n}\n\nexport class PeakValleyTimer {\n private static instance?: PeakValleyTimer = undefined;\n\n public static getInstance(): PeakValleyTimer {\n if (!PeakValleyTimer.instance) {\n PeakValleyTimer.instance = new PeakValleyTimer();\n }\n return PeakValleyTimer.instance;\n }\n\n private groups: number[];\n private mode: PeakValleyMode;\n private time: NormalizedTimeRange[];\n private peakMsg: string;\n private valleyMsg: string;\n private cmdPeakMsg: string;\n private cmdValleyMsg: string;\n private readonly reportedToday: Set<string> = new Set();\n private timer: NodeJS.Timeout | undefined = undefined;\n\n public constructor() {\n const config = loadConfig();\n this.groups = config.groups;\n this.mode = config.mode;\n this.time = config.time;\n this.peakMsg = config.peak_msg;\n this.valleyMsg = config.valley_msg;\n this.cmdPeakMsg = config.cmd_peak_msg;\n this.cmdValleyMsg = config.cmd_valley_msg;\n }\n\n /**\n * \u4ECE\u78C1\u76D8\u91CD\u65B0\u8BFB\u53D6\u914D\u7F6E\uFF08/reload \u7528\uFF09\u3002\n * \u672C\u7C7B\u5728\u6784\u9020\u65F6\u628A\u914D\u7F6E\u8BFB\u8FDB\u5B57\u6BB5\uFF08\u975E ConfigStore \u70ED\u91CD\u8F7D\uFF09\uFF0C\u6545\u9700\u663E\u5F0F\u91CD\u8F7D\u5165\u53E3\u3002\n */\n public reloadConfig(): void {\n const config = loadConfig();\n this.groups = config.groups;\n this.mode = config.mode;\n this.time = config.time;\n this.peakMsg = config.peak_msg;\n this.valleyMsg = config.valley_msg;\n this.cmdPeakMsg = config.cmd_peak_msg;\n this.cmdValleyMsg = config.cmd_valley_msg;\n }\n\n public start(bot: QQBot): void {\n bot.logger?.info(\n `PeakValleyTimer started: ${this.time.length} time range(s), ${this.groups.length} group(s), mode=${this.mode}`\n );\n this.timer = setInterval(() => this.check(bot), CHECK_INTERVAL_MS);\n setTimeout(() => this.check(bot), 1000);\n }\n\n public stop(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = undefined;\n }\n }\n\n /**\n * /pvtime \u547D\u4EE4\u5904\u7406\uFF1A\u6839\u636E\u5F53\u524D\u65F6\u95F4\u662F\u5426\u5904\u4E8E\u67D0\u4E2A peak/valley \u533A\u95F4\u8FD4\u56DE\u5BF9\u5E94\u6D88\u606F\u3002\n * \u547D\u4E2D\u533A\u95F4 \u2192 \u8BE5\u533A\u95F4\u7C7B\u578B\uFF08\u81EA\u8EAB mode \u4F18\u5148\uFF0C\u5426\u5219\u5168\u5C40 mode\uFF09\u7684 cmd \u6D88\u606F\uFF1B\n * \u672A\u547D\u4E2D\u4EFB\u4F55\u533A\u95F4 \u2192 \u5168\u5C40 mode \u76F8\u53CD\u7C7B\u578B\u7684 cmd \u6D88\u606F\u3002\n */\n public getCommandMessage(): string {\n const now = dayjs();\n const weekday = now.day();\n const nowMinutes = now.hour() * 60 + now.minute();\n const currentTime = now.format('HH:mm');\n\n for (const range of this.time) {\n if (this.isInRange(range, weekday, nowMinutes)) {\n const rangeMode: PeakValleyMode = range.mode ?? this.mode;\n return this.renderMessage(rangeMode === 'peak' ? this.cmdPeakMsg : this.cmdValleyMsg, currentTime);\n }\n }\n const opposite: PeakValleyMode = this.mode === 'peak' ? 'valley' : 'peak';\n return this.renderMessage(opposite === 'peak' ? this.cmdPeakMsg : this.cmdValleyMsg, currentTime);\n }\n\n /**\n * \u5224\u65AD\u67D0\u65F6\u523B\u662F\u5426\u5904\u4E8E\u533A\u95F4\u5185\u3002\u533A\u95F4\u5F52\u5C5E\u65E5\uFF1A\u4E0D\u8DE8\u5929\u533A\u95F4\u5F53\u5929\u5728 days \u4E2D\uFF1B\n * \u8DE8\u5929\u533A\u95F4\uFF08end <= start\uFF09[start, 24:00) \u5F53\u5929\u5728 days\uFF0C[0, end) \u524D\u4E00\u5929\u5728 days\u3002\n */\n private isInRange(range: NormalizedTimeRange, weekday: number, nowMinutes: number): boolean {\n if (range.endMinutes > range.startMinutes) {\n return range.days.includes(weekday) && nowMinutes >= range.startMinutes && nowMinutes < range.endMinutes;\n }\n if (nowMinutes >= range.startMinutes && range.days.includes(weekday)) return true;\n if (nowMinutes < range.endMinutes && range.days.includes((weekday + 6) % 7)) return true;\n return false;\n }\n\n private renderMessage(template: string, currentTime: string): string {\n return template.replace(/\\$\\{time\\}/g, currentTime);\n }\n\n private check(bot: QQBot): void {\n const now = dayjs();\n const today = now.format('YYYY-MM-DD');\n const weekday = now.day();\n const secondsIntoDay = now.hour() * 3600 + now.minute() * 60 + now.second();\n const nowMinutes = now.hour() * 60 + now.minute();\n\n for (const range of this.time) {\n this.checkStart(bot, range, today, weekday, secondsIntoDay, now, nowMinutes);\n this.checkEnd(bot, range, today, weekday, secondsIntoDay, now, nowMinutes);\n }\n }\n\n /**\n * \u533A\u95F4\u5F00\u59CB\u89E6\u53D1\uFF1A\u5F53\u5929\u5728 days \u4E2D\u4E14\u5230\u8FBE start \u65F6\u523B \u2192 \u62A5\u533A\u95F4\u7C7B\u578B\"\u5230\"\u3002\n */\n private checkStart(\n bot: QQBot,\n range: NormalizedTimeRange,\n today: string,\n weekday: number,\n secondsIntoDay: number,\n now: dayjs.Dayjs,\n nowMinutes: number\n ): void {\n if (!range.days.includes(weekday)) return;\n const elapsed = secondsIntoDay - range.startMinutes * 60;\n if (elapsed < 0 || elapsed >= CATCH_UP_WINDOW_SECONDS) return;\n const reportKey = `${today}:${range.key}:start`;\n if (this.reportedToday.has(reportKey)) return;\n this.reportedToday.add(reportKey);\n bot.logger?.info(`PeakValleyTimer start triggered: ${range.key} at ${nowMinutes}`);\n const rangeMode: PeakValleyMode = range.mode ?? this.mode;\n const message = this.buildMessage(rangeMode, range.msg, now);\n this.broadcast(bot, message);\n }\n\n /**\n * \u533A\u95F4\u7ED3\u675F\u89E6\u53D1\uFF1A\u5230\u8FBE end \u65F6\u523B \u2192 \u62A5\u76F8\u53CD\u7C7B\u578B\"\u5230\"\uFF08\u79BB\u5F00\u8BE5\u7C7B\u578B\u65F6\u6BB5\uFF09\u3002\n * \u4E0D\u8DE8\u5929\uFF1Aend \u89E6\u53D1\u65E5\u5728 days \u4E2D\u5F53\u5929\uFF1B\u8DE8\u5929\uFF08end <= start\uFF09\uFF1Astart \u5728\n * \u5F00\u59CB\u65E5 D \u7684 startMinutes\uFF0Cend \u5728 D+1 \u7684 endMinutes\uFF0C\u56E0\u6B64\u8981\u6C42\n * \u524D\u4E00\u5929\u5728 days \u4E2D\uFF0C\u4E14\u6B64\u523B\u5C1A\u672A\u5230\u4ECA\u5929\u7684 start\u3002\n */\n private checkEnd(\n bot: QQBot,\n range: NormalizedTimeRange,\n today: string,\n weekday: number,\n secondsIntoDay: number,\n now: dayjs.Dayjs,\n nowMinutes: number\n ): void {\n if (range.endMinutes > range.startMinutes) {\n if (!range.days.includes(weekday)) return;\n } else {\n // \u8DE8\u5929\u533A\u95F4\uFF1Aend \u5728\u5F00\u59CB\u65E5\u7684\u6B21\u65E5\u51CC\u6668\n const prevWeekday = (weekday + 6) % 7;\n if (!range.days.includes(prevWeekday)) return;\n if (nowMinutes >= range.startMinutes) return; // \u4ECA\u5929\u5DF2\u5230 start\uFF0C\u5C5E\u4E8E\u65B0\u4E00\u8F6E\u533A\u95F4\n }\n const elapsed = secondsIntoDay - range.endMinutes * 60;\n if (elapsed < 0 || elapsed >= CATCH_UP_WINDOW_SECONDS) return;\n const reportKey = `${today}:${range.key}:end`;\n if (this.reportedToday.has(reportKey)) return;\n this.reportedToday.add(reportKey);\n bot.logger?.info(`PeakValleyTimer end triggered: ${range.key} at ${nowMinutes}`);\n const rangeMode: PeakValleyMode = range.mode ?? this.mode;\n const opposite: PeakValleyMode = rangeMode === 'peak' ? 'valley' : 'peak';\n const message = this.buildMessage(opposite, range.msg, now);\n this.broadcast(bot, message);\n }\n\n private broadcast(bot: QQBot, message: string): void {\n for (const groupId of this.groups) {\n const sentMessage: Message[] = [\n {\n type: 'text',\n data: {\n text: message\n }\n }\n ];\n bot.sendGroupMsg(groupId, sentMessage);\n }\n }\n\n private buildMessage(rangeMode: PeakValleyMode, customMsg: string | undefined, now: dayjs.Dayjs): string {\n const currentTime = now.format('HH:mm');\n let template: string;\n if (customMsg) {\n template = customMsg;\n } else {\n template = rangeMode === 'valley' ? this.valleyMsg : this.peakMsg;\n }\n return template.replace(/\\$\\{time\\}/g, currentTime);\n }\n}", "import { bot, QQBot } from '@/index';\nimport {\n GroupDecreaseNoticeWSMSG,\n GroupIncreaseNoticeWSMSG,\n GroupMessageWSMSG,\n GroupRequestWSMSG,\n Message,\n NotifyNoticeWSMSG,\n PrivateMessageWSMSG\n} from '@/type';\nimport { EventDataManager } from '@/event';\nimport { ParenthesesMatching } from '@/features/parentheses';\nimport { Github } from '@/features/github';\nimport { GitHubBindingManager } from '@/features/github/binding';\nimport { Poke } from '@/features/poke';\nimport { Arguments, CommandManager, CommandSource } from 'gugle-command';\nimport { isBotCommandSource, resolveOperatorId } from '@/command';\nimport { MinecraftAPI } from '@/features/minecraft';\nimport { ModrinthAPI } from '@/features/modrinth';\nimport {\n getFeatureGroups,\n isValidRepositoryName,\n isValidRepositoryOwner,\n subscribeGithubRepository\n} from '@/config/features';\nimport { addGithubAllowedRepository, isGithubAllowedPattern } from '@/config';\nimport { checkReleasedVersion } from '@/features/version-tracker';\nimport { Bili } from '@/features/bili';\nimport { Management } from '@/features/management';\nimport { Welcome } from '@/features/welcome';\nimport { PeakValleyTimer } from '@/features/peak-valley-timer';\nimport {\n isRestartedByCommand,\n isRestartInProgress,\n reloadRuntime,\n restartApplication,\n restartStartDelayMs,\n shutdownApplication\n} from '@/features/admin';\nimport { DiscordBridge } from '@/features/discord-bridge';\nimport { HermesBridge } from '@/features/hermes';\n\nclass CustomBot {\n private static readonly peakValleyTimer: PeakValleyTimer = PeakValleyTimer.getInstance();\n\n /** \u91CD\u542F/\u5173\u505C\u5728\u53D1\u9001\u786E\u8BA4\u6D88\u606F\u540E\u7684\u5EF6\u8FDF\uFF0C\u786E\u4FDD\u56DE\u6267\u5148\u53D1\u51FA\u518D\u52A8\u8FDB\u7A0B */\n private static readonly REBOOT_CONFIRM_DELAY_MS = 500;\n\n public static helpCommand(source: CommandSource) {\n source.success(`\u5E2E\u52A9\n\u00B7 /mcv\uFF1A\u83B7\u53D6 Minecraft \u7248\u672C\u4FE1\u606F\n\u00B7 /server <ip> <port?>\uFF1A\u83B7\u53D6 Minecraft \u670D\u52A1\u5668\u72B6\u6001\n\u00B7 /wiki <query>\uFF1A\u641C\u7D22 Minecraft \u7EF4\u57FA\n\u00B7 /github bind <Username>\uFF1A\u7ED1\u5B9A GitHub \u7528\u6237\u540D\n\u00B7 /github subscribe <owner/repo>\uFF1A\u8BA2\u9605\u4ED3\u5E93\u6D88\u606F\u63A8\u9001\n\u00B7 /github allow <owner|owner/repo>\uFF1A\u6388\u6743\u4ED3\u5E93\u8BBF\u95EE\uFF08\u7BA1\u7406\u5458\uFF09\n\u00B7 /pardon <QQ\u53F7>\uFF1A\u628A\u7528\u6237\u79FB\u51FA\u9ED1\u540D\u5355\uFF08\u7BA1\u7406\u5458\uFF09\n\u00B7 /send <msg> [group|channel]\uFF1A\u5411\u4E92\u901A\u7684 Discord \u9891\u9053/QQ \u7FA4\u53D1\u9001\u6D88\u606F\uFF08\u4E0D\u586B\u76EE\u6807\u65F6\u53D1\u9001\u5230\u7B2C\u4E00\u4E2A\u4E92\u901A\u6761\u76EE\u7684\u5BF9\u7AEF\uFF09\n\u00B7 /pvtime\uFF1A\u67E5\u8BE2\u5F53\u524D\u662F\u6881\u6587\u5CF0\u65F6\u95F4\u8FD8\u662F\u6881\u6587\u8C37\u65F6\u95F4\n\u00B7 /reload\uFF1A\u4ECE\u78C1\u76D8\u91CD\u8F7D\u914D\u7F6E\u4E0E\u6570\u636E\u6587\u4EF6\uFF08\u7BA1\u7406\u5458\uFF09\n\u00B7 /reboot\uFF1A\u91CD\u542F\u5E94\u7528\uFF08\u7BA1\u7406\u5458\uFF09\n\u00B7 /shutdown\uFF1A\u5173\u95ED\u5E94\u7528\uFF08\u7BA1\u7406\u5458\uFF09\n\u4EE5\u4E0A\u547D\u4EE4\u5747\u53EF\u5728\u4E92\u901A\u7684 Discord \u9891\u9053\u4E2D\u4F7F\u7528\uFF08/ \u6216 ! \u524D\u7F00\uFF09\uFF0C\u56DE\u590D\u53D1\u5728 Discord \u9891\u9053\n\u5728\u542F\u7528\u7684\u7FA4/\u4E92\u901A\u9891\u9053 @\u673A\u5668\u4EBA \u6216\u547D\u4E2D\u5173\u952E\u8BCD\u5373\u53EF\u4E0E AI \u5BF9\u8BDD\uFF08\u505C\u6B62 / \u6E05\u9664\u4E0A\u4E0B\u6587 \u4E3A\u63A7\u5236\u6307\u4EE4\uFF09`);\n }\n\n public static peakValleyTimeCommand(source: CommandSource) {\n source.success(CustomBot.peakValleyTimer.getCommandMessage());\n }\n\n public static minecraftVersionCommand(source: CommandSource) {\n MinecraftAPI.getVersion().then(version => {\n source.success(\n `Minecraft \u7248\u672C\u4FE1\u606F\n\u00B7 \u6700\u65B0\u6B63\u5F0F\u7248\uFF1A${version.latest.release}\n\u00B7 \u6700\u65B0\u5FEB\u7167\u7248\uFF1A${version.latest.snapshot}`\n );\n });\n }\n\n public static serverCommand(source: CommandSource, ip: string, port: number = 25565) {\n MinecraftAPI.getMinecraftServerStatus(ip, port).then(status => {\n if (!status.online) {\n source.success('\u670D\u52A1\u5668\u5DF2\u79BB\u7EBF');\n return;\n }\n let onlinePlayers = '';\n for (const player of status.players.list) {\n if (onlinePlayers) onlinePlayers += '\\n';\n onlinePlayers += ` - ${player}`;\n }\n source.success(\n `${status.motd}\n\u00B7 \u6E38\u620F\u7248\u672C\uFF1A${status.version}\n\u00B7 \u5728\u7EBF\u73A9\u5BB6\uFF1A${status.players.online}/${status.players.max}\n\u00B7 \u5728\u7EBF\u5217\u8868\uFF1A\\n` + onlinePlayers\n );\n });\n }\n\n public static wikiCommand(source: CommandSource, query: string) {\n MinecraftAPI.getMinecraftWiki(query).then(wiki => {\n if (!wiki.success) {\n source.success('\u672A\u627E\u5230\u76F8\u5173\u7ED3\u679C\uFF01');\n return;\n }\n source.success(`${wiki.title}\n${wiki.desc}\n${wiki.url}`);\n });\n }\n\n public static githubBindCommand(source: CommandSource, username: string) {\n if (!isBotCommandSource(source)) {\n source.fail('GitHub \u7ED1\u5B9A\u4EC5\u652F\u6301\u5728\u7FA4\u804A\u6216\u4E92\u901A\u9891\u9053\u4E2D\u4F7F\u7528');\n return;\n }\n\n GitHubBindingManager.bind(source.getUserId(), username)\n .then(result => {\n if (result.state === 'bound') {\n source.success(`GitHub \u7528\u6237 ${username} \u7ED1\u5B9A\u6210\u529F`);\n return;\n }\n source.success(`\u8BF7\u5728 GitHub \u7528\u6237 ${username} \u7684 Bio \u4E2D\u6DFB\u52A0\u9A8C\u8BC1\u7801\uFF1A${result.code}\\n\u6DFB\u52A0\u540E\u518D\u6B21\u6267\u884C /github bind ${username}`);\n })\n .catch(error => {\n source.fail(error instanceof Error ? error.message : String(error));\n });\n }\n\n public static githubSubscribeCommand(source: CommandSource, repository: string) {\n if (!isBotCommandSource(source)) {\n source.fail('GitHub \u8BA2\u9605\u4EC5\u652F\u6301\u5728\u7FA4\u804A\u6216\u4E92\u901A\u9891\u9053\u4E2D\u4F7F\u7528');\n return;\n }\n if (!isValidRepositoryName(repository)) {\n source.fail(`\u4ED3\u5E93\u540D\u683C\u5F0F\u9519\u8BEF\uFF1A${repository}\\n\u5E94\u4E3A owner/repo\uFF0C\u4F8B\u5982 Anvil-Dev/AnvilCraft`);\n return;\n }\n const groupId = source.getGroupId();\n if (groupId === undefined) {\n source.fail('\u5F53\u524D\u9891\u9053\u672A\u7ED1\u5B9A\u4E92\u901A QQ \u7FA4\uFF0C\u65E0\u6CD5\u8BA2\u9605');\n return;\n }\n // \u4E0E\u6D88\u606F\u67E5\u8BE2\u540C\u4E00\u5957\u5224\u5B9A\uFF1Aowner \u5DF2\u7ED1\u5B9A GitHub \u8D26\u53F7\uFF0C\u6216\u547D\u4E2D\u5141\u8BB8\u5217\u8868\u914D\u7F6E\n let allowedText = '';\n if (!Github.isAllowedRepository(repository)) {\n if (!source.isAdmin()) {\n source.fail(\n `\u4ED3\u5E93 ${repository} \u4E0D\u5728\u5141\u8BB8\u8BBF\u95EE\u7684\u4ED3\u5E93\u5217\u8868\u4E2D\uFF0C\u65E0\u6CD5\u8BA2\u9605\\n` +\n `\u5982\u8FD9\u662F\u4F60\u81EA\u5DF1\u7684\u4ED3\u5E93\uFF0C\u53EF\u5148\u7528 /github bind <\u7528\u6237\u540D> \u7ED1\u5B9A GitHub \u8D26\u53F7\uFF0C\u6216\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0`\n );\n return;\n }\n // \u7BA1\u7406\u5458\u53EF\u81EA\u52A9\u5F00\u767D\uFF1A\u628A\u8BE5 owner \u52A0\u5165\u5141\u8BB8\u5217\u8868\u540E\u7EE7\u7EED\u8BA2\u9605\n const owner = repository.split('/')[0]!;\n addGithubAllowedRepository(owner);\n allowedText = `\u00B7 \u5DF2\u81EA\u52A8\u5C06 ${owner} \u52A0\u5165\u5141\u8BB8\u8BBF\u95EE\u7684\u4ED3\u5E93\u5217\u8868\\n`;\n }\n const subscribers = subscribeGithubRepository(repository, groupId);\n const current = subscribers.includes(groupId) ? `\u5DF2\u8BA2\u9605\u4ED3\u5E93 ${repository} \u7684\u6D88\u606F\u63A8\u9001` : `\u8BA2\u9605\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5`;\n source.success(\n `${current}\n\u00B7 \u5F53\u524D\u8BA2\u9605\u8BE5\u4ED3\u5E93\u7684\u7FA4\uFF1A${subscribers.length > 0 ? subscribers.join('\u3001') : '\uFF08\u65E0\uFF09'}\n${allowedText}\u00B7 \u82E5\u8BE5\u4ED3\u5E93\u5C1A\u672A\u914D\u7F6E webhook\uFF0C\u8BF7\u5728\u4ED3\u5E93\u9875\u9762 Settings \u2192 Webhooks \u2192 Add webhook \u6DFB\u52A0\uFF1A\n \u00B7 Payload URL\uFF1Ahttps://hook.example.com\n \u00B7 Content type\uFF1Aapplication/json\n \u00B7 Secret\uFF1A\u7559\u7A7A\n \u00B7 Which events would you like to trigger this webhook?\uFF1ASend me everything.`\n );\n }\n\n /**\n * /github allow <owner|owner/repo>\uFF1A\u628A GitHub owner \u6216\u4ED3\u5E93\u52A0\u5165\u5141\u8BB8\u8BBF\u95EE\u5217\u8868\uFF08\u7BA1\u7406\u5458\uFF09\u3002\n * \u4F20\u5165 owner \u65F6\u6388\u6743\u5176\u4E0B\u5168\u90E8\u4ED3\u5E93\uFF0C\u4F20\u5165 owner/repo \u65F6\u53EA\u6388\u6743\u8BE5\u4ED3\u5E93\u3002\n */\n /** \u7BA1\u7406\u5458\u8FD0\u7EF4\u547D\u4EE4\u7684\u516C\u5171\u6743\u9650\u6821\u9A8C\uFF08QQ \u4FA7 operators / Discord \u4FA7\u670D\u52A1\u5668\u7BA1\u7406\u5458\uFF09 */\n private static requireAdmin(source: CommandSource): boolean {\n if (!isBotCommandSource(source)) {\n source.fail('\u8BE5\u547D\u4EE4\u4EC5\u652F\u6301\u5728\u7FA4\u804A\u6216\u4E92\u901A\u9891\u9053\u4E2D\u4F7F\u7528');\n return false;\n }\n if (!source.isAdmin()) {\n source.fail('\u8BE5\u547D\u4EE4\u4EC5\u9650\u7BA1\u7406\u5458\u4F7F\u7528');\n return false;\n }\n return true;\n }\n\n /** /reload\uFF1A\u4ECE\u78C1\u76D8\u91CD\u8F7D\u914D\u7F6E\u4E0E\u6570\u636E\u6587\u4EF6 */\n public static reloadCommand(source: CommandSource) {\n if (!CustomBot.requireAdmin(source)) return;\n reloadRuntime({\n eventData: () => EventDataManager.reload(),\n githubBindings: () => GitHubBindingManager.reload(),\n extra: [{ name: '\u5CF0\u8C37\u65F6\u6BB5\u8BA1\u65F6\u5668', run: () => CustomBot.peakValleyTimer.reloadConfig() }]\n })\n .then(report => {\n const lines = [\n '\u267B\uFE0F \u914D\u7F6E\u4E0E\u6570\u636E\u5DF2\u4ECE\u78C1\u76D8\u91CD\u8F7D',\n `\u00B7 \u914D\u7F6E\u6587\u4EF6\uFF1A${report.configStores} \u4E2A`,\n `\u00B7 \u4E8B\u4EF6\u6570\u636E\uFF08\u9ED1\u540D\u5355/\u7248\u672C\u8BB0\u5F55\uFF09\uFF1A${report.eventData ? '\u5DF2\u91CD\u8F7D' : '\u5931\u8D25'}`,\n `\u00B7 GitHub \u7ED1\u5B9A\uFF1A${report.githubBindings ? '\u5DF2\u91CD\u8F7D' : '\u5931\u8D25'}`\n ];\n if (report.extras.length > 0) lines.push(`\u00B7 \u5176\u4ED6\u6A21\u5757\uFF1A${report.extras.join('\u3001')}`);\n lines.push('\u00B7 bot-config.json \u4E2D\u7684\u7AEF\u53E3/Token \u9700\u91CD\u542F\u624D\u751F\u6548\uFF08/reboot\uFF09');\n source.success(lines.join('\\n'));\n })\n .catch(error => {\n source.fail(`\u91CD\u8F7D\u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}`);\n });\n }\n\n /**\n * /reboot\uFF1A\u91CD\u542F\u5E94\u7528\uFF08\u62C9\u8D77\u65B0\u8FDB\u7A0B \u2192 \u5065\u5EB7\u68C0\u67E5\u901A\u8FC7\u540E\u624D\u9000\u51FA\u5F53\u524D\u8FDB\u7A0B\uFF09\u3002\n *\n * \u5173\u952E\uFF1A\u5065\u5EB7\u68C0\u67E5\u5931\u8D25\u65F6**\u4E0D\u9000\u51FA**\u5F53\u524D\u8FDB\u7A0B\uFF08\u673A\u5668\u4EBA\u4FDD\u6301\u5728\u7EBF\uFF09\uFF0C\u5E76\u5411\u89E6\u53D1\u8005\u56DE\u62A5\u5931\u8D25\u539F\u56E0\u3002\n * \u65E9\u671F\u5B9E\u73B0 spawn \u540E\u65E0\u6761\u4EF6 exit\uFF0C\u4E00\u65E6\u65B0\u8FDB\u7A0B\u542F\u52A8\u5931\u8D25\uFF08\u5982\u4E22\u5931 tsx \u52A0\u8F7D\u5668\u5BFC\u81F4\u522B\u540D\u89E3\u6790\u5931\u8D25\uFF09\n * \u5C31\u4F1A\u8BA9\u673A\u5668\u4EBA\u76F4\u63A5\u79BB\u7EBF\u3002\n */\n public static async rebootCommand(source: CommandSource) {\n if (!CustomBot.requireAdmin(source)) return;\n if (isRestartInProgress()) {\n source.fail('\u5DF2\u6709\u91CD\u542F\u6B63\u5728\u8FDB\u884C\u4E2D\uFF0C\u8BF7\u7A0D\u5019');\n return;\n }\n source.success('\uD83D\uDD04 \u6B63\u5728\u91CD\u542F\u5E94\u7528\uFF0C\u8BF7\u7A0D\u5019\u2026');\n // \u5148\u8BA9\u786E\u8BA4\u6D88\u606F\u53D1\u51FA\u53BB\uFF0C\u518D\u6267\u884C\u91CD\u542F\uFF08restartApplication \u5185\u90E8\u542B\u5065\u5EB7\u68C0\u67E5\u4E0E\u9000\u51FA\u7F13\u51B2\uFF09\n await new Promise(resolve => setTimeout(resolve, CustomBot.REBOOT_CONFIRM_DELAY_MS));\n try {\n process.send?.({ type: 'restart' }); return; const result = await restartApplication();\n if (!result.alive) {\n const detail = result.logTail ? `\\n${result.logTail.split('\\n').slice(-6).join('\\n')}` : '';\n bot.logger?.error(`[Admin] /reboot \u5931\u8D25\uFF1A\u65B0\u8FDB\u7A0B ${result.pid} \u5DF2\u9000\u51FA\uFF0C\u5F53\u524D\u8FDB\u7A0B\u7EE7\u7EED\u8FD0\u884C${detail}`);\n source.fail(`\u91CD\u542F\u5931\u8D25\uFF08\u5F53\u524D\u8FDB\u7A0B\u7EE7\u7EED\u8FD0\u884C\uFF09\uFF1A\u65B0\u8FDB\u7A0B\u542F\u52A8\u540E\u7ACB\u5373\u9000\u51FA${detail}`);\n return;\n }\n bot.logger?.info(`[Admin] /reboot \u7531 ${source.getName()} \u89E6\u53D1\uFF0C\u65B0\u8FDB\u7A0B pid=${result.pid}\uFF0C\u5F53\u524D\u8FDB\u7A0B\u5373\u5C06\u9000\u51FA`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n bot.logger?.error(`[Admin] /reboot \u5931\u8D25: ${message}`);\n source.fail(`\u91CD\u542F\u5931\u8D25\uFF08\u5F53\u524D\u8FDB\u7A0B\u7EE7\u7EED\u8FD0\u884C\uFF09\uFF1A${message}`);\n }\n }\n\n /** /shutdown\uFF1A\u5173\u95ED\u5E94\u7528 */\n public static shutdownCommand(source: CommandSource) {\n if (!CustomBot.requireAdmin(source)) return;\n source.success('\uD83D\uDED1 \u6B63\u5728\u5173\u95ED\u5E94\u7528\u2026');\n bot.logger?.warn(`[Admin] /shutdown \u7531 ${source.getName()} \u89E6\u53D1`);\n setTimeout(() => shutdownApplication(() => bot.stop()), CustomBot.REBOOT_CONFIRM_DELAY_MS);\n }\n\n public static githubAllowCommand(source: CommandSource, target: string) {\n if (!isBotCommandSource(source)) {\n source.fail('GitHub allow \u4EC5\u652F\u6301\u5728\u7FA4\u804A\u6216\u4E92\u901A\u9891\u9053\u4E2D\u4F7F\u7528');\n return;\n }\n if (!source.isAdmin()) {\n source.fail('\u8BE5\u547D\u4EE4\u4EC5\u9650\u7BA1\u7406\u5458\u4F7F\u7528');\n return;\n }\n const repositoryTarget = target.includes('/');\n if (repositoryTarget ? !isValidRepositoryName(target) : !isValidRepositoryOwner(target)) {\n source.fail(\n `\u76EE\u6807\u683C\u5F0F\u9519\u8BEF\uFF1A${target}\\n` +\n `\u5E94\u4E3A owner\uFF08\u6388\u6743\u5176\u4E0B\u5168\u90E8\u4ED3\u5E93\uFF0C\u5982 Anvil-Dev\uFF09\u6216 owner/repo\uFF08\u4EC5\u6388\u6743\u8BE5\u4ED3\u5E93\uFF0C\u5982 Anvil-Dev/AnvilCraft\uFF09`\n );\n return;\n }\n const existed = isGithubAllowedPattern(target);\n const pattern = addGithubAllowedRepository(target);\n if (existed) {\n source.success(`${pattern} \u5DF2\u5728\u5141\u8BB8\u8BBF\u95EE\u7684\u4ED3\u5E93\u5217\u8868\u4E2D`);\n return;\n }\n source.success(\n repositoryTarget\n ? `\u5DF2\u5C06\u4ED3\u5E93 ${pattern} \u52A0\u5165\u5141\u8BB8\u8BBF\u95EE\u7684\u4ED3\u5E93\u5217\u8868`\n : `\u5DF2\u5C06 ${pattern} \u52A0\u5165\u5141\u8BB8\u8BBF\u95EE\u7684\u4ED3\u5E93\u5217\u8868\uFF0C\u5176\u4E0B\u5168\u90E8\u4ED3\u5E93\u5747\u53EF\u8BBF\u95EE`\n );\n }\n\n public static pardonCommand(source: CommandSource, userId: string) {\n if (!isBotCommandSource(source)) {\n source.fail('\u53EA\u652F\u6301\u5728\u7FA4\u804A\u6216\u4E92\u901A\u9891\u9053\u4E2D\u4F7F\u7528');\n return;\n }\n // \u547D\u4EE4\u5C42\u5148\u62E6\u4E00\u9053\uFF1A\u975E\u7BA1\u7406\u5458\u76F4\u63A5\u62D2\u7EDD\uFF08QQ = operators \u767D\u540D\u5355\uFF1BDiscord = \u670D\u52A1\u5668\u7BA1\u7406\u5458\u6216 dc \u767D\u540D\u5355\uFF09\n if (!source.isAdmin()) {\n source.fail('\u8BE5\u547D\u4EE4\u4EC5\u9650\u7BA1\u7406\u5458\u4F7F\u7528');\n return;\n }\n const groupId = source.getGroupId();\n if (groupId === undefined) {\n source.fail('\u5F53\u524D\u9891\u9053\u672A\u7ED1\u5B9A\u4E92\u901A QQ \u7FA4\uFF0C\u65E0\u6CD5\u8D66\u514D');\n return;\n }\n const qq = Number(userId);\n if (!Number.isSafeInteger(qq) || qq <= 0) {\n source.fail(`QQ \u53F7\u683C\u5F0F\u9519\u8BEF\uFF1A${userId}`);\n return;\n }\n // \u547D\u4EE4\u6E90\u7ECF FailOnceCommandSource \u4EE3\u7406\u540E\u4E0D\u518D\u662F GroupMsgCommandSource \u5B9E\u4F8B\uFF0C\n // \u56E0\u6B64\u4E0D\u80FD\u7528 instanceof \u533A\u5206\u5E73\u53F0\uFF08\u5426\u5219 QQ \u7BA1\u7406\u5458\u4F1A\u88AB\u5F53\u6210 Discord \u7528\u6237\u5224\u5931\u8D25\uFF09\u3002\n // \u6539\u4E3A\u6309 BotCommandSource \u7684 id \u7EA6\u5B9A\u63A8\u5BFC\uFF08\u7EAF\u6570\u5B57=QQ \u53F7\uFF0Cdc: \u524D\u7F00=Discord \u7528\u6237\uFF09\u3002\n const operatorId = resolveOperatorId(source);\n Management.pardon(groupId, operatorId, qq)\n .then(removed => {\n if (removed) {\n source.success(`\u5DF2\u4ECE\u9ED1\u540D\u5355\u4E2D\u79FB\u9664\u7528\u6237 ${qq}`);\n } else {\n source.fail('\u7528\u6237\u4E0D\u5728\u9ED1\u540D\u5355\u4E2D\uFF0C\u6216\u4F60\u6CA1\u6709\u8D66\u514D\u6743\u9650');\n }\n })\n .catch(error => {\n source.fail(error instanceof Error ? error.message : String(error));\n });\n }\n\n @bot.subscribe('notice-event-notify', false)\n public listenPokeMsg(bot: QQBot, msg: NotifyNoticeWSMSG): void {\n if (!msg.sub_type || msg.sub_type != 'poke') return;\n const sentMessage: Message[] = [\n {\n type: 'at',\n data: {\n qq: msg.user_id\n }\n },\n {\n type: 'text',\n data: {\n text: ' '\n }\n }\n ];\n Poke.processPokeMsg(bot, msg, sentMessage);\n if (sentMessage.length > 2) {\n if (msg.group_id) {\n bot.sendGroupMsg(msg.group_id, sentMessage);\n } else {\n bot.sendPrivateMsg(msg.user_id, sentMessage);\n }\n }\n }\n\n @bot.subscribe('message-event-group', false)\n public listenGroupMsg(bot: QQBot, msg: GroupMessageWSMSG): void {\n const sentMessage: Message[] = [\n {\n type: 'reply',\n data: {\n id: msg.message_id\n }\n }\n ];\n ParenthesesMatching.parenthesesMatching(msg, sentMessage);\n // \u5404\u5904\u7406\u5668\u5931\u8D25\u53EA\u8BB0\u5F55\u65E5\u5FD7\uFF1A\u65E2\u4E0D\u80FD\u4EA7\u751F\u672A\u5904\u7406\u7684 rejection\uFF08\u4F1A\u7EC8\u6B62\u8FDB\u7A0B\uFF09\uFF0C\n // \u4E5F\u4E0D\u80FD\u4E2D\u65AD\u540E\u7EED\u5904\u7406\u5668\u4E0E\u6700\u7EC8\u7684\u6D88\u606F\u53D1\u9001\n Github.processMessage(bot, msg, sentMessage)\n .catch(e => {\n bot.logger?.error(`Github message process failed: ${e?.message ?? e}`);\n })\n .then(() => Bili.processMessage(bot, msg, sentMessage))\n .catch(e => {\n bot.logger?.error(`Bili message process failed: ${e?.message ?? e}`);\n })\n .then(() => {\n if (sentMessage.length > 1) {\n bot.sendGroupMsg(msg.group_id, sentMessage);\n }\n });\n }\n\n @bot.subscribe('message-event-group', false)\n public listenDiscordBridgeMsg(bot: QQBot, msg: GroupMessageWSMSG): void {\n // QQ \u21C4 Discord \u4E92\u901A\uFF1A\u8F6C\u53D1\u4E0E /send \u5206\u53D1\uFF08\u4E0D\u4F9D\u8D56 command \u767D\u540D\u5355\uFF0C\u5185\u90E8\u81EA\u884C\u95E8\u63A7\uFF09\n DiscordBridge.getInstance().handleQQMessage(bot, msg);\n }\n\n @bot.subscribe('message-event-group', false)\n public listenHermesGroupMsg(bot: QQBot, msg: GroupMessageWSMSG): void {\n // QQ \u21C4 Hermes Agent \u6865\u63A5\uFF1A@\u63D0\u53CA/\u5173\u952E\u8BCD\u89E6\u53D1 AI \u5BF9\u8BDD\uFF08\u5185\u90E8\u81EA\u884C\u95E8\u63A7\uFF1Ahermes.json groups\uFF09\n HermesBridge.getInstance().handleGroupMessage(bot, msg);\n }\n\n @bot.subscribe('message-event-private', false)\n public listenHermesPrivateMsg(bot: QQBot, msg: PrivateMessageWSMSG): void {\n // Hermes \u79C1\u804A\u59CB\u7EC8\u53EF\u89E6\u53D1\uFF08\u53D7 hermes.json \u7528\u6237\u9ED1\u767D\u540D\u5355\u7EA6\u675F\uFF09\n HermesBridge.getInstance().handlePrivateMessage(bot, msg);\n }\n\n @bot.subscribe('notice-event-group-decrease', false)\n public listenGroupDecreaseNotice(bot: QQBot, msg: GroupDecreaseNoticeWSMSG): void {\n Management.handleGroupDecreaseNotice(bot, msg);\n }\n\n @bot.subscribe('notice-event-group-increase', false)\n public listenGroupIncreaseNotice(bot: QQBot, msg: GroupIncreaseNoticeWSMSG): void {\n Management.handleGroupIncreaseNotice(bot, msg);\n Welcome.handleGroupIncreaseNotice(bot, msg);\n }\n\n @bot.subscribe('request-event-group', false)\n public listenGroupRequest(bot: QQBot, msg: GroupRequestWSMSG): void {\n Management.handleGroupRequest(bot, msg);\n }\n\n @bot.subscribe('command-register', false)\n public cmd(bot: QQBot, command: CommandManager) {\n command.register('gugle-command', CommandManager.literal('help').execute(CustomBot.helpCommand));\n command.register('gugle-command', CommandManager.literal('mcv').execute(CustomBot.minecraftVersionCommand));\n command.register(\n 'gugle-command',\n CommandManager.literal('server').then(\n CommandManager.argument('ip', Arguments.STRING)\n .execute(CustomBot.serverCommand)\n .then(CommandManager.argument('port', Arguments.NUMBER).execute(CustomBot.serverCommand))\n )\n );\n command.register(\n 'gugle-command',\n CommandManager.literal('wiki').then(\n CommandManager.argument('query', Arguments.STRING).execute(CustomBot.wikiCommand)\n )\n );\n command.register(\n 'gugle-command',\n CommandManager.literal('github')\n .then(\n CommandManager.literal('bind').then(\n CommandManager.argument('username', Arguments.STRING).execute(CustomBot.githubBindCommand)\n )\n )\n .then(\n CommandManager.literal('subscribe').then(\n CommandManager.argument('repository', Arguments.STRING).execute(CustomBot.githubSubscribeCommand)\n )\n )\n .then(\n CommandManager.literal('allow').then(\n CommandManager.argument('target', Arguments.STRING).execute(CustomBot.githubAllowCommand)\n )\n )\n );\n command.register(\n 'gugle-command',\n CommandManager.literal('pardon').then(\n CommandManager.argument('userId', Arguments.STRING).execute(CustomBot.pardonCommand)\n )\n );\n command.register('gugle-command', CommandManager.literal('pvtime').execute(CustomBot.peakValleyTimeCommand));\n // \u7BA1\u7406\u5458\u8FD0\u7EF4\u547D\u4EE4\uFF1A\u6743\u9650\u5728\u547D\u4EE4\u5B9E\u73B0\u5185\u6821\u9A8C\uFF08QQ operators / Discord \u670D\u52A1\u5668\u7BA1\u7406\u5458\uFF09\n command.register('gugle-command', CommandManager.literal('reboot').execute(CustomBot.rebootCommand));\n command.register('gugle-command', CommandManager.literal('reload').execute(CustomBot.reloadCommand));\n command.register('gugle-command', CommandManager.literal('shutdown').execute(CustomBot.shutdownCommand));\n }\n\n @bot.subscribe('after-start', false)\n public startPeakValleyTimer(bot: QQBot): void {\n if (isRestartedByCommand()) {\n bot.logger?.info('[Admin] \u672C\u8FDB\u7A0B\u7531 /reboot \u62C9\u8D77\uFF08\u4E0A\u4E00\u8FDB\u7A0B\u5DF2\u9000\u51FA\uFF09');\n }\n CustomBot.peakValleyTimer.start(bot);\n DiscordBridge.getInstance().start(bot);\n // \u6E05\u7406\u4E0A\u6B21\u8FDB\u7A0B\u5728 AI \u5904\u7406\u4E2D\u9000\u51FA\u65F6\u6B8B\u7559\u7684 QQ \u8868\u60C5\u56DE\u5E94\uFF08Discord \u6B8B\u7559\u5728 clientReady \u540E\u6E05\u7406\uFF09\n HermesBridge.getInstance().cleanupStaleQQReactions(bot).catch(error => {\n bot.logger?.error(`[Hermes] \u6B8B\u7559\u8868\u60C5\u56DE\u5E94\u6E05\u7406\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`);\n });\n }\n\n @bot.cron('*/30 * * * * *')\n public cronCheckMinecraftVersion() {\n MinecraftAPI.getVersion().then(version => {\n if (!version.success) return;\n // \u4ECE manifest \u4E2D\u6309 id \u67E5\u627E release / snapshot \u7684\u6743\u5A01 releaseTime\uFF0C\n // \u7528\u65F6\u95F4\u6233\u5355\u8C03\u6027\u5224\u65AD\u65B0\u53D1\u5E03\uFF0C\u907F\u514D CDN \u7F13\u5B58\u6296\u52A8\u91CD\u590D\u53D1\u9001\n const releaseId = version.latest.release;\n const snapshotId = version.latest.snapshot;\n const releaseTime = version.versions.find(v => v.id === releaseId)?.releaseTime || '';\n const snapshotTime = version.versions.find(v => v.id === snapshotId)?.releaseTime || '';\n Promise.all([\n checkReleasedVersion('mcupdate:release', releaseId, Date.parse(releaseTime) || 0),\n checkReleasedVersion('mcupdate:snapshot', snapshotId, Date.parse(snapshotTime) || 0)\n ]).then(([releaseNew, snapshotNew]) => {\n const msg: Message[] = [\n {\n type: 'text',\n data: {\n text: '\u53D1\u73B0\u65B0\u7248\u672C\uFF01\\n'\n }\n }\n ];\n if (releaseNew) {\n msg.push({\n type: 'text',\n data: {\n text: `\u00B7 \u6700\u65B0\u6B63\u5F0F\u7248\uFF1A${version.latest.release}\\n`\n }\n });\n msg.push({\n type: 'text',\n data: {\n text: `https://www.minecraft.net/en-us/article/minecraft-java-edition-${version.latest.release.replace('.', '-')}`\n }\n });\n }\n if (snapshotNew) {\n msg.push({\n type: 'text',\n data: {\n text: `\u00B7 \u6700\u65B0\u5FEB\u7167\u7248\uFF1A${version.latest.snapshot}\\n`\n }\n });\n let snapshot = version.latest.snapshot;\n if (snapshot.includes('pre')) {\n snapshot = snapshot.replace('pre', 'pre-release');\n } else if (snapshot.includes('rc')) {\n snapshot = snapshot.replace('rc', 'release-candidate');\n }\n snapshot = snapshot.replace('.', '-');\n msg.push({\n type: 'text',\n data: {\n text: `https://www.minecraft.net/en-us/article/minecraft-${snapshot}`\n }\n });\n }\n // \u53EA\u6709 release \u6216 snapshot \u4EFB\u4E00\u88AB\u786E\u8BA4\u4E3A\u65B0\u53D1\u5E03\u624D\u53D1\u9001\n if (!releaseNew && !snapshotNew) return;\n for (const listener of getFeatureGroups('minecraft')) {\n bot.sendGroupMsg(listener, msg);\n }\n });\n });\n }\n\n // @bot.cron('0/30 * * * * *')\n // public cronCheckAeronauticsVersion() {\n // ModrinthAPI.checkVersion(bot, 'create-aeronautics', '\u822A\u7A7A\u5B66');\n // }\n}\n\nexport default function run() {\n return new CustomBot();\n}\n", "import process from 'node:process';\nimport { Logger } from 'winston';\nimport { RawData, WebSocket } from 'ws';\nimport { botConfig, BotConfig } from '@/config';\nimport { getFeatureGroups, isOperator } from '@/config/features';\nimport fs from 'node:fs';\nimport dayjs from 'dayjs';\nimport { LoggerFactory } from '@/logger';\nimport { EventManager } from 'gugle-event';\nimport { GroupMessageWSMSG, LoginInfo, LoginInfoData, Message, NodeMessage, SentMessage, WSMSG } from '@/type';\nimport axios, { AxiosInstance } from 'axios';\nimport { Github } from '@/features/github';\nimport * as cron from 'node-cron';\nimport { BotEvent, BotEventCancelable, EventCallback } from '@/type/event';\nimport { CommandManager, CommandSource } from 'gugle-command';\nimport { BotCommandSource, executeCommand } from '@/command';\nimport { restartStartDelayMs } from '@/features/admin';\n\n/** bot.sendGroupMsg \u7684\u9644\u52A0\u9009\u9879 */\nexport interface SendGroupMsgOptions {\n /**\n * \u6807\u8BB0\u8BE5\u6D88\u606F\u7531\u4E92\u901A\u6865\u81EA\u8EAB\u53D1\u51FA\uFF08Discord\u2192QQ \u8F6C\u53D1\u3001/send \u7ED3\u679C\u7B49\uFF09\u3002\n * \u53D1\u9001\u51FA\u53E3\u76D1\u542C\u5668\u636E\u6B64\u8DF3\u8FC7\uFF0C\u907F\u514D\u300C\u8F6C\u53D1\u51FA\u53BB\u7684\u6D88\u606F\u53C8\u88AB\u8F6C\u53D1\u56DE Discord\u300D\u5F62\u6210\u56DE\u73AF\u3002\n */\n fromBridge?: boolean;\n /**\n * \u6807\u8BB0\u8BE5\u6D88\u606F\u7531 webhook \u4E8B\u4EF6\u63A8\u9001\uFF08GitHub issue/PR/release \u8BA2\u9605\u901A\u77E5\uFF09\u3002\n * \u4E92\u901A\u6865\u636E\u6B64\u5C0A\u91CD\u9891\u9053\u95E8\u63A7\uFF1A\u4E0D\u8F6C\u53D1\u5230 need_cmd / need_reply \u4E3A true \u7684\u9891\u9053\n * \uFF08\u547D\u4EE4\u56DE\u590D\u3001AI \u5BF9\u8BDD\u7B49\u4EA4\u4E92\u5F0F\u673A\u5668\u4EBA\u6D88\u606F\u4E0D\u53D7\u6B64\u9650\uFF09\u3002\n */\n fromWebhook?: boolean;\n}\n\n/** bot.sendGroupForwardMsg \u7684\u5408\u5E76\u8F6C\u53D1\u8282\u70B9 */\nexport interface ForwardNode {\n /** \u8282\u70B9\u7F72\u540D\uFF08\u7F3A\u7701\u7528 bot \u6635\u79F0/QQ \u53F7\u7531\u8C03\u7528\u65B9\u51B3\u5B9A\uFF09 */\n name?: string;\n uin?: string | number;\n /** \u8282\u70B9\u5185\u5BB9\uFF1A\u7EAF\u6587\u672C\u6216\u6D88\u606F\u6BB5\u6570\u7EC4 */\n content: string | Message[];\n}\n\n/** bot.sendGroupMsg \u6210\u529F\u540E\u7684\u901A\u77E5\u8D1F\u8F7D\uFF08\u4F9B\u4E92\u901A\u6865\u628A\u673A\u5668\u4EBA\u81EA\u8EAB\u6D88\u606F\u8F6C\u53D1\u5230 Discord\uFF09 */\nexport interface GroupMessageSentEvent {\n groupId: number;\n message: SentMessage;\n /** OneBot \u8FD4\u56DE\u7684\u6D88\u606F id\uFF1B\u5931\u8D25\u65F6\u4E3A undefined */\n messageId: number | undefined;\n /** \u662F\u5426\u7531\u4E92\u901A\u6865\u81EA\u8EAB\u53D1\u51FA\uFF08true \u65F6\u76D1\u542C\u65B9\u5E94\u5FFD\u7565\uFF09 */\n fromBridge: boolean;\n /** \u662F\u5426\u7531 webhook \u4E8B\u4EF6\u63A8\u9001\uFF08true \u65F6\u4E92\u901A\u6865\u5C0A\u91CD\u9891\u9053\u95E8\u63A7\uFF09 */\n fromWebhook: boolean;\n}\n\nexport class GroupMsgCommandSource implements BotCommandSource {\n public readonly bot: QQBot;\n public readonly msg: GroupMessageWSMSG;\n private readonly replayMsg: Message[];\n\n public constructor(bot: QQBot, msg: GroupMessageWSMSG) {\n this.bot = bot;\n this.msg = msg;\n this.replayMsg = [\n {\n type: 'reply',\n data: {\n id: this.msg.message_id\n }\n }\n ];\n }\n\n public getUserId(): string {\n return String(this.msg.sender.user_id);\n }\n\n public getGroupId(): number | undefined {\n return this.msg.group_id;\n }\n\n public isAdmin(): boolean {\n return isOperator(this.msg.sender.user_id);\n }\n\n public success(message: string): void {\n bot.sendGroupMsg(this.msg.group_id, [\n ...this.replayMsg,\n {\n type: 'text',\n data: {\n text: message\n }\n }\n ]);\n }\n\n public fail(message: string): void {\n bot.sendGroupMsg(this.msg.group_id, [\n ...this.replayMsg,\n {\n type: 'text',\n data: {\n text: message\n }\n }\n ]);\n bot.logger?.error(`[${this.msg.sender.nickname}|${this.msg.sender.user_id}] ${message}: ${this.msg.raw_message}`);\n }\n\n public getName(): string {\n return this.msg.sender.nickname;\n }\n\n public hasPermission(permission: string): boolean {\n if (!permission) return true;\n const getPermissionLevel = (text: string): number => {\n const level = Number.parseInt(text);\n if (Number.isNaN(level)) {\n return text === 'owner' ? 2 : text === 'admin' ? 1 : 0;\n }\n return Number.parseInt(text);\n };\n const permissionLevel: number = getPermissionLevel(this.msg.sender.role);\n const needPermissionLevel: number = getPermissionLevel(permission);\n bot.logger?.debug(\n `permission: ${permission}, role: ${this.msg.sender.role}, permissionLevel: ${permissionLevel}, needPermissionLevel: ${needPermissionLevel}, ${permissionLevel >= needPermissionLevel}`\n );\n return permissionLevel >= needPermissionLevel;\n }\n}\n\nexport class QQBot {\n /** \u65AD\u7EBF\u540E\u7684\u6700\u5927\u91CD\u8FDE\u6B21\u6570 */\n private static readonly MAX_RECONNECT_ATTEMPTS = 10;\n /** \u91CD\u8FDE\u57FA\u7840\u9000\u907F\u65F6\u957F\uFF08\u6BEB\u79D2\uFF09\uFF1A\u7B2C n \u6B21\u91CD\u8BD5\u7B49\u5F85 BASE * 2^(n-1) */\n private static readonly RECONNECT_BASE_DELAY_MS = 2000;\n /** \u5355\u6B21\u9000\u907F\u4E0A\u9650\uFF08\u6BEB\u79D2\uFF09\uFF1A\u6307\u6570\u589E\u957F\u5230\u6B64\u503C\u540E\u4E0D\u518D\u589E\u5927\uFF0C\u907F\u514D\u7B49\u5F85\u65F6\u95F4\u5931\u63A7 */\n private static readonly RECONNECT_MAX_DELAY_MS = 60000;\n\n private loginInfo?: LoginInfoData = undefined;\n private path: string = process.cwd();\n logger?: Logger;\n private readonly config: BotConfig;\n public readonly axiosInstance: AxiosInstance;\n private ws?: WebSocket;\n private readonly eventManager: EventManager;\n private readonly commandManager: CommandManager;\n private wsOpened: boolean = false;\n private lastHeartbeatTime: number = 0;\n private checkHeartbeatFunc?: NodeJS.Timeout = undefined;\n private reconnectFunc?: NodeJS.Timeout = undefined;\n private reconnectAttempts: number = 0;\n private stopping: boolean = false;\n private operationQueue: (() => void)[] = [];\n private lastOperationHandle = -1;\n /** bot.sendGroupMsg \u6210\u529F\u540E\u7684\u76D1\u542C\u5668\uFF08\u4E92\u901A\u6865\u7528\u5B83\u8F6C\u53D1\u673A\u5668\u4EBA\u81EA\u8EAB\u6D88\u606F\u5230 Discord\uFF09 */\n private groupMessageSentListeners: ((event: GroupMessageSentEvent) => void)[] = [];\n\n public constructor(config: BotConfig) {\n this.config = config;\n this.eventManager = new EventManager();\n this.commandManager = new CommandManager();\n this.axiosInstance = axios.create({\n timeout: 15000,\n baseURL: config.httpUrl,\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.httpToken}`,\n 'User-Agent': config.userAgent\n }\n });\n setInterval(() => this.handlerOperationQueue(this), 2000);\n }\n\n /**\n * \u5EFA\u7ACB WebSocket \u8FDE\u63A5\u5E76\u6CE8\u518C\u4E8B\u4EF6\u5904\u7406\u3002\n * \u8FDE\u63A5\u5931\u8D25\u6216\u65AD\u5F00\u65F6\u7531 close \u4E8B\u4EF6\u7EDF\u4E00\u89E6\u53D1\u9000\u907F\u91CD\u8FDE\uFF0C\u56E0\u6B64\u8FD9\u91CC\u53EF\u4EE5\u5B89\u5168\u5730\u91CD\u590D\u8C03\u7528\u3002\n */\n private connect(): void {\n const bot = this;\n bot.ws = new WebSocket(`${bot.config.wsUrl}/${bot.config.tokenParams}${bot.config.wsToken}`);\n bot.ws.on('open', () => {\n bot.wsOpened = true;\n bot.lastHeartbeatTime = Date.now();\n bot.logger?.info('WebSocket connected');\n bot.checkHeartbeatFunc = setTimeout(() => bot.checkHeartbeat(bot), 40000);\n bot\n .getLoginInfo()\n .then(info => {\n bot.loginInfo = info;\n })\n .catch((error: Error) => {\n bot.logger?.error(`\u83B7\u53D6\u767B\u5F55\u4FE1\u606F\u5931\u8D25\uFF1A${error.message}`);\n });\n });\n bot.ws.on('message', rawData => {\n bot.post('websocket-message', bot, rawData).then();\n });\n bot.ws.on('error', (error: Error) => {\n if (error.message.endsWith('401')) {\n bot.logger?.error(`\u65E0\u6CD5\u8FDE\u63A5\u81F3 WebSocket \u670D\u52A1\u5668\uFF0C\u8BF7\u68C0\u67E5\u4F60\u7684 Token\uFF01${error.stack}`);\n return;\n }\n bot.logger?.error(`WebSocket error: ${error.stack ?? error.message}`);\n });\n bot.ws.on('close', (code: number, reason: Buffer) => {\n bot.wsOpened = false;\n clearTimeout(bot.checkHeartbeatFunc);\n bot.logger?.warn(`WebSocket closed: ${code} ${reason.toString('utf-8')}`);\n if (bot.stopping) return;\n bot.scheduleReconnect();\n });\n }\n\n /** \u6309\u6307\u6570\u9000\u907F\u5B89\u6392\u4E0B\u4E00\u6B21\u91CD\u8FDE\uFF1B\u8FBE\u5230\u4E0A\u9650\u540E\u8BB0\u5F55\u9519\u8BEF\u5E76\u505C\u6B62\u91CD\u8FDE */\n private scheduleReconnect(): void {\n const bot = this;\n if (bot.reconnectAttempts >= QQBot.MAX_RECONNECT_ATTEMPTS) {\n bot.logger?.error(`WebSocket \u91CD\u8FDE\u5DF2\u8FBE\u4E0A\u9650\uFF08${QQBot.MAX_RECONNECT_ATTEMPTS} \u6B21\uFF09\uFF0C\u505C\u6B62\u91CD\u8FDE`);\n return;\n }\n bot.reconnectAttempts++;\n const delay = Math.min(\n QQBot.RECONNECT_BASE_DELAY_MS * 2 ** (bot.reconnectAttempts - 1),\n QQBot.RECONNECT_MAX_DELAY_MS\n );\n bot.logger?.warn(\n `WebSocket \u8FDE\u63A5\u5DF2\u65AD\u5F00\uFF0C${delay}ms \u540E\u53D1\u8D77\u7B2C ${bot.reconnectAttempts}/${QQBot.MAX_RECONNECT_ATTEMPTS} \u6B21\u91CD\u8FDE`\n );\n bot.reconnectFunc = setTimeout(() => bot.connect(), delay);\n }\n\n public async start(path: string = process.cwd()): Promise<QQBot> {\n const bot = this;\n return new Promise(resolve => {\n bot.post('before-start', bot, path).then(args => {\n path = args[1];\n bot.path = path;\n const logPath = `${bot.path}/logs`;\n if (!fs.existsSync(logPath)) fs.mkdirSync(logPath);\n if (fs.existsSync(`${logPath}/latest.log`)) {\n let logName = `${logPath}/${dayjs().format('YYYY-MM-DD-HH-mm-ss')}.log`;\n let count = 0;\n while (fs.existsSync(logName)) {\n count++;\n logName = `${logPath}/${dayjs().format('YYYY-MM-DD-HH-mm-ss')}-${count}.log`;\n }\n fs.renameSync(`${logPath}/latest.log`, logName);\n }\n bot.logger = LoggerFactory.createLogger('QQBot', logPath, bot.config.logLevel || 'info');\n bot.logger.info(`QQ Bot starting...`);\n bot.listen('websocket-message', bot.onWebsocketMsg);\n bot.listen('meta-event-heartbeat', bot.onHeartbeat);\n bot.listen('message-event-group', bot.onGroupMsg);\n bot.connect();\n bot.post('command-register', bot, this.commandManager).then();\n bot.post('after-start', bot).then();\n resolve(bot);\n }).catch((error: Error) => {\n bot.logger?.error(`\u542F\u52A8\u6D41\u7A0B\u5931\u8D25\uFF1A${error.stack ?? error.message}`);\n resolve(bot);\n });\n });\n }\n\n public stop(): QQBot {\n this.stopping = true;\n clearTimeout(this.reconnectFunc);\n clearTimeout(this.checkHeartbeatFunc);\n // \u5728\u505C\u6B62\u4E4B\u524D\u89E6\u53D1'before-stop'\u4E8B\u4EF6\uFF0C\u4F20\u9012\u5F53\u524D\u5B9E\u4F8B\n this.post('before-stop', this).then(() => {\n // \u5982\u679CWebSocket\u8FDE\u63A5\u662F\u6253\u5F00\u7684\u72B6\u6001\uFF0C\u5173\u95ED\u8FDE\u63A5\n if (this.wsOpened) this.ws?.close();\n // \u5728\u505C\u6B62\u4E4B\u540E\u89E6\u53D1'after-stop'\u4E8B\u4EF6\uFF0C\u4F20\u9012\u5F53\u524D\u5B9E\u4F8B\n this.post('after-stop', this).then();\n });\n return this;\n }\n\n public async post(event: BotEvent, ...args: any): Promise<any[]> {\n return await this.eventManager.post(event, ...args);\n }\n\n /**\n * \u5FC3\u8DF3\u8D85\u65F6\u68C0\u6D4B\uFF1A\u8D85\u8FC7 100 \u79D2\u672A\u6536\u5230\u5FC3\u8DF3\u89C6\u4E3A\u8FDE\u63A5\u5DF2\u65AD\u5F00\uFF0C\u4E3B\u52A8\u5173\u95ED\u4EE5\u89E6\u53D1\u91CD\u8FDE\u3002\n * lastHeartbeatTime \u8BB0\u5F55\u7684\u662F\u300C\u672C\u6B21\u8FDE\u63A5\u5F00\u59CB\u7B49\u5F85\u5FC3\u8DF3\u7684\u65F6\u523B\u300D\uFF0C\u907F\u514D\u521A\u8FDE\u4E0A\u5C31\u8BEF\u5224\u8D85\u65F6\u3002\n */\n private checkHeartbeat(bot: QQBot) {\n if (Date.now() - bot.lastHeartbeatTime > 100000) {\n bot.logger?.error('WebSocket \u5FC3\u8DF3\u8D85\u65F6\uFF0C\u91CD\u5EFA\u8FDE\u63A5...');\n bot.ws?.terminate();\n } else {\n bot.checkHeartbeatFunc = setTimeout(() => bot.checkHeartbeat(bot), 40000);\n }\n }\n\n private handlerOperationQueue(bot: QQBot) {\n const operation = bot.operationQueue.shift();\n if (!operation) return;\n this.lastOperationHandle = dayjs().valueOf();\n operation();\n }\n\n private operation(func: () => void) {\n const time = dayjs().valueOf();\n if (time - this.lastOperationHandle > 2000) {\n func();\n this.lastOperationHandle = time;\n } else {\n this.operationQueue.push(func);\n }\n }\n\n public listen<T extends BotEvent, C extends BotEventCancelable>(\n event: T,\n callback: EventCallback<T, C>,\n namespace: string = 'gugle-event',\n priority: number = 100,\n cancelable: C = false as C\n ) {\n this.eventManager.listen(event, callback, namespace, priority, cancelable);\n }\n\n private onWebsocketMsg(bot: QQBot, data: RawData) {\n let msg: WSMSG;\n try {\n msg = JSON.parse(data.toString('utf-8'));\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n bot.logger?.error(`\u89E3\u6790 WebSocket \u6D88\u606F\u5931\u8D25\uFF1A${reason}`);\n return;\n }\n bot.logger?.debug(`Received message: ${JSON.stringify(msg)}`);\n let eventName = 'unknown-event';\n if (msg.post_type == 'meta_event') {\n eventName = `meta-event-${msg.meta_event_type}`;\n }\n if (msg.post_type == 'message') {\n eventName = `message-event-${msg.message_type}`;\n }\n if (msg.post_type == 'notice') {\n eventName = `notice-event-${msg.notice_type}`;\n }\n if (msg.post_type == 'request') {\n eventName = `request-event-${msg.request_type}`;\n }\n eventName = eventName.toLowerCase().replace(/_/g, '-');\n bot.logger?.debug(`post notice event: ${eventName}`);\n bot.post(eventName, bot, msg).then();\n }\n\n private onHeartbeat(bot: QQBot, data: RawData) {\n bot.logger?.debug(`Received heartbeat: ${JSON.stringify(data)}`);\n // \u6536\u5230\u5FC3\u8DF3\u624D\u8BF4\u660E\u5BF9\u7AEF\u786E\u5B9E\u5728\u5DE5\u4F5C\uFF0C\u6B64\u65F6\u624D\u89C6\u4E3A\u8FDE\u63A5\u771F\u6B63\u6062\u590D\u5E76\u6E05\u96F6\u91CD\u8FDE\u8BA1\u6570\uFF1B\n // \u4EC5\u63E1\u624B\u6210\u529F\uFF08open\uFF09\u4E0D\u8DB3\u4EE5\u5224\u65AD\uFF0Cnapcat \u4E0D\u53EF\u7528\u65F6\u4E5F\u4F1A\u5B8C\u6210\u63E1\u624B\u540E\u7ACB\u5373\u65AD\u5F00\u3002\n bot.reconnectAttempts = 0;\n bot.lastHeartbeatTime = Date.now();\n clearTimeout(bot.checkHeartbeatFunc);\n bot.checkHeartbeatFunc = setTimeout(() => bot.checkHeartbeat(bot), 40000);\n }\n\n private onGroupMsg(bot: QQBot, msg: GroupMessageWSMSG): void {\n if (!getFeatureGroups('command').includes(msg.group_id)) return;\n const command = msg.raw_message;\n if (!command.startsWith('/')) return;\n // \u4E92\u901A\u6865\u63A5\u7684 /send \u7531 DiscordBridge \u81EA\u884C\u5904\u7406\uFF0C\u4E0D\u8D70\u547D\u4EE4\u5206\u53D1\n // \uFF08\u5426\u5219 gugle-command \u627E\u4E0D\u5230 send \u8282\u70B9\u4F1A\u56DE\u53D1 Invalid command\uFF09\n if (command.startsWith('/send')) {\n void import('@/features/discord-bridge')\n .then(module => {\n // /send \u540E\u5FC5\u987B\u662F\u7A7A\u767D\u6216\u7ED3\u5C3E\uFF0C/sendxxx \u4ECD\u4EA4\u7ED9\u547D\u4EE4\u5206\u53D1\n const rest = command.slice('/send'.length);\n if (rest && !/^\\s/.test(rest)) {\n executeCommand(bot.commandManager, new GroupMsgCommandSource(bot, msg), command);\n return;\n }\n if (!module.DiscordBridge.isSendTargetGroup(msg.group_id)) {\n executeCommand(bot.commandManager, new GroupMsgCommandSource(bot, msg), command);\n }\n })\n .catch(e => {\n bot.logger?.error(`DiscordBridge /send dispatch failed: ${e?.message ?? e}`);\n });\n return;\n }\n executeCommand(bot.commandManager, new GroupMsgCommandSource(bot, msg), command);\n }\n\n // ------------------------------------------------\n // Decorators\n // ------------------------------------------------\n\n /**\n * \u5B9A\u4E49\u4E00\u4E2A cron \u88C5\u9970\u5668\uFF0C\u7528\u4E8E\u6839\u636E\u7ED9\u5B9A\u7684 cron \u8868\u8FBE\u5F0F\u8C03\u5EA6\u4EFB\u52A1\n *\n * @param _cron cron \u8868\u8FBE\u5F0F\uFF0C\u516D\u4F4D\u683C\u5F0F\uFF1A\u79D2 \u5206 \u65F6 \u65E5 \u6708 \u5468\uFF1B\u79D2\u4E0E\u5206\u4F7F\u7528\u6B65\u8FDB\u8BED\u6CD5\uFF08\u661F\u53F7\u52A0\u659C\u6760\u6B65\u957F\uFF09\n * @returns {(executor: (bot: QQBot) => void) => void} \u8FD4\u56DE\u4E00\u4E2A\u51FD\u6570\uFF0C\u8BE5\u51FD\u6570\u63A5\u53D7\u4E00\u4E2A\u6267\u884C\u5668\u51FD\u6570\u4F5C\u4E3A\u53C2\u6570\uFF0C\u5E76\u5728\u6307\u5B9A\u65F6\u95F4\u6267\u884C\u8BE5\u6267\u884C\u5668\u51FD\u6570\n *\n * @example\n * \u6BCF 30 \u79D2\u6267\u884C\u4E00\u6B21\uFF1A\u79D2\u5B57\u6BB5\u5199\u6B65\u8FDB 30\uFF0C\u5176\u4F59\u5B57\u6BB5\u5199\u661F\u53F7\n */\n public cron(_cron: string): (executor: (bot: QQBot) => void) => void {\n // \u4FDD\u5B58\u5F53\u524D\u5B9E\u4F8B\u7684\u5F15\u7528\uFF0C\u4EE5\u4FBF\u5728\u540E\u7EED\u7684\u6267\u884C\u5668\u51FD\u6570\u4E2D\u4F7F\u7528\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const self: QQBot = this;\n // \u8FD4\u56DE\u4E00\u4E2A\u51FD\u6570\uFF0C\u8BE5\u51FD\u6570\u8D1F\u8D23\u8C03\u5EA6\u6267\u884C\u5668\u51FD\u6570\n return function (executor: (bot: QQBot) => void) {\n // \u8868\u8FBE\u5F0F\u975E\u6CD5\u65F6 cron.schedule \u4F1A\u76F4\u63A5\u629B\u51FA\uFF0C\u8FD9\u91CC\u8BB0\u5F55\u5E26\u8868\u8FBE\u5F0F\u7684\u9519\u8BEF\u4FBF\u4E8E\u5B9A\u4F4D\n try {\n cron.schedule(_cron, () => {\n executor(self);\n });\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n self.logger?.error(`cron \u8868\u8FBE\u5F0F\u975E\u6CD5\uFF08${_cron}\uFF09\uFF1A${reason}`);\n }\n };\n }\n\n /**\n * \u5B9A\u4E49\u4E00\u4E2A\u4E8B\u4EF6\u8BA2\u9605\u88C5\u9970\u5668\uFF0C\u7528\u4E8E\u6839\u636E\u4E8B\u4EF6\u89E6\u53D1\u56DE\u8C03\n *\n * @param event {string} \u4E8B\u4EF6\u540D\u79F0\n * @param namespace {string} \u547D\u540D\u7A7A\u95F4\uFF0C\u7528\u4E8E\u7EC4\u7EC7\u4E8B\u4EF6\u76D1\u542C\u5668\n * @param priority {number} \u4F18\u5148\u7EA7\uFF0C\u51B3\u5B9A\u4E8B\u4EF6\u56DE\u8C03\u7684\u6267\u884C\u987A\u5E8F\n * @param cancelable {boolean} \u662F\u5426\u53EF\u53D6\u6D88\uFF0C\u51B3\u5B9A\u662F\u5426\u53EF\u4EE5\u53D6\u6D88\u4E8B\u4EF6\uFF0C\u4E3A true \u65F6\uFF0C\u5904\u7406\u5668\u7B2C\u4E00\u4E2A\u53C2\u6570\u4F1A\u4F20\u5165 Cancelable\n * @returns {(callback: (...args: any) => void) => void} \u4E00\u4E2A\u51FD\u6570\uFF0C\u63A5\u53D7\u4E8B\u4EF6\u56DE\u8C03\u5E76\u6CE8\u518C\u8BE5\u56DE\u8C03\u5230\u6307\u5B9A\u4E8B\u4EF6\n *\n * @example\n * @ bot.subscribe('after-start', true)\n * public test(cancelable: Cancelable, bot: HeyBoxBot) {}\n */\n public subscribe<T extends BotEvent, C extends BotEventCancelable>(\n event: T,\n cancelable: C = false as C,\n namespace: string = 'gugle-event',\n priority: number = 100\n ): (callback: EventCallback<T, C>) => void {\n return this.eventManager.subscribe(event, namespace, priority, cancelable);\n }\n\n // ------------------------------------------------\n // API Methods\n // ------------------------------------------------\n\n public sendPrivateMsg(userID: string | number, message: SentMessage): Promise<number | undefined> {\n return new Promise(resolve => {\n this.operation(() => {\n this.axiosInstance\n .post(`/send_private_msg`, {\n user_id: userID,\n message: message\n })\n .then(res => resolve(res?.data?.data?.message_id))\n .catch(error => {\n this.logger?.error(`Failed to send private message to ${userID}: ${error.message}`);\n resolve(undefined);\n });\n });\n });\n }\n\n public sendGroupMsg(\n groupId: string | number,\n message: SentMessage,\n options?: SendGroupMsgOptions\n ): Promise<number | undefined> {\n return new Promise(resolve => {\n this.operation(() => {\n this.axiosInstance\n .post(`/send_group_msg`, {\n group_id: groupId,\n message: message\n })\n .then(res => {\n const messageId: number | undefined = res?.data?.data?.message_id;\n this.notifyGroupMessageSent({\n groupId: Number(groupId),\n message,\n messageId,\n fromBridge: options?.fromBridge === true,\n fromWebhook: options?.fromWebhook === true\n });\n resolve(messageId);\n })\n .catch(error => {\n this.logger?.error(`Failed to send group message to ${groupId}: ${error.message}`);\n resolve(undefined);\n });\n });\n });\n }\n\n /**\n * \u4EE5\u5408\u5E76\u8F6C\u53D1\uFF08\u804A\u5929\u8BB0\u5F55\uFF09\u5F62\u5F0F\u53D1\u9001\u7FA4\u6D88\u606F\uFF0C\u7528\u4E8E\u8D85\u957F AI \u56DE\u590D\u7B49\u573A\u666F\u3002\n *\n * \u7ED3\u6784\u5B8C\u5168\u5BF9\u9F50 sendGroupMsg\uFF1A\u540C\u6837\u8D70 operation \u961F\u5217\uFF0C\u5931\u8D25\u53EA\u8BB0\u5F55\u65E5\u5FD7\u5E76\u56DE\u4F20 undefined\uFF08\u4E0D reject\uFF09\uFF1B\n * \u6210\u529F\u540E\u7167\u5E38\u901A\u77E5\u4E92\u901A\u6865\uFF0CDiscordBridge \u56E0\u800C\u4ECD\u80FD\u955C\u50CF\u8FD9\u6761\u6D88\u606F\uFF08\u5B83\u4F1A\u628A node \u6BB5\u6587\u672C\u62CD\u5E73\u540E\u8F6C\u53D1\uFF09\u3002\n */\n public sendGroupForwardMsg(\n groupId: string | number,\n nodes: ForwardNode[],\n options?: SendGroupMsgOptions\n ): Promise<number | undefined> {\n return new Promise(resolve => {\n this.operation(() => {\n const message: NodeMessage[] = nodes.map(node => QQBot.toForwardNodeMessage(node));\n this.axiosInstance\n .post(`/send_group_forward_msg`, {\n group_id: groupId,\n message: message\n })\n .then(res => {\n const messageId: number | undefined = res?.data?.data?.message_id;\n this.notifyGroupMessageSent({\n groupId: Number(groupId),\n message,\n messageId,\n fromBridge: options?.fromBridge === true,\n fromWebhook: options?.fromWebhook === true\n });\n resolve(messageId);\n })\n .catch(error => {\n this.logger?.error(`Failed to send group forward message to ${groupId}: ${error.message}`);\n resolve(undefined);\n });\n });\n });\n }\n\n /** \u628A\u8C03\u7528\u65B9\u7684 ForwardNode \u8865\u6210 OneBot \u7684 node \u6BB5\uFF08NapCat \u53E3\u5F84\uFF1A\u6635\u79F0\u4E0E QQ \u53F7\u4E00\u5F8B\u5B57\u7B26\u4E32\u5316\uFF09 */\n private static toForwardNodeMessage(node: ForwardNode): NodeMessage {\n // user_id \u5728\u7C7B\u578B\u91CC\u58F0\u660E\u4E3A number\uFF0C\u4F46 OneBot \u5B9E\u9645\u63A5\u53D7/\u56DE\u4F20\u5B57\u7B26\u4E32\uFF08NapCat \u53E3\u5F84\u5373 String(uin)\uFF09\uFF0C\u8FD9\u91CC\u6309\u8F7D\u8377\u9700\u8981\u65AD\u8A00\n return {\n type: 'node',\n data: {\n nickname: String(node.name ?? ''),\n user_id: String(node.uin ?? '') as unknown as number,\n content: typeof node.content === 'string' ? [{ type: 'text', data: { text: node.content } }] : node.content\n }\n };\n }\n\n /**\n * \u6CE8\u518C\u300C\u7FA4\u6D88\u606F\u53D1\u9001\u6210\u529F\u300D\u76D1\u542C\u5668\u3002\n * \u4E92\u901A\u6865\u501F\u6B64\u628A\u673A\u5668\u4EBA\u81EA\u8EAB\u53D1\u51FA\u7684\u6D88\u606F\uFF08GitHub \u901A\u77E5\u3001\u547D\u4EE4\u56DE\u590D\u7B49\uFF09\u8F6C\u53D1\u5230 Discord\u3002\n * \u6CE8\u610F\uFF1A\u76D1\u542C\u5668\u5FC5\u987B\u5FFD\u7565 `fromBridge === true` \u7684\u4E8B\u4EF6\uFF0C\u907F\u514D\u56DE\u73AF\u3002\n */\n public onGroupMessageSent(listener: (event: GroupMessageSentEvent) => void): void {\n this.groupMessageSentListeners.push(listener);\n }\n\n private notifyGroupMessageSent(event: GroupMessageSentEvent): void {\n for (const listener of this.groupMessageSentListeners) {\n try {\n listener(event);\n } catch (e) {\n this.logger?.error(`groupMessageSent listener failed: ${(e as Error)?.message ?? e}`);\n }\n }\n }\n\n /** \u66B4\u9732\u547D\u4EE4\u7BA1\u7406\u5668\uFF08Discord \u4FA7\u547D\u4EE4\u5206\u53D1\u9700\u8981\u590D\u7528\u540C\u4E00\u5957\u547D\u4EE4\u6811\uFF09 */\n public getCommandManager(): CommandManager {\n return this.commandManager;\n }\n\n // \u8BBE\u7F6E\u7981\u8A00\n public ban(groupId: number, userId: number, duration: number) {\n const bot = this;\n this.operation(() => {\n bot.logger?.info(`ban ${userId} in group ${groupId} for ${duration} seconds`);\n bot.axiosInstance\n .post(`/set_group_ban`, {\n group_id: `${groupId}`,\n user_id: `${userId}`,\n duration: duration\n })\n .then();\n });\n }\n\n // \u8E22\u51FA\u7FA4\u804A\n public kick(groupId: number, userId: number) {\n const bot = this;\n this.operation(() => {\n bot.logger?.info(`kick ${userId} in group`);\n bot.axiosInstance\n .post(`/set_group_kick`, {\n group_id: `${groupId}`,\n user_id: `${userId}`,\n reject_add_request: false\n })\n .then();\n });\n }\n\n public getLoginInfo(): Promise<LoginInfoData> {\n return new Promise<LoginInfoData>((resolve, reject) => {\n if (!this.loginInfo) {\n this.axiosInstance\n .get<LoginInfo>(`/get_login_info`)\n .then(res => resolve(res.data.data))\n .catch(reject);\n } else {\n resolve(this.loginInfo);\n }\n });\n }\n\n public getUserInfo(userId: number) {\n const bot = this;\n return new Promise<{ user_id: string; nickname: string; qid: string }>((resolve, reject) => {\n this.operation(() => {\n bot.axiosInstance\n .post(`/get_stranger_info`, {\n user_id: `${userId}`\n })\n .then(res => resolve(res.data.data))\n .then();\n });\n });\n }\n\n public getLoginInfoSync(): LoginInfoData | undefined {\n return this.loginInfo;\n }\n\n public approveGroupAddRequest(flag: string) {\n const bot = this;\n this.operation(() => {\n bot.axiosInstance\n .post(`/set_group_add_request`, {\n flag: flag,\n sub_type: 'add',\n approve: true\n })\n .then();\n });\n }\n\n public denyGroupAddRequest(flag: string, reason?: string) {\n const bot = this;\n this.operation(() => {\n bot.axiosInstance\n .post(`/set_group_add_request`, {\n flag: flag,\n sub_type: 'add',\n approve: false,\n reason: reason\n })\n .then();\n });\n }\n}\n\nexport const bot = new QQBot(botConfig);\n\n// \u5168\u5C40\u515C\u5E95\uFF1A\u5355\u4E2A\u5F02\u6B65\u4EFB\u52A1\u5931\u8D25\uFF08\u5982 B \u7AD9\u8FD4\u56DE 412\u3001\u7F51\u7EDC\u8D85\u65F6\uFF09\u4E0D\u5E94\u7EC8\u6B62\u673A\u5668\u4EBA\u8FDB\u7A0B\u3002\n// \u672A\u5904\u7406\u7684 Promise rejection \u4E0E\u672A\u6355\u83B7\u5F02\u5E38\u7EDF\u4E00\u8BB0\u5F55\u65E5\u5FD7\u540E\u7EE7\u7EED\u8FD0\u884C\u3002\nprocess.on('unhandledRejection', reason => {\n const error = reason instanceof Error ? reason : new Error(String(reason));\n (bot.logger ?? console).error(`Unhandled promise rejection: ${error.stack ?? error.message}`);\n});\n\nprocess.on('uncaughtException', error => {\n (bot.logger ?? console).error(`Uncaught exception: ${error.stack ?? error.message}`);\n});\n\nrequire('@/custom');\n\n/**\n * \u542F\u52A8\u673A\u5668\u4EBA\uFF08\u542B /reboot \u62C9\u8D77\u65F6\u7684\u5EF6\u8FDF\u542F\u52A8\uFF09\u3002\n *\n * /reboot \u4F1A\u5148\u62C9\u8D77\u65B0\u8FDB\u7A0B\u518D\u9000\u51FA\u65E7\u8FDB\u7A0B\uFF1B\u82E5\u65B0\u8FDB\u7A0B\u7ACB\u523B\u8FDE\u63A5 NapCat\uFF0C\u4F1A\u4E0E\u65E7\u8FDB\u7A0B\u7684\n * \u540C\u53F7\u8FDE\u63A5\u51B2\u7A81\uFF08\u4E92\u8E22 / \u6D88\u606F\u4E22\u5931\uFF09\u3002\u56E0\u6B64\u88AB /reboot \u62C9\u8D77\u65F6\u6309 QQBOT_START_DELAY_MS\n * \u5EF6\u8FDF\u542F\u52A8\uFF0C\u7ED9\u65E7\u8FDB\u7A0B\u7559\u51FA\u65AD\u5F00\u65F6\u95F4\u3002\u6B63\u5E38\u542F\u52A8\u8BE5\u5EF6\u8FDF\u4E3A 0\uFF0C\u884C\u4E3A\u4E0D\u53D8\u3002\n */\nasync function startBot(): Promise<void> {\n const restartDelay = restartStartDelayMs();\n if (restartDelay > 0) {\n (bot.logger ?? console).info(\n `[Admin] \u672C\u8FDB\u7A0B\u7531 /reboot \u62C9\u8D77\uFF0C\u5EF6\u8FDF ${restartDelay}ms \u542F\u52A8\u4EE5\u907F\u514D\u4E0E\u65E7\u8FDE\u63A5\u91CD\u53E0`\n );\n await new Promise(resolve => setTimeout(resolve, restartDelay));\n }\n const started = await bot.start();\n const github = new Github(started);\n \n process.on('message', (message: any) => {\n if (message?.type === 'github') void github.post('github-' + message.event, bot, message.payload);\n });\n process.on('disconnect', () => process.exit(0));\n process.send?.({ type: 'ready' });\n}\n\nstartBot().catch(error => {\n (bot.logger ?? console).error(`\u542F\u52A8\u5931\u8D25: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}`);\n});\n"],
5
+ "mappings": "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",
6
+ "names": ["fs", "path", "createBackupConfigFile", "readConfigFile", "writeConfigFile", "path", "fs", "import_node_fs", "import_node_path", "path", "current", "readConfigFile", "writeConfigFile", "import_node_path", "path", "dayjs", "console", "puppeteer", "path", "import_node_fs", "import_node_path", "fs", "dayjs", "issue", "import_dayjs", "init_image", "getRepositoryParts", "dayjs", "pr", "import_dayjs", "init_image", "getRepositoryParts", "dayjs", "import_dayjs", "init_image", "axios", "escapeHtml", "index", "markdownit", "hljs", "emoji", "init_image", "import_node_path", "import_axios", "path", "axios", "writeConfigFile", "import_axios", "init_image", "axios", "bot", "http", "imageData", "msg", "fs", "path", "import_node_fs", "import_node_path", "escapeHtml", "import_markdown_it", "import_markdown_it_emoji", "MarkdownIt", "emoji", "bot", "init_config", "init_config", "bot", "dayjs", "import_dayjs", "init_image", "bot", "init_image", "init_image", "bot", "res", "import_axios", "axios", "document", "fs", "writeConfigFile", "t", "import_axios", "axios", "bot", "bot", "import_node_fs", "import_node_path", "path", "fs", "import_node_fs", "import_node_path", "init_config", "path", "fs", "writeConfigFile", "bot", "emoji", "escapeHtml", "init_config", "import_node_fs", "import_node_path", "init_config", "bot", "run", "fs", "path", "bot", "import_discord", "import_discord", "DISCORD_MESSAGE_LIMIT", "init_config", "bot", "emoji", "msg", "import_dayjs", "bot", "dayjs", "bot", "bot", "isNumberArray", "normalizeConfig", "import_dayjs", "bot", "dayjs", "import_gugle_command", "bot", "QQBot", "process", "import_node_fs", "import_dayjs", "import_gugle_event", "import_axios", "import_gugle_command", "bot", "axios", "path", "fs", "dayjs", "module"]
7
+ }