@zhin.js/adapter-dingtalk 4.0.2 → 4.0.3
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 +41 -0
- package/README.md +58 -345
- package/adapters/dingtalk.ts +26 -0
- package/agent/tools/add_chat_members.ts +2 -2
- package/agent/tools/create_chat.ts +2 -2
- package/agent/tools/dept_info.ts +2 -2
- package/agent/tools/get_dept_users.ts +2 -2
- package/agent/tools/get_user.ts +2 -2
- package/agent/tools/list_departments.ts +2 -2
- package/agent/tools/send_work_notice.ts +2 -2
- package/agent/tools/update_chat.ts +2 -2
- package/lib/dingtalk-agent-deps.d.ts +26 -0
- package/lib/dingtalk-agent-deps.js +30 -0
- package/lib/endpoint.d.ts +55 -0
- package/lib/endpoint.js +312 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.js +5 -0
- package/lib/platform-permit.d.ts +15 -0
- package/lib/{src/platform-permit.js → platform-permit.js} +1 -2
- package/lib/protocol.d.ts +121 -0
- package/lib/protocol.js +221 -0
- package/lib/webhook.d.ts +13 -0
- package/lib/webhook.js +48 -0
- package/package.json +45 -23
- package/plugin.ts +12 -0
- package/schema.json +23 -0
- package/src/dingtalk-agent-deps.ts +49 -9
- package/src/endpoint.ts +263 -479
- package/src/index.ts +45 -59
- package/src/platform-permit.ts +1 -1
- package/src/protocol.ts +338 -0
- package/src/webhook.ts +76 -0
- package/lib/agent/tools/add_chat_members.js +0 -21
- package/lib/agent/tools/add_chat_members.js.map +0 -1
- package/lib/agent/tools/create_chat.js +0 -22
- package/lib/agent/tools/create_chat.js.map +0 -1
- package/lib/agent/tools/dept_info.js +0 -17
- package/lib/agent/tools/dept_info.js.map +0 -1
- package/lib/agent/tools/get_dept_users.js +0 -18
- package/lib/agent/tools/get_dept_users.js.map +0 -1
- package/lib/agent/tools/get_user.js +0 -17
- package/lib/agent/tools/get_user.js.map +0 -1
- package/lib/agent/tools/list_departments.js +0 -18
- package/lib/agent/tools/list_departments.js.map +0 -1
- package/lib/agent/tools/send_work_notice.js +0 -20
- package/lib/agent/tools/send_work_notice.js.map +0 -1
- package/lib/agent/tools/update_chat.js +0 -31
- package/lib/agent/tools/update_chat.js.map +0 -1
- package/lib/src/adapter.js +0 -40
- package/lib/src/adapter.js.map +0 -1
- package/lib/src/dingtalk-agent-deps.js +0 -10
- package/lib/src/dingtalk-agent-deps.js.map +0 -1
- package/lib/src/endpoint.js +0 -547
- package/lib/src/endpoint.js.map +0 -1
- package/lib/src/index.js +0 -43
- package/lib/src/index.js.map +0 -1
- package/lib/src/platform-permit.js.map +0 -1
- package/lib/src/segment-mapper.js +0 -2
- package/lib/src/segment-mapper.js.map +0 -1
- package/lib/src/types.js +0 -5
- package/lib/src/types.js.map +0 -1
- package/plugin.yml +0 -3
- package/src/adapter.ts +0 -46
- package/src/segment-mapper.ts +0 -1
- package/src/types.ts +0 -56
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
# @zhin.js/adapter-dingtalk
|
|
2
2
|
|
|
3
|
+
## 4.0.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- cc5c94d: 约定式插件运行时迁移(breaking):插件与适配器由 `usePlugin()` / `extends Adapter` 迁移为 `definePlugin` / `defineAdapter` + `plugin.ts` + 约定目录(`adapters/`、`commands/`、`components/`、`tools/` 等)。
|
|
8
|
+
|
|
9
|
+
- 新增约定式运行时包:`@zhin.js/plugin-runtime`、`@zhin.js/adapter`、`@zhin.js/runtime`、`@zhin.js/host-http`(首版 1.0.0 走 init-publish,不在本 changeset 内 bump)。
|
|
10
|
+
- 全部 20 个平台适配器改为约定式 `defineAdapter`,旧 `usePlugin` / `extends Adapter` / `segment-mapper` 生产入口已删除;onebot11 反向 WSS、onebot12 webhook/wss、milky sse/webhook/wss、satori webhook、kook webhook、qq webhook/middleware 等 slice 1 推迟的连接模式已补齐。
|
|
11
|
+
- 游戏 / 工具 / 服务插件同步迁移到约定目录结构。
|
|
12
|
+
- CLI 增加 plugin-runtime host installer(http/database/outbound/schedule/console 等)。
|
|
13
|
+
|
|
14
|
+
后续加固(同批):
|
|
15
|
+
|
|
16
|
+
- CLI:`zhin runtime start --daemon`(pidfile/崩溃拉起/风暴保护),orphan watchdog 防僵尸进程;legacy `zhin dev` / `zhin start` 已移除(含 `zhin restart`),`zhin stop` 兼容新 daemon。
|
|
17
|
+
- 安全:builtin 工具统一走 `security/policy-facade.ts` 的 `runToolPolicies`(声明式策略表,deny 优先);审计日志 close flush + 背压队列;`splitCompoundCommand` 引号感知、`extractCommandName` 去引号堵绕过。
|
|
18
|
+
- 日志:Logger 双堆栈修复、本地时区、`getLogger` 挂树(`setLevel` 递归生效)、第三方库(log4js/discord)桥接、启动人读总结。
|
|
19
|
+
- 结构:`plugins/games/shared` 迁为 `packages/game-kit`(`@zhin.js/game-kit`);死目录 `plugins/adapters/common` 删除。
|
|
20
|
+
- 脚手架:`create-zhin-app` / `zhin new` / scaffold-wizard 生成物改为 Plugin Runtime 形态(minimal-bot 同构,新配置格式)。
|
|
21
|
+
- Console:endpoint.list 真实名称与 phase、schema:get-all 按 instanceKey 映射、db:\* 接 DatabaseHost。
|
|
22
|
+
|
|
23
|
+
注:按仓库发布惯例(见 1bb345dd2),本次 breaking 迁移统一使用 patch,避免 zhin.js 5.0 级联。
|
|
24
|
+
|
|
25
|
+
- 447f3e2: 迁移缺口修复(legacy 功能对齐):
|
|
26
|
+
|
|
27
|
+
- html 段出站规范化:经 `@zhin.js/html-renderer` 渲染为 image 段(sandbox 豁免、无渲染器时降级文本),修复真实平台 `[object Object]`。
|
|
28
|
+
- 群聊 @ 触发 AI:适配器入站标注 `metadata.mentioned`(icqq/qq/slack/onebot11/onebot12/napcat/milky/discord/telegram/kook/dingtalk/satori),`matchAiTrigger` 补齐 ignorePrefixes/respondToAt/respondToPrivate/keywords(默认值与 legacy 对齐)。
|
|
29
|
+
- im_transcripts 全量流水恢复写入(chat_history 工具可用);群聊旁听上下文回迁。
|
|
30
|
+
- `ai.trigger.timeout/thinkingMessage/errorTemplate` 生效;masters/trusted 角色解析对齐 legacy。
|
|
31
|
+
- `Message.sender` 统一为用户 ID(onebot11/12、napcat、milky 原误传显示名);quote_id 经 metadata 接入 AI 引用上下文。
|
|
32
|
+
|
|
33
|
+
- Updated dependencies [16ec4e8]
|
|
34
|
+
- Updated dependencies [cc5c94d]
|
|
35
|
+
- Updated dependencies [447f3e2]
|
|
36
|
+
- @zhin.js/core@1.3.5
|
|
37
|
+
- @zhin.js/agent@1.0.4
|
|
38
|
+
- @zhin.js/host-http@1.0.1
|
|
39
|
+
- zhin.js@4.1.3
|
|
40
|
+
- @zhin.js/logger@1.0.75
|
|
41
|
+
- @zhin.js/plugin-runtime@1.0.1
|
|
42
|
+
- @zhin.js/adapter@1.0.1
|
|
43
|
+
|
|
3
44
|
## 4.0.2
|
|
4
45
|
|
|
5
46
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
# @zhin.js/adapter-dingtalk
|
|
2
2
|
|
|
3
|
-
Zhin.js
|
|
3
|
+
Zhin.js 钉钉适配器(Plugin Runtime),通过 Runtime Host HTTP Webhook 收发消息。
|
|
4
|
+
|
|
5
|
+
## 功能
|
|
6
|
+
|
|
7
|
+
- Webhook 事件接收(`httpHostToken` POST + HMAC-SHA256 签名验证)
|
|
8
|
+
- Access Token 自动刷新
|
|
9
|
+
- Session Webhook 优先回复 / `/robot/send` 主动发送
|
|
10
|
+
- 约定式 `defineAdapter` / `definePlugin`(无需 `usePlugin`)
|
|
4
11
|
|
|
5
12
|
## 安装
|
|
6
13
|
|
|
@@ -8,367 +15,73 @@ Zhin.js 钉钉适配器,支持企业内部机器人和钉钉开放平台机器
|
|
|
8
15
|
pnpm add @zhin.js/adapter-dingtalk
|
|
9
16
|
```
|
|
10
17
|
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
### 基础配置
|
|
14
|
-
|
|
15
|
-
```typescript
|
|
16
|
-
import { DingTalkEndpointConfig } from '@zhin.js/adapter-dingtalk';
|
|
17
|
-
|
|
18
|
-
const config: DingTalkEndpointConfig = {
|
|
19
|
-
context: 'dingtalk',
|
|
20
|
-
name: 'my-dingtalk-bot',
|
|
21
|
-
appKey: 'YOUR_APP_KEY', // 钉钉应用 AppKey
|
|
22
|
-
appSecret: 'YOUR_APP_SECRET', // 钉钉应用 AppSecret
|
|
23
|
-
webhookPath: '/dingtalk/webhook', // Webhook 路径
|
|
24
|
-
robotCode: 'YOUR_ROBOT_CODE' // 机器人编码(可选)
|
|
25
|
-
}
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
### 完整配置
|
|
29
|
-
|
|
30
|
-
```typescript
|
|
31
|
-
const config: DingTalkEndpointConfig = {
|
|
32
|
-
context: 'dingtalk',
|
|
33
|
-
name: 'my-dingtalk-bot',
|
|
34
|
-
appKey: 'YOUR_APP_KEY',
|
|
35
|
-
appSecret: 'YOUR_APP_SECRET',
|
|
36
|
-
webhookPath: '/dingtalk/webhook',
|
|
37
|
-
robotCode: 'YOUR_ROBOT_CODE',
|
|
38
|
-
|
|
39
|
-
// API 配置
|
|
40
|
-
apiBaseUrl: 'https://oapi.dingtalk.com' // 自定义API地址(可选)
|
|
41
|
-
}
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
### 配置参数说明
|
|
45
|
-
|
|
46
|
-
- `appKey` (必需): 钉钉应用的 AppKey,在开发者后台获取
|
|
47
|
-
- `appSecret` (必需): 钉钉应用的 AppSecret,在开发者后台获取
|
|
48
|
-
- `webhookPath` (必需): Webhook 路径,如 `/dingtalk/webhook`
|
|
49
|
-
- `robotCode` (可选): 机器人编码,用于发送消息时识别机器人身份
|
|
50
|
-
- `apiBaseUrl` (可选): 自定义 API 基础地址,默认为 `https://oapi.dingtalk.com`
|
|
51
|
-
|
|
52
|
-
## 获取配置信息
|
|
53
|
-
|
|
54
|
-
### 创建钉钉企业内部应用
|
|
55
|
-
|
|
56
|
-
1. **访问钉钉开放平台**
|
|
57
|
-
- 登录 [钉钉开放平台](https://open.dingtalk.com/)
|
|
58
|
-
- 进入「应用开发」→「企业内部开发」
|
|
59
|
-
|
|
60
|
-
2. **创建应用**
|
|
61
|
-
- 点击「创建应用」
|
|
62
|
-
- 选择「企业内部开发」→「机器人」
|
|
63
|
-
- 填写应用基本信息(应用名称、应用描述、应用图标等)
|
|
64
|
-
|
|
65
|
-
3. **获取应用凭证**
|
|
66
|
-
- 在应用详情页面找到「开发管理」
|
|
67
|
-
- 获取 **AppKey** 和 **AppSecret**
|
|
68
|
-
- 记录这些信息用于配置
|
|
69
|
-
|
|
70
|
-
### 配置机器人
|
|
71
|
-
|
|
72
|
-
1. **配置机器人能力**
|
|
73
|
-
- 在应用详情页面,进入「机器人配置」
|
|
74
|
-
- 启用「消息接收」功能
|
|
75
|
-
- 获取机器人的 **RobotCode**(机器人编码)
|
|
76
|
-
|
|
77
|
-
2. **配置消息接收地址**
|
|
78
|
-
- 在「消息接收」设置中配置接收地址
|
|
79
|
-
- 设置请求 URL:`https://yourdomain.com/dingtalk/webhook`
|
|
80
|
-
- 钉钉会通过该地址推送消息事件
|
|
81
|
-
|
|
82
|
-
3. **配置权限**
|
|
83
|
-
- 在「权限管理」中申请所需权限:
|
|
84
|
-
- `通讯录只读权限` - 读取通讯录信息
|
|
85
|
-
- `企业通讯录个人信息读权限` - 获取用户详细信息
|
|
86
|
-
- `消息通知` - 发送工作通知
|
|
87
|
-
- 其他业务需要的权限
|
|
88
|
-
|
|
89
|
-
4. **发布应用**
|
|
90
|
-
- 完成配置后,发布应用
|
|
91
|
-
- 在企业工作台添加应用
|
|
92
|
-
- 员工可在钉钉中找到并使用该机器人
|
|
18
|
+
## Plugin Runtime
|
|
93
19
|
|
|
94
|
-
|
|
20
|
+
- `@zhin.js/adapter` — 约定式薄入口 `adapters/dingtalk.ts`(`defineAdapter`)
|
|
21
|
+
- 实现:`src/endpoint.ts`(生命周期/出站/OpenAPI)、`src/webhook.ts`(验签入站)、`src/protocol.ts`
|
|
22
|
+
- `@zhin.js/core` — `messageGatewayToken` 入站/出站
|
|
23
|
+
- `@zhin.js/host-http` — `httpHostToken` 注册 Webhook 路由(**非** legacy host-router/Koa)
|
|
24
|
+
- `@zhin.js/plugin-runtime` — `plugin.ts`(`definePlugin`)
|
|
25
|
+
- 配置经插件 `schema.json` 落到 `plugins.<instanceKey>`
|
|
95
26
|
|
|
96
|
-
|
|
27
|
+
入站:`gateway.receive({ adapter, target: conversationId, content: text, sender, metadata })`
|
|
28
|
+
出站:`send({ target, payload })` → sessionWebhook 或 `/robot/send`
|
|
97
29
|
|
|
98
|
-
|
|
99
|
-
- 进入钉钉群聊
|
|
100
|
-
- 点击群设置 → 「智能群助手」
|
|
101
|
-
- 选择「添加机器人」→「自定义机器人」
|
|
30
|
+
## 前置条件
|
|
102
31
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
32
|
+
1. 在 [钉钉开放平台](https://open.dingtalk.com/) 创建企业内部应用 / 机器人
|
|
33
|
+
2. 获取 **AppKey**、**AppSecret**(可选 RobotCode)
|
|
34
|
+
3. 设置消息接收 URL 为 `https://your-domain/dingtalk/webhook`
|
|
35
|
+
4. Runtime Host(`http`)须已 listen,Webhook 才可达
|
|
107
36
|
|
|
108
|
-
|
|
109
|
-
- 群机器人只能在对应群聊中使用
|
|
110
|
-
- 功能相对简单,适合轻量级场景
|
|
37
|
+
必填字段:`appKey`、`appSecret`。
|
|
111
38
|
|
|
112
|
-
##
|
|
39
|
+
## 最小配置
|
|
113
40
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
{
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
appKey: 'dingxxxxxxxxxx',
|
|
125
|
-
appSecret: 'your-app-secret',
|
|
126
|
-
webhookPath: '/dingtalk/webhook',
|
|
127
|
-
robotCode: 'dingxxxxxxxxxxxxxxxx'
|
|
128
|
-
}
|
|
129
|
-
],
|
|
130
|
-
plugins: [
|
|
131
|
-
'@zhin.js/host-router',
|
|
132
|
-
'@zhin.js/adapter-dingtalk',
|
|
133
|
-
],
|
|
134
|
-
})
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### 接收和发送消息
|
|
138
|
-
|
|
139
|
-
```typescript
|
|
140
|
-
import { usePlugin, MessageCommand } from 'zhin.js'
|
|
141
|
-
|
|
142
|
-
const { addCommand, logger } = usePlugin()
|
|
143
|
-
|
|
144
|
-
// 定义命令
|
|
145
|
-
addCommand(new MessageCommand('hello <name:text>')
|
|
146
|
-
.action(async (message, result) => {
|
|
147
|
-
logger.info(`收到来自 ${result.params.name} 的问候`)
|
|
148
|
-
return `你好,${result.params.name}!欢迎使用钉钉机器人。`
|
|
149
|
-
})
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
// 监听所有消息
|
|
153
|
-
import { onMessage } from 'zhin.js'
|
|
154
|
-
|
|
155
|
-
onMessage(async (message) => {
|
|
156
|
-
logger.info(`收到消息:${message.$raw}`)
|
|
157
|
-
logger.info(`发送者:${message.$sender.name}`)
|
|
158
|
-
logger.info(`会话类型:${message.$channel.type}`)
|
|
159
|
-
})
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
### 使用 @ 功能
|
|
163
|
-
|
|
164
|
-
```typescript
|
|
165
|
-
addCommand(new MessageCommand('notify <...users:at>')
|
|
166
|
-
.action(async (message, result) => {
|
|
167
|
-
const users = result.params.users
|
|
168
|
-
return [
|
|
169
|
-
{ type: 'text', data: { content: '通知以下用户:\n' } },
|
|
170
|
-
...users.map(user => ({
|
|
171
|
-
type: 'at',
|
|
172
|
-
data: { id: user.data.id, name: user.data.name }
|
|
173
|
-
})),
|
|
174
|
-
{ type: 'text', data: { content: '\n请注意查看!' } }
|
|
175
|
-
]
|
|
176
|
-
})
|
|
177
|
-
)
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
### 发送富文本消息
|
|
181
|
-
|
|
182
|
-
```typescript
|
|
183
|
-
addCommand(new MessageCommand('info')
|
|
184
|
-
.action(async (message) => {
|
|
185
|
-
return [
|
|
186
|
-
{
|
|
187
|
-
type: 'markdown',
|
|
188
|
-
data: {
|
|
189
|
-
title: '系统信息',
|
|
190
|
-
content: `
|
|
191
|
-
# 系统信息
|
|
192
|
-
|
|
193
|
-
## 基本信息
|
|
194
|
-
- **服务器**: 运行正常
|
|
195
|
-
- **版本**: v1.0.0
|
|
196
|
-
- **状态**: ✅ 在线
|
|
197
|
-
|
|
198
|
-
## 功能列表
|
|
199
|
-
1. 消息接收与发送
|
|
200
|
-
2. 命令解析
|
|
201
|
-
3. @ 提醒功能
|
|
202
|
-
`
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
]
|
|
206
|
-
})
|
|
207
|
-
)
|
|
41
|
+
```yaml
|
|
42
|
+
# zhin.config.yml(Plugin Runtime)
|
|
43
|
+
plugins:
|
|
44
|
+
dingtalk:
|
|
45
|
+
name: my-dingtalk-bot
|
|
46
|
+
appKey: ${DINGTALK_APP_KEY}
|
|
47
|
+
appSecret: ${DINGTALK_APP_SECRET}
|
|
48
|
+
robotCode: ${DINGTALK_ROBOT_CODE} # 可选
|
|
49
|
+
webhookPath: /dingtalk/webhook # 可选,默认 /dingtalk/webhook
|
|
50
|
+
apiBaseUrl: https://oapi.dingtalk.com # 可选
|
|
208
51
|
```
|
|
209
52
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
### 接收消息类型
|
|
213
|
-
|
|
214
|
-
- ✅ **文本消息** (`text`) - 普通文本和 @ 提醒
|
|
215
|
-
- ✅ **图片消息** (`picture`) - 图片文件
|
|
216
|
-
- ✅ **文件消息** (`file`) - 各类文件
|
|
217
|
-
- ✅ **语音消息** (`audio`) - 音频文件
|
|
218
|
-
- ✅ **视频消息** (`video`) - 视频文件
|
|
219
|
-
- ✅ **富文本消息** (`richText`) - 富文本内容
|
|
220
|
-
- ✅ **Markdown 消息** (`markdown`) - Markdown 格式
|
|
221
|
-
|
|
222
|
-
### 发送消息类型
|
|
53
|
+
根插件 `zhin.plugins`(或项目图)需引用 `@zhin.js/adapter-dingtalk`(`instanceKey: dingtalk`)。
|
|
223
54
|
|
|
224
|
-
|
|
225
|
-
- ✅ **图片消息** - 通过 URL 发送
|
|
226
|
-
- ✅ **Markdown 消息** - 富文本展示
|
|
227
|
-
- ✅ **链接消息** - 卡片式链接
|
|
228
|
-
- ❌ **撤回消息** - 钉钉机器人不支持撤回
|
|
55
|
+
## 环境变量
|
|
229
56
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const endpoint = app.adapters.get('dingtalk')?.endpoints.get('dingtalk-bot')
|
|
236
|
-
if (bot) {
|
|
237
|
-
const userInfo = await endpoint.getUserInfo('user-id')
|
|
238
|
-
console.log(userInfo)
|
|
239
|
-
}
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
### 获取部门用户列表
|
|
243
|
-
|
|
244
|
-
```typescript
|
|
245
|
-
const users = await endpoint.getDepartmentUsers(1) // 部门 ID
|
|
246
|
-
console.log(users)
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
### 发送工作通知
|
|
250
|
-
|
|
251
|
-
```typescript
|
|
252
|
-
await endpoint.sendWorkNotice(
|
|
253
|
-
['user1', 'user2'], // 用户 ID 列表
|
|
254
|
-
{
|
|
255
|
-
msgtype: 'text',
|
|
256
|
-
text: { content: '这是一条工作通知' }
|
|
257
|
-
}
|
|
258
|
-
)
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
## 安全说明
|
|
262
|
-
|
|
263
|
-
### 签名验证
|
|
264
|
-
|
|
265
|
-
钉钉会在 Webhook 请求的 Header 中携带签名信息:
|
|
266
|
-
- `timestamp`: 时间戳
|
|
267
|
-
- `sign`: 签名值
|
|
268
|
-
|
|
269
|
-
适配器会自动验证签名,确保请求来自钉钉服务器。
|
|
270
|
-
|
|
271
|
-
### Token 管理
|
|
272
|
-
|
|
273
|
-
适配器会自动管理 access_token:
|
|
274
|
-
- 首次连接时获取 token
|
|
275
|
-
- Token 过期前 5 分钟自动刷新
|
|
276
|
-
- 所有 API 请求自动携带有效 token
|
|
277
|
-
|
|
278
|
-
## 最佳实践
|
|
279
|
-
|
|
280
|
-
### 1. 配置环境变量
|
|
281
|
-
|
|
282
|
-
不要在代码中硬编码敏感信息:
|
|
283
|
-
|
|
284
|
-
```typescript
|
|
285
|
-
export default defineConfig({
|
|
286
|
-
endpoints: [{
|
|
287
|
-
name: 'dingtalk-bot',
|
|
288
|
-
context: 'dingtalk',
|
|
289
|
-
appKey: process.env.DINGTALK_APP_KEY!,
|
|
290
|
-
appSecret: process.env.DINGTALK_APP_SECRET!,
|
|
291
|
-
webhookPath: '/dingtalk/webhook',
|
|
292
|
-
robotCode: process.env.DINGTALK_ROBOT_CODE
|
|
293
|
-
}]
|
|
294
|
-
})
|
|
295
|
-
```
|
|
57
|
+
| 变量 | 说明 |
|
|
58
|
+
|------|------|
|
|
59
|
+
| `DINGTALK_APP_KEY` | 应用 AppKey |
|
|
60
|
+
| `DINGTALK_APP_SECRET` | 应用 AppSecret |
|
|
61
|
+
| `DINGTALK_BOT_NAME` | 默认 endpoint 名称(可选) |
|
|
296
62
|
|
|
297
|
-
|
|
63
|
+
## 消息类型映射
|
|
298
64
|
|
|
299
|
-
|
|
65
|
+
| 钉钉类型 | 入站 content(文本摘要) | 出站 wire |
|
|
66
|
+
|----------|--------------------------|-----------|
|
|
67
|
+
| text | 原文 | text |
|
|
68
|
+
| picture | `[image]` | picture(需 `url`) |
|
|
69
|
+
| file | `[file: name]` | — |
|
|
70
|
+
| audio / video | `[audio]` / `[video]` | — |
|
|
71
|
+
| markdown | 原文或 `[markdown]` | markdown |
|
|
72
|
+
| link | — | link |
|
|
300
73
|
|
|
301
|
-
|
|
302
|
-
onMessage(async (message) => {
|
|
303
|
-
try {
|
|
304
|
-
// 处理消息逻辑
|
|
305
|
-
} catch (error) {
|
|
306
|
-
logger.error('处理消息失败:', error)
|
|
307
|
-
await message.$reply('抱歉,处理消息时出现错误')
|
|
308
|
-
}
|
|
309
|
-
})
|
|
310
|
-
```
|
|
74
|
+
## Agent 工具
|
|
311
75
|
|
|
312
|
-
|
|
76
|
+
`agent/` 目录保留(get_user、部门、群聊、工作通知等)。Endpoint 在 `start` 时自注册到 `dingtalk-agent-deps`。
|
|
313
77
|
|
|
314
|
-
|
|
78
|
+
## 平台权限(platform permit)
|
|
315
79
|
|
|
316
|
-
|
|
80
|
+
`plugin.ts` 在 generation setup 注册 `src/platform-permit.ts` checker,并在 dispose 注销;CapabilityIngress 与 ToolSystem 统一经 Core `canAccessTool()` 消费工具权限。
|
|
317
81
|
|
|
318
|
-
|
|
82
|
+
## 测试
|
|
319
83
|
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
// ... 其他配置
|
|
324
|
-
})
|
|
84
|
+
```bash
|
|
85
|
+
pnpm --filter @zhin.js/adapter-dingtalk build
|
|
86
|
+
pnpm --filter @zhin.js/adapter-dingtalk test
|
|
325
87
|
```
|
|
326
|
-
|
|
327
|
-
## 常见问题
|
|
328
|
-
|
|
329
|
-
### Q: Webhook 收不到消息?
|
|
330
|
-
|
|
331
|
-
A: 检查以下几点:
|
|
332
|
-
1. Webhook URL 是否可以从公网访问
|
|
333
|
-
2. 钉钉后台的消息接收地址是否配置正确
|
|
334
|
-
3. 应用是否已经发布并添加到工作台
|
|
335
|
-
4. 检查服务器日志是否有错误信息
|
|
336
|
-
|
|
337
|
-
### Q: 发送消息失败?
|
|
338
|
-
|
|
339
|
-
A: 可能的原因:
|
|
340
|
-
1. AppKey 或 AppSecret 配置错误
|
|
341
|
-
2. access_token 获取失败
|
|
342
|
-
3. 机器人编码(robotCode)配置错误
|
|
343
|
-
4. 网络问题或 API 限流
|
|
344
|
-
|
|
345
|
-
### Q: @ 功能不生效?
|
|
346
|
-
|
|
347
|
-
A: 确保:
|
|
348
|
-
1. 使用正确的用户 ID 格式
|
|
349
|
-
2. @ 的用户在当前会话中
|
|
350
|
-
3. 消息格式正确包含 at 字段
|
|
351
|
-
|
|
352
|
-
## 相关链接
|
|
353
|
-
|
|
354
|
-
- [钉钉开放平台](https://open.dingtalk.com/)
|
|
355
|
-
- [钉钉机器人开发文档](https://open.dingtalk.com/document/robots/robot-overview)
|
|
356
|
-
- [Zhin.js 官方文档](https://github.com/zhinjs/zhin)
|
|
357
|
-
|
|
358
|
-
## AI 工具
|
|
359
|
-
|
|
360
|
-
| 类别 | 路径 |
|
|
361
|
-
|------|------|
|
|
362
|
-
| Permit 词汇 | `agent/PERMITS.md` |
|
|
363
|
-
| 平台工具(8 个) | `agent/tools/`(`dingtalk_*`:部门、用户、群聊、工作通知等) |
|
|
364
|
-
| 技能说明 | `agent/skills/dingtalk.md` |
|
|
365
|
-
| 群管标准工具 | `createSceneManagementTools()` |
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
## 许可证
|
|
369
|
-
|
|
370
|
-
MIT License
|
|
371
|
-
|
|
372
|
-
## 贡献
|
|
373
|
-
|
|
374
|
-
欢迎提交 Issue 和 Pull Request!
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convention entry: discover `adapters/dingtalk.ts` → defineAdapter.
|
|
3
|
+
*/
|
|
4
|
+
import { defineAdapter } from '@zhin.js/adapter';
|
|
5
|
+
import { messageGatewayToken } from '@zhin.js/core/runtime';
|
|
6
|
+
import { httpHostToken } from '@zhin.js/host-http';
|
|
7
|
+
import { DingTalkEndpoint } from '../src/endpoint.js';
|
|
8
|
+
import {
|
|
9
|
+
resolveDingTalkConfig,
|
|
10
|
+
type DingTalkAdapterConfig,
|
|
11
|
+
} from '../src/protocol.js';
|
|
12
|
+
|
|
13
|
+
export { DingTalkEndpoint } from '../src/endpoint.js';
|
|
14
|
+
export type { DingTalkEndpointOptions, DingTalkFetch } from '../src/endpoint.js';
|
|
15
|
+
|
|
16
|
+
export default defineAdapter<DingTalkAdapterConfig>({
|
|
17
|
+
capabilities: ['inbound', 'outbound'],
|
|
18
|
+
create(context) {
|
|
19
|
+
return new DingTalkEndpoint({
|
|
20
|
+
id: context.id,
|
|
21
|
+
gateway: context.use(messageGatewayToken),
|
|
22
|
+
http: context.use(httpHostToken),
|
|
23
|
+
config: resolveDingTalkConfig(context.config),
|
|
24
|
+
});
|
|
25
|
+
},
|
|
26
|
+
});
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineAgentTool } from '@zhin.js/agent/tools';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { getDingtalkAgentDeps } from '../../src/dingtalk-agent-deps.js';
|
|
4
4
|
import { platformPermit } from '../../src/platform-permit.js';
|
|
5
5
|
|
|
6
|
-
export default
|
|
6
|
+
export default defineAgentTool<{ endpoint_id: string; chat_id: string; user_ids: string }>({
|
|
7
7
|
description: '向钉钉群聊添加成员',
|
|
8
8
|
inputSchema: z.object({
|
|
9
9
|
endpoint_id: z.string().describe('Endpoint 名称'),
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineAgentTool } from '@zhin.js/agent/tools';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { getDingtalkAgentDeps } from '../../src/dingtalk-agent-deps.js';
|
|
4
4
|
import { platformPermit } from '../../src/platform-permit.js';
|
|
5
5
|
|
|
6
|
-
export default
|
|
6
|
+
export default defineAgentTool<{ endpoint_id: string; name: string; owner: string; members: string }>({
|
|
7
7
|
description: '创建钉钉群聊',
|
|
8
8
|
inputSchema: z.object({
|
|
9
9
|
endpoint_id: z.string().describe('Endpoint 名称'),
|
package/agent/tools/dept_info.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineAgentTool } from '@zhin.js/agent/tools';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { getDingtalkAgentDeps } from '../../src/dingtalk-agent-deps.js';
|
|
4
|
-
export default
|
|
4
|
+
export default defineAgentTool<{ endpoint_id: string; dept_id: string }>({
|
|
5
5
|
description: '获取钉钉部门详细信息',
|
|
6
6
|
inputSchema: z.object({
|
|
7
7
|
endpoint_id: z.string().describe('Endpoint 名称'),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineAgentTool } from '@zhin.js/agent/tools';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { getDingtalkAgentDeps } from '../../src/dingtalk-agent-deps.js';
|
|
4
|
-
export default
|
|
4
|
+
export default defineAgentTool<{ endpoint_id: string; dept_id: string }>({
|
|
5
5
|
description: '获取钉钉部门用户列表',
|
|
6
6
|
inputSchema: z.object({
|
|
7
7
|
endpoint_id: z.string().describe('Endpoint 名称'),
|
package/agent/tools/get_user.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineAgentTool } from '@zhin.js/agent/tools';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { getDingtalkAgentDeps } from '../../src/dingtalk-agent-deps.js';
|
|
4
|
-
export default
|
|
4
|
+
export default defineAgentTool<{ endpoint_id: string; user_id: string }>({
|
|
5
5
|
description: '获取钉钉用户信息',
|
|
6
6
|
inputSchema: z.object({
|
|
7
7
|
endpoint_id: z.string().describe('Endpoint 名称'),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineAgentTool } from '@zhin.js/agent/tools';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { getDingtalkAgentDeps } from '../../src/dingtalk-agent-deps.js';
|
|
4
|
-
export default
|
|
4
|
+
export default defineAgentTool<{ endpoint_id: string; dept_id?: string }>({
|
|
5
5
|
description: '获取钉钉部门列表',
|
|
6
6
|
inputSchema: z.object({
|
|
7
7
|
endpoint_id: z.string().describe('Endpoint 名称'),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineAgentTool } from '@zhin.js/agent/tools';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { getDingtalkAgentDeps } from '../../src/dingtalk-agent-deps.js';
|
|
4
|
-
export default
|
|
4
|
+
export default defineAgentTool<{ endpoint_id: string; user_ids: string; content: string }>({
|
|
5
5
|
description: '向指定用户发送钉钉工作通知',
|
|
6
6
|
inputSchema: z.object({
|
|
7
7
|
endpoint_id: z.string().describe('Endpoint 名称'),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineAgentTool } from '@zhin.js/agent/tools';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { getDingtalkAgentDeps } from '../../src/dingtalk-agent-deps.js';
|
|
4
|
-
export default
|
|
4
|
+
export default defineAgentTool<{ endpoint_id: string; chat_id: string; name?: string; owner?: string; add_members?: string; remove_members?: string }>({
|
|
5
5
|
description: '更新钉钉群聊设置(改名、换群主、增减成员)',
|
|
6
6
|
inputSchema: z.object({
|
|
7
7
|
endpoint_id: z.string().describe('Endpoint 名称'),
|
|
@@ -0,0 +1,26 @@
|
|
|
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: (endpointId: string) => DingtalkAgentEndpoint;
|
|
22
|
+
}
|
|
23
|
+
export declare function registerDingtalkAgentEndpoint(endpointId: 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;
|
|
@@ -0,0 +1,30 @@
|
|
|
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(endpointId, endpoint) {
|
|
8
|
+
endpoints.set(endpointId, endpoint);
|
|
9
|
+
return () => {
|
|
10
|
+
if (endpoints.get(endpointId) === endpoint) {
|
|
11
|
+
endpoints.delete(endpointId);
|
|
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(endpointId) {
|
|
24
|
+
const endpoint = endpoints.get(endpointId);
|
|
25
|
+
if (!endpoint)
|
|
26
|
+
throw new Error(`Endpoint ${endpointId} 不存在`);
|
|
27
|
+
return endpoint;
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|