@zhin.js/adapter-dingtalk 1.0.80 → 1.1.0

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 (65) hide show
  1. package/CHANGELOG.md +621 -0
  2. package/README.md +67 -333
  3. package/adapters/dingtalk.js +33 -0
  4. package/adapters/dingtalk.ts +38 -0
  5. package/agent/PERMITS.md +19 -0
  6. package/agent/tools/add_chat_members.ts +20 -0
  7. package/agent/tools/create_chat.ts +21 -0
  8. package/agent/tools/dept_info.ts +15 -0
  9. package/agent/tools/get_dept_users.ts +16 -0
  10. package/agent/tools/get_user.ts +15 -0
  11. package/agent/tools/list_departments.ts +16 -0
  12. package/agent/tools/send_work_notice.ts +18 -0
  13. package/agent/tools/update_chat.ts +25 -0
  14. package/commands/endpoint/add/[id].js +3 -0
  15. package/commands/endpoint/add/[id].ts +3 -0
  16. package/commands/endpoint/list.js +3 -0
  17. package/commands/endpoint/list.ts +3 -0
  18. package/commands/endpoint/remove/[id].js +3 -0
  19. package/commands/endpoint/remove/[id].ts +3 -0
  20. package/lib/client.d.ts +12 -0
  21. package/lib/client.js +2 -0
  22. package/lib/dingtalk-endpoint-commands.d.ts +1 -0
  23. package/lib/dingtalk-endpoint-commands.js +18 -0
  24. package/lib/dingtalk-runtime-state.d.ts +1 -0
  25. package/lib/dingtalk-runtime-state.js +6 -0
  26. package/lib/endpoint.d.ts +72 -0
  27. package/lib/endpoint.js +353 -0
  28. package/lib/index.d.ts +5 -15
  29. package/lib/index.js +5 -214
  30. package/lib/platform-permit.d.ts +14 -0
  31. package/lib/platform-permit.js +33 -0
  32. package/lib/protocol.d.ts +134 -0
  33. package/lib/protocol.js +265 -0
  34. package/lib/webhook.d.ts +13 -0
  35. package/lib/webhook.js +47 -0
  36. package/package.json +64 -18
  37. package/plugin.js +19 -0
  38. package/schema.json +92 -0
  39. package/src/client.ts +16 -0
  40. package/src/dingtalk-endpoint-commands.ts +19 -0
  41. package/src/dingtalk-runtime-state.ts +7 -0
  42. package/src/endpoint.ts +439 -0
  43. package/src/index.ts +44 -228
  44. package/src/platform-permit.ts +48 -0
  45. package/src/protocol.ts +385 -0
  46. package/src/webhook.ts +75 -0
  47. package/lib/adapter.d.ts +0 -16
  48. package/lib/adapter.d.ts.map +0 -1
  49. package/lib/adapter.js +0 -37
  50. package/lib/adapter.js.map +0 -1
  51. package/lib/bot.d.ts +0 -46
  52. package/lib/bot.d.ts.map +0 -1
  53. package/lib/bot.js +0 -539
  54. package/lib/bot.js.map +0 -1
  55. package/lib/index.d.ts.map +0 -1
  56. package/lib/index.js.map +0 -1
  57. package/lib/types.d.ts +0 -58
  58. package/lib/types.d.ts.map +0 -1
  59. package/lib/types.js +0 -5
  60. package/lib/types.js.map +0 -1
  61. package/plugin.yml +0 -3
  62. package/src/adapter.ts +0 -44
  63. package/src/bot.ts +0 -590
  64. package/src/types.ts +0 -56
  65. /package/{skills/dingtalk/SKILL.md → agent/skills/dingtalk.md} +0 -0
