---
name: feishu-ops
description: Use when a task needs to operate Feishu Open Platform APIs directly, especially calendar events and basic IM/Drive-adjacent automation. This skill provides fixed scripts for tenant token retrieval and Feishu calendar CRUD so the agent can call stable scripts instead of hand-writing ad hoc requests.
---

# Feishu Ops

Use this skill when the task needs direct Feishu Open Platform operations and CheapClaw does not already expose a dedicated tool.

Current focus:
- get tenant access token
- list calendars
- list calendar events
- create calendar events
- update calendar events
- delete calendar events
- create docs
- append text into docs
- create spreadsheets
- read spreadsheet metadata
- write spreadsheet cells
- create bitable apps
- list bitable fields
- insert bitable records
- send interactive card messages to Feishu chats

## Real-world Notes

- The current scripts have been tested against a real CheapClaw Feishu bot config.
- Calendar APIs require application scopes. If you see `code=99991672`, the app is missing calendar permissions.
- Creating or updating events does **not** accept a raw ISO string directly in `start_time` / `end_time`.
  The accepted structure is:

```json
{
  "timestamp": "1774541280",
  "timezone": "Asia/Shanghai"
}
```

- The scripts accept either:
  - RFC3339 / ISO datetime with timezone offset, for example `2026-03-27T00:08:00+08:00`
  - or unix timestamp seconds
  and convert it to Feishu's required `{timestamp, timezone}` structure.
- Spreadsheet cell updates in Feishu Sheets use the **sheet_id** in the range, not the visible sheet title.
  Example: `026ff1!A1:B2` works, while `Sheet1!A1:B2` may return `sheetId not found`.
- Bitable record creation should use the real field names returned by the fields API, not guessed English aliases.
- A Feishu doc created by API can usually be opened with a URL shaped like:
  `https://<tenant-domain>.feishu.cn/docx/<document_id>`
  but the user must be logged into the correct Feishu account.

## Inputs

The scripts expect:
- `--app-id`
- `--app-secret`

You can usually obtain them from the current bot config or manifest if the bot is already connected to Feishu.

## Workflow

1. First obtain a tenant access token:

```bash
python scripts/get_tenant_token.py --app-id <APP_ID> --app-secret <APP_SECRET>
```

2. Then use one of the calendar scripts:

```bash
python scripts/list_calendars.py --app-id <APP_ID> --app-secret <APP_SECRET>
python scripts/list_events.py --app-id <APP_ID> --app-secret <APP_SECRET> --calendar-id <CAL_ID>
python scripts/create_event.py --app-id <APP_ID> --app-secret <APP_SECRET> --calendar-id <CAL_ID> --summary "..." --start "2026-03-26T09:00:00+08:00" --end "2026-03-26T10:00:00+08:00"
python scripts/update_event.py --app-id <APP_ID> --app-secret <APP_SECRET> --calendar-id <CAL_ID> --event-id <EVENT_ID> --summary "..." --start "2026-03-26T10:00:00+08:00"
python scripts/delete_event.py --app-id <APP_ID> --app-secret <APP_SECRET> --calendar-id <CAL_ID> --event-id <EVENT_ID>
python scripts/create_doc.py --app-id <APP_ID> --app-secret <APP_SECRET> --title "CheapClaw Doc Smoke"
python scripts/write_doc_text.py --app-id <APP_ID> --app-secret <APP_SECRET> --document-id <DOC_ID> --text "hello from CheapClaw"
python scripts/create_sheet.py --app-id <APP_ID> --app-secret <APP_SECRET> --title "CheapClaw Sheet Smoke"
python scripts/get_sheet_meta.py --app-id <APP_ID> --app-secret <APP_SECRET> --spreadsheet-token <SHEET_TOKEN>
python scripts/write_sheet_values.py --app-id <APP_ID> --app-secret <APP_SECRET> --spreadsheet-token <SHEET_TOKEN> --range "026ff1!A1:B2" --values-json '[[\"A1\",\"B1\"],[\"A2\",\"B2\"]]'
python scripts/create_bitable.py --app-id <APP_ID> --app-secret <APP_SECRET> --name "CheapClaw Bitable Smoke"
python scripts/list_bitable_fields.py --app-id <APP_ID> --app-secret <APP_SECRET> --app-token <APP_TOKEN> --table-id <TABLE_ID>
python scripts/insert_bitable_record.py --app-id <APP_ID> --app-secret <APP_SECRET> --app-token <APP_TOKEN> --table-id <TABLE_ID> --fields-json '{\"文本\":\"hello\"}'
python scripts/send_card_message.py --app-id <APP_ID> --app-secret <APP_SECRET> --chat-id <CHAT_ID> --title "CheapClaw Card" --markdown "这是 **卡片** 内容"
```

## Few-shot

### Example 1: Verify the app is ready for calendar access

```bash
python scripts/list_calendars.py \
  --app-id <APP_ID> \
  --app-secret <APP_SECRET>
```

