@jeik/dingtalk-connector 0.8.21
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 +684 -0
- package/LICENSE +21 -0
- package/README.en.md +179 -0
- package/README.md +219 -0
- package/bin/dingtalk-connector.js +838 -0
- package/bin/wizard-config.mjs +94 -0
- package/dist/accounts-BAzdqkAV.mjs +268 -0
- package/dist/accounts-BQptOmgB.mjs +2 -0
- package/dist/chunk-upload-BBQgGtcZ.mjs +193 -0
- package/dist/chunk-upload-DaLXXZH3.mjs +2 -0
- package/dist/common-C8pYKU_y.mjs +2 -0
- package/dist/common-Dt9n6fQN.mjs +101 -0
- package/dist/connection-DHHFFNQJ.mjs +423 -0
- package/dist/entry-bundled.d.mts +16 -0
- package/dist/entry-bundled.mjs +31 -0
- package/dist/game-xiyou-CqHt-6Q1.mjs +4271 -0
- package/dist/gateway-methods-C4tcgI7P.mjs +771 -0
- package/dist/gateway-methods-Ci31A3vg.mjs +2 -0
- package/dist/http-client-CpnJHB89.mjs +2 -0
- package/dist/http-client-DFWZgO1n.mjs +33 -0
- package/dist/index.d.mts +193 -0
- package/dist/index.mjs +45 -0
- package/dist/logger-BmJkQkm1.mjs +2 -0
- package/dist/logger-mZ9OSbmD.mjs +58 -0
- package/dist/media-C_SVin7s.mjs +2 -0
- package/dist/media-cz72EVS3.mjs +509 -0
- package/dist/message-handler-DESzFFDc.mjs +1971 -0
- package/dist/messaging-B6l1sRvX.mjs +1044 -0
- package/dist/runtime-DUgpo5zC.mjs +1422 -0
- package/dist/session-DJ4jYqPv.mjs +114 -0
- package/dist/utils-Bjh4r_qS.mjs +4 -0
- package/dist/utils-CIfI_3Jh.mjs +63 -0
- package/dist/utils-legacy-CALCPP1t.mjs +230 -0
- package/dist/utils-legacy-CFYDBM4r.mjs +3 -0
- package/docs/DEAP_AGENT_GUIDE.en.md +115 -0
- package/docs/DEAP_AGENT_GUIDE.md +115 -0
- package/docs/DINGTALK_MANUAL_SETUP.md +50 -0
- package/docs/MULTI_AGENT_SETUP.md +306 -0
- package/docs/RELEASE_NOTES_V0.7.10.md +40 -0
- package/docs/RELEASE_NOTES_V0.7.2.md +143 -0
- package/docs/RELEASE_NOTES_V0.7.3.md +149 -0
- package/docs/RELEASE_NOTES_V0.7.4.md +206 -0
- package/docs/RELEASE_NOTES_V0.7.5.md +267 -0
- package/docs/RELEASE_NOTES_V0.7.6.md +219 -0
- package/docs/RELEASE_NOTES_V0.7.7.md +122 -0
- package/docs/RELEASE_NOTES_V0.7.8.md +101 -0
- package/docs/RELEASE_NOTES_V0.7.9.md +65 -0
- package/docs/RELEASE_NOTES_V0.8.0.md +53 -0
- package/docs/RELEASE_NOTES_V0.8.1.md +47 -0
- package/docs/RELEASE_NOTES_V0.8.10.md +49 -0
- package/docs/RELEASE_NOTES_V0.8.11.md +51 -0
- package/docs/RELEASE_NOTES_V0.8.12.md +63 -0
- package/docs/RELEASE_NOTES_V0.8.13-beta.0.md +69 -0
- package/docs/RELEASE_NOTES_V0.8.13.md +62 -0
- package/docs/RELEASE_NOTES_V0.8.14.md +86 -0
- package/docs/RELEASE_NOTES_V0.8.16.md +40 -0
- package/docs/RELEASE_NOTES_V0.8.17.md +87 -0
- package/docs/RELEASE_NOTES_V0.8.18.md +64 -0
- package/docs/RELEASE_NOTES_V0.8.19.md +62 -0
- package/docs/RELEASE_NOTES_V0.8.2.md +55 -0
- package/docs/RELEASE_NOTES_V0.8.20.md +49 -0
- package/docs/RELEASE_NOTES_V0.8.3.md +63 -0
- package/docs/RELEASE_NOTES_V0.8.4.md +45 -0
- package/docs/RELEASE_NOTES_V0.8.7.md +49 -0
- package/docs/RELEASE_NOTES_V0.8.8.md +63 -0
- package/docs/RELEASE_NOTES_V0.8.9.md +81 -0
- package/docs/RELEASE_NOTES_v0.7.0.md +142 -0
- package/docs/RELEASE_NOTES_v0.7.1.md +74 -0
- package/docs/TROUBLESHOOTING.md +122 -0
- package/index.ts +77 -0
- package/openclaw.plugin.json +551 -0
- package/package.json +147 -0
- package/skills/dingtalk-channel-rules/SKILL.md +91 -0
- package/skills/dingtalk-troubleshoot/SKILL.md +93 -0
- package/skills/dws-cli/SKILL.md +129 -0
- package/skills/dws-cli/references/error-codes.md +95 -0
- package/skills/dws-cli/references/field-rules.md +105 -0
- package/skills/dws-cli/references/global-reference.md +104 -0
- package/skills/dws-cli/references/intent-guide.md +114 -0
- package/skills/dws-cli/references/products/aitable.md +452 -0
- package/skills/dws-cli/references/products/attendance.md +93 -0
- package/skills/dws-cli/references/products/calendar.md +217 -0
- package/skills/dws-cli/references/products/chat.md +292 -0
- package/skills/dws-cli/references/products/contact.md +108 -0
- package/skills/dws-cli/references/products/ding.md +57 -0
- package/skills/dws-cli/references/products/report.md +162 -0
- package/skills/dws-cli/references/products/simple.md +128 -0
- package/skills/dws-cli/references/products/todo.md +138 -0
- package/skills/dws-cli/references/products/workbench.md +39 -0
- package/skills/dws-cli/references/recovery-guide.md +94 -0
- package/src/channel.ts +588 -0
- package/src/config/accounts.ts +242 -0
- package/src/config/schema.ts +180 -0
- package/src/core/connection.ts +741 -0
- package/src/core/message-handler.ts +1788 -0
- package/src/core/provider.ts +111 -0
- package/src/core/state.ts +54 -0
- package/src/device-auth-config.ts +14 -0
- package/src/device-auth.ts +197 -0
- package/src/directory.ts +95 -0
- package/src/docs.ts +293 -0
- package/src/game-xiyou/achievement-engine.ts +252 -0
- package/src/game-xiyou/bounty-system.ts +315 -0
- package/src/game-xiyou/commands.ts +223 -0
- package/src/game-xiyou/drop-engine.ts +241 -0
- package/src/game-xiyou/encounter-system.ts +135 -0
- package/src/game-xiyou/escape-engine.ts +164 -0
- package/src/game-xiyou/exp-calculator.ts +139 -0
- package/src/game-xiyou/index.ts +479 -0
- package/src/game-xiyou/level-system.ts +91 -0
- package/src/game-xiyou/monster-pool.ts +180 -0
- package/src/game-xiyou/pity-counter.ts +114 -0
- package/src/game-xiyou/random-event-engine.ts +648 -0
- package/src/game-xiyou/renderer.ts +679 -0
- package/src/game-xiyou/storage.ts +218 -0
- package/src/game-xiyou/treasure-system.ts +105 -0
- package/src/game-xiyou/types.ts +582 -0
- package/src/game-xiyou/uid-resolver.ts +49 -0
- package/src/gateway-methods.ts +740 -0
- package/src/onboarding.ts +553 -0
- package/src/policy.ts +32 -0
- package/src/probe.ts +210 -0
- package/src/reply-dispatcher.ts +874 -0
- package/src/runtime.ts +32 -0
- package/src/sdk/helpers.ts +322 -0
- package/src/sdk/types.ts +519 -0
- package/src/secret-input.ts +19 -0
- package/src/services/media/audio.ts +54 -0
- package/src/services/media/chunk-upload.ts +296 -0
- package/src/services/media/common.ts +155 -0
- package/src/services/media/file.ts +75 -0
- package/src/services/media/image.ts +81 -0
- package/src/services/media/index.ts +10 -0
- package/src/services/media/video.ts +162 -0
- package/src/services/media.ts +1143 -0
- package/src/services/messaging/card.ts +604 -0
- package/src/services/messaging/index.ts +18 -0
- package/src/services/messaging/mentions.ts +267 -0
- package/src/services/messaging/send.ts +141 -0
- package/src/services/messaging.ts +1191 -0
- package/src/services/reply-markers.ts +55 -0
- package/src/targets.ts +45 -0
- package/src/types/index.ts +59 -0
- package/src/types/pdf-parse.d.ts +3 -0
- package/src/utils/agent.ts +63 -0
- package/src/utils/async.ts +51 -0
- package/src/utils/constants.ts +27 -0
- package/src/utils/http-client.ts +38 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/logger.ts +78 -0
- package/src/utils/session.ts +147 -0
- package/src/utils/token.ts +93 -0
- package/src/utils/utils-legacy.ts +454 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { DEFAULT_ACCOUNT_ID, normalizeAccountId , normalizeResolvedSecretInputString, normalizeSecretInputString } from "../sdk/helpers.ts";
|
|
2
|
+
import type { ClawdbotConfig } from "openclaw/plugin-sdk";
|
|
3
|
+
import type {
|
|
4
|
+
DingtalkConfig,
|
|
5
|
+
DingtalkAccountConfig,
|
|
6
|
+
DingtalkDefaultAccountSelectionSource,
|
|
7
|
+
ResolvedDingtalkAccount,
|
|
8
|
+
} from "../types/index.ts";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* List all configured account IDs from the accounts field.
|
|
12
|
+
*/
|
|
13
|
+
function listConfiguredAccountIds(cfg: ClawdbotConfig): string[] {
|
|
14
|
+
const accounts = (cfg.channels?.["dingtalk-connector"] as DingtalkConfig)?.accounts;
|
|
15
|
+
if (!accounts || typeof accounts !== "object") {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
return Object.keys(accounts).filter(Boolean);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* List all DingTalk account IDs.
|
|
23
|
+
* If no accounts are configured, returns [DEFAULT_ACCOUNT_ID] for backward compatibility.
|
|
24
|
+
*/
|
|
25
|
+
export function listDingtalkAccountIds(cfg: ClawdbotConfig): string[] {
|
|
26
|
+
const ids = listConfiguredAccountIds(cfg);
|
|
27
|
+
if (ids.length === 0) {
|
|
28
|
+
// Backward compatibility: no accounts configured, use default
|
|
29
|
+
return [DEFAULT_ACCOUNT_ID];
|
|
30
|
+
}
|
|
31
|
+
return [...ids].sort((a, b) => a.localeCompare(b));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Resolve the default account selection and its source.
|
|
36
|
+
*/
|
|
37
|
+
export function resolveDefaultDingtalkAccountSelection(cfg: ClawdbotConfig): {
|
|
38
|
+
accountId: string;
|
|
39
|
+
source: DingtalkDefaultAccountSelectionSource;
|
|
40
|
+
} {
|
|
41
|
+
const preferredRaw = (cfg.channels?.["dingtalk-connector"] as DingtalkConfig | undefined)?.defaultAccount?.trim();
|
|
42
|
+
const preferred = preferredRaw ? normalizeAccountId(preferredRaw) : undefined;
|
|
43
|
+
if (preferred) {
|
|
44
|
+
return {
|
|
45
|
+
accountId: preferred,
|
|
46
|
+
source: "explicit-default",
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const ids = listDingtalkAccountIds(cfg);
|
|
50
|
+
if (ids.includes(DEFAULT_ACCOUNT_ID)) {
|
|
51
|
+
return {
|
|
52
|
+
accountId: DEFAULT_ACCOUNT_ID,
|
|
53
|
+
source: "mapped-default",
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
accountId: ids[0] ?? DEFAULT_ACCOUNT_ID,
|
|
58
|
+
source: "fallback",
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Resolve the default account ID.
|
|
64
|
+
*/
|
|
65
|
+
export function resolveDefaultDingtalkAccountId(cfg: ClawdbotConfig): string {
|
|
66
|
+
return resolveDefaultDingtalkAccountSelection(cfg).accountId;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get the raw account-specific config.
|
|
71
|
+
*/
|
|
72
|
+
function resolveAccountConfig(
|
|
73
|
+
cfg: ClawdbotConfig,
|
|
74
|
+
accountId: string,
|
|
75
|
+
): DingtalkAccountConfig | undefined {
|
|
76
|
+
const accounts = (cfg.channels?.["dingtalk-connector"] as DingtalkConfig)?.accounts;
|
|
77
|
+
if (!accounts || typeof accounts !== "object") {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
return accounts[accountId];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Merge top-level config with account-specific config.
|
|
85
|
+
* Account-specific fields override top-level fields.
|
|
86
|
+
*/
|
|
87
|
+
function mergeDingtalkAccountConfig(cfg: ClawdbotConfig, accountId: string): DingtalkConfig {
|
|
88
|
+
const dingtalkCfg = cfg.channels?.["dingtalk-connector"] as DingtalkConfig | undefined;
|
|
89
|
+
|
|
90
|
+
// Extract base config (exclude accounts field to avoid recursion)
|
|
91
|
+
const { accounts: _ignored, defaultAccount: _ignoredDefaultAccount, ...base } = dingtalkCfg ?? {};
|
|
92
|
+
|
|
93
|
+
// Get account-specific overrides
|
|
94
|
+
const account = resolveAccountConfig(cfg, accountId) ?? {};
|
|
95
|
+
|
|
96
|
+
// Merge: account config overrides base config
|
|
97
|
+
return { ...base, ...account } as DingtalkConfig;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Resolve DingTalk credentials from a config.
|
|
102
|
+
*/
|
|
103
|
+
export function resolveDingtalkCredentials(cfg?: DingtalkConfig): {
|
|
104
|
+
clientId: string;
|
|
105
|
+
clientSecret: string;
|
|
106
|
+
} | null;
|
|
107
|
+
export function resolveDingtalkCredentials(
|
|
108
|
+
cfg: DingtalkConfig | undefined,
|
|
109
|
+
options: { allowUnresolvedSecretRef?: boolean },
|
|
110
|
+
): {
|
|
111
|
+
clientId: string;
|
|
112
|
+
clientSecret: string;
|
|
113
|
+
} | null;
|
|
114
|
+
export function resolveDingtalkCredentials(
|
|
115
|
+
cfg?: DingtalkConfig,
|
|
116
|
+
options?: { allowUnresolvedSecretRef?: boolean },
|
|
117
|
+
): {
|
|
118
|
+
clientId: string;
|
|
119
|
+
clientSecret: string;
|
|
120
|
+
} | null {
|
|
121
|
+
const normalizeString = (value: unknown): string | undefined => {
|
|
122
|
+
if (typeof value === "number") {
|
|
123
|
+
return String(value);
|
|
124
|
+
}
|
|
125
|
+
if (typeof value !== "string") {
|
|
126
|
+
return undefined;
|
|
127
|
+
}
|
|
128
|
+
const trimmed = value.trim();
|
|
129
|
+
return trimmed ? trimmed : undefined;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const resolveSecretLike = (value: unknown, path: string): string | undefined => {
|
|
133
|
+
// Missing credential: treat as not configured (no exception).
|
|
134
|
+
// This path is used in non-onboarding contexts (e.g. channel listing/status),
|
|
135
|
+
// so we must not throw when credentials are absent.
|
|
136
|
+
if (value === undefined || value === null) {
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const asString = normalizeString(value);
|
|
141
|
+
if (asString) {
|
|
142
|
+
return asString;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// In relaxed/onboarding paths only: allow direct env SecretRef reads for UX.
|
|
146
|
+
// Default resolution path must preserve unresolved-ref diagnostics/policy semantics.
|
|
147
|
+
if (options?.allowUnresolvedSecretRef && typeof value === "object" && value !== null) {
|
|
148
|
+
const rec = value as Record<string, unknown>;
|
|
149
|
+
const source = normalizeString(rec.source)?.toLowerCase();
|
|
150
|
+
const id = normalizeString(rec.id);
|
|
151
|
+
if (source === "env" && id) {
|
|
152
|
+
const envValue = normalizeString(process.env[id]);
|
|
153
|
+
if (envValue) {
|
|
154
|
+
return envValue;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (options?.allowUnresolvedSecretRef) {
|
|
160
|
+
return normalizeSecretInputString(value);
|
|
161
|
+
}
|
|
162
|
+
return normalizeResolvedSecretInputString({ value, path });
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const clientId = resolveSecretLike(cfg?.clientId, "channels.dingtalk-connector.clientId");
|
|
166
|
+
const clientSecret = resolveSecretLike(cfg?.clientSecret, "channels.dingtalk-connector.clientSecret");
|
|
167
|
+
|
|
168
|
+
if (!clientId || !clientSecret) {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
clientId,
|
|
173
|
+
clientSecret,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Resolve a complete DingTalk account with merged config.
|
|
179
|
+
*/
|
|
180
|
+
export function resolveDingtalkAccount(params: {
|
|
181
|
+
cfg: ClawdbotConfig;
|
|
182
|
+
accountId?: string | null;
|
|
183
|
+
}): ResolvedDingtalkAccount {
|
|
184
|
+
const hasExplicitAccountId =
|
|
185
|
+
typeof params.accountId === "string" && params.accountId.trim() !== "";
|
|
186
|
+
const defaultSelection = hasExplicitAccountId
|
|
187
|
+
? null
|
|
188
|
+
: resolveDefaultDingtalkAccountSelection(params.cfg);
|
|
189
|
+
const accountId = hasExplicitAccountId
|
|
190
|
+
? normalizeAccountId(params.accountId ?? "")
|
|
191
|
+
: (defaultSelection?.accountId ?? DEFAULT_ACCOUNT_ID);
|
|
192
|
+
const selectionSource = hasExplicitAccountId
|
|
193
|
+
? "explicit"
|
|
194
|
+
: (defaultSelection?.source ?? "fallback");
|
|
195
|
+
const dingtalkCfg = params.cfg.channels?.["dingtalk-connector"] as DingtalkConfig | undefined;
|
|
196
|
+
|
|
197
|
+
// Base enabled state (top-level)
|
|
198
|
+
const baseEnabled = dingtalkCfg?.enabled !== false;
|
|
199
|
+
|
|
200
|
+
// Merge configs
|
|
201
|
+
const merged = mergeDingtalkAccountConfig(params.cfg, accountId);
|
|
202
|
+
|
|
203
|
+
// Account-level enabled state
|
|
204
|
+
const accountEnabled = merged.enabled !== false;
|
|
205
|
+
const enabled = baseEnabled && accountEnabled;
|
|
206
|
+
|
|
207
|
+
// Resolve credentials from merged config
|
|
208
|
+
const creds = resolveDingtalkCredentials(merged);
|
|
209
|
+
const accountName = (merged as DingtalkAccountConfig).name;
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
accountId,
|
|
213
|
+
selectionSource,
|
|
214
|
+
enabled,
|
|
215
|
+
configured: Boolean(creds),
|
|
216
|
+
name: typeof accountName === "string" ? accountName.trim() || undefined : undefined,
|
|
217
|
+
clientId: creds?.clientId,
|
|
218
|
+
clientSecret: creds?.clientSecret,
|
|
219
|
+
config: merged,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* List all enabled and configured accounts.
|
|
225
|
+
* Deduplicates by clientId to avoid creating multiple connections with the same credentials.
|
|
226
|
+
*/
|
|
227
|
+
export function listEnabledDingtalkAccounts(cfg: ClawdbotConfig): ResolvedDingtalkAccount[] {
|
|
228
|
+
const accounts = listDingtalkAccountIds(cfg)
|
|
229
|
+
.map((accountId) => resolveDingtalkAccount({ cfg, accountId }))
|
|
230
|
+
.filter((account) => account.enabled && account.configured);
|
|
231
|
+
|
|
232
|
+
// Deduplicate by clientId to avoid multiple connections with same credentials
|
|
233
|
+
const seen = new Set<string>();
|
|
234
|
+
return accounts.filter((account) => {
|
|
235
|
+
if (!account.clientId) return true;
|
|
236
|
+
if (seen.has(account.clientId)) {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
seen.add(account.clientId);
|
|
240
|
+
return true;
|
|
241
|
+
});
|
|
242
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { normalizeAccountId } from "../sdk/helpers.ts";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export { z };
|
|
4
|
+
import { buildSecretInputSchema, hasConfiguredSecretInput } from "../secret-input.ts";
|
|
5
|
+
|
|
6
|
+
const DmPolicySchema = z.enum(["open", "pairing", "allowlist"]);
|
|
7
|
+
const GroupPolicySchema = z.enum(["open", "allowlist", "disabled"]);
|
|
8
|
+
|
|
9
|
+
const ToolPolicySchema = z
|
|
10
|
+
.object({
|
|
11
|
+
allow: z.array(z.string()).optional(),
|
|
12
|
+
deny: z.array(z.string()).optional(),
|
|
13
|
+
})
|
|
14
|
+
.strict()
|
|
15
|
+
.optional();
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Group session scope for routing DingTalk group messages.
|
|
19
|
+
* - "group" (default): one session per group chat
|
|
20
|
+
* - "group_sender": one session per (group + sender)
|
|
21
|
+
*/
|
|
22
|
+
const GroupSessionScopeSchema = z
|
|
23
|
+
.enum(["group", "group_sender"])
|
|
24
|
+
.optional();
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Group reply mode for DingTalk group messages.
|
|
28
|
+
* - "aicard" (default): use AI Card with streaming support
|
|
29
|
+
* - "text": use plain text reply (supports @bot mentions, no AI Card)
|
|
30
|
+
* - "markdown": use markdown reply (supports @bot mentions, no AI Card)
|
|
31
|
+
*
|
|
32
|
+
* When set to "text" or "markdown", group messages will be sent as
|
|
33
|
+
* plain text/markdown instead of AI Card. This enables bots to @mention
|
|
34
|
+
* each other in multi-Agent group scenarios.
|
|
35
|
+
*
|
|
36
|
+
* ⚠️ Warning: enabling text/markdown mode disables AI Card in group chats.
|
|
37
|
+
*/
|
|
38
|
+
const GroupReplyModeSchema = z
|
|
39
|
+
.enum(["aicard", "text", "markdown"])
|
|
40
|
+
.optional();
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Dingtalk tools configuration.
|
|
44
|
+
* Controls which tool categories are enabled.
|
|
45
|
+
*/
|
|
46
|
+
const DingtalkToolsConfigSchema = z
|
|
47
|
+
.object({
|
|
48
|
+
docs: z.boolean().optional(), // Document operations (default: true)
|
|
49
|
+
media: z.boolean().optional(), // Media upload operations (default: true)
|
|
50
|
+
})
|
|
51
|
+
.strict()
|
|
52
|
+
.optional();
|
|
53
|
+
|
|
54
|
+
export const DingtalkGroupSchema = z
|
|
55
|
+
.object({
|
|
56
|
+
requireMention: z.boolean().optional(),
|
|
57
|
+
tools: ToolPolicySchema,
|
|
58
|
+
enabled: z.boolean().optional(),
|
|
59
|
+
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
60
|
+
systemPrompt: z.string().optional(),
|
|
61
|
+
groupSessionScope: GroupSessionScopeSchema,
|
|
62
|
+
})
|
|
63
|
+
.strict();
|
|
64
|
+
|
|
65
|
+
const DingtalkSharedConfigShape = {
|
|
66
|
+
dmPolicy: DmPolicySchema.optional(),
|
|
67
|
+
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
68
|
+
groupPolicy: GroupPolicySchema.optional(),
|
|
69
|
+
groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
70
|
+
requireMention: z.boolean().optional(),
|
|
71
|
+
groups: z.record(z.string(), DingtalkGroupSchema.optional()).optional(),
|
|
72
|
+
historyLimit: z.number().int().min(0).optional(),
|
|
73
|
+
textChunkLimit: z.number().int().positive().optional(),
|
|
74
|
+
mediaMaxMb: z.number().positive().optional(),
|
|
75
|
+
tools: DingtalkToolsConfigSchema,
|
|
76
|
+
typingIndicator: z.boolean().optional(),
|
|
77
|
+
resolveSenderNames: z.boolean().optional(),
|
|
78
|
+
separateSessionByConversation: z.boolean().optional(),
|
|
79
|
+
sharedMemoryAcrossConversations: z.boolean().optional(),
|
|
80
|
+
groupSessionScope: GroupSessionScopeSchema,
|
|
81
|
+
asyncMode: z.boolean().optional(),
|
|
82
|
+
ackText: z.string().optional(),
|
|
83
|
+
endpoint: z.string().optional(), // DWClient gateway endpoint
|
|
84
|
+
debug: z.boolean().optional(), // DWClient debug mode
|
|
85
|
+
enableMediaUpload: z.boolean().optional(),
|
|
86
|
+
systemPrompt: z.string().optional(),
|
|
87
|
+
groupReplyMode: GroupReplyModeSchema,
|
|
88
|
+
/** AI Card 模板 ID,不填则使用官方默认模板 */
|
|
89
|
+
cardTemplateId: z.string().optional(),
|
|
90
|
+
/** AI Card 最终内容变量名,对应卡片模板中的变量字段,不填默认 msgContent */
|
|
91
|
+
cardContentVar: z.string().optional().default("msgContent"),
|
|
92
|
+
/** AI Card 中间过程变量名,不填默认 cardContentVar 同值 */
|
|
93
|
+
cardProcessVar: z.string().optional(),
|
|
94
|
+
/** AI Card 工具输出变量名,不填则不写入工具输出 */
|
|
95
|
+
cardToolVar: z.string().optional(),
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Per-account configuration.
|
|
100
|
+
* All fields are optional - missing fields inherit from top-level config.
|
|
101
|
+
*/
|
|
102
|
+
export const DingtalkAccountConfigSchema = z
|
|
103
|
+
.object({
|
|
104
|
+
enabled: z.boolean().optional(),
|
|
105
|
+
name: z.string().optional(), // Display name for this account
|
|
106
|
+
clientId: z.union([z.string(), z.number()]).optional(),
|
|
107
|
+
clientSecret: buildSecretInputSchema().optional(),
|
|
108
|
+
/**
|
|
109
|
+
* Encrypted DingTalk identity of this bot, used by other agents to @-mention
|
|
110
|
+
* this bot in group messages. Fill from log line `[BotIdentity] chatbotUserId=...`
|
|
111
|
+
* after the bot has received at least one group/DM message.
|
|
112
|
+
*/
|
|
113
|
+
chatbotUserId: z.string().optional(),
|
|
114
|
+
chatbotCorpId: z.string().optional(),
|
|
115
|
+
...DingtalkSharedConfigShape,
|
|
116
|
+
})
|
|
117
|
+
.strict();
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Base schema (ZodObject) without superRefine, used for JSON Schema generation (Web UI).
|
|
121
|
+
* superRefine turns the schema into ZodEffects which is not compatible with buildChannelConfigSchema.
|
|
122
|
+
*/
|
|
123
|
+
export const DingtalkConfigBaseSchema = z
|
|
124
|
+
.object({
|
|
125
|
+
enabled: z.boolean().optional(),
|
|
126
|
+
defaultAccount: z.string().optional(),
|
|
127
|
+
// Top-level credentials (backward compatible for single-account mode)
|
|
128
|
+
clientId: z.union([z.string(), z.number()]).optional(),
|
|
129
|
+
clientSecret: buildSecretInputSchema().optional(),
|
|
130
|
+
...DingtalkSharedConfigShape,
|
|
131
|
+
dmPolicy: DmPolicySchema.optional().default("open"),
|
|
132
|
+
groupPolicy: GroupPolicySchema.optional().default("open"),
|
|
133
|
+
requireMention: z.boolean().optional().default(true),
|
|
134
|
+
separateSessionByConversation: z.boolean().optional().default(true),
|
|
135
|
+
sharedMemoryAcrossConversations: z.boolean().optional().default(false),
|
|
136
|
+
groupSessionScope: GroupSessionScopeSchema.optional().default("group"),
|
|
137
|
+
// Multi-account configuration
|
|
138
|
+
accounts: z.record(z.string(), DingtalkAccountConfigSchema.optional()).optional(),
|
|
139
|
+
})
|
|
140
|
+
.strict();
|
|
141
|
+
|
|
142
|
+
export const DingtalkConfigSchema = DingtalkConfigBaseSchema.superRefine((value, ctx) => {
|
|
143
|
+
const defaultAccount = value.defaultAccount?.trim();
|
|
144
|
+
if (defaultAccount && value.accounts && Object.keys(value.accounts).length > 0) {
|
|
145
|
+
const normalizedDefaultAccount = normalizeAccountId(defaultAccount);
|
|
146
|
+
if (!Object.prototype.hasOwnProperty.call(value.accounts, normalizedDefaultAccount)) {
|
|
147
|
+
ctx.addIssue({
|
|
148
|
+
code: z.ZodIssueCode.custom,
|
|
149
|
+
path: ["defaultAccount"],
|
|
150
|
+
message: `channels.dingtalk-connector.defaultAccount="${defaultAccount}" does not match a configured account key`,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Validate dmPolicy and allowFrom consistency
|
|
156
|
+
if (value.dmPolicy === "allowlist") {
|
|
157
|
+
const allowFrom = value.allowFrom ?? [];
|
|
158
|
+
if (allowFrom.length === 0) {
|
|
159
|
+
ctx.addIssue({
|
|
160
|
+
code: z.ZodIssueCode.custom,
|
|
161
|
+
path: ["allowFrom"],
|
|
162
|
+
message:
|
|
163
|
+
'channels.dingtalk-connector.dmPolicy="allowlist" requires channels.dingtalk-connector.allowFrom to contain at least one entry',
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Validate groupPolicy and groupAllowFrom consistency
|
|
169
|
+
if (value.groupPolicy === "allowlist") {
|
|
170
|
+
const groupAllowFrom = value.groupAllowFrom ?? [];
|
|
171
|
+
if (groupAllowFrom.length === 0) {
|
|
172
|
+
ctx.addIssue({
|
|
173
|
+
code: z.ZodIssueCode.custom,
|
|
174
|
+
path: ["groupAllowFrom"],
|
|
175
|
+
message:
|
|
176
|
+
'channels.dingtalk-connector.groupPolicy="allowlist" requires channels.dingtalk-connector.groupAllowFrom to contain at least one entry',
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
});
|