@yaoyuanchao/dingtalk 1.2.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.
- package/CHANGELOG.md +149 -0
- package/LICENSE +21 -0
- package/README.md +301 -0
- package/clawdbot.plugin.json +9 -0
- package/index.ts +16 -0
- package/package.json +55 -0
- package/src/accounts.ts +73 -0
- package/src/api.ts +382 -0
- package/src/channel.ts +271 -0
- package/src/config-schema.ts +115 -0
- package/src/monitor.ts +622 -0
- package/src/onboarding.ts +152 -0
- package/src/probe.ts +36 -0
- package/src/runtime.ts +10 -0
- package/src/types.ts +56 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.2.0] - 2026-01-28
|
|
9
|
+
|
|
10
|
+
### 🎉 Major Features - Official Plugin Release
|
|
11
|
+
|
|
12
|
+
This release transforms the DingTalk plugin into an official Clawdbot plugin that can be installed via `clawdbot plugins install` command.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **Official NPM Installation Support**
|
|
17
|
+
- Published to NPM as `@yaoyuanchao/dingtalk`
|
|
18
|
+
- Install with: `clawdbot plugins install @yaoyuanchao/dingtalk`
|
|
19
|
+
- Added `clawdbot.install` configuration in package.json
|
|
20
|
+
|
|
21
|
+
- **Interactive Onboarding Wizard** (`src/onboarding.ts`)
|
|
22
|
+
- Run with: `clawdbot onboard --channel dingtalk`
|
|
23
|
+
- Step-by-step guided configuration
|
|
24
|
+
- Automatic connection testing
|
|
25
|
+
- Policy selection with descriptions
|
|
26
|
+
- Auto-save to configuration file
|
|
27
|
+
|
|
28
|
+
- **Zod Schema Validation** (`src/config-schema.ts`)
|
|
29
|
+
- Type-safe configuration validation
|
|
30
|
+
- Automatic error messages with detailed paths
|
|
31
|
+
- Default values for all optional fields
|
|
32
|
+
- Strict mode to catch unknown properties
|
|
33
|
+
|
|
34
|
+
- **Health Check System** (`src/probe.ts`)
|
|
35
|
+
- Connection status monitoring
|
|
36
|
+
- Latency measurement
|
|
37
|
+
- Improved `status.probeAccount` with detailed error reporting
|
|
38
|
+
|
|
39
|
+
- **Enhanced Error Messages**
|
|
40
|
+
- Validation errors show exact field and reason
|
|
41
|
+
- Helpful hints for environment variable configuration
|
|
42
|
+
- Guidance for troubleshooting connection issues
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
|
|
46
|
+
- **Package Configuration**
|
|
47
|
+
- Renamed from `@clawdbot/dingtalk` to `@yaoyuanchao/dingtalk`
|
|
48
|
+
- Version bumped from 0.1.0 to 1.2.0
|
|
49
|
+
- Added MIT license
|
|
50
|
+
- Added keywords for NPM discoverability
|
|
51
|
+
- Added `peerDependencies` for clawdbot version requirement
|
|
52
|
+
- Added `files` field to control NPM publish content
|
|
53
|
+
|
|
54
|
+
- **Configuration Validation**
|
|
55
|
+
- Migrated from JSON Schema to Zod
|
|
56
|
+
- Environment variables merged before validation
|
|
57
|
+
- Better error messages for invalid configurations
|
|
58
|
+
|
|
59
|
+
- **Module Imports**
|
|
60
|
+
- Separated probe functionality into dedicated module
|
|
61
|
+
- Improved type safety with Zod type inference
|
|
62
|
+
|
|
63
|
+
### Technical Details
|
|
64
|
+
|
|
65
|
+
- **Dependencies**: Added `zod@^3.22.0`
|
|
66
|
+
- **Peer Dependencies**: Requires `clawdbot >= 2026.1.24`
|
|
67
|
+
- **TypeScript**: Published as TypeScript source (not compiled JS)
|
|
68
|
+
- **Backward Compatibility**: All v0.1.0 features preserved
|
|
69
|
+
|
|
70
|
+
### Migration Guide
|
|
71
|
+
|
|
72
|
+
If you're upgrading from v0.1.0:
|
|
73
|
+
|
|
74
|
+
1. **No configuration changes required** - existing configs work as-is
|
|
75
|
+
2. **Optional**: Try the new onboarding wizard for fresh setup
|
|
76
|
+
3. **Optional**: Reinstall via NPM for easier updates:
|
|
77
|
+
```bash
|
|
78
|
+
clawdbot plugins uninstall dingtalk
|
|
79
|
+
clawdbot plugins install @yaoyuanchao/dingtalk
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## [0.1.0] - 2026-01-26
|
|
83
|
+
|
|
84
|
+
### Initial Release
|
|
85
|
+
|
|
86
|
+
#### Added
|
|
87
|
+
|
|
88
|
+
- **Stream Mode Connection**
|
|
89
|
+
- WebSocket-based connection via `dingtalk-stream@2.1.4`
|
|
90
|
+
- No public domain required
|
|
91
|
+
- Auto-reconnection support
|
|
92
|
+
|
|
93
|
+
- **Private Messages (DM)**
|
|
94
|
+
- Support for 1-on-1 conversations
|
|
95
|
+
- Pairing mode with automatic staffId display
|
|
96
|
+
- Allowlist mode for restricted access
|
|
97
|
+
- Open mode for unrestricted access
|
|
98
|
+
|
|
99
|
+
- **Group Chat Support**
|
|
100
|
+
- @mention detection
|
|
101
|
+
- Group allowlist (conversation IDs)
|
|
102
|
+
- Optional mention requirement
|
|
103
|
+
|
|
104
|
+
- **Message Sending**
|
|
105
|
+
- Dual-route strategy:
|
|
106
|
+
- SessionWebhook (preferred, 35-minute validity)
|
|
107
|
+
- REST API (fallback for expired sessions)
|
|
108
|
+
- Text message support
|
|
109
|
+
- Markdown format support (limited by DingTalk)
|
|
110
|
+
- Auto-converts tables to code blocks
|
|
111
|
+
|
|
112
|
+
- **Access Control**
|
|
113
|
+
- DM policies: disabled, pairing, allowlist, open
|
|
114
|
+
- Group policies: disabled, allowlist, open
|
|
115
|
+
- Per-user staffId authorization
|
|
116
|
+
- Per-group conversationId authorization
|
|
117
|
+
|
|
118
|
+
- **Configuration**
|
|
119
|
+
- Environment variable support (DINGTALK_CLIENT_ID, etc.)
|
|
120
|
+
- JSON configuration in clawdbot.json
|
|
121
|
+
- Credential source detection (config vs env)
|
|
122
|
+
|
|
123
|
+
- **Core Modules**
|
|
124
|
+
- `src/monitor.ts` - Stream connection and message handling
|
|
125
|
+
- `src/api.ts` - DingTalk REST API wrapper
|
|
126
|
+
- `src/channel.ts` - Clawdbot ChannelPlugin implementation
|
|
127
|
+
- `src/accounts.ts` - Account credential resolution
|
|
128
|
+
- `src/types.ts` - TypeScript type definitions
|
|
129
|
+
|
|
130
|
+
#### Known Limitations
|
|
131
|
+
|
|
132
|
+
- **Markdown Support**: DingTalk doesn't support tables in markdown
|
|
133
|
+
- **Media Messages**: Sending files/images not yet implemented
|
|
134
|
+
- **Rate Limits**: 20 messages/minute/group (DingTalk limit)
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Legend
|
|
139
|
+
|
|
140
|
+
- 🎉 **Major Features**: Significant new functionality
|
|
141
|
+
- ✨ **Enhancements**: Improvements to existing features
|
|
142
|
+
- 🐛 **Bug Fixes**: Fixes for issues
|
|
143
|
+
- 🔒 **Security**: Security-related changes
|
|
144
|
+
- 📝 **Documentation**: Documentation updates
|
|
145
|
+
- ⚠️ **Breaking Changes**: Changes that may require migration
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
**Note**: This changelog focuses on user-facing changes. For detailed commit history, see the Git log.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yao Yuanchao
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
# DingTalk Channel Plugin for Clawdbot
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@yaoyuanchao/dingtalk)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
钉钉(DingTalk)频道插件,支持通过 Stream 模式接收和回复私聊及群聊消息。**无需公网域名,开箱即用!**
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 功能特性
|
|
11
|
+
|
|
12
|
+
- ✅ Stream 模式连接(无需公网域名)
|
|
13
|
+
- ✅ 私聊消息支持(DM)
|
|
14
|
+
- ✅ 群聊消息支持(@机器人)
|
|
15
|
+
- ✅ Pairing 模式访问控制
|
|
16
|
+
- ✅ SessionWebhook 优先回复(35分钟内快速响应)
|
|
17
|
+
- ✅ REST API 兜底回复
|
|
18
|
+
|
|
19
|
+
## 前置要求
|
|
20
|
+
|
|
21
|
+
1. 钉钉企业内部应用(已创建并配置 Stream 模式)
|
|
22
|
+
2. Node.js 环境(clawdbot 已安装)
|
|
23
|
+
3. 应用的 AppKey (clientId) 和 AppSecret (clientSecret)
|
|
24
|
+
|
|
25
|
+
## 快速开始
|
|
26
|
+
|
|
27
|
+
### 方式一:官方安装(推荐)
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# 安装插件
|
|
31
|
+
clawdbot plugins install @yaoyuanchao/dingtalk
|
|
32
|
+
|
|
33
|
+
# 运行交互式配置向导
|
|
34
|
+
clawdbot onboard --channel dingtalk
|
|
35
|
+
|
|
36
|
+
# 启动网关
|
|
37
|
+
clawdbot gateway
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
就这么简单!配置向导会引导你完成所有设置。
|
|
41
|
+
|
|
42
|
+
### 方式二:手动安装
|
|
43
|
+
|
|
44
|
+
如果你想从源码安装或进行开发:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# 克隆或下载源码
|
|
48
|
+
git clone https://github.com/yourusername/dingtalk-clawdbot.git
|
|
49
|
+
|
|
50
|
+
# 本地安装
|
|
51
|
+
cd dingtalk-clawdbot
|
|
52
|
+
npm install
|
|
53
|
+
clawdbot plugins install .
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## 配置说明
|
|
57
|
+
|
|
58
|
+
### 交互式配置(推荐)
|
|
59
|
+
|
|
60
|
+
运行 `clawdbot onboard --channel dingtalk` 启动配置向导,它会:
|
|
61
|
+
|
|
62
|
+
1. ✅ 要求输入 Client ID 和 Client Secret
|
|
63
|
+
2. ✅ 自动测试连接
|
|
64
|
+
3. ✅ 引导选择私聊策略(Pairing/Allowlist/Open/Disabled)
|
|
65
|
+
4. ✅ 引导选择群聊策略(Allowlist/Open/Disabled)
|
|
66
|
+
5. ✅ 自动保存配置到 `~/.clawdbot/clawdbot.json`
|
|
67
|
+
|
|
68
|
+
### 手动配置 clawdbot.json
|
|
69
|
+
|
|
70
|
+
编辑 `~/.clawdbot/clawdbot.json`,添加 DingTalk 频道配置:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"channels": {
|
|
75
|
+
"dingtalk": {
|
|
76
|
+
"enabled": true,
|
|
77
|
+
"clientId": "dingXXXXXXXXXXXXXXXX",
|
|
78
|
+
"clientSecret": "YOUR_APP_SECRET_HERE",
|
|
79
|
+
"dm": {
|
|
80
|
+
"policy": "pairing",
|
|
81
|
+
"allowFrom": ["YOUR_STAFF_ID"]
|
|
82
|
+
},
|
|
83
|
+
"groupPolicy": "allowlist",
|
|
84
|
+
"groupAllowlist": ["cidlnNrtqQ4kGskU56Qni6zTg=="],
|
|
85
|
+
"requireMention": true
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**配置说明**:
|
|
92
|
+
- `clientId`: 钉钉应用的 AppKey
|
|
93
|
+
- `clientSecret`: 钉钉应用的 AppSecret
|
|
94
|
+
- `dm.policy`: 私聊策略
|
|
95
|
+
- `"pairing"`: 需要明确允许的用户才能使用(推荐)
|
|
96
|
+
- `"open"`: 任何人都可以私聊
|
|
97
|
+
- `"disabled"`: 禁用私聊
|
|
98
|
+
- `dm.allowFrom`: 允许私聊的 staffId 列表(当 policy 为 pairing 时生效)
|
|
99
|
+
- `groupPolicy`: 群聊策略
|
|
100
|
+
- `"allowlist"`: 仅允许列表中的群
|
|
101
|
+
- `"open"`: 允许所有群
|
|
102
|
+
- `"disabled"`: 禁用群聊
|
|
103
|
+
- `groupAllowlist`: 允许的群聊 conversationId 列表(当 groupPolicy 为 allowlist 时生效)
|
|
104
|
+
- `requireMention`: 是否要求在群聊中 @机器人(推荐 true)
|
|
105
|
+
- `messageFormat`: 消息格式(可选)
|
|
106
|
+
- `"text"`: 纯文本格式(默认,推荐)
|
|
107
|
+
- `"markdown"`: Markdown 格式(⚠️ 钉钉仅支持有限的 Markdown 语法,**不支持表格**)
|
|
108
|
+
|
|
109
|
+
**Markdown 格式说明**:
|
|
110
|
+
- ✅ 支持:标题、粗体、斜体、链接、图片、引用、列表、代码块
|
|
111
|
+
- ❌ 不支持:**表格**、复杂嵌套列表、HTML 标签、任务列表
|
|
112
|
+
- 📝 自动转换:插件会自动将 Markdown 表格转换为纯文本代码块显示
|
|
113
|
+
- ⏰ 仅 SessionWebhook 可用:REST API 兜底时自动降级为纯文本
|
|
114
|
+
|
|
115
|
+
**配置示例**:
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"messageFormat": "markdown"
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 4. 启动 clawdbot gateway
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# 如果已经在运行,需要重启
|
|
126
|
+
clawdbot gateway restart
|
|
127
|
+
|
|
128
|
+
# 或者首次启动
|
|
129
|
+
clawdbot gateway
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 5. 查看日志验证
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
# 查看实时日志
|
|
136
|
+
tail -f /tmp/clawdbot/clawdbot-$(date +%Y-%m-%d).log | grep dingtalk
|
|
137
|
+
|
|
138
|
+
# 成功的标志:
|
|
139
|
+
# [dingtalk:default] Starting Stream...
|
|
140
|
+
# [dingtalk:default] Stream connected
|
|
141
|
+
# [dingtalk] Stream connection started successfully
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## 获取你的 staffId
|
|
145
|
+
|
|
146
|
+
首次使用时,机器人会告诉你的 staffId:
|
|
147
|
+
|
|
148
|
+
1. 在钉钉中找到机器人
|
|
149
|
+
2. 发送任意消息
|
|
150
|
+
3. 机器人会回复:"Access denied. Your staffId: 050914185922786044 Ask admin to add you."
|
|
151
|
+
4. 将这个 staffId 添加到配置文件的 `dm.allowFrom` 数组中
|
|
152
|
+
5. 重启 gateway
|
|
153
|
+
|
|
154
|
+
## 获取群聊 conversationId
|
|
155
|
+
|
|
156
|
+
如果使用 `groupPolicy: "allowlist"`,需要获取群聊的 conversationId:
|
|
157
|
+
|
|
158
|
+
**方法1: 查看日志**
|
|
159
|
+
1. 在群聊中 @机器人发送消息
|
|
160
|
+
2. 查看 gateway 日志:
|
|
161
|
+
```bash
|
|
162
|
+
tail -f /tmp/clawdbot/clawdbot-$(date +%Y-%m-%d).log | grep "dingtalk.*Group"
|
|
163
|
+
```
|
|
164
|
+
3. 日志会显示:`[dingtalk] Group from XXX: ...` 以及相关的 conversationId
|
|
165
|
+
4. 或者查看日志中的 "Group not in allowlist" 消息获取 conversationId
|
|
166
|
+
|
|
167
|
+
**方法2: 临时设置为 open**
|
|
168
|
+
1. 临时修改配置为 `groupPolicy: "open"`
|
|
169
|
+
2. 重启 gateway
|
|
170
|
+
3. 在群聊中 @机器人发送消息
|
|
171
|
+
4. 查看日志获取 conversationId(格式类似 `cidlnNrtqQ4kGskU56Qni6zTg==`)
|
|
172
|
+
5. 将 conversationId 添加到 `groupAllowlist` 数组
|
|
173
|
+
6. 改回 `groupPolicy: "allowlist"` 并重启
|
|
174
|
+
|
|
175
|
+
**配置示例**:
|
|
176
|
+
```json
|
|
177
|
+
{
|
|
178
|
+
"groupPolicy": "allowlist",
|
|
179
|
+
"groupAllowlist": [
|
|
180
|
+
"cidlnNrtqQ4kGskU56Qni6zTg==",
|
|
181
|
+
"anotherConversationId123=="
|
|
182
|
+
],
|
|
183
|
+
"requireMention": true
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## 钉钉应用配置
|
|
188
|
+
|
|
189
|
+
### 创建应用
|
|
190
|
+
|
|
191
|
+
1. 登录 [钉钉开发者平台](https://open-dev.dingtalk.com)
|
|
192
|
+
2. 进入 **应用开发** → **企业内部开发**
|
|
193
|
+
3. 点击 **创建应用**
|
|
194
|
+
4. 添加 **机器人** 能力
|
|
195
|
+
5. 消息接收模式选择 **Stream 模式**
|
|
196
|
+
|
|
197
|
+
### 配置权限
|
|
198
|
+
|
|
199
|
+
应用需要以下权限:
|
|
200
|
+
- `qyapi_chat_manage`(企业会话管理)
|
|
201
|
+
- `qyapi_robot_sendmsg`(机器人发送消息)
|
|
202
|
+
|
|
203
|
+
### 获取凭证
|
|
204
|
+
|
|
205
|
+
在应用详情页面:
|
|
206
|
+
- **AppKey** → 这是你的 `clientId`
|
|
207
|
+
- **AppSecret** → 点击查看并复制,这是你的 `clientSecret`
|
|
208
|
+
|
|
209
|
+
### 发布应用
|
|
210
|
+
|
|
211
|
+
配置完成后,点击 **发布** 使应用生效。
|
|
212
|
+
|
|
213
|
+
## 故障排查
|
|
214
|
+
|
|
215
|
+
### Stream 连接失败
|
|
216
|
+
|
|
217
|
+
```
|
|
218
|
+
[dingtalk] Failed to start Stream
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**可能原因**:
|
|
222
|
+
1. clientId 或 clientSecret 错误
|
|
223
|
+
2. 应用未选择 Stream 模式
|
|
224
|
+
3. 应用未发布
|
|
225
|
+
|
|
226
|
+
**解决方法**:
|
|
227
|
+
- 检查配置文件中的凭证是否正确
|
|
228
|
+
- 确认钉钉应用已选择 Stream 模式并发布
|
|
229
|
+
|
|
230
|
+
### 发送消息无响应
|
|
231
|
+
|
|
232
|
+
**可能原因**:
|
|
233
|
+
1. staffId 未添加到 allowFrom 列表
|
|
234
|
+
2. 群聊未 @机器人(requireMention 为 true 时)
|
|
235
|
+
|
|
236
|
+
**解决方法**:
|
|
237
|
+
- 查看日志获取 staffId 并添加到配置
|
|
238
|
+
- 在群聊中使用 @机器人名称 来触发
|
|
239
|
+
|
|
240
|
+
### 配置修改未生效
|
|
241
|
+
|
|
242
|
+
**原因**:配置修改需要重启 gateway
|
|
243
|
+
|
|
244
|
+
**解决方法**:
|
|
245
|
+
```bash
|
|
246
|
+
clawdbot gateway restart
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## 技术细节
|
|
250
|
+
|
|
251
|
+
### 核心实现
|
|
252
|
+
|
|
253
|
+
- **Stream SDK**: `dingtalk-stream@2.1.4`
|
|
254
|
+
- **消息处理**: 通过 `DWClient` 建立 WebSocket 长连接
|
|
255
|
+
- **配置加载**: 自动调用 `cfg.loadConfig()` 获取实际配置(重要修复)
|
|
256
|
+
- **回复策略**: SessionWebhook (35分钟内) → REST API (兜底)
|
|
257
|
+
|
|
258
|
+
### 关键文件
|
|
259
|
+
|
|
260
|
+
| 文件 | 作用 |
|
|
261
|
+
|------|------|
|
|
262
|
+
| `src/monitor.ts` | Stream 连接 + 消息处理核心逻辑 |
|
|
263
|
+
| `src/api.ts` | 钉钉 REST API 封装 |
|
|
264
|
+
| `src/channel.ts` | ChannelPlugin 接口实现 |
|
|
265
|
+
| `src/accounts.ts` | 账号凭证解析 |
|
|
266
|
+
|
|
267
|
+
### 已知限制
|
|
268
|
+
|
|
269
|
+
- **Markdown 有限支持**:钉钉不支持 Markdown 表格,插件会自动转换为纯文本代码块
|
|
270
|
+
- **消息格式**:默认纯文本,可选 Markdown(但有语法限制)
|
|
271
|
+
- **媒体消息**:暂不支持文件、图片等媒体消息发送
|
|
272
|
+
- **钉钉限流**:20条/分钟/群,超限后10分钟限流
|
|
273
|
+
|
|
274
|
+
## 更新日志
|
|
275
|
+
|
|
276
|
+
查看 [CHANGELOG.md](./CHANGELOG.md) 获取详细的版本历史。
|
|
277
|
+
|
|
278
|
+
### v1.2.0 (2026-01-28) - 官方插件发布
|
|
279
|
+
|
|
280
|
+
- ✅ **官方 NPM 安装支持** - `clawdbot plugins install @yaoyuanchao/dingtalk`
|
|
281
|
+
- ✅ **交互式配置向导** - `clawdbot onboard --channel dingtalk`
|
|
282
|
+
- ✅ **Zod 配置验证** - 类型安全、自动错误提示
|
|
283
|
+
- ✅ **健康检查** - 自动探测连接状态和延迟
|
|
284
|
+
- ✅ 保留所有 v0.1.0 功能
|
|
285
|
+
|
|
286
|
+
### v0.1.0 (2026-01-26)
|
|
287
|
+
|
|
288
|
+
- ✅ 初始版本发布
|
|
289
|
+
- ✅ Stream 模式连接
|
|
290
|
+
- ✅ 私聊 + 群聊支持
|
|
291
|
+
- ✅ Pairing 访问控制
|
|
292
|
+
- ✅ 群聊白名单(groupAllowlist)
|
|
293
|
+
- ✅ Markdown 消息格式支持(自动转换表格为纯文本)
|
|
294
|
+
|
|
295
|
+
## 许可证
|
|
296
|
+
|
|
297
|
+
MIT License - 查看 [LICENSE](./LICENSE) 文件获取详细信息。
|
|
298
|
+
|
|
299
|
+
## 贡献
|
|
300
|
+
|
|
301
|
+
发现问题或有改进建议?欢迎提交 Issue 或 Pull Request。
|
package/index.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { dingtalkPlugin } from "./src/channel.js";
|
|
2
|
+
import { setDingTalkRuntime } from "./src/runtime.js";
|
|
3
|
+
import { dingTalkConfigSchema } from "./src/config-schema.js";
|
|
4
|
+
|
|
5
|
+
const plugin = {
|
|
6
|
+
id: "dingtalk",
|
|
7
|
+
name: "DingTalk",
|
|
8
|
+
description: "DingTalk channel plugin with Stream Mode support",
|
|
9
|
+
configSchema: dingTalkConfigSchema,
|
|
10
|
+
register(api: any) {
|
|
11
|
+
setDingTalkRuntime(api.runtime);
|
|
12
|
+
api.registerChannel({ plugin: dingtalkPlugin });
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default plugin;
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@yaoyuanchao/dingtalk",
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "DingTalk channel plugin for Clawdbot with Stream Mode support",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Yao Yuanchao",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"clawdbot",
|
|
10
|
+
"dingtalk",
|
|
11
|
+
"channel-plugin",
|
|
12
|
+
"stream-mode",
|
|
13
|
+
"ai-assistant"
|
|
14
|
+
],
|
|
15
|
+
"clawdbot": {
|
|
16
|
+
"extensions": [
|
|
17
|
+
"./index.ts"
|
|
18
|
+
],
|
|
19
|
+
"channel": {
|
|
20
|
+
"id": "dingtalk",
|
|
21
|
+
"label": "DingTalk",
|
|
22
|
+
"selectionLabel": "DingTalk (钉钉)",
|
|
23
|
+
"detailLabel": "DingTalk Stream Mode",
|
|
24
|
+
"docsPath": "/channels/dingtalk",
|
|
25
|
+
"docsLabel": "dingtalk",
|
|
26
|
+
"blurb": "DingTalk bot via Stream Mode (WebSocket) - No public domain required",
|
|
27
|
+
"aliases": [
|
|
28
|
+
"dingding",
|
|
29
|
+
"dd"
|
|
30
|
+
],
|
|
31
|
+
"order": 75,
|
|
32
|
+
"quickstartAllowFrom": true
|
|
33
|
+
},
|
|
34
|
+
"install": {
|
|
35
|
+
"npmSpec": "@yaoyuanchao/dingtalk",
|
|
36
|
+
"localPath": ".",
|
|
37
|
+
"defaultChoice": "npm"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"dingtalk-stream": "^2.1.4",
|
|
42
|
+
"zod": "^3.22.0"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"clawdbot": ">=2026.1.24"
|
|
46
|
+
},
|
|
47
|
+
"files": [
|
|
48
|
+
"index.ts",
|
|
49
|
+
"src/**/*.ts",
|
|
50
|
+
"clawdbot.plugin.json",
|
|
51
|
+
"README.md",
|
|
52
|
+
"CHANGELOG.md",
|
|
53
|
+
"LICENSE"
|
|
54
|
+
]
|
|
55
|
+
}
|
package/src/accounts.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { ResolvedDingTalkAccount, DingTalkChannelConfig } from "./types.js";
|
|
2
|
+
import { validateDingTalkConfig, type DingTalkConfig } from "./config-schema.js";
|
|
3
|
+
|
|
4
|
+
const DEFAULT_ACCOUNT_ID = "default";
|
|
5
|
+
const ENV_CLIENT_ID = "DINGTALK_CLIENT_ID";
|
|
6
|
+
const ENV_CLIENT_SECRET = "DINGTALK_CLIENT_SECRET";
|
|
7
|
+
const ENV_ROBOT_CODE = "DINGTALK_ROBOT_CODE";
|
|
8
|
+
|
|
9
|
+
export function listDingTalkAccountIds(cfg: any): string[] {
|
|
10
|
+
const channel = cfg?.channels?.dingtalk as DingTalkChannelConfig | undefined;
|
|
11
|
+
if (!channel) return [DEFAULT_ACCOUNT_ID];
|
|
12
|
+
return [DEFAULT_ACCOUNT_ID];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function resolveDefaultDingTalkAccountId(cfg: any): string {
|
|
16
|
+
return DEFAULT_ACCOUNT_ID;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function resolveDingTalkAccount(params: {
|
|
20
|
+
cfg: any;
|
|
21
|
+
accountId?: string | null;
|
|
22
|
+
}): ResolvedDingTalkAccount {
|
|
23
|
+
const accountId = params.accountId?.trim() || DEFAULT_ACCOUNT_ID;
|
|
24
|
+
const rawConfig = params.cfg?.channels?.dingtalk ?? {};
|
|
25
|
+
|
|
26
|
+
// Merge configuration with environment variables
|
|
27
|
+
// Environment variables serve as fallback for missing config values
|
|
28
|
+
const configWithEnv = {
|
|
29
|
+
...rawConfig,
|
|
30
|
+
clientId: rawConfig.clientId || process.env[ENV_CLIENT_ID],
|
|
31
|
+
clientSecret: rawConfig.clientSecret || process.env[ENV_CLIENT_SECRET],
|
|
32
|
+
robotCode: rawConfig.robotCode || process.env[ENV_ROBOT_CODE],
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// Validate configuration with Zod
|
|
36
|
+
let validatedConfig: DingTalkConfig;
|
|
37
|
+
let credentialSource: "config" | "env" | "none" = "none";
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
validatedConfig = validateDingTalkConfig(configWithEnv);
|
|
41
|
+
|
|
42
|
+
// Determine credential source
|
|
43
|
+
if (rawConfig.clientId && rawConfig.clientSecret) {
|
|
44
|
+
credentialSource = "config";
|
|
45
|
+
} else if (validatedConfig.clientId && validatedConfig.clientSecret) {
|
|
46
|
+
credentialSource = "env";
|
|
47
|
+
}
|
|
48
|
+
} catch (error) {
|
|
49
|
+
// If validation fails, throw a helpful error message
|
|
50
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
51
|
+
throw new Error(
|
|
52
|
+
`DingTalk configuration validation failed for account "${accountId}":\n${errorMsg}\n\n` +
|
|
53
|
+
`Please check your configuration at channels.dingtalk or set environment variables:\n` +
|
|
54
|
+
` - ${ENV_CLIENT_ID}\n` +
|
|
55
|
+
` - ${ENV_CLIENT_SECRET}\n` +
|
|
56
|
+
` - ${ENV_ROBOT_CODE} (optional)`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const configured = !!(validatedConfig.clientId && validatedConfig.clientSecret);
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
accountId,
|
|
64
|
+
name: "DingTalk Bot",
|
|
65
|
+
enabled: validatedConfig.enabled,
|
|
66
|
+
configured,
|
|
67
|
+
clientId: validatedConfig.clientId,
|
|
68
|
+
clientSecret: validatedConfig.clientSecret,
|
|
69
|
+
robotCode: validatedConfig.robotCode || validatedConfig.clientId,
|
|
70
|
+
credentialSource,
|
|
71
|
+
config: validatedConfig as unknown as Record<string, any>,
|
|
72
|
+
};
|
|
73
|
+
}
|