@dingxiang-me/openclaw-wechat 1.4.1 → 1.7.2
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/CHANGELOG.md +119 -0
- package/README.en.md +89 -12
- package/README.md +103 -15
- package/docs/channels/wecom.md +28 -3
- package/openclaw.plugin.json +467 -10
- package/package.json +13 -2
- package/src/core/agent-routing.js +6 -0
- package/src/core.js +564 -35
- package/src/wecom/account-config-core.js +28 -8
- package/src/wecom/account-config.js +55 -0
- package/src/wecom/account-diagnostics.js +121 -0
- package/src/wecom/account-paths.js +39 -0
- package/src/wecom/agent-inbound-dispatch.js +9 -0
- package/src/wecom/agent-inbound-guards.js +24 -4
- package/src/wecom/agent-inbound-processor.js +27 -0
- package/src/wecom/agent-webhook-handler.js +11 -0
- package/src/wecom/bot-context.js +2 -1
- package/src/wecom/bot-dispatch-fallback.js +2 -1
- package/src/wecom/bot-inbound-content.js +73 -3
- package/src/wecom/bot-inbound-dispatch-runtime.js +2 -1
- package/src/wecom/bot-inbound-executor-helpers.js +56 -5
- package/src/wecom/bot-inbound-executor.js +19 -0
- package/src/wecom/bot-inbound-guards.js +36 -4
- package/src/wecom/bot-runtime-context.js +5 -3
- package/src/wecom/bot-webhook-dispatch.js +45 -12
- package/src/wecom/bot-webhook-handler.js +45 -13
- package/src/wecom/command-handlers.js +26 -0
- package/src/wecom/command-status-text.js +76 -7
- package/src/wecom/observability-metrics.js +133 -0
- package/src/wecom/outbound-agent-push.js +2 -1
- package/src/wecom/outbound-bot-card.js +103 -0
- package/src/wecom/outbound-delivery.js +92 -7
- package/src/wecom/outbound-response-delivery.js +10 -6
- package/src/wecom/outbound-webhook-delivery.js +42 -1
- package/src/wecom/plugin-account-policy-services.js +19 -0
- package/src/wecom/plugin-base-services.js +13 -0
- package/src/wecom/plugin-constants.js +1 -1
- package/src/wecom/plugin-delivery-inbound-services.js +8 -0
- package/src/wecom/plugin-processing-deps.js +4 -0
- package/src/wecom/plugin-route-runtime-deps.js +5 -0
- package/src/wecom/plugin-services.js +7 -0
- package/src/wecom/policy-resolvers.js +82 -5
- package/src/wecom/register-runtime.js +31 -2
- package/src/wecom/route-registration.js +173 -41
- package/src/wecom/runtime-utils.js +7 -2
- package/src/wecom/webhook-adapter.js +61 -0
- package/src/wecom/webhook-bot.js +26 -0
package/src/core.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
|
+
import { buildDefaultBotWebhookPath } from "./wecom/account-paths.js";
|
|
2
3
|
|
|
3
4
|
export const WECOM_TEXT_BYTE_LIMIT = 2000;
|
|
4
5
|
export const INBOUND_DEDUPE_TTL_MS = 5 * 60 * 1000;
|
|
@@ -41,17 +42,59 @@ const DEFAULT_COMMAND_ALLOWLIST = Object.freeze([
|
|
|
41
42
|
"/compact",
|
|
42
43
|
]);
|
|
43
44
|
const DEFAULT_ALLOW_FROM_REJECT_MESSAGE = "当前账号未授权,请联系管理员。";
|
|
45
|
+
const DEFAULT_EVENT_ENTER_AGENT_WELCOME_TEXT = "你好,我是 AI 助手,直接发消息即可开始对话。";
|
|
44
46
|
const DEFAULT_DELIVERY_FALLBACK_ORDER = Object.freeze([
|
|
45
47
|
"active_stream",
|
|
46
48
|
"response_url",
|
|
47
49
|
"webhook_bot",
|
|
48
50
|
"agent_push",
|
|
49
51
|
]);
|
|
52
|
+
const DEFAULT_BOT_CARD_MODE = "markdown";
|
|
53
|
+
const BOT_CARD_MODE_SET = new Set(["markdown", "template_card"]);
|
|
50
54
|
const DELIVERY_FALLBACK_LAYER_SET = new Set(DEFAULT_DELIVERY_FALLBACK_ORDER);
|
|
51
55
|
const DYNAMIC_AGENT_MAP_SPLITTER = /[,\n]/;
|
|
52
56
|
const GROUP_CHAT_TRIGGER_MODE_SET = new Set(["direct", "mention", "keyword"]);
|
|
57
|
+
const DM_POLICY_MODE_SET = new Set(["open", "allowlist", "deny"]);
|
|
53
58
|
const DYNAMIC_AGENT_MODE_SET = new Set(["mapping", "deterministic", "hybrid"]);
|
|
54
59
|
const DYNAMIC_AGENT_ID_STRATEGY_SET = new Set(["readable-hash"]);
|
|
60
|
+
const LEGACY_INLINE_ACCOUNT_RESERVED_KEYS = new Set([
|
|
61
|
+
"name",
|
|
62
|
+
"enabled",
|
|
63
|
+
"corpId",
|
|
64
|
+
"corpSecret",
|
|
65
|
+
"agentId",
|
|
66
|
+
"callbackToken",
|
|
67
|
+
"token",
|
|
68
|
+
"callbackAesKey",
|
|
69
|
+
"encodingAesKey",
|
|
70
|
+
"webhookPath",
|
|
71
|
+
"outboundProxy",
|
|
72
|
+
"proxyUrl",
|
|
73
|
+
"proxy",
|
|
74
|
+
"webhooks",
|
|
75
|
+
"allowFrom",
|
|
76
|
+
"allowFromRejectMessage",
|
|
77
|
+
"rejectUnauthorizedMessage",
|
|
78
|
+
"adminUsers",
|
|
79
|
+
"commandAllowlist",
|
|
80
|
+
"commandBlockMessage",
|
|
81
|
+
"commands",
|
|
82
|
+
"workspaceTemplate",
|
|
83
|
+
"groupChat",
|
|
84
|
+
"dynamicAgent",
|
|
85
|
+
"dynamicAgents",
|
|
86
|
+
"dm",
|
|
87
|
+
"debounce",
|
|
88
|
+
"streaming",
|
|
89
|
+
"bot",
|
|
90
|
+
"delivery",
|
|
91
|
+
"webhookBot",
|
|
92
|
+
"stream",
|
|
93
|
+
"observability",
|
|
94
|
+
"voiceTranscription",
|
|
95
|
+
"accounts",
|
|
96
|
+
"agent",
|
|
97
|
+
]);
|
|
55
98
|
|
|
56
99
|
const inboundMessageDedupe = new Map();
|
|
57
100
|
|
|
@@ -188,6 +231,32 @@ function normalizeAccountIdForEnv(accountId) {
|
|
|
188
231
|
return normalized || "default";
|
|
189
232
|
}
|
|
190
233
|
|
|
234
|
+
function resolveLegacyInlineAccountConfig(channelConfig, accountId) {
|
|
235
|
+
if (!channelConfig || typeof channelConfig !== "object") return null;
|
|
236
|
+
const normalizedAccountId = normalizeAccountIdForEnv(accountId);
|
|
237
|
+
for (const [key, value] of Object.entries(channelConfig)) {
|
|
238
|
+
const normalizedKey = normalizeAccountIdForEnv(key);
|
|
239
|
+
if (LEGACY_INLINE_ACCOUNT_RESERVED_KEYS.has(normalizedKey)) continue;
|
|
240
|
+
if (normalizedKey !== normalizedAccountId) continue;
|
|
241
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
242
|
+
return value;
|
|
243
|
+
}
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function collectLegacyInlineAccountIds(channelConfig) {
|
|
248
|
+
if (!channelConfig || typeof channelConfig !== "object") return [];
|
|
249
|
+
const ids = [];
|
|
250
|
+
for (const [key, value] of Object.entries(channelConfig)) {
|
|
251
|
+
const normalizedKey = normalizeAccountIdForEnv(key);
|
|
252
|
+
if (!normalizedKey) continue;
|
|
253
|
+
if (LEGACY_INLINE_ACCOUNT_RESERVED_KEYS.has(normalizedKey)) continue;
|
|
254
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
255
|
+
ids.push(normalizedKey);
|
|
256
|
+
}
|
|
257
|
+
return Array.from(new Set(ids));
|
|
258
|
+
}
|
|
259
|
+
|
|
191
260
|
function readAllowFromEnv(envVars, processEnv, accountId = "default") {
|
|
192
261
|
const normalizedId = normalizeAccountIdForEnv(accountId);
|
|
193
262
|
const scopedAllowFromKey = normalizedId === "default" ? null : `WECOM_${normalizedId.toUpperCase()}_ALLOW_FROM`;
|
|
@@ -211,6 +280,98 @@ function readAllowFromRejectMessageEnv(envVars, processEnv, accountId = "default
|
|
|
211
280
|
);
|
|
212
281
|
}
|
|
213
282
|
|
|
283
|
+
function readDmPolicyModeEnv(envVars, processEnv, accountId = "default") {
|
|
284
|
+
const normalizedId = normalizeAccountIdForEnv(accountId);
|
|
285
|
+
const scopedDmPolicyKey = normalizedId === "default" ? null : `WECOM_${normalizedId.toUpperCase()}_DM_POLICY`;
|
|
286
|
+
const scopedDmModeKey = normalizedId === "default" ? null : `WECOM_${normalizedId.toUpperCase()}_DM_MODE`;
|
|
287
|
+
return pickFirstNonEmptyString(
|
|
288
|
+
scopedDmPolicyKey ? envVars?.[scopedDmPolicyKey] : undefined,
|
|
289
|
+
scopedDmPolicyKey ? processEnv?.[scopedDmPolicyKey] : undefined,
|
|
290
|
+
scopedDmModeKey ? envVars?.[scopedDmModeKey] : undefined,
|
|
291
|
+
scopedDmModeKey ? processEnv?.[scopedDmModeKey] : undefined,
|
|
292
|
+
envVars?.WECOM_DM_POLICY,
|
|
293
|
+
processEnv?.WECOM_DM_POLICY,
|
|
294
|
+
envVars?.WECOM_DM_MODE,
|
|
295
|
+
processEnv?.WECOM_DM_MODE,
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function readDmAllowFromEnv(envVars, processEnv, accountId = "default") {
|
|
300
|
+
const normalizedId = normalizeAccountIdForEnv(accountId);
|
|
301
|
+
const scopedAllowFromKey = normalizedId === "default" ? null : `WECOM_${normalizedId.toUpperCase()}_DM_ALLOW_FROM`;
|
|
302
|
+
const scoped = parseStringList(
|
|
303
|
+
scopedAllowFromKey ? envVars?.[scopedAllowFromKey] : undefined,
|
|
304
|
+
scopedAllowFromKey ? processEnv?.[scopedAllowFromKey] : undefined,
|
|
305
|
+
);
|
|
306
|
+
if (scoped.length > 0) return scoped;
|
|
307
|
+
return parseStringList(envVars?.WECOM_DM_ALLOW_FROM, processEnv?.WECOM_DM_ALLOW_FROM);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function readDmRejectMessageEnv(envVars, processEnv, accountId = "default") {
|
|
311
|
+
const normalizedId = normalizeAccountIdForEnv(accountId);
|
|
312
|
+
const scopedRejectMessageKey =
|
|
313
|
+
normalizedId === "default" ? null : `WECOM_${normalizedId.toUpperCase()}_DM_REJECT_MESSAGE`;
|
|
314
|
+
return pickFirstNonEmptyString(
|
|
315
|
+
scopedRejectMessageKey ? envVars?.[scopedRejectMessageKey] : undefined,
|
|
316
|
+
scopedRejectMessageKey ? processEnv?.[scopedRejectMessageKey] : undefined,
|
|
317
|
+
envVars?.WECOM_DM_REJECT_MESSAGE,
|
|
318
|
+
processEnv?.WECOM_DM_REJECT_MESSAGE,
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function readEventEnabledEnv(envVars, processEnv, accountId = "default") {
|
|
323
|
+
const normalizedId = normalizeAccountIdForEnv(accountId);
|
|
324
|
+
const scopedEnabledKey = normalizedId === "default" ? null : `WECOM_${normalizedId.toUpperCase()}_EVENT_ENABLED`;
|
|
325
|
+
const scopedEventsEnabledKey =
|
|
326
|
+
normalizedId === "default" ? null : `WECOM_${normalizedId.toUpperCase()}_EVENTS_ENABLED`;
|
|
327
|
+
return pickFirstNonEmptyString(
|
|
328
|
+
scopedEnabledKey ? envVars?.[scopedEnabledKey] : undefined,
|
|
329
|
+
scopedEnabledKey ? processEnv?.[scopedEnabledKey] : undefined,
|
|
330
|
+
scopedEventsEnabledKey ? envVars?.[scopedEventsEnabledKey] : undefined,
|
|
331
|
+
scopedEventsEnabledKey ? processEnv?.[scopedEventsEnabledKey] : undefined,
|
|
332
|
+
envVars?.WECOM_EVENT_ENABLED,
|
|
333
|
+
processEnv?.WECOM_EVENT_ENABLED,
|
|
334
|
+
envVars?.WECOM_EVENTS_ENABLED,
|
|
335
|
+
processEnv?.WECOM_EVENTS_ENABLED,
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function readEventEnterAgentWelcomeEnabledEnv(envVars, processEnv, accountId = "default") {
|
|
340
|
+
const normalizedId = normalizeAccountIdForEnv(accountId);
|
|
341
|
+
const scopedEnabledKey =
|
|
342
|
+
normalizedId === "default" ? null : `WECOM_${normalizedId.toUpperCase()}_EVENT_ENTER_AGENT_WELCOME_ENABLED`;
|
|
343
|
+
const scopedEventsEnabledKey =
|
|
344
|
+
normalizedId === "default" ? null : `WECOM_${normalizedId.toUpperCase()}_EVENTS_ENTER_AGENT_WELCOME_ENABLED`;
|
|
345
|
+
return pickFirstNonEmptyString(
|
|
346
|
+
scopedEnabledKey ? envVars?.[scopedEnabledKey] : undefined,
|
|
347
|
+
scopedEnabledKey ? processEnv?.[scopedEnabledKey] : undefined,
|
|
348
|
+
scopedEventsEnabledKey ? envVars?.[scopedEventsEnabledKey] : undefined,
|
|
349
|
+
scopedEventsEnabledKey ? processEnv?.[scopedEventsEnabledKey] : undefined,
|
|
350
|
+
envVars?.WECOM_EVENT_ENTER_AGENT_WELCOME_ENABLED,
|
|
351
|
+
processEnv?.WECOM_EVENT_ENTER_AGENT_WELCOME_ENABLED,
|
|
352
|
+
envVars?.WECOM_EVENTS_ENTER_AGENT_WELCOME_ENABLED,
|
|
353
|
+
processEnv?.WECOM_EVENTS_ENTER_AGENT_WELCOME_ENABLED,
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function readEventEnterAgentWelcomeTextEnv(envVars, processEnv, accountId = "default") {
|
|
358
|
+
const normalizedId = normalizeAccountIdForEnv(accountId);
|
|
359
|
+
const scopedTextKey =
|
|
360
|
+
normalizedId === "default" ? null : `WECOM_${normalizedId.toUpperCase()}_EVENT_ENTER_AGENT_WELCOME_TEXT`;
|
|
361
|
+
const scopedEventsTextKey =
|
|
362
|
+
normalizedId === "default" ? null : `WECOM_${normalizedId.toUpperCase()}_EVENTS_ENTER_AGENT_WELCOME_TEXT`;
|
|
363
|
+
return pickFirstNonEmptyString(
|
|
364
|
+
scopedTextKey ? envVars?.[scopedTextKey] : undefined,
|
|
365
|
+
scopedTextKey ? processEnv?.[scopedTextKey] : undefined,
|
|
366
|
+
scopedEventsTextKey ? envVars?.[scopedEventsTextKey] : undefined,
|
|
367
|
+
scopedEventsTextKey ? processEnv?.[scopedEventsTextKey] : undefined,
|
|
368
|
+
envVars?.WECOM_EVENT_ENTER_AGENT_WELCOME_TEXT,
|
|
369
|
+
processEnv?.WECOM_EVENT_ENTER_AGENT_WELCOME_TEXT,
|
|
370
|
+
envVars?.WECOM_EVENTS_ENTER_AGENT_WELCOME_TEXT,
|
|
371
|
+
processEnv?.WECOM_EVENTS_ENTER_AGENT_WELCOME_TEXT,
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
|
|
214
375
|
function readProxyEnv(envVars, processEnv, accountId = "default") {
|
|
215
376
|
const normalizedId = normalizeAccountIdForEnv(accountId);
|
|
216
377
|
const scopedProxyKey = normalizedId === "default" ? null : `WECOM_${normalizedId.toUpperCase()}_PROXY`;
|
|
@@ -389,6 +550,27 @@ function uniqueDeliveryFallbackOrder(values) {
|
|
|
389
550
|
return deduped;
|
|
390
551
|
}
|
|
391
552
|
|
|
553
|
+
function normalizeWecomBotCardMode(value, fallback = DEFAULT_BOT_CARD_MODE) {
|
|
554
|
+
const normalized = String(value ?? "")
|
|
555
|
+
.trim()
|
|
556
|
+
.toLowerCase();
|
|
557
|
+
if (!normalized) return fallback;
|
|
558
|
+
if (normalized === "template-card" || normalized === "templatecard") return "template_card";
|
|
559
|
+
if (BOT_CARD_MODE_SET.has(normalized)) return normalized;
|
|
560
|
+
return fallback;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function normalizeWecomDmPolicyMode(value, fallback = "open") {
|
|
564
|
+
const normalized = String(value ?? "")
|
|
565
|
+
.trim()
|
|
566
|
+
.toLowerCase();
|
|
567
|
+
if (!normalized) return fallback;
|
|
568
|
+
if (normalized === "closed" || normalized === "close") return "deny";
|
|
569
|
+
if (normalized === "whitelist") return "allowlist";
|
|
570
|
+
if (DM_POLICY_MODE_SET.has(normalized)) return normalized;
|
|
571
|
+
return fallback;
|
|
572
|
+
}
|
|
573
|
+
|
|
392
574
|
export function normalizeWecomAllowFromEntry(raw) {
|
|
393
575
|
const trimmed = String(raw ?? "").trim();
|
|
394
576
|
if (!trimmed) return "";
|
|
@@ -541,23 +723,31 @@ export function resolveWecomCommandPolicyConfig({
|
|
|
541
723
|
} = {}) {
|
|
542
724
|
const commandConfig =
|
|
543
725
|
channelConfig?.commands && typeof channelConfig.commands === "object" ? channelConfig.commands : {};
|
|
544
|
-
const
|
|
545
|
-
commandConfig.enabled,
|
|
546
|
-
parseBooleanLike(envVars?.WECOM_COMMANDS_ENABLED, parseBooleanLike(processEnv?.WECOM_COMMANDS_ENABLED, false)),
|
|
547
|
-
);
|
|
726
|
+
const legacyAllowlist = uniqueCommandList(parseStringList(channelConfig?.commandAllowlist));
|
|
548
727
|
const configuredAllowlist = uniqueCommandList(
|
|
549
728
|
parseStringList(
|
|
550
729
|
commandConfig.allowlist,
|
|
730
|
+
legacyAllowlist,
|
|
551
731
|
envVars?.WECOM_COMMANDS_ALLOWLIST,
|
|
552
732
|
processEnv?.WECOM_COMMANDS_ALLOWLIST,
|
|
553
733
|
),
|
|
554
734
|
);
|
|
735
|
+
const allowlistEnabledByConfig = configuredAllowlist.length > 0;
|
|
736
|
+
const enabled = parseBooleanLike(
|
|
737
|
+
commandConfig.enabled,
|
|
738
|
+
parseBooleanLike(
|
|
739
|
+
envVars?.WECOM_COMMANDS_ENABLED,
|
|
740
|
+
parseBooleanLike(processEnv?.WECOM_COMMANDS_ENABLED, allowlistEnabledByConfig),
|
|
741
|
+
),
|
|
742
|
+
);
|
|
555
743
|
const allowlist = configuredAllowlist.length > 0 ? configuredAllowlist : Array.from(DEFAULT_COMMAND_ALLOWLIST);
|
|
556
744
|
const adminUsers = uniqueLowerCaseList(
|
|
557
745
|
parseStringList(channelConfig?.adminUsers, envVars?.WECOM_ADMIN_USERS, processEnv?.WECOM_ADMIN_USERS),
|
|
558
746
|
);
|
|
559
747
|
const rejectMessage = pickFirstNonEmptyString(
|
|
560
748
|
commandConfig.rejectMessage,
|
|
749
|
+
commandConfig.blockMessage,
|
|
750
|
+
channelConfig?.commandBlockMessage,
|
|
561
751
|
envVars?.WECOM_COMMANDS_REJECT_MESSAGE,
|
|
562
752
|
processEnv?.WECOM_COMMANDS_REJECT_MESSAGE,
|
|
563
753
|
"该指令未开放,请联系管理员。",
|
|
@@ -578,8 +768,12 @@ export function resolveWecomAllowFromPolicyConfig({
|
|
|
578
768
|
processEnv = process.env,
|
|
579
769
|
accountId = "default",
|
|
580
770
|
} = {}) {
|
|
581
|
-
const accountAllowFrom = uniqueAllowFromList(
|
|
582
|
-
|
|
771
|
+
const accountAllowFrom = uniqueAllowFromList(
|
|
772
|
+
parseStringList(accountConfig?.allowFrom, accountConfig?.dm?.allowFrom),
|
|
773
|
+
);
|
|
774
|
+
const channelAllowFrom = uniqueAllowFromList(
|
|
775
|
+
parseStringList(channelConfig?.allowFrom, channelConfig?.dm?.allowFrom),
|
|
776
|
+
);
|
|
583
777
|
const envAllowFrom = uniqueAllowFromList(readAllowFromEnv(envVars, processEnv, accountId));
|
|
584
778
|
const allowFrom = accountAllowFrom.length > 0 ? accountAllowFrom : channelAllowFrom.length > 0 ? channelAllowFrom : envAllowFrom;
|
|
585
779
|
const rejectMessage = pickFirstNonEmptyString(
|
|
@@ -596,6 +790,85 @@ export function resolveWecomAllowFromPolicyConfig({
|
|
|
596
790
|
};
|
|
597
791
|
}
|
|
598
792
|
|
|
793
|
+
export function resolveWecomDmPolicyConfig({
|
|
794
|
+
channelConfig = {},
|
|
795
|
+
accountConfig = {},
|
|
796
|
+
envVars = {},
|
|
797
|
+
processEnv = process.env,
|
|
798
|
+
accountId = "default",
|
|
799
|
+
} = {}) {
|
|
800
|
+
const channelDmConfig = channelConfig?.dm && typeof channelConfig.dm === "object" ? channelConfig.dm : {};
|
|
801
|
+
const accountDmConfig = accountConfig?.dm && typeof accountConfig.dm === "object" ? accountConfig.dm : {};
|
|
802
|
+
const mode = normalizeWecomDmPolicyMode(
|
|
803
|
+
pickFirstNonEmptyString(
|
|
804
|
+
accountDmConfig.mode,
|
|
805
|
+
channelDmConfig.mode,
|
|
806
|
+
readDmPolicyModeEnv(envVars, processEnv, accountId),
|
|
807
|
+
"open",
|
|
808
|
+
),
|
|
809
|
+
);
|
|
810
|
+
const allowFrom = uniqueAllowFromList(
|
|
811
|
+
parseStringList(
|
|
812
|
+
accountDmConfig.allowFrom,
|
|
813
|
+
channelDmConfig.allowFrom,
|
|
814
|
+
readDmAllowFromEnv(envVars, processEnv, accountId),
|
|
815
|
+
),
|
|
816
|
+
);
|
|
817
|
+
const rejectMessage = pickFirstNonEmptyString(
|
|
818
|
+
accountDmConfig.rejectMessage,
|
|
819
|
+
accountDmConfig.blockMessage,
|
|
820
|
+
channelDmConfig.rejectMessage,
|
|
821
|
+
channelDmConfig.blockMessage,
|
|
822
|
+
readDmRejectMessageEnv(envVars, processEnv, accountId),
|
|
823
|
+
mode === "deny" ? "当前渠道私聊已关闭,请联系管理员。" : "当前私聊账号未授权,请联系管理员。",
|
|
824
|
+
);
|
|
825
|
+
const effectiveMode = mode === "allowlist" && allowFrom.length === 0 ? "deny" : mode;
|
|
826
|
+
return {
|
|
827
|
+
mode: effectiveMode,
|
|
828
|
+
allowFrom,
|
|
829
|
+
rejectMessage,
|
|
830
|
+
enabled: effectiveMode !== "open" || allowFrom.length > 0,
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
export function resolveWecomEventPolicyConfig({
|
|
835
|
+
channelConfig = {},
|
|
836
|
+
accountConfig = {},
|
|
837
|
+
envVars = {},
|
|
838
|
+
processEnv = process.env,
|
|
839
|
+
accountId = "default",
|
|
840
|
+
} = {}) {
|
|
841
|
+
const channelEventConfig = channelConfig?.events && typeof channelConfig.events === "object" ? channelConfig.events : {};
|
|
842
|
+
const accountEventConfig = accountConfig?.events && typeof accountConfig.events === "object" ? accountConfig.events : {};
|
|
843
|
+
const enabled = parseBooleanLike(
|
|
844
|
+
accountEventConfig.enabled,
|
|
845
|
+
parseBooleanLike(
|
|
846
|
+
channelEventConfig.enabled,
|
|
847
|
+
parseBooleanLike(readEventEnabledEnv(envVars, processEnv, accountId), true),
|
|
848
|
+
),
|
|
849
|
+
);
|
|
850
|
+
const enterAgentWelcomeEnabled = enabled
|
|
851
|
+
? parseBooleanLike(
|
|
852
|
+
accountEventConfig.enterAgentWelcomeEnabled,
|
|
853
|
+
parseBooleanLike(
|
|
854
|
+
channelEventConfig.enterAgentWelcomeEnabled,
|
|
855
|
+
parseBooleanLike(readEventEnterAgentWelcomeEnabledEnv(envVars, processEnv, accountId), false),
|
|
856
|
+
),
|
|
857
|
+
)
|
|
858
|
+
: false;
|
|
859
|
+
const enterAgentWelcomeText = pickFirstNonEmptyString(
|
|
860
|
+
accountEventConfig.enterAgentWelcomeText,
|
|
861
|
+
channelEventConfig.enterAgentWelcomeText,
|
|
862
|
+
readEventEnterAgentWelcomeTextEnv(envVars, processEnv, accountId),
|
|
863
|
+
DEFAULT_EVENT_ENTER_AGENT_WELCOME_TEXT,
|
|
864
|
+
);
|
|
865
|
+
return {
|
|
866
|
+
enabled,
|
|
867
|
+
enterAgentWelcomeEnabled,
|
|
868
|
+
enterAgentWelcomeText,
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
|
|
599
872
|
export function isWecomSenderAllowed({ senderId, allowFrom = [] } = {}) {
|
|
600
873
|
const sender = normalizeWecomAllowFromEntry(senderId);
|
|
601
874
|
if (!sender) return false;
|
|
@@ -817,8 +1090,16 @@ export function resolveWecomDynamicAgentConfig({
|
|
|
817
1090
|
envVars = {},
|
|
818
1091
|
processEnv = process.env,
|
|
819
1092
|
} = {}) {
|
|
820
|
-
const
|
|
821
|
-
channelConfig?.dynamicAgent && typeof channelConfig.dynamicAgent === "object"
|
|
1093
|
+
const dynamicAgentConfig =
|
|
1094
|
+
channelConfig?.dynamicAgent && typeof channelConfig.dynamicAgent === "object"
|
|
1095
|
+
? channelConfig.dynamicAgent
|
|
1096
|
+
: {};
|
|
1097
|
+
const dynamicAgentsCompatConfig =
|
|
1098
|
+
channelConfig?.dynamicAgents && typeof channelConfig.dynamicAgents === "object"
|
|
1099
|
+
? channelConfig.dynamicAgents
|
|
1100
|
+
: {};
|
|
1101
|
+
const dynamicConfig = Object.keys(dynamicAgentConfig).length > 0 ? dynamicAgentConfig : dynamicAgentsCompatConfig;
|
|
1102
|
+
const dmCompatConfig = channelConfig?.dm && typeof channelConfig.dm === "object" ? channelConfig.dm : {};
|
|
822
1103
|
const enabled = parseBooleanLike(
|
|
823
1104
|
dynamicConfig.enabled,
|
|
824
1105
|
parseBooleanLike(
|
|
@@ -866,6 +1147,7 @@ export function resolveWecomDynamicAgentConfig({
|
|
|
866
1147
|
);
|
|
867
1148
|
const workspaceTemplate = pickFirstNonEmptyString(
|
|
868
1149
|
dynamicConfig.workspaceTemplate,
|
|
1150
|
+
channelConfig?.workspaceTemplate,
|
|
869
1151
|
envVars?.WECOM_DYNAMIC_AGENT_WORKSPACE_TEMPLATE,
|
|
870
1152
|
processEnv?.WECOM_DYNAMIC_AGENT_WORKSPACE_TEMPLATE,
|
|
871
1153
|
);
|
|
@@ -911,6 +1193,23 @@ export function resolveWecomDynamicAgentConfig({
|
|
|
911
1193
|
parseBooleanLike(processEnv?.WECOM_DYNAMIC_AGENT_ALLOW_FALLBACK, true),
|
|
912
1194
|
),
|
|
913
1195
|
);
|
|
1196
|
+
const dmCreateAgent = parseBooleanLike(
|
|
1197
|
+
dynamicConfig.dmCreateAgentOnFirstMessage,
|
|
1198
|
+
parseBooleanLike(
|
|
1199
|
+
dmCompatConfig.createAgentOnFirstMessage,
|
|
1200
|
+
parseBooleanLike(
|
|
1201
|
+
envVars?.WECOM_DM_CREATE_AGENT_ON_FIRST_MESSAGE,
|
|
1202
|
+
parseBooleanLike(processEnv?.WECOM_DM_CREATE_AGENT_ON_FIRST_MESSAGE, true),
|
|
1203
|
+
),
|
|
1204
|
+
),
|
|
1205
|
+
);
|
|
1206
|
+
const groupEnabled = parseBooleanLike(
|
|
1207
|
+
dynamicConfig.groupEnabled,
|
|
1208
|
+
parseBooleanLike(
|
|
1209
|
+
channelConfig?.groupChat?.enabled,
|
|
1210
|
+
parseBooleanLike(envVars?.WECOM_GROUP_CHAT_ENABLED, parseBooleanLike(processEnv?.WECOM_GROUP_CHAT_ENABLED, true)),
|
|
1211
|
+
),
|
|
1212
|
+
);
|
|
914
1213
|
const userMap = parseDynamicAgentMap(
|
|
915
1214
|
dynamicConfig.userMap,
|
|
916
1215
|
envVars?.WECOM_DYNAMIC_AGENT_USER_MAP,
|
|
@@ -941,6 +1240,8 @@ export function resolveWecomDynamicAgentConfig({
|
|
|
941
1240
|
forceAgentSessionKey,
|
|
942
1241
|
preferMentionMap,
|
|
943
1242
|
allowFallbackToDefaultRoute,
|
|
1243
|
+
dmCreateAgent,
|
|
1244
|
+
groupEnabled,
|
|
944
1245
|
userMap,
|
|
945
1246
|
groupMap,
|
|
946
1247
|
mentionMap,
|
|
@@ -1077,90 +1378,318 @@ export function resolveWecomObservabilityConfig({
|
|
|
1077
1378
|
};
|
|
1078
1379
|
}
|
|
1079
1380
|
|
|
1381
|
+
export function resolveWecomBotCardConfig({
|
|
1382
|
+
botConfig = {},
|
|
1383
|
+
envVars = {},
|
|
1384
|
+
processEnv = process.env,
|
|
1385
|
+
} = {}) {
|
|
1386
|
+
const cardConfig = botConfig?.card && typeof botConfig.card === "object" ? botConfig.card : {};
|
|
1387
|
+
const enabled = parseBooleanLike(
|
|
1388
|
+
cardConfig.enabled,
|
|
1389
|
+
parseBooleanLike(
|
|
1390
|
+
envVars?.WECOM_BOT_CARD_ENABLED,
|
|
1391
|
+
parseBooleanLike(processEnv?.WECOM_BOT_CARD_ENABLED, false),
|
|
1392
|
+
),
|
|
1393
|
+
);
|
|
1394
|
+
const mode = normalizeWecomBotCardMode(
|
|
1395
|
+
pickFirstNonEmptyString(
|
|
1396
|
+
cardConfig.mode,
|
|
1397
|
+
envVars?.WECOM_BOT_CARD_MODE,
|
|
1398
|
+
processEnv?.WECOM_BOT_CARD_MODE,
|
|
1399
|
+
DEFAULT_BOT_CARD_MODE,
|
|
1400
|
+
),
|
|
1401
|
+
);
|
|
1402
|
+
const title = pickFirstNonEmptyString(
|
|
1403
|
+
cardConfig.title,
|
|
1404
|
+
envVars?.WECOM_BOT_CARD_TITLE,
|
|
1405
|
+
processEnv?.WECOM_BOT_CARD_TITLE,
|
|
1406
|
+
"OpenClaw-Wechat",
|
|
1407
|
+
);
|
|
1408
|
+
const subtitle = pickFirstNonEmptyString(
|
|
1409
|
+
cardConfig.subtitle,
|
|
1410
|
+
cardConfig.subTitle,
|
|
1411
|
+
envVars?.WECOM_BOT_CARD_SUBTITLE,
|
|
1412
|
+
processEnv?.WECOM_BOT_CARD_SUBTITLE,
|
|
1413
|
+
);
|
|
1414
|
+
const footer = pickFirstNonEmptyString(
|
|
1415
|
+
cardConfig.footer,
|
|
1416
|
+
envVars?.WECOM_BOT_CARD_FOOTER,
|
|
1417
|
+
processEnv?.WECOM_BOT_CARD_FOOTER,
|
|
1418
|
+
);
|
|
1419
|
+
const maxContentLength = asBoundedPositiveInteger(
|
|
1420
|
+
cardConfig.maxContentLength ??
|
|
1421
|
+
cardConfig.maxBodyChars ??
|
|
1422
|
+
envVars?.WECOM_BOT_CARD_MAX_CONTENT_LENGTH ??
|
|
1423
|
+
processEnv?.WECOM_BOT_CARD_MAX_CONTENT_LENGTH,
|
|
1424
|
+
1400,
|
|
1425
|
+
200,
|
|
1426
|
+
4000,
|
|
1427
|
+
);
|
|
1428
|
+
const responseUrlEnabled = parseBooleanLike(
|
|
1429
|
+
cardConfig.responseUrlEnabled,
|
|
1430
|
+
parseBooleanLike(
|
|
1431
|
+
envVars?.WECOM_BOT_CARD_RESPONSE_URL_ENABLED,
|
|
1432
|
+
parseBooleanLike(processEnv?.WECOM_BOT_CARD_RESPONSE_URL_ENABLED, true),
|
|
1433
|
+
),
|
|
1434
|
+
);
|
|
1435
|
+
const webhookBotEnabled = parseBooleanLike(
|
|
1436
|
+
cardConfig.webhookBotEnabled,
|
|
1437
|
+
parseBooleanLike(
|
|
1438
|
+
envVars?.WECOM_BOT_CARD_WEBHOOK_BOT_ENABLED,
|
|
1439
|
+
parseBooleanLike(processEnv?.WECOM_BOT_CARD_WEBHOOK_BOT_ENABLED, true),
|
|
1440
|
+
),
|
|
1441
|
+
);
|
|
1442
|
+
return {
|
|
1443
|
+
enabled,
|
|
1444
|
+
mode,
|
|
1445
|
+
title,
|
|
1446
|
+
subtitle: subtitle || undefined,
|
|
1447
|
+
footer: footer || undefined,
|
|
1448
|
+
maxContentLength,
|
|
1449
|
+
responseUrlEnabled,
|
|
1450
|
+
webhookBotEnabled,
|
|
1451
|
+
};
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1080
1454
|
export function resolveWecomBotModeConfig({
|
|
1081
1455
|
channelConfig = {},
|
|
1082
1456
|
envVars = {},
|
|
1083
1457
|
processEnv = process.env,
|
|
1458
|
+
accountId = "default",
|
|
1459
|
+
botConfigOverride,
|
|
1084
1460
|
} = {}) {
|
|
1085
|
-
const
|
|
1461
|
+
const normalizedAccountId = normalizeAccountIdForEnv(accountId);
|
|
1462
|
+
const legacyInlineAccountConfig = resolveLegacyInlineAccountConfig(channelConfig, normalizedAccountId);
|
|
1463
|
+
const defaultInlineAccountConfig = resolveLegacyInlineAccountConfig(channelConfig, "default");
|
|
1464
|
+
const accountConfig =
|
|
1465
|
+
normalizedAccountId === "default"
|
|
1466
|
+
? (defaultInlineAccountConfig ?? channelConfig)
|
|
1467
|
+
: channelConfig?.accounts && typeof channelConfig.accounts === "object"
|
|
1468
|
+
? (channelConfig.accounts[normalizedAccountId] ?? legacyInlineAccountConfig)
|
|
1469
|
+
: legacyInlineAccountConfig;
|
|
1470
|
+
const scopedBotConfig =
|
|
1471
|
+
normalizedAccountId === "default"
|
|
1472
|
+
? channelConfig?.bot
|
|
1473
|
+
: accountConfig && typeof accountConfig === "object"
|
|
1474
|
+
? accountConfig.bot
|
|
1475
|
+
: null;
|
|
1476
|
+
const botConfig =
|
|
1477
|
+
botConfigOverride && typeof botConfigOverride === "object"
|
|
1478
|
+
? botConfigOverride
|
|
1479
|
+
: scopedBotConfig && typeof scopedBotConfig === "object"
|
|
1480
|
+
? scopedBotConfig
|
|
1481
|
+
: {};
|
|
1482
|
+
|
|
1483
|
+
const scopedEnvVars = { ...(envVars && typeof envVars === "object" ? envVars : {}) };
|
|
1484
|
+
const scopedProcessEnv = { ...(processEnv && typeof processEnv === "object" ? processEnv : {}) };
|
|
1485
|
+
const botEnvSuffixes = [
|
|
1486
|
+
"ENABLED",
|
|
1487
|
+
"TOKEN",
|
|
1488
|
+
"ENCODING_AES_KEY",
|
|
1489
|
+
"WEBHOOK_PATH",
|
|
1490
|
+
"PLACEHOLDER_TEXT",
|
|
1491
|
+
"STREAM_EXPIRE_MS",
|
|
1492
|
+
"REPLY_TIMEOUT_MS",
|
|
1493
|
+
"LATE_REPLY_WATCH_MS",
|
|
1494
|
+
"LATE_REPLY_POLL_MS",
|
|
1495
|
+
"CARD_ENABLED",
|
|
1496
|
+
"CARD_MODE",
|
|
1497
|
+
"CARD_TITLE",
|
|
1498
|
+
"CARD_SUBTITLE",
|
|
1499
|
+
"CARD_FOOTER",
|
|
1500
|
+
"CARD_MAX_CONTENT_LENGTH",
|
|
1501
|
+
"CARD_RESPONSE_URL_ENABLED",
|
|
1502
|
+
"CARD_WEBHOOK_BOT_ENABLED",
|
|
1503
|
+
];
|
|
1504
|
+
if (normalizedAccountId !== "default") {
|
|
1505
|
+
const accountPrefix = `WECOM_${normalizedAccountId.toUpperCase()}_BOT_`;
|
|
1506
|
+
for (const suffix of botEnvSuffixes) {
|
|
1507
|
+
const scopedKey = `${accountPrefix}${suffix}`;
|
|
1508
|
+
const mappedKey = `WECOM_BOT_${suffix}`;
|
|
1509
|
+
if (Object.prototype.hasOwnProperty.call(scopedEnvVars, scopedKey)) {
|
|
1510
|
+
scopedEnvVars[mappedKey] = scopedEnvVars[scopedKey];
|
|
1511
|
+
}
|
|
1512
|
+
if (Object.prototype.hasOwnProperty.call(scopedProcessEnv, scopedKey)) {
|
|
1513
|
+
scopedProcessEnv[mappedKey] = scopedProcessEnv[scopedKey];
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1086
1517
|
const enabled = parseBooleanLike(
|
|
1087
1518
|
botConfig.enabled,
|
|
1088
|
-
parseBooleanLike(
|
|
1519
|
+
parseBooleanLike(scopedEnvVars?.WECOM_BOT_ENABLED, parseBooleanLike(scopedProcessEnv?.WECOM_BOT_ENABLED, false)),
|
|
1089
1520
|
);
|
|
1521
|
+
const legacyAgentCompat =
|
|
1522
|
+
accountConfig?.agent && typeof accountConfig.agent === "object" ? accountConfig.agent : null;
|
|
1523
|
+
const legacyTopLevelBotToken = legacyAgentCompat ? pickFirstNonEmptyString(accountConfig?.token) : "";
|
|
1524
|
+
const legacyTopLevelBotAesKey = legacyAgentCompat ? pickFirstNonEmptyString(accountConfig?.encodingAesKey) : "";
|
|
1525
|
+
const legacyTopLevelBotWebhookPath = legacyAgentCompat ? pickFirstNonEmptyString(accountConfig?.webhookPath) : "";
|
|
1090
1526
|
const token = pickFirstNonEmptyString(
|
|
1091
1527
|
botConfig.token,
|
|
1092
|
-
|
|
1093
|
-
|
|
1528
|
+
botConfig.callbackToken,
|
|
1529
|
+
legacyTopLevelBotToken,
|
|
1530
|
+
scopedEnvVars?.WECOM_BOT_TOKEN,
|
|
1531
|
+
scopedProcessEnv?.WECOM_BOT_TOKEN,
|
|
1094
1532
|
);
|
|
1095
1533
|
const encodingAesKey = pickFirstNonEmptyString(
|
|
1096
1534
|
botConfig.encodingAesKey,
|
|
1097
|
-
|
|
1098
|
-
|
|
1535
|
+
botConfig.callbackAesKey,
|
|
1536
|
+
legacyTopLevelBotAesKey,
|
|
1537
|
+
scopedEnvVars?.WECOM_BOT_ENCODING_AES_KEY,
|
|
1538
|
+
scopedProcessEnv?.WECOM_BOT_ENCODING_AES_KEY,
|
|
1099
1539
|
);
|
|
1100
1540
|
const webhookPath = pickFirstNonEmptyString(
|
|
1101
1541
|
botConfig.webhookPath,
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1542
|
+
legacyTopLevelBotWebhookPath,
|
|
1543
|
+
scopedEnvVars?.WECOM_BOT_WEBHOOK_PATH,
|
|
1544
|
+
scopedProcessEnv?.WECOM_BOT_WEBHOOK_PATH,
|
|
1545
|
+
buildDefaultBotWebhookPath(normalizedAccountId),
|
|
1105
1546
|
);
|
|
1106
1547
|
const hasOwn = (obj, key) => Object.prototype.hasOwnProperty.call(obj ?? {}, key);
|
|
1107
1548
|
const placeholderText = (() => {
|
|
1108
1549
|
if (hasOwn(botConfig, "placeholderText")) return String(botConfig.placeholderText ?? "");
|
|
1109
|
-
if (hasOwn(
|
|
1110
|
-
if (hasOwn(
|
|
1111
|
-
return String(
|
|
1550
|
+
if (hasOwn(scopedEnvVars, "WECOM_BOT_PLACEHOLDER_TEXT")) return String(scopedEnvVars.WECOM_BOT_PLACEHOLDER_TEXT ?? "");
|
|
1551
|
+
if (hasOwn(scopedProcessEnv, "WECOM_BOT_PLACEHOLDER_TEXT"))
|
|
1552
|
+
return String(scopedProcessEnv.WECOM_BOT_PLACEHOLDER_TEXT ?? "");
|
|
1112
1553
|
return "消息已收到,正在处理中,请稍等片刻。";
|
|
1113
1554
|
})();
|
|
1114
1555
|
const streamExpireMs = asBoundedPositiveInteger(
|
|
1115
1556
|
botConfig.streamExpireMs ??
|
|
1116
|
-
|
|
1117
|
-
|
|
1557
|
+
scopedEnvVars?.WECOM_BOT_STREAM_EXPIRE_MS ??
|
|
1558
|
+
scopedProcessEnv?.WECOM_BOT_STREAM_EXPIRE_MS,
|
|
1118
1559
|
10 * 60 * 1000,
|
|
1119
1560
|
30 * 1000,
|
|
1120
1561
|
60 * 60 * 1000,
|
|
1121
1562
|
);
|
|
1122
1563
|
const replyTimeoutMs = asBoundedPositiveInteger(
|
|
1123
1564
|
botConfig.replyTimeoutMs ??
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1565
|
+
scopedEnvVars?.WECOM_BOT_REPLY_TIMEOUT_MS ??
|
|
1566
|
+
scopedProcessEnv?.WECOM_BOT_REPLY_TIMEOUT_MS ??
|
|
1567
|
+
scopedEnvVars?.WECOM_REPLY_TIMEOUT_MS ??
|
|
1568
|
+
scopedProcessEnv?.WECOM_REPLY_TIMEOUT_MS,
|
|
1128
1569
|
90000,
|
|
1129
1570
|
15000,
|
|
1130
1571
|
10 * 60 * 1000,
|
|
1131
1572
|
);
|
|
1132
1573
|
const lateReplyWatchMs = asBoundedPositiveInteger(
|
|
1133
1574
|
botConfig.lateReplyWatchMs ??
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1575
|
+
scopedEnvVars?.WECOM_BOT_LATE_REPLY_WATCH_MS ??
|
|
1576
|
+
scopedProcessEnv?.WECOM_BOT_LATE_REPLY_WATCH_MS ??
|
|
1577
|
+
scopedEnvVars?.WECOM_LATE_REPLY_WATCH_MS ??
|
|
1578
|
+
scopedProcessEnv?.WECOM_LATE_REPLY_WATCH_MS,
|
|
1138
1579
|
180000,
|
|
1139
1580
|
30000,
|
|
1140
1581
|
10 * 60 * 1000,
|
|
1141
1582
|
);
|
|
1142
1583
|
const lateReplyPollMs = asBoundedPositiveInteger(
|
|
1143
1584
|
botConfig.lateReplyPollMs ??
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1585
|
+
scopedEnvVars?.WECOM_BOT_LATE_REPLY_POLL_MS ??
|
|
1586
|
+
scopedProcessEnv?.WECOM_BOT_LATE_REPLY_POLL_MS ??
|
|
1587
|
+
scopedEnvVars?.WECOM_LATE_REPLY_POLL_MS ??
|
|
1588
|
+
scopedProcessEnv?.WECOM_LATE_REPLY_POLL_MS,
|
|
1148
1589
|
2000,
|
|
1149
1590
|
500,
|
|
1150
1591
|
10000,
|
|
1151
1592
|
);
|
|
1593
|
+
const card = resolveWecomBotCardConfig({
|
|
1594
|
+
botConfig,
|
|
1595
|
+
envVars: scopedEnvVars,
|
|
1596
|
+
processEnv: scopedProcessEnv,
|
|
1597
|
+
});
|
|
1152
1598
|
|
|
1153
1599
|
return {
|
|
1600
|
+
accountId: normalizedAccountId,
|
|
1154
1601
|
enabled,
|
|
1155
1602
|
token: token || undefined,
|
|
1156
1603
|
encodingAesKey: encodingAesKey || undefined,
|
|
1157
|
-
webhookPath: webhookPath ||
|
|
1604
|
+
webhookPath: webhookPath || buildDefaultBotWebhookPath(normalizedAccountId),
|
|
1158
1605
|
placeholderText,
|
|
1159
1606
|
streamExpireMs,
|
|
1160
1607
|
replyTimeoutMs,
|
|
1161
1608
|
lateReplyWatchMs,
|
|
1162
1609
|
lateReplyPollMs,
|
|
1610
|
+
card,
|
|
1611
|
+
};
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
export function resolveWecomBotModeAccountsConfig({
|
|
1615
|
+
channelConfig = {},
|
|
1616
|
+
envVars = {},
|
|
1617
|
+
processEnv = process.env,
|
|
1618
|
+
} = {}) {
|
|
1619
|
+
const accountIds = new Set(["default"]);
|
|
1620
|
+
const channelAccounts = channelConfig?.accounts;
|
|
1621
|
+
if (channelAccounts && typeof channelAccounts === "object") {
|
|
1622
|
+
for (const accountId of Object.keys(channelAccounts)) {
|
|
1623
|
+
accountIds.add(normalizeAccountIdForEnv(accountId));
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
for (const accountId of collectLegacyInlineAccountIds(channelConfig)) {
|
|
1627
|
+
accountIds.add(normalizeAccountIdForEnv(accountId));
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
const scopedBotIdRegex =
|
|
1631
|
+
/^WECOM_([A-Z0-9]+)_BOT_(ENABLED|TOKEN|ENCODING_AES_KEY|WEBHOOK_PATH|PLACEHOLDER_TEXT|STREAM_EXPIRE_MS|REPLY_TIMEOUT_MS|LATE_REPLY_WATCH_MS|LATE_REPLY_POLL_MS|PROXY|CARD_ENABLED|CARD_MODE|CARD_TITLE|CARD_SUBTITLE|CARD_FOOTER|CARD_MAX_CONTENT_LENGTH|CARD_RESPONSE_URL_ENABLED|CARD_WEBHOOK_BOT_ENABLED)$/;
|
|
1632
|
+
const collectScopedIds = (obj) => {
|
|
1633
|
+
if (!obj || typeof obj !== "object") return;
|
|
1634
|
+
for (const key of Object.keys(obj)) {
|
|
1635
|
+
const match = key.match(scopedBotIdRegex);
|
|
1636
|
+
if (!match) continue;
|
|
1637
|
+
const candidate = String(match[1] ?? "").trim().toLowerCase();
|
|
1638
|
+
if (candidate) accountIds.add(candidate);
|
|
1639
|
+
}
|
|
1640
|
+
};
|
|
1641
|
+
collectScopedIds(envVars);
|
|
1642
|
+
collectScopedIds(processEnv);
|
|
1643
|
+
|
|
1644
|
+
const hasScopedBotEnv = (accountId) => {
|
|
1645
|
+
const normalizedAccountId = normalizeAccountIdForEnv(accountId);
|
|
1646
|
+
if (normalizedAccountId === "default") return false;
|
|
1647
|
+
const prefix = `WECOM_${normalizedAccountId.toUpperCase()}_BOT_`;
|
|
1648
|
+
const hasIn = (obj) =>
|
|
1649
|
+
Boolean(
|
|
1650
|
+
obj &&
|
|
1651
|
+
typeof obj === "object" &&
|
|
1652
|
+
Object.keys(obj).some((key) => String(key ?? "").startsWith(prefix)),
|
|
1653
|
+
);
|
|
1654
|
+
return hasIn(envVars) || hasIn(processEnv);
|
|
1163
1655
|
};
|
|
1656
|
+
|
|
1657
|
+
const ordered = Array.from(accountIds).sort((a, b) => {
|
|
1658
|
+
if (a === "default" && b !== "default") return -1;
|
|
1659
|
+
if (a !== "default" && b === "default") return 1;
|
|
1660
|
+
return a.localeCompare(b);
|
|
1661
|
+
});
|
|
1662
|
+
|
|
1663
|
+
const botConfigs = [];
|
|
1664
|
+
for (const accountId of ordered) {
|
|
1665
|
+
const resolved = resolveWecomBotModeConfig({
|
|
1666
|
+
channelConfig,
|
|
1667
|
+
envVars,
|
|
1668
|
+
processEnv,
|
|
1669
|
+
accountId,
|
|
1670
|
+
});
|
|
1671
|
+
const normalizedAccountId = normalizeAccountIdForEnv(accountId);
|
|
1672
|
+
const accountCfg =
|
|
1673
|
+
normalizedAccountId === "default"
|
|
1674
|
+
? channelConfig
|
|
1675
|
+
: channelConfig?.accounts && typeof channelConfig.accounts === "object"
|
|
1676
|
+
? (channelConfig.accounts[normalizedAccountId] ??
|
|
1677
|
+
resolveLegacyInlineAccountConfig(channelConfig, normalizedAccountId))
|
|
1678
|
+
: resolveLegacyInlineAccountConfig(channelConfig, normalizedAccountId);
|
|
1679
|
+
const hasBotConfigObject = Boolean(accountCfg && typeof accountCfg === "object" && accountCfg.bot && typeof accountCfg.bot === "object");
|
|
1680
|
+
if (
|
|
1681
|
+
normalizedAccountId !== "default" &&
|
|
1682
|
+
!hasBotConfigObject &&
|
|
1683
|
+
!hasScopedBotEnv(normalizedAccountId) &&
|
|
1684
|
+
resolved.enabled !== true &&
|
|
1685
|
+
!resolved.token &&
|
|
1686
|
+
!resolved.encodingAesKey
|
|
1687
|
+
) {
|
|
1688
|
+
continue;
|
|
1689
|
+
}
|
|
1690
|
+
botConfigs.push(resolved);
|
|
1691
|
+
}
|
|
1692
|
+
return botConfigs;
|
|
1164
1693
|
}
|
|
1165
1694
|
|
|
1166
1695
|
function readVoiceEnv(envVars, processEnv, suffix) {
|