---
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.

## Delegation Contract

If an upper-layer bot delegates a task that uses this skill, it should explicitly provide:

- `app_id`
- `app_secret`
- `sender_id` from the triggering Feishu inbound message when the target is "the current message sender"
- `conversation_id` only if the task should send a follow-up message back to the same chat
- `calendar_id` if already known

Interpretation rules:

- Treat Feishu `sender_id` as `user_open_id`.
- Never treat `conversation_id` / `chat_id` as a user identity.
- If the request is "create or inspect the current user's calendar" and the task was delegated without `sender_id`, stop and report missing parameters instead of guessing.
- If credentials are missing, stop and report missing parameters instead of trying to discover config files by yourself.

Current focus:
- get tenant access token
- get primary calendar
- 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.
- When an upper-layer bot delegates a Feishu task that uses this skill, it should explicitly provide:
  - `app_id`
  - `app_secret`
  - `sender_id` from the triggering Feishu message when the task target is "the current user"
  - `conversation_id` only if a follow-up message should be sent back to the same chat
- 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.
- `conversation_id/chat_id` is for sending replies, not for identifying a user's personal calendar.
- In current testing with a tenant token, passing `sender_id(open_id)` into the primary-calendar endpoint still returned the same primary calendar object. So the skill should accept `sender_id/user_open_id`, but must not promise that tenant-token calls can switch to a different personal calendar without further user-scoped auth.

## Inputs

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

You can usually obtain them from the current bot config or manifest if the upper-layer bot already knows that the current CheapClaw bot is connected to Feishu.

For personal-calendar tasks, the upper-layer bot should also pass:
- `sender_id`, treated as `user_open_id`

If a delegated task is missing credentials, or is supposed to act on "the current user" but no `sender_id/user_open_id` is provided, stop and report missing parameters instead of guessing.

## Workflow

1. First obtain a tenant access token:

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

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/get_primary_calendar.py \
  --app-id <APP_ID> \
  --app-secret <APP_SECRET> \
  --user-open-id <SENDER_OPEN_ID>
```

Expected success shape:

```json
{
  "code": 0,
  "data": {
    "calendar_id": "...",
    "summary": "...",
    "type": "primary"
  }
}
```

If you see `code=99991672`, stop and report that Feishu app permissions are not enabled yet.
If you are using a tenant token, do not assume this primary calendar is guaranteed to be the message sender's personal calendar; report that limitation clearly.

### 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.
- If the task is delegated from an upper-layer bot, require explicit credentials and the triggering `sender_id` for user-targeted calendar work.
- Treat Feishu `sender_id` as `user_open_id` when a task says "for the current user".
- Never treat `conversation_id` as a user identity.
- If the task is "create a calendar event for the current message sender" and no `sender_id/user_open_id` is provided, stop and report missing parameters.
- 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.
