@huo15/dingtalk-connector-pro 1.0.0 → 1.0.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.
Files changed (60) hide show
  1. package/dist/hooks/init.js +16 -0
  2. package/hooks/init.js +18 -0
  3. package/package.json +25 -11
  4. package/CHANGELOG.md +0 -485
  5. package/docs/AGENT_ROUTING.md +0 -335
  6. package/docs/DEAP_AGENT_GUIDE.en.md +0 -115
  7. package/docs/DEAP_AGENT_GUIDE.md +0 -115
  8. package/docs/images/dingtalk.svg +0 -1
  9. package/docs/images/image-1.png +0 -0
  10. package/docs/images/image-2.png +0 -0
  11. package/docs/images/image-3.png +0 -0
  12. package/docs/images/image-4.png +0 -0
  13. package/docs/images/image-5.png +0 -0
  14. package/docs/images/image-6.png +0 -0
  15. package/docs/images/image-7.png +0 -0
  16. package/install-beta.sh +0 -438
  17. package/install-npm.sh +0 -167
  18. package/openclaw.plugin.json +0 -498
  19. package/src/channel.ts +0 -463
  20. package/src/config/accounts.ts +0 -242
  21. package/src/config/schema.ts +0 -148
  22. package/src/core/connection.ts +0 -722
  23. package/src/core/message-handler.ts +0 -1700
  24. package/src/core/provider.ts +0 -111
  25. package/src/core/state.ts +0 -54
  26. package/src/directory.ts +0 -95
  27. package/src/docs.ts +0 -293
  28. package/src/gateway-methods.ts +0 -404
  29. package/src/onboarding.ts +0 -413
  30. package/src/policy.ts +0 -32
  31. package/src/probe.ts +0 -212
  32. package/src/reply-dispatcher.ts +0 -630
  33. package/src/runtime.ts +0 -32
  34. package/src/sdk/helpers.ts +0 -322
  35. package/src/sdk/types.ts +0 -513
  36. package/src/secret-input.ts +0 -19
  37. package/src/services/media/audio.ts +0 -54
  38. package/src/services/media/chunk-upload.ts +0 -296
  39. package/src/services/media/common.ts +0 -155
  40. package/src/services/media/file.ts +0 -70
  41. package/src/services/media/image.ts +0 -81
  42. package/src/services/media/index.ts +0 -10
  43. package/src/services/media/video.ts +0 -162
  44. package/src/services/media.ts +0 -1136
  45. package/src/services/messaging/card.ts +0 -342
  46. package/src/services/messaging/index.ts +0 -17
  47. package/src/services/messaging/send.ts +0 -141
  48. package/src/services/messaging.ts +0 -1013
  49. package/src/targets.ts +0 -45
  50. package/src/types/index.ts +0 -59
  51. package/src/utils/agent.ts +0 -63
  52. package/src/utils/async.ts +0 -51
  53. package/src/utils/constants.ts +0 -27
  54. package/src/utils/http-client.ts +0 -37
  55. package/src/utils/index.ts +0 -8
  56. package/src/utils/logger.ts +0 -78
  57. package/src/utils/session.ts +0 -147
  58. package/src/utils/token.ts +0 -93
  59. package/src/utils/utils-legacy.ts +0 -454
  60. package/tsconfig.json +0 -20
