---
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 | 压缩文件或分片 |
