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.
- package/README.md +596 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +99 -0
- package/dist/src/accounts.d.ts +57 -0
- package/dist/src/accounts.js +247 -0
- package/dist/src/agent/api-client.d.ts +95 -0
- package/dist/src/agent/api-client.js +425 -0
- package/dist/src/agent/handler.d.ts +64 -0
- package/dist/src/agent/handler.js +731 -0
- package/dist/src/agent/index.d.ts +5 -0
- package/dist/src/agent/index.js +21 -0
- package/dist/src/agent/webhook.d.ts +25 -0
- package/dist/src/agent/webhook.js +294 -0
- package/dist/src/agent/xml.d.ts +21 -0
- package/dist/src/agent/xml.js +43 -0
- package/dist/src/channel.d.ts +5 -0
- package/dist/src/channel.js +815 -0
- package/dist/src/chat-queue.d.ts +31 -0
- package/dist/src/chat-queue.js +53 -0
- package/dist/src/config-schema.d.ts +587 -0
- package/dist/src/config-schema.js +146 -0
- package/dist/src/const.d.ts +128 -0
- package/dist/src/const.js +168 -0
- package/dist/src/dm-policy.d.ts +29 -0
- package/dist/src/dm-policy.js +146 -0
- package/dist/src/dynamic-agent.d.ts +37 -0
- package/dist/src/dynamic-agent.js +67 -0
- package/dist/src/dynamic-routing.d.ts +65 -0
- package/dist/src/dynamic-routing.js +62 -0
- package/dist/src/endpoint-dispatch.d.ts +54 -0
- package/dist/src/endpoint-dispatch.js +967 -0
- package/dist/src/endpoint-event-adapter.d.ts +15 -0
- package/dist/src/endpoint-event-adapter.js +427 -0
- package/dist/src/group-policy.d.ts +30 -0
- package/dist/src/group-policy.js +126 -0
- package/dist/src/http.d.ts +27 -0
- package/dist/src/http.js +168 -0
- package/dist/src/im-runtime-telemetry.d.ts +25 -0
- package/dist/src/im-runtime-telemetry.js +68 -0
- package/dist/src/interface.d.ts +192 -0
- package/dist/src/interface.js +5 -0
- package/dist/src/markdown-chunk.d.ts +1 -0
- package/dist/src/markdown-chunk.js +396 -0
- package/dist/src/mcp/index.d.ts +6 -0
- package/dist/src/mcp/index.js +28 -0
- package/dist/src/mcp/interceptors/biz-error.d.ts +11 -0
- package/dist/src/mcp/interceptors/biz-error.js +73 -0
- package/dist/src/mcp/interceptors/doc-auth-error.d.ts +10 -0
- package/dist/src/mcp/interceptors/doc-auth-error.js +235 -0
- package/dist/src/mcp/interceptors/index.d.ts +35 -0
- package/dist/src/mcp/interceptors/index.js +143 -0
- package/dist/src/mcp/interceptors/msg-media.d.ts +11 -0
- package/dist/src/mcp/interceptors/msg-media.js +201 -0
- package/dist/src/mcp/interceptors/smartpage-create.d.ts +30 -0
- package/dist/src/mcp/interceptors/smartpage-create.js +252 -0
- package/dist/src/mcp/interceptors/smartpage-export.d.ts +17 -0
- package/dist/src/mcp/interceptors/smartpage-export.js +135 -0
- package/dist/src/mcp/interceptors/smartsheet-upload.d.ts +22 -0
- package/dist/src/mcp/interceptors/smartsheet-upload.js +388 -0
- package/dist/src/mcp/interceptors/types.d.ts +64 -0
- package/dist/src/mcp/interceptors/types.js +8 -0
- package/dist/src/mcp/schema.d.ts +11 -0
- package/dist/src/mcp/schema.js +115 -0
- package/dist/src/mcp/tool.d.ts +63 -0
- package/dist/src/mcp/tool.js +318 -0
- package/dist/src/mcp/transport.d.ts +94 -0
- package/dist/src/mcp/transport.js +702 -0
- package/dist/src/media-handler.d.ts +55 -0
- package/dist/src/media-handler.js +306 -0
- package/dist/src/media-uploader.d.ts +142 -0
- package/dist/src/media-uploader.js +446 -0
- package/dist/src/message-parser.d.ts +104 -0
- package/dist/src/message-parser.js +232 -0
- package/dist/src/message-sender.d.ts +54 -0
- package/dist/src/message-sender.js +210 -0
- package/dist/src/monitor.d.ts +69 -0
- package/dist/src/monitor.js +1846 -0
- package/dist/src/onboarding.d.ts +8 -0
- package/dist/src/onboarding.js +248 -0
- package/dist/src/openclaw-compat.d.ts +148 -0
- package/dist/src/openclaw-compat.js +839 -0
- package/dist/src/proactive-markdown-send.d.ts +14 -0
- package/dist/src/proactive-markdown-send.js +205 -0
- package/dist/src/reqid-store.d.ts +23 -0
- package/dist/src/reqid-store.js +136 -0
- package/dist/src/runtime.d.ts +2 -0
- package/dist/src/runtime.js +7 -0
- package/dist/src/shared/command-auth.d.ts +23 -0
- package/dist/src/shared/command-auth.js +112 -0
- package/dist/src/shared/xml-parser.d.ts +46 -0
- package/dist/src/shared/xml-parser.js +228 -0
- package/dist/src/state-dir-resolve.d.ts +2 -0
- package/dist/src/state-dir-resolve.js +33 -0
- package/dist/src/state-manager.d.ts +115 -0
- package/dist/src/state-manager.js +413 -0
- package/dist/src/target.d.ts +35 -0
- package/dist/src/target.js +71 -0
- package/dist/src/template-card-manager.d.ts +55 -0
- package/dist/src/template-card-manager.js +316 -0
- package/dist/src/template-card-parser.d.ts +37 -0
- package/dist/src/template-card-parser.js +672 -0
- package/dist/src/timeout.d.ts +20 -0
- package/dist/src/timeout.js +57 -0
- package/dist/src/types/account.d.ts +29 -0
- package/dist/src/types/account.js +5 -0
- package/dist/src/types/config.d.ts +98 -0
- package/dist/src/types/config.js +8 -0
- package/dist/src/types/constants.d.ts +42 -0
- package/dist/src/types/constants.js +45 -0
- package/dist/src/types/index.d.ts +7 -0
- package/dist/src/types/index.js +17 -0
- package/dist/src/types/message.d.ts +238 -0
- package/dist/src/types/message.js +6 -0
- package/dist/src/utils.d.ts +148 -0
- package/dist/src/utils.js +92 -0
- package/dist/src/version.d.ts +2 -0
- package/dist/src/version.js +28 -0
- package/dist/src/webhook/command-auth.d.ts +47 -0
- package/dist/src/webhook/command-auth.js +137 -0
- package/dist/src/webhook/gateway.d.ts +36 -0
- package/dist/src/webhook/gateway.js +297 -0
- package/dist/src/webhook/handler.d.ts +19 -0
- package/dist/src/webhook/handler.js +481 -0
- package/dist/src/webhook/helpers.d.ts +157 -0
- package/dist/src/webhook/helpers.js +936 -0
- package/dist/src/webhook/http.d.ts +27 -0
- package/dist/src/webhook/http.js +168 -0
- package/dist/src/webhook/index.d.ts +11 -0
- package/dist/src/webhook/index.js +43 -0
- package/dist/src/webhook/media.d.ts +30 -0
- package/dist/src/webhook/media.js +152 -0
- package/dist/src/webhook/monitor.d.ts +59 -0
- package/dist/src/webhook/monitor.js +1672 -0
- package/dist/src/webhook/state.d.ts +220 -0
- package/dist/src/webhook/state.js +568 -0
- package/dist/src/webhook/target.d.ts +41 -0
- package/dist/src/webhook/target.js +165 -0
- package/dist/src/webhook/types.d.ts +348 -0
- package/dist/src/webhook/types.js +36 -0
- package/dist/src/webhook/video-frame.d.ts +13 -0
- package/dist/src/webhook/video-frame.js +108 -0
- package/openclaw.plugin.json +19 -0
- package/package.json +96 -0
- package/schema.json +534 -0
- package/scripts/generate-schema.mjs +33 -0
- package/skills/wecom-contact/SKILL.md +162 -0
- package/skills/wecom-doc/SKILL.md +162 -0
- package/skills/wecom-doc/references/create-doc.md +56 -0
- package/skills/wecom-doc/references/edit-doc-content.md +68 -0
- package/skills/wecom-doc/references/get-doc-content.md +88 -0
- package/skills/wecom-doc/references/smartpage-create.md +125 -0
- package/skills/wecom-doc/references/smartpage-export.md +160 -0
- package/skills/wecom-meeting/SKILL.md +441 -0
- package/skills/wecom-meeting/references/example-full.md +30 -0
- package/skills/wecom-meeting/references/example-reminder.md +46 -0
- package/skills/wecom-meeting/references/example-security.md +22 -0
- package/skills/wecom-meeting/references/response-get-meeting-info.md +148 -0
- package/skills/wecom-msg/SKILL.md +157 -0
- package/skills/wecom-msg/references/api-get-messages.md +93 -0
- package/skills/wecom-msg/references/api-get-msg-chat-list.md +58 -0
- package/skills/wecom-msg/references/api-get-msg-media.md +44 -0
- package/skills/wecom-msg/references/api-send-message.md +39 -0
- package/skills/wecom-preflight/SKILL.md +141 -0
- package/skills/wecom-schedule/SKILL.md +161 -0
- package/skills/wecom-schedule/references/api-check-availability.md +56 -0
- package/skills/wecom-schedule/references/api-create-schedule.md +38 -0
- package/skills/wecom-schedule/references/api-get-schedule-detail.md +81 -0
- package/skills/wecom-schedule/references/api-update-schedule.md +32 -0
- package/skills/wecom-schedule/references/ref-reminders.md +24 -0
- package/skills/wecom-send-media/SKILL.md +68 -0
- package/skills/wecom-send-template-card/SKILL.md +157 -0
- package/skills/wecom-send-template-card/references/api-template-card-types.md +358 -0
- package/skills/wecom-smartsheet/SKILL.md +164 -0
- package/skills/wecom-smartsheet/references/smartsheet-cell-value-formats.md +163 -0
- package/skills/wecom-smartsheet/references/smartsheet-field-types.md +44 -0
- package/skills/wecom-smartsheet/references/smartsheet-get-records.md +96 -0
- package/skills/wecom-smartsheet/references/webhook-examples.md +185 -0
- package/skills/wecom-smartsheet/references/webhook-fallback.md +184 -0
- package/skills/wecom-todo/SKILL.md +392 -0
- package/skills/wecom-todo/examples/workflows.md +163 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 企业微信消息内容解析模块
|
|
4
|
+
*
|
|
5
|
+
* 负责从 WsFrame 中提取文本、图片、引用等内容
|
|
6
|
+
*/
|
|
7
|
+
var __spreadArrays = (this && this.__spreadArrays) || function () {
|
|
8
|
+
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
|
|
9
|
+
for (var r = Array(s), k = 0, i = 0; i < il; i++)
|
|
10
|
+
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
|
|
11
|
+
r[k] = a[j];
|
|
12
|
+
return r;
|
|
13
|
+
};
|
|
14
|
+
var _a;
|
|
15
|
+
exports.__esModule = true;
|
|
16
|
+
exports.parseMessageContent = void 0;
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// 解析函数
|
|
19
|
+
// ============================================================================
|
|
20
|
+
/**
|
|
21
|
+
* 将模板卡片事件回调格式化为可继续路由给大模型的文本。
|
|
22
|
+
*
|
|
23
|
+
* 这样后续 Agent 可以直接从 question_key / option_id 中理解用户的真实选择。
|
|
24
|
+
*/
|
|
25
|
+
function buildTemplateCardEventText(body) {
|
|
26
|
+
var _a, _b, _c, _d, _e, _f;
|
|
27
|
+
var templateCardEvent = (_a = body.event) === null || _a === void 0 ? void 0 : _a.template_card_event;
|
|
28
|
+
if (body.msgtype !== "event" ||
|
|
29
|
+
((_b = body.event) === null || _b === void 0 ? void 0 : _b.eventtype) !== "template_card_event" ||
|
|
30
|
+
!templateCardEvent) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
var selectedItems = (_d = (_c = templateCardEvent.selected_items) === null || _c === void 0 ? void 0 : _c.selected_item) !== null && _d !== void 0 ? _d : [];
|
|
34
|
+
var selectedLines = selectedItems.map(function (item) {
|
|
35
|
+
var _a, _b, _c, _d;
|
|
36
|
+
var questionKey = ((_a = item.question_key) === null || _a === void 0 ? void 0 : _a.trim()) || "unknown_question";
|
|
37
|
+
var optionIds = (_d = (_c = (_b = item.option_ids) === null || _b === void 0 ? void 0 : _b.option_id) === null || _c === void 0 ? void 0 : _c.filter(Boolean)) !== null && _d !== void 0 ? _d : [];
|
|
38
|
+
return "- " + questionKey + ": " + (optionIds.length > 0 ? optionIds.join(", ") : "(未选择)");
|
|
39
|
+
});
|
|
40
|
+
var senderUserId = ((_e = body.from) === null || _e === void 0 ? void 0 : _e.userid) || "";
|
|
41
|
+
var senderCorpId = ((_f = body.from) === null || _f === void 0 ? void 0 : _f.corpid) || "";
|
|
42
|
+
var chatId = body.chatid || senderUserId;
|
|
43
|
+
return __spreadArrays([
|
|
44
|
+
"[企业微信模板卡片回调]",
|
|
45
|
+
"event_type(\u4E8B\u4EF6\u7C7B\u578B): template_card_event",
|
|
46
|
+
body.msgid ? "msgid(\u6D88\u606F id): " + body.msgid : undefined,
|
|
47
|
+
body.aibotid ? "aibotid(\u673A\u5668\u4EBA id): " + body.aibotid : undefined,
|
|
48
|
+
body.chattype ? "chat_type(\u4F1A\u8BDD\u7C7B\u578B): " + body.chattype : undefined,
|
|
49
|
+
chatId ? "chat_id(\u4F1A\u8BDD id): " + chatId : undefined,
|
|
50
|
+
senderCorpId ? "from.corpid(\u4F01\u4E1A id): " + senderCorpId : undefined,
|
|
51
|
+
senderUserId ? "from.userid(\u53D1\u9001\u4EBA id): " + senderUserId : undefined,
|
|
52
|
+
senderUserId ? "sender_userid(\u53D1\u9001\u4EBA id): " + senderUserId : undefined,
|
|
53
|
+
templateCardEvent.card_type ? "card_type(\u5361\u7247\u7C7B\u578B): " + templateCardEvent.card_type : undefined,
|
|
54
|
+
templateCardEvent.event_key ? "event_key(\u4E8B\u4EF6 key): " + templateCardEvent.event_key : undefined,
|
|
55
|
+
templateCardEvent.task_id ? "task_id(\u4EFB\u52A1 id): " + templateCardEvent.task_id : undefined,
|
|
56
|
+
selectedLines.length > 0 ? "selected_items(选择项):" : "selected_items(选择项): []"
|
|
57
|
+
], selectedLines).filter(function (line) { return Boolean(line); })
|
|
58
|
+
.join("\n");
|
|
59
|
+
}
|
|
60
|
+
// ============================================================================
|
|
61
|
+
// 权限类型映射
|
|
62
|
+
// ============================================================================
|
|
63
|
+
/** 权限类型枚举 */
|
|
64
|
+
var AuthType;
|
|
65
|
+
(function (AuthType) {
|
|
66
|
+
/** 新建和编辑文档 */
|
|
67
|
+
AuthType[AuthType["CREATE_AND_EDIT_DOC"] = 1] = "CREATE_AND_EDIT_DOC";
|
|
68
|
+
/** 获取成员文档内容 */
|
|
69
|
+
AuthType[AuthType["GET_DOC_CONTENT"] = 2] = "GET_DOC_CONTENT";
|
|
70
|
+
})(AuthType || (AuthType = {}));
|
|
71
|
+
/** 权限类型值 → 中文描述 */
|
|
72
|
+
var AUTH_TYPE_MAP = (_a = {},
|
|
73
|
+
_a[AuthType.CREATE_AND_EDIT_DOC] = "新建和编辑文档",
|
|
74
|
+
_a[AuthType.GET_DOC_CONTENT] = "获取成员文档内容",
|
|
75
|
+
_a);
|
|
76
|
+
/**
|
|
77
|
+
* 将权限变更事件回调格式化为可继续路由给大模型的文本。
|
|
78
|
+
*
|
|
79
|
+
* 当管理员在授权页面变更权限后,系统收到 auth_change_event 回调,
|
|
80
|
+
* 根据 auth_list 生成对应的提示文本,引导 Agent 继续操作。
|
|
81
|
+
*/
|
|
82
|
+
function buildAuthChangeEventText(body) {
|
|
83
|
+
var _a, _b, _c, _d, _e, _f;
|
|
84
|
+
console.log("authChangeEventCheck", body.event);
|
|
85
|
+
var authChangeEvent = (_a = body.event) === null || _a === void 0 ? void 0 : _a.auth_change_event;
|
|
86
|
+
if (body.msgtype !== "event" ||
|
|
87
|
+
((_b = body.event) === null || _b === void 0 ? void 0 : _b.eventtype) !== "auth_change_event" ||
|
|
88
|
+
!authChangeEvent) {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
var authList = (_c = authChangeEvent.auth_list) !== null && _c !== void 0 ? _c : [];
|
|
92
|
+
var authDescriptions = authList
|
|
93
|
+
.map(function (code) { return AUTH_TYPE_MAP[code] || "\u672A\u77E5\u6743\u9650(" + code + ")"; })
|
|
94
|
+
.join("、");
|
|
95
|
+
// 根据权限列表内容生成不同的操作指引
|
|
96
|
+
var hasDocContentAuth = authList.includes(AuthType.GET_DOC_CONTENT);
|
|
97
|
+
var actionHint;
|
|
98
|
+
if (hasDocContentAuth) {
|
|
99
|
+
// 包含"获取成员文档内容"权限,引导 Agent 继续文档操作
|
|
100
|
+
actionHint = "用户已授予文档内容读取权限,请继续之前的文档操作。";
|
|
101
|
+
}
|
|
102
|
+
else if (authList.length > 0) {
|
|
103
|
+
// 有其他权限但没有文档内容读取权限
|
|
104
|
+
actionHint = "当前授权不包含文档内容读取权限,无法继续文档操作。请引导用户授予「获取成员文档内容」权限,该权限需要向管理员申请,管理员审批通过后可使用。";
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
// 权限列表为空
|
|
108
|
+
actionHint = "当前无任何文档权限,无法继续文档操作。请引导用户完成文档授权。";
|
|
109
|
+
}
|
|
110
|
+
var senderUserId = ((_d = body.from) === null || _d === void 0 ? void 0 : _d.userid) || "";
|
|
111
|
+
var senderCorpId = ((_e = body.from) === null || _e === void 0 ? void 0 : _e.corpid) || "";
|
|
112
|
+
var chatId = ((_f = body.from) === null || _f === void 0 ? void 0 : _f.chat_id) || body.chatid || senderUserId;
|
|
113
|
+
return [
|
|
114
|
+
"[企业微信文档权限变更回调]",
|
|
115
|
+
"event_type(\u4E8B\u4EF6\u7C7B\u578B): auth_change_event",
|
|
116
|
+
"auth_list(\u5F53\u524D\u6743\u9650\u5217\u8868): [" + authList.join(", ") + "] (" + (authDescriptions || "无") + ")",
|
|
117
|
+
body.msgid ? "msgid(\u6D88\u606F id): " + body.msgid : undefined,
|
|
118
|
+
body.aibotid ? "aibotid(\u673A\u5668\u4EBA id): " + body.aibotid : undefined,
|
|
119
|
+
body.chattype ? "chat_type(\u4F1A\u8BDD\u7C7B\u578B): " + body.chattype : undefined,
|
|
120
|
+
chatId ? "chat_id(\u4F1A\u8BDD id): " + chatId : undefined,
|
|
121
|
+
senderCorpId ? "from.corpid(\u4F01\u4E1A id): " + senderCorpId : undefined,
|
|
122
|
+
senderUserId ? "from.userid(\u53D1\u9001\u4EBA id): " + senderUserId : undefined,
|
|
123
|
+
"",
|
|
124
|
+
"[\u64CD\u4F5C\u6307\u5F15] " + actionHint,
|
|
125
|
+
]
|
|
126
|
+
.filter(function (line) { return line !== undefined; })
|
|
127
|
+
.join("\n");
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* 解析消息内容(支持单条消息、图文混排、事件回调和引用消息)
|
|
131
|
+
* @returns 提取的文本数组、图片URL数组和引用消息内容
|
|
132
|
+
*/
|
|
133
|
+
function parseMessageContent(body) {
|
|
134
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
135
|
+
var textParts = [];
|
|
136
|
+
var imageUrls = [];
|
|
137
|
+
var imageAesKeys = new Map();
|
|
138
|
+
var fileUrls = [];
|
|
139
|
+
var fileAesKeys = new Map();
|
|
140
|
+
var quoteContent;
|
|
141
|
+
if (body.msgtype === "event") {
|
|
142
|
+
// 处理权限变更事件回调(如文档授权)
|
|
143
|
+
var authChangeText = buildAuthChangeEventText(body);
|
|
144
|
+
if (authChangeText) {
|
|
145
|
+
textParts.push(authChangeText);
|
|
146
|
+
return { textParts: textParts, imageUrls: imageUrls, imageAesKeys: imageAesKeys, fileUrls: fileUrls, fileAesKeys: fileAesKeys, quoteContent: quoteContent };
|
|
147
|
+
}
|
|
148
|
+
// 处理模板卡片事件回调
|
|
149
|
+
var eventText = buildTemplateCardEventText(body);
|
|
150
|
+
if (eventText) {
|
|
151
|
+
textParts.push(eventText);
|
|
152
|
+
}
|
|
153
|
+
return { textParts: textParts, imageUrls: imageUrls, imageAesKeys: imageAesKeys, fileUrls: fileUrls, fileAesKeys: fileAesKeys, quoteContent: quoteContent };
|
|
154
|
+
}
|
|
155
|
+
// 处理图文混排消息
|
|
156
|
+
if (body.msgtype === "mixed" && ((_a = body.mixed) === null || _a === void 0 ? void 0 : _a.msg_item)) {
|
|
157
|
+
for (var _i = 0, _p = body.mixed.msg_item; _i < _p.length; _i++) {
|
|
158
|
+
var item = _p[_i];
|
|
159
|
+
if (item.msgtype === "text" && ((_b = item.text) === null || _b === void 0 ? void 0 : _b.content)) {
|
|
160
|
+
textParts.push(item.text.content);
|
|
161
|
+
}
|
|
162
|
+
else if (item.msgtype === "image" && ((_c = item.image) === null || _c === void 0 ? void 0 : _c.url)) {
|
|
163
|
+
imageUrls.push(item.image.url);
|
|
164
|
+
if (item.image.aeskey) {
|
|
165
|
+
imageAesKeys.set(item.image.url, item.image.aeskey);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
// 处理单条消息
|
|
172
|
+
if ((_d = body.text) === null || _d === void 0 ? void 0 : _d.content) {
|
|
173
|
+
textParts.push(body.text.content);
|
|
174
|
+
}
|
|
175
|
+
// 处理语音消息(语音转文字后的文本内容)
|
|
176
|
+
if (body.msgtype === "voice" && ((_e = body.voice) === null || _e === void 0 ? void 0 : _e.content)) {
|
|
177
|
+
textParts.push(body.voice.content);
|
|
178
|
+
}
|
|
179
|
+
if ((_f = body.image) === null || _f === void 0 ? void 0 : _f.url) {
|
|
180
|
+
imageUrls.push(body.image.url);
|
|
181
|
+
if (body.image.aeskey) {
|
|
182
|
+
imageAesKeys.set(body.image.url, body.image.aeskey);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// 处理文件消息
|
|
186
|
+
if (body.msgtype === "file" && ((_g = body.file) === null || _g === void 0 ? void 0 : _g.url)) {
|
|
187
|
+
fileUrls.push(body.file.url);
|
|
188
|
+
if (body.file.aeskey) {
|
|
189
|
+
fileAesKeys.set(body.file.url, body.file.aeskey);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// 处理视频消息(沿用 file 下载/解密通路,作为文件附件透传)
|
|
193
|
+
if (body.msgtype === "video" && ((_h = body.video) === null || _h === void 0 ? void 0 : _h.url)) {
|
|
194
|
+
fileUrls.push(body.video.url);
|
|
195
|
+
if (body.video.aeskey) {
|
|
196
|
+
fileAesKeys.set(body.video.url, body.video.aeskey);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// 处理引用消息
|
|
201
|
+
if (body.quote) {
|
|
202
|
+
if (body.quote.msgtype === "text" && ((_j = body.quote.text) === null || _j === void 0 ? void 0 : _j.content)) {
|
|
203
|
+
quoteContent = body.quote.text.content;
|
|
204
|
+
}
|
|
205
|
+
else if (body.quote.msgtype === "voice" && ((_k = body.quote.voice) === null || _k === void 0 ? void 0 : _k.content)) {
|
|
206
|
+
quoteContent = body.quote.voice.content;
|
|
207
|
+
}
|
|
208
|
+
else if (body.quote.msgtype === "image" && ((_l = body.quote.image) === null || _l === void 0 ? void 0 : _l.url)) {
|
|
209
|
+
// 引用的图片消息:将图片 URL 加入下载列表
|
|
210
|
+
imageUrls.push(body.quote.image.url);
|
|
211
|
+
if (body.quote.image.aeskey) {
|
|
212
|
+
imageAesKeys.set(body.quote.image.url, body.quote.image.aeskey);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else if (body.quote.msgtype === "file" && ((_m = body.quote.file) === null || _m === void 0 ? void 0 : _m.url)) {
|
|
216
|
+
// 引用的文件消息:将文件 URL 加入下载列表
|
|
217
|
+
fileUrls.push(body.quote.file.url);
|
|
218
|
+
if (body.quote.file.aeskey) {
|
|
219
|
+
fileAesKeys.set(body.quote.file.url, body.quote.file.aeskey);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
else if (body.quote.msgtype === "video" && ((_o = body.quote.video) === null || _o === void 0 ? void 0 : _o.url)) {
|
|
223
|
+
// 引用的视频消息:沿用文件下载通路
|
|
224
|
+
fileUrls.push(body.quote.video.url);
|
|
225
|
+
if (body.quote.video.aeskey) {
|
|
226
|
+
fileAesKeys.set(body.quote.video.url, body.quote.video.aeskey);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return { textParts: textParts, imageUrls: imageUrls, imageAesKeys: imageAesKeys, fileUrls: fileUrls, fileAesKeys: fileAesKeys, quoteContent: quoteContent };
|
|
231
|
+
}
|
|
232
|
+
exports.parseMessageContent = parseMessageContent;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 企业微信消息发送模块
|
|
3
|
+
*
|
|
4
|
+
* 负责通过 WSClient 发送回复消息,包含超时保护
|
|
5
|
+
*/
|
|
6
|
+
import type { RuntimeEnv } from "ylib-openclaw/plugin-sdk";
|
|
7
|
+
import { WSClient, WsFrame } from "@wecom/aibot-node-sdk";
|
|
8
|
+
/** 流式回复超时错误码(超过企业微信服务端有效期后,服务端拒绝继续更新) */
|
|
9
|
+
export declare const STREAM_EXPIRED_ERRCODE = 846608;
|
|
10
|
+
/**
|
|
11
|
+
* 流式回复过期错误
|
|
12
|
+
* 当服务端返回 errcode=846608 时抛出,表示流式消息已超过企业微信服务端有效期无法更新,
|
|
13
|
+
* 调用方需降级为主动发送(sendMessage)方式回复。
|
|
14
|
+
*/
|
|
15
|
+
export declare class StreamExpiredError extends Error {
|
|
16
|
+
readonly errcode = 846608;
|
|
17
|
+
constructor(message?: string);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* 发送企业微信回复消息
|
|
21
|
+
* 供 monitor 内部和 channel outbound 使用
|
|
22
|
+
*
|
|
23
|
+
* @returns messageId (streamId)
|
|
24
|
+
*/
|
|
25
|
+
export declare function sendWeComReply(params: {
|
|
26
|
+
wsClient: WSClient;
|
|
27
|
+
frame: WsFrame;
|
|
28
|
+
text?: string;
|
|
29
|
+
runtime: RuntimeEnv;
|
|
30
|
+
/** 是否为流式回复的最终消息,默认为 true */
|
|
31
|
+
finish?: boolean;
|
|
32
|
+
/** 指定 streamId,用于流式回复时保持相同的 streamId */
|
|
33
|
+
streamId?: string;
|
|
34
|
+
accountId?: string;
|
|
35
|
+
}): Promise<string>;
|
|
36
|
+
/**
|
|
37
|
+
* 非阻塞流式文本回复
|
|
38
|
+
*
|
|
39
|
+
* 基于 SDK 的 replyStreamNonBlocking 方法:
|
|
40
|
+
* - 如果上一条同 reqId 的消息尚未收到 ack,则跳过本次发送(返回 'skipped'),
|
|
41
|
+
* 避免流式中间帧排队积压导致延迟。
|
|
42
|
+
* - finish=true 的最终帧不受此限制,始终保证发送。
|
|
43
|
+
*
|
|
44
|
+
* @returns 'skipped' 表示被跳过,否则返回 streamId
|
|
45
|
+
*/
|
|
46
|
+
export declare function sendWeComReplyNonBlocking(params: {
|
|
47
|
+
wsClient: WSClient;
|
|
48
|
+
frame: WsFrame;
|
|
49
|
+
text: string;
|
|
50
|
+
runtime: RuntimeEnv;
|
|
51
|
+
streamId: string;
|
|
52
|
+
finish?: boolean;
|
|
53
|
+
accountId?: string;
|
|
54
|
+
}): Promise<string | 'skipped'>;
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 企业微信消息发送模块
|
|
4
|
+
*
|
|
5
|
+
* 负责通过 WSClient 发送回复消息,包含超时保护
|
|
6
|
+
*/
|
|
7
|
+
var __extends = (this && this.__extends) || (function () {
|
|
8
|
+
var extendStatics = function (d, b) {
|
|
9
|
+
extendStatics = Object.setPrototypeOf ||
|
|
10
|
+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
11
|
+
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
|
12
|
+
return extendStatics(d, b);
|
|
13
|
+
};
|
|
14
|
+
return function (d, b) {
|
|
15
|
+
extendStatics(d, b);
|
|
16
|
+
function __() { this.constructor = d; }
|
|
17
|
+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
18
|
+
};
|
|
19
|
+
})();
|
|
20
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
21
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
22
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
23
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
24
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
25
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
26
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
30
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
31
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
32
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
33
|
+
function step(op) {
|
|
34
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
35
|
+
while (_) try {
|
|
36
|
+
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;
|
|
37
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
38
|
+
switch (op[0]) {
|
|
39
|
+
case 0: case 1: t = op; break;
|
|
40
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
41
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
42
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
43
|
+
default:
|
|
44
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
45
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
46
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
47
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
48
|
+
if (t[2]) _.ops.pop();
|
|
49
|
+
_.trys.pop(); continue;
|
|
50
|
+
}
|
|
51
|
+
op = body.call(thisArg, _);
|
|
52
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
53
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
exports.__esModule = true;
|
|
57
|
+
exports.sendWeComReplyNonBlocking = exports.sendWeComReply = exports.StreamExpiredError = exports.STREAM_EXPIRED_ERRCODE = void 0;
|
|
58
|
+
var aibot_node_sdk_1 = require("@wecom/aibot-node-sdk");
|
|
59
|
+
var const_js_1 = require("./const.js");
|
|
60
|
+
var timeout_js_1 = require("./timeout.js");
|
|
61
|
+
var im_runtime_telemetry_js_1 = require("./im-runtime-telemetry.js");
|
|
62
|
+
var proactive_markdown_send_js_1 = require("./proactive-markdown-send.js");
|
|
63
|
+
// ============================================================================
|
|
64
|
+
// 流式过期错误(errcode 846608)
|
|
65
|
+
// ============================================================================
|
|
66
|
+
/** 流式回复超时错误码(超过企业微信服务端有效期后,服务端拒绝继续更新) */
|
|
67
|
+
exports.STREAM_EXPIRED_ERRCODE = 846608;
|
|
68
|
+
/**
|
|
69
|
+
* 流式回复过期错误
|
|
70
|
+
* 当服务端返回 errcode=846608 时抛出,表示流式消息已超过企业微信服务端有效期无法更新,
|
|
71
|
+
* 调用方需降级为主动发送(sendMessage)方式回复。
|
|
72
|
+
*/
|
|
73
|
+
var StreamExpiredError = /** @class */ (function (_super) {
|
|
74
|
+
__extends(StreamExpiredError, _super);
|
|
75
|
+
function StreamExpiredError(message) {
|
|
76
|
+
var _this = _super.call(this, message !== null && message !== void 0 ? message : "Stream message update expired (errcode=" + exports.STREAM_EXPIRED_ERRCODE + ")") || this;
|
|
77
|
+
_this.errcode = exports.STREAM_EXPIRED_ERRCODE;
|
|
78
|
+
_this.name = "StreamExpiredError";
|
|
79
|
+
return _this;
|
|
80
|
+
}
|
|
81
|
+
return StreamExpiredError;
|
|
82
|
+
}(Error));
|
|
83
|
+
exports.StreamExpiredError = StreamExpiredError;
|
|
84
|
+
// ============================================================================
|
|
85
|
+
// 消息发送
|
|
86
|
+
// ============================================================================
|
|
87
|
+
/**
|
|
88
|
+
* 发送企业微信回复消息
|
|
89
|
+
* 供 monitor 内部和 channel outbound 使用
|
|
90
|
+
*
|
|
91
|
+
* @returns messageId (streamId)
|
|
92
|
+
*/
|
|
93
|
+
function sendWeComReply(params) {
|
|
94
|
+
var _a, _b, _c, _d, _e;
|
|
95
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
96
|
+
var wsClient, frame, text, runtime, _f, finish, existingStreamId, accountId, streamId, body, chatId, err_1, errMsg;
|
|
97
|
+
return __generator(this, function (_g) {
|
|
98
|
+
switch (_g.label) {
|
|
99
|
+
case 0:
|
|
100
|
+
wsClient = params.wsClient, frame = params.frame, text = params.text, runtime = params.runtime, _f = params.finish, finish = _f === void 0 ? true : _f, existingStreamId = params.streamId, accountId = params.accountId;
|
|
101
|
+
if (!text) {
|
|
102
|
+
return [2 /*return*/, ""];
|
|
103
|
+
}
|
|
104
|
+
streamId = existingStreamId || aibot_node_sdk_1.generateReqId("stream");
|
|
105
|
+
if (!wsClient.isConnected) {
|
|
106
|
+
(_a = runtime.error) === null || _a === void 0 ? void 0 : _a.call(runtime, "[wecom] WSClient not connected, cannot send reply");
|
|
107
|
+
throw new Error("WSClient not connected");
|
|
108
|
+
}
|
|
109
|
+
body = frame.body;
|
|
110
|
+
if (!(body.msgtype === "event")) return [3 /*break*/, 2];
|
|
111
|
+
// 中间帧(thinking / 流式增量)直接跳过,仅在最终帧主动发一次文本。
|
|
112
|
+
if (!finish) {
|
|
113
|
+
(_b = runtime.log) === null || _b === void 0 ? void 0 : _b.call(runtime, "[plugin -> server] skip non-final stream for event callback, streamId=" + streamId);
|
|
114
|
+
return [2 /*return*/, streamId];
|
|
115
|
+
}
|
|
116
|
+
chatId = body.chatid || ((_c = body.from) === null || _c === void 0 ? void 0 : _c.userid);
|
|
117
|
+
if (!chatId) {
|
|
118
|
+
throw new Error("Missing chatId for event callback reply");
|
|
119
|
+
}
|
|
120
|
+
return [4 /*yield*/, proactive_markdown_send_js_1.sendWeComMarkdownMessageChunks({
|
|
121
|
+
wsClient: wsClient,
|
|
122
|
+
chatId: chatId,
|
|
123
|
+
text: text,
|
|
124
|
+
runtime: runtime,
|
|
125
|
+
accountId: accountId,
|
|
126
|
+
label: "event-active-send",
|
|
127
|
+
timeoutMs: const_js_1.REPLY_SEND_TIMEOUT_MS
|
|
128
|
+
})];
|
|
129
|
+
case 1:
|
|
130
|
+
_g.sent();
|
|
131
|
+
(_d = runtime.log) === null || _d === void 0 ? void 0 : _d.call(runtime, "[plugin -> server] event-active-send chatId=" + chatId + ", finish=" + finish);
|
|
132
|
+
return [2 /*return*/, streamId];
|
|
133
|
+
case 2:
|
|
134
|
+
_g.trys.push([2, 4, , 5]);
|
|
135
|
+
return [4 /*yield*/, timeout_js_1.withTimeout(wsClient.replyStream(frame, streamId, text, finish), const_js_1.REPLY_SEND_TIMEOUT_MS, "Reply send timed out (streamId=" + streamId + ")")];
|
|
136
|
+
case 3:
|
|
137
|
+
_g.sent();
|
|
138
|
+
return [3 /*break*/, 5];
|
|
139
|
+
case 4:
|
|
140
|
+
err_1 = _g.sent();
|
|
141
|
+
errMsg = (err_1 === null || err_1 === void 0 ? void 0 : err_1.errmsg) || (err_1 === null || err_1 === void 0 ? void 0 : err_1.message) || String(err_1);
|
|
142
|
+
if ((err_1 === null || err_1 === void 0 ? void 0 : err_1.errcode) === exports.STREAM_EXPIRED_ERRCODE ||
|
|
143
|
+
errMsg.includes(String(exports.STREAM_EXPIRED_ERRCODE))) {
|
|
144
|
+
throw new StreamExpiredError(errMsg);
|
|
145
|
+
}
|
|
146
|
+
throw err_1;
|
|
147
|
+
case 5:
|
|
148
|
+
if (accountId) {
|
|
149
|
+
im_runtime_telemetry_js_1.imRuntimeRecordChannelActivity("wecom", accountId, "outbound");
|
|
150
|
+
}
|
|
151
|
+
(_e = runtime.log) === null || _e === void 0 ? void 0 : _e.call(runtime, "[plugin -> server] streamId=" + streamId + ", finish=" + finish);
|
|
152
|
+
return [2 /*return*/, streamId];
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
exports.sendWeComReply = sendWeComReply;
|
|
158
|
+
// ============================================================================
|
|
159
|
+
// 非阻塞流式发送(用于 onPartialReply 场景)
|
|
160
|
+
// ============================================================================
|
|
161
|
+
/**
|
|
162
|
+
* 非阻塞流式文本回复
|
|
163
|
+
*
|
|
164
|
+
* 基于 SDK 的 replyStreamNonBlocking 方法:
|
|
165
|
+
* - 如果上一条同 reqId 的消息尚未收到 ack,则跳过本次发送(返回 'skipped'),
|
|
166
|
+
* 避免流式中间帧排队积压导致延迟。
|
|
167
|
+
* - finish=true 的最终帧不受此限制,始终保证发送。
|
|
168
|
+
*
|
|
169
|
+
* @returns 'skipped' 表示被跳过,否则返回 streamId
|
|
170
|
+
*/
|
|
171
|
+
function sendWeComReplyNonBlocking(params) {
|
|
172
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
173
|
+
var wsClient, frame, text, runtime, streamId, _a, finish, accountId, result, err_2, errMsg;
|
|
174
|
+
return __generator(this, function (_b) {
|
|
175
|
+
switch (_b.label) {
|
|
176
|
+
case 0:
|
|
177
|
+
wsClient = params.wsClient, frame = params.frame, text = params.text, runtime = params.runtime, streamId = params.streamId, _a = params.finish, finish = _a === void 0 ? false : _a, accountId = params.accountId;
|
|
178
|
+
if (!text) {
|
|
179
|
+
return [2 /*return*/, 'skipped'];
|
|
180
|
+
}
|
|
181
|
+
if (!wsClient.isConnected) {
|
|
182
|
+
return [2 /*return*/, 'skipped'];
|
|
183
|
+
}
|
|
184
|
+
_b.label = 1;
|
|
185
|
+
case 1:
|
|
186
|
+
_b.trys.push([1, 3, , 4]);
|
|
187
|
+
return [4 /*yield*/, wsClient.replyStreamNonBlocking(frame, streamId, text, finish)];
|
|
188
|
+
case 2:
|
|
189
|
+
result = _b.sent();
|
|
190
|
+
if (result === 'skipped') {
|
|
191
|
+
return [2 /*return*/, 'skipped'];
|
|
192
|
+
}
|
|
193
|
+
if (accountId) {
|
|
194
|
+
im_runtime_telemetry_js_1.imRuntimeRecordChannelActivity("wecom", accountId, "outbound");
|
|
195
|
+
}
|
|
196
|
+
return [2 /*return*/, streamId];
|
|
197
|
+
case 3:
|
|
198
|
+
err_2 = _b.sent();
|
|
199
|
+
errMsg = (err_2 === null || err_2 === void 0 ? void 0 : err_2.errmsg) || (err_2 === null || err_2 === void 0 ? void 0 : err_2.message) || String(err_2);
|
|
200
|
+
if ((err_2 === null || err_2 === void 0 ? void 0 : err_2.errcode) === exports.STREAM_EXPIRED_ERRCODE ||
|
|
201
|
+
errMsg.includes(String(exports.STREAM_EXPIRED_ERRCODE))) {
|
|
202
|
+
throw new StreamExpiredError(errMsg);
|
|
203
|
+
}
|
|
204
|
+
throw err_2;
|
|
205
|
+
case 4: return [2 /*return*/];
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
exports.sendWeComReplyNonBlocking = sendWeComReplyNonBlocking;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 企业微信 WebSocket 监控器主模块
|
|
3
|
+
*
|
|
4
|
+
* 负责:
|
|
5
|
+
* - 建立和管理 WebSocket 连接
|
|
6
|
+
* - 协调消息处理流程(解析→策略检查→下载图片→路由回复)
|
|
7
|
+
* - 资源生命周期管理
|
|
8
|
+
*
|
|
9
|
+
* 子模块:
|
|
10
|
+
* - message-parser.ts : 消息内容解析
|
|
11
|
+
* - message-sender.ts : 消息发送(带超时保护)
|
|
12
|
+
* - media-handler.ts : 图片下载和保存(带超时保护)
|
|
13
|
+
* - group-policy.ts : 群组访问控制
|
|
14
|
+
* - dm-policy.ts : 私聊访问控制
|
|
15
|
+
* - state-manager.ts : 全局状态管理(带 TTL 清理)
|
|
16
|
+
* - timeout.ts : 超时工具
|
|
17
|
+
*/
|
|
18
|
+
import { WSClient } from "@wecom/aibot-node-sdk";
|
|
19
|
+
import type { WsFrame } from "@wecom/aibot-node-sdk";
|
|
20
|
+
import type { RuntimeEnv } from "ylib-openclaw/plugin-sdk";
|
|
21
|
+
import type { WeComMonitorOptions, MessageState } from "./interface.js";
|
|
22
|
+
import type { ResolvedWeComAccount } from "./utils.js";
|
|
23
|
+
export type { WeComMonitorOptions } from "./interface.js";
|
|
24
|
+
export { WeComCommand } from "./const.js";
|
|
25
|
+
export { getWeComWebSocket, setReqIdForChat, getReqIdForChatAsync, getReqIdForChat, deleteReqIdForChat, warmupReqIdStore, flushReqIdStore, } from "./state-manager.js";
|
|
26
|
+
export { sendWeComReply } from "./message-sender.js";
|
|
27
|
+
/** deliver 回调所需的上下文 */
|
|
28
|
+
interface DeliverContext {
|
|
29
|
+
wsClient: WSClient;
|
|
30
|
+
frame: WsFrame;
|
|
31
|
+
state: MessageState;
|
|
32
|
+
account: ResolvedWeComAccount;
|
|
33
|
+
runtime: RuntimeEnv;
|
|
34
|
+
}
|
|
35
|
+
declare function isStreamReplyLocallyExpired(state: MessageState, now?: number): boolean;
|
|
36
|
+
declare function markStreamReplyLocallyExpired(state: MessageState, runtime: RuntimeEnv, reason: string): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* 关闭 thinking 流(发送 finish=true 的流式消息)
|
|
39
|
+
*
|
|
40
|
+
* thinking 是通过 replyStream 用 streamId 发的流式消息,
|
|
41
|
+
* 只有同一 streamId 的 replyStream(finish=true) 才能关闭它。
|
|
42
|
+
*
|
|
43
|
+
* ⚠️ 注意:企微会忽略空格等不可见内容,必须用有可见字符的文案才能真正
|
|
44
|
+
* 替换掉 thinking 动画,否则 thinking 会一直残留。
|
|
45
|
+
*
|
|
46
|
+
* 关闭策略(按优先级):
|
|
47
|
+
* 1. 有可见文本 → 用完整文本关闭
|
|
48
|
+
* 2. 有模板卡片发送成功 → "📋 卡片消息已发送。"
|
|
49
|
+
* 3. 有媒体成功发送(通过 deliver 回调) → 用友好提示"文件已发送"
|
|
50
|
+
* 4. 媒体发送失败 → 直接用错误摘要替换 thinking
|
|
51
|
+
*
|
|
52
|
+
* 降级策略:
|
|
53
|
+
* - 当 streamExpired=true(errcode 846608 或本地 TTL 保护触发)时,流式通道不可用,
|
|
54
|
+
* 改用 wsClient.sendMessage 主动发送完整文本。
|
|
55
|
+
*
|
|
56
|
+
* 注意:模板卡片的检测和发送已在 finishThinkingStream 之前由
|
|
57
|
+
* processTemplateCardsIfNeeded 完成,此处只关心最后的消息发送。
|
|
58
|
+
*/
|
|
59
|
+
declare function finishThinkingStream(ctx: DeliverContext): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* 监听企业微信 WebSocket 连接
|
|
62
|
+
* 使用 aibot-node-sdk 简化连接管理
|
|
63
|
+
*/
|
|
64
|
+
export declare function monitorWeComProvider(options: WeComMonitorOptions): Promise<void>;
|
|
65
|
+
export declare const __monitorTestables: {
|
|
66
|
+
finishThinkingStream: typeof finishThinkingStream;
|
|
67
|
+
isStreamReplyLocallyExpired: typeof isStreamReplyLocallyExpired;
|
|
68
|
+
markStreamReplyLocallyExpired: typeof markStreamReplyLocallyExpired;
|
|
69
|
+
};
|