package/src/adapter.ts DELETED
@@ -1,44 +0,0 @@
1
- /**
2
- * 钉钉适配器
3
- */
4
- import {
5
- Adapter,
6
- Plugin,
7
- } from "zhin.js";
8
- import { DingTalkBot } from "./bot.js";
9
- import type { DingTalkBotConfig } from "./types.js";
10
-
11
- export class DingTalkAdapter extends Adapter<DingTalkBot> {
12
- #router: any;
13
-
14
- constructor(plugin: Plugin, router: any) {
15
- super(plugin, "dingtalk", []);
16
- this.#router = router;
17
- }
18
-
19
- createBot(config: DingTalkBotConfig): DingTalkBot {
20
- return new DingTalkBot(this, this.#router, config);
21
- }
22
-
23
- async kickMember(botId: string, sceneId: string, userId: string) {
24
- const bot = this.bots.get(botId);
25
- if (!bot) throw new Error(`Bot ${botId} 不存在`);
26
- return bot.updateChat(sceneId, { del_useridlist: [userId] });
27
- }
28
-
29
- async setGroupName(botId: string, sceneId: string, name: string) {
30
- const bot = this.bots.get(botId);
31
- if (!bot) throw new Error(`Bot ${botId} 不存在`);
32
- return bot.updateChat(sceneId, { name });
33
- }
34
-
35
- async getGroupInfo(botId: string, sceneId: string) {
36
- const bot = this.bots.get(botId);
37
- if (!bot) throw new Error(`Bot ${botId} 不存在`);
38
- return bot.getChatInfo(sceneId);
39
- }
40
-
41
- async start(): Promise<void> {
42
- await super.start();
43
- }
44
- }
package/src/bot.ts DELETED
@@ -1,590 +0,0 @@
1
- /**
2
- * 钉钉 Bot 实现
3
- */
4
- import { formatCompact, Bot, Message, MessageSegment, segment, SendContent, SendOptions } from 'zhin.js';
5
- import { registerFetchRoute, type Router, type RouterContext } from "@zhin.js/host-router/router";
6
- import { createHmac } from "crypto";
7
- import type {
8
- DingTalkBotConfig,
9
- DingTalkMessage,
10
- DingTalkEvent,
11
- AccessToken,
12
- } from "./types.js";
13
- import type { DingTalkAdapter } from "./adapter.js";
14
-
15
- export class DingTalkBot implements Bot<DingTalkBotConfig, DingTalkMessage> {
16
- $connected: boolean;
17
- private router: any;
18
- private accessToken: AccessToken;
19
- private baseURL: string;
20
- private sessionWebhooks: Map<string, string> = new Map();
21
-
22
- get $id() {
23
- return this.$config.name;
24
- }
25
-
26
- get logger() {
27
- return this.adapter.plugin.logger;
28
- }
29
-
30
- constructor(
31
- public adapter: DingTalkAdapter,
32
- router: any,
33
- public $config: DingTalkBotConfig
34
- ) {
35
- this.router = router;
36
- this.$connected = false;
37
- this.accessToken = { token: "", expires_in: 0, timestamp: 0 };
38
- this.baseURL = $config.apiBaseUrl || "https://oapi.dingtalk.com";
39
- this.setupWebhookRoute();
40
- }
41
-
42
- private async request(
43
- path: string,
44
- options: {
45
- method?: "GET" | "POST";
46
- params?: Record<string, any>;
47
- body?: any;
48
- } = {}
49
- ): Promise<any> {
50
- await this.ensureAccessToken();
51
- const { method = "GET", params = {}, body } = options;
52
- const urlParams = new URLSearchParams({
53
- ...params,
54
- access_token: this.accessToken.token,
55
- });
56
- const url = `${this.baseURL}${path}?${urlParams.toString()}`;
57
- const fetchOptions: RequestInit = {
58
- method,
59
- headers: {
60
- "Content-Type": "application/json; charset=utf-8",
61
- },
62
- };
63
- if (body && method === "POST") {
64
- fetchOptions.body = JSON.stringify(body);
65
- }
66
- const response = await fetch(url, fetchOptions);
67
- return await response.json();
68
- }
69
-
70
- private setupWebhookRoute(): void {
71
- registerFetchRoute(this.router, "POST", this.$config.webhookPath, (ctx: RouterContext) => {
72
- void this.handleWebhook(ctx);
73
- });
74
- }
75
-
76
- private async handleWebhook(ctx: RouterContext): Promise<void> {
77
- try {
78
- const body = ctx.request.body;
79
- const timestamp = ctx.get("timestamp");
80
- const sign = ctx.get("sign");
81
- if (timestamp && sign) {
82
- if (!this.verifySignature(timestamp, sign)) {
83
- this.logger.warn(formatCompact( { op: "webhook", ok: false, error: "invalid signature" }));
84
- ctx.status = 403;
85
- ctx.body = { code: -1, msg: "Forbidden" };
86
- return;
87
- }
88
- }
89
- const event = body as DingTalkEvent;
90
- if (event.msgtype) {
91
- await this.handleEvent(event);
92
- }
93
- ctx.status = 200;
94
- ctx.body = { code: 0, msg: "success" };
95
- } catch (error) {
96
- this.logger.error("Webhook error:", error);
97
- ctx.status = 500;
98
- ctx.body = { code: -1, msg: "Internal Server Error" };
99
- }
100
- }
101
-
102
- private verifySignature(timestamp: string, sign: string): boolean {
103
- try {
104
- const stringToSign = `${timestamp}\n${this.$config.appSecret}`;
105
- const hmac = createHmac("sha256", this.$config.appSecret);
106
- hmac.update(stringToSign);
107
- const calculatedSign = hmac.digest("base64");
108
- return calculatedSign === sign;
109
- } catch (error) {
110
- this.logger.error("Signature verification error:", error);
111
- return false;
112
- }
113
- }
114
-
115
- private async handleEvent(event: DingTalkEvent): Promise<void> {
116
- if (event.sessionWebhook && event.conversationId) {
117
- this.sessionWebhooks.set(event.conversationId, event.sessionWebhook);
118
- }
119
- const message = this.$formatMessage(event as any);
120
- this.adapter.emit("message.receive", message);
121
- this.logger.debug(formatCompact( {
122
- op: "recv",
123
- bot: this.$config.name,
124
- channel: message.$channel.type,
125
- id: message.$channel.id,
126
- len: segment.raw(message.$content).length,
127
- }));
128
- }
129
-
130
- private async ensureAccessToken(): Promise<void> {
131
- const now = Date.now();
132
- if (
133
- this.accessToken.token &&
134
- now <
135
- this.accessToken.timestamp +
136
- (this.accessToken.expires_in - 300) * 1000
137
- ) {
138
- return;
139
- }
140
- await this.refreshAccessToken();
141
- }
142
-
143
- private async refreshAccessToken(): Promise<void> {
144
- try {
145
- const baseURL =
146
- this.$config.apiBaseUrl || "https://oapi.dingtalk.com";
147
- const params = new URLSearchParams({
148
- appkey: this.$config.appKey,
149
- appsecret: this.$config.appSecret,
150
- });
151
- const url = `${baseURL}/gettoken?${params.toString()}`;
152
- const response = await fetch(url);
153
- const data = await response.json();
154
- if (data.errcode === 0) {
155
- this.accessToken = {
156
- token: data.access_token,
157
- expires_in: data.expires_in,
158
- timestamp: Date.now(),
159
- };
160
- this.logger.debug("Access token refreshed successfully");
161
- } else {
162
- throw new Error(`Failed to get access token: ${data.errmsg}`);
163
- }
164
- } catch (error) {
165
- this.logger.error("Failed to refresh access token:", error);
166
- throw error;
167
- }
168
- }
169
-
170
- $formatMessage(msg: DingTalkMessage): Message<DingTalkMessage> {
171
- const content = this.parseMessageContent(msg);
172
- const chatType = msg.conversationType === "2" ? "group" : "private";
173
- return Message.from(msg, {
174
- $id: msg.msgId || Date.now().toString(),
175
- $adapter: "dingtalk",
176
- $bot: this.$config.name,
177
- $sender: {
178
- id: msg.senderId || msg.senderStaffId || "unknown",
179
- name: msg.senderNick || msg.senderId || "Unknown User",
180
- },
181
- $channel: {
182
- id: msg.conversationId || "unknown",
183
- type: chatType as any,
184
- },
185
- $content: content,
186
- $raw: JSON.stringify(msg),
187
- $timestamp: msg.createAt || Date.now(),
188
- $recall: async () => {
189
- await this.$recallMessage(msg.msgId || "");
190
- },
191
- $reply: async (content: SendContent): Promise<string> => {
192
- return await this.adapter.sendMessage({
193
- context: "dingtalk",
194
- bot: this.$config.name,
195
- id: msg.conversationId || msg.senderId || "unknown",
196
- type: chatType,
197
- content: content,
198
- });
199
- },
200
- });
201
- }
202
-
203
- private parseMessageContent(msg: DingTalkMessage): MessageSegment[] {
204
- const content: MessageSegment[] = [];
205
- if (!msg.msgtype) return content;
206
- try {
207
- switch (msg.msgtype) {
208
- case "text":
209
- if (msg.text?.content) {
210
- content.push(segment("text", { content: msg.text.content }));
211
- if (msg.atUsers && msg.atUsers.length > 0) {
212
- for (const atUser of msg.atUsers) {
213
- content.push(
214
- segment("at", {
215
- id: atUser.dingtalkId || atUser.staffId,
216
- name: atUser.dingtalkId || atUser.staffId,
217
- })
218
- );
219
- }
220
- }
221
- }
222
- break;
223
- case "picture":
224
- if (msg.content) {
225
- content.push(
226
- segment("image", {
227
- url:
228
- msg.content.downloadCode ||
229
- msg.content.pictureDownloadCode,
230
- file:
231
- msg.content.downloadCode ||
232
- msg.content.pictureDownloadCode,
233
- })
234
- );
235
- }
236
- break;
237
- case "file":
238
- if (msg.content) {
239
- content.push(
240
- segment("file", {
241
- file: msg.content.downloadCode,
242
- name: msg.content.fileName,
243
- size: msg.content.fileSize,
244
- })
245
- );
246
- }
247
- break;
248
- case "audio":
249
- if (msg.content) {
250
- content.push(
251
- segment("audio", {
252
- file: msg.content.downloadCode,
253
- duration: msg.content.duration,
254
- })
255
- );
256
- }
257
- break;
258
- case "video":
259
- if (msg.content) {
260
- content.push(
261
- segment("video", {
262
- file: msg.content.downloadCode,
263
- duration: msg.content.duration,
264
- size: msg.content.videoSize,
265
- })
266
- );
267
- }
268
- break;
269
- case "richText":
270
- if (msg.content?.richText) {
271
- for (const item of msg.content.richText) {
272
- if (item.text) {
273
- content.push(segment("text", { content: item.text }));
274
- }
275
- }
276
- }
277
- break;
278
- case "markdown":
279
- if (msg.content?.text) {
280
- content.push(
281
- segment("markdown", {
282
- content: msg.content.text,
283
- title: msg.content.title,
284
- })
285
- );
286
- }
287
- break;
288
- default:
289
- content.push(
290
- segment("text", {
291
- content: `[不支持的消息类型: ${msg.msgtype}]`,
292
- })
293
- );
294
- break;
295
- }
296
- } catch (error) {
297
- this.logger.error("Failed to parse message content:", error);
298
- content.push(segment("text", { content: "[消息解析失败]" }));
299
- }
300
- return content;
301
- }
302
-
303
- async $sendMessage(options: SendOptions): Promise<string> {
304
- const conversationId = options.id;
305
- const content = this.formatSendContent(options.content);
306
- try {
307
- const sessionWebhook = this.sessionWebhooks.get(conversationId);
308
- if (sessionWebhook) {
309
- const response = await fetch(sessionWebhook, {
310
- method: "POST",
311
- headers: {
312
- "Content-Type": "application/json; charset=utf-8",
313
- },
314
- body: JSON.stringify(content),
315
- });
316
- const data = await response.json();
317
- if (data.errcode !== 0) {
318
- throw new Error(
319
- `Failed to send message via session webhook: ${data.errmsg}`
320
- );
321
- }
322
- this.logger.debug("Message sent via session webhook");
323
- return data.msgId || Date.now().toString();
324
- }
325
- const data = await this.request("/robot/send", {
326
- method: "POST",
327
- body: {
328
- ...content,
329
- robotCode: this.$config.robotCode,
330
- },
331
- });
332
- if (data.errcode !== 0) {
333
- throw new Error(`Failed to send message: ${data.errmsg}`);
334
- }
335
- this.logger.debug("Message sent successfully");
336
- return data.msgId || Date.now().toString();
337
- } catch (error) {
338
- this.logger.error("Failed to send message:", error);
339
- throw error;
340
- }
341
- }
342
-
343
- async $recallMessage(id: string): Promise<void> {
344
- this.logger.warn(formatCompact( { op: "recall", ok: false, error: "not supported" }));
345
- }
346
-
347
- private formatSendContent(content: SendContent): any {
348
- if (typeof content === "string") {
349
- return { msgtype: "text", text: { content } };
350
- }
351
- if (Array.isArray(content)) {
352
- const textParts: string[] = [];
353
- const atUserIds: string[] = [];
354
- let hasMedia = false;
355
- let mediaContent: any = null;
356
- for (const item of content) {
357
- if (typeof item === "string") {
358
- textParts.push(item);
359
- } else {
360
- const seg = item as MessageSegment;
361
- switch (seg.type) {
362
- case "text":
363
- textParts.push(seg.data.content || seg.data.text || "");
364
- break;
365
- case "at":
366
- const userId = seg.data.id || seg.data.userId;
367
- if (userId) {
368
- atUserIds.push(userId);
369
- textParts.push(`@${seg.data.name || userId} `);
370
- }
371
- break;
372
- case "image":
373
- if (!hasMedia) {
374
- hasMedia = true;
375
- mediaContent = {
376
- msgtype: "picture",
377
- picture: {
378
- picURL: seg.data.url || seg.data.file,
379
- },
380
- };
381
- }
382
- break;
383
- case "markdown":
384
- if (!hasMedia) {
385
- hasMedia = true;
386
- mediaContent = {
387
- msgtype: "markdown",
388
- markdown: {
389
- title: seg.data.title || "消息",
390
- text: seg.data.content || seg.data.text,
391
- },
392
- };
393
- }
394
- break;
395
- case "link":
396
- if (!hasMedia) {
397
- hasMedia = true;
398
- mediaContent = {
399
- msgtype: "link",
400
- link: {
401
- title: seg.data.title || "链接",
402
- text: seg.data.text || seg.data.content || "",
403
- messageUrl: seg.data.url,
404
- picUrl: seg.data.picUrl,
405
- },
406
- };
407
- }
408
- break;
409
- }
410
- }
411
- }
412
- if (hasMedia && mediaContent) return mediaContent;
413
- const result: any = {
414
- msgtype: "text",
415
- text: { content: textParts.join("") },
416
- };
417
- if (atUserIds.length > 0) {
418
- result.at = { atUserIds, isAtAll: false };
419
- }
420
- return result;
421
- }
422
- return { msgtype: "text", text: { content: String(content) } };
423
- }
424
-
425
- async $connect(): Promise<void> {
426
- try {
427
- await this.refreshAccessToken();
428
- this.$connected = true;
429
- this.logger.info(formatCompact({ bot: this.$config.name }));
430
- this.logger.info(formatCompact( { op: "webhook", path: this.$config.webhookPath }));
431
- } catch (error) {
432
- this.logger.error("Failed to connect DingTalk bot:", error);
433
- throw error;
434
- }
435
- }
436
-
437
- async $disconnect(): Promise<void> {
438
- try {
439
- this.sessionWebhooks.clear();
440
- this.$connected = false;
441
- this.logger.info(formatCompact( { op: "disconnect", bot: this.$config.name }));
442
- } catch (error) {
443
- this.logger.error("Error disconnecting DingTalk bot:", error);
444
- }
445
- }
446
-
447
- async getUserInfo(userId: string): Promise<any> {
448
- try {
449
- const data = await this.request("/topapi/v2/user/get", {
450
- method: "POST",
451
- body: { userid: userId },
452
- });
453
- if (data.errcode === 0) return data.result;
454
- throw new Error(`Failed to get user info: ${data.errmsg}`);
455
- } catch (error) {
456
- this.logger.error("Failed to get user info:", error);
457
- return null;
458
- }
459
- }
460
-
461
- async getDepartmentUsers(deptId: number): Promise<any[]> {
462
- try {
463
- const data = await this.request("/topapi/user/listid", {
464
- method: "POST",
465
- body: { dept_id: deptId },
466
- });
467
- if (data.errcode === 0) return data.result.userid_list || [];
468
- throw new Error(`Failed to get department users: ${data.errmsg}`);
469
- } catch (error) {
470
- this.logger.error("Failed to get department users:", error);
471
- return [];
472
- }
473
- }
474
-
475
- async sendWorkNotice(userIdList: string[], content: any): Promise<boolean> {
476
- try {
477
- const data = await this.request(
478
- "/topapi/message/corpconversation/asyncsend_v2",
479
- {
480
- method: "POST",
481
- body: {
482
- agent_id: this.$config.robotCode,
483
- userid_list: userIdList.join(","),
484
- msg: content,
485
- },
486
- }
487
- );
488
- if (data.errcode === 0) {
489
- this.logger.debug("Work notice sent successfully");
490
- return true;
491
- }
492
- throw new Error(`Failed to send work notice: ${data.errmsg}`);
493
- } catch (error) {
494
- this.logger.error("Failed to send work notice:", error);
495
- return false;
496
- }
497
- }
498
-
499
- async getDepartmentList(deptId: number = 1): Promise<any[]> {
500
- try {
501
- const data = await this.request("/topapi/v2/department/listsub", {
502
- method: "POST",
503
- body: { dept_id: deptId },
504
- });
505
- if (data.errcode === 0) return data.result || [];
506
- throw new Error(`Failed to get department list: ${data.errmsg}`);
507
- } catch (error) {
508
- this.logger.error("Failed to get department list:", error);
509
- return [];
510
- }
511
- }
512
-
513
- async getDepartmentInfo(deptId: number): Promise<any> {
514
- try {
515
- const data = await this.request("/topapi/v2/department/get", {
516
- method: "POST",
517
- body: { dept_id: deptId },
518
- });
519
- if (data.errcode === 0) return data.result;
520
- throw new Error(`Failed to get department info: ${data.errmsg}`);
521
- } catch (error) {
522
- this.logger.error("Failed to get department info:", error);
523
- return null;
524
- }
525
- }
526
-
527
- async createChat(
528
- name: string,
529
- ownerUserId: string,
530
- userIdList: string[]
531
- ): Promise<string | null> {
532
- try {
533
- const data = await this.request("/topapi/chat/create", {
534
- method: "POST",
535
- body: {
536
- name,
537
- owner: ownerUserId,
538
- useridlist: userIdList,
539
- },
540
- });
541
- if (data.errcode === 0) {
542
- this.logger.debug(formatCompact( { op: "create_chat", chat: data.chatid }));
543
- return data.chatid;
544
- }
545
- throw new Error(`Failed to create chat: ${data.errmsg}`);
546
- } catch (error) {
547
- this.logger.error("Failed to create chat:", error);
548
- return null;
549
- }
550
- }
551
-
552
- async getChatInfo(chatId: string): Promise<any> {
553
- try {
554
- const data = await this.request("/topapi/chat/get", {
555
- method: "POST",
556
- body: { chatid: chatId },
557
- });
558
- if (data.errcode === 0) return data.chat_info;
559
- throw new Error(`Failed to get chat info: ${data.errmsg}`);
560
- } catch (error) {
561
- this.logger.error("Failed to get chat info:", error);
562
- return null;
563
- }
564
- }
565
-
566
- async updateChat(
567
- chatId: string,
568
- options: {
569
- name?: string;
570
- owner?: string;
571
- add_useridlist?: string[];
572
- del_useridlist?: string[];
573
- }
574
- ): Promise<boolean> {
575
- try {
576
- const data = await this.request("/topapi/chat/update", {
577
- method: "POST",
578
- body: { chatid: chatId, ...options },
579
- });
580
- if (data.errcode === 0) {
581
- this.logger.debug(formatCompact( { op: "update_chat", chat: chatId }));
582
- return true;
583
- }
584
- throw new Error(`Failed to update chat: ${data.errmsg}`);
585
- } catch (error) {
586
- this.logger.error("Failed to update chat:", error);
587
- return false;
588
- }
589
- }
590
- }
package/src/types.ts DELETED
@@ -1,56 +0,0 @@
1
- /**
2
- * 钉钉适配器类型定义
3
- */
4
-
5
- export interface DingTalkBotConfig {
6
- context: "dingtalk";
7
- name: string;
8
- appKey: string;
9
- appSecret: string;
10
- webhookPath: string;
11
- robotCode?: string;
12
- apiBaseUrl?: string;
13
- }
14
-
15
- export interface DingTalkMessage {
16
- msgtype?: string;
17
- text?: { content?: string };
18
- msgId?: string;
19
- createAt?: number;
20
- conversationType?: string;
21
- conversationId?: string;
22
- senderId?: string;
23
- senderNick?: string;
24
- senderCorpId?: string;
25
- sessionWebhook?: string;
26
- chatbotCorpId?: string;
27
- chatbotUserId?: string;
28
- isAdmin?: boolean;
29
- senderStaffId?: string;
30
- atUsers?: Array<{ dingtalkId?: string; staffId?: string }>;
31
- content?: any;
32
- }
33
-
34
- export interface DingTalkEvent {
35
- msgtype?: string;
36
- text?: any;
37
- conversationId?: string;
38
- atUsers?: any[];
39
- chatbotUserId?: string;
40
- msgId?: string;
41
- senderNick?: string;
42
- isAdmin?: boolean;
43
- senderStaffId?: string;
44
- sessionWebhook?: string;
45
- createAt?: number;
46
- senderCorpId?: string;
47
- conversationType?: string;
48
- senderId?: string;
49
- [key: string]: any;
50
- }
51
-
52
- export interface AccessToken {
53
- token: string;
54
- expires_in: number;
55
- timestamp: number;
56
- }