openclaw-channel-dingtalk 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +174 -0
- package/dist/index.d.ts +64 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +503 -0
- package/dist/index.js.map +1 -0
- package/openclaw.plugin.json +63 -0
- package/package.json +70 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024
|
|
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,174 @@
|
|
|
1
|
+
# @openclaw/channel-dingtalk
|
|
2
|
+
|
|
3
|
+
钉钉 Stream 机器人 Channel 插件 for OpenClaw - 支持 Markdown、ActionCard 和双向实时通信。
|
|
4
|
+
|
|
5
|
+
## ✨ 功能特性
|
|
6
|
+
|
|
7
|
+
- 🔄 **Stream 实时通信** - 基于钉钉 Stream SDK 的双向实时消息
|
|
8
|
+
- 📝 **Markdown 支持** - 自动检测并渲染 Markdown 格式消息
|
|
9
|
+
- 🃏 **ActionCard 卡片** - 支持带交互按钮的卡片消息
|
|
10
|
+
- 👥 **群聊 & 私聊** - 同时支持群聊和单聊场景
|
|
11
|
+
- 🔐 **自动 Token 管理** - Access Token 自动获取和缓存
|
|
12
|
+
- 🔁 **消息去重** - 内置消息去重机制,防止重复处理
|
|
13
|
+
- 📱 **多账号支持** - 可配置多个钉钉机器人账号
|
|
14
|
+
|
|
15
|
+
## 📦 安装
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @openclaw/channel-dingtalk
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
或者使用 OpenClaw CLI:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
openclaw plugin install @openclaw/channel-dingtalk
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## ⚙️ 配置
|
|
28
|
+
|
|
29
|
+
在 OpenClaw 配置文件 `~/.openclaw/openclaw.json` 中添加:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"channels": {
|
|
34
|
+
"dingtalk": {
|
|
35
|
+
"accounts": {
|
|
36
|
+
"default": {
|
|
37
|
+
"enabled": true,
|
|
38
|
+
"clientId": "YOUR_APP_KEY",
|
|
39
|
+
"clientSecret": "YOUR_APP_SECRET",
|
|
40
|
+
"robotCode": "YOUR_ROBOT_CODE"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"plugins": {
|
|
46
|
+
"load": {
|
|
47
|
+
"paths": ["node_modules/@openclaw/channel-dingtalk"]
|
|
48
|
+
},
|
|
49
|
+
"entries": {
|
|
50
|
+
"dingtalk": {
|
|
51
|
+
"enabled": true
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 获取凭证
|
|
59
|
+
|
|
60
|
+
1. 登录 [钉钉开放平台](https://open.dingtalk.com/)
|
|
61
|
+
2. 创建企业内部应用或第三方应用
|
|
62
|
+
3. 在应用中创建机器人
|
|
63
|
+
4. 获取以下信息:
|
|
64
|
+
- **clientId (AppKey)**: 应用凭证中的 AppKey
|
|
65
|
+
- **clientSecret (AppSecret)**: 应用凭证中的 AppSecret
|
|
66
|
+
- **robotCode**: 机器人的唯一标识
|
|
67
|
+
|
|
68
|
+
## 🚀 使用
|
|
69
|
+
|
|
70
|
+
### 基本消息
|
|
71
|
+
|
|
72
|
+
配置完成后,机器人会自动接收和响应消息。发送包含 Markdown 语法的回复会自动渲染:
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
**粗体** *斜体* `代码`
|
|
76
|
+
- 列表项1
|
|
77
|
+
- 列表项2
|
|
78
|
+
[链接](https://example.com)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### ActionCard 卡片消息
|
|
82
|
+
|
|
83
|
+
通过编程方式发送带按钮的卡片:
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
import { sendGroupActionCard } from '@openclaw/channel-dingtalk';
|
|
87
|
+
|
|
88
|
+
await sendGroupActionCard(accessToken, conversationId, {
|
|
89
|
+
title: "任务通知",
|
|
90
|
+
text: "您有一个新任务需要处理",
|
|
91
|
+
buttons: [
|
|
92
|
+
{ title: "查看详情", actionURL: "https://example.com/task/1" },
|
|
93
|
+
{ title: "稍后处理", actionURL: "https://example.com/later" }
|
|
94
|
+
]
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 检查状态
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
openclaw channel status dingtalk
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## 📋 消息格式
|
|
105
|
+
|
|
106
|
+
| 格式 | 自动检测 | 说明 |
|
|
107
|
+
|------|----------|------|
|
|
108
|
+
| 纯文本 | ✅ | 普通文本消息 |
|
|
109
|
+
| Markdown | ✅ | 检测到 `**`, `#`, `-` 等语法自动使用 |
|
|
110
|
+
| ActionCard | ❌ | 需通过 API 显式调用 |
|
|
111
|
+
|
|
112
|
+
### 支持的 Markdown 语法
|
|
113
|
+
|
|
114
|
+
- `# 标题` - 一至六级标题
|
|
115
|
+
- `**粗体**` - 加粗文本
|
|
116
|
+
- `*斜体*` - 斜体文本
|
|
117
|
+
- `[链接](url)` - 超链接
|
|
118
|
+
- `- 列表` - 无序列表
|
|
119
|
+
- `1. 列表` - 有序列表
|
|
120
|
+
- `> 引用` - 引用块
|
|
121
|
+
- `` `代码` `` - 行内代码
|
|
122
|
+
|
|
123
|
+
## 🔒 安全提示
|
|
124
|
+
|
|
125
|
+
- ⚠️ `clientSecret` 是敏感信息,请妥善保管
|
|
126
|
+
- 建议使用环境变量注入敏感配置
|
|
127
|
+
- 不要将包含真实密钥的配置文件提交到版本控制
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
# 使用环境变量
|
|
131
|
+
export DINGTALK_CLIENT_SECRET="your-secret"
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## 📖 API 参考
|
|
135
|
+
|
|
136
|
+
### 导出函数
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
// 检测消息格式
|
|
140
|
+
function detectMessageFormat(text: string): 'text' | 'markdown';
|
|
141
|
+
|
|
142
|
+
// 构建消息体
|
|
143
|
+
function buildMessageBody(content: string, forceFormat?: 'text' | 'markdown'): object;
|
|
144
|
+
|
|
145
|
+
// 构建 ActionCard 消息体
|
|
146
|
+
function buildActionCardBody(options: ActionCardOptions): object;
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### ActionCardOptions
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
interface ActionCardOptions {
|
|
153
|
+
title: string; // 卡片标题
|
|
154
|
+
text: string; // 卡片内容 (支持 Markdown)
|
|
155
|
+
singleTitle?: string; // 单按钮标题
|
|
156
|
+
singleURL?: string; // 单按钮链接
|
|
157
|
+
buttons?: Array<{ // 多按钮配置
|
|
158
|
+
title: string;
|
|
159
|
+
actionURL: string;
|
|
160
|
+
}>;
|
|
161
|
+
btnOrientation?: '0' | '1'; // 按钮排列: 0=竖向, 1=横向
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## 📚 参考文档
|
|
166
|
+
|
|
167
|
+
- [钉钉开放平台](https://open.dingtalk.com/)
|
|
168
|
+
- [Stream 模式接入](https://open.dingtalk.com/document/orgapp/stream-mode-introduction)
|
|
169
|
+
- [机器人消息类型](https://open.dingtalk.com/document/orgapp/robot-message-types-and-data-format)
|
|
170
|
+
- [OpenClaw 文档](https://openclaw.dev/)
|
|
171
|
+
|
|
172
|
+
## 📄 License
|
|
173
|
+
|
|
174
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
interface ChannelPlugin {
|
|
2
|
+
id: string;
|
|
3
|
+
meta: {
|
|
4
|
+
id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
selectionLabel: string;
|
|
7
|
+
docsPath: string;
|
|
8
|
+
blurb: string;
|
|
9
|
+
aliases: string[];
|
|
10
|
+
};
|
|
11
|
+
capabilities: {
|
|
12
|
+
chatTypes: string[];
|
|
13
|
+
canReply: boolean;
|
|
14
|
+
canEdit: boolean;
|
|
15
|
+
canDelete: boolean;
|
|
16
|
+
supportsImages: boolean;
|
|
17
|
+
supportsFiles: boolean;
|
|
18
|
+
supportsVoice: boolean;
|
|
19
|
+
};
|
|
20
|
+
config: {
|
|
21
|
+
listAccountIds: (cfg: any) => string[];
|
|
22
|
+
resolveAccount: (cfg: any, accountId?: string) => any;
|
|
23
|
+
};
|
|
24
|
+
gateway?: any;
|
|
25
|
+
outbound?: any;
|
|
26
|
+
setup?: any;
|
|
27
|
+
status?: any;
|
|
28
|
+
}
|
|
29
|
+
type MessageFormat = "text" | "markdown";
|
|
30
|
+
interface ActionCardOptions {
|
|
31
|
+
title: string;
|
|
32
|
+
text: string;
|
|
33
|
+
buttons?: Array<{
|
|
34
|
+
title: string;
|
|
35
|
+
actionURL: string;
|
|
36
|
+
}>;
|
|
37
|
+
singleTitle?: string;
|
|
38
|
+
singleURL?: string;
|
|
39
|
+
btnOrientation?: "0" | "1";
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 检测文本内容是否包含 Markdown 语法
|
|
43
|
+
*/
|
|
44
|
+
declare function detectMessageFormat(text: string): MessageFormat;
|
|
45
|
+
/**
|
|
46
|
+
* 构建消息体 - 根据内容自动选择格式
|
|
47
|
+
*/
|
|
48
|
+
declare function buildMessageBody(content: string, forceFormat?: MessageFormat): object;
|
|
49
|
+
/**
|
|
50
|
+
* 构建 ActionCard 消息体
|
|
51
|
+
*/
|
|
52
|
+
declare function buildActionCardBody(options: ActionCardOptions): object;
|
|
53
|
+
declare const dingtalkPlugin: ChannelPlugin;
|
|
54
|
+
export default function register(api: {
|
|
55
|
+
registerChannel: (opts: {
|
|
56
|
+
plugin: typeof dingtalkPlugin;
|
|
57
|
+
}) => void;
|
|
58
|
+
logger: {
|
|
59
|
+
info: (msg: string) => void;
|
|
60
|
+
error: (msg: string) => void;
|
|
61
|
+
};
|
|
62
|
+
}): void;
|
|
63
|
+
export { detectMessageFormat, buildMessageBody, buildActionCardBody, type MessageFormat, type ActionCardOptions, };
|
|
64
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AACA,UAAU,aAAa;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,cAAc,EAAE,MAAM,CAAC;QACvB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,EAAE,CAAC;KACnB,CAAC;IACF,YAAY,EAAE;QACZ,SAAS,EAAE,MAAM,EAAE,CAAC;QACpB,QAAQ,EAAE,OAAO,CAAC;QAClB,OAAO,EAAE,OAAO,CAAC;QACjB,SAAS,EAAE,OAAO,CAAC;QACnB,cAAc,EAAE,OAAO,CAAC;QACxB,aAAa,EAAE,OAAO,CAAC;QACvB,aAAa,EAAE,OAAO,CAAC;KACxB,CAAC;IACF,MAAM,EAAE;QACN,cAAc,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,MAAM,EAAE,CAAC;QACvC,cAAc,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,GAAG,CAAC;KACvD,CAAC;IACF,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,MAAM,CAAC,EAAE,GAAG,CAAC;CACd;AAyBD,KAAK,aAAa,GAAG,MAAM,GAAG,UAAU,CAAC;AAGzC,UAAU,iBAAiB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC;CAC5B;AAED;;GAEG;AACH,iBAAS,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAYxD;AAED;;GAEG;AACH,iBAAS,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,aAAa,GAAG,MAAM,CAmB9E;AAED;;GAEG;AACH,iBAAS,mBAAmB,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAoB/D;AAiLD,QAAA,MAAM,cAAc,EAAE,aA0WrB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,GAAG,EAAE;IACpC,eAAe,EAAE,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,OAAO,cAAc,CAAA;KAAE,KAAK,IAAI,CAAC;IACnE,MAAM,EAAE;QAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;CACvE,QAGA;AAGD,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,EACnB,KAAK,aAAa,EAClB,KAAK,iBAAiB,GACvB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
import { DWClient, EventAck } from "dingtalk-stream";
|
|
2
|
+
/**
|
|
3
|
+
* 检测文本内容是否包含 Markdown 语法
|
|
4
|
+
*/
|
|
5
|
+
function detectMessageFormat(text) {
|
|
6
|
+
const markdownPatterns = [
|
|
7
|
+
/^#{1,6}\s/m, // 标题
|
|
8
|
+
/\*\*.+?\*\*/, // 粗体
|
|
9
|
+
/\[.+?\]\(.+?\)/, // 链接
|
|
10
|
+
/^\s*[-*]\s/m, // 无序列表
|
|
11
|
+
/^\s*\d+\.\s/m, // 有序列表
|
|
12
|
+
/^>/m, // 引用
|
|
13
|
+
/```[\s\S]*?```/, // 代码块
|
|
14
|
+
/`[^`]+`/, // 行内代码
|
|
15
|
+
];
|
|
16
|
+
return markdownPatterns.some(p => p.test(text)) ? "markdown" : "text";
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 构建消息体 - 根据内容自动选择格式
|
|
20
|
+
*/
|
|
21
|
+
function buildMessageBody(content, forceFormat) {
|
|
22
|
+
const format = forceFormat || detectMessageFormat(content);
|
|
23
|
+
if (format === "markdown") {
|
|
24
|
+
// 提取第一行作为标题(钉钉通知栏显示)
|
|
25
|
+
const firstLine = content.split('\n')[0].replace(/^#+\s*/, '').slice(0, 20);
|
|
26
|
+
return {
|
|
27
|
+
msgtype: "markdown",
|
|
28
|
+
markdown: {
|
|
29
|
+
title: firstLine || "消息",
|
|
30
|
+
text: content,
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
msgtype: "text",
|
|
36
|
+
text: { content },
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* 构建 ActionCard 消息体
|
|
41
|
+
*/
|
|
42
|
+
function buildActionCardBody(options) {
|
|
43
|
+
const actionCard = {
|
|
44
|
+
title: options.title,
|
|
45
|
+
text: options.text,
|
|
46
|
+
};
|
|
47
|
+
if (options.buttons && options.buttons.length > 0) {
|
|
48
|
+
// 多按钮模式
|
|
49
|
+
actionCard.btnOrientation = options.btnOrientation || "0";
|
|
50
|
+
actionCard.btns = options.buttons;
|
|
51
|
+
}
|
|
52
|
+
else if (options.singleTitle && options.singleURL) {
|
|
53
|
+
// 单按钮模式
|
|
54
|
+
actionCard.singleTitle = options.singleTitle;
|
|
55
|
+
actionCard.singleURL = options.singleURL;
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
msgtype: "actionCard",
|
|
59
|
+
actionCard,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
// Token 缓存
|
|
63
|
+
const tokenCache = new Map();
|
|
64
|
+
async function getAccessToken(clientId, clientSecret) {
|
|
65
|
+
const cacheKey = `${clientId}`;
|
|
66
|
+
const cached = tokenCache.get(cacheKey);
|
|
67
|
+
if (cached && cached.expiresAt > Date.now() + 5 * 60 * 1000) {
|
|
68
|
+
return cached.token;
|
|
69
|
+
}
|
|
70
|
+
const response = await fetch("https://api.dingtalk.com/v1.0/oauth2/accessToken", {
|
|
71
|
+
method: "POST",
|
|
72
|
+
headers: { "Content-Type": "application/json" },
|
|
73
|
+
body: JSON.stringify({ appKey: clientId, appSecret: clientSecret }),
|
|
74
|
+
});
|
|
75
|
+
if (!response.ok) {
|
|
76
|
+
const error = await response.text();
|
|
77
|
+
throw new Error(`Failed to get access token: ${response.status} ${error}`);
|
|
78
|
+
}
|
|
79
|
+
const data = (await response.json());
|
|
80
|
+
tokenCache.set(cacheKey, {
|
|
81
|
+
token: data.accessToken,
|
|
82
|
+
expiresAt: Date.now() + data.expireIn * 1000,
|
|
83
|
+
});
|
|
84
|
+
return data.accessToken;
|
|
85
|
+
}
|
|
86
|
+
async function sendGroupMessage(accessToken, openConversationId, content, format) {
|
|
87
|
+
const detectedFormat = format || detectMessageFormat(content);
|
|
88
|
+
const isMarkdown = detectedFormat === "markdown";
|
|
89
|
+
// 钉钉 API 使用 msgKey 区分消息类型:sampleText / sampleMarkdown
|
|
90
|
+
const msgKey = isMarkdown ? "sampleMarkdown" : "sampleText";
|
|
91
|
+
const msgParam = isMarkdown
|
|
92
|
+
? { title: content.split('\n')[0].replace(/^#+\s*/, '').slice(0, 20) || "消息", text: content }
|
|
93
|
+
: { content };
|
|
94
|
+
const response = await fetch("https://api.dingtalk.com/v1.0/robot/groupMessages/send", {
|
|
95
|
+
method: "POST",
|
|
96
|
+
headers: {
|
|
97
|
+
"Content-Type": "application/json",
|
|
98
|
+
"x-acs-dingtalk-access-token": accessToken,
|
|
99
|
+
},
|
|
100
|
+
body: JSON.stringify({
|
|
101
|
+
msgParam: JSON.stringify(msgParam),
|
|
102
|
+
msgKey,
|
|
103
|
+
openConversationId,
|
|
104
|
+
}),
|
|
105
|
+
});
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
const error = await response.text();
|
|
108
|
+
throw new Error(`Failed to send message: ${response.status} ${error}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async function sendPrivateMessage(accessToken, robotCode, userId, content, format) {
|
|
112
|
+
const detectedFormat = format || detectMessageFormat(content);
|
|
113
|
+
const isMarkdown = detectedFormat === "markdown";
|
|
114
|
+
const msgKey = isMarkdown ? "sampleMarkdown" : "sampleText";
|
|
115
|
+
const msgParam = isMarkdown
|
|
116
|
+
? { title: content.split('\n')[0].replace(/^#+\s*/, '').slice(0, 20) || "消息", text: content }
|
|
117
|
+
: { content };
|
|
118
|
+
const response = await fetch("https://api.dingtalk.com/v1.0/robot/oToMessages/batchSend", {
|
|
119
|
+
method: "POST",
|
|
120
|
+
headers: {
|
|
121
|
+
"Content-Type": "application/json",
|
|
122
|
+
"x-acs-dingtalk-access-token": accessToken,
|
|
123
|
+
},
|
|
124
|
+
body: JSON.stringify({
|
|
125
|
+
robotCode,
|
|
126
|
+
userIds: [userId],
|
|
127
|
+
msgKey,
|
|
128
|
+
msgParam: JSON.stringify(msgParam),
|
|
129
|
+
}),
|
|
130
|
+
});
|
|
131
|
+
if (!response.ok) {
|
|
132
|
+
const error = await response.text();
|
|
133
|
+
throw new Error(`Failed to send private message: ${response.status} ${error}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* 发送 ActionCard 卡片消息到群
|
|
138
|
+
*/
|
|
139
|
+
async function sendGroupActionCard(accessToken, openConversationId, options) {
|
|
140
|
+
// 钉钉机器人 API 对 ActionCard 使用 sampleActionCard / sampleActionCard2 等 msgKey
|
|
141
|
+
const hasBtns = options.buttons && options.buttons.length > 0;
|
|
142
|
+
const msgKey = hasBtns ? "sampleActionCard2" : "sampleActionCard";
|
|
143
|
+
const msgParam = {
|
|
144
|
+
title: options.title,
|
|
145
|
+
text: options.text,
|
|
146
|
+
};
|
|
147
|
+
if (hasBtns) {
|
|
148
|
+
msgParam.actionTitle1 = options.buttons[0].title;
|
|
149
|
+
msgParam.actionUrl1 = options.buttons[0].actionURL;
|
|
150
|
+
if (options.buttons.length > 1) {
|
|
151
|
+
msgParam.actionTitle2 = options.buttons[1].title;
|
|
152
|
+
msgParam.actionUrl2 = options.buttons[1].actionURL;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
msgParam.singleTitle = options.singleTitle || "查看详情";
|
|
157
|
+
msgParam.singleUrl = options.singleURL || "";
|
|
158
|
+
}
|
|
159
|
+
const response = await fetch("https://api.dingtalk.com/v1.0/robot/groupMessages/send", {
|
|
160
|
+
method: "POST",
|
|
161
|
+
headers: {
|
|
162
|
+
"Content-Type": "application/json",
|
|
163
|
+
"x-acs-dingtalk-access-token": accessToken,
|
|
164
|
+
},
|
|
165
|
+
body: JSON.stringify({
|
|
166
|
+
msgParam: JSON.stringify(msgParam),
|
|
167
|
+
msgKey,
|
|
168
|
+
openConversationId,
|
|
169
|
+
}),
|
|
170
|
+
});
|
|
171
|
+
if (!response.ok) {
|
|
172
|
+
const error = await response.text();
|
|
173
|
+
throw new Error(`Failed to send action card: ${response.status} ${error}`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Stream 客户端管理
|
|
177
|
+
const streamClients = new Map();
|
|
178
|
+
// 消息去重缓存 (messageId -> timestamp)
|
|
179
|
+
const processedMessages = new Map();
|
|
180
|
+
const DEDUP_WINDOW_MS = 5 * 60 * 1000; // 5分钟去重窗口 (钉钉可能在60秒后重试)
|
|
181
|
+
// 清理过期消息 ID
|
|
182
|
+
function cleanupOldMessageIds() {
|
|
183
|
+
const now = Date.now();
|
|
184
|
+
for (const [msgId, timestamp] of processedMessages) {
|
|
185
|
+
if (now - timestamp > DEDUP_WINDOW_MS) {
|
|
186
|
+
processedMessages.delete(msgId);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
// 检查消息是否已处理
|
|
191
|
+
function isDuplicateMessage(msgId) {
|
|
192
|
+
cleanupOldMessageIds();
|
|
193
|
+
if (processedMessages.has(msgId)) {
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
processedMessages.set(msgId, Date.now());
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
const dingtalkPlugin = {
|
|
200
|
+
id: "dingtalk",
|
|
201
|
+
meta: {
|
|
202
|
+
id: "dingtalk",
|
|
203
|
+
label: "钉钉机器人",
|
|
204
|
+
selectionLabel: "钉钉机器人 (Dingtalk)",
|
|
205
|
+
docsPath: "/channels/dingtalk",
|
|
206
|
+
blurb: "通过钉钉 Stream SDK 接收和发送消息",
|
|
207
|
+
aliases: ["dt", "dingding"],
|
|
208
|
+
},
|
|
209
|
+
capabilities: {
|
|
210
|
+
chatTypes: ["group", "dm"],
|
|
211
|
+
canReply: true,
|
|
212
|
+
canEdit: false,
|
|
213
|
+
canDelete: false,
|
|
214
|
+
supportsImages: false,
|
|
215
|
+
supportsFiles: false,
|
|
216
|
+
supportsVoice: false,
|
|
217
|
+
},
|
|
218
|
+
config: {
|
|
219
|
+
listAccountIds: (cfg) => {
|
|
220
|
+
const config = cfg.channels?.dingtalk;
|
|
221
|
+
if (!config?.accounts)
|
|
222
|
+
return [];
|
|
223
|
+
return Object.entries(config.accounts)
|
|
224
|
+
.filter(([, acc]) => acc.enabled !== false)
|
|
225
|
+
.map(([id]) => id);
|
|
226
|
+
},
|
|
227
|
+
resolveAccount: (cfg, accountId = "default") => {
|
|
228
|
+
const config = cfg.channels?.dingtalk;
|
|
229
|
+
const account = config?.accounts?.[accountId];
|
|
230
|
+
if (!account)
|
|
231
|
+
return { accountId };
|
|
232
|
+
return {
|
|
233
|
+
accountId,
|
|
234
|
+
enabled: account.enabled !== false,
|
|
235
|
+
configured: Boolean(account.clientId && account.clientSecret),
|
|
236
|
+
};
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
// Gateway 管理 - 启动 Stream 连接
|
|
240
|
+
gateway: {
|
|
241
|
+
startAccount: async (ctx) => {
|
|
242
|
+
const { account, log, cfg } = ctx;
|
|
243
|
+
const accountId = account.accountId;
|
|
244
|
+
log?.info(`[dingtalk:${accountId}] startAccount called`);
|
|
245
|
+
log?.info(`[dingtalk:${accountId}] ctx keys: ${JSON.stringify(Object.keys(ctx))}`);
|
|
246
|
+
log?.info(`[dingtalk:${accountId}] runtime: ${JSON.stringify(Object.keys(ctx.runtime || {}))}`);
|
|
247
|
+
log?.info(`[dingtalk:${accountId}] account object: ${JSON.stringify(Object.keys(account))}`);
|
|
248
|
+
log?.info(`[dingtalk:${accountId}] cfg channels: ${JSON.stringify(Object.keys(cfg.channels || {}))}`);
|
|
249
|
+
// 直接从配置读取账号信息
|
|
250
|
+
const channelConfig = cfg.channels?.dingtalk;
|
|
251
|
+
log?.info(`[dingtalk:${accountId}] channelConfig accounts: ${JSON.stringify(Object.keys(channelConfig?.accounts || {}))}`);
|
|
252
|
+
const config = channelConfig?.accounts?.[accountId];
|
|
253
|
+
log?.info(`[dingtalk:${accountId}] config: ${config ? 'found' : 'NOT FOUND'}`);
|
|
254
|
+
if (!config?.clientId || !config?.clientSecret) {
|
|
255
|
+
log?.error(`[dingtalk:${accountId}] Missing credentials. clientId: ${!!config?.clientId}, clientSecret: ${!!config?.clientSecret}`);
|
|
256
|
+
throw new Error(`Account ${accountId}: missing clientId or clientSecret`);
|
|
257
|
+
}
|
|
258
|
+
log?.info(`[dingtalk:${accountId}] Starting Stream connection with clientId: ${config.clientId.slice(0, 8)}...`);
|
|
259
|
+
const client = new DWClient({
|
|
260
|
+
clientId: config.clientId,
|
|
261
|
+
clientSecret: config.clientSecret,
|
|
262
|
+
});
|
|
263
|
+
// 连接事件
|
|
264
|
+
client.on("connect", () => {
|
|
265
|
+
log?.info(`[dingtalk:${accountId}] Stream connected`);
|
|
266
|
+
});
|
|
267
|
+
client.on("disconnect", () => {
|
|
268
|
+
log?.info(`[dingtalk:${accountId}] Stream disconnected`);
|
|
269
|
+
});
|
|
270
|
+
client.on("error", (error) => {
|
|
271
|
+
log?.error(`[dingtalk:${accountId}] Stream error: ${error.message}`);
|
|
272
|
+
});
|
|
273
|
+
// 注册消息监听
|
|
274
|
+
client.registerCallbackListener("/v1.0/im/bot/messages/get", async (message) => {
|
|
275
|
+
// ⚡ 立即返回 SUCCESS,防止钉钉重试
|
|
276
|
+
// 消息处理改为异步
|
|
277
|
+
(async () => {
|
|
278
|
+
try {
|
|
279
|
+
log?.info(`[dingtalk:${accountId}] Received message callback: ${JSON.stringify(message).slice(0, 200)}...`);
|
|
280
|
+
log?.info(`[dingtalk:${accountId}] Raw message.data: ${message.data?.slice(0, 500)}...`);
|
|
281
|
+
const data = JSON.parse(message.data);
|
|
282
|
+
// 使用钉钉消息的 msgId 进行去重(这是实际的聊天消息 ID)
|
|
283
|
+
const dedupId = data.msgId || `${data.conversationId}-${data.createAt}-${data.senderStaffId}`;
|
|
284
|
+
if (isDuplicateMessage(dedupId)) {
|
|
285
|
+
log?.info(`[dingtalk:${accountId}] Duplicate message ignored: ${dedupId}`);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
log?.info(`[dingtalk:${accountId}] Parsed data: msgtype=${data.msgtype}, text=${JSON.stringify(data.text)?.slice(0, 100)}, senderNick=${data.senderNick}, msgId=${data.msgId}`);
|
|
289
|
+
// 获取文本内容 - 钉钉 Stream API 中文本消息在 text.content 字段
|
|
290
|
+
const textContent = data.text?.content?.trim();
|
|
291
|
+
if (data.msgtype !== "text" || !textContent) {
|
|
292
|
+
log?.info(`[dingtalk:${accountId}] Skipping non-text message: msgtype=${data.msgtype}, hasTextContent=${!!textContent}`);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
log?.info(`[dingtalk:${accountId}] Extracted text content: ${textContent?.slice(0, 100)}`);
|
|
296
|
+
// 构建 SessionKey: dingtalk:<accountId>:<conversationId>
|
|
297
|
+
const sessionKey = `dingtalk:${accountId}:${data.conversationId}`;
|
|
298
|
+
const chatType = data.conversationType === "2" ? "group" : "direct";
|
|
299
|
+
log?.info(`[dingtalk:${accountId}] Dispatching to agent: sessionKey=${sessionKey}, chatType=${chatType}`);
|
|
300
|
+
// 使用 OpenClaw 的 dispatchReplyWithBufferedBlockDispatcher 来处理消息
|
|
301
|
+
const { dispatchReplyWithBufferedBlockDispatcher } = await import("/usr/local/lib/node_modules/openclaw/dist/auto-reply/reply/provider-dispatcher.js");
|
|
302
|
+
// 构建 MsgContext
|
|
303
|
+
const msgContext = {
|
|
304
|
+
Body: textContent,
|
|
305
|
+
BodyForAgent: textContent,
|
|
306
|
+
CommandBody: textContent,
|
|
307
|
+
BodyForCommands: textContent,
|
|
308
|
+
From: data.senderStaffId,
|
|
309
|
+
SessionKey: sessionKey,
|
|
310
|
+
AccountId: accountId,
|
|
311
|
+
SenderName: data.senderNick,
|
|
312
|
+
SenderId: data.senderStaffId,
|
|
313
|
+
ChatType: chatType,
|
|
314
|
+
Provider: "dingtalk",
|
|
315
|
+
Surface: "dingtalk",
|
|
316
|
+
OriginatingChannel: "dingtalk",
|
|
317
|
+
OriginatingTo: data.conversationId,
|
|
318
|
+
Timestamp: data.createAt,
|
|
319
|
+
CommandAuthorized: true,
|
|
320
|
+
};
|
|
321
|
+
log?.info(`[dingtalk:${accountId}] MsgContext built, calling dispatchReplyWithBufferedBlockDispatcher...`);
|
|
322
|
+
// 存储 webhook 用于回复
|
|
323
|
+
const webhook = data.sessionWebhook;
|
|
324
|
+
const result = await dispatchReplyWithBufferedBlockDispatcher({
|
|
325
|
+
ctx: msgContext,
|
|
326
|
+
cfg,
|
|
327
|
+
dispatcherOptions: {
|
|
328
|
+
deliver: async (payload, info) => {
|
|
329
|
+
log?.info(`[dingtalk:${accountId}] deliver called: kind=${info.kind}, text=${payload.text?.slice(0, 100)}`);
|
|
330
|
+
if (payload.text) {
|
|
331
|
+
log?.info(`[dingtalk:${accountId}] Sending reply: ${payload.text.slice(0, 100)}...`);
|
|
332
|
+
// 通过 webhook 发送回复
|
|
333
|
+
if (webhook) {
|
|
334
|
+
try {
|
|
335
|
+
// 自动检测并使用合适的消息格式
|
|
336
|
+
const detectedFormat = detectMessageFormat(payload.text);
|
|
337
|
+
log?.info(`[dingtalk:${accountId}] Detected format: ${detectedFormat}`);
|
|
338
|
+
const messageBody = buildMessageBody(payload.text);
|
|
339
|
+
log?.info(`[dingtalk:${accountId}] Sending msgtype: ${messageBody.msgtype}`);
|
|
340
|
+
log?.info(`[dingtalk:${accountId}] Body: ${JSON.stringify(messageBody).slice(0, 500)}`);
|
|
341
|
+
const resp = await fetch(webhook, {
|
|
342
|
+
method: "POST",
|
|
343
|
+
headers: { "Content-Type": "application/json" },
|
|
344
|
+
body: JSON.stringify(messageBody),
|
|
345
|
+
});
|
|
346
|
+
const respText = await resp.text();
|
|
347
|
+
if (!resp.ok) {
|
|
348
|
+
log?.error(`[dingtalk:${accountId}] Failed: ${resp.status} ${respText}`);
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
log?.info(`[dingtalk:${accountId}] Success: ${respText}`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
catch (err) {
|
|
355
|
+
log?.error(`[dingtalk:${accountId}] Error sending reply: ${err}`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
log?.warn(`[dingtalk:${accountId}] No webhook available for reply`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
});
|
|
365
|
+
log?.info(`[dingtalk:${accountId}] Dispatch result: ${JSON.stringify(result)}`);
|
|
366
|
+
}
|
|
367
|
+
catch (error) {
|
|
368
|
+
log?.error(`[dingtalk:${accountId}] Failed to process message: ${error}`);
|
|
369
|
+
}
|
|
370
|
+
})();
|
|
371
|
+
// 立即返回成功,不让钉钉重试
|
|
372
|
+
return EventAck.SUCCESS;
|
|
373
|
+
});
|
|
374
|
+
// 启动连接
|
|
375
|
+
client.connect();
|
|
376
|
+
streamClients.set(accountId, client);
|
|
377
|
+
log?.info(`[dingtalk:${accountId}] Stream client started`);
|
|
378
|
+
return { ok: true };
|
|
379
|
+
},
|
|
380
|
+
stopAccount: async (ctx) => {
|
|
381
|
+
const { account, log } = ctx;
|
|
382
|
+
const accountId = account.accountId;
|
|
383
|
+
const client = streamClients.get(accountId);
|
|
384
|
+
if (client) {
|
|
385
|
+
await client.disconnect();
|
|
386
|
+
streamClients.delete(accountId);
|
|
387
|
+
log?.info(`[dingtalk:${accountId}] Stream client stopped`);
|
|
388
|
+
}
|
|
389
|
+
return { ok: true };
|
|
390
|
+
},
|
|
391
|
+
},
|
|
392
|
+
// 出站消息
|
|
393
|
+
outbound: {
|
|
394
|
+
sendText: async ({ text, config: msgConfig, incoming }) => {
|
|
395
|
+
const accountId = msgConfig.accountId ?? "default";
|
|
396
|
+
const cfg = msgConfig.cfg;
|
|
397
|
+
const account = cfg.channels?.dingtalk?.accounts?.[accountId];
|
|
398
|
+
if (!account) {
|
|
399
|
+
return { ok: false, error: `Account '${accountId}' not found` };
|
|
400
|
+
}
|
|
401
|
+
if (!account.clientId || !account.clientSecret) {
|
|
402
|
+
return { ok: false, error: `Account '${accountId}' missing credentials` };
|
|
403
|
+
}
|
|
404
|
+
try {
|
|
405
|
+
const accessToken = await getAccessToken(account.clientId, account.clientSecret);
|
|
406
|
+
if (incoming?.raw) {
|
|
407
|
+
const { conversationId, conversationType, senderStaffId } = incoming.raw;
|
|
408
|
+
if (conversationType === "2") {
|
|
409
|
+
await sendGroupMessage(accessToken, conversationId, text);
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
if (!account.robotCode) {
|
|
413
|
+
return { ok: false, error: `Account '${accountId}' missing robotCode` };
|
|
414
|
+
}
|
|
415
|
+
await sendPrivateMessage(accessToken, account.robotCode, senderStaffId, text);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
return { ok: false, error: "Cannot send proactive message without conversation context" };
|
|
420
|
+
}
|
|
421
|
+
return { ok: true };
|
|
422
|
+
}
|
|
423
|
+
catch (error) {
|
|
424
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
// 发送 ActionCard 卡片消息(支持交互按钮)
|
|
428
|
+
sendActionCard: async ({ title, text, buttons, singleTitle, singleURL, config: msgConfig, incoming }) => {
|
|
429
|
+
const accountId = msgConfig.accountId ?? "default";
|
|
430
|
+
const cfg = msgConfig.cfg;
|
|
431
|
+
const account = cfg.channels?.dingtalk?.accounts?.[accountId];
|
|
432
|
+
if (!account) {
|
|
433
|
+
return { ok: false, error: `Account '${accountId}' not found` };
|
|
434
|
+
}
|
|
435
|
+
if (!account.clientId || !account.clientSecret) {
|
|
436
|
+
return { ok: false, error: `Account '${accountId}' missing credentials` };
|
|
437
|
+
}
|
|
438
|
+
try {
|
|
439
|
+
const accessToken = await getAccessToken(account.clientId, account.clientSecret);
|
|
440
|
+
if (incoming?.raw) {
|
|
441
|
+
const { conversationId, conversationType } = incoming.raw;
|
|
442
|
+
if (conversationType === "2") {
|
|
443
|
+
await sendGroupActionCard(accessToken, conversationId, {
|
|
444
|
+
title,
|
|
445
|
+
text,
|
|
446
|
+
buttons,
|
|
447
|
+
singleTitle,
|
|
448
|
+
singleURL,
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
// 私聊暂不支持 ActionCard,降级为 Markdown
|
|
453
|
+
return { ok: false, error: "ActionCard is only supported in group chat" };
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
return { ok: false, error: "Cannot send proactive message without conversation context" };
|
|
458
|
+
}
|
|
459
|
+
return { ok: true };
|
|
460
|
+
}
|
|
461
|
+
catch (error) {
|
|
462
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
463
|
+
}
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
// 配置向导
|
|
467
|
+
setup: {
|
|
468
|
+
run: async ({ prompter }) => {
|
|
469
|
+
const clientId = await prompter.text("请输入钉钉机器人 ClientId (AppKey):");
|
|
470
|
+
const clientSecret = await prompter.secret("请输入钉钉机器人 ClientSecret (AppSecret):");
|
|
471
|
+
const robotCode = await prompter.text("请输入机器人编码 (robotCode,可选):");
|
|
472
|
+
return {
|
|
473
|
+
config: {
|
|
474
|
+
accounts: {
|
|
475
|
+
default: {
|
|
476
|
+
enabled: true,
|
|
477
|
+
clientId,
|
|
478
|
+
clientSecret,
|
|
479
|
+
robotCode: robotCode || undefined,
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
},
|
|
483
|
+
};
|
|
484
|
+
},
|
|
485
|
+
},
|
|
486
|
+
// 状态检查
|
|
487
|
+
status: {
|
|
488
|
+
check: async ({ account }) => {
|
|
489
|
+
const client = streamClients.get(account.accountId);
|
|
490
|
+
if (client) {
|
|
491
|
+
return { ok: true, message: "Stream client is running" };
|
|
492
|
+
}
|
|
493
|
+
return { ok: false, message: "Stream client not connected" };
|
|
494
|
+
},
|
|
495
|
+
},
|
|
496
|
+
};
|
|
497
|
+
export default function register(api) {
|
|
498
|
+
api.registerChannel({ plugin: dingtalkPlugin });
|
|
499
|
+
api.logger.info("[dingtalk] Stream channel plugin registered");
|
|
500
|
+
}
|
|
501
|
+
// 导出类型和工具函数,方便外部使用
|
|
502
|
+
export { detectMessageFormat, buildMessageBody, buildActionCardBody, };
|
|
503
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "dingtalk",
|
|
3
|
+
"name": "钉钉机器人 Channel",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "钉钉群机器人 Channel 插件 for OpenClaw",
|
|
6
|
+
"channels": [
|
|
7
|
+
"dingtalk"
|
|
8
|
+
],
|
|
9
|
+
"configSchema": {
|
|
10
|
+
"type": "object",
|
|
11
|
+
"additionalProperties": false,
|
|
12
|
+
"properties": {
|
|
13
|
+
"accounts": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"additionalProperties": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"properties": {
|
|
18
|
+
"enabled": {
|
|
19
|
+
"type": "boolean"
|
|
20
|
+
},
|
|
21
|
+
"clientId": {
|
|
22
|
+
"type": "string"
|
|
23
|
+
},
|
|
24
|
+
"clientSecret": {
|
|
25
|
+
"type": "string"
|
|
26
|
+
},
|
|
27
|
+
"robotCode": {
|
|
28
|
+
"type": "string"
|
|
29
|
+
},
|
|
30
|
+
"defaultMessageFormat": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"enum": [
|
|
33
|
+
"auto",
|
|
34
|
+
"text",
|
|
35
|
+
"markdown"
|
|
36
|
+
],
|
|
37
|
+
"default": "auto",
|
|
38
|
+
"description": "默认消息格式:auto=自动检测, text=纯文本, markdown=Markdown格式"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"uiHints": {
|
|
46
|
+
"accounts": {
|
|
47
|
+
"label": "账号配置"
|
|
48
|
+
},
|
|
49
|
+
"accounts.*.enabled": {
|
|
50
|
+
"label": "启用"
|
|
51
|
+
},
|
|
52
|
+
"accounts.*.clientId": {
|
|
53
|
+
"label": "ClientId (AppKey)"
|
|
54
|
+
},
|
|
55
|
+
"accounts.*.clientSecret": {
|
|
56
|
+
"label": "ClientSecret (AppSecret)",
|
|
57
|
+
"sensitive": true
|
|
58
|
+
},
|
|
59
|
+
"accounts.*.robotCode": {
|
|
60
|
+
"label": "机器人编码 (robotCode)"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "openclaw-channel-dingtalk",
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "钉钉 Stream 机器人 Channel 插件 for OpenClaw - 支持 Markdown、ActionCard 和双向通信",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"openclaw.plugin.json",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc",
|
|
22
|
+
"prepublishOnly": "npm run build"
|
|
23
|
+
},
|
|
24
|
+
"openclaw": {
|
|
25
|
+
"extensions": [
|
|
26
|
+
"./dist/index.js"
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"openclaw",
|
|
31
|
+
"openclaw-plugin",
|
|
32
|
+
"openclaw-channel",
|
|
33
|
+
"channel",
|
|
34
|
+
"dingtalk",
|
|
35
|
+
"钉钉",
|
|
36
|
+
"stream",
|
|
37
|
+
"chatbot",
|
|
38
|
+
"机器人",
|
|
39
|
+
"markdown",
|
|
40
|
+
"actioncard"
|
|
41
|
+
],
|
|
42
|
+
"author": "",
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": ""
|
|
47
|
+
},
|
|
48
|
+
"homepage": "",
|
|
49
|
+
"bugs": {
|
|
50
|
+
"url": ""
|
|
51
|
+
},
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=18.0.0"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"openclaw": ">=2024.0.0"
|
|
57
|
+
},
|
|
58
|
+
"peerDependenciesMeta": {
|
|
59
|
+
"openclaw": {
|
|
60
|
+
"optional": true
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@types/node": "^20.0.0",
|
|
65
|
+
"typescript": "^5.9.3"
|
|
66
|
+
},
|
|
67
|
+
"dependencies": {
|
|
68
|
+
"dingtalk-stream": "^2.1.4"
|
|
69
|
+
}
|
|
70
|
+
}
|