Add DingTalk success notes and long-term memory - token and msgKey configuration
This commit is contained in:
168
MEMORY.md
Normal file
168
MEMORY.md
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
# MEMORY.md - 长期记忆
|
||||||
|
|
||||||
|
这个文件用于记录重要的长期知识和经验教训。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DingTalk媒体发送成功经验
|
||||||
|
|
||||||
|
**日期**: 2026-03-05
|
||||||
|
**重要性**: ⭐⭐⭐⭐⭐
|
||||||
|
|
||||||
|
### 问题背景
|
||||||
|
尝试通过DingTalk发送文件时遇到多次失败,反复尝试了多种方法:
|
||||||
|
- 使用message工具 - 失败("Failed to upload media")
|
||||||
|
- 直接调用robot/send API - 失败("token is not exist" / "缺少参数 access_token")
|
||||||
|
- 尝试不同的API端点和消息格式 - 大部分失败
|
||||||
|
|
||||||
|
### 解决方案(成功方式)
|
||||||
|
|
||||||
|
#### Token获取
|
||||||
|
```javascript
|
||||||
|
// 关键:使用GET + URL参数(非params)
|
||||||
|
const tokenUrl = `https://oapi.dingtalk.com/gettoken?appkey=${APP_KEY}&appsecret=${APP_SECRET}`;
|
||||||
|
const response = await axios.get(tokenUrl);
|
||||||
|
const accessToken = response.data.access_token;
|
||||||
|
```
|
||||||
|
|
||||||
|
**重要**:不要使用`axios.get(url, {params: {...}})`,必须在URL中直接拼接参数
|
||||||
|
|
||||||
|
#### 文件上传
|
||||||
|
```javascript
|
||||||
|
const uploadUrl = `https://oapi.dingtalk.com/media/upload?access_token=${accessToken}`;
|
||||||
|
const form = new FormData();
|
||||||
|
form.append('media', fs.createReadStream(filePath));
|
||||||
|
form.append('type', 'file');
|
||||||
|
const upload = await axios.post(uploadUrl, form, {headers: form.getHeaders()});
|
||||||
|
const media_id = upload.data.media_id;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 消息发送(关键成功点)
|
||||||
|
```javascript
|
||||||
|
const url = "https://api.dingtalk.com/v1.0/robot/groupMessages/send";
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
"x-acs-dingtalk-access-token": accessToken, // ← 关键:在Header中
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
};
|
||||||
|
|
||||||
|
const body = {
|
||||||
|
robotCode: ROBOT_CODE,
|
||||||
|
openConversationId: OPEN_CONVERSATION_ID,
|
||||||
|
msgKey: "sampleFile", // ← 文件用sampleFile,图片用sampleMarkdown
|
||||||
|
msgParam: JSON.stringify({ // ← 关键:必须是字符串!
|
||||||
|
mediaId: media_id,
|
||||||
|
fileName: fileName
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await axios.post(url, body, { headers });
|
||||||
|
```
|
||||||
|
|
||||||
|
### 关键msgKey对应关系
|
||||||
|
|
||||||
|
| 媒体类型 | msgKey | 测试状态 |
|
||||||
|
|---------|--------|---------|
|
||||||
|
| 文件 | `sampleFile` | ✅ 验证成功 |
|
||||||
|
| 图片 | `sampleMarkdown` | ✅ 验证成功 |
|
||||||
|
| 视频 | `sampleVideo` | 待测试 |
|
||||||
|
|
||||||
|
### 失败原因总结
|
||||||
|
|
||||||
|
1. **使用旧的robot/send API**
|
||||||
|
- 返回 "token is not exist"
|
||||||
|
- 使用v1.0 API后解决
|
||||||
|
|
||||||
|
2. **msgParam使用对象而非字符串**
|
||||||
|
- 返回 "MissingmsgParam"
|
||||||
|
- 必须使用`JSON.stringify()`
|
||||||
|
|
||||||
|
3. **access_token放在错误位置**
|
||||||
|
- 在URL中会失败
|
||||||
|
- 必须在Header中:`x-acs-dingtalk-access-token`
|
||||||
|
|
||||||
|
### 验证的端点差异
|
||||||
|
|
||||||
|
| API端点 | 状态 | 说明 |
|
||||||
|
|---------|------|------|
|
||||||
|
| `https://oapi.dingtalk.com/gettoken` | ✅ 可用 | 必须用URL参数 |
|
||||||
|
| `https://oapi.dingtalk.com/media/upload` | ✅ 可用 | 支持文件上传 |
|
||||||
|
| `https://api.dingtalk.com/v1.0/robot/groupMessages/send` | ✅ 可用 | 成功发送消息 |
|
||||||
|
| `https://oapi.dingtalk.com/robot/send` | ❌ 失败 | 返回token错误 |
|
||||||
|
|
||||||
|
### 配置信息
|
||||||
|
|
||||||
|
```
|
||||||
|
AppKey: ding4ursdp0l2giat4bj
|
||||||
|
AppSecret: J0gBicjKiIHoKla7WfKKhRs1Tv8L6Xd5UhW3EVQByF16G7Vn7UUcRhP6u-PBCQNo
|
||||||
|
RobotCode: ding4ursdp0l2giat4bj
|
||||||
|
OpenConversationId: cidcjYshXVtKck5LfOO9AqOJg==
|
||||||
|
```
|
||||||
|
|
||||||
|
### 成功案例
|
||||||
|
|
||||||
|
**2026-03-05 18:44**
|
||||||
|
- 项目:database_analysis_summary.txt
|
||||||
|
- 文件大小:3,155 bytes
|
||||||
|
- media_id: @lAjPM2GaR32g2U3OC2pNq84Clg_0
|
||||||
|
- msgKey: sampleFile
|
||||||
|
- API: /v1.0/robot/groupMessages/send
|
||||||
|
- ProcessQueryKey: lXs/uLRd0YxJVk1x0VyTLfSdY2YKCE1yrGn2vGlQ+GM=
|
||||||
|
- 状态:成功发送到钉钉群聊
|
||||||
|
|
||||||
|
### 经验教训
|
||||||
|
|
||||||
|
1. **不要在OpenClaw环境中直接调用原始钉钉API**
|
||||||
|
- OpenClaw插件会拦截请求
|
||||||
|
- 认证可能不匹配
|
||||||
|
|
||||||
|
2. **msgKey是关键**
|
||||||
|
- 文件 → sampleFile
|
||||||
|
- 图片 → sampleMarkdown
|
||||||
|
- 视频 → sampleVideo
|
||||||
|
|
||||||
|
3. **msgParam必须是JSON字符串**
|
||||||
|
- 这是容易出错的地方
|
||||||
|
- 必须用`JSON.stringify()`包裹
|
||||||
|
|
||||||
|
4. **使用正确的API版本**
|
||||||
|
- v1.0 API是正确选择
|
||||||
|
- 旧版API不再有效
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Python环境问题
|
||||||
|
|
||||||
|
**日期**: 2026-03-05
|
||||||
|
**重要性**: ⭐⭐⭐
|
||||||
|
|
||||||
|
**问题**: PowerShell直接调用Python时遇到路径和编码问题
|
||||||
|
|
||||||
|
**解决方案**:
|
||||||
|
1. 设置PATH: `$env:PATH = "F:\pyenv\pyenv-win\pyenv-win\pyenv-win\versions\3.14.2;$env:PATH"`
|
||||||
|
2. 使用UTF-8编码: `sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Git配置
|
||||||
|
|
||||||
|
**日期**: 2026-03-05
|
||||||
|
**重要性**: ⭐⭐⭐
|
||||||
|
|
||||||
|
**Git服务器**: git.alicorns.co.jp (Gitea)
|
||||||
|
**用户**: aitest
|
||||||
|
**密码**: Aiitest123456
|
||||||
|
|
||||||
|
**主要仓库**:
|
||||||
|
- `aitest/office-file-handler.git`
|
||||||
|
- `aitest/dingtalk-media-sender.git`
|
||||||
|
- `aitest/workspace.git`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 记录准则
|
||||||
|
|
||||||
|
1. 只记录重要且会重复用到的信息
|
||||||
|
2. 包括成功和失败的经验
|
||||||
|
3. 提供可执行的代码示例
|
||||||
|
4. 标注日期和重要性
|
||||||
83
TOOLS.md
83
TOOLS.md
@@ -44,4 +44,87 @@ Skills are shared. Your setup is yours. Keeping them apart means you can update
|
|||||||
- **robotCode**: ding4ursdp0l2giat4bj
|
- **robotCode**: ding4ursdp0l2giat4bj
|
||||||
- **OpenConversationId**: cidcjYshXVtKck5LfOO9AqOJg==
|
- **OpenConversationId**: cidcjYshXVtKck5LfOO9AqOJg==
|
||||||
|
|
||||||
|
## DingTalk成功发送的关键配置
|
||||||
|
|
||||||
|
### Token获取(必须使用GET + URL参数)
|
||||||
|
```javascript
|
||||||
|
// ✓ 正确方式
|
||||||
|
const tokenUrl = `https://oapi.dingtalk.com/gettoken?appkey=${APP_KEY}&appsecret=${APP_SECRET}`;
|
||||||
|
const token = await axios.get(tokenUrl).data.access_token;
|
||||||
|
|
||||||
|
// ✗ 错误方式(会失败)
|
||||||
|
const tokenUrl = "https://oapi.dingtalk.com/gettoken";
|
||||||
|
const token = await axios.get(tokenUrl, {
|
||||||
|
params: {appkey: APP_KEY, appsecret: APP_SECRET}
|
||||||
|
}).data.access_token;
|
||||||
|
```
|
||||||
|
|
||||||
|
### v1.0 API发送消息(成功方式)
|
||||||
|
```javascript
|
||||||
|
// API端点
|
||||||
|
const url = "https://api.dingtalk.com/v1.0/robot/groupMessages/send";
|
||||||
|
|
||||||
|
// Headers(关键:包含access_token)
|
||||||
|
const headers = {
|
||||||
|
"x-acs-dingtalk-access-token": accessToken,
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Body格式
|
||||||
|
const body = {
|
||||||
|
robotCode: ROBOT_CODE,
|
||||||
|
openConversationId: OPEN_CONVERSATION_ID,
|
||||||
|
msgKey: "sampleFile", // 或 "sampleMarkdown"
|
||||||
|
msgParam: JSON.stringify({ // ← 必须是字符串!
|
||||||
|
mediaId: media_id,
|
||||||
|
fileName: "file.txt"
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
await axios.post(url, body, { headers });
|
||||||
|
```
|
||||||
|
|
||||||
|
### 关键msgKey对应关系
|
||||||
|
|
||||||
|
| 媒体类型 | msgKey | 说明 |
|
||||||
|
|---------|--------|------|
|
||||||
|
| **文件** | `sampleFile` | 发送.txt, .docx, .pdf等文件 |
|
||||||
|
| **图片** | `sampleMarkdown` | 使用Markdown格式嵌入图片 |
|
||||||
|
| **视频** | `sampleVideo` | 发送.mp4等视频文件(待测试) |
|
||||||
|
|
||||||
|
### 重要注意事项
|
||||||
|
|
||||||
|
1. **msgParam必须是字符串**
|
||||||
|
```javascript
|
||||||
|
// ✓ 正确
|
||||||
|
msgParam: JSON.stringify({mediaId: "@xxx", fileName: "test.txt"})
|
||||||
|
|
||||||
|
// ✗ 错误
|
||||||
|
msgParam: {mediaId: "@xxx", fileName: "test.txt"}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **直接调用API会失败于OpenClaw环境**
|
||||||
|
- OpenClaw的DingTalk插件会拦截所有API请求
|
||||||
|
- 直接调用`robot/send`会返回"token is not exist"或"缺少参数 access_token"
|
||||||
|
- 必须使用v1.0 API + Headers中的access_token
|
||||||
|
|
||||||
|
3. **文件上传API**
|
||||||
|
```javascript
|
||||||
|
// 上传(GET方式获取token已经验证有效)
|
||||||
|
const uploadUrl = `https://oapi.dingtalk.com/media/upload?access_token=${accessToken}`;
|
||||||
|
const form = new FormData();
|
||||||
|
form.append('media', fs.createReadStream(filePath));
|
||||||
|
form.append('type', 'file');
|
||||||
|
await axios.post(uploadUrl, form, {headers: form.getHeaders()});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 成功案例记录
|
||||||
|
|
||||||
|
**2026-03-05 18:44** - 成功发送database_analysis_summary.txt
|
||||||
|
- 文件: database_analysis_summary.txt (3,155 bytes)
|
||||||
|
- media_id: @lAjPM2GaR32g2U3OC2pNq84Clg_0
|
||||||
|
- msgKey: sampleFile
|
||||||
|
- API: /v1.0/robot/groupMessages/send
|
||||||
|
- ProcessQueryKey: lXs/uLRd0YxJVk1x0VyTLfSdY2YKCE1yrGn2vGlQ+GM=
|
||||||
|
|
||||||
Add whatever helps you do your job. This is your cheat sheet.
|
Add whatever helps you do your job. This is your cheat sheet.
|
||||||
|
|||||||
Reference in New Issue
Block a user