ylib-wecom-openclaw-plugin 2026.4.29

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 (180) hide show
  1. package/README.md +596 -0
  2. package/dist/index.d.ts +10 -0
  3. package/dist/index.js +99 -0
  4. package/dist/src/accounts.d.ts +57 -0
  5. package/dist/src/accounts.js +247 -0
  6. package/dist/src/agent/api-client.d.ts +95 -0
  7. package/dist/src/agent/api-client.js +425 -0
  8. package/dist/src/agent/handler.d.ts +64 -0
  9. package/dist/src/agent/handler.js +731 -0
  10. package/dist/src/agent/index.d.ts +5 -0
  11. package/dist/src/agent/index.js +21 -0
  12. package/dist/src/agent/webhook.d.ts +25 -0
  13. package/dist/src/agent/webhook.js +294 -0
  14. package/dist/src/agent/xml.d.ts +21 -0
  15. package/dist/src/agent/xml.js +43 -0
  16. package/dist/src/channel.d.ts +5 -0
  17. package/dist/src/channel.js +815 -0
  18. package/dist/src/chat-queue.d.ts +31 -0
  19. package/dist/src/chat-queue.js +53 -0
  20. package/dist/src/config-schema.d.ts +587 -0
  21. package/dist/src/config-schema.js +146 -0
  22. package/dist/src/const.d.ts +128 -0
  23. package/dist/src/const.js +168 -0
  24. package/dist/src/dm-policy.d.ts +29 -0
  25. package/dist/src/dm-policy.js +146 -0
  26. package/dist/src/dynamic-agent.d.ts +37 -0
  27. package/dist/src/dynamic-agent.js +67 -0
  28. package/dist/src/dynamic-routing.d.ts +65 -0
  29. package/dist/src/dynamic-routing.js +62 -0
  30. package/dist/src/endpoint-dispatch.d.ts +54 -0
  31. package/dist/src/endpoint-dispatch.js +967 -0
  32. package/dist/src/endpoint-event-adapter.d.ts +15 -0
  33. package/dist/src/endpoint-event-adapter.js +427 -0
  34. package/dist/src/group-policy.d.ts +30 -0
  35. package/dist/src/group-policy.js +126 -0
  36. package/dist/src/http.d.ts +27 -0
  37. package/dist/src/http.js +168 -0
  38. package/dist/src/im-runtime-telemetry.d.ts +25 -0
  39. package/dist/src/im-runtime-telemetry.js +68 -0
  40. package/dist/src/interface.d.ts +192 -0
  41. package/dist/src/interface.js +5 -0
  42. package/dist/src/markdown-chunk.d.ts +1 -0
  43. package/dist/src/markdown-chunk.js +396 -0
  44. package/dist/src/mcp/index.d.ts +6 -0
  45. package/dist/src/mcp/index.js +28 -0
  46. package/dist/src/mcp/interceptors/biz-error.d.ts +11 -0
  47. package/dist/src/mcp/interceptors/biz-error.js +73 -0
  48. package/dist/src/mcp/interceptors/doc-auth-error.d.ts +10 -0
  49. package/dist/src/mcp/interceptors/doc-auth-error.js +235 -0
  50. package/dist/src/mcp/interceptors/index.d.ts +35 -0
  51. package/dist/src/mcp/interceptors/index.js +143 -0
  52. package/dist/src/mcp/interceptors/msg-media.d.ts +11 -0
  53. package/dist/src/mcp/interceptors/msg-media.js +201 -0
  54. package/dist/src/mcp/interceptors/smartpage-create.d.ts +30 -0
  55. package/dist/src/mcp/interceptors/smartpage-create.js +252 -0
  56. package/dist/src/mcp/interceptors/smartpage-export.d.ts +17 -0
  57. package/dist/src/mcp/interceptors/smartpage-export.js +135 -0
  58. package/dist/src/mcp/interceptors/smartsheet-upload.d.ts +22 -0
  59. package/dist/src/mcp/interceptors/smartsheet-upload.js +388 -0
  60. package/dist/src/mcp/interceptors/types.d.ts +64 -0
  61. package/dist/src/mcp/interceptors/types.js +8 -0
  62. package/dist/src/mcp/schema.d.ts +11 -0
  63. package/dist/src/mcp/schema.js +115 -0
  64. package/dist/src/mcp/tool.d.ts +63 -0
  65. package/dist/src/mcp/tool.js +318 -0
  66. package/dist/src/mcp/transport.d.ts +94 -0
  67. package/dist/src/mcp/transport.js +702 -0
  68. package/dist/src/media-handler.d.ts +55 -0
  69. package/dist/src/media-handler.js +306 -0
  70. package/dist/src/media-uploader.d.ts +142 -0
  71. package/dist/src/media-uploader.js +446 -0
  72. package/dist/src/message-parser.d.ts +104 -0
  73. package/dist/src/message-parser.js +232 -0
  74. package/dist/src/message-sender.d.ts +54 -0
  75. package/dist/src/message-sender.js +210 -0
  76. package/dist/src/monitor.d.ts +69 -0
  77. package/dist/src/monitor.js +1846 -0
  78. package/dist/src/onboarding.d.ts +8 -0
  79. package/dist/src/onboarding.js +248 -0
  80. package/dist/src/openclaw-compat.d.ts +148 -0
  81. package/dist/src/openclaw-compat.js +839 -0
  82. package/dist/src/proactive-markdown-send.d.ts +14 -0
  83. package/dist/src/proactive-markdown-send.js +205 -0
  84. package/dist/src/reqid-store.d.ts +23 -0
  85. package/dist/src/reqid-store.js +136 -0
  86. package/dist/src/runtime.d.ts +2 -0
  87. package/dist/src/runtime.js +7 -0
  88. package/dist/src/shared/command-auth.d.ts +23 -0
  89. package/dist/src/shared/command-auth.js +112 -0
  90. package/dist/src/shared/xml-parser.d.ts +46 -0
  91. package/dist/src/shared/xml-parser.js +228 -0
  92. package/dist/src/state-dir-resolve.d.ts +2 -0
  93. package/dist/src/state-dir-resolve.js +33 -0
  94. package/dist/src/state-manager.d.ts +115 -0
  95. package/dist/src/state-manager.js +413 -0
  96. package/dist/src/target.d.ts +35 -0
  97. package/dist/src/target.js +71 -0
  98. package/dist/src/template-card-manager.d.ts +55 -0
  99. package/dist/src/template-card-manager.js +316 -0
  100. package/dist/src/template-card-parser.d.ts +37 -0
  101. package/dist/src/template-card-parser.js +672 -0
  102. package/dist/src/timeout.d.ts +20 -0
  103. package/dist/src/timeout.js +57 -0
  104. package/dist/src/types/account.d.ts +29 -0
  105. package/dist/src/types/account.js +5 -0
  106. package/dist/src/types/config.d.ts +98 -0
  107. package/dist/src/types/config.js +8 -0
  108. package/dist/src/types/constants.d.ts +42 -0
  109. package/dist/src/types/constants.js +45 -0
  110. package/dist/src/types/index.d.ts +7 -0
  111. package/dist/src/types/index.js +17 -0
  112. package/dist/src/types/message.d.ts +238 -0
  113. package/dist/src/types/message.js +6 -0
  114. package/dist/src/utils.d.ts +148 -0
  115. package/dist/src/utils.js +92 -0
  116. package/dist/src/version.d.ts +2 -0
  117. package/dist/src/version.js +28 -0
  118. package/dist/src/webhook/command-auth.d.ts +47 -0
  119. package/dist/src/webhook/command-auth.js +137 -0
  120. package/dist/src/webhook/gateway.d.ts +36 -0
  121. package/dist/src/webhook/gateway.js +297 -0
  122. package/dist/src/webhook/handler.d.ts +19 -0
  123. package/dist/src/webhook/handler.js +481 -0
  124. package/dist/src/webhook/helpers.d.ts +157 -0
  125. package/dist/src/webhook/helpers.js +936 -0
  126. package/dist/src/webhook/http.d.ts +27 -0
  127. package/dist/src/webhook/http.js +168 -0
  128. package/dist/src/webhook/index.d.ts +11 -0
  129. package/dist/src/webhook/index.js +43 -0
  130. package/dist/src/webhook/media.d.ts +30 -0
  131. package/dist/src/webhook/media.js +152 -0
  132. package/dist/src/webhook/monitor.d.ts +59 -0
  133. package/dist/src/webhook/monitor.js +1672 -0
  134. package/dist/src/webhook/state.d.ts +220 -0
  135. package/dist/src/webhook/state.js +568 -0
  136. package/dist/src/webhook/target.d.ts +41 -0
  137. package/dist/src/webhook/target.js +165 -0
  138. package/dist/src/webhook/types.d.ts +348 -0
  139. package/dist/src/webhook/types.js +36 -0
  140. package/dist/src/webhook/video-frame.d.ts +13 -0
  141. package/dist/src/webhook/video-frame.js +108 -0
  142. package/openclaw.plugin.json +19 -0
  143. package/package.json +96 -0
  144. package/schema.json +534 -0
  145. package/scripts/generate-schema.mjs +33 -0
  146. package/skills/wecom-contact/SKILL.md +162 -0
  147. package/skills/wecom-doc/SKILL.md +162 -0
  148. package/skills/wecom-doc/references/create-doc.md +56 -0
  149. package/skills/wecom-doc/references/edit-doc-content.md +68 -0
  150. package/skills/wecom-doc/references/get-doc-content.md +88 -0
  151. package/skills/wecom-doc/references/smartpage-create.md +125 -0
  152. package/skills/wecom-doc/references/smartpage-export.md +160 -0
  153. package/skills/wecom-meeting/SKILL.md +441 -0
  154. package/skills/wecom-meeting/references/example-full.md +30 -0
  155. package/skills/wecom-meeting/references/example-reminder.md +46 -0
  156. package/skills/wecom-meeting/references/example-security.md +22 -0
  157. package/skills/wecom-meeting/references/response-get-meeting-info.md +148 -0
  158. package/skills/wecom-msg/SKILL.md +157 -0
  159. package/skills/wecom-msg/references/api-get-messages.md +93 -0
  160. package/skills/wecom-msg/references/api-get-msg-chat-list.md +58 -0
  161. package/skills/wecom-msg/references/api-get-msg-media.md +44 -0
  162. package/skills/wecom-msg/references/api-send-message.md +39 -0
  163. package/skills/wecom-preflight/SKILL.md +141 -0
  164. package/skills/wecom-schedule/SKILL.md +161 -0
  165. package/skills/wecom-schedule/references/api-check-availability.md +56 -0
  166. package/skills/wecom-schedule/references/api-create-schedule.md +38 -0
  167. package/skills/wecom-schedule/references/api-get-schedule-detail.md +81 -0
  168. package/skills/wecom-schedule/references/api-update-schedule.md +32 -0
  169. package/skills/wecom-schedule/references/ref-reminders.md +24 -0
  170. package/skills/wecom-send-media/SKILL.md +68 -0
  171. package/skills/wecom-send-template-card/SKILL.md +157 -0
  172. package/skills/wecom-send-template-card/references/api-template-card-types.md +358 -0
  173. package/skills/wecom-smartsheet/SKILL.md +164 -0
  174. package/skills/wecom-smartsheet/references/smartsheet-cell-value-formats.md +163 -0
  175. package/skills/wecom-smartsheet/references/smartsheet-field-types.md +44 -0
  176. package/skills/wecom-smartsheet/references/smartsheet-get-records.md +96 -0
  177. package/skills/wecom-smartsheet/references/webhook-examples.md +185 -0
  178. package/skills/wecom-smartsheet/references/webhook-fallback.md +184 -0
  179. package/skills/wecom-todo/SKILL.md +392 -0
  180. package/skills/wecom-todo/examples/workflows.md +163 -0
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+ /**
3
+ * Webhook Target 管理
4
+ *
5
+ * 从 @mocrane/wecom monitor.ts 部分迁移(仅 Webhook Target 相关)。
6
+ * 维护全局已注册 Target 列表,提供注册/注销/查询功能。
7
+ *
8
+ * Target 按路径索引:Map<path, WecomWebhookTarget[]>
9
+ * 同一路径可能注册多个账号(老路径兼容模式),通过签名验证匹配到正确账号。
10
+ */
11
+ var __assign = (this && this.__assign) || function () {
12
+ __assign = Object.assign || function(t) {
13
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
14
+ s = arguments[i];
15
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
16
+ t[p] = s[p];
17
+ }
18
+ return t;
19
+ };
20
+ return __assign.apply(this, arguments);
21
+ };
22
+ var __spreadArrays = (this && this.__spreadArrays) || function () {
23
+ for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
24
+ for (var r = Array(s), k = 0, i = 0; i < il; i++)
25
+ for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
26
+ r[k] = a[j];
27
+ return r;
28
+ };
29
+ exports.__esModule = true;
30
+ exports.parseWebhookPath = exports.hasActiveTargets = exports.getRegisteredTargets = exports.getWebhookTargetsMap = exports.registerWecomWebhookTarget = void 0;
31
+ // ============================================================================
32
+ // 全局 Target 注册表(按路径索引)
33
+ // ============================================================================
34
+ /** 已注册的 Webhook Target(按路径索引) */
35
+ var webhookTargets = new Map();
36
+ // ============================================================================
37
+ // 路径工具函数
38
+ // ============================================================================
39
+ /**
40
+ * 标准化 Webhook 路径
41
+ *
42
+ * 统一格式:以 `/` 开头且不以 `/` 结尾。
43
+ */
44
+ function normalizeWebhookPath(raw) {
45
+ var trimmed = raw.trim();
46
+ if (!trimmed)
47
+ return "/";
48
+ var withSlash = trimmed.startsWith("/") ? trimmed : "/" + trimmed;
49
+ if (withSlash.length > 1 && withSlash.endsWith("/"))
50
+ return withSlash.slice(0, -1);
51
+ return withSlash;
52
+ }
53
+ /**
54
+ * 去除重复路径
55
+ */
56
+ function uniquePaths(paths) {
57
+ return Array.from(new Set(paths.map(function (p) { return normalizeWebhookPath(p); }).filter(Boolean)));
58
+ }
59
+ // ============================================================================
60
+ // 注册 / 注销
61
+ // ============================================================================
62
+ /**
63
+ * 注册 Webhook Target(单条路径)
64
+ *
65
+ * 将 Target 加入指定路径的列表中,返回注销函数。
66
+ */
67
+ function registerTargetForPath(path, target) {
68
+ var _a;
69
+ var key = normalizeWebhookPath(path);
70
+ var normalizedTarget = __assign(__assign({}, target), { path: key });
71
+ var existing = (_a = webhookTargets.get(key)) !== null && _a !== void 0 ? _a : [];
72
+ webhookTargets.set(key, __spreadArrays(existing, [normalizedTarget]));
73
+ return function () {
74
+ var _a;
75
+ var updated = ((_a = webhookTargets.get(key)) !== null && _a !== void 0 ? _a : []).filter(function (entry) { return entry !== normalizedTarget; });
76
+ if (updated.length > 0)
77
+ webhookTargets.set(key, updated);
78
+ else
79
+ webhookTargets["delete"](key);
80
+ };
81
+ }
82
+ /**
83
+ * 注册 Webhook Target(多条路径)
84
+ *
85
+ * 为每条路径分别注册 Target,返回一个注销函数(一次性注销所有路径)。
86
+ */
87
+ function registerWecomWebhookTarget(target, paths) {
88
+ var unregisters = [];
89
+ for (var _i = 0, _a = uniquePaths(paths); _i < _a.length; _i++) {
90
+ var path = _a[_i];
91
+ unregisters.push(registerTargetForPath(path, target));
92
+ }
93
+ return function () {
94
+ for (var _i = 0, unregisters_1 = unregisters; _i < unregisters_1.length; _i++) {
95
+ var unregister = unregisters_1[_i];
96
+ unregister();
97
+ }
98
+ };
99
+ }
100
+ exports.registerWecomWebhookTarget = registerWecomWebhookTarget;
101
+ /**
102
+ * 获取全局 Target 注册表
103
+ *
104
+ * 返回完整的 Map<path, Target[]>,供 handler.ts 路由匹配使用。
105
+ */
106
+ function getWebhookTargetsMap() {
107
+ return webhookTargets;
108
+ }
109
+ exports.getWebhookTargetsMap = getWebhookTargetsMap;
110
+ /**
111
+ * 获取所有已注册的 Webhook Target(扁平列表)
112
+ *
113
+ * 用于无法精确匹配路径时的逐一签名验证场景。
114
+ */
115
+ function getRegisteredTargets() {
116
+ var seen = new Set();
117
+ var result = [];
118
+ for (var _i = 0, _a = webhookTargets.values(); _i < _a.length; _i++) {
119
+ var list = _a[_i];
120
+ for (var _b = 0, list_1 = list; _b < list_1.length; _b++) {
121
+ var target = list_1[_b];
122
+ if (!seen.has(target)) {
123
+ seen.add(target);
124
+ result.push(target);
125
+ }
126
+ }
127
+ }
128
+ return result;
129
+ }
130
+ exports.getRegisteredTargets = getRegisteredTargets;
131
+ /**
132
+ * 判断是否有活跃 Target
133
+ */
134
+ function hasActiveTargets() {
135
+ return webhookTargets.size > 0;
136
+ }
137
+ exports.hasActiveTargets = hasActiveTargets;
138
+ /**
139
+ * 从 URL 中解析 accountId(多账号路径)
140
+ *
141
+ * 支持路径格式:
142
+ * - /plugins/wecom/bot/{accountId}
143
+ * - /wecom/bot/{accountId}
144
+ * - /wecom/{accountId}
145
+ */
146
+ function parseWebhookPath(url) {
147
+ var patterns = [
148
+ /\/plugins\/wecom\/bot\/([^/?]+)/,
149
+ /\/wecom\/bot\/([^/?]+)/,
150
+ /\/wecom\/([^/?]+)/,
151
+ ];
152
+ for (var _i = 0, patterns_1 = patterns; _i < patterns_1.length; _i++) {
153
+ var pattern = patterns_1[_i];
154
+ var match = url.match(pattern);
155
+ if (match === null || match === void 0 ? void 0 : match[1]) {
156
+ var segment = match[1];
157
+ // 排除已知的非 accountId 路径段
158
+ if (segment === "bot")
159
+ continue;
160
+ return segment;
161
+ }
162
+ }
163
+ return undefined;
164
+ }
165
+ exports.parseWebhookPath = parseWebhookPath;
@@ -0,0 +1,348 @@
1
+ /**
2
+ * Webhook 模式专用类型定义
3
+ *
4
+ * 从 @mocrane/wecom monitor/types.ts 完整迁移,适配目标项目类型体系。
5
+ */
6
+ /// <reference types="node" />
7
+ import type { OpenClawConfig, PluginRuntime } from "ylib-openclaw/plugin-sdk";
8
+ import type { RuntimeEnv } from "ylib-openclaw/plugin-sdk";
9
+ import type { ResolvedWeComAccount } from "../utils.js";
10
+ /** StreamState 过期时间 (10 分钟) */
11
+ export declare const STREAM_TTL_MS: number;
12
+ /** ActiveReply 过期时间 (1 小时) */
13
+ export declare const ACTIVE_REPLY_TTL_MS: number;
14
+ /** 消息防抖间隔 (500ms) */
15
+ export declare const DEFAULT_DEBOUNCE_MS = 500;
16
+ /** stream 回复最大字节数 (20KB) */
17
+ export declare const STREAM_MAX_BYTES = 20480;
18
+ /** 企微 Bot 回复窗口 (6 分钟) */
19
+ export declare const BOT_WINDOW_MS: number;
20
+ /** 超时安全边际 (30 秒) */
21
+ export declare const BOT_SWITCH_MARGIN_MS = 30000;
22
+ /** HTTP 请求超时 (15 秒) */
23
+ export declare const REQUEST_TIMEOUT_MS = 15000;
24
+ /** 自动清理间隔 (60 秒) */
25
+ export declare const PRUNE_INTERVAL_MS = 60000;
26
+ /** 固定 Webhook 路径 */
27
+ export declare const WEBHOOK_PATHS: {
28
+ /** Bot 模式历史兼容路径 */
29
+ readonly BOT: "/wecom";
30
+ /** Bot 模式历史备用兼容路径 */
31
+ readonly BOT_ALT: "/wecom/bot";
32
+ /** Bot 模式推荐路径前缀 */
33
+ readonly BOT_PLUGIN: "/plugins/wecom/bot";
34
+ };
35
+ /**
36
+ * Webhook 模式下的额外账号配置字段
37
+ */
38
+ export interface WebhookAccountConfig {
39
+ /** 连接模式:webhook | websocket(默认 websocket) */
40
+ connectionMode?: "webhook" | "websocket";
41
+ /** Webhook 验证 token */
42
+ token?: string;
43
+ /** AES 加密密钥(43 字符 Base64) */
44
+ encodingAESKey?: string;
45
+ /** 接收方 ID */
46
+ receiveId?: string;
47
+ /** enter_chat 欢迎消息 */
48
+ welcomeText?: string;
49
+ }
50
+ /**
51
+ * 解析后的 Webhook 账号信息
52
+ * 在 ResolvedWeComAccount 基础上增加 Webhook 专用字段
53
+ */
54
+ export interface ResolvedWebhookAccount extends ResolvedWeComAccount {
55
+ connectionMode: "webhook";
56
+ token: string;
57
+ encodingAESKey: string;
58
+ receiveId: string;
59
+ welcomeText?: string;
60
+ }
61
+ /**
62
+ * Webhook 运行时环境
63
+ *
64
+ * 包含基础的日志和错误报告接口,用于解耦对 PluginRuntime 的直接依赖。
65
+ */
66
+ export interface WecomRuntimeEnv {
67
+ log?: (message: string) => void;
68
+ error?: (message: string) => void;
69
+ }
70
+ /**
71
+ * Webhook 目标上下文
72
+ *
73
+ * 描述一个注册的 Bot 接收端点。包含处理该端点所需的所有上下文信息。
74
+ */
75
+ export interface WecomWebhookTarget {
76
+ /** 解析后的 Bot 账号信息 (Token, AESKey 等) */
77
+ account: ResolvedWebhookAccount;
78
+ /** 插件全局配置 */
79
+ config: OpenClawConfig;
80
+ /** 运行时环境 (日志) */
81
+ runtime: WecomRuntimeEnv;
82
+ /** OpenClaw 插件核心运行时 */
83
+ core: PluginRuntime;
84
+ /** 该 Target 注册的 Webhook 路径 */
85
+ path: string;
86
+ /** 反馈最后接收/发送时间 */
87
+ statusSink?: (patch: {
88
+ lastInboundAt?: number;
89
+ lastOutboundAt?: number;
90
+ }) => void;
91
+ }
92
+ /**
93
+ * 流式会话状态
94
+ *
95
+ * 记录一个流式请求的生命周期状态。
96
+ */
97
+ export interface StreamState {
98
+ /** 唯一会话 ID */
99
+ streamId: string;
100
+ /** 关联的企业微信消息 ID(用于去重) */
101
+ msgid?: string;
102
+ /** 会话键(同一人同一会话,用于队列/批次) */
103
+ conversationKey?: string;
104
+ /** 批次键(conversationKey + 批次序号) */
105
+ batchKey?: string;
106
+ /** 触发者 userid(用于 Agent 私信兜底) */
107
+ userId?: string;
108
+ /** 会话类型(用于群聊兜底逻辑) */
109
+ chatType?: "group" | "direct";
110
+ /** 群聊 chatid(用于日志/提示,不用于 Agent 发群) */
111
+ chatId?: string;
112
+ /** 智能机器人 aibotid(用于 taskKey 生成与日志) */
113
+ aibotid?: string;
114
+ /** Bot 回调幂等键(用于最终交付幂等) */
115
+ taskKey?: string;
116
+ /** 创建时间 */
117
+ createdAt: number;
118
+ /** 最后更新时间(用于 Prune) */
119
+ updatedAt: number;
120
+ /** 是否已开始处理(Agent 已介入) */
121
+ started: boolean;
122
+ /** 是否已完成(Agent 输出完毕或出错) */
123
+ finished: boolean;
124
+ /** 错误信息 */
125
+ error?: string;
126
+ /** 已积累的响应内容(用于长轮询返回) */
127
+ content: string;
128
+ /** 过程中生成的图片(Base64 + MD5) */
129
+ images?: Array<{
130
+ base64: string;
131
+ md5: string;
132
+ }>;
133
+ /** 兜底模式(仅作为内部状态,不暴露给企微) */
134
+ fallbackMode?: "media" | "timeout" | "error";
135
+ /** 群内兜底提示是否已发送(用于防重复刷屏) */
136
+ fallbackPromptSentAt?: number;
137
+ /** Agent 私信最终交付是否已完成(用于防重复发送) */
138
+ finalDeliveredAt?: number;
139
+ /** 用于私信兜底的完整内容(不受 STREAM_MAX_BYTES 限制,但仍需上限保护) */
140
+ dmContent?: string;
141
+ /** 已通过 Agent 私信发送过的媒体标识(防重复发送附件) */
142
+ agentMediaKeys?: string[];
143
+ /** 是否来自 WebSocket 长链接模式(用于跳过 6 分钟超时等 webhook 特有逻辑) */
144
+ wsMode?: boolean;
145
+ }
146
+ /**
147
+ * Webhook 入站消息(解密后的 JSON 格式)
148
+ *
149
+ * 字段命名对齐企微 Bot 回调的实际 JSON 结构(与原版 WecomBotInboundBase 一致):
150
+ * - 发送者:`from.userid`(非 from.user_id)
151
+ * - 会话类型:`chattype`(扁平字段,非 chat_info.chat_type)
152
+ * - 群聊 ID:`chatid`(扁平字段,非 chat_info.chat_id)
153
+ * - 事件类型:`event.eventtype`(非 event.event_type)
154
+ * - 流 ID:`stream.id`(非 stream.stream_id)
155
+ */
156
+ export interface WebhookInboundMessage {
157
+ msgtype: string;
158
+ msgid?: string;
159
+ /** 企微 Bot 回调中的机器人 ID */
160
+ aibotid?: string;
161
+ /** 会话类型:single | group(扁平字段) */
162
+ chattype?: "single" | "group";
163
+ /** 群聊 ID(扁平字段,仅群组时存在) */
164
+ chatid?: string;
165
+ /** 附件数量 */
166
+ attachment_count?: number;
167
+ text?: {
168
+ content?: string;
169
+ };
170
+ image?: {
171
+ url?: string;
172
+ aeskey?: string;
173
+ encrypt_file_key?: string;
174
+ file_url?: string;
175
+ base64?: string;
176
+ md5?: string;
177
+ };
178
+ file?: {
179
+ url?: string;
180
+ aeskey?: string;
181
+ encrypt_file_key?: string;
182
+ file_url?: string;
183
+ filename?: string;
184
+ file_name?: string;
185
+ fileName?: string;
186
+ };
187
+ voice?: {
188
+ content?: string;
189
+ url?: string;
190
+ aeskey?: string;
191
+ encrypt_file_key?: string;
192
+ file_url?: string;
193
+ };
194
+ video?: {
195
+ url?: string;
196
+ aeskey?: string;
197
+ encrypt_file_key?: string;
198
+ file_url?: string;
199
+ };
200
+ mixed?: {
201
+ msg_item: Array<{
202
+ msgtype: string;
203
+ text?: {
204
+ content?: string;
205
+ };
206
+ image?: {
207
+ url?: string;
208
+ aeskey?: string;
209
+ encrypt_file_key?: string;
210
+ file_url?: string;
211
+ base64?: string;
212
+ md5?: string;
213
+ };
214
+ file?: {
215
+ url?: string;
216
+ aeskey?: string;
217
+ encrypt_file_key?: string;
218
+ file_url?: string;
219
+ filename?: string;
220
+ };
221
+ [key: string]: unknown;
222
+ }>;
223
+ };
224
+ /** 引用消息 */
225
+ quote?: WebhookInboundQuote;
226
+ /** 发送者(企微 Bot 回调中 userid 是 from.userid) */
227
+ from?: {
228
+ userid?: string;
229
+ corpid?: string;
230
+ };
231
+ /** 事件(eventtype 非 event_type) */
232
+ event?: {
233
+ eventtype?: string;
234
+ event_key?: string;
235
+ template_card_event?: Record<string, unknown>;
236
+ [key: string]: unknown;
237
+ };
238
+ /** 流式消息 */
239
+ stream?: {
240
+ id?: string;
241
+ };
242
+ /** 下行回复 URL */
243
+ response_url?: string;
244
+ }
245
+ /**
246
+ * 引用消息结构(对齐原版 WecomInboundQuote)
247
+ */
248
+ export interface WebhookInboundQuote {
249
+ msgtype?: "text" | "image" | "mixed" | "voice" | "file" | "video";
250
+ text?: {
251
+ content?: string;
252
+ };
253
+ image?: {
254
+ url?: string;
255
+ };
256
+ mixed?: {
257
+ msg_item?: Array<{
258
+ msgtype: "text" | "image";
259
+ text?: {
260
+ content?: string;
261
+ };
262
+ image?: {
263
+ url?: string;
264
+ };
265
+ }>;
266
+ };
267
+ voice?: {
268
+ content?: string;
269
+ };
270
+ file?: {
271
+ url?: string;
272
+ };
273
+ video?: {
274
+ url?: string;
275
+ };
276
+ }
277
+ /**
278
+ * 待处理/防抖消息
279
+ *
280
+ * 暂存在队列中的消息,等待防抖计时器结束进行聚合。
281
+ */
282
+ export interface PendingInbound {
283
+ /** 预分配的流 ID */
284
+ streamId: string;
285
+ /** 会话标识 */
286
+ conversationKey: string;
287
+ /** 批次键 */
288
+ batchKey: string;
289
+ /** 目标 Webhook 上下文 */
290
+ target: WecomWebhookTarget;
291
+ /** 原始消息对象(如果聚合,通常指第一条) */
292
+ msg: WebhookInboundMessage;
293
+ /** 聚合的消息内容列表 */
294
+ contents: string[];
295
+ /** 附带的媒体文件(如果有) */
296
+ media?: {
297
+ buffer: Buffer;
298
+ contentType: string;
299
+ filename: string;
300
+ };
301
+ /** 聚合的所有消息 ID(用于去重) */
302
+ msgids: string[];
303
+ /** 回调 nonce */
304
+ nonce: string;
305
+ /** 回调 timestamp */
306
+ timestamp: string;
307
+ /** 防抖定时器句柄 */
308
+ timeout: ReturnType<typeof setTimeout> | null;
309
+ /** 已到达防抖截止时间,但因前序批次仍在处理中而暂存 */
310
+ readyToFlush?: boolean;
311
+ /** 创建时间 */
312
+ createdAt: number;
313
+ }
314
+ /**
315
+ * 主动回复地址状态
316
+ *
317
+ * 存储企业微信回调中提供的 response_url,用于后续主动推送。
318
+ */
319
+ export interface ActiveReplyState {
320
+ /** 企业微信提供的回调回复 URL */
321
+ response_url: string;
322
+ /** 如果配置了代理,存储代理地址 */
323
+ proxyUrl?: string;
324
+ /** 创建时间 */
325
+ createdAt: number;
326
+ /** 使用时间(仅当 policy="once" 时有意义) */
327
+ usedAt?: number;
328
+ /** 最后一次发送失败的错误信息 */
329
+ lastError?: string;
330
+ }
331
+ /**
332
+ * Webhook Gateway 上下文
333
+ */
334
+ export interface WebhookGatewayContext {
335
+ account: ResolvedWebhookAccount;
336
+ config: OpenClawConfig;
337
+ /** RuntimeEnv 日志环境 (来自 ChannelGatewayContext.runtime) */
338
+ runtime: RuntimeEnv;
339
+ /** PluginRuntime 用于访问 channel.reply 等核心功能 */
340
+ channelRuntime?: PluginRuntime;
341
+ abortSignal?: AbortSignal;
342
+ setStatus?: (next: Record<string, unknown>) => void;
343
+ log?: {
344
+ info: (msg: string) => void;
345
+ error: (msg: string) => void;
346
+ };
347
+ accountId: string;
348
+ }
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ /**
3
+ * Webhook 模式专用类型定义
4
+ *
5
+ * 从 @mocrane/wecom monitor/types.ts 完整迁移,适配目标项目类型体系。
6
+ */
7
+ exports.__esModule = true;
8
+ exports.WEBHOOK_PATHS = exports.PRUNE_INTERVAL_MS = exports.REQUEST_TIMEOUT_MS = exports.BOT_SWITCH_MARGIN_MS = exports.BOT_WINDOW_MS = exports.STREAM_MAX_BYTES = exports.DEFAULT_DEBOUNCE_MS = exports.ACTIVE_REPLY_TTL_MS = exports.STREAM_TTL_MS = void 0;
9
+ // ============================================================================
10
+ // 常量
11
+ // ============================================================================
12
+ /** StreamState 过期时间 (10 分钟) */
13
+ exports.STREAM_TTL_MS = 10 * 60 * 1000;
14
+ /** ActiveReply 过期时间 (1 小时) */
15
+ exports.ACTIVE_REPLY_TTL_MS = 60 * 60 * 1000;
16
+ /** 消息防抖间隔 (500ms) */
17
+ exports.DEFAULT_DEBOUNCE_MS = 500;
18
+ /** stream 回复最大字节数 (20KB) */
19
+ exports.STREAM_MAX_BYTES = 20480;
20
+ /** 企微 Bot 回复窗口 (6 分钟) */
21
+ exports.BOT_WINDOW_MS = 6 * 60 * 1000;
22
+ /** 超时安全边际 (30 秒) */
23
+ exports.BOT_SWITCH_MARGIN_MS = 30000;
24
+ /** HTTP 请求超时 (15 秒) */
25
+ exports.REQUEST_TIMEOUT_MS = 15000;
26
+ /** 自动清理间隔 (60 秒) */
27
+ exports.PRUNE_INTERVAL_MS = 60000;
28
+ /** 固定 Webhook 路径 */
29
+ exports.WEBHOOK_PATHS = {
30
+ /** Bot 模式历史兼容路径 */
31
+ BOT: "/wecom",
32
+ /** Bot 模式历史备用兼容路径 */
33
+ BOT_ALT: "/wecom/bot",
34
+ /** Bot 模式推荐路径前缀 */
35
+ BOT_PLUGIN: "/plugins/wecom/bot"
36
+ };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * 视频第一帧提取(ffmpeg)
3
+ *
4
+ * 独立文件,避免 child_process 与网络请求在同一文件中触发安全扫描误报。
5
+ */
6
+ /**
7
+ * 使用 ffmpeg 提取视频第一帧为 JPEG 图片。
8
+ *
9
+ * @param mediaPath 视频文件路径
10
+ * @param timeoutMs 超时时间(默认 10s)
11
+ * @returns 成功返回帧图片路径,失败或 ffmpeg 不可用返回 undefined
12
+ */
13
+ export declare function extractVideoFirstFrame(mediaPath: string, timeoutMs?: number): Promise<string | undefined>;
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ /**
3
+ * 视频第一帧提取(ffmpeg)
4
+ *
5
+ * 独立文件,避免 child_process 与网络请求在同一文件中触发安全扫描误报。
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || function (mod) {
20
+ if (mod && mod.__esModule) return mod;
21
+ var result = {};
22
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
23
+ __setModuleDefault(result, mod);
24
+ return result;
25
+ };
26
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
27
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
28
+ return new (P || (P = Promise))(function (resolve, reject) {
29
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
30
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
31
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
32
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
33
+ });
34
+ };
35
+ var __generator = (this && this.__generator) || function (thisArg, body) {
36
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
37
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
38
+ function verb(n) { return function (v) { return step([n, v]); }; }
39
+ function step(op) {
40
+ if (f) throw new TypeError("Generator is already executing.");
41
+ while (_) try {
42
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
43
+ if (y = 0, t) op = [op[0] & 2, t.value];
44
+ switch (op[0]) {
45
+ case 0: case 1: t = op; break;
46
+ case 4: _.label++; return { value: op[1], done: false };
47
+ case 5: _.label++; y = op[1]; op = [0]; continue;
48
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
49
+ default:
50
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
51
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
52
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
53
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
54
+ if (t[2]) _.ops.pop();
55
+ _.trys.pop(); continue;
56
+ }
57
+ op = body.call(thisArg, _);
58
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
59
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
60
+ }
61
+ };
62
+ exports.__esModule = true;
63
+ exports.extractVideoFirstFrame = void 0;
64
+ /**
65
+ * 使用 ffmpeg 提取视频第一帧为 JPEG 图片。
66
+ *
67
+ * @param mediaPath 视频文件路径
68
+ * @param timeoutMs 超时时间(默认 10s)
69
+ * @returns 成功返回帧图片路径,失败或 ffmpeg 不可用返回 undefined
70
+ */
71
+ function extractVideoFirstFrame(mediaPath, timeoutMs) {
72
+ if (timeoutMs === void 0) { timeoutMs = 10000; }
73
+ return __awaiter(this, void 0, void 0, function () {
74
+ var execFile, promisify, fs, execFileAsync, framePath, stat, _a;
75
+ return __generator(this, function (_b) {
76
+ switch (_b.label) {
77
+ case 0:
78
+ _b.trys.push([0, 6, , 7]);
79
+ return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("node:child_process")); })];
80
+ case 1:
81
+ execFile = (_b.sent()).execFile;
82
+ return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("node:util")); })];
83
+ case 2:
84
+ promisify = (_b.sent()).promisify;
85
+ return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require("node:fs/promises")); })];
86
+ case 3:
87
+ fs = _b.sent();
88
+ execFileAsync = promisify(execFile);
89
+ framePath = mediaPath.replace(/\.[^.]+$/, "_frame1.jpg");
90
+ return [4 /*yield*/, execFileAsync("ffmpeg", ["-i", mediaPath, "-vframes", "1", "-q:v", "2", "-y", framePath], { timeout: timeoutMs })];
91
+ case 4:
92
+ _b.sent();
93
+ return [4 /*yield*/, fs.stat(framePath)];
94
+ case 5:
95
+ stat = _b.sent();
96
+ if (stat.size > 0) {
97
+ return [2 /*return*/, framePath];
98
+ }
99
+ return [2 /*return*/, undefined];
100
+ case 6:
101
+ _a = _b.sent();
102
+ return [2 /*return*/, undefined];
103
+ case 7: return [2 /*return*/];
104
+ }
105
+ });
106
+ });
107
+ }
108
+ exports.extractVideoFirstFrame = extractVideoFirstFrame;
@@ -0,0 +1,19 @@
1
+ {
2
+ "id": "wecom-openclaw-plugin",
3
+ "channels": [
4
+ "wecom"
5
+ ],
6
+ "contracts": {
7
+ "tools": [
8
+ "wecom_mcp"
9
+ ]
10
+ },
11
+ "skills": [
12
+ "./skills"
13
+ ],
14
+ "configSchema": {
15
+ "type": "object",
16
+ "additionalProperties": false,
17
+ "properties": {}
18
+ }
19
+ }