feat: 新增飞书本地文件发送技能,支持Excel等各类附件发送
This commit is contained in:
parent
426fc57f66
commit
f6b9998854
69
send_feishu_file.py
Normal file
69
send_feishu_file.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
|
||||||
|
# 读取环境变量里的飞书凭证(需要提前配置FEISHU_APP_ID和FEISHU_APP_SECRET)
|
||||||
|
FEISHU_APP_ID = os.getenv("FEISHU_APP_ID", "cli_a4d9e0f56e7a8b9c")
|
||||||
|
FEISHU_APP_SECRET = os.getenv("FEISHU_APP_SECRET", "your_app_secret_here")
|
||||||
|
TARGET_USER_OPEN_ID = "ou_d0474502fe89122e69d0e13123c7bb45"
|
||||||
|
FILE_PATH = "/root/.openclaw/workspace-xiaoban/output/260126/账户id_2148_角色id_2895_导出时间_20260303.xlsx"
|
||||||
|
FILE_NAME = "账户id_2148_角色id_2895_学习行为数据.xlsx"
|
||||||
|
|
||||||
|
def get_tenant_access_token():
|
||||||
|
url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal"
|
||||||
|
payload = json.dumps({
|
||||||
|
"app_id": FEISHU_APP_ID,
|
||||||
|
"app_secret": FEISHU_APP_SECRET
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload)
|
||||||
|
return response.json()["tenant_access_token"]
|
||||||
|
|
||||||
|
def upload_file(token):
|
||||||
|
url = "https://open.feishu.cn/open-apis/im/v1/files"
|
||||||
|
params = {
|
||||||
|
"file_type": "xls",
|
||||||
|
"file_name": FILE_NAME
|
||||||
|
}
|
||||||
|
payload = {}
|
||||||
|
files=[
|
||||||
|
('file',(FILE_NAME,open(FILE_PATH,'rb'),'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'))
|
||||||
|
]
|
||||||
|
headers = {
|
||||||
|
'Authorization': f'Bearer {token}'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload, files=files, params=params)
|
||||||
|
return response.json()["data"]["file_key"]
|
||||||
|
|
||||||
|
def send_file_message(token, file_key):
|
||||||
|
url = "https://open.feishu.cn/open-apis/im/v1/messages"
|
||||||
|
params = {
|
||||||
|
"receive_id_type": "open_id"
|
||||||
|
}
|
||||||
|
payload = json.dumps({
|
||||||
|
"receive_id": TARGET_USER_OPEN_ID,
|
||||||
|
"msg_type": "file",
|
||||||
|
"content": json.dumps({
|
||||||
|
"file_key": file_key
|
||||||
|
})
|
||||||
|
})
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': f'Bearer {token}'
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, headers=headers, data=payload, params=params)
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
token = get_tenant_access_token()
|
||||||
|
print(f"获取token成功: {token[:10]}...")
|
||||||
|
file_key = upload_file(token)
|
||||||
|
print(f"上传文件成功,file_key: {file_key}")
|
||||||
|
res = send_file_message(token, file_key)
|
||||||
|
print(f"发送消息结果: {json.dumps(res, indent=2, ensure_ascii=False)}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"出错了: {e}")
|
||||||
131
skills/feishu_send_file/SKILL.md
Normal file
131
skills/feishu_send_file/SKILL.md
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
---
|
||||||
|
name: feishu-send-file
|
||||||
|
description: |
|
||||||
|
通过飞书API发送本地文件(Excel/PDF/Word/PPT等)到飞书用户或群组。
|
||||||
|
绕过OpenClaw message工具的限制,直接调用飞书原生文件上传+发送API。
|
||||||
|
metadata:
|
||||||
|
{
|
||||||
|
"openclaw":
|
||||||
|
{
|
||||||
|
"requires": { "tools": ["exec"] },
|
||||||
|
"categories": ["feishu", "file", "messaging"]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
# 飞书本地文件发送技能
|
||||||
|
|
||||||
|
## When to Use
|
||||||
|
|
||||||
|
当用户要求将**本地文件**(Excel、PDF、Word、PPT、音视频等)通过飞书发送给某人或某个群时使用此技能。
|
||||||
|
|
||||||
|
> **注意**: OpenClaw 内置的 message 工具仅支持发送文本和URL媒体,不支持本地文件路径。本技能通过 `exec` 工具直接调用飞书 API 实现文件发送。
|
||||||
|
|
||||||
|
## Core Rules
|
||||||
|
|
||||||
|
### 1. 确定飞书账号凭证
|
||||||
|
|
||||||
|
从 OpenClaw 配置文件 `/root/.openclaw/openclaw.json` 的 `channels.feishu.accounts` 中读取对应账号的 `appId` 和 `appSecret`。
|
||||||
|
|
||||||
|
根据当前 agent 绑定关系选择账号:
|
||||||
|
- **xiaoban** agent → 使用 `xiaoban` 账号
|
||||||
|
- **xiaoxi** agent → 使用 `xiaoxi` 账号
|
||||||
|
|
||||||
|
### 2. 文件类型映射
|
||||||
|
|
||||||
|
根据文件扩展名确定飞书 `file_type` 参数:
|
||||||
|
|
||||||
|
| 扩展名 | file_type |
|
||||||
|
|--------|-----------|
|
||||||
|
| `.xls` `.xlsx` | `xls` |
|
||||||
|
| `.doc` `.docx` | `doc` |
|
||||||
|
| `.pdf` | `pdf` |
|
||||||
|
| `.ppt` `.pptx` | `ppt` |
|
||||||
|
| `.mp4` `.mov` `.avi` | `mp4` |
|
||||||
|
| `.opus` `.ogg` | `opus` |
|
||||||
|
| 其他 | `stream` |
|
||||||
|
|
||||||
|
### 3. 发送目标格式
|
||||||
|
|
||||||
|
- **个人**: 使用 `open_id`(格式 `ou_xxxx`),`receive_id_type` 为 `open_id`
|
||||||
|
- **群组**: 使用 `chat_id`(格式 `oc_xxxx`),`receive_id_type` 为 `chat_id`
|
||||||
|
|
||||||
|
### 4. 执行流程(三步)
|
||||||
|
|
||||||
|
通过 `exec` 工具执行以下 shell 脚本,**一次性完成全部三步**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# === 配置区(根据实际情况填写)===
|
||||||
|
APP_ID="<appId>"
|
||||||
|
APP_SECRET="<appSecret>"
|
||||||
|
FILE_PATH="<本地文件绝对路径>"
|
||||||
|
FILE_NAME="<文件名,如 report.xlsx>"
|
||||||
|
FILE_TYPE="<文件类型,如 xls>"
|
||||||
|
RECEIVE_ID="<目标open_id或chat_id>"
|
||||||
|
RECEIVE_ID_TYPE="<open_id 或 chat_id>"
|
||||||
|
|
||||||
|
# === Step 1: 获取 tenant_access_token ===
|
||||||
|
TOKEN_RESP=$(curl -s -X POST "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"app_id\":\"${APP_ID}\",\"app_secret\":\"${APP_SECRET}\"}")
|
||||||
|
|
||||||
|
TOKEN=$(echo "$TOKEN_RESP" | grep -o '"tenant_access_token":"[^"]*"' | cut -d'"' -f4)
|
||||||
|
|
||||||
|
if [ -z "$TOKEN" ]; then
|
||||||
|
echo "ERROR: 获取 tenant_access_token 失败"
|
||||||
|
echo "$TOKEN_RESP"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Step 1 OK: token acquired"
|
||||||
|
|
||||||
|
# === Step 2: 上传文件获取 file_key ===
|
||||||
|
UPLOAD_RESP=$(curl -s -X POST "https://open.feishu.cn/open-apis/im/v1/files" \
|
||||||
|
-H "Authorization: Bearer ${TOKEN}" \
|
||||||
|
-F "file_type=${FILE_TYPE}" \
|
||||||
|
-F "file_name=${FILE_NAME}" \
|
||||||
|
-F "file=@${FILE_PATH}")
|
||||||
|
|
||||||
|
FILE_KEY=$(echo "$UPLOAD_RESP" | grep -o '"file_key":"[^"]*"' | cut -d'"' -f4)
|
||||||
|
|
||||||
|
if [ -z "$FILE_KEY" ]; then
|
||||||
|
echo "ERROR: 文件上传失败"
|
||||||
|
echo "$UPLOAD_RESP"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Step 2 OK: file_key=${FILE_KEY}"
|
||||||
|
|
||||||
|
# === Step 3: 发送文件消息 ===
|
||||||
|
SEND_RESP=$(curl -s -X POST "https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=${RECEIVE_ID_TYPE}" \
|
||||||
|
-H "Authorization: Bearer ${TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"receive_id\":\"${RECEIVE_ID}\",\"msg_type\":\"file\",\"content\":\"{\\\"file_key\\\":\\\"${FILE_KEY}\\\"}\"}")
|
||||||
|
|
||||||
|
MSG_ID=$(echo "$SEND_RESP" | grep -o '"message_id":"[^"]*"' | cut -d'"' -f4)
|
||||||
|
|
||||||
|
if [ -z "$MSG_ID" ]; then
|
||||||
|
echo "ERROR: 消息发送失败"
|
||||||
|
echo "$SEND_RESP"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Step 3 OK: message sent, message_id=${MSG_ID}"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 注意事项
|
||||||
|
|
||||||
|
- 文件大小上限 **30MB**
|
||||||
|
- 发送前用 `ls -la <文件路径>` 确认文件存在且大小合理
|
||||||
|
- 如果发送音视频文件(mp4/opus),Step 3 中 `msg_type` 改为 `"media"`,content 改为 `{"file_key":"..."}` 格式不变
|
||||||
|
- 飞书应用需要 `im:message:send_as_bot` 和 `im:resource` 权限
|
||||||
|
- 如遇权限错误(code 99991672),返回的 msg 中通常包含权限申请链接,告知用户去审批
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
| 问题 | 原因 | 解决 |
|
||||||
|
|------|------|------|
|
||||||
|
| token 获取失败 | appId/appSecret 错误 | 核对 openclaw.json 配置 |
|
||||||
|
| 上传返回 99991672 | 缺少 `im:resource` 权限 | 去飞书开放平台添加权限并审批 |
|
||||||
|
| 发送返回权限错误 | 缺少 `im:message:send_as_bot` | 同上 |
|
||||||
|
| 文件过大 | 超过 30MB | 压缩文件或分片 |
|
||||||
Loading…
Reference in New Issue
Block a user