@largezhou/ddingtalk 1.4.2 → 1.5.0-beta.1

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/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
2
- import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
2
+ import { emptyPluginConfigSchema } from "./src/compat.js";
3
3
  import { dingtalkPlugin } from "./src/channel.js";
4
4
  import { setDingTalkRuntime } from "./src/runtime.js";
5
5
  import { PLUGIN_ID } from "./src/constants.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@largezhou/ddingtalk",
3
- "version": "1.4.2",
3
+ "version": "1.5.0-beta.1",
4
4
  "description": "OpenClaw DingTalk (钉钉) channel plugin",
5
5
  "main": "index.ts",
6
6
  "type": "module",
package/src/accounts.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  import {
2
- DEFAULT_ACCOUNT_ID,
3
- normalizeAccountId,
4
2
  type OpenClawConfig,
5
3
  } from "openclaw/plugin-sdk";
4
+ import {
5
+ DEFAULT_ACCOUNT_ID,
6
+ normalizeAccountId,
7
+ } from "./compat.js";
6
8
  import type {
7
9
  DingTalkConfig,
8
10
  DingTalkAccountConfig,
package/src/channel.ts CHANGED
@@ -1,3 +1,9 @@
1
+ import {
2
+ type ChannelPlugin,
3
+ type ChannelStatusIssue,
4
+ type ChannelAccountSnapshot,
5
+ type OpenClawConfig,
6
+ } from "openclaw/plugin-sdk";
1
7
  import {
2
8
  buildChannelConfigSchema,
3
9
  DEFAULT_ACCOUNT_ID,
@@ -8,11 +14,7 @@ import {
8
14
  loadWebMedia,
9
15
  missingTargetError,
10
16
  normalizeAccountId,
11
- type ChannelPlugin,
12
- type ChannelStatusIssue,
13
- type ChannelAccountSnapshot,
14
- type OpenClawConfig,
15
- } from "openclaw/plugin-sdk";
17
+ } from "./compat.js";
16
18
  import path from "path";
17
19
  import { getDingTalkRuntime } from "./runtime.js";
18
20
  import {
package/src/compat.ts ADDED
@@ -0,0 +1,258 @@
1
+ /**
2
+ * 兼容层:处理不同 OpenClaw 版本的 API 差异
3
+ *
4
+ * 策略:自定义精确的类型接口契约,运行时一次性解析,导出强类型符号。
5
+ * 调用方享受完整的 TypeScript 静态检查,不会出现 any 类型污染。
6
+ *
7
+ * 版本差异说明:
8
+ * - v2026.2.x ~ v2026.3.11:所有符号从 "openclaw/plugin-sdk" 主入口导出
9
+ * - v2026.3.22+:运行时符号分散到子模块路径,类型仍从主入口导出
10
+ */
11
+
12
+ import { createRequire } from "node:module";
13
+ import type { ZodTypeAny } from "zod";
14
+
15
+ // ============================================================================
16
+ // 类型契约:精确定义我们需要的每个符号的类型签名
17
+ // 这些类型基于 openclaw 源码中的 .d.ts 文件提取,保证与真实接口一致
18
+ // ============================================================================
19
+
20
+ /** @see openclaw/dist/plugin-sdk/channels/plugins/types.plugin.d.ts */
21
+ export type ChannelConfigUiHint = {
22
+ label?: string;
23
+ help?: string;
24
+ tags?: string[];
25
+ advanced?: boolean;
26
+ sensitive?: boolean;
27
+ placeholder?: string;
28
+ itemTemplate?: unknown;
29
+ };
30
+
31
+ export type ChannelConfigSchema = {
32
+ schema: Record<string, unknown>;
33
+ uiHints?: Record<string, ChannelConfigUiHint>;
34
+ };
35
+
36
+ /** OpenClaw 配置的最小类型约束(避免直接依赖 OpenClawConfig 的子路径) */
37
+ type CfgLike = { channels?: Record<string, unknown>;[key: string]: unknown };
38
+
39
+ // 签名类型定义
40
+ type BuildChannelConfigSchemaFn = (schema: ZodTypeAny) => ChannelConfigSchema;
41
+ type NormalizeAccountIdFn = (value: string | undefined | null) => string;
42
+ type SetAccountEnabledFn = (params: {
43
+ cfg: CfgLike; sectionKey: string; accountId: string; enabled: boolean; allowTopLevel?: boolean;
44
+ }) => CfgLike;
45
+ type DeleteAccountFn = (params: {
46
+ cfg: CfgLike; sectionKey: string; accountId: string; clearBaseFields?: string[];
47
+ }) => CfgLike;
48
+ type ApplyAccountNameFn = (params: {
49
+ cfg: CfgLike; channelKey: string; accountId: string; name?: string; alwaysUseAccounts?: boolean;
50
+ }) => CfgLike;
51
+ type FormatPairingApproveHintFn = (channelId: string) => string;
52
+
53
+ /** @see openclaw/dist/plugin-sdk/web/media.d.ts */
54
+ export type WebMediaResult = {
55
+ buffer: Buffer;
56
+ contentType?: string;
57
+ kind: string;
58
+ fileName?: string;
59
+ };
60
+ type LoadWebMediaFn = (
61
+ mediaUrl: string,
62
+ maxBytesOrOptions?: number | Record<string, unknown>,
63
+ options?: Record<string, unknown>,
64
+ ) => Promise<WebMediaResult>;
65
+
66
+ type MissingTargetErrorFn = (provider: string, hint?: string) => Error;
67
+
68
+ /** @see openclaw/dist/plugin-sdk/channels/session.d.ts */
69
+ type RecordInboundSessionFn = (params: {
70
+ storePath: string;
71
+ sessionKey: string;
72
+ ctx: Record<string, unknown>;
73
+ groupResolution?: unknown;
74
+ createIfMissing?: boolean;
75
+ updateLastRoute?: {
76
+ sessionKey: string;
77
+ channel: string;
78
+ to: string;
79
+ accountId?: string;
80
+ threadId?: string | number;
81
+ };
82
+ onRecordError: (err: unknown) => void;
83
+ }) => Promise<void>;
84
+
85
+ /** @see openclaw/dist/plugin-sdk/plugin-sdk/onboarding.d.ts */
86
+ type PromptAccountIdFn = (params: {
87
+ cfg: CfgLike;
88
+ prompter: unknown;
89
+ label: string;
90
+ currentId?: string;
91
+ listAccountIds: (cfg: CfgLike) => string[];
92
+ defaultAccountId: string;
93
+ }) => Promise<string>;
94
+
95
+ type EmptyPluginConfigSchemaFn = () => unknown;
96
+
97
+ // ============================================================================
98
+ // 运行时解析引擎
99
+ // ============================================================================
100
+
101
+ const require = createRequire(import.meta.url);
102
+
103
+ /**
104
+ * 从多个模块路径中尝试加载指定符号。
105
+ * 模块路径按优先级排列,先新版子路径,后旧版主入口。
106
+ */
107
+ function resolve<T>(symbolName: string, modulePaths: string[], fallback: T): T {
108
+ for (const modulePath of modulePaths) {
109
+ try {
110
+ const mod = require(modulePath);
111
+ if (symbolName in mod && typeof mod[symbolName] !== "undefined") {
112
+ return mod[symbolName] as T;
113
+ }
114
+ } catch {
115
+ // 路径不存在,继续
116
+ }
117
+ }
118
+ return fallback;
119
+ }
120
+
121
+ // ============================================================================
122
+ // 一次性解析所有符号(模块加载时执行,后续调用零开销)
123
+ // ============================================================================
124
+
125
+ // --- buildChannelConfigSchema ---
126
+ export const buildChannelConfigSchema: BuildChannelConfigSchemaFn = resolve(
127
+ "buildChannelConfigSchema",
128
+ [
129
+ "openclaw/plugin-sdk/channel-config-schema",
130
+ "openclaw/plugin-sdk/channel-plugin-common",
131
+ "openclaw/plugin-sdk/core",
132
+ "openclaw/plugin-sdk",
133
+ ],
134
+ ((_schema: ZodTypeAny): ChannelConfigSchema => ({
135
+ schema: { type: "object", additionalProperties: true },
136
+ })),
137
+ );
138
+
139
+ // --- DEFAULT_ACCOUNT_ID ---
140
+ export const DEFAULT_ACCOUNT_ID: string = resolve(
141
+ "DEFAULT_ACCOUNT_ID",
142
+ [
143
+ "openclaw/plugin-sdk/channel-plugin-common",
144
+ "openclaw/plugin-sdk/core",
145
+ "openclaw/plugin-sdk",
146
+ ],
147
+ "default",
148
+ );
149
+
150
+ // --- normalizeAccountId ---
151
+ export const normalizeAccountId: NormalizeAccountIdFn = resolve(
152
+ "normalizeAccountId",
153
+ [
154
+ "openclaw/plugin-sdk/channel-plugin-common",
155
+ "openclaw/plugin-sdk/core",
156
+ "openclaw/plugin-sdk",
157
+ ],
158
+ ((value?: string | null) => value?.trim() || "default"),
159
+ );
160
+
161
+ // --- setAccountEnabledInConfigSection ---
162
+ export const setAccountEnabledInConfigSection: SetAccountEnabledFn = resolve(
163
+ "setAccountEnabledInConfigSection",
164
+ [
165
+ "openclaw/plugin-sdk/channel-plugin-common",
166
+ "openclaw/plugin-sdk/core",
167
+ "openclaw/plugin-sdk",
168
+ ],
169
+ ((params) => params.cfg),
170
+ );
171
+
172
+ // --- deleteAccountFromConfigSection ---
173
+ export const deleteAccountFromConfigSection: DeleteAccountFn = resolve(
174
+ "deleteAccountFromConfigSection",
175
+ [
176
+ "openclaw/plugin-sdk/channel-plugin-common",
177
+ "openclaw/plugin-sdk/core",
178
+ "openclaw/plugin-sdk",
179
+ ],
180
+ ((params) => params.cfg),
181
+ );
182
+
183
+ // --- applyAccountNameToChannelSection ---
184
+ export const applyAccountNameToChannelSection: ApplyAccountNameFn = resolve(
185
+ "applyAccountNameToChannelSection",
186
+ [
187
+ "openclaw/plugin-sdk/channel-plugin-common",
188
+ "openclaw/plugin-sdk/core",
189
+ "openclaw/plugin-sdk",
190
+ ],
191
+ ((params) => params.cfg),
192
+ );
193
+
194
+ // --- formatPairingApproveHint ---
195
+ export const formatPairingApproveHint: FormatPairingApproveHintFn = resolve(
196
+ "formatPairingApproveHint",
197
+ [
198
+ "openclaw/plugin-sdk/channel-plugin-common",
199
+ "openclaw/plugin-sdk",
200
+ ],
201
+ ((channelId: string) => `Approve pairing for ${channelId}`),
202
+ );
203
+
204
+ // --- loadWebMedia ---
205
+ export const loadWebMedia: LoadWebMediaFn = resolve(
206
+ "loadWebMedia",
207
+ [
208
+ "openclaw/plugin-sdk/web-media",
209
+ "openclaw/plugin-sdk/msteams",
210
+ "openclaw/plugin-sdk",
211
+ ],
212
+ (async (_url: string) => {
213
+ throw new Error("loadWebMedia is not available in this OpenClaw version");
214
+ }),
215
+ );
216
+
217
+ // --- missingTargetError ---
218
+ export const missingTargetError: MissingTargetErrorFn = resolve(
219
+ "missingTargetError",
220
+ [
221
+ "openclaw/plugin-sdk/channel-feedback",
222
+ "openclaw/plugin-sdk/googlechat",
223
+ "openclaw/plugin-sdk",
224
+ ],
225
+ ((provider: string, hint?: string) =>
226
+ new Error(`Missing target for ${provider}${hint ? `. Expected: ${hint}` : ""}`)),
227
+ );
228
+
229
+ // --- recordInboundSession ---
230
+ export const recordInboundSession: RecordInboundSessionFn = resolve(
231
+ "recordInboundSession",
232
+ [
233
+ "openclaw/plugin-sdk/conversation-runtime",
234
+ "openclaw/plugin-sdk",
235
+ ],
236
+ (async () => { /* no-op */ }),
237
+ );
238
+
239
+ // --- promptAccountId ---
240
+ export const promptAccountId: PromptAccountIdFn = resolve(
241
+ "promptAccountId",
242
+ [
243
+ "openclaw/plugin-sdk/matrix",
244
+ "openclaw/plugin-sdk",
245
+ ],
246
+ (async (params) => params.currentId || params.defaultAccountId || "default"),
247
+ );
248
+
249
+ // --- emptyPluginConfigSchema ---
250
+ export const emptyPluginConfigSchema: EmptyPluginConfigSchemaFn = resolve(
251
+ "emptyPluginConfigSchema",
252
+ [
253
+ "openclaw/plugin-sdk",
254
+ "openclaw/plugin-sdk/core",
255
+ "openclaw/plugin-sdk/channel-plugin-common",
256
+ ],
257
+ (() => ({ schema: {} })),
258
+ );
package/src/monitor.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { DWClient, TOPIC_ROBOT, type DWClientDownStream } from "dingtalk-stream";
2
- import { recordInboundSession, type OpenClawConfig, type RuntimeEnv } from "openclaw/plugin-sdk";
2
+ import { type OpenClawConfig, type RuntimeEnv } from "openclaw/plugin-sdk";
3
+ import { recordInboundSession } from "./compat.js";
3
4
  import type { DingTalkMessageData, ResolvedDingTalkAccount, DingTalkGroupConfig, AudioContent, VideoContent, FileContent, PictureContent, RichTextContent, RichTextElement, RichTextPictureElement } from "./types.js";
4
5
  import { replyViaWebhook, getFileDownloadUrl, downloadFromUrl, sendTextMessage } from "./client.js";
5
6
  import { resolveDingTalkAccount } from "./accounts.js";
package/src/onboarding.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { ChannelOnboardingAdapter } from "openclaw/plugin-sdk";
2
- import { DEFAULT_ACCOUNT_ID, promptAccountId } from "openclaw/plugin-sdk";
2
+ import { DEFAULT_ACCOUNT_ID, promptAccountId } from "./compat.js";
3
3
  import type { DingTalkConfig } from "./types.js";
4
4
  import {
5
5
  listDingTalkAccountIds,