@zhin.js/adapter-dingtalk 7.0.0 → 7.0.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/CHANGELOG.md +22 -0
- package/README.md +11 -2
- package/adapters/dingtalk.js +0 -3
- package/adapters/dingtalk.ts +0 -3
- package/agent/tools/add_chat_members.ts +4 -6
- package/agent/tools/create_chat.ts +4 -6
- package/agent/tools/dept_info.ts +4 -6
- package/agent/tools/get_dept_users.ts +4 -6
- package/agent/tools/get_user.ts +4 -6
- package/agent/tools/list_departments.ts +4 -6
- package/agent/tools/send_work_notice.ts +4 -6
- package/agent/tools/update_chat.ts +4 -6
- package/lib/client.d.ts +12 -0
- package/lib/client.js +2 -0
- package/lib/dingtalk-endpoint-commands.d.ts +1 -1
- package/lib/endpoint.d.ts +32 -18
- package/lib/endpoint.js +47 -15
- package/lib/index.d.ts +2 -2
- package/lib/index.js +2 -2
- package/lib/protocol.d.ts +1 -1
- package/lib/protocol.js +1 -1
- package/package.json +15 -14
- package/src/client.ts +16 -0
- package/src/endpoint.ts +66 -19
- package/src/index.ts +3 -7
- package/src/protocol.ts +1 -1
- package/lib/dingtalk-agent-deps.d.ts +0 -26
- package/lib/dingtalk-agent-deps.js +0 -30
- package/src/dingtalk-agent-deps.ts +0 -58
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @zhin.js/adapter-dingtalk
|
|
2
2
|
|
|
3
|
+
## 7.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 1fc78bc: Unify native platform Client access behind the literal `adapter` discriminant. Handlers infer both native events and Clients, while command, inbound/outbound middleware, and both Agent tool authoring surfaces expose the exact operation-scoped Client through a lazy `$client` getter. Definitions without `adapter` keep `$client` typed as `unknown`, and runtime dispatch rejects adapter mismatches before resolving the Client. Bundled platform tools now use this single path instead of model-provided endpoint ids and adapter-specific dependency wrappers. Every adapter registers one Client/EventMap contract, and protocol adapters including NapCat, Milky, OneBot and Satori now produce transport-independent Client objects rather than letting Endpoint instances impersonate Clients.
|
|
8
|
+
- Updated dependencies [e9c6a73]
|
|
9
|
+
- Updated dependencies [4e8117c]
|
|
10
|
+
- Updated dependencies [902fa35]
|
|
11
|
+
- Updated dependencies [54bfd6b]
|
|
12
|
+
- Updated dependencies [12025ee]
|
|
13
|
+
- Updated dependencies [09b14d6]
|
|
14
|
+
- Updated dependencies [1fc78bc]
|
|
15
|
+
- @zhin.js/agent@1.1.16
|
|
16
|
+
- @zhin.js/adapter@1.2.1
|
|
17
|
+
- @zhin.js/core@1.5.14
|
|
18
|
+
- @zhin.js/host-http@1.0.13
|
|
19
|
+
- @zhin.js/command@1.0.16
|
|
20
|
+
- @zhin.js/logger@1.0.77
|
|
21
|
+
- zhin.js@6.0.14
|
|
22
|
+
- @zhin.js/feature-kit@1.0.13
|
|
23
|
+
- @zhin.js/permission@1.0.4
|
|
24
|
+
|
|
3
25
|
## 7.0.0
|
|
4
26
|
|
|
5
27
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ pnpm add @zhin.js/adapter-dingtalk
|
|
|
20
20
|
|
|
21
21
|
- `@zhin.js/adapter` — 约定式薄入口 `adapters/dingtalk.ts`(`defineAdapter`)
|
|
22
22
|
- 实现:`src/endpoint.ts`(生命周期/出站/OpenAPI)、`src/webhook.ts`(验签入站)、`src/protocol.ts`
|
|
23
|
-
- `@zhin.js/core` — `
|
|
23
|
+
- `@zhin.js/core` — `Endpoint.emit(...)` 入站、`outboundMessageToken` 出站
|
|
24
24
|
- `@zhin.js/host-http` — `httpHostToken` 注册 Webhook 路由(**非** legacy host-router/Koa)
|
|
25
25
|
- `zhin.js` — `plugin.ts`(`definePlugin`)
|
|
26
26
|
- 配置经插件 `schema.json` 落到 `plugins.<instanceKey>`
|
|
@@ -75,7 +75,7 @@ plugins:
|
|
|
75
75
|
|
|
76
76
|
## Agent 工具
|
|
77
77
|
|
|
78
|
-
`agent/` 目录保留(get_user
|
|
78
|
+
`agent/` 目录保留(get_user、部门、群聊、工作通知等)。工具声明 `adapter: 'dingtalk'` 后,通过惰性的 `context.$client` 自动取得当前操作的 `DingTalkClient`;无需把 Endpoint id 暴露给模型。
|
|
79
79
|
|
|
80
80
|
## 平台权限(platform permit)
|
|
81
81
|
|
|
@@ -87,3 +87,12 @@ plugins:
|
|
|
87
87
|
pnpm --filter @zhin.js/adapter-dingtalk build
|
|
88
88
|
pnpm --filter @zhin.js/adapter-dingtalk test
|
|
89
89
|
```
|
|
90
|
+
|
|
91
|
+
## 故障排查
|
|
92
|
+
|
|
93
|
+
| 现象 | 排查 |
|
|
94
|
+
| --- | --- |
|
|
95
|
+
| 平台校验 URL 失败 | 确认公网 HTTPS 可达,HTTP Host 已监听,路径与 `webhookPath` 一致 |
|
|
96
|
+
| Webhook 返回 401/403 | 检查 `appSecret`、签名时间戳与服务器时钟 |
|
|
97
|
+
| 能收到但无法回复 | 检查 `robotCode`、应用权限与 session webhook 是否有效 |
|
|
98
|
+
| Endpoint 未出现 | 在日志查 Schema 或凭据错误,再到运行时能力核对 Endpoint |
|
package/adapters/dingtalk.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* Convention entry: discover `adapters/dingtalk.ts` → defineAdapter.
|
|
4
4
|
*/
|
|
5
5
|
import { defineAdapter } from 'zhin.js/adapter';
|
|
6
|
-
import { messageGatewayToken, sideEventGatewayToken } from '@zhin.js/core/runtime';
|
|
7
6
|
import { httpHostToken } from '@zhin.js/host-http';
|
|
8
7
|
import { DingTalkEndpoint } from "../lib/endpoint.js";
|
|
9
8
|
import { resolveDingTalkConfig, } from "../lib/protocol.js";
|
|
@@ -27,8 +26,6 @@ export default defineAdapter({
|
|
|
27
26
|
});
|
|
28
27
|
return new DingTalkEndpoint({
|
|
29
28
|
id: context.id,
|
|
30
|
-
gateway: context.use(messageGatewayToken),
|
|
31
|
-
sideEvents: context.use(sideEventGatewayToken),
|
|
32
29
|
http: context.use(httpHostToken),
|
|
33
30
|
config,
|
|
34
31
|
});
|
package/adapters/dingtalk.ts
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* Convention entry: discover `adapters/dingtalk.ts` → defineAdapter.
|
|
3
3
|
*/
|
|
4
4
|
import { defineAdapter } from 'zhin.js/adapter';
|
|
5
|
-
import { messageGatewayToken, sideEventGatewayToken } from '@zhin.js/core/runtime';
|
|
6
5
|
import { httpHostToken } from '@zhin.js/host-http';
|
|
7
6
|
import { DingTalkEndpoint } from '../src/endpoint.js';
|
|
8
7
|
import {
|
|
@@ -32,8 +31,6 @@ export default defineAdapter<DingTalkAdapterConfig>({
|
|
|
32
31
|
});
|
|
33
32
|
return new DingTalkEndpoint({
|
|
34
33
|
id: context.id,
|
|
35
|
-
gateway: context.use(messageGatewayToken),
|
|
36
|
-
sideEvents: context.use(sideEventGatewayToken),
|
|
37
34
|
http: context.use(httpHostToken),
|
|
38
35
|
config,
|
|
39
36
|
});
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import { defineAgentTool } from '@zhin.js/agent/tools';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { getDingtalkAgentDeps } from '../../src/dingtalk-agent-deps.js';
|
|
4
3
|
import { platformPermit } from '../../src/platform-permit.js';
|
|
5
4
|
|
|
6
|
-
export default defineAgentTool<{
|
|
5
|
+
export default defineAgentTool<{ chat_id: string; user_ids: string }>({
|
|
7
6
|
description: '向钉钉群聊添加成员',
|
|
8
7
|
inputSchema: z.object({
|
|
9
|
-
endpoint_id: z.string().describe('Endpoint 名称'),
|
|
10
8
|
chat_id: z.string().describe('群聊 ID'),
|
|
11
9
|
user_ids: z.string().describe('要添加的用户 ID 列表,逗号分隔'),
|
|
12
10
|
}),
|
|
13
|
-
|
|
11
|
+
adapter: 'dingtalk',
|
|
14
12
|
tags: ['dingtalk'],
|
|
15
13
|
permissions: [platformPermit('chat_admin')],
|
|
16
|
-
async execute({
|
|
17
|
-
const endpoint =
|
|
14
|
+
async execute({ chat_id, user_ids }: { chat_id: string; user_ids: string }, context) {
|
|
15
|
+
const endpoint = context.$client;
|
|
18
16
|
const success = await endpoint.updateChat(chat_id, { add_useridlist: user_ids.split(',') });
|
|
19
17
|
return { success, message: success ? '成员添加成功' : '添加失败' };
|
|
20
18
|
},
|
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
import { defineAgentTool } from '@zhin.js/agent/tools';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { getDingtalkAgentDeps } from '../../src/dingtalk-agent-deps.js';
|
|
4
3
|
import { platformPermit } from '../../src/platform-permit.js';
|
|
5
4
|
|
|
6
|
-
export default defineAgentTool<{
|
|
5
|
+
export default defineAgentTool<{ name: string; owner: string; members: string }>({
|
|
7
6
|
description: '创建钉钉群聊',
|
|
8
7
|
inputSchema: z.object({
|
|
9
|
-
endpoint_id: z.string().describe('Endpoint 名称'),
|
|
10
8
|
name: z.string().describe('群名'),
|
|
11
9
|
owner: z.string().describe('群主用户 ID'),
|
|
12
10
|
members: z.string().describe('成员用户 ID 列表,逗号分隔'),
|
|
13
11
|
}),
|
|
14
|
-
|
|
12
|
+
adapter: 'dingtalk',
|
|
15
13
|
tags: ['dingtalk'],
|
|
16
14
|
permissions: [platformPermit('chat_owner')],
|
|
17
|
-
async execute({
|
|
18
|
-
const endpoint =
|
|
15
|
+
async execute({ name, owner, members }: { name: string; owner: string; members: string }, context) {
|
|
16
|
+
const endpoint = context.$client;
|
|
19
17
|
const chatId = await endpoint.createChat(name, owner, members.split(','));
|
|
20
18
|
return { success: !!chatId, chat_id: chatId, message: chatId ? `群聊创建成功: ${chatId}` : '创建失败' };
|
|
21
19
|
},
|
package/agent/tools/dept_info.ts
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import { defineAgentTool } from '@zhin.js/agent/tools';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
|
|
4
|
-
export default defineAgentTool<{ endpoint_id: string; dept_id: string }>({
|
|
3
|
+
export default defineAgentTool<{ dept_id: string }>({
|
|
5
4
|
description: '获取钉钉部门详细信息',
|
|
6
5
|
inputSchema: z.object({
|
|
7
|
-
endpoint_id: z.string().describe('Endpoint 名称'),
|
|
8
6
|
dept_id: z.string().describe('部门 ID'),
|
|
9
7
|
}),
|
|
10
|
-
|
|
8
|
+
adapter: 'dingtalk',
|
|
11
9
|
tags: ['dingtalk'],
|
|
12
|
-
async execute({
|
|
13
|
-
const endpoint =
|
|
10
|
+
async execute({ dept_id }: { dept_id: string }, context) {
|
|
11
|
+
const endpoint = context.$client;
|
|
14
12
|
return await endpoint.getDepartmentInfo(Number(dept_id));
|
|
15
13
|
},
|
|
16
14
|
});
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import { defineAgentTool } from '@zhin.js/agent/tools';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
|
|
4
|
-
export default defineAgentTool<{ endpoint_id: string; dept_id: string }>({
|
|
3
|
+
export default defineAgentTool<{ dept_id: string }>({
|
|
5
4
|
description: '获取钉钉部门用户列表',
|
|
6
5
|
inputSchema: z.object({
|
|
7
|
-
endpoint_id: z.string().describe('Endpoint 名称'),
|
|
8
6
|
dept_id: z.string().describe('部门 ID'),
|
|
9
7
|
}),
|
|
10
|
-
|
|
8
|
+
adapter: 'dingtalk',
|
|
11
9
|
tags: ['dingtalk'],
|
|
12
|
-
async execute({
|
|
13
|
-
const endpoint =
|
|
10
|
+
async execute({ dept_id }: { dept_id: string }, context) {
|
|
11
|
+
const endpoint = context.$client;
|
|
14
12
|
const users = await endpoint.getDepartmentUsers(Number(dept_id));
|
|
15
13
|
return { users, count: users.length };
|
|
16
14
|
},
|
package/agent/tools/get_user.ts
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import { defineAgentTool } from '@zhin.js/agent/tools';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
|
|
4
|
-
export default defineAgentTool<{ endpoint_id: string; user_id: string }>({
|
|
3
|
+
export default defineAgentTool<{ user_id: string }>({
|
|
5
4
|
description: '获取钉钉用户信息',
|
|
6
5
|
inputSchema: z.object({
|
|
7
|
-
endpoint_id: z.string().describe('Endpoint 名称'),
|
|
8
6
|
user_id: z.string().describe('用户 ID'),
|
|
9
7
|
}),
|
|
10
|
-
|
|
8
|
+
adapter: 'dingtalk',
|
|
11
9
|
tags: ['dingtalk'],
|
|
12
|
-
async execute({
|
|
13
|
-
const endpoint =
|
|
10
|
+
async execute({ user_id }: { user_id: string }, context) {
|
|
11
|
+
const endpoint = context.$client;
|
|
14
12
|
return await endpoint.getUserInfo(user_id);
|
|
15
13
|
},
|
|
16
14
|
});
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import { defineAgentTool } from '@zhin.js/agent/tools';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
|
|
4
|
-
export default defineAgentTool<{ endpoint_id: string; dept_id?: string }>({
|
|
3
|
+
export default defineAgentTool<{ dept_id?: string }>({
|
|
5
4
|
description: '获取钉钉部门列表',
|
|
6
5
|
inputSchema: z.object({
|
|
7
|
-
endpoint_id: z.string().describe('Endpoint 名称'),
|
|
8
6
|
dept_id: z.string().optional().describe('父部门 ID,默认 1(根部门)'),
|
|
9
7
|
}),
|
|
10
|
-
|
|
8
|
+
adapter: 'dingtalk',
|
|
11
9
|
tags: ['dingtalk'],
|
|
12
|
-
async execute({
|
|
13
|
-
const endpoint =
|
|
10
|
+
async execute({ dept_id }: { dept_id?: string }, context) {
|
|
11
|
+
const endpoint = context.$client;
|
|
14
12
|
const departments = await endpoint.getDepartmentList(Number(dept_id || '1'));
|
|
15
13
|
return { departments, count: departments.length };
|
|
16
14
|
},
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import { defineAgentTool } from '@zhin.js/agent/tools';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
|
|
4
|
-
export default defineAgentTool<{ endpoint_id: string; user_ids: string; content: string }>({
|
|
3
|
+
export default defineAgentTool<{ user_ids: string; content: string }>({
|
|
5
4
|
description: '向指定用户发送钉钉工作通知',
|
|
6
5
|
inputSchema: z.object({
|
|
7
|
-
endpoint_id: z.string().describe('Endpoint 名称'),
|
|
8
6
|
user_ids: z.string().describe('用户 ID 列表,逗号分隔'),
|
|
9
7
|
content: z.string().describe('通知内容'),
|
|
10
8
|
}),
|
|
11
|
-
|
|
9
|
+
adapter: 'dingtalk',
|
|
12
10
|
tags: ['dingtalk'],
|
|
13
|
-
async execute({
|
|
14
|
-
const endpoint =
|
|
11
|
+
async execute({ user_ids, content }: { user_ids: string; content: string }, context) {
|
|
12
|
+
const endpoint = context.$client;
|
|
15
13
|
const msgContent = { msgtype: 'text', text: { content } };
|
|
16
14
|
const success = await endpoint.sendWorkNotice(user_ids.split(','), msgContent);
|
|
17
15
|
return { success, message: success ? '工作通知已发送' : '发送失败' };
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import { defineAgentTool } from '@zhin.js/agent/tools';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
|
|
4
|
-
export default defineAgentTool<{ endpoint_id: string; chat_id: string; name?: string; owner?: string; add_members?: string; remove_members?: string }>({
|
|
3
|
+
export default defineAgentTool<{ chat_id: string; name?: string; owner?: string; add_members?: string; remove_members?: string }>({
|
|
5
4
|
description: '更新钉钉群聊设置(改名、换群主、增减成员)',
|
|
6
5
|
inputSchema: z.object({
|
|
7
|
-
endpoint_id: z.string().describe('Endpoint 名称'),
|
|
8
6
|
chat_id: z.string().describe('群聊 ID'),
|
|
9
7
|
name: z.string().optional().describe('新群名(可选)'),
|
|
10
8
|
owner: z.string().optional().describe('新群主 userId(可选)'),
|
|
11
9
|
add_members: z.string().optional().describe('要添加的成员 userId,逗号分隔(可选)'),
|
|
12
10
|
remove_members: z.string().optional().describe('要移除的成员 userId,逗号分隔(可选)'),
|
|
13
11
|
}),
|
|
14
|
-
|
|
12
|
+
adapter: 'dingtalk',
|
|
15
13
|
tags: ['dingtalk'],
|
|
16
|
-
async execute({
|
|
17
|
-
const endpoint =
|
|
14
|
+
async execute({ chat_id, name, owner, add_members, remove_members }: { chat_id: string; name?: string; owner?: string; add_members?: string; remove_members?: string }, context) {
|
|
15
|
+
const endpoint = context.$client;
|
|
18
16
|
const options: Record<string, unknown> = {};
|
|
19
17
|
if (name) options.name = name;
|
|
20
18
|
if (owner) options.owner = owner;
|
package/lib/client.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { DingTalkClient } from './endpoint.js';
|
|
2
|
+
import type { DingTalkEvent } from './protocol.js';
|
|
3
|
+
export type DingtalkClientEventMap = Record<string, DingTalkEvent>;
|
|
4
|
+
declare module '@zhin.js/feature-kit' {
|
|
5
|
+
interface AdapterClientRegistry {
|
|
6
|
+
readonly dingtalk: {
|
|
7
|
+
readonly client: DingTalkClient;
|
|
8
|
+
readonly events: DingtalkClientEventMap;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export declare const dingtalkClient: import("@zhin.js/adapter").EndpointClientToken<DingTalkClient, DingtalkClientEventMap>;
|
package/lib/client.js
ADDED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const dingtalkEndpointCommands: import("@zhin.js/adapter").EndpointCommands<Readonly<import("@zhin.js/command").CommandDefinition<unknown, unknown, import("@zhin.js/command").CommandMessage>>>;
|
|
1
|
+
export declare const dingtalkEndpointCommands: import("@zhin.js/adapter").EndpointCommands<Readonly<import("@zhin.js/command").CommandDefinition<unknown, unknown, import("@zhin.js/command").CommandMessage, string | undefined>>>;
|
package/lib/endpoint.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { Endpoint } from 'zhin.js/adapter';
|
|
1
2
|
/**
|
|
2
3
|
* DingTalkEndpoint — lifecycle, outbound, admit, OpenAPI helpers for agent tools.
|
|
3
4
|
*/
|
|
4
|
-
import
|
|
5
|
-
import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
|
|
5
|
+
import { type EndpointSendRequest } from 'zhin.js/adapter';
|
|
6
6
|
import type { HttpHost } from '@zhin.js/host-http';
|
|
7
7
|
import type { CapabilityId } from 'zhin.js';
|
|
8
8
|
import { type DingTalkEvent, type DingTalkMessage, type ResolvedDingTalkConfig } from './protocol.js';
|
|
@@ -18,19 +18,46 @@ export type DingTalkFetch = (url: string, init?: {
|
|
|
18
18
|
}>;
|
|
19
19
|
export interface DingTalkEndpointOptions {
|
|
20
20
|
readonly id: CapabilityId;
|
|
21
|
-
readonly gateway: MessageGateway;
|
|
22
|
-
readonly sideEvents?: SideEventGateway;
|
|
23
21
|
readonly http: HttpHost;
|
|
24
22
|
readonly config: ResolvedDingTalkConfig;
|
|
25
23
|
readonly fetch?: DingTalkFetch;
|
|
26
24
|
}
|
|
25
|
+
export interface DingTalkClientApi {
|
|
26
|
+
getUserInfo(userId: string): Promise<unknown>;
|
|
27
|
+
getDepartmentUsers(deptId: number): Promise<unknown[]>;
|
|
28
|
+
sendWorkNotice(userIdList: string[], content: unknown): Promise<boolean>;
|
|
29
|
+
getDepartmentList(deptId?: number): Promise<unknown[]>;
|
|
30
|
+
getDepartmentInfo(deptId: number): Promise<unknown>;
|
|
31
|
+
createChat(name: string, ownerUserId: string, userIdList: string[]): Promise<string | null>;
|
|
32
|
+
getChatInfo(chatId: string): Promise<unknown>;
|
|
33
|
+
updateChat(chatId: string, options: {
|
|
34
|
+
name?: string;
|
|
35
|
+
owner?: string;
|
|
36
|
+
add_useridlist?: string[];
|
|
37
|
+
del_useridlist?: string[];
|
|
38
|
+
}): Promise<boolean>;
|
|
39
|
+
}
|
|
40
|
+
/** SDK-like DingTalk OpenAPI surface available on every Endpoint event. */
|
|
41
|
+
export declare class DingTalkClient implements DingTalkClientApi {
|
|
42
|
+
readonly api: DingTalkClientApi;
|
|
43
|
+
constructor(api: DingTalkClientApi);
|
|
44
|
+
getUserInfo: (userId: string) => Promise<unknown>;
|
|
45
|
+
getDepartmentUsers: (deptId: number) => Promise<unknown[]>;
|
|
46
|
+
sendWorkNotice: (userIds: string[], content: unknown) => Promise<boolean>;
|
|
47
|
+
getDepartmentList: (deptId?: number) => Promise<unknown[]>;
|
|
48
|
+
getDepartmentInfo: (deptId: number) => Promise<unknown>;
|
|
49
|
+
createChat: (name: string, owner: string, users: string[]) => Promise<string | null>;
|
|
50
|
+
getChatInfo: (chatId: string) => Promise<unknown>;
|
|
51
|
+
updateChat: (chatId: string, options: Parameters<DingTalkClientApi["updateChat"]>[1]) => Promise<boolean>;
|
|
52
|
+
}
|
|
27
53
|
/**
|
|
28
54
|
* 钉钉机器人(webhook/stream 模式)无常规群列表 API——机器人不持有
|
|
29
55
|
* 「我所在的群」枚举面,仅能收发消息;
|
|
30
56
|
* 因此本 endpoint 不暴露 EndpointManagement(Console 社交面 RPC 对该平台保持未接线)。
|
|
31
57
|
*/
|
|
32
|
-
export declare class DingTalkEndpoint
|
|
58
|
+
export declare class DingTalkEndpoint extends Endpoint<DingTalkClient> {
|
|
33
59
|
#private;
|
|
60
|
+
readonly client: DingTalkClient;
|
|
34
61
|
constructor(options: DingTalkEndpointOptions);
|
|
35
62
|
/** Used by webhook handler. */
|
|
36
63
|
get isOpen(): boolean;
|
|
@@ -42,17 +69,4 @@ export declare class DingTalkEndpoint implements EndpointInstance {
|
|
|
42
69
|
send({ conversation, payload }: EndpointSendRequest): Promise<string>;
|
|
43
70
|
/** Test / internal: admit a parsed event when open (non-webhook path). */
|
|
44
71
|
admit(event: DingTalkEvent | DingTalkMessage): void;
|
|
45
|
-
getUserInfo(userId: string): Promise<unknown>;
|
|
46
|
-
getDepartmentUsers(deptId: number): Promise<unknown[]>;
|
|
47
|
-
sendWorkNotice(userIdList: string[], content: unknown): Promise<boolean>;
|
|
48
|
-
getDepartmentList(deptId?: number): Promise<unknown[]>;
|
|
49
|
-
getDepartmentInfo(deptId: number): Promise<unknown>;
|
|
50
|
-
createChat(name: string, ownerUserId: string, userIdList: string[]): Promise<string | null>;
|
|
51
|
-
getChatInfo(chatId: string): Promise<unknown>;
|
|
52
|
-
updateChat(chatId: string, options: {
|
|
53
|
-
name?: string;
|
|
54
|
-
owner?: string;
|
|
55
|
-
add_useridlist?: string[];
|
|
56
|
-
del_useridlist?: string[];
|
|
57
|
-
}): Promise<boolean>;
|
|
58
72
|
}
|
package/lib/endpoint.js
CHANGED
|
@@ -1,14 +1,39 @@
|
|
|
1
|
+
import { Endpoint } from 'zhin.js/adapter';
|
|
1
2
|
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
2
|
-
import { registerDingtalkAgentEndpoint } from './dingtalk-agent-deps.js';
|
|
3
3
|
import { normalizeDingtalkSenderForPermit } from './platform-permit.js';
|
|
4
4
|
import { dingtalkInboundConversation, formatInboundContent, formatOutboundBody, generateMessageId, isDingtalkBotMentioned, resolveChatType, resolveSender, } from './protocol.js';
|
|
5
5
|
import { registerDingTalkWebhookRoutes } from './webhook.js';
|
|
6
|
+
/** SDK-like DingTalk OpenAPI surface available on every Endpoint event. */
|
|
7
|
+
export class DingTalkClient {
|
|
8
|
+
api;
|
|
9
|
+
constructor(api) {
|
|
10
|
+
this.api = api;
|
|
11
|
+
}
|
|
12
|
+
getUserInfo = (userId) => this.api.getUserInfo(userId);
|
|
13
|
+
getDepartmentUsers = (deptId) => this.api.getDepartmentUsers(deptId);
|
|
14
|
+
sendWorkNotice = (userIds, content) => this.api.sendWorkNotice(userIds, content);
|
|
15
|
+
getDepartmentList = (deptId) => this.api.getDepartmentList(deptId);
|
|
16
|
+
getDepartmentInfo = (deptId) => this.api.getDepartmentInfo(deptId);
|
|
17
|
+
createChat = (name, owner, users) => this.api.createChat(name, owner, users);
|
|
18
|
+
getChatInfo = (chatId) => this.api.getChatInfo(chatId);
|
|
19
|
+
updateChat = (chatId, options) => this.api.updateChat(chatId, options);
|
|
20
|
+
}
|
|
6
21
|
/**
|
|
7
22
|
* 钉钉机器人(webhook/stream 模式)无常规群列表 API——机器人不持有
|
|
8
23
|
* 「我所在的群」枚举面,仅能收发消息;
|
|
9
24
|
* 因此本 endpoint 不暴露 EndpointManagement(Console 社交面 RPC 对该平台保持未接线)。
|
|
10
25
|
*/
|
|
11
|
-
export class DingTalkEndpoint {
|
|
26
|
+
export class DingTalkEndpoint extends Endpoint {
|
|
27
|
+
client = new DingTalkClient({
|
|
28
|
+
getUserInfo: (userId) => this.#getUserInfo(userId),
|
|
29
|
+
getDepartmentUsers: (deptId) => this.#getDepartmentUsers(deptId),
|
|
30
|
+
sendWorkNotice: (userIds, content) => this.#sendWorkNotice(userIds, content),
|
|
31
|
+
getDepartmentList: (deptId) => this.#getDepartmentList(deptId),
|
|
32
|
+
getDepartmentInfo: (deptId) => this.#getDepartmentInfo(deptId),
|
|
33
|
+
createChat: (name, owner, users) => this.#createChat(name, owner, users),
|
|
34
|
+
getChatInfo: (chatId) => this.#getChatInfo(chatId),
|
|
35
|
+
updateChat: (chatId, options) => this.#updateChat(chatId, options),
|
|
36
|
+
});
|
|
12
37
|
#logger;
|
|
13
38
|
#options;
|
|
14
39
|
#fetch;
|
|
@@ -18,8 +43,8 @@ export class DingTalkEndpoint {
|
|
|
18
43
|
#sessionWebhooks = new Map();
|
|
19
44
|
#open = false;
|
|
20
45
|
#started = false;
|
|
21
|
-
#unregisterAgent;
|
|
22
46
|
constructor(options) {
|
|
47
|
+
super();
|
|
23
48
|
this.#logger = getAdapterLogger('dingtalk', options.config.id);
|
|
24
49
|
this.#options = options;
|
|
25
50
|
this.#fetch = options.fetch ?? globalThis.fetch;
|
|
@@ -37,7 +62,6 @@ export class DingTalkEndpoint {
|
|
|
37
62
|
this.#started = true;
|
|
38
63
|
try {
|
|
39
64
|
await this.#refreshAccessToken();
|
|
40
|
-
this.#unregisterAgent = registerDingtalkAgentEndpoint(this.#options.config.id, this);
|
|
41
65
|
this.#routeReleases.push(...registerDingTalkWebhookRoutes(this.#options.http, this));
|
|
42
66
|
this.#logger.debug(formatCompact({
|
|
43
67
|
endpoint: this.#options.config.id,
|
|
@@ -62,8 +86,6 @@ export class DingTalkEndpoint {
|
|
|
62
86
|
this.#sessionWebhooks.clear();
|
|
63
87
|
for (const release of this.#routeReleases.splice(0))
|
|
64
88
|
release();
|
|
65
|
-
this.#unregisterAgent?.();
|
|
66
|
-
this.#unregisterAgent = undefined;
|
|
67
89
|
this.#started = false;
|
|
68
90
|
this.#logger.debug(formatCompact({ op: 'disconnect' }));
|
|
69
91
|
}
|
|
@@ -108,13 +130,14 @@ export class DingTalkEndpoint {
|
|
|
108
130
|
admit(event) {
|
|
109
131
|
if (!this.#open)
|
|
110
132
|
return;
|
|
133
|
+
this.#emitPlatformEvent(event.msgtype || 'event', event);
|
|
111
134
|
if (event.sessionWebhook && event.conversationId) {
|
|
112
135
|
this.#sessionWebhooks.set(event.conversationId, event.sessionWebhook);
|
|
113
136
|
}
|
|
114
137
|
const conversation = dingtalkInboundConversation(String(this.#options.id), event);
|
|
115
138
|
const chatType = resolveChatType(event.conversationType);
|
|
116
139
|
const permit = normalizeDingtalkSenderForPermit({ isAdmin: event.isAdmin === true });
|
|
117
|
-
void this
|
|
140
|
+
void this.emit('message.receive', {
|
|
118
141
|
conversation,
|
|
119
142
|
message: { conversation, id: generateMessageId(event) },
|
|
120
143
|
content: formatInboundContent(event),
|
|
@@ -141,7 +164,16 @@ export class DingTalkEndpoint {
|
|
|
141
164
|
}));
|
|
142
165
|
});
|
|
143
166
|
}
|
|
144
|
-
|
|
167
|
+
#emitPlatformEvent(name, event) {
|
|
168
|
+
void this.emitPlatform(name, event).catch((error) => {
|
|
169
|
+
this.#logger.warn(formatCompact({
|
|
170
|
+
op: 'dingtalk_platform_event_failed',
|
|
171
|
+
event: name,
|
|
172
|
+
error: error instanceof Error ? error.message : String(error),
|
|
173
|
+
}));
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
async #getUserInfo(userId) {
|
|
145
177
|
try {
|
|
146
178
|
const data = await this.#request('/topapi/v2/user/get', {
|
|
147
179
|
method: 'POST',
|
|
@@ -156,7 +188,7 @@ export class DingTalkEndpoint {
|
|
|
156
188
|
return null;
|
|
157
189
|
}
|
|
158
190
|
}
|
|
159
|
-
async getDepartmentUsers(deptId) {
|
|
191
|
+
async #getDepartmentUsers(deptId) {
|
|
160
192
|
try {
|
|
161
193
|
const data = await this.#request('/topapi/user/listid', {
|
|
162
194
|
method: 'POST',
|
|
@@ -173,7 +205,7 @@ export class DingTalkEndpoint {
|
|
|
173
205
|
return [];
|
|
174
206
|
}
|
|
175
207
|
}
|
|
176
|
-
async sendWorkNotice(userIdList, content) {
|
|
208
|
+
async #sendWorkNotice(userIdList, content) {
|
|
177
209
|
try {
|
|
178
210
|
const data = await this.#request('/topapi/message/corpconversation/asyncsend_v2', {
|
|
179
211
|
method: 'POST',
|
|
@@ -192,7 +224,7 @@ export class DingTalkEndpoint {
|
|
|
192
224
|
return false;
|
|
193
225
|
}
|
|
194
226
|
}
|
|
195
|
-
async getDepartmentList(deptId = 1) {
|
|
227
|
+
async #getDepartmentList(deptId = 1) {
|
|
196
228
|
try {
|
|
197
229
|
const data = await this.#request('/topapi/v2/department/listsub', {
|
|
198
230
|
method: 'POST',
|
|
@@ -207,7 +239,7 @@ export class DingTalkEndpoint {
|
|
|
207
239
|
return [];
|
|
208
240
|
}
|
|
209
241
|
}
|
|
210
|
-
async getDepartmentInfo(deptId) {
|
|
242
|
+
async #getDepartmentInfo(deptId) {
|
|
211
243
|
try {
|
|
212
244
|
const data = await this.#request('/topapi/v2/department/get', {
|
|
213
245
|
method: 'POST',
|
|
@@ -222,7 +254,7 @@ export class DingTalkEndpoint {
|
|
|
222
254
|
return null;
|
|
223
255
|
}
|
|
224
256
|
}
|
|
225
|
-
async createChat(name, ownerUserId, userIdList) {
|
|
257
|
+
async #createChat(name, ownerUserId, userIdList) {
|
|
226
258
|
try {
|
|
227
259
|
const data = await this.#request('/topapi/chat/create', {
|
|
228
260
|
method: 'POST',
|
|
@@ -237,7 +269,7 @@ export class DingTalkEndpoint {
|
|
|
237
269
|
return null;
|
|
238
270
|
}
|
|
239
271
|
}
|
|
240
|
-
async getChatInfo(chatId) {
|
|
272
|
+
async #getChatInfo(chatId) {
|
|
241
273
|
try {
|
|
242
274
|
const data = await this.#request('/topapi/chat/get', {
|
|
243
275
|
method: 'POST',
|
|
@@ -252,7 +284,7 @@ export class DingTalkEndpoint {
|
|
|
252
284
|
return null;
|
|
253
285
|
}
|
|
254
286
|
}
|
|
255
|
-
async updateChat(chatId, options) {
|
|
287
|
+
async #updateChat(chatId, options) {
|
|
256
288
|
try {
|
|
257
289
|
const data = await this.#request('/topapi/chat/update', {
|
|
258
290
|
method: 'POST',
|
package/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { dingtalkInboundConversation, formatInboundContent, formatOutboundBody, generateMessageId, headerValue, normalizeWebhookPath, readTextBody, resolveChatType, resolveDingTalkConfig, resolveSender, verifySignature, type AccessToken, type DingTalkAdapterConfig, type DingTalkApiResponse, type DingTalkEvent, type DingTalkMessage, type DingTalkSendBody, type DingTalkWireSegment, type ResolvedDingTalkConfig, } from './protocol.js';
|
|
2
|
-
export { DingTalkEndpoint, type DingTalkEndpointOptions, type DingTalkFetch, } from './endpoint.js';
|
|
2
|
+
export { DingTalkClient, DingTalkEndpoint, type DingTalkClientApi, type DingTalkEndpointOptions, type DingTalkFetch, } from './endpoint.js';
|
|
3
3
|
export { registerDingTalkWebhookRoutes, handleDingTalkWebhookRequest, type DingTalkWebhookHandler, } from './webhook.js';
|
|
4
|
-
export {
|
|
4
|
+
export { dingtalkClient, type DingtalkClientEventMap } from './client.js';
|
|
5
5
|
export { checkDingtalkPlatformPermit, dingtalkGroupPermitResolver, normalizeDingtalkSenderForPermit, platformPermit, } from './platform-permit.js';
|
package/lib/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { dingtalkInboundConversation, formatInboundContent, formatOutboundBody, generateMessageId, headerValue, normalizeWebhookPath, readTextBody, resolveChatType, resolveDingTalkConfig, resolveSender, verifySignature, } from './protocol.js';
|
|
2
|
-
export { DingTalkEndpoint, } from './endpoint.js';
|
|
2
|
+
export { DingTalkClient, DingTalkEndpoint, } from './endpoint.js';
|
|
3
3
|
export { registerDingTalkWebhookRoutes, handleDingTalkWebhookRequest, } from './webhook.js';
|
|
4
|
-
export {
|
|
4
|
+
export { dingtalkClient } from './client.js';
|
|
5
5
|
export { checkDingtalkPlatformPermit, dingtalkGroupPermitResolver, normalizeDingtalkSenderForPermit, platformPermit, } from './platform-permit.js';
|
package/lib/protocol.d.ts
CHANGED
|
@@ -113,7 +113,7 @@ export declare function resolveChatType(conversationType?: string): 'group' | 'p
|
|
|
113
113
|
export declare function dingtalkInboundConversation(endpointKey: string, msg: DingTalkMessage): ConversationRef;
|
|
114
114
|
export declare function resolveSender(msg: DingTalkMessage): string;
|
|
115
115
|
export declare function generateMessageId(msg: DingTalkMessage): string;
|
|
116
|
-
/** Build inbound text for
|
|
116
|
+
/** Build inbound text for OutboundMessageService.receive. */
|
|
117
117
|
export declare function formatInboundContent(msg: DingTalkMessage): string;
|
|
118
118
|
/** DingTalk timestamps are epoch milliseconds; reject replays outside ±1 hour. */
|
|
119
119
|
export declare const MAX_TIMESTAMP_DRIFT_MS: number;
|
package/lib/protocol.js
CHANGED
|
@@ -71,7 +71,7 @@ export function resolveSender(msg) {
|
|
|
71
71
|
export function generateMessageId(msg) {
|
|
72
72
|
return msg.msgId || `${msg.createAt ?? Date.now()}`;
|
|
73
73
|
}
|
|
74
|
-
/** Build inbound text for
|
|
74
|
+
/** Build inbound text for OutboundMessageService.receive. */
|
|
75
75
|
export function formatInboundContent(msg) {
|
|
76
76
|
if (!msg.msgtype)
|
|
77
77
|
return '';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-dingtalk",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.1",
|
|
4
4
|
"description": "Zhin.js DingTalk (钉钉) adapter for Plugin Runtime (HTTP webhook)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -33,21 +33,22 @@
|
|
|
33
33
|
"directory": "plugins/adapters/dingtalk"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@zhin.js/adapter": "1.2.
|
|
37
|
-
"@zhin.js/core": "1.5.
|
|
38
|
-
"@zhin.js/
|
|
36
|
+
"@zhin.js/adapter": "1.2.1",
|
|
37
|
+
"@zhin.js/core": "1.5.14",
|
|
38
|
+
"@zhin.js/feature-kit": "1.0.13",
|
|
39
|
+
"@zhin.js/host-http": "1.0.13",
|
|
39
40
|
"@zhin.js/im-contract": "1.0.4",
|
|
40
|
-
"@zhin.js/logger": "1.0.
|
|
41
|
+
"@zhin.js/logger": "1.0.77"
|
|
41
42
|
},
|
|
42
43
|
"peerDependencies": {
|
|
43
44
|
"zod": "^4.0.0",
|
|
44
|
-
"@zhin.js/adapter": "1.2.
|
|
45
|
-
"@zhin.js/agent": "1.1.
|
|
46
|
-
"@zhin.js/command": "1.0.
|
|
47
|
-
"@zhin.js/core": "1.5.
|
|
48
|
-
"@zhin.js/host-http": "1.0.
|
|
49
|
-
"@zhin.js/permission": "1.0.
|
|
50
|
-
"zhin.js": "6.0.
|
|
45
|
+
"@zhin.js/adapter": "1.2.1",
|
|
46
|
+
"@zhin.js/agent": "1.1.16",
|
|
47
|
+
"@zhin.js/command": "1.0.16",
|
|
48
|
+
"@zhin.js/core": "1.5.14",
|
|
49
|
+
"@zhin.js/host-http": "1.0.13",
|
|
50
|
+
"@zhin.js/permission": "1.0.4",
|
|
51
|
+
"zhin.js": "6.0.14"
|
|
51
52
|
},
|
|
52
53
|
"peerDependenciesMeta": {
|
|
53
54
|
"@zhin.js/agent": {
|
|
@@ -68,8 +69,8 @@
|
|
|
68
69
|
"typescript": "^6.0.3",
|
|
69
70
|
"vitest": "^4.1.10",
|
|
70
71
|
"zod": "^4.4.3",
|
|
71
|
-
"@zhin.js/agent": "1.1.
|
|
72
|
-
"zhin.js": "6.0.
|
|
72
|
+
"@zhin.js/agent": "1.1.16",
|
|
73
|
+
"zhin.js": "6.0.14"
|
|
73
74
|
},
|
|
74
75
|
"files": [
|
|
75
76
|
"adapters",
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineEndpointClient } from 'zhin.js/adapter';
|
|
2
|
+
import type { DingTalkClient } from './endpoint.js';
|
|
3
|
+
import type { DingTalkEvent } from './protocol.js';
|
|
4
|
+
|
|
5
|
+
export type DingtalkClientEventMap = Record<string, DingTalkEvent>;
|
|
6
|
+
|
|
7
|
+
declare module '@zhin.js/feature-kit' {
|
|
8
|
+
interface AdapterClientRegistry {
|
|
9
|
+
readonly dingtalk: {
|
|
10
|
+
readonly client: DingTalkClient;
|
|
11
|
+
readonly events: DingtalkClientEventMap;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const dingtalkClient = defineEndpointClient<DingTalkClient, DingtalkClientEventMap>('dingtalk');
|
package/src/endpoint.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
+
import { Endpoint } from 'zhin.js/adapter';
|
|
1
2
|
/**
|
|
2
3
|
* DingTalkEndpoint — lifecycle, outbound, admit, OpenAPI helpers for agent tools.
|
|
3
4
|
*/
|
|
4
|
-
import
|
|
5
|
-
import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
|
|
5
|
+
import { type EndpointSendRequest } from 'zhin.js/adapter';
|
|
6
6
|
import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
|
|
7
7
|
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
8
8
|
import type { CapabilityId } from 'zhin.js';
|
|
9
|
-
import { registerDingtalkAgentEndpoint } from './dingtalk-agent-deps.js';
|
|
10
9
|
import { normalizeDingtalkSenderForPermit } from './platform-permit.js';
|
|
11
10
|
import {
|
|
12
11
|
dingtalkInboundConversation,
|
|
@@ -41,19 +40,59 @@ export type DingTalkFetch = (
|
|
|
41
40
|
|
|
42
41
|
export interface DingTalkEndpointOptions {
|
|
43
42
|
readonly id: CapabilityId;
|
|
44
|
-
readonly gateway: MessageGateway;
|
|
45
|
-
readonly sideEvents?: SideEventGateway;
|
|
46
43
|
readonly http: HttpHost;
|
|
47
44
|
readonly config: ResolvedDingTalkConfig;
|
|
48
45
|
readonly fetch?: DingTalkFetch;
|
|
49
46
|
}
|
|
50
47
|
|
|
48
|
+
export interface DingTalkClientApi {
|
|
49
|
+
getUserInfo(userId: string): Promise<unknown>;
|
|
50
|
+
getDepartmentUsers(deptId: number): Promise<unknown[]>;
|
|
51
|
+
sendWorkNotice(userIdList: string[], content: unknown): Promise<boolean>;
|
|
52
|
+
getDepartmentList(deptId?: number): Promise<unknown[]>;
|
|
53
|
+
getDepartmentInfo(deptId: number): Promise<unknown>;
|
|
54
|
+
createChat(name: string, ownerUserId: string, userIdList: string[]): Promise<string | null>;
|
|
55
|
+
getChatInfo(chatId: string): Promise<unknown>;
|
|
56
|
+
updateChat(chatId: string, options: {
|
|
57
|
+
name?: string;
|
|
58
|
+
owner?: string;
|
|
59
|
+
add_useridlist?: string[];
|
|
60
|
+
del_useridlist?: string[];
|
|
61
|
+
}): Promise<boolean>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** SDK-like DingTalk OpenAPI surface available on every Endpoint event. */
|
|
65
|
+
export class DingTalkClient implements DingTalkClientApi {
|
|
66
|
+
constructor(readonly api: DingTalkClientApi) {}
|
|
67
|
+
getUserInfo = (userId: string) => this.api.getUserInfo(userId);
|
|
68
|
+
getDepartmentUsers = (deptId: number) => this.api.getDepartmentUsers(deptId);
|
|
69
|
+
sendWorkNotice = (userIds: string[], content: unknown) =>
|
|
70
|
+
this.api.sendWorkNotice(userIds, content);
|
|
71
|
+
getDepartmentList = (deptId?: number) => this.api.getDepartmentList(deptId);
|
|
72
|
+
getDepartmentInfo = (deptId: number) => this.api.getDepartmentInfo(deptId);
|
|
73
|
+
createChat = (name: string, owner: string, users: string[]) =>
|
|
74
|
+
this.api.createChat(name, owner, users);
|
|
75
|
+
getChatInfo = (chatId: string) => this.api.getChatInfo(chatId);
|
|
76
|
+
updateChat = (chatId: string, options: Parameters<DingTalkClientApi['updateChat']>[1]) =>
|
|
77
|
+
this.api.updateChat(chatId, options);
|
|
78
|
+
}
|
|
79
|
+
|
|
51
80
|
/**
|
|
52
81
|
* 钉钉机器人(webhook/stream 模式)无常规群列表 API——机器人不持有
|
|
53
82
|
* 「我所在的群」枚举面,仅能收发消息;
|
|
54
83
|
* 因此本 endpoint 不暴露 EndpointManagement(Console 社交面 RPC 对该平台保持未接线)。
|
|
55
84
|
*/
|
|
56
|
-
export class DingTalkEndpoint
|
|
85
|
+
export class DingTalkEndpoint extends Endpoint<DingTalkClient> {
|
|
86
|
+
readonly client = new DingTalkClient({
|
|
87
|
+
getUserInfo: (userId) => this.#getUserInfo(userId),
|
|
88
|
+
getDepartmentUsers: (deptId) => this.#getDepartmentUsers(deptId),
|
|
89
|
+
sendWorkNotice: (userIds, content) => this.#sendWorkNotice(userIds, content),
|
|
90
|
+
getDepartmentList: (deptId) => this.#getDepartmentList(deptId),
|
|
91
|
+
getDepartmentInfo: (deptId) => this.#getDepartmentInfo(deptId),
|
|
92
|
+
createChat: (name, owner, users) => this.#createChat(name, owner, users),
|
|
93
|
+
getChatInfo: (chatId) => this.#getChatInfo(chatId),
|
|
94
|
+
updateChat: (chatId, options) => this.#updateChat(chatId, options),
|
|
95
|
+
});
|
|
57
96
|
readonly #logger!: ReturnType<typeof getAdapterLogger>;
|
|
58
97
|
|
|
59
98
|
readonly #options: DingTalkEndpointOptions;
|
|
@@ -64,9 +103,9 @@ export class DingTalkEndpoint implements EndpointInstance {
|
|
|
64
103
|
#sessionWebhooks = new Map<string, string>();
|
|
65
104
|
#open = false;
|
|
66
105
|
#started = false;
|
|
67
|
-
#unregisterAgent?: () => void;
|
|
68
106
|
|
|
69
107
|
constructor(options: DingTalkEndpointOptions) {
|
|
108
|
+
super();
|
|
70
109
|
this.#logger = getAdapterLogger('dingtalk', options.config.id);
|
|
71
110
|
this.#options = options;
|
|
72
111
|
this.#fetch = options.fetch ?? globalThis.fetch;
|
|
@@ -86,7 +125,6 @@ export class DingTalkEndpoint implements EndpointInstance {
|
|
|
86
125
|
this.#started = true;
|
|
87
126
|
try {
|
|
88
127
|
await this.#refreshAccessToken();
|
|
89
|
-
this.#unregisterAgent = registerDingtalkAgentEndpoint(this.#options.config.id, this);
|
|
90
128
|
this.#routeReleases.push(...registerDingTalkWebhookRoutes(this.#options.http, this));
|
|
91
129
|
this.#logger.debug(formatCompact({
|
|
92
130
|
endpoint: this.#options.config.id,
|
|
@@ -112,8 +150,6 @@ export class DingTalkEndpoint implements EndpointInstance {
|
|
|
112
150
|
this.#open = false;
|
|
113
151
|
this.#sessionWebhooks.clear();
|
|
114
152
|
for (const release of this.#routeReleases.splice(0)) release();
|
|
115
|
-
this.#unregisterAgent?.();
|
|
116
|
-
this.#unregisterAgent = undefined;
|
|
117
153
|
this.#started = false;
|
|
118
154
|
this.#logger.debug(formatCompact({ op: 'disconnect' }));
|
|
119
155
|
}
|
|
@@ -160,13 +196,14 @@ export class DingTalkEndpoint implements EndpointInstance {
|
|
|
160
196
|
/** Test / internal: admit a parsed event when open (non-webhook path). */
|
|
161
197
|
admit(event: DingTalkEvent | DingTalkMessage): void {
|
|
162
198
|
if (!this.#open) return;
|
|
199
|
+
this.#emitPlatformEvent(event.msgtype || 'event', event);
|
|
163
200
|
if (event.sessionWebhook && event.conversationId) {
|
|
164
201
|
this.#sessionWebhooks.set(event.conversationId, event.sessionWebhook);
|
|
165
202
|
}
|
|
166
203
|
const conversation = dingtalkInboundConversation(String(this.#options.id), event);
|
|
167
204
|
const chatType = resolveChatType(event.conversationType);
|
|
168
205
|
const permit = normalizeDingtalkSenderForPermit({ isAdmin: event.isAdmin === true });
|
|
169
|
-
void this
|
|
206
|
+
void this.emit('message.receive', {
|
|
170
207
|
conversation,
|
|
171
208
|
message: { conversation, id: generateMessageId(event) },
|
|
172
209
|
content: formatInboundContent(event),
|
|
@@ -194,7 +231,17 @@ export class DingTalkEndpoint implements EndpointInstance {
|
|
|
194
231
|
});
|
|
195
232
|
}
|
|
196
233
|
|
|
197
|
-
|
|
234
|
+
#emitPlatformEvent(name: string, event: unknown): void {
|
|
235
|
+
void this.emitPlatform(name, event).catch((error) => {
|
|
236
|
+
this.#logger.warn(formatCompact({
|
|
237
|
+
op: 'dingtalk_platform_event_failed',
|
|
238
|
+
event: name,
|
|
239
|
+
error: error instanceof Error ? error.message : String(error),
|
|
240
|
+
}));
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async #getUserInfo(userId: string): Promise<unknown> {
|
|
198
245
|
try {
|
|
199
246
|
const data = await this.#request('/topapi/v2/user/get', {
|
|
200
247
|
method: 'POST',
|
|
@@ -208,7 +255,7 @@ export class DingTalkEndpoint implements EndpointInstance {
|
|
|
208
255
|
}
|
|
209
256
|
}
|
|
210
257
|
|
|
211
|
-
async getDepartmentUsers(deptId: number): Promise<unknown[]> {
|
|
258
|
+
async #getDepartmentUsers(deptId: number): Promise<unknown[]> {
|
|
212
259
|
try {
|
|
213
260
|
const data = await this.#request('/topapi/user/listid', {
|
|
214
261
|
method: 'POST',
|
|
@@ -225,7 +272,7 @@ export class DingTalkEndpoint implements EndpointInstance {
|
|
|
225
272
|
}
|
|
226
273
|
}
|
|
227
274
|
|
|
228
|
-
async sendWorkNotice(userIdList: string[], content: unknown): Promise<boolean> {
|
|
275
|
+
async #sendWorkNotice(userIdList: string[], content: unknown): Promise<boolean> {
|
|
229
276
|
try {
|
|
230
277
|
const data = await this.#request('/topapi/message/corpconversation/asyncsend_v2', {
|
|
231
278
|
method: 'POST',
|
|
@@ -243,7 +290,7 @@ export class DingTalkEndpoint implements EndpointInstance {
|
|
|
243
290
|
}
|
|
244
291
|
}
|
|
245
292
|
|
|
246
|
-
async getDepartmentList(deptId: number = 1): Promise<unknown[]> {
|
|
293
|
+
async #getDepartmentList(deptId: number = 1): Promise<unknown[]> {
|
|
247
294
|
try {
|
|
248
295
|
const data = await this.#request('/topapi/v2/department/listsub', {
|
|
249
296
|
method: 'POST',
|
|
@@ -257,7 +304,7 @@ export class DingTalkEndpoint implements EndpointInstance {
|
|
|
257
304
|
}
|
|
258
305
|
}
|
|
259
306
|
|
|
260
|
-
async getDepartmentInfo(deptId: number): Promise<unknown> {
|
|
307
|
+
async #getDepartmentInfo(deptId: number): Promise<unknown> {
|
|
261
308
|
try {
|
|
262
309
|
const data = await this.#request('/topapi/v2/department/get', {
|
|
263
310
|
method: 'POST',
|
|
@@ -271,7 +318,7 @@ export class DingTalkEndpoint implements EndpointInstance {
|
|
|
271
318
|
}
|
|
272
319
|
}
|
|
273
320
|
|
|
274
|
-
async createChat(
|
|
321
|
+
async #createChat(
|
|
275
322
|
name: string,
|
|
276
323
|
ownerUserId: string,
|
|
277
324
|
userIdList: string[],
|
|
@@ -289,7 +336,7 @@ export class DingTalkEndpoint implements EndpointInstance {
|
|
|
289
336
|
}
|
|
290
337
|
}
|
|
291
338
|
|
|
292
|
-
async getChatInfo(chatId: string): Promise<unknown> {
|
|
339
|
+
async #getChatInfo(chatId: string): Promise<unknown> {
|
|
293
340
|
try {
|
|
294
341
|
const data = await this.#request('/topapi/chat/get', {
|
|
295
342
|
method: 'POST',
|
|
@@ -303,7 +350,7 @@ export class DingTalkEndpoint implements EndpointInstance {
|
|
|
303
350
|
}
|
|
304
351
|
}
|
|
305
352
|
|
|
306
|
-
async updateChat(
|
|
353
|
+
async #updateChat(
|
|
307
354
|
chatId: string,
|
|
308
355
|
options: {
|
|
309
356
|
name?: string;
|
package/src/index.ts
CHANGED
|
@@ -21,7 +21,9 @@ export {
|
|
|
21
21
|
} from './protocol.js';
|
|
22
22
|
|
|
23
23
|
export {
|
|
24
|
+
DingTalkClient,
|
|
24
25
|
DingTalkEndpoint,
|
|
26
|
+
type DingTalkClientApi,
|
|
25
27
|
type DingTalkEndpointOptions,
|
|
26
28
|
type DingTalkFetch,
|
|
27
29
|
} from './endpoint.js';
|
|
@@ -32,13 +34,7 @@ export {
|
|
|
32
34
|
type DingTalkWebhookHandler,
|
|
33
35
|
} from './webhook.js';
|
|
34
36
|
|
|
35
|
-
export {
|
|
36
|
-
getDingtalkAgentDeps,
|
|
37
|
-
registerDingtalkAgentEndpoint,
|
|
38
|
-
setDingtalkAgentDeps,
|
|
39
|
-
type DingtalkAgentDeps,
|
|
40
|
-
type DingtalkAgentEndpoint,
|
|
41
|
-
} from './dingtalk-agent-deps.js';
|
|
37
|
+
export { dingtalkClient, type DingtalkClientEventMap } from './client.js';
|
|
42
38
|
|
|
43
39
|
export {
|
|
44
40
|
checkDingtalkPlatformPermit,
|
package/src/protocol.ts
CHANGED
|
@@ -176,7 +176,7 @@ export function generateMessageId(msg: DingTalkMessage): string {
|
|
|
176
176
|
return msg.msgId || `${msg.createAt ?? Date.now()}`;
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
-
/** Build inbound text for
|
|
179
|
+
/** Build inbound text for OutboundMessageService.receive. */
|
|
180
180
|
export function formatInboundContent(msg: DingTalkMessage): string {
|
|
181
181
|
if (!msg.msgtype) return '';
|
|
182
182
|
switch (msg.msgtype) {
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent tool deps for dingtalk (user / dept / chat / work notice).
|
|
3
|
-
* Endpoints register themselves on start; tools look up by endpoint id.
|
|
4
|
-
*/
|
|
5
|
-
export interface DingtalkAgentEndpoint {
|
|
6
|
-
getUserInfo(userId: string): Promise<unknown>;
|
|
7
|
-
getDepartmentUsers(deptId: number): Promise<unknown[]>;
|
|
8
|
-
sendWorkNotice(userIdList: string[], content: unknown): Promise<boolean>;
|
|
9
|
-
getDepartmentList(deptId?: number): Promise<unknown[]>;
|
|
10
|
-
getDepartmentInfo(deptId: number): Promise<unknown>;
|
|
11
|
-
createChat(name: string, ownerUserId: string, userIdList: string[]): Promise<string | null>;
|
|
12
|
-
getChatInfo(chatId: string): Promise<unknown>;
|
|
13
|
-
updateChat(chatId: string, options: {
|
|
14
|
-
name?: string;
|
|
15
|
-
owner?: string;
|
|
16
|
-
add_useridlist?: string[];
|
|
17
|
-
del_useridlist?: string[];
|
|
18
|
-
}): Promise<boolean>;
|
|
19
|
-
}
|
|
20
|
-
export interface DingtalkAgentDeps {
|
|
21
|
-
getEndpoint: (endpointKey: string) => DingtalkAgentEndpoint;
|
|
22
|
-
}
|
|
23
|
-
export declare function registerDingtalkAgentEndpoint(endpointKey: string, endpoint: DingtalkAgentEndpoint): () => void;
|
|
24
|
-
/** Optional override used by tests / transitional callers. Pass `null` to clear. */
|
|
25
|
-
export declare function setDingtalkAgentDeps(deps: DingtalkAgentDeps | null): void;
|
|
26
|
-
export declare function getDingtalkAgentDeps(): DingtalkAgentDeps;
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent tool deps for dingtalk (user / dept / chat / work notice).
|
|
3
|
-
* Endpoints register themselves on start; tools look up by endpoint id.
|
|
4
|
-
*/
|
|
5
|
-
const endpoints = new Map();
|
|
6
|
-
let override = null;
|
|
7
|
-
export function registerDingtalkAgentEndpoint(endpointKey, endpoint) {
|
|
8
|
-
endpoints.set(endpointKey, endpoint);
|
|
9
|
-
return () => {
|
|
10
|
-
if (endpoints.get(endpointKey) === endpoint) {
|
|
11
|
-
endpoints.delete(endpointKey);
|
|
12
|
-
}
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
/** Optional override used by tests / transitional callers. Pass `null` to clear. */
|
|
16
|
-
export function setDingtalkAgentDeps(deps) {
|
|
17
|
-
override = deps;
|
|
18
|
-
}
|
|
19
|
-
export function getDingtalkAgentDeps() {
|
|
20
|
-
if (override)
|
|
21
|
-
return override;
|
|
22
|
-
return {
|
|
23
|
-
getEndpoint(endpointKey) {
|
|
24
|
-
const endpoint = endpoints.get(endpointKey);
|
|
25
|
-
if (!endpoint)
|
|
26
|
-
throw new Error(`Endpoint ${endpointKey} 不存在`);
|
|
27
|
-
return endpoint;
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent tool deps for dingtalk (user / dept / chat / work notice).
|
|
3
|
-
* Endpoints register themselves on start; tools look up by endpoint id.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export interface DingtalkAgentEndpoint {
|
|
7
|
-
getUserInfo(userId: string): Promise<unknown>;
|
|
8
|
-
getDepartmentUsers(deptId: number): Promise<unknown[]>;
|
|
9
|
-
sendWorkNotice(userIdList: string[], content: unknown): Promise<boolean>;
|
|
10
|
-
getDepartmentList(deptId?: number): Promise<unknown[]>;
|
|
11
|
-
getDepartmentInfo(deptId: number): Promise<unknown>;
|
|
12
|
-
createChat(name: string, ownerUserId: string, userIdList: string[]): Promise<string | null>;
|
|
13
|
-
getChatInfo(chatId: string): Promise<unknown>;
|
|
14
|
-
updateChat(
|
|
15
|
-
chatId: string,
|
|
16
|
-
options: {
|
|
17
|
-
name?: string;
|
|
18
|
-
owner?: string;
|
|
19
|
-
add_useridlist?: string[];
|
|
20
|
-
del_useridlist?: string[];
|
|
21
|
-
},
|
|
22
|
-
): Promise<boolean>;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface DingtalkAgentDeps {
|
|
26
|
-
getEndpoint: (endpointKey: string) => DingtalkAgentEndpoint;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const endpoints = new Map<string, DingtalkAgentEndpoint>();
|
|
30
|
-
let override: DingtalkAgentDeps | null = null;
|
|
31
|
-
|
|
32
|
-
export function registerDingtalkAgentEndpoint(
|
|
33
|
-
endpointKey: string,
|
|
34
|
-
endpoint: DingtalkAgentEndpoint,
|
|
35
|
-
): () => void {
|
|
36
|
-
endpoints.set(endpointKey, endpoint);
|
|
37
|
-
return () => {
|
|
38
|
-
if (endpoints.get(endpointKey) === endpoint) {
|
|
39
|
-
endpoints.delete(endpointKey);
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** Optional override used by tests / transitional callers. Pass `null` to clear. */
|
|
45
|
-
export function setDingtalkAgentDeps(deps: DingtalkAgentDeps | null): void {
|
|
46
|
-
override = deps;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function getDingtalkAgentDeps(): DingtalkAgentDeps {
|
|
50
|
-
if (override) return override;
|
|
51
|
-
return {
|
|
52
|
-
getEndpoint(endpointKey) {
|
|
53
|
-
const endpoint = endpoints.get(endpointKey);
|
|
54
|
-
if (!endpoint) throw new Error(`Endpoint ${endpointKey} 不存在`);
|
|
55
|
-
return endpoint;
|
|
56
|
-
},
|
|
57
|
-
};
|
|
58
|
-
}
|