@wecom/wecom-openclaw-plugin 1.0.0 → 1.0.3-beta.0
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/dist/index.cjs.js +76 -78
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +76 -78
- package/dist/index.esm.js.map +1 -1
- package/dist/src/message-sender.d.ts +1 -1
- package/dist/src/utils.d.ts +0 -22
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { WSClient } from '@wecom/aibot-node-sdk';
|
|
1
|
+
import { resolveDefaultGroupPolicy, resolveOpenProviderRuntimeGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce, readJsonFileWithFallback, withFileLock, writeJsonFileAtomically, DEFAULT_ACCOUNT_ID, addWildcardAllowFrom, formatPairingApproveHint, emptyPluginConfigSchema } from 'openclaw/plugin-sdk';
|
|
2
|
+
import { generateReqId, WSClient } from '@wecom/aibot-node-sdk';
|
|
3
3
|
import { fileTypeFromBuffer } from 'file-type';
|
|
4
4
|
import os from 'node:os';
|
|
5
5
|
import path from 'node:path';
|
|
@@ -77,73 +77,6 @@ const DEFAULT_MEDIA_MAX_MB = 5;
|
|
|
77
77
|
/** 文本分块大小上限 */
|
|
78
78
|
const TEXT_CHUNK_LIMIT = 4000;
|
|
79
79
|
|
|
80
|
-
/**
|
|
81
|
-
* 企业微信公共工具函数
|
|
82
|
-
*/
|
|
83
|
-
const DefaultWsUrl = "wss://openws.work.weixin.qq.com";
|
|
84
|
-
/**
|
|
85
|
-
* 解析企业微信账户配置
|
|
86
|
-
*/
|
|
87
|
-
function resolveWeComAccount(cfg) {
|
|
88
|
-
const wecomConfig = (cfg.channels?.[CHANNEL_ID] ?? {});
|
|
89
|
-
return {
|
|
90
|
-
accountId: DEFAULT_ACCOUNT_ID,
|
|
91
|
-
name: wecomConfig.name ?? "企业微信",
|
|
92
|
-
enabled: wecomConfig.enabled ?? false,
|
|
93
|
-
websocketUrl: wecomConfig.websocketUrl || DefaultWsUrl,
|
|
94
|
-
botId: wecomConfig.botId ?? "",
|
|
95
|
-
secret: wecomConfig.secret ?? "",
|
|
96
|
-
sendThinkingMessage: wecomConfig.sendThinkingMessage ?? true,
|
|
97
|
-
config: wecomConfig,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* 设置企业微信账户配置
|
|
102
|
-
*/
|
|
103
|
-
function setWeComAccount(cfg, account) {
|
|
104
|
-
const existing = (cfg.channels?.[CHANNEL_ID] ?? {});
|
|
105
|
-
const merged = {
|
|
106
|
-
enabled: account.enabled ?? existing?.enabled ?? true,
|
|
107
|
-
botId: account.botId ?? existing?.botId ?? "",
|
|
108
|
-
secret: account.secret ?? existing?.secret ?? "",
|
|
109
|
-
allowFrom: account.allowFrom ?? existing?.allowFrom,
|
|
110
|
-
dmPolicy: account.dmPolicy ?? existing?.dmPolicy,
|
|
111
|
-
// 以下字段仅在已有配置值或显式传入时才写入,onboarding 时不主动生成
|
|
112
|
-
...(account.websocketUrl || existing?.websocketUrl
|
|
113
|
-
? { websocketUrl: account.websocketUrl ?? existing?.websocketUrl }
|
|
114
|
-
: {}),
|
|
115
|
-
...(account.name || existing?.name
|
|
116
|
-
? { name: account.name ?? existing?.name }
|
|
117
|
-
: {}),
|
|
118
|
-
...(account.sendThinkingMessage !== undefined || existing?.sendThinkingMessage !== undefined
|
|
119
|
-
? { sendThinkingMessage: account.sendThinkingMessage ?? existing?.sendThinkingMessage }
|
|
120
|
-
: {}),
|
|
121
|
-
};
|
|
122
|
-
return {
|
|
123
|
-
...cfg,
|
|
124
|
-
channels: {
|
|
125
|
-
...cfg.channels,
|
|
126
|
-
[CHANNEL_ID]: merged,
|
|
127
|
-
},
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* 生成随机字符串
|
|
132
|
-
*/
|
|
133
|
-
function generateRandomString(length = 9) {
|
|
134
|
-
return Math.random().toString(36).substr(2, length);
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* 生成企业微信请求 ID
|
|
138
|
-
* @param prefix - 请求类型前缀(subscribe, ping, response 等)
|
|
139
|
-
* @returns 格式化的请求 ID
|
|
140
|
-
*/
|
|
141
|
-
function generateReqId(prefix) {
|
|
142
|
-
const timestamp = Date.now();
|
|
143
|
-
const random = generateRandomString();
|
|
144
|
-
return `${prefix}_${timestamp}_${random}`;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
80
|
/**
|
|
148
81
|
* 企业微信消息内容解析模块
|
|
149
82
|
*
|
|
@@ -567,10 +500,14 @@ async function checkDmPolicy(params) {
|
|
|
567
500
|
if (dmPolicy === "open") {
|
|
568
501
|
return { allowed: true };
|
|
569
502
|
}
|
|
570
|
-
//
|
|
571
|
-
const
|
|
503
|
+
// OpenClaw <= 2026.2.19 signature: readAllowFromStore(channel, env?, accountId?)
|
|
504
|
+
const oldStoreAllowFrom = await core.channel.pairing.readAllowFromStore('wecom', undefined, account.accountId).catch(() => []);
|
|
505
|
+
// Compatibility fallback for newer OpenClaw implementations.
|
|
506
|
+
const newStoreAllowFrom = await core.channel.pairing
|
|
572
507
|
.readAllowFromStore({ channel: CHANNEL_ID, accountId: account.accountId })
|
|
573
508
|
.catch(() => []);
|
|
509
|
+
// 检查发送者是否在允许列表中
|
|
510
|
+
const storeAllowFrom = [...oldStoreAllowFrom, ...newStoreAllowFrom];
|
|
574
511
|
const effectiveAllowFrom = [...configAllowFrom, ...storeAllowFrom];
|
|
575
512
|
const senderAllowedResult = isSenderAllowed(senderId, effectiveAllowFrom);
|
|
576
513
|
if (senderAllowedResult) {
|
|
@@ -1076,7 +1013,7 @@ function buildMessageContext(frame, account, config, text, mediaList, quoteConte
|
|
|
1076
1013
|
MediaPaths: mediaPaths,
|
|
1077
1014
|
MediaTypes: mediaTypes,
|
|
1078
1015
|
MediaUrls: mediaPaths,
|
|
1079
|
-
|
|
1016
|
+
ReplyToBody: quoteContent,
|
|
1080
1017
|
});
|
|
1081
1018
|
}
|
|
1082
1019
|
// ============================================================================
|
|
@@ -1108,6 +1045,14 @@ async function sendThinkingReply(params) {
|
|
|
1108
1045
|
async function routeAndDispatchMessage(params) {
|
|
1109
1046
|
const { ctxPayload, config, wsClient, frame, state, runtime, onCleanup } = params;
|
|
1110
1047
|
const core = getWeComRuntime();
|
|
1048
|
+
// 防止 onCleanup 被多次调用(onError 回调与 catch 块可能重复触发)
|
|
1049
|
+
let cleanedUp = false;
|
|
1050
|
+
const safeCleanup = () => {
|
|
1051
|
+
if (!cleanedUp) {
|
|
1052
|
+
cleanedUp = true;
|
|
1053
|
+
onCleanup();
|
|
1054
|
+
}
|
|
1055
|
+
};
|
|
1111
1056
|
try {
|
|
1112
1057
|
await core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
1113
1058
|
ctx: ctxPayload,
|
|
@@ -1128,7 +1073,7 @@ async function routeAndDispatchMessage(params) {
|
|
|
1128
1073
|
},
|
|
1129
1074
|
onError: (err, info) => {
|
|
1130
1075
|
runtime.error?.(`[WeCom] ${info.kind} reply failed: ${String(err)}`);
|
|
1131
|
-
|
|
1076
|
+
// 仅记录错误,不立即 cleanup,让外层 try/catch 统一处理最终回复和 cleanup
|
|
1132
1077
|
},
|
|
1133
1078
|
},
|
|
1134
1079
|
});
|
|
@@ -1143,11 +1088,11 @@ async function routeAndDispatchMessage(params) {
|
|
|
1143
1088
|
streamId: state.streamId,
|
|
1144
1089
|
});
|
|
1145
1090
|
}
|
|
1146
|
-
|
|
1091
|
+
safeCleanup();
|
|
1147
1092
|
}
|
|
1148
1093
|
catch (err) {
|
|
1149
1094
|
runtime.error?.(`[WeCom] Failed to process message: ${String(err)}`);
|
|
1150
|
-
|
|
1095
|
+
safeCleanup();
|
|
1151
1096
|
}
|
|
1152
1097
|
}
|
|
1153
1098
|
/**
|
|
@@ -1362,19 +1307,72 @@ async function monitorWeComProvider(options) {
|
|
|
1362
1307
|
runtime.error?.(`[${account.accountId}] Failed to process message: ${String(err)}`);
|
|
1363
1308
|
}
|
|
1364
1309
|
});
|
|
1365
|
-
// 启动前预热 reqId
|
|
1310
|
+
// 启动前预热 reqId 缓存,确保完成后再建立连接,避免 getSync 在预热完成前返回 undefined
|
|
1366
1311
|
warmupReqIdStore(account.accountId, (...args) => runtime.log?.(...args))
|
|
1367
1312
|
.then((count) => {
|
|
1368
1313
|
runtime.log?.(`[${account.accountId}] Warmed up ${count} reqId entries from disk`);
|
|
1369
1314
|
})
|
|
1370
1315
|
.catch((err) => {
|
|
1371
1316
|
runtime.error?.(`[${account.accountId}] Failed to warmup reqId store: ${String(err)}`);
|
|
1317
|
+
})
|
|
1318
|
+
.finally(() => {
|
|
1319
|
+
// 无论预热成功或失败,都建立连接
|
|
1320
|
+
wsClient.connect();
|
|
1372
1321
|
});
|
|
1373
|
-
// 建立连接
|
|
1374
|
-
wsClient.connect();
|
|
1375
1322
|
});
|
|
1376
1323
|
}
|
|
1377
1324
|
|
|
1325
|
+
/**
|
|
1326
|
+
* 企业微信公共工具函数
|
|
1327
|
+
*/
|
|
1328
|
+
const DefaultWsUrl = "wss://openws.work.weixin.qq.com";
|
|
1329
|
+
/**
|
|
1330
|
+
* 解析企业微信账户配置
|
|
1331
|
+
*/
|
|
1332
|
+
function resolveWeComAccount(cfg) {
|
|
1333
|
+
const wecomConfig = (cfg.channels?.[CHANNEL_ID] ?? {});
|
|
1334
|
+
return {
|
|
1335
|
+
accountId: DEFAULT_ACCOUNT_ID,
|
|
1336
|
+
name: wecomConfig.name ?? "企业微信",
|
|
1337
|
+
enabled: wecomConfig.enabled ?? false,
|
|
1338
|
+
websocketUrl: wecomConfig.websocketUrl || DefaultWsUrl,
|
|
1339
|
+
botId: wecomConfig.botId ?? "",
|
|
1340
|
+
secret: wecomConfig.secret ?? "",
|
|
1341
|
+
sendThinkingMessage: wecomConfig.sendThinkingMessage ?? true,
|
|
1342
|
+
config: wecomConfig,
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
/**
|
|
1346
|
+
* 设置企业微信账户配置
|
|
1347
|
+
*/
|
|
1348
|
+
function setWeComAccount(cfg, account) {
|
|
1349
|
+
const existing = (cfg.channels?.[CHANNEL_ID] ?? {});
|
|
1350
|
+
const merged = {
|
|
1351
|
+
enabled: account.enabled ?? existing?.enabled ?? true,
|
|
1352
|
+
botId: account.botId ?? existing?.botId ?? "",
|
|
1353
|
+
secret: account.secret ?? existing?.secret ?? "",
|
|
1354
|
+
allowFrom: account.allowFrom ?? existing?.allowFrom,
|
|
1355
|
+
dmPolicy: account.dmPolicy ?? existing?.dmPolicy,
|
|
1356
|
+
// 以下字段仅在已有配置值或显式传入时才写入,onboarding 时不主动生成
|
|
1357
|
+
...(account.websocketUrl || existing?.websocketUrl
|
|
1358
|
+
? { websocketUrl: account.websocketUrl ?? existing?.websocketUrl }
|
|
1359
|
+
: {}),
|
|
1360
|
+
...(account.name || existing?.name
|
|
1361
|
+
? { name: account.name ?? existing?.name }
|
|
1362
|
+
: {}),
|
|
1363
|
+
...(account.sendThinkingMessage !== undefined || existing?.sendThinkingMessage !== undefined
|
|
1364
|
+
? { sendThinkingMessage: account.sendThinkingMessage ?? existing?.sendThinkingMessage }
|
|
1365
|
+
: {}),
|
|
1366
|
+
};
|
|
1367
|
+
return {
|
|
1368
|
+
...cfg,
|
|
1369
|
+
channels: {
|
|
1370
|
+
...cfg.channels,
|
|
1371
|
+
[CHANNEL_ID]: merged,
|
|
1372
|
+
},
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1378
1376
|
/**
|
|
1379
1377
|
* 企业微信 onboarding adapter for CLI setup wizard.
|
|
1380
1378
|
*/
|