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,425 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* WeCom Agent API 客户端
|
|
4
|
+
* 管理 AccessToken 缓存和 API 调用
|
|
5
|
+
*/
|
|
6
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
7
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
8
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
9
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
10
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
11
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
12
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
16
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
17
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
18
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
19
|
+
function step(op) {
|
|
20
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
21
|
+
while (_) try {
|
|
22
|
+
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;
|
|
23
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
24
|
+
switch (op[0]) {
|
|
25
|
+
case 0: case 1: t = op; break;
|
|
26
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
27
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
28
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
29
|
+
default:
|
|
30
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
31
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
32
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
33
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
34
|
+
if (t[2]) _.ops.pop();
|
|
35
|
+
_.trys.pop(); continue;
|
|
36
|
+
}
|
|
37
|
+
op = body.call(thisArg, _);
|
|
38
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
39
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
43
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
44
|
+
};
|
|
45
|
+
exports.__esModule = true;
|
|
46
|
+
exports.downloadMedia = exports.sendMedia = exports.uploadMedia = exports.sendText = exports.getAccessToken = void 0;
|
|
47
|
+
var node_crypto_1 = __importDefault(require("node:crypto"));
|
|
48
|
+
var constants_js_1 = require("../types/constants.js");
|
|
49
|
+
var http_js_1 = require("../http.js");
|
|
50
|
+
function resolveWecomEgressProxyUrlFromNetwork(network) {
|
|
51
|
+
return network === null || network === void 0 ? void 0 : network.egressProxyUrl;
|
|
52
|
+
}
|
|
53
|
+
var tokenCaches = new Map();
|
|
54
|
+
function normalizeUploadFilename(filename) {
|
|
55
|
+
var trimmed = filename.trim();
|
|
56
|
+
if (!trimmed)
|
|
57
|
+
return "file.bin";
|
|
58
|
+
var ext = trimmed.includes(".") ? "." + trimmed.split(".").pop().toLowerCase() : "";
|
|
59
|
+
var base = ext ? trimmed.slice(0, -ext.length) : trimmed;
|
|
60
|
+
var sanitizedBase = base
|
|
61
|
+
.replace(/[^\x20-\x7e]/g, "_")
|
|
62
|
+
.replace(/["\\\/;=]/g, "_")
|
|
63
|
+
.replace(/\s+/g, "_")
|
|
64
|
+
.replace(/_+/g, "_")
|
|
65
|
+
.replace(/^_+|_+$/g, "");
|
|
66
|
+
var safeBase = sanitizedBase || "file";
|
|
67
|
+
var safeExt = ext.replace(/[^a-z0-9.]/g, "");
|
|
68
|
+
return "" + safeBase + (safeExt || ".bin");
|
|
69
|
+
}
|
|
70
|
+
function guessUploadContentType(filename) {
|
|
71
|
+
var _a;
|
|
72
|
+
var ext = ((_a = filename.split(".").pop()) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || "";
|
|
73
|
+
var contentTypeMap = {
|
|
74
|
+
// image
|
|
75
|
+
jpg: "image/jpg", jpeg: "image/jpeg", png: "image/png", gif: "image/gif", webp: "image/webp", bmp: "image/bmp",
|
|
76
|
+
// audio / video
|
|
77
|
+
amr: "voice/amr", mp3: "audio/mpeg", wav: "audio/wav", m4a: "audio/mp4", ogg: "audio/ogg", mp4: "video/mp4", mov: "video/quicktime",
|
|
78
|
+
// documents
|
|
79
|
+
txt: "text/plain", md: "text/markdown", csv: "text/csv", tsv: "text/tab-separated-values", json: "application/json",
|
|
80
|
+
xml: "application/xml", yaml: "application/yaml", yml: "application/yaml",
|
|
81
|
+
pdf: "application/pdf", doc: "application/msword", docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
82
|
+
xls: "application/vnd.ms-excel", xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
83
|
+
ppt: "application/vnd.ms-powerpoint", pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
84
|
+
rtf: "application/rtf", odt: "application/vnd.oasis.opendocument.text",
|
|
85
|
+
// archives
|
|
86
|
+
zip: "application/zip", rar: "application/vnd.rar", "7z": "application/x-7z-compressed",
|
|
87
|
+
gz: "application/gzip", tgz: "application/gzip", tar: "application/x-tar"
|
|
88
|
+
};
|
|
89
|
+
return contentTypeMap[ext] || "application/octet-stream";
|
|
90
|
+
}
|
|
91
|
+
function requireAgentId(agent) {
|
|
92
|
+
if (typeof agent.agentId === "number" && Number.isFinite(agent.agentId))
|
|
93
|
+
return agent.agentId;
|
|
94
|
+
throw new Error("wecom agent account=" + agent.accountId + " missing agentId; sending via cgi-bin/message/send requires agentId");
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* **getAccessToken (获取 AccessToken)**
|
|
98
|
+
*
|
|
99
|
+
* 获取企业微信 API 调用所需的 AccessToken。
|
|
100
|
+
* 具备自动缓存和过期刷新机制。
|
|
101
|
+
*
|
|
102
|
+
* @param agent Agent 账号信息
|
|
103
|
+
* @returns 有效的 AccessToken
|
|
104
|
+
*/
|
|
105
|
+
function getAccessToken(agent) {
|
|
106
|
+
var _a;
|
|
107
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
108
|
+
var cacheKey, cache, now;
|
|
109
|
+
var _this = this;
|
|
110
|
+
return __generator(this, function (_b) {
|
|
111
|
+
cacheKey = agent.corpId + ":" + String((_a = agent.agentId) !== null && _a !== void 0 ? _a : "na");
|
|
112
|
+
cache = tokenCaches.get(cacheKey);
|
|
113
|
+
if (!cache) {
|
|
114
|
+
cache = { token: "", expiresAt: 0, refreshPromise: null };
|
|
115
|
+
tokenCaches.set(cacheKey, cache);
|
|
116
|
+
}
|
|
117
|
+
now = Date.now();
|
|
118
|
+
if (cache.token && cache.expiresAt > now + constants_js_1.LIMITS.TOKEN_REFRESH_BUFFER_MS) {
|
|
119
|
+
return [2 /*return*/, cache.token];
|
|
120
|
+
}
|
|
121
|
+
// 防止并发刷新
|
|
122
|
+
if (cache.refreshPromise) {
|
|
123
|
+
return [2 /*return*/, cache.refreshPromise];
|
|
124
|
+
}
|
|
125
|
+
cache.refreshPromise = (function () { return __awaiter(_this, void 0, void 0, function () {
|
|
126
|
+
var url, res, json;
|
|
127
|
+
var _a;
|
|
128
|
+
return __generator(this, function (_b) {
|
|
129
|
+
switch (_b.label) {
|
|
130
|
+
case 0:
|
|
131
|
+
_b.trys.push([0, , 3, 4]);
|
|
132
|
+
url = constants_js_1.API_ENDPOINTS.GET_TOKEN + "?corpid=" + encodeURIComponent(agent.corpId) + "&corpsecret=" + encodeURIComponent(agent.corpSecret);
|
|
133
|
+
return [4 /*yield*/, http_js_1.wecomFetch(url, undefined, { proxyUrl: resolveWecomEgressProxyUrlFromNetwork(agent.network), timeoutMs: constants_js_1.LIMITS.REQUEST_TIMEOUT_MS })];
|
|
134
|
+
case 1:
|
|
135
|
+
res = _b.sent();
|
|
136
|
+
return [4 /*yield*/, res.json()];
|
|
137
|
+
case 2:
|
|
138
|
+
json = _b.sent();
|
|
139
|
+
if (!(json === null || json === void 0 ? void 0 : json.access_token)) {
|
|
140
|
+
throw new Error("gettoken failed: " + (json === null || json === void 0 ? void 0 : json.errcode) + " " + (json === null || json === void 0 ? void 0 : json.errmsg));
|
|
141
|
+
}
|
|
142
|
+
cache.token = json.access_token;
|
|
143
|
+
cache.expiresAt = Date.now() + ((_a = json.expires_in) !== null && _a !== void 0 ? _a : 7200) * 1000;
|
|
144
|
+
return [2 /*return*/, cache.token];
|
|
145
|
+
case 3:
|
|
146
|
+
cache.refreshPromise = null;
|
|
147
|
+
return [7 /*endfinally*/];
|
|
148
|
+
case 4: return [2 /*return*/];
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}); })();
|
|
152
|
+
return [2 /*return*/, cache.refreshPromise];
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
exports.getAccessToken = getAccessToken;
|
|
157
|
+
/**
|
|
158
|
+
* **sendText (发送文本消息)**
|
|
159
|
+
*
|
|
160
|
+
* 调用 `message/send` (Agent) 或 `appchat/send` (群聊) 发送文本。
|
|
161
|
+
*
|
|
162
|
+
* @param params.agent 发送方 Agent
|
|
163
|
+
* @param params.toUser 接收用户 ID (单聊可选,可与 toParty/toTag 同时使用)
|
|
164
|
+
* @param params.toParty 接收部门 ID (单聊可选)
|
|
165
|
+
* @param params.toTag 接收标签 ID (单聊可选)
|
|
166
|
+
* @param params.chatId 接收群 ID (群聊模式必填,互斥)
|
|
167
|
+
* @param params.text 消息内容
|
|
168
|
+
*/
|
|
169
|
+
function sendText(params) {
|
|
170
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
171
|
+
var agent, toUser, toParty, toTag, chatId, text, token, useChat, url, body, res, json, details;
|
|
172
|
+
return __generator(this, function (_a) {
|
|
173
|
+
switch (_a.label) {
|
|
174
|
+
case 0:
|
|
175
|
+
agent = params.agent, toUser = params.toUser, toParty = params.toParty, toTag = params.toTag, chatId = params.chatId, text = params.text;
|
|
176
|
+
return [4 /*yield*/, getAccessToken(agent)];
|
|
177
|
+
case 1:
|
|
178
|
+
token = _a.sent();
|
|
179
|
+
useChat = Boolean(chatId);
|
|
180
|
+
url = useChat
|
|
181
|
+
? constants_js_1.API_ENDPOINTS.SEND_APPCHAT + "?access_token=" + encodeURIComponent(token)
|
|
182
|
+
: constants_js_1.API_ENDPOINTS.SEND_MESSAGE + "?access_token=" + encodeURIComponent(token);
|
|
183
|
+
body = useChat
|
|
184
|
+
? { chatid: chatId, msgtype: "text", text: { content: text } }
|
|
185
|
+
: {
|
|
186
|
+
touser: toUser,
|
|
187
|
+
toparty: toParty,
|
|
188
|
+
totag: toTag,
|
|
189
|
+
msgtype: "text",
|
|
190
|
+
agentid: requireAgentId(agent),
|
|
191
|
+
text: { content: text }
|
|
192
|
+
};
|
|
193
|
+
return [4 /*yield*/, http_js_1.wecomFetch(url, {
|
|
194
|
+
method: "POST",
|
|
195
|
+
headers: { "Content-Type": "application/json" },
|
|
196
|
+
body: JSON.stringify(body)
|
|
197
|
+
}, { proxyUrl: resolveWecomEgressProxyUrlFromNetwork(agent.network), timeoutMs: constants_js_1.LIMITS.REQUEST_TIMEOUT_MS })];
|
|
198
|
+
case 2:
|
|
199
|
+
res = _a.sent();
|
|
200
|
+
return [4 /*yield*/, res.json()];
|
|
201
|
+
case 3:
|
|
202
|
+
json = _a.sent();
|
|
203
|
+
if ((json === null || json === void 0 ? void 0 : json.errcode) !== 0) {
|
|
204
|
+
throw new Error("send failed: " + (json === null || json === void 0 ? void 0 : json.errcode) + " " + (json === null || json === void 0 ? void 0 : json.errmsg));
|
|
205
|
+
}
|
|
206
|
+
if ((json === null || json === void 0 ? void 0 : json.invaliduser) || (json === null || json === void 0 ? void 0 : json.invalidparty) || (json === null || json === void 0 ? void 0 : json.invalidtag)) {
|
|
207
|
+
details = [
|
|
208
|
+
json.invaliduser ? "invaliduser=" + json.invaliduser : "",
|
|
209
|
+
json.invalidparty ? "invalidparty=" + json.invalidparty : "",
|
|
210
|
+
json.invalidtag ? "invalidtag=" + json.invalidtag : ""
|
|
211
|
+
].filter(Boolean).join(", ");
|
|
212
|
+
throw new Error("send partial failure: " + details);
|
|
213
|
+
}
|
|
214
|
+
return [2 /*return*/];
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
exports.sendText = sendText;
|
|
220
|
+
/**
|
|
221
|
+
* **uploadMedia (上传媒体文件)**
|
|
222
|
+
*
|
|
223
|
+
* 上传临时素材到企业微信。
|
|
224
|
+
* 素材有效期为 3 天。
|
|
225
|
+
*
|
|
226
|
+
* @param params.type 媒体类型 (image, voice, video, file)
|
|
227
|
+
* @param params.buffer 文件二进制数据
|
|
228
|
+
* @param params.filename 文件名 (需包含正确扩展名)
|
|
229
|
+
* @returns 媒体 ID (media_id)
|
|
230
|
+
*/
|
|
231
|
+
function uploadMedia(params) {
|
|
232
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
233
|
+
var agent, type, buffer, filename, safeFilename, token, proxyUrl, url, uploadOnce, preferredContentType, json;
|
|
234
|
+
var _this = this;
|
|
235
|
+
return __generator(this, function (_a) {
|
|
236
|
+
switch (_a.label) {
|
|
237
|
+
case 0:
|
|
238
|
+
agent = params.agent, type = params.type, buffer = params.buffer, filename = params.filename;
|
|
239
|
+
safeFilename = normalizeUploadFilename(filename);
|
|
240
|
+
return [4 /*yield*/, getAccessToken(agent)];
|
|
241
|
+
case 1:
|
|
242
|
+
token = _a.sent();
|
|
243
|
+
proxyUrl = resolveWecomEgressProxyUrlFromNetwork(agent.network);
|
|
244
|
+
url = constants_js_1.API_ENDPOINTS.UPLOAD_MEDIA + "?access_token=" + encodeURIComponent(token) + "&type=" + encodeURIComponent(type) + "&debug=1";
|
|
245
|
+
// DEBUG: 输出上传信息
|
|
246
|
+
console.log("[wecom-upload] Uploading media: type=" + type + ", filename=" + safeFilename + ", size=" + buffer.length + " bytes");
|
|
247
|
+
uploadOnce = function (fileContentType) { return __awaiter(_this, void 0, void 0, function () {
|
|
248
|
+
var boundary, header, footer, body, res, json;
|
|
249
|
+
return __generator(this, function (_a) {
|
|
250
|
+
switch (_a.label) {
|
|
251
|
+
case 0:
|
|
252
|
+
boundary = "----WebKitFormBoundary" + node_crypto_1["default"].randomBytes(16).toString("hex");
|
|
253
|
+
header = Buffer.from("--" + boundary + "\r\n" +
|
|
254
|
+
("Content-Disposition: form-data; name=\"media\"; filename=\"" + safeFilename + "\"; filelength=" + buffer.length + "\r\n") +
|
|
255
|
+
("Content-Type: " + fileContentType + "\r\n\r\n"));
|
|
256
|
+
footer = Buffer.from("\r\n--" + boundary + "--\r\n");
|
|
257
|
+
body = Buffer.concat([header, buffer, footer]);
|
|
258
|
+
console.log("[wecom-upload] Multipart body size=" + body.length + ", boundary=" + boundary + ", fileContentType=" + fileContentType);
|
|
259
|
+
return [4 /*yield*/, http_js_1.wecomFetch(url, {
|
|
260
|
+
method: "POST",
|
|
261
|
+
headers: {
|
|
262
|
+
"Content-Type": "multipart/form-data; boundary=" + boundary,
|
|
263
|
+
"Content-Length": String(body.length)
|
|
264
|
+
},
|
|
265
|
+
body: body
|
|
266
|
+
}, { proxyUrl: proxyUrl, timeoutMs: constants_js_1.LIMITS.REQUEST_TIMEOUT_MS })];
|
|
267
|
+
case 1:
|
|
268
|
+
res = _a.sent();
|
|
269
|
+
return [4 /*yield*/, res.json()];
|
|
270
|
+
case 2:
|
|
271
|
+
json = _a.sent();
|
|
272
|
+
console.log("[wecom-upload] Response:", JSON.stringify(json));
|
|
273
|
+
return [2 /*return*/, json];
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
}); };
|
|
277
|
+
preferredContentType = guessUploadContentType(safeFilename);
|
|
278
|
+
return [4 /*yield*/, uploadOnce(preferredContentType)];
|
|
279
|
+
case 2:
|
|
280
|
+
json = _a.sent();
|
|
281
|
+
if (!(!(json === null || json === void 0 ? void 0 : json.media_id) && preferredContentType !== "application/octet-stream")) return [3 /*break*/, 4];
|
|
282
|
+
console.warn("[wecom-upload] Upload failed with " + preferredContentType + ", retrying as application/octet-stream: " + (json === null || json === void 0 ? void 0 : json.errcode) + " " + (json === null || json === void 0 ? void 0 : json.errmsg));
|
|
283
|
+
return [4 /*yield*/, uploadOnce("application/octet-stream")];
|
|
284
|
+
case 3:
|
|
285
|
+
json = _a.sent();
|
|
286
|
+
_a.label = 4;
|
|
287
|
+
case 4:
|
|
288
|
+
if (!(json === null || json === void 0 ? void 0 : json.media_id)) {
|
|
289
|
+
throw new Error("upload failed: " + (json === null || json === void 0 ? void 0 : json.errcode) + " " + (json === null || json === void 0 ? void 0 : json.errmsg));
|
|
290
|
+
}
|
|
291
|
+
return [2 /*return*/, json.media_id];
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
exports.uploadMedia = uploadMedia;
|
|
297
|
+
/**
|
|
298
|
+
* **sendMedia (发送媒体消息)**
|
|
299
|
+
*
|
|
300
|
+
* 发送图片、音频、视频或文件。需先通过 `uploadMedia` 获取 media_id。
|
|
301
|
+
*
|
|
302
|
+
* @param params.agent 发送方 Agent
|
|
303
|
+
* @param params.toUser 接收用户 ID (单聊可选)
|
|
304
|
+
* @param params.toParty 接收部门 ID (单聊可选)
|
|
305
|
+
* @param params.toTag 接收标签 ID (单聊可选)
|
|
306
|
+
* @param params.chatId 接收群 ID (群聊模式必填)
|
|
307
|
+
* @param params.mediaId 媒体 ID
|
|
308
|
+
* @param params.mediaType 媒体类型
|
|
309
|
+
* @param params.title 视频标题 (可选)
|
|
310
|
+
* @param params.description 视频描述 (可选)
|
|
311
|
+
*/
|
|
312
|
+
function sendMedia(params) {
|
|
313
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
314
|
+
var agent, toUser, toParty, toTag, chatId, mediaId, mediaType, title, description, token, useChat, url, mediaPayload, body, res, json, details;
|
|
315
|
+
var _a, _b;
|
|
316
|
+
return __generator(this, function (_c) {
|
|
317
|
+
switch (_c.label) {
|
|
318
|
+
case 0:
|
|
319
|
+
agent = params.agent, toUser = params.toUser, toParty = params.toParty, toTag = params.toTag, chatId = params.chatId, mediaId = params.mediaId, mediaType = params.mediaType, title = params.title, description = params.description;
|
|
320
|
+
return [4 /*yield*/, getAccessToken(agent)];
|
|
321
|
+
case 1:
|
|
322
|
+
token = _c.sent();
|
|
323
|
+
useChat = Boolean(chatId);
|
|
324
|
+
url = useChat
|
|
325
|
+
? constants_js_1.API_ENDPOINTS.SEND_APPCHAT + "?access_token=" + encodeURIComponent(token)
|
|
326
|
+
: constants_js_1.API_ENDPOINTS.SEND_MESSAGE + "?access_token=" + encodeURIComponent(token);
|
|
327
|
+
mediaPayload = mediaType === "video"
|
|
328
|
+
? { media_id: mediaId, title: title !== null && title !== void 0 ? title : "Video", description: description !== null && description !== void 0 ? description : "" }
|
|
329
|
+
: { media_id: mediaId };
|
|
330
|
+
body = useChat
|
|
331
|
+
? (_a = { chatid: chatId, msgtype: mediaType }, _a[mediaType] = mediaPayload, _a) : (_b = {
|
|
332
|
+
touser: toUser,
|
|
333
|
+
toparty: toParty,
|
|
334
|
+
totag: toTag,
|
|
335
|
+
msgtype: mediaType,
|
|
336
|
+
agentid: requireAgentId(agent)
|
|
337
|
+
},
|
|
338
|
+
_b[mediaType] = mediaPayload,
|
|
339
|
+
_b);
|
|
340
|
+
return [4 /*yield*/, http_js_1.wecomFetch(url, {
|
|
341
|
+
method: "POST",
|
|
342
|
+
headers: { "Content-Type": "application/json" },
|
|
343
|
+
body: JSON.stringify(body)
|
|
344
|
+
}, { proxyUrl: resolveWecomEgressProxyUrlFromNetwork(agent.network), timeoutMs: constants_js_1.LIMITS.REQUEST_TIMEOUT_MS })];
|
|
345
|
+
case 2:
|
|
346
|
+
res = _c.sent();
|
|
347
|
+
return [4 /*yield*/, res.json()];
|
|
348
|
+
case 3:
|
|
349
|
+
json = _c.sent();
|
|
350
|
+
if ((json === null || json === void 0 ? void 0 : json.errcode) !== 0) {
|
|
351
|
+
throw new Error("send " + mediaType + " failed: " + (json === null || json === void 0 ? void 0 : json.errcode) + " " + (json === null || json === void 0 ? void 0 : json.errmsg));
|
|
352
|
+
}
|
|
353
|
+
if ((json === null || json === void 0 ? void 0 : json.invaliduser) || (json === null || json === void 0 ? void 0 : json.invalidparty) || (json === null || json === void 0 ? void 0 : json.invalidtag)) {
|
|
354
|
+
details = [
|
|
355
|
+
json.invaliduser ? "invaliduser=" + json.invaliduser : "",
|
|
356
|
+
json.invalidparty ? "invalidparty=" + json.invalidparty : "",
|
|
357
|
+
json.invalidtag ? "invalidtag=" + json.invalidtag : ""
|
|
358
|
+
].filter(Boolean).join(", ");
|
|
359
|
+
throw new Error("send " + mediaType + " partial failure: " + details);
|
|
360
|
+
}
|
|
361
|
+
return [2 /*return*/];
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
exports.sendMedia = sendMedia;
|
|
367
|
+
/**
|
|
368
|
+
* **downloadMedia (下载媒体文件)**
|
|
369
|
+
*
|
|
370
|
+
* 通过 media_id 从企业微信服务器下载临时素材。
|
|
371
|
+
*
|
|
372
|
+
* @returns { buffer, contentType }
|
|
373
|
+
*/
|
|
374
|
+
function downloadMedia(params) {
|
|
375
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
376
|
+
var agent, mediaId, token, url, res, contentType, disposition, filename, json, buffer;
|
|
377
|
+
return __generator(this, function (_a) {
|
|
378
|
+
switch (_a.label) {
|
|
379
|
+
case 0:
|
|
380
|
+
agent = params.agent, mediaId = params.mediaId;
|
|
381
|
+
return [4 /*yield*/, getAccessToken(agent)];
|
|
382
|
+
case 1:
|
|
383
|
+
token = _a.sent();
|
|
384
|
+
url = constants_js_1.API_ENDPOINTS.DOWNLOAD_MEDIA + "?access_token=" + encodeURIComponent(token) + "&media_id=" + encodeURIComponent(mediaId);
|
|
385
|
+
return [4 /*yield*/, http_js_1.wecomFetch(url, undefined, { proxyUrl: resolveWecomEgressProxyUrlFromNetwork(agent.network), timeoutMs: constants_js_1.LIMITS.REQUEST_TIMEOUT_MS })];
|
|
386
|
+
case 2:
|
|
387
|
+
res = _a.sent();
|
|
388
|
+
if (!res.ok) {
|
|
389
|
+
throw new Error("download failed: " + res.status);
|
|
390
|
+
}
|
|
391
|
+
contentType = res.headers.get("content-type") || "application/octet-stream";
|
|
392
|
+
disposition = res.headers.get("content-disposition") || "";
|
|
393
|
+
filename = (function () {
|
|
394
|
+
// 兼容:filename="a.md" / filename=a.md / filename*=UTF-8''a%2Eb.md
|
|
395
|
+
var mStar = disposition.match(/filename\*\s*=\s*([^;]+)/i);
|
|
396
|
+
if (mStar) {
|
|
397
|
+
var raw = mStar[1].trim().replace(/^"(.*)"$/, "$1");
|
|
398
|
+
var parts = raw.split("''");
|
|
399
|
+
var encoded = parts.length === 2 ? parts[1] : raw;
|
|
400
|
+
try {
|
|
401
|
+
return decodeURIComponent(encoded);
|
|
402
|
+
}
|
|
403
|
+
catch (_a) {
|
|
404
|
+
return encoded;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
var m = disposition.match(/filename\s*=\s*([^;]+)/i);
|
|
408
|
+
if (!m)
|
|
409
|
+
return undefined;
|
|
410
|
+
return m[1].trim().replace(/^"(.*)"$/, "$1") || undefined;
|
|
411
|
+
})();
|
|
412
|
+
if (!contentType.includes("application/json")) return [3 /*break*/, 4];
|
|
413
|
+
return [4 /*yield*/, res.json()];
|
|
414
|
+
case 3:
|
|
415
|
+
json = _a.sent();
|
|
416
|
+
throw new Error("download failed: " + (json === null || json === void 0 ? void 0 : json.errcode) + " " + (json === null || json === void 0 ? void 0 : json.errmsg));
|
|
417
|
+
case 4: return [4 /*yield*/, http_js_1.readResponseBodyAsBuffer(res, params.maxBytes)];
|
|
418
|
+
case 5:
|
|
419
|
+
buffer = _a.sent();
|
|
420
|
+
return [2 /*return*/, { buffer: buffer, contentType: contentType, filename: filename }];
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
exports.downloadMedia = downloadMedia;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WeCom Agent Webhook 处理器
|
|
3
|
+
* 处理 XML 格式回调
|
|
4
|
+
*/
|
|
5
|
+
/// <reference types="node" />
|
|
6
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
7
|
+
import type { OpenClawConfig, PluginRuntime } from "ylib-openclaw/plugin-sdk";
|
|
8
|
+
import type { ResolvedAgentAccount } from "../types/index.js";
|
|
9
|
+
import type { WecomAgentInboundMessage } from "../types/index.js";
|
|
10
|
+
/**
|
|
11
|
+
* **AgentWebhookParams (Webhook 处理器参数)**
|
|
12
|
+
*
|
|
13
|
+
* 传递给 Agent Webhook 处理函数的上下文参数集合。
|
|
14
|
+
* @property req Node.js 原始请求对象
|
|
15
|
+
* @property res Node.js 原始响应对象
|
|
16
|
+
* @property agent 解析后的 Agent 账号信息
|
|
17
|
+
* @property config 全局插件配置
|
|
18
|
+
* @property core OpenClaw 插件运行时
|
|
19
|
+
* @property log 可选日志输出函数
|
|
20
|
+
* @property error 可选错误输出函数
|
|
21
|
+
*/
|
|
22
|
+
export declare type AgentWebhookParams = {
|
|
23
|
+
req: IncomingMessage;
|
|
24
|
+
res: ServerResponse;
|
|
25
|
+
/**
|
|
26
|
+
* 上游已完成验签/解密时传入,避免重复协议处理。
|
|
27
|
+
* 仅用于 POST 消息回调流程。
|
|
28
|
+
*/
|
|
29
|
+
verifiedPost?: {
|
|
30
|
+
timestamp: string;
|
|
31
|
+
nonce: string;
|
|
32
|
+
signature: string;
|
|
33
|
+
encrypted: string;
|
|
34
|
+
decrypted: string;
|
|
35
|
+
parsed: WecomAgentInboundMessage;
|
|
36
|
+
};
|
|
37
|
+
agent: ResolvedAgentAccount;
|
|
38
|
+
config: OpenClawConfig;
|
|
39
|
+
core: PluginRuntime;
|
|
40
|
+
log?: (msg: string) => void;
|
|
41
|
+
error?: (msg: string) => void;
|
|
42
|
+
};
|
|
43
|
+
export declare type AgentInboundProcessDecision = {
|
|
44
|
+
shouldProcess: boolean;
|
|
45
|
+
reason: string;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* 仅允许“用户意图消息”进入 AI 会话。
|
|
49
|
+
* - event 回调(如 enter_agent/subscribe)不应触发会话与自动回复
|
|
50
|
+
* - 系统发送者(sys)不应触发会话与自动回复
|
|
51
|
+
* - 缺失发送者时默认丢弃,避免写入异常会话
|
|
52
|
+
*/
|
|
53
|
+
export declare function shouldProcessAgentInboundMessage(params: {
|
|
54
|
+
msgType: string;
|
|
55
|
+
fromUser: string;
|
|
56
|
+
eventType?: string;
|
|
57
|
+
}): AgentInboundProcessDecision;
|
|
58
|
+
/**
|
|
59
|
+
* **handleAgentWebhook (Agent Webhook 入口)**
|
|
60
|
+
*
|
|
61
|
+
* 统一处理 Agent 模式的 POST 消息回调请求。
|
|
62
|
+
* URL 验证与验签/解密由 monitor 层统一处理后再调用本函数。
|
|
63
|
+
*/
|
|
64
|
+
export declare function handleAgentWebhook(params: AgentWebhookParams): Promise<boolean>;
|