Expected success shape:

```json
{
  "code": 0,
  "data": {
    "calendar_list": [
      {
        "calendar_id": "...",
        "summary": "cheapclawtest",
        "type": "primary"
      }
    ]
  }
}
```

If you see `code=99991672`, stop and report that Feishu app permissions are not enabled yet.

### Example 2: Create and then delete a smoke-test event

```bash
python scripts/create_event.py \
  --app-id <APP_ID> \
  --app-secret <APP_SECRET> \
  --calendar-id <CAL_ID> \
  --summary "CheapClaw smoke test" \
  --description "temporary test event" \
  --start "2026-03-27T09:00:00+08:00" \
  --end "2026-03-27T09:30:00+08:00"

python scripts/delete_event.py \
  --app-id <APP_ID> \
  --app-secret <APP_SECRET> \
  --calendar-id <CAL_ID> \
  --event-id <EVENT_ID>
```

Use this pattern when you need to validate write permissions without leaving permanent data behind.

### Example 3: Create a spreadsheet and a bitable app

```bash
python scripts/create_sheet.py \
  --app-id <APP_ID> \
  --app-secret <APP_SECRET> \
  --title "CheapClaw Sheet Smoke"

python scripts/create_bitable.py \
  --app-id <APP_ID> \
  --app-secret <APP_SECRET> \
  --name "CheapClaw Bitable Smoke"
```

Expected success shape:

```json
{
  "code": 0,
  "data": {
    "spreadsheet": {
      "spreadsheet_token": "...",
      "url": "https://...feishu.cn/sheets/..."
    }
  }
}
```

and

```json
{
  "code": 0,
  "data": {
    "app": {
      "app_token": "...",
      "url": "https://...feishu.cn/base/..."
    }
  }
}
```

### Example 4: Write values into a spreadsheet

```bash
python scripts/get_sheet_meta.py \
  --app-id <APP_ID> \
  --app-secret <APP_SECRET> \
  --spreadsheet-token <SHEET_TOKEN>

python scripts/write_sheet_values.py \
  --app-id <APP_ID> \
  --app-secret <APP_SECRET> \
  --spreadsheet-token <SHEET_TOKEN> \
  --range "026ff1!A1:B2" \
  --values-json '[[\"CheapClaw\",\"2026-03-27\"],[\"status\",\"filled\"]]'
```

Use the returned `sheetId` such as `026ff1`, not the visible title `Sheet1`, when writing values.

### Example 5: Insert a record into bitable

```bash
python scripts/list_bitable_fields.py \
  --app-id <APP_ID> \
  --app-secret <APP_SECRET> \
  --app-token <APP_TOKEN> \
  --table-id <TABLE_ID>

python scripts/insert_bitable_record.py \
  --app-id <APP_ID> \
  --app-secret <APP_SECRET> \
  --app-token <APP_TOKEN> \
  --table-id <TABLE_ID> \
  --fields-json '{\"文本\":\"CheapClaw Bitable Smoke Row\",\"日期\":1774541250017}'
```

Always read fields first and use the real field names returned by the API.

### Example 6: Return created object links back to the user

If the current task has native CheapClaw messaging tools available, prefer sending a short text message with:

- object type
- title/name
- direct URL
- one-line note on what was created

If native send tools are **not** available in the current task, put the same information in `final_output`, and let the supervisor relay it.

Recommended response shape:

```text
已创建飞书表格：
- 标题：CheapClaw Sheet Smoke
- 链接：https://...feishu.cn/sheets/...
- 说明：已写入 A1:B2 的测试内容
```

### Example 7: Send an interactive card

```bash
python scripts/send_card_message.py \
  --app-id <APP_ID> \
  --app-secret <APP_SECRET> \
  --chat-id <CHAT_ID> \
  --title "CheapClaw Card Smoke" \
  --markdown "这是一条 **CheapClaw** 发送的飞书卡片消息。"
```

This path has been smoke-tested against a real Feishu chat.

## Rules

- Prefer these scripts over writing raw `requests` code inline.
- Return concise summaries plus any important IDs (`calendar_id`, `event_id`).
- When creating or updating events, always use absolute timestamps with timezone offsets.
- When you create a Feishu document, spreadsheet, or bitable app, prefer returning a direct URL rather than exporting a local file.
- If `cheapclaw_send_message` or `cheapclaw_send_file` is available in the current task, you may use it to send the resulting link back to the user.
- If those tools are not available, write the link, title, and brief content summary into `final_output`; the upper-layer supervisor can relay it.
- If the task only needs Feishu chat send/receive, prefer CheapClaw's existing channel capabilities instead of this skill.
- CheapClaw does not currently provide a native top-level Feishu card tool, but this skill now includes a tested script for sending simple interactive cards when `chat_id` is known.
- When a plain text link is enough, prefer plain text plus URL over cards to reduce rendering/debugging complexity.
- For docs and cloud objects, the most reliable return format is still plain text plus URL.
