@openclaw-china/dingtalk 2026.3.7 → 2026.3.8-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/dist/index.d.ts +525 -69
- package/dist/index.js +524 -105
- package/dist/index.js.map +1 -1
- package/openclaw.plugin.json +55 -11
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -4215,13 +4215,24 @@ var coerce = {
|
|
|
4215
4215
|
date: ((arg) => ZodDate.create({ ...arg, coerce: true }))
|
|
4216
4216
|
};
|
|
4217
4217
|
var NEVER = INVALID;
|
|
4218
|
-
|
|
4218
|
+
function toTrimmedString(value) {
|
|
4219
|
+
if (value === void 0 || value === null) return void 0;
|
|
4220
|
+
const next = String(value).trim();
|
|
4221
|
+
return next ? next : void 0;
|
|
4222
|
+
}
|
|
4223
|
+
var optionalCoercedString = external_exports.preprocess(
|
|
4224
|
+
(value) => toTrimmedString(value),
|
|
4225
|
+
external_exports.string().min(1).optional()
|
|
4226
|
+
);
|
|
4227
|
+
var DingtalkAccountSchema = external_exports.object({
|
|
4228
|
+
/** 账户显示名 */
|
|
4229
|
+
name: external_exports.string().optional(),
|
|
4219
4230
|
/** 是否启用钉钉渠道 */
|
|
4220
4231
|
enabled: external_exports.boolean().optional().default(true),
|
|
4221
4232
|
/** 钉钉应用 AppKey (clientId) */
|
|
4222
|
-
clientId:
|
|
4233
|
+
clientId: optionalCoercedString,
|
|
4223
4234
|
/** 钉钉应用 AppSecret (clientSecret) */
|
|
4224
|
-
clientSecret:
|
|
4235
|
+
clientSecret: optionalCoercedString,
|
|
4225
4236
|
/** 单聊策略: open=开放, pairing=配对, allowlist=白名单 */
|
|
4226
4237
|
dmPolicy: external_exports.enum(["open", "pairing", "allowlist"]).optional().default("open"),
|
|
4227
4238
|
/** 群聊策略: open=开放, allowlist=白名单, disabled=禁用 */
|
|
@@ -4252,6 +4263,29 @@ var DingtalkConfigSchema = external_exports.object({
|
|
|
4252
4263
|
keepDays: external_exports.number().optional()
|
|
4253
4264
|
}).optional()
|
|
4254
4265
|
});
|
|
4266
|
+
DingtalkAccountSchema.extend({
|
|
4267
|
+
defaultAccount: external_exports.string().optional(),
|
|
4268
|
+
accounts: external_exports.record(DingtalkAccountSchema).optional()
|
|
4269
|
+
});
|
|
4270
|
+
var DEFAULT_ACCOUNT_ID = "default";
|
|
4271
|
+
var DINGTALK_ACCOUNT_KEYS = [
|
|
4272
|
+
"name",
|
|
4273
|
+
"clientId",
|
|
4274
|
+
"clientSecret",
|
|
4275
|
+
"dmPolicy",
|
|
4276
|
+
"groupPolicy",
|
|
4277
|
+
"requireMention",
|
|
4278
|
+
"allowFrom",
|
|
4279
|
+
"groupAllowFrom",
|
|
4280
|
+
"historyLimit",
|
|
4281
|
+
"textChunkLimit",
|
|
4282
|
+
"longTaskNoticeDelayMs",
|
|
4283
|
+
"enableAICard",
|
|
4284
|
+
"gatewayToken",
|
|
4285
|
+
"gatewayPassword",
|
|
4286
|
+
"maxFileSizeMB",
|
|
4287
|
+
"inboundMedia"
|
|
4288
|
+
];
|
|
4255
4289
|
var DEFAULT_INBOUND_MEDIA_DIR = join(homedir(), ".openclaw", "media", "dingtalk", "inbound");
|
|
4256
4290
|
var DEFAULT_INBOUND_MEDIA_KEEP_DAYS = 7;
|
|
4257
4291
|
var DEFAULT_INBOUND_MEDIA_TEMP_DIR = join(tmpdir(), "dingtalk-media");
|
|
@@ -4265,16 +4299,116 @@ function resolveInboundMediaKeepDays(config) {
|
|
|
4265
4299
|
function resolveInboundMediaTempDir() {
|
|
4266
4300
|
return DEFAULT_INBOUND_MEDIA_TEMP_DIR;
|
|
4267
4301
|
}
|
|
4302
|
+
function cloneIfObject(value) {
|
|
4303
|
+
if (value && typeof value === "object") {
|
|
4304
|
+
return structuredClone(value);
|
|
4305
|
+
}
|
|
4306
|
+
return value;
|
|
4307
|
+
}
|
|
4308
|
+
function baseLooksLikeConcreteAccount(cfg) {
|
|
4309
|
+
if (!cfg) return false;
|
|
4310
|
+
return Boolean(
|
|
4311
|
+
toTrimmedString(cfg.clientId) || toTrimmedString(cfg.clientSecret) || toTrimmedString(cfg.gatewayToken) || toTrimmedString(cfg.gatewayPassword) || toTrimmedString(cfg.name)
|
|
4312
|
+
);
|
|
4313
|
+
}
|
|
4314
|
+
function listConfiguredAccountIds(cfg) {
|
|
4315
|
+
const accounts = cfg.channels?.dingtalk?.accounts;
|
|
4316
|
+
if (!accounts || typeof accounts !== "object") return [];
|
|
4317
|
+
return Object.keys(accounts).filter(Boolean);
|
|
4318
|
+
}
|
|
4319
|
+
function listDingtalkAccountIds(cfg) {
|
|
4320
|
+
const ids = new Set(listConfiguredAccountIds(cfg));
|
|
4321
|
+
if (ids.size === 0) return [DEFAULT_ACCOUNT_ID];
|
|
4322
|
+
if (baseLooksLikeConcreteAccount(cfg.channels?.dingtalk) && !ids.has(DEFAULT_ACCOUNT_ID)) {
|
|
4323
|
+
ids.add(DEFAULT_ACCOUNT_ID);
|
|
4324
|
+
}
|
|
4325
|
+
return Array.from(ids).sort((a, b) => a.localeCompare(b));
|
|
4326
|
+
}
|
|
4327
|
+
function resolveDefaultDingtalkAccountId(cfg) {
|
|
4328
|
+
const dingtalkConfig = cfg.channels?.dingtalk;
|
|
4329
|
+
const preferred = toTrimmedString(dingtalkConfig?.defaultAccount);
|
|
4330
|
+
if (preferred && listDingtalkAccountIds(cfg).includes(preferred)) {
|
|
4331
|
+
return preferred;
|
|
4332
|
+
}
|
|
4333
|
+
const ids = listDingtalkAccountIds(cfg);
|
|
4334
|
+
if (ids.includes(DEFAULT_ACCOUNT_ID)) return DEFAULT_ACCOUNT_ID;
|
|
4335
|
+
return ids[0] ?? DEFAULT_ACCOUNT_ID;
|
|
4336
|
+
}
|
|
4337
|
+
function resolveDingtalkAccountId(cfg, rawAccountId) {
|
|
4338
|
+
return toTrimmedString(rawAccountId) ?? resolveDefaultDingtalkAccountId(cfg);
|
|
4339
|
+
}
|
|
4340
|
+
function resolveAccountConfig(cfg, accountId) {
|
|
4341
|
+
const accounts = cfg.channels?.dingtalk?.accounts;
|
|
4342
|
+
if (!accounts || typeof accounts !== "object") return void 0;
|
|
4343
|
+
return accounts[accountId];
|
|
4344
|
+
}
|
|
4345
|
+
function extractBaseAccountPatch(cfg) {
|
|
4346
|
+
const patch = {};
|
|
4347
|
+
const patchRecord = patch;
|
|
4348
|
+
if (!cfg) return patch;
|
|
4349
|
+
for (const key of DINGTALK_ACCOUNT_KEYS) {
|
|
4350
|
+
const value = cfg[key];
|
|
4351
|
+
if (value !== void 0) {
|
|
4352
|
+
patchRecord[key] = cloneIfObject(value);
|
|
4353
|
+
}
|
|
4354
|
+
}
|
|
4355
|
+
return patch;
|
|
4356
|
+
}
|
|
4357
|
+
function moveDingtalkSingleAccountConfigToDefaultAccount(cfg) {
|
|
4358
|
+
const dingtalkConfig = cfg.channels?.dingtalk;
|
|
4359
|
+
if (!dingtalkConfig) {
|
|
4360
|
+
return cfg;
|
|
4361
|
+
}
|
|
4362
|
+
const accounts = dingtalkConfig.accounts ?? {};
|
|
4363
|
+
if (accounts[DEFAULT_ACCOUNT_ID]) {
|
|
4364
|
+
return cfg;
|
|
4365
|
+
}
|
|
4366
|
+
if (!baseLooksLikeConcreteAccount(dingtalkConfig)) {
|
|
4367
|
+
return cfg;
|
|
4368
|
+
}
|
|
4369
|
+
const patch = extractBaseAccountPatch(dingtalkConfig);
|
|
4370
|
+
if (Object.keys(patch).length === 0) {
|
|
4371
|
+
return cfg;
|
|
4372
|
+
}
|
|
4373
|
+
const nextChannel = { ...dingtalkConfig };
|
|
4374
|
+
for (const key of DINGTALK_ACCOUNT_KEYS) {
|
|
4375
|
+
delete nextChannel[key];
|
|
4376
|
+
}
|
|
4377
|
+
return {
|
|
4378
|
+
...cfg,
|
|
4379
|
+
channels: {
|
|
4380
|
+
...cfg.channels,
|
|
4381
|
+
dingtalk: {
|
|
4382
|
+
...nextChannel,
|
|
4383
|
+
accounts: {
|
|
4384
|
+
...accounts,
|
|
4385
|
+
[DEFAULT_ACCOUNT_ID]: {
|
|
4386
|
+
...patch
|
|
4387
|
+
}
|
|
4388
|
+
}
|
|
4389
|
+
}
|
|
4390
|
+
}
|
|
4391
|
+
};
|
|
4392
|
+
}
|
|
4393
|
+
function mergeDingtalkAccountConfig(cfg, accountId) {
|
|
4394
|
+
const base = cfg.channels?.dingtalk ?? {};
|
|
4395
|
+
const { accounts: _ignored, defaultAccount: _ignored2, ...baseConfig } = base;
|
|
4396
|
+
const account = resolveAccountConfig(cfg, accountId) ?? {};
|
|
4397
|
+
return { ...baseConfig, ...account };
|
|
4398
|
+
}
|
|
4268
4399
|
function isConfigured(config) {
|
|
4269
|
-
|
|
4400
|
+
const credentials = resolveDingtalkCredentials(config);
|
|
4401
|
+
return Boolean(credentials);
|
|
4270
4402
|
}
|
|
4271
4403
|
function resolveDingtalkCredentials(config) {
|
|
4272
|
-
|
|
4404
|
+
const clientId = toTrimmedString(config?.clientId);
|
|
4405
|
+
const clientSecret = toTrimmedString(config?.clientSecret);
|
|
4406
|
+
if (!clientId || !clientSecret) {
|
|
4273
4407
|
return void 0;
|
|
4274
4408
|
}
|
|
4275
4409
|
return {
|
|
4276
|
-
clientId
|
|
4277
|
-
clientSecret
|
|
4410
|
+
clientId,
|
|
4411
|
+
clientSecret
|
|
4278
4412
|
};
|
|
4279
4413
|
}
|
|
4280
4414
|
function createDingtalkClient(opts) {
|
|
@@ -5291,7 +5425,20 @@ async function finalizeInboundMediaFile(options) {
|
|
|
5291
5425
|
await fsPromises.mkdir(datedDir, { recursive: true });
|
|
5292
5426
|
await fsPromises.rename(current, target);
|
|
5293
5427
|
return target;
|
|
5294
|
-
} catch {
|
|
5428
|
+
} catch (error) {
|
|
5429
|
+
const code = error?.code ?? "";
|
|
5430
|
+
if (code === "EXDEV") {
|
|
5431
|
+
try {
|
|
5432
|
+
await fsPromises.copyFile(current, target);
|
|
5433
|
+
try {
|
|
5434
|
+
await fsPromises.unlink(current);
|
|
5435
|
+
} catch {
|
|
5436
|
+
}
|
|
5437
|
+
return target;
|
|
5438
|
+
} catch {
|
|
5439
|
+
return current;
|
|
5440
|
+
}
|
|
5441
|
+
}
|
|
5295
5442
|
return current;
|
|
5296
5443
|
}
|
|
5297
5444
|
}
|
|
@@ -6370,7 +6517,7 @@ function isCommandLike(value) {
|
|
|
6370
6517
|
}
|
|
6371
6518
|
return typeof value.command === "function" && typeof value.description === "function" && typeof value.action === "function";
|
|
6372
6519
|
}
|
|
6373
|
-
function
|
|
6520
|
+
function toTrimmedString2(value) {
|
|
6374
6521
|
if (typeof value !== "string") {
|
|
6375
6522
|
return void 0;
|
|
6376
6523
|
}
|
|
@@ -6403,7 +6550,7 @@ function getPreferredAccountConfig(channelCfg) {
|
|
|
6403
6550
|
if (!isRecord(accounts)) {
|
|
6404
6551
|
return void 0;
|
|
6405
6552
|
}
|
|
6406
|
-
const defaultAccountId =
|
|
6553
|
+
const defaultAccountId = toTrimmedString2(channelCfg.defaultAccount);
|
|
6407
6554
|
if (defaultAccountId) {
|
|
6408
6555
|
const preferred = accounts[defaultAccountId];
|
|
6409
6556
|
if (isRecord(preferred)) {
|
|
@@ -6540,12 +6687,12 @@ async function configureDingtalk(prompter, cfg) {
|
|
|
6540
6687
|
const existing = getChannelConfig(cfg, "dingtalk");
|
|
6541
6688
|
const clientId = await prompter.askText({
|
|
6542
6689
|
label: "DingTalk clientId\uFF08AppKey\uFF09",
|
|
6543
|
-
defaultValue:
|
|
6690
|
+
defaultValue: toTrimmedString2(existing.clientId),
|
|
6544
6691
|
required: true
|
|
6545
6692
|
});
|
|
6546
6693
|
const clientSecret = await prompter.askSecret({
|
|
6547
6694
|
label: "DingTalk clientSecret\uFF08AppSecret\uFF09",
|
|
6548
|
-
existingValue:
|
|
6695
|
+
existingValue: toTrimmedString2(existing.clientSecret),
|
|
6549
6696
|
required: true
|
|
6550
6697
|
});
|
|
6551
6698
|
const enableAICard = await prompter.askConfirm(
|
|
@@ -6564,12 +6711,12 @@ async function configureFeishu(prompter, cfg) {
|
|
|
6564
6711
|
const existing = getChannelConfig(cfg, "feishu-china");
|
|
6565
6712
|
const appId = await prompter.askText({
|
|
6566
6713
|
label: "Feishu appId",
|
|
6567
|
-
defaultValue:
|
|
6714
|
+
defaultValue: toTrimmedString2(existing.appId),
|
|
6568
6715
|
required: true
|
|
6569
6716
|
});
|
|
6570
6717
|
const appSecret = await prompter.askSecret({
|
|
6571
6718
|
label: "Feishu appSecret",
|
|
6572
|
-
existingValue:
|
|
6719
|
+
existingValue: toTrimmedString2(existing.appSecret),
|
|
6573
6720
|
required: true
|
|
6574
6721
|
});
|
|
6575
6722
|
const sendMarkdownAsCard = await prompter.askConfirm(
|
|
@@ -6588,17 +6735,17 @@ async function configureWecom(prompter, cfg) {
|
|
|
6588
6735
|
const existing = getChannelConfig(cfg, "wecom");
|
|
6589
6736
|
const webhookPath = await prompter.askText({
|
|
6590
6737
|
label: "Webhook \u8DEF\u5F84\uFF08\u9700\u4E0E\u4F01\u4E1A\u5FAE\u4FE1\u540E\u53F0\u914D\u7F6E\u4E00\u81F4\uFF0C\u9ED8\u8BA4 /wecom\uFF09",
|
|
6591
|
-
defaultValue:
|
|
6738
|
+
defaultValue: toTrimmedString2(existing.webhookPath) ?? "/wecom",
|
|
6592
6739
|
required: true
|
|
6593
6740
|
});
|
|
6594
6741
|
const token = await prompter.askSecret({
|
|
6595
6742
|
label: "WeCom token",
|
|
6596
|
-
existingValue:
|
|
6743
|
+
existingValue: toTrimmedString2(existing.token),
|
|
6597
6744
|
required: true
|
|
6598
6745
|
});
|
|
6599
6746
|
const encodingAESKey = await prompter.askSecret({
|
|
6600
6747
|
label: "WeCom encodingAESKey",
|
|
6601
|
-
existingValue:
|
|
6748
|
+
existingValue: toTrimmedString2(existing.encodingAESKey),
|
|
6602
6749
|
required: true
|
|
6603
6750
|
});
|
|
6604
6751
|
return mergeChannelConfig(cfg, "wecom", {
|
|
@@ -6614,17 +6761,17 @@ async function configureWecomApp(prompter, cfg) {
|
|
|
6614
6761
|
const existingAsr = isRecord(existing.asr) ? existing.asr : {};
|
|
6615
6762
|
const webhookPath = await prompter.askText({
|
|
6616
6763
|
label: "Webhook \u8DEF\u5F84\uFF08\u9700\u4E0E\u4F01\u4E1A\u5FAE\u4FE1\u540E\u53F0\u914D\u7F6E\u4E00\u81F4\uFF0C\u9ED8\u8BA4 /wecom-app\uFF09",
|
|
6617
|
-
defaultValue:
|
|
6764
|
+
defaultValue: toTrimmedString2(existing.webhookPath) ?? "/wecom-app",
|
|
6618
6765
|
required: true
|
|
6619
6766
|
});
|
|
6620
6767
|
const token = await prompter.askSecret({
|
|
6621
6768
|
label: "WeCom App token",
|
|
6622
|
-
existingValue:
|
|
6769
|
+
existingValue: toTrimmedString2(existing.token),
|
|
6623
6770
|
required: true
|
|
6624
6771
|
});
|
|
6625
6772
|
const encodingAESKey = await prompter.askSecret({
|
|
6626
6773
|
label: "WeCom App encodingAESKey",
|
|
6627
|
-
existingValue:
|
|
6774
|
+
existingValue: toTrimmedString2(existing.encodingAESKey),
|
|
6628
6775
|
required: true
|
|
6629
6776
|
});
|
|
6630
6777
|
const patch = {
|
|
@@ -6634,12 +6781,12 @@ async function configureWecomApp(prompter, cfg) {
|
|
|
6634
6781
|
};
|
|
6635
6782
|
const corpId = await prompter.askText({
|
|
6636
6783
|
label: "corpId",
|
|
6637
|
-
defaultValue:
|
|
6784
|
+
defaultValue: toTrimmedString2(existing.corpId),
|
|
6638
6785
|
required: true
|
|
6639
6786
|
});
|
|
6640
6787
|
const corpSecret = await prompter.askSecret({
|
|
6641
6788
|
label: "corpSecret",
|
|
6642
|
-
existingValue:
|
|
6789
|
+
existingValue: toTrimmedString2(existing.corpSecret),
|
|
6643
6790
|
required: true
|
|
6644
6791
|
});
|
|
6645
6792
|
const agentId = await prompter.askNumber({
|
|
@@ -6667,17 +6814,17 @@ async function configureWecomApp(prompter, cfg) {
|
|
|
6667
6814
|
);
|
|
6668
6815
|
asr.appId = await prompter.askText({
|
|
6669
6816
|
label: "ASR appId\uFF08\u817E\u8BAF\u4E91\uFF09",
|
|
6670
|
-
defaultValue:
|
|
6817
|
+
defaultValue: toTrimmedString2(existingAsr.appId),
|
|
6671
6818
|
required: true
|
|
6672
6819
|
});
|
|
6673
6820
|
asr.secretId = await prompter.askSecret({
|
|
6674
6821
|
label: "ASR secretId\uFF08\u817E\u8BAF\u4E91\uFF09",
|
|
6675
|
-
existingValue:
|
|
6822
|
+
existingValue: toTrimmedString2(existingAsr.secretId),
|
|
6676
6823
|
required: true
|
|
6677
6824
|
});
|
|
6678
6825
|
asr.secretKey = await prompter.askSecret({
|
|
6679
6826
|
label: "ASR secretKey\uFF08\u817E\u8BAF\u4E91\uFF09",
|
|
6680
|
-
existingValue:
|
|
6827
|
+
existingValue: toTrimmedString2(existingAsr.secretKey),
|
|
6681
6828
|
required: true
|
|
6682
6829
|
});
|
|
6683
6830
|
}
|
|
@@ -6691,12 +6838,12 @@ async function configureQQBot(prompter, cfg) {
|
|
|
6691
6838
|
const existingAsr = isRecord(existing.asr) ? existing.asr : {};
|
|
6692
6839
|
const appId = await prompter.askText({
|
|
6693
6840
|
label: "QQBot appId",
|
|
6694
|
-
defaultValue:
|
|
6841
|
+
defaultValue: toTrimmedString2(existing.appId),
|
|
6695
6842
|
required: true
|
|
6696
6843
|
});
|
|
6697
6844
|
const clientSecret = await prompter.askSecret({
|
|
6698
6845
|
label: "QQBot clientSecret",
|
|
6699
|
-
existingValue:
|
|
6846
|
+
existingValue: toTrimmedString2(existing.clientSecret),
|
|
6700
6847
|
required: true
|
|
6701
6848
|
});
|
|
6702
6849
|
const asrEnabled = await prompter.askConfirm(
|
|
@@ -6710,17 +6857,17 @@ async function configureQQBot(prompter, cfg) {
|
|
|
6710
6857
|
Ve("ASR \u5F00\u901A\u65B9\u5F0F\u8BE6\u60C5\u8BF7\u67E5\u770B\u914D\u7F6E\u6587\u6863\u3002", "\u63D0\u793A");
|
|
6711
6858
|
asr.appId = await prompter.askText({
|
|
6712
6859
|
label: "ASR appId\uFF08\u817E\u8BAF\u4E91\uFF09",
|
|
6713
|
-
defaultValue:
|
|
6860
|
+
defaultValue: toTrimmedString2(existingAsr.appId),
|
|
6714
6861
|
required: true
|
|
6715
6862
|
});
|
|
6716
6863
|
asr.secretId = await prompter.askSecret({
|
|
6717
6864
|
label: "ASR secretId\uFF08\u817E\u8BAF\u4E91\uFF09",
|
|
6718
|
-
existingValue:
|
|
6865
|
+
existingValue: toTrimmedString2(existingAsr.secretId),
|
|
6719
6866
|
required: true
|
|
6720
6867
|
});
|
|
6721
6868
|
asr.secretKey = await prompter.askSecret({
|
|
6722
6869
|
label: "ASR secretKey\uFF08\u817E\u8BAF\u4E91\uFF09",
|
|
6723
|
-
existingValue:
|
|
6870
|
+
existingValue: toTrimmedString2(existingAsr.secretKey),
|
|
6724
6871
|
required: true
|
|
6725
6872
|
});
|
|
6726
6873
|
}
|
|
@@ -7628,11 +7775,14 @@ var dingtalkOutbound = {
|
|
|
7628
7775
|
* 发送文本消息
|
|
7629
7776
|
*/
|
|
7630
7777
|
sendText: async (params) => {
|
|
7631
|
-
const { cfg, to, text } = params;
|
|
7632
|
-
|
|
7633
|
-
if (!dingtalkCfg) {
|
|
7778
|
+
const { cfg, to, text, accountId } = params;
|
|
7779
|
+
if (!cfg.channels?.dingtalk) {
|
|
7634
7780
|
throw new Error("DingTalk channel not configured");
|
|
7635
7781
|
}
|
|
7782
|
+
const dingtalkCfg = mergeDingtalkAccountConfig(
|
|
7783
|
+
cfg,
|
|
7784
|
+
resolveDingtalkAccountId(cfg, accountId)
|
|
7785
|
+
);
|
|
7636
7786
|
const { targetId, chatType } = parseTarget(to);
|
|
7637
7787
|
const result = await sendMessageDingtalk({
|
|
7638
7788
|
cfg: dingtalkCfg,
|
|
@@ -7651,11 +7801,14 @@ var dingtalkOutbound = {
|
|
|
7651
7801
|
* 发送媒体消息(含回退逻辑)
|
|
7652
7802
|
*/
|
|
7653
7803
|
sendMedia: async (params) => {
|
|
7654
|
-
const { cfg, to, text, mediaUrl } = params;
|
|
7655
|
-
|
|
7656
|
-
if (!dingtalkCfg) {
|
|
7804
|
+
const { cfg, to, text, mediaUrl, accountId } = params;
|
|
7805
|
+
if (!cfg.channels?.dingtalk) {
|
|
7657
7806
|
throw new Error("DingTalk channel not configured");
|
|
7658
7807
|
}
|
|
7808
|
+
const dingtalkCfg = mergeDingtalkAccountConfig(
|
|
7809
|
+
cfg,
|
|
7810
|
+
resolveDingtalkAccountId(cfg, accountId)
|
|
7811
|
+
);
|
|
7659
7812
|
const { targetId, chatType } = parseTarget(to);
|
|
7660
7813
|
if (text?.trim()) {
|
|
7661
7814
|
await sendMessageDingtalk({
|
|
@@ -8418,6 +8571,7 @@ async function handleDingtalkMessage(params) {
|
|
|
8418
8571
|
const {
|
|
8419
8572
|
cfg,
|
|
8420
8573
|
raw,
|
|
8574
|
+
accountId = DEFAULT_ACCOUNT_ID,
|
|
8421
8575
|
enableAICard = false
|
|
8422
8576
|
} = params;
|
|
8423
8577
|
const logger = createLogger("dingtalk", {
|
|
@@ -8435,8 +8589,9 @@ async function handleDingtalkMessage(params) {
|
|
|
8435
8589
|
mentionedBot: ctx.mentionedBot
|
|
8436
8590
|
})}`
|
|
8437
8591
|
);
|
|
8438
|
-
const
|
|
8439
|
-
const
|
|
8592
|
+
const pluginCfg = cfg ?? {};
|
|
8593
|
+
const hasChannelConfig = Boolean(pluginCfg.channels?.dingtalk);
|
|
8594
|
+
const channelCfg = hasChannelConfig ? mergeDingtalkAccountConfig(pluginCfg, accountId) : void 0;
|
|
8440
8595
|
const inboundMediaDir = resolveInboundMediaDir(channelCfg);
|
|
8441
8596
|
const inboundMediaKeepDays = resolveInboundMediaKeepDays(channelCfg);
|
|
8442
8597
|
const inboundMediaTempDir = resolveInboundMediaTempDir();
|
|
@@ -8516,6 +8671,7 @@ async function handleDingtalkMessage(params) {
|
|
|
8516
8671
|
const route = resolveAgentRoute({
|
|
8517
8672
|
cfg,
|
|
8518
8673
|
channel: "dingtalk",
|
|
8674
|
+
accountId,
|
|
8519
8675
|
peer: {
|
|
8520
8676
|
kind: isGroup ? "group" : "dm",
|
|
8521
8677
|
id: isGroup ? ctx.conversationId : ctx.senderId
|
|
@@ -8607,7 +8763,11 @@ async function handleDingtalkMessage(params) {
|
|
|
8607
8763
|
downloadedRichTextImages = [];
|
|
8608
8764
|
}
|
|
8609
8765
|
}
|
|
8610
|
-
const inboundCtx = buildInboundContext(
|
|
8766
|
+
const inboundCtx = buildInboundContext(
|
|
8767
|
+
ctx,
|
|
8768
|
+
route?.sessionKey,
|
|
8769
|
+
route?.accountId ?? accountId
|
|
8770
|
+
);
|
|
8611
8771
|
if (audioRecognition) {
|
|
8612
8772
|
inboundCtx.Transcript = audioRecognition;
|
|
8613
8773
|
}
|
|
@@ -8979,15 +9139,15 @@ function trimMessageDedupeCache(now) {
|
|
|
8979
9139
|
processedMessages.delete(oldest);
|
|
8980
9140
|
}
|
|
8981
9141
|
}
|
|
8982
|
-
function isDuplicateMessage(
|
|
8983
|
-
const previous = processedMessages.get(
|
|
9142
|
+
function isDuplicateMessage(messageKey, now) {
|
|
9143
|
+
const previous = processedMessages.get(messageKey);
|
|
8984
9144
|
if (typeof previous === "number" && now - previous < MESSAGE_DEDUP_TTL_MS) {
|
|
8985
9145
|
return true;
|
|
8986
9146
|
}
|
|
8987
9147
|
if (typeof previous === "number") {
|
|
8988
|
-
processedMessages.delete(
|
|
9148
|
+
processedMessages.delete(messageKey);
|
|
8989
9149
|
}
|
|
8990
|
-
processedMessages.set(
|
|
9150
|
+
processedMessages.set(messageKey, now);
|
|
8991
9151
|
trimMessageDedupeCache(now);
|
|
8992
9152
|
return false;
|
|
8993
9153
|
}
|
|
@@ -9035,6 +9195,7 @@ function processDingtalkInbound(params) {
|
|
|
9035
9195
|
onParseError
|
|
9036
9196
|
} = params;
|
|
9037
9197
|
const streamMessageId = payload?.headers?.messageId;
|
|
9198
|
+
const dedupeKey = streamMessageId ? `${accountId}:${streamMessageId}` : void 0;
|
|
9038
9199
|
if (streamMessageId) {
|
|
9039
9200
|
try {
|
|
9040
9201
|
client.socketCallBackResponse(streamMessageId, { success: true });
|
|
@@ -9043,7 +9204,7 @@ function processDingtalkInbound(params) {
|
|
|
9043
9204
|
logger.error(`failed to ACK message ${streamMessageId}: ${String(err)}`);
|
|
9044
9205
|
}
|
|
9045
9206
|
}
|
|
9046
|
-
if (
|
|
9207
|
+
if (dedupeKey && isDuplicateMessage(dedupeKey, Date.now())) {
|
|
9047
9208
|
onDedupeHit?.(streamMessageId);
|
|
9048
9209
|
logger.debug(`duplicate message ignored: ${streamMessageId}`);
|
|
9049
9210
|
return;
|
|
@@ -9073,7 +9234,7 @@ function processDingtalkInbound(params) {
|
|
|
9073
9234
|
}
|
|
9074
9235
|
}
|
|
9075
9236
|
function registerDingtalkBotHandler(params) {
|
|
9076
|
-
const dingtalkCfg = params.config
|
|
9237
|
+
const dingtalkCfg = params.config ? mergeDingtalkAccountConfig(params.config, params.accountId) : void 0;
|
|
9077
9238
|
params.client.registerCallbackListener(TOPIC_ROBOT, (payload) => {
|
|
9078
9239
|
processDingtalkInbound({
|
|
9079
9240
|
payload,
|
|
@@ -9114,8 +9275,19 @@ var DISCONNECT_GRACE_MS = 15e3;
|
|
|
9114
9275
|
var RECONNECT_BASE_DELAY_MS = 1e3;
|
|
9115
9276
|
var RECONNECT_MAX_DELAY_MS = 6e4;
|
|
9116
9277
|
var RECONNECT_JITTER_RATIO = 0.2;
|
|
9117
|
-
var
|
|
9118
|
-
|
|
9278
|
+
var activeConnections = /* @__PURE__ */ new Map();
|
|
9279
|
+
function getOrCreateConnection(accountId) {
|
|
9280
|
+
let conn = activeConnections.get(accountId);
|
|
9281
|
+
if (!conn) {
|
|
9282
|
+
conn = {
|
|
9283
|
+
client: null,
|
|
9284
|
+
promise: null,
|
|
9285
|
+
stop: null
|
|
9286
|
+
};
|
|
9287
|
+
activeConnections.set(accountId, conn);
|
|
9288
|
+
}
|
|
9289
|
+
return conn;
|
|
9290
|
+
}
|
|
9119
9291
|
function toRecord(value) {
|
|
9120
9292
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
9121
9293
|
return {};
|
|
@@ -9350,7 +9522,7 @@ async function runGatewaySession(params) {
|
|
|
9350
9522
|
}
|
|
9351
9523
|
}
|
|
9352
9524
|
async function runGatewayLoop(params) {
|
|
9353
|
-
const { config, dingtalkCfg, accountId, logger, signal } = params;
|
|
9525
|
+
const { config, dingtalkCfg, accountId, logger, signal, conn, setStatus } = params;
|
|
9354
9526
|
const metrics = createGatewayMetrics();
|
|
9355
9527
|
const stateRef = { state: "idle" };
|
|
9356
9528
|
let reconnectAttempt = 0;
|
|
@@ -9367,6 +9539,7 @@ async function runGatewayLoop(params) {
|
|
|
9367
9539
|
logger.error(`[gateway] fatal client init error: ${String(err)}`);
|
|
9368
9540
|
throw err;
|
|
9369
9541
|
}
|
|
9542
|
+
conn.client = client;
|
|
9370
9543
|
try {
|
|
9371
9544
|
sessionResult = await runGatewaySession({
|
|
9372
9545
|
client,
|
|
@@ -9382,6 +9555,9 @@ async function runGatewayLoop(params) {
|
|
|
9382
9555
|
sessionResult = { kind: "reconnect", reason: "session_error" };
|
|
9383
9556
|
} finally {
|
|
9384
9557
|
safeDisconnect(client, logger);
|
|
9558
|
+
if (conn.client === client) {
|
|
9559
|
+
conn.client = null;
|
|
9560
|
+
}
|
|
9385
9561
|
}
|
|
9386
9562
|
if (sessionResult.kind === "stopped" || signal.aborted) {
|
|
9387
9563
|
break;
|
|
@@ -9400,33 +9576,46 @@ async function runGatewayLoop(params) {
|
|
|
9400
9576
|
logger.warn(
|
|
9401
9577
|
`[gateway] reconnect scheduled in ${delayMs}ms (attempt=${reconnectAttempt}, reason=${sessionResult.reason})`
|
|
9402
9578
|
);
|
|
9579
|
+
setStatus?.({
|
|
9580
|
+
accountId,
|
|
9581
|
+
state: "reconnecting",
|
|
9582
|
+
reconnectAttempt,
|
|
9583
|
+
reconnectReason: sessionResult.reason,
|
|
9584
|
+
lastReconnectAt: metrics.lastReconnectAt
|
|
9585
|
+
});
|
|
9403
9586
|
const keepRunning = await sleepWithAbort(delayMs, signal);
|
|
9404
9587
|
if (!keepRunning) {
|
|
9405
9588
|
break;
|
|
9406
9589
|
}
|
|
9407
9590
|
}
|
|
9408
9591
|
transitionState({ ref: stateRef, next: "stopped", logger, reason: "abort/stop" });
|
|
9592
|
+
setStatus?.({
|
|
9593
|
+
accountId,
|
|
9594
|
+
state: "stopped",
|
|
9595
|
+
reconnectCountTotal: metrics.reconnectCountTotal,
|
|
9596
|
+
ackFailCount: metrics.ackFailCount,
|
|
9597
|
+
dedupeHitCount: metrics.dedupeHitCount,
|
|
9598
|
+
parseErrorCount: metrics.parseErrorCount
|
|
9599
|
+
});
|
|
9409
9600
|
logger.info(
|
|
9410
9601
|
`[gateway] stopped; reconnects=${metrics.reconnectCountTotal} ackFail=${metrics.ackFailCount} dedupeHit=${metrics.dedupeHitCount} parseErr=${metrics.parseErrorCount}`
|
|
9411
9602
|
);
|
|
9412
9603
|
}
|
|
9413
9604
|
async function monitorDingtalkProvider(opts = {}) {
|
|
9414
|
-
const { config, runtime: runtime2, abortSignal, accountId =
|
|
9605
|
+
const { config, runtime: runtime2, abortSignal, accountId = DEFAULT_ACCOUNT_ID, setStatus } = opts;
|
|
9415
9606
|
const logger = createLogger2("dingtalk", {
|
|
9416
9607
|
log: runtime2?.log,
|
|
9417
9608
|
error: runtime2?.error
|
|
9418
9609
|
});
|
|
9419
|
-
|
|
9420
|
-
|
|
9421
|
-
throw new Error(`DingTalk already running for account ${currentAccountId}`);
|
|
9422
|
-
}
|
|
9610
|
+
const conn = getOrCreateConnection(accountId);
|
|
9611
|
+
if (conn.promise) {
|
|
9423
9612
|
logger.debug(`existing gateway for account ${accountId} is active, reusing promise`);
|
|
9424
|
-
return
|
|
9613
|
+
return conn.promise;
|
|
9425
9614
|
}
|
|
9426
|
-
|
|
9427
|
-
|
|
9428
|
-
throw new Error("DingTalk configuration not found");
|
|
9615
|
+
if (!config?.channels?.dingtalk) {
|
|
9616
|
+
throw new Error(`DingTalk configuration not found for account ${accountId}`);
|
|
9429
9617
|
}
|
|
9618
|
+
const dingtalkCfg = mergeDingtalkAccountConfig(config, accountId);
|
|
9430
9619
|
await ensureGatewayHttpEnabled({ dingtalkCfg, logger });
|
|
9431
9620
|
const stopController = new AbortController();
|
|
9432
9621
|
const stopSignal = stopController.signal;
|
|
@@ -9438,23 +9627,43 @@ async function monitorDingtalkProvider(opts = {}) {
|
|
|
9438
9627
|
} else {
|
|
9439
9628
|
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
9440
9629
|
}
|
|
9441
|
-
|
|
9630
|
+
conn.stop = () => {
|
|
9631
|
+
logger.info("stop requested, stopping Stream gateway");
|
|
9632
|
+
stopController.abort();
|
|
9633
|
+
};
|
|
9442
9634
|
const runPromise = runGatewayLoop({
|
|
9443
9635
|
config,
|
|
9444
9636
|
dingtalkCfg,
|
|
9445
9637
|
accountId,
|
|
9446
9638
|
logger,
|
|
9447
|
-
signal: stopSignal
|
|
9639
|
+
signal: stopSignal,
|
|
9640
|
+
conn,
|
|
9641
|
+
setStatus
|
|
9448
9642
|
}).finally(() => {
|
|
9449
9643
|
abortSignal?.removeEventListener("abort", onAbort);
|
|
9450
|
-
if (
|
|
9451
|
-
|
|
9452
|
-
|
|
9644
|
+
if (conn.promise === runPromise) {
|
|
9645
|
+
conn.client = null;
|
|
9646
|
+
conn.stop = null;
|
|
9647
|
+
conn.promise = null;
|
|
9648
|
+
activeConnections.delete(accountId);
|
|
9453
9649
|
}
|
|
9454
9650
|
});
|
|
9455
|
-
|
|
9651
|
+
conn.promise = runPromise;
|
|
9456
9652
|
return runPromise;
|
|
9457
9653
|
}
|
|
9654
|
+
function stopDingtalkMonitorForAccount(accountId = DEFAULT_ACCOUNT_ID) {
|
|
9655
|
+
const conn = activeConnections.get(accountId);
|
|
9656
|
+
if (!conn) return;
|
|
9657
|
+
if (conn.stop) {
|
|
9658
|
+
conn.stop();
|
|
9659
|
+
return;
|
|
9660
|
+
}
|
|
9661
|
+
if (conn.client) {
|
|
9662
|
+
const logger = createLogger2("dingtalk");
|
|
9663
|
+
safeDisconnect(conn.client, logger);
|
|
9664
|
+
}
|
|
9665
|
+
activeConnections.delete(accountId);
|
|
9666
|
+
}
|
|
9458
9667
|
|
|
9459
9668
|
// src/onboarding.ts
|
|
9460
9669
|
function setDingtalkDmPolicy(cfg, dmPolicy2) {
|
|
@@ -9560,13 +9769,19 @@ var dingtalkOnboardingAdapter = {
|
|
|
9560
9769
|
* 获取渠道配置状态
|
|
9561
9770
|
*/
|
|
9562
9771
|
getStatus: async (params) => {
|
|
9563
|
-
const
|
|
9564
|
-
const
|
|
9772
|
+
const accountIds = listDingtalkAccountIds(params.cfg);
|
|
9773
|
+
const configuredAccountId = accountIds.find(
|
|
9774
|
+
(accountId) => isConfigured(mergeDingtalkAccountConfig(params.cfg, accountId))
|
|
9775
|
+
);
|
|
9776
|
+
const configured = Boolean(configuredAccountId);
|
|
9777
|
+
const defaultAccountId = resolveDefaultDingtalkAccountId(params.cfg);
|
|
9565
9778
|
const statusLines = [];
|
|
9566
9779
|
if (!configured) {
|
|
9567
9780
|
statusLines.push("\u9489\u9489: \u9700\u8981\u914D\u7F6E\u5E94\u7528\u51ED\u8BC1");
|
|
9568
9781
|
} else {
|
|
9569
|
-
statusLines.push(
|
|
9782
|
+
statusLines.push(
|
|
9783
|
+
configuredAccountId && configuredAccountId !== DEFAULT_ACCOUNT_ID ? `\u9489\u9489: \u5DF2\u914D\u7F6E (${configuredAccountId})` : `\u9489\u9489: \u5DF2\u914D\u7F6E${defaultAccountId !== DEFAULT_ACCOUNT_ID ? ` (default=${defaultAccountId})` : ""}`
|
|
9784
|
+
);
|
|
9570
9785
|
}
|
|
9571
9786
|
return {
|
|
9572
9787
|
channel: "dingtalk",
|
|
@@ -9580,7 +9795,8 @@ var dingtalkOnboardingAdapter = {
|
|
|
9580
9795
|
* 交互式配置向导
|
|
9581
9796
|
*/
|
|
9582
9797
|
configure: async (params) => {
|
|
9583
|
-
const
|
|
9798
|
+
const defaultAccountId = resolveDefaultDingtalkAccountId(params.cfg);
|
|
9799
|
+
const dingtalkCfg = mergeDingtalkAccountConfig(params.cfg, defaultAccountId);
|
|
9584
9800
|
const resolved = resolveDingtalkCredentials(dingtalkCfg);
|
|
9585
9801
|
const hasConfigCreds = Boolean(
|
|
9586
9802
|
dingtalkCfg?.clientId?.trim() && dingtalkCfg?.clientSecret?.trim()
|
|
@@ -9710,7 +9926,6 @@ var dingtalkOnboardingAdapter = {
|
|
|
9710
9926
|
};
|
|
9711
9927
|
|
|
9712
9928
|
// src/channel.ts
|
|
9713
|
-
var DEFAULT_ACCOUNT_ID = "default";
|
|
9714
9929
|
var meta = {
|
|
9715
9930
|
id: "dingtalk",
|
|
9716
9931
|
label: "DingTalk",
|
|
@@ -9722,19 +9937,36 @@ var meta = {
|
|
|
9722
9937
|
order: 71
|
|
9723
9938
|
};
|
|
9724
9939
|
function resolveDingtalkAccount(params) {
|
|
9725
|
-
const { cfg
|
|
9726
|
-
const
|
|
9727
|
-
const
|
|
9728
|
-
const
|
|
9729
|
-
const
|
|
9940
|
+
const { cfg } = params;
|
|
9941
|
+
const accountId = resolveDingtalkAccountId(cfg, params.accountId);
|
|
9942
|
+
const merged = mergeDingtalkAccountConfig(cfg, accountId);
|
|
9943
|
+
const baseEnabled = cfg.channels?.dingtalk?.enabled !== false;
|
|
9944
|
+
const enabled = baseEnabled && merged.enabled !== false;
|
|
9945
|
+
const credentials = resolveDingtalkCredentials(merged);
|
|
9730
9946
|
const configured = Boolean(credentials);
|
|
9731
9947
|
return {
|
|
9732
9948
|
accountId,
|
|
9733
|
-
enabled
|
|
9949
|
+
enabled,
|
|
9734
9950
|
configured,
|
|
9735
9951
|
clientId: credentials?.clientId
|
|
9736
9952
|
};
|
|
9737
9953
|
}
|
|
9954
|
+
function canStoreDefaultAccountInAccounts(cfg) {
|
|
9955
|
+
return Boolean(cfg.channels?.dingtalk?.accounts?.[DEFAULT_ACCOUNT_ID]);
|
|
9956
|
+
}
|
|
9957
|
+
function resolveRuntimeCandidate(params) {
|
|
9958
|
+
const runtimeRecord = params.runtime && typeof params.runtime === "object" ? params.runtime : void 0;
|
|
9959
|
+
const runtimeChannel = runtimeRecord?.channel && typeof runtimeRecord.channel === "object" ? runtimeRecord.channel : void 0;
|
|
9960
|
+
const channelRuntime = params.channelRuntime && typeof params.channelRuntime === "object" ? params.channelRuntime : void 0;
|
|
9961
|
+
const resolvedChannel = channelRuntime ?? (runtimeChannel?.routing || runtimeChannel?.reply || runtimeChannel?.session || runtimeChannel?.text ? runtimeChannel : void 0);
|
|
9962
|
+
if (!resolvedChannel) {
|
|
9963
|
+
return runtimeRecord;
|
|
9964
|
+
}
|
|
9965
|
+
return {
|
|
9966
|
+
...runtimeRecord ?? {},
|
|
9967
|
+
channel: resolvedChannel
|
|
9968
|
+
};
|
|
9969
|
+
}
|
|
9738
9970
|
var dingtalkPlugin = {
|
|
9739
9971
|
id: "dingtalk",
|
|
9740
9972
|
/**
|
|
@@ -9768,8 +10000,11 @@ var dingtalkPlugin = {
|
|
|
9768
10000
|
additionalProperties: false,
|
|
9769
10001
|
properties: {
|
|
9770
10002
|
enabled: { type: "boolean" },
|
|
10003
|
+
name: { type: "string" },
|
|
10004
|
+
defaultAccount: { type: "string" },
|
|
9771
10005
|
clientId: { type: "string" },
|
|
9772
10006
|
clientSecret: { type: "string" },
|
|
10007
|
+
connectionMode: { type: "string", enum: ["stream", "webhook"] },
|
|
9773
10008
|
dmPolicy: { type: "string", enum: ["open", "pairing", "allowlist"] },
|
|
9774
10009
|
groupPolicy: { type: "string", enum: ["open", "allowlist", "disabled"] },
|
|
9775
10010
|
requireMention: { type: "boolean" },
|
|
@@ -9789,6 +10024,40 @@ var dingtalkPlugin = {
|
|
|
9789
10024
|
dir: { type: "string" },
|
|
9790
10025
|
keepDays: { type: "number", minimum: 0 }
|
|
9791
10026
|
}
|
|
10027
|
+
},
|
|
10028
|
+
accounts: {
|
|
10029
|
+
type: "object",
|
|
10030
|
+
additionalProperties: {
|
|
10031
|
+
type: "object",
|
|
10032
|
+
additionalProperties: false,
|
|
10033
|
+
properties: {
|
|
10034
|
+
name: { type: "string" },
|
|
10035
|
+
enabled: { type: "boolean" },
|
|
10036
|
+
clientId: { type: "string" },
|
|
10037
|
+
clientSecret: { type: "string" },
|
|
10038
|
+
connectionMode: { type: "string", enum: ["stream", "webhook"] },
|
|
10039
|
+
dmPolicy: { type: "string", enum: ["open", "pairing", "allowlist"] },
|
|
10040
|
+
groupPolicy: { type: "string", enum: ["open", "allowlist", "disabled"] },
|
|
10041
|
+
requireMention: { type: "boolean" },
|
|
10042
|
+
allowFrom: { type: "array", items: { type: "string" } },
|
|
10043
|
+
groupAllowFrom: { type: "array", items: { type: "string" } },
|
|
10044
|
+
historyLimit: { type: "integer", minimum: 0 },
|
|
10045
|
+
textChunkLimit: { type: "integer", minimum: 1 },
|
|
10046
|
+
longTaskNoticeDelayMs: { type: "integer", minimum: 0 },
|
|
10047
|
+
enableAICard: { type: "boolean" },
|
|
10048
|
+
gatewayToken: { type: "string" },
|
|
10049
|
+
gatewayPassword: { type: "string" },
|
|
10050
|
+
maxFileSizeMB: { type: "number", minimum: 0 },
|
|
10051
|
+
inboundMedia: {
|
|
10052
|
+
type: "object",
|
|
10053
|
+
additionalProperties: false,
|
|
10054
|
+
properties: {
|
|
10055
|
+
dir: { type: "string" },
|
|
10056
|
+
keepDays: { type: "number", minimum: 0 }
|
|
10057
|
+
}
|
|
10058
|
+
}
|
|
10059
|
+
}
|
|
10060
|
+
}
|
|
9792
10061
|
}
|
|
9793
10062
|
}
|
|
9794
10063
|
}
|
|
@@ -9806,7 +10075,7 @@ var dingtalkPlugin = {
|
|
|
9806
10075
|
* 列出所有账户 ID
|
|
9807
10076
|
* Requirements: 2.1
|
|
9808
10077
|
*/
|
|
9809
|
-
listAccountIds: (
|
|
10078
|
+
listAccountIds: (cfg) => listDingtalkAccountIds(cfg),
|
|
9810
10079
|
/**
|
|
9811
10080
|
* 解析账户配置
|
|
9812
10081
|
* Requirements: 2.2
|
|
@@ -9815,19 +10084,38 @@ var dingtalkPlugin = {
|
|
|
9815
10084
|
/**
|
|
9816
10085
|
* 获取默认账户 ID
|
|
9817
10086
|
*/
|
|
9818
|
-
defaultAccountId: () =>
|
|
10087
|
+
defaultAccountId: (cfg) => resolveDefaultDingtalkAccountId(cfg),
|
|
9819
10088
|
/**
|
|
9820
10089
|
* 设置账户启用状态
|
|
9821
10090
|
*/
|
|
9822
10091
|
setAccountEnabled: (params) => {
|
|
9823
|
-
const
|
|
10092
|
+
const accountId = resolveDingtalkAccountId(params.cfg, params.accountId);
|
|
10093
|
+
const seededCfg = moveDingtalkSingleAccountConfigToDefaultAccount(params.cfg);
|
|
10094
|
+
const existing = seededCfg.channels?.dingtalk ?? {};
|
|
10095
|
+
if (accountId === DEFAULT_ACCOUNT_ID && !canStoreDefaultAccountInAccounts(seededCfg)) {
|
|
10096
|
+
return {
|
|
10097
|
+
...seededCfg,
|
|
10098
|
+
channels: {
|
|
10099
|
+
...seededCfg.channels,
|
|
10100
|
+
dingtalk: {
|
|
10101
|
+
...existing,
|
|
10102
|
+
enabled: params.enabled
|
|
10103
|
+
}
|
|
10104
|
+
}
|
|
10105
|
+
};
|
|
10106
|
+
}
|
|
10107
|
+
const accounts = existing.accounts ?? {};
|
|
10108
|
+
const account = accounts[accountId] ?? {};
|
|
9824
10109
|
return {
|
|
9825
|
-
...
|
|
10110
|
+
...seededCfg,
|
|
9826
10111
|
channels: {
|
|
9827
|
-
...
|
|
10112
|
+
...seededCfg.channels,
|
|
9828
10113
|
dingtalk: {
|
|
9829
|
-
...
|
|
9830
|
-
|
|
10114
|
+
...existing,
|
|
10115
|
+
accounts: {
|
|
10116
|
+
...accounts,
|
|
10117
|
+
[accountId]: { ...account, enabled: params.enabled }
|
|
10118
|
+
}
|
|
9831
10119
|
}
|
|
9832
10120
|
}
|
|
9833
10121
|
};
|
|
@@ -9836,21 +10124,68 @@ var dingtalkPlugin = {
|
|
|
9836
10124
|
* 删除账户配置
|
|
9837
10125
|
*/
|
|
9838
10126
|
deleteAccount: (params) => {
|
|
9839
|
-
const
|
|
9840
|
-
const
|
|
9841
|
-
|
|
9842
|
-
if (
|
|
9843
|
-
|
|
9844
|
-
|
|
9845
|
-
|
|
10127
|
+
const accountId = resolveDingtalkAccountId(params.cfg, params.accountId);
|
|
10128
|
+
const seededCfg = moveDingtalkSingleAccountConfigToDefaultAccount(params.cfg);
|
|
10129
|
+
const existing = seededCfg.channels?.dingtalk;
|
|
10130
|
+
if (!existing) return seededCfg;
|
|
10131
|
+
const accounts = existing.accounts ?? {};
|
|
10132
|
+
if (!accounts[accountId]) {
|
|
10133
|
+
if (accountId === DEFAULT_ACCOUNT_ID && Object.keys(accounts).length === 0 && !canStoreDefaultAccountInAccounts(seededCfg)) {
|
|
10134
|
+
const next = { ...seededCfg };
|
|
10135
|
+
const nextChannels = { ...seededCfg.channels };
|
|
10136
|
+
delete nextChannels.dingtalk;
|
|
10137
|
+
if (Object.keys(nextChannels).length > 0) {
|
|
10138
|
+
next.channels = nextChannels;
|
|
10139
|
+
} else {
|
|
10140
|
+
delete next.channels;
|
|
10141
|
+
}
|
|
10142
|
+
return next;
|
|
10143
|
+
}
|
|
10144
|
+
return seededCfg;
|
|
10145
|
+
}
|
|
10146
|
+
const { [accountId]: _removed, ...remainingAccounts } = accounts;
|
|
10147
|
+
const remainingIds = Object.keys(remainingAccounts).sort((a, b) => a.localeCompare(b));
|
|
10148
|
+
const preferred = existing.defaultAccount?.trim();
|
|
10149
|
+
let nextDefaultAccount = preferred;
|
|
10150
|
+
if (preferred && !remainingAccounts[preferred]) {
|
|
10151
|
+
nextDefaultAccount = remainingIds.includes(DEFAULT_ACCOUNT_ID) ? DEFAULT_ACCOUNT_ID : remainingIds[0] ?? "";
|
|
10152
|
+
}
|
|
10153
|
+
const nextChannel = {
|
|
10154
|
+
...existing,
|
|
10155
|
+
accounts: remainingIds.length > 0 ? remainingAccounts : void 0,
|
|
10156
|
+
defaultAccount: nextDefaultAccount || void 0
|
|
10157
|
+
};
|
|
10158
|
+
const hasNonTrivialRootConfig = Object.entries(nextChannel).some(
|
|
10159
|
+
([key, value]) => key !== "enabled" && key !== "accounts" && key !== "defaultAccount" && value !== void 0
|
|
10160
|
+
);
|
|
10161
|
+
if (remainingIds.length === 0 && !hasNonTrivialRootConfig) {
|
|
10162
|
+
const next = { ...seededCfg };
|
|
10163
|
+
const nextChannels = { ...seededCfg.channels };
|
|
10164
|
+
delete nextChannels.dingtalk;
|
|
10165
|
+
if (Object.keys(nextChannels).length > 0) {
|
|
10166
|
+
next.channels = nextChannels;
|
|
10167
|
+
} else {
|
|
10168
|
+
delete next.channels;
|
|
10169
|
+
}
|
|
10170
|
+
return next;
|
|
9846
10171
|
}
|
|
9847
|
-
return
|
|
10172
|
+
return {
|
|
10173
|
+
...seededCfg,
|
|
10174
|
+
channels: {
|
|
10175
|
+
...seededCfg.channels,
|
|
10176
|
+
dingtalk: nextChannel
|
|
10177
|
+
}
|
|
10178
|
+
};
|
|
9848
10179
|
},
|
|
9849
10180
|
/**
|
|
9850
10181
|
* 检查账户是否已配置
|
|
9851
10182
|
* Requirements: 2.3
|
|
9852
10183
|
*/
|
|
9853
|
-
isConfigured: (_account, cfg) =>
|
|
10184
|
+
isConfigured: (_account, cfg, accountId) => {
|
|
10185
|
+
const id = accountId ?? _account.accountId;
|
|
10186
|
+
const merged = mergeDingtalkAccountConfig(cfg, id);
|
|
10187
|
+
return Boolean(merged.clientId && merged.clientSecret);
|
|
10188
|
+
},
|
|
9854
10189
|
/**
|
|
9855
10190
|
* 描述账户信息
|
|
9856
10191
|
*/
|
|
@@ -9862,7 +10197,11 @@ var dingtalkPlugin = {
|
|
|
9862
10197
|
/**
|
|
9863
10198
|
* 解析白名单
|
|
9864
10199
|
*/
|
|
9865
|
-
resolveAllowFrom: (params) =>
|
|
10200
|
+
resolveAllowFrom: (params) => {
|
|
10201
|
+
const accountId = resolveDingtalkAccountId(params.cfg, params.accountId);
|
|
10202
|
+
const merged = mergeDingtalkAccountConfig(params.cfg, accountId);
|
|
10203
|
+
return merged.allowFrom ?? [];
|
|
10204
|
+
},
|
|
9866
10205
|
/**
|
|
9867
10206
|
* 格式化白名单条目
|
|
9868
10207
|
*/
|
|
@@ -9885,16 +10224,40 @@ var dingtalkPlugin = {
|
|
|
9885
10224
|
* 设置向导适配器
|
|
9886
10225
|
*/
|
|
9887
10226
|
setup: {
|
|
9888
|
-
resolveAccountId: () =>
|
|
10227
|
+
resolveAccountId: (params) => resolveDingtalkAccountId(params.cfg, params.accountId),
|
|
9889
10228
|
applyAccountConfig: (params) => {
|
|
9890
|
-
const
|
|
10229
|
+
const accountId = resolveDingtalkAccountId(params.cfg, params.accountId);
|
|
10230
|
+
const seededCfg = moveDingtalkSingleAccountConfigToDefaultAccount(params.cfg);
|
|
10231
|
+
const existing = seededCfg.channels?.dingtalk ?? {};
|
|
10232
|
+
if (accountId === DEFAULT_ACCOUNT_ID && !canStoreDefaultAccountInAccounts(seededCfg)) {
|
|
10233
|
+
return {
|
|
10234
|
+
...seededCfg,
|
|
10235
|
+
channels: {
|
|
10236
|
+
...seededCfg.channels,
|
|
10237
|
+
dingtalk: {
|
|
10238
|
+
...existing,
|
|
10239
|
+
...params.config,
|
|
10240
|
+
enabled: true
|
|
10241
|
+
}
|
|
10242
|
+
}
|
|
10243
|
+
};
|
|
10244
|
+
}
|
|
10245
|
+
const accounts = existing.accounts ?? {};
|
|
9891
10246
|
return {
|
|
9892
|
-
...
|
|
10247
|
+
...seededCfg,
|
|
9893
10248
|
channels: {
|
|
9894
|
-
...
|
|
10249
|
+
...seededCfg.channels,
|
|
9895
10250
|
dingtalk: {
|
|
9896
|
-
...
|
|
9897
|
-
enabled: true
|
|
10251
|
+
...existing,
|
|
10252
|
+
enabled: true,
|
|
10253
|
+
accounts: {
|
|
10254
|
+
...accounts,
|
|
10255
|
+
[accountId]: {
|
|
10256
|
+
...accounts[accountId],
|
|
10257
|
+
...params.config,
|
|
10258
|
+
enabled: true
|
|
10259
|
+
}
|
|
10260
|
+
}
|
|
9898
10261
|
}
|
|
9899
10262
|
}
|
|
9900
10263
|
};
|
|
@@ -9921,11 +10284,13 @@ var dingtalkPlugin = {
|
|
|
9921
10284
|
startAccount: async (ctx) => {
|
|
9922
10285
|
ctx.setStatus?.({ accountId: ctx.accountId });
|
|
9923
10286
|
ctx.log?.info(`[dingtalk] starting provider for account ${ctx.accountId}`);
|
|
9924
|
-
|
|
9925
|
-
|
|
9926
|
-
|
|
9927
|
-
|
|
9928
|
-
|
|
10287
|
+
const runtimeCandidate = resolveRuntimeCandidate({
|
|
10288
|
+
runtime: ctx.runtime,
|
|
10289
|
+
channelRuntime: ctx.channelRuntime
|
|
10290
|
+
});
|
|
10291
|
+
const candidate = runtimeCandidate;
|
|
10292
|
+
if (candidate?.channel?.routing?.resolveAgentRoute && candidate.channel?.reply?.dispatchReplyFromConfig) {
|
|
10293
|
+
setDingtalkRuntime(runtimeCandidate);
|
|
9929
10294
|
}
|
|
9930
10295
|
return monitorDingtalkProvider({
|
|
9931
10296
|
config: ctx.cfg,
|
|
@@ -9934,9 +10299,14 @@ var dingtalkPlugin = {
|
|
|
9934
10299
|
error: ctx.log?.error ?? console.error
|
|
9935
10300
|
},
|
|
9936
10301
|
abortSignal: ctx.abortSignal,
|
|
9937
|
-
accountId: ctx.accountId
|
|
10302
|
+
accountId: ctx.accountId,
|
|
10303
|
+
setStatus: ctx.setStatus
|
|
9938
10304
|
});
|
|
9939
|
-
}
|
|
10305
|
+
},
|
|
10306
|
+
stopAccount: async (ctx) => {
|
|
10307
|
+
stopDingtalkMonitorForAccount(ctx.accountId);
|
|
10308
|
+
},
|
|
10309
|
+
getStatus: () => ({ connected: true })
|
|
9940
10310
|
}
|
|
9941
10311
|
};
|
|
9942
10312
|
|
|
@@ -9950,6 +10320,8 @@ var plugin = {
|
|
|
9950
10320
|
additionalProperties: false,
|
|
9951
10321
|
properties: {
|
|
9952
10322
|
enabled: { type: "boolean" },
|
|
10323
|
+
name: { type: "string" },
|
|
10324
|
+
defaultAccount: { type: "string" },
|
|
9953
10325
|
clientId: { type: "string" },
|
|
9954
10326
|
clientSecret: { type: "string" },
|
|
9955
10327
|
dmPolicy: { type: "string", enum: ["open", "pairing", "allowlist"] },
|
|
@@ -9959,7 +10331,54 @@ var plugin = {
|
|
|
9959
10331
|
groupAllowFrom: { type: "array", items: { type: "string" } },
|
|
9960
10332
|
historyLimit: { type: "integer", minimum: 0 },
|
|
9961
10333
|
textChunkLimit: { type: "integer", minimum: 1 },
|
|
9962
|
-
longTaskNoticeDelayMs: { type: "integer", minimum: 0 }
|
|
10334
|
+
longTaskNoticeDelayMs: { type: "integer", minimum: 0 },
|
|
10335
|
+
connectionMode: { type: "string", enum: ["stream", "webhook"] },
|
|
10336
|
+
enableAICard: { type: "boolean" },
|
|
10337
|
+
gatewayToken: { type: "string" },
|
|
10338
|
+
gatewayPassword: { type: "string" },
|
|
10339
|
+
maxFileSizeMB: { type: "number", minimum: 1 },
|
|
10340
|
+
inboundMedia: {
|
|
10341
|
+
type: "object",
|
|
10342
|
+
additionalProperties: false,
|
|
10343
|
+
properties: {
|
|
10344
|
+
dir: { type: "string" },
|
|
10345
|
+
keepDays: { type: "number", minimum: 0 }
|
|
10346
|
+
}
|
|
10347
|
+
},
|
|
10348
|
+
accounts: {
|
|
10349
|
+
type: "object",
|
|
10350
|
+
additionalProperties: {
|
|
10351
|
+
type: "object",
|
|
10352
|
+
additionalProperties: false,
|
|
10353
|
+
properties: {
|
|
10354
|
+
name: { type: "string" },
|
|
10355
|
+
enabled: { type: "boolean" },
|
|
10356
|
+
clientId: { type: "string" },
|
|
10357
|
+
clientSecret: { type: "string" },
|
|
10358
|
+
dmPolicy: { type: "string", enum: ["open", "pairing", "allowlist"] },
|
|
10359
|
+
groupPolicy: { type: "string", enum: ["open", "allowlist", "disabled"] },
|
|
10360
|
+
requireMention: { type: "boolean" },
|
|
10361
|
+
allowFrom: { type: "array", items: { type: "string" } },
|
|
10362
|
+
groupAllowFrom: { type: "array", items: { type: "string" } },
|
|
10363
|
+
historyLimit: { type: "integer", minimum: 0 },
|
|
10364
|
+
textChunkLimit: { type: "integer", minimum: 1 },
|
|
10365
|
+
longTaskNoticeDelayMs: { type: "integer", minimum: 0 },
|
|
10366
|
+
connectionMode: { type: "string", enum: ["stream", "webhook"] },
|
|
10367
|
+
enableAICard: { type: "boolean" },
|
|
10368
|
+
gatewayToken: { type: "string" },
|
|
10369
|
+
gatewayPassword: { type: "string" },
|
|
10370
|
+
maxFileSizeMB: { type: "number", minimum: 1 },
|
|
10371
|
+
inboundMedia: {
|
|
10372
|
+
type: "object",
|
|
10373
|
+
additionalProperties: false,
|
|
10374
|
+
properties: {
|
|
10375
|
+
dir: { type: "string" },
|
|
10376
|
+
keepDays: { type: "number", minimum: 0 }
|
|
10377
|
+
}
|
|
10378
|
+
}
|
|
10379
|
+
}
|
|
10380
|
+
}
|
|
10381
|
+
}
|
|
9963
10382
|
}
|
|
9964
10383
|
},
|
|
9965
10384
|
/**
|