@@ -1,404 +0,0 @@
1
- /**
2
- * Gateway Methods 注册
3
- *
4
- * 提供钉钉插件的 RPC 接口,允许外部系统、AI Agent 和其他插件调用钉钉功能
5
- */
6
-
7
- import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
8
- import { resolveDingtalkAccount } from "./config/accounts.ts";
9
- import { DingtalkDocsClient } from "./docs.ts";
10
- import { sendProactive } from "./services/messaging.ts";
11
- import { getUnionId } from "./utils/utils-legacy.ts";
12
-
13
- /**
14
- * 注册所有 Gateway Methods
15
- */
16
- export function registerGatewayMethods(api: OpenClawPluginApi) {
17
- const log = api.logger;
18
-
19
- // ============ 消息发送类 ============
20
-
21
- /**
22
- * 主动发送单聊消息
23
- *
24
- * @example
25
- * ```typescript
26
- * await gateway.call('dingtalk-connector.sendToUser', {
27
- * userId: 'user123',
28
- * content: '任务已完成!',
29
- * useAICard: true
30
- * });
31
- * ```
32
- */
33
- api.registerGatewayMethod('dingtalk-connector.sendToUser', async ({ context, params, respond }) => {
34
- const { loadConfig } = await import('openclaw/plugin-sdk/config-runtime');
35
- const cfg = loadConfig();
36
- try {
37
- const { userId, userIds, content, msgType, title, useAICard, fallbackToNormal, accountId } = params || {};
38
- const account = resolveDingtalkAccount({ cfg, accountId });
39
- if (!account.config?.clientId) {
40
- return respond(false, { error: 'DingTalk not configured' });
41
- }
42
-
43
- const targetUserIds = userIds || (userId ? [userId] : []);
44
- if (targetUserIds.length === 0) {
45
- return respond(false, { error: 'userId or userIds is required' });
46
- }
47
-
48
- if (!content) {
49
- return respond(false, { error: 'content is required' });
50
- }
51
-
52
- // 构建目标
53
- const target = targetUserIds.length === 1
54
- ? { userId: targetUserIds[0] }
55
- : { userIds: targetUserIds };
56
-
57
- const result = await sendProactive(account.config, target, content, {
58
- msgType,
59
- title,
60
- log,
61
- useAICard: useAICard !== false,
62
- fallbackToNormal: fallbackToNormal !== false,
63
- });
64
-
65
- respond(result.ok, result);
66
- } catch (err: any) {
67
- log?.error?.(`[Gateway][sendToUser] 错误: ${err.message}`);
68
- respond(false, { error: err.message });
69
- }
70
- });
71
-
72
- /**
73
- * 主动发送群聊消息
74
- *
75
- * @example
76
- * ```typescript
77
- * await gateway.call('dingtalk-connector.sendToGroup', {
78
- * openConversationId: 'cid123',
79
- * content: '构建失败,请检查日志',
80
- * useAICard: true
81
- * });
82
- * ```
83
- */
84
- api.registerGatewayMethod('dingtalk-connector.sendToGroup', async ({ context, params, respond }) => {
85
- const { loadConfig } = await import('openclaw/plugin-sdk/config-runtime');
86
- const cfg = loadConfig();
87
- try {
88
- const { openConversationId, content, msgType, title, useAICard, fallbackToNormal, accountId } = params || {};
89
- const account = resolveDingtalkAccount({ cfg, accountId });
90
- if (!account.config?.clientId) {
91
- return respond(false, { error: 'DingTalk not configured' });
92
- }
93
-
94
- if (!openConversationId) {
95
- return respond(false, { error: 'openConversationId is required' });
96
- }
97
-
98
- if (!content) {
99
- return respond(false, { error: 'content is required' });
100
- }
101
-
102
- const result = await sendProactive(account.config, { openConversationId }, content, {
103
- msgType,
104
- title,
105
- log,
106
- useAICard: useAICard !== false,
107
- fallbackToNormal: fallbackToNormal !== false,
108
- });
109
-
110
- respond(result.ok, result);
111
- } catch (err: any) {
112
- log?.error?.(`[Gateway][sendToGroup] 错误: ${err.message}`);
113
- console.error(err);
114
- respond(false, { error: err.message });
115
- }
116
- });
117
-
118
- api.registerGatewayMethod('dingtalk-connector.send', async ({ context, params, respond }) => {
119
- const { loadConfig } = await import('openclaw/plugin-sdk/config-runtime');
120
- const cfg = loadConfig();
121
- try {
122
- const { target, content, message, msgType, title, useAICard, fallbackToNormal, accountId } = params || {};
123
- const actualContent = content || message;
124
- const account = resolveDingtalkAccount({ cfg, accountId });
125
- log?.info?.(`[Gateway][send] 收到请求: target=${target}, contentLen=${actualContent?.length}`);
126
-
127
- if (!account.config?.clientId) {
128
- return respond(false, { error: 'DingTalk not configured' });
129
- }
130
-
131
- if (!target) {
132
- return respond(false, { error: 'target is required (format: user:<userId> or group:<openConversationId>)' });
133
- }
134
-
135
- if (!actualContent) {
136
- return respond(false, { error: 'content is required' });
137
- }
138
-
139
- const targetStr = String(target);
140
- let sendTarget: { userId?: string; openConversationId?: string };
141
-
142
- if (targetStr.startsWith('user:')) {
143
- sendTarget = { userId: targetStr.slice(5) };
144
- } else if (targetStr.startsWith('group:')) {
145
- sendTarget = { openConversationId: targetStr.slice(6) };
146
- } else {
147
- // 默认当作 userId
148
- sendTarget = { userId: targetStr };
149
- }
150
-
151
- const result = await sendProactive(account.config, sendTarget, actualContent, {
152
- msgType,
153
- title,
154
- log,
155
- useAICard: useAICard !== false,
156
- fallbackToNormal: fallbackToNormal !== false,
157
- });
158
-
159
- respond(result.ok, result);
160
- } catch (err: any) {
161
- log?.error?.(`[Gateway][send] 错误: ${err.message}`);
162
- respond(false, { error: err.message });
163
- }
164
- });
165
-
166
- // ============ 文档操作类 ============
167
-
168
- api.registerGatewayMethod('dingtalk-connector.docs.read', async ({ context, params, respond }) => {
169
- const { loadConfig } = await import('openclaw/plugin-sdk/config-runtime');
170
- const cfg = loadConfig();
171
- try {
172
- const { docId, operatorId: rawOperatorId, accountId } = params || {};
173
- const account = resolveDingtalkAccount({ cfg, accountId });
174
-
175
- if (!account.config?.clientId) {
176
- return respond(false, { error: 'DingTalk not configured' });
177
- }
178
-
179
- if (!docId) {
180
- return respond(false, { error: 'docId is required' });
181
- }
182
-
183
- if (!rawOperatorId) {
184
- return respond(false, { error: 'operatorId (unionId or staffId) is required' });
185
- }
186
-
187
- // 如果 operatorId 不像 unionId,尝试转换
188
- let operatorId = rawOperatorId;
189
- if (!rawOperatorId.includes('$')) {
190
- const resolved = await getUnionId(rawOperatorId, account.config, log);
191
- if (resolved) operatorId = resolved;
192
- }
193
-
194
- const client = new DingtalkDocsClient(account.config, log);
195
- const content = await client.readDoc(docId, operatorId);
196
-
197
- if (content !== null) {
198
- respond(true, { content });
199
- } else {
200
- respond(false, { error: 'Failed to read document node' });
201
- }
202
- } catch (err: any) {
203
- log?.error?.(`[Gateway][docs.read] 错误: ${err.message}`);
204
- respond(false, { error: err.message });
205
- }
206
- });
207
-
208
- /**
209
- * 创建钉钉文档
210
- *
211
- * @example
212
- * ```typescript
213
- * const result = await gateway.call('dingtalk-connector.docs.create', {
214
- * spaceId: 'workspace123',
215
- * title: '会议纪要',
216
- * content: '今天讨论了...'
217
- * });
218
- * console.log('文档ID:', result.docId);
219
- * ```
220
- */
221
- api.registerGatewayMethod('dingtalk-connector.docs.create', async ({ context, params, respond }) => {
222
- const { loadConfig } = await import('openclaw/plugin-sdk/config-runtime');
223
- const cfg = loadConfig();
224
- try {
225
- const { spaceId, title, content, accountId } = params || {};
226
- const account = resolveDingtalkAccount({ cfg, accountId });
227
-
228
- if (!account.config?.clientId) {
229
- return respond(false, { error: 'DingTalk not configured' });
230
- }
231
-
232
- if (!spaceId || !title) {
233
- return respond(false, { error: 'spaceId and title are required' });
234
- }
235
-
236
- const client = new DingtalkDocsClient(account.config, log);
237
- const doc = await client.createDoc(spaceId, title, content);
238
-
239
- if (doc) {
240
- respond(true, doc);
241
- } else {
242
- respond(false, { error: 'Failed to create document' });
243
- }
244
- } catch (err: any) {
245
- log?.error?.(`[Gateway][docs.create] 错误: ${err.message}`);
246
- respond(false, { error: err.message });
247
- }
248
- });
249
-
250
- /**
251
- * 向钉钉文档追加内容
252
- *
253
- * @example
254
- * ```typescript
255
- * await gateway.call('dingtalk-connector.docs.append', {
256
- * docId: 'doc123',
257
- * content: '补充内容...'
258
- * });
259
- * ```
260
- */
261
- api.registerGatewayMethod('dingtalk-connector.docs.append', async ({ context, params, respond }) => {
262
- const { loadConfig } = await import('openclaw/plugin-sdk/config-runtime');
263
- const cfg = loadConfig();
264
- try {
265
- const { docId, content, accountId } = params || {};
266
- const account = resolveDingtalkAccount({ cfg, accountId });
267
-
268
- if (!account.config?.clientId) {
269
- return respond(false, { error: 'DingTalk not configured' });
270
- }
271
-
272
- if (!docId || !content) {
273
- return respond(false, { error: 'docId and content are required' });
274
- }
275
-
276
- const client = new DingtalkDocsClient(account.config, log);
277
- const ok = await client.appendToDoc(docId, content);
278
-
279
- respond(ok, ok ? { success: true } : { error: 'Failed to append to document' });
280
- } catch (err: any) {
281
- log?.error?.(`[Gateway][docs.append] 错误: ${err.message}`);
282
- respond(false, { error: err.message });
283
- }
284
- });
285
-
286
- /**
287
- * 搜索钉钉文档
288
- *
289
- * @example
290
- * ```typescript
291
- * const result = await gateway.call('dingtalk-connector.docs.search', {
292
- * keyword: '项目规范',
293
- * spaceId: 'workspace123' // 可选
294
- * });
295
- * console.log('找到文档:', result.docs);
296
- * ```
297
- */
298
- api.registerGatewayMethod('dingtalk-connector.docs.search', async ({ context, params, respond }) => {
299
- const { loadConfig } = await import('openclaw/plugin-sdk/config-runtime');
300
- const cfg = loadConfig();
301
- try {
302
- const { keyword, spaceId, accountId } = params || {};
303
- const account = resolveDingtalkAccount({ cfg, accountId });
304
-
305
- if (!account.config?.clientId) {
306
- return respond(false, { error: 'DingTalk not configured' });
307
- }
308
-
309
- if (!keyword) {
310
- return respond(false, { error: 'keyword is required' });
311
- }
312
-
313
- const client = new DingtalkDocsClient(account.config, log);
314
- const docs = await client.searchDocs(keyword, spaceId);
315
-
316
- respond(true, { docs });
317
- } catch (err: any) {
318
- log?.error?.(`[Gateway][docs.search] 错误: ${err.message}`);
319
- respond(false, { error: err.message });
320
- }
321
- });
322
-
323
- /**
324
- * 列出空间下的文档
325
- *
326
- * @example
327
- * ```typescript
328
- * const result = await gateway.call('dingtalk-connector.docs.list', {
329
- * spaceId: 'workspace123',
330
- * parentId: 'folder456' // 可选,不传则列出根目录
331
- * });
332
- * console.log('文档列表:', result.docs);
333
- * ```
334
- */
335
- api.registerGatewayMethod('dingtalk-connector.docs.list', async ({ context, params, respond }) => {
336
- const { loadConfig } = await import('openclaw/plugin-sdk/config-runtime');
337
- const cfg = loadConfig();
338
- try {
339
- const { spaceId, parentId, accountId } = params || {};
340
- const account = resolveDingtalkAccount({ cfg, accountId });
341
-
342
- if (!account.config?.clientId) {
343
- return respond(false, { error: 'DingTalk not configured' });
344
- }
345
-
346
- if (!spaceId) {
347
- return respond(false, { error: 'spaceId is required' });
348
- }
349
-
350
- const client = new DingtalkDocsClient(account.config, log);
351
- const docs = await client.listDocs(spaceId, parentId);
352
-
353
- respond(true, { docs });
354
- } catch (err: any) {
355
- log?.error?.(`[Gateway][docs.list] 错误: ${err.message}`);
356
- respond(false, { error: err.message });
357
- }
358
- });
359
-
360
- // ============ 状态检查类 ============
361
-
362
- api.registerGatewayMethod('dingtalk-connector.status', async ({ context, params, respond }) => {
363
- const { loadConfig } = await import('openclaw/plugin-sdk/config-runtime');
364
- const cfg = loadConfig();
365
- try {
366
- const accountId = (params as any)?.accountId as string | undefined;
367
- const account = resolveDingtalkAccount({ cfg, accountId });
368
- const hasClientId = !!account.config?.clientId;
369
- const hasClientSecret = !!account.config?.clientSecret;
370
-
371
- respond(true, {
372
- configured: hasClientId && hasClientSecret,
373
- enabled: account.enabled,
374
- accountId: account.accountId,
375
- clientId: hasClientId ? String(account.config!.clientId).substring(0, 8) + '...' : undefined,
376
- });
377
- } catch (err: any) {
378
- log?.error?.(`[Gateway][status] 错误: ${err.message}`);
379
- respond(false, { error: err.message });
380
- }
381
- });
382
-
383
- api.registerGatewayMethod('dingtalk-connector.probe', async ({ context, respond }) => {
384
- const { loadConfig } = await import('openclaw/plugin-sdk/config-runtime');
385
- const cfg = loadConfig();
386
- try {
387
- const account = resolveDingtalkAccount({ cfg });
388
-
389
- if (!account.config?.clientId || !account.config?.clientSecret) {
390
- return respond(false, { error: 'Not configured' });
391
- }
392
-
393
- // 尝试获取 access token 来验证连接
394
- const { getAccessToken } = await import('./utils/utils-legacy.ts');
395
- await getAccessToken(account.config);
396
-
397
- respond(true, { ok: true, details: { clientId: account.config.clientId } });
398
- } catch (err: any) {
399
- log?.error?.(`[Gateway][probe] 错误: ${err.message}`);
400
- respond(false, { ok: false, error: err.message });
401
- }
402
- });
403
-
404
- }