mirror of
https://github.com/larksuite/cli.git
synced 2026-07-03 14:02:43 +08:00
Compare commits
5 Commits
v1.0.59
...
docs/lark-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4fb1d96da3 | ||
|
|
9218586733 | ||
|
|
5790c38e37 | ||
|
|
eaecda034b | ||
|
|
368da695c9 |
@@ -1,71 +1,46 @@
|
||||
## Core Concepts
|
||||
|
||||
- **Message**: A single message in a chat, identified by `message_id` (om_xxx). Supports types: text, post, image, file, audio, video, sticker, interactive (card), share_chat, share_user, merge_forward, etc.
|
||||
- **Chat**: A group chat or P2P conversation, identified by `chat_id` (oc_xxx).
|
||||
- **Thread**: A reply thread under a message, identified by `thread_id` (om_xxx or omt_xxx).
|
||||
- **Reaction**: An emoji reaction on a message.
|
||||
- **Flag**: A bookmark on a message or thread.
|
||||
- **Feed Shortcut**: A chat pinned to the current user's feed sidebar, identified by `feed_card_id` (an `oc_xxx` open_chat_id for CHAT type).
|
||||
- **Feed Group**: A tag that groups feed cards in the feed list, identified by `feed_group_id` (ofg_xxx). Members are feed cards, each identified by `feed_id` + `feed_type`. Two types: `normal` (members managed explicitly) and `rule` (members auto-derived from rules).
|
||||
|
||||
## Resource Relationships
|
||||
|
||||
```
|
||||
Chat (oc_xxx)
|
||||
├── Message (om_xxx)
|
||||
│ ├── Thread (reply thread)
|
||||
│ ├── Reaction (emoji)
|
||||
│ └── Resource (image / file / video / audio)
|
||||
└── Member (user / bot)
|
||||
```
|
||||
- **Message** `message_id` (om_xxx) · **Chat** `chat_id` (oc_xxx, group or P2P) · **Thread** `thread_id` (om_xxx / omt_xxx).
|
||||
- **Flag** — bookmark on a message/thread (two layers, see below).
|
||||
- **Feed Shortcut** `feed_card_id` (oc_xxx) — a chat pinned to the user's feed sidebar.
|
||||
- **Feed Group** `feed_group_id` (ofg_xxx) — a tag grouping feed cards (`feed_id`+`feed_type`); `normal` (explicit) / `rule` (auto-derived).
|
||||
|
||||
## Important Notes
|
||||
|
||||
### Identity and Token Mapping
|
||||
### Identity (user vs bot)
|
||||
|
||||
- `--as user` means **user identity** and uses `user_access_token`. Calls run as the authorized end user, so permissions depend on both the app scopes and that user's own access to the target chat/message/resource.
|
||||
- `--as bot` means **bot identity** and uses `tenant_access_token`. Calls run as the app bot, so behavior depends on the bot's membership, app visibility, availability range, and bot-specific scopes.
|
||||
- If an IM API says it supports both `user` and `bot`, the token type changes who the operator is. The same API can succeed with one identity and fail with the other because owner/admin status, chat membership, tenant boundary, or app availability are checked against the current caller.
|
||||
- `--as user` (`user_access_token`): runs as the authorized user; permission = app scopes + that user's own access to the target.
|
||||
- `--as bot` (`tenant_access_token`): runs as the app bot; depends on bot's chat membership, app visibility range, bot scopes.
|
||||
- When an API supports both, the token decides *who* operates — owner/admin, membership, tenant, visibility are checked against the caller, so the same API can pass on one identity and fail on the other.
|
||||
|
||||
### Sender Name Resolution with Bot Identity
|
||||
### Sender name resolution
|
||||
|
||||
When using bot identity (`--as bot`) to fetch messages (e.g. `+chat-messages-list`, `+threads-messages-list`, `+messages-mget`), sender names may not be resolved (shown as open_id instead of display name). This happens when the bot cannot access the user's contact info.
|
||||
As **bot**, the sender may show as `open_id` (bot visibility range doesn't cover it); `--as user` gives real names.
|
||||
|
||||
**Root cause**: The bot's app visibility settings do not include the message sender, so the contact API returns no name.
|
||||
```bash
|
||||
lark-cli im +chat-messages-list --chat-id oc_xxx --as bot # BAD: sender = open_id
|
||||
lark-cli im +chat-messages-list --chat-id oc_xxx --as user # GOOD: sender = real name
|
||||
```
|
||||
|
||||
**Solution**: Check the app's visibility settings in the Lark Developer Console — ensure the app's visible range covers the users whose names need to be resolved. Alternatively, use `--as user` to fetch messages with user identity, which typically has broader contact access.
|
||||
### Default message enrichment
|
||||
|
||||
### Default message enrichment (reactions / update_time)
|
||||
|
||||
The four message-pulling shortcuts (`+messages-mget`, `+chat-messages-list`, `+messages-search`, `+threads-messages-list`) automatically attach a `reactions` block and (for edited messages) `update_time` to each returned message — no separate `im.reactions.batch_query` call is needed. Pass `--no-reactions` to opt out. For the full contract (output shape, the `im:message.reactions:read` scope requirement, and the "missing field ≠ fetch failure" data rules), read [`references/lark-im-message-enrichment.md`](references/lark-im-message-enrichment.md).
|
||||
|
||||
### Opt-in resource auto-download (`--download-resources`)
|
||||
|
||||
`+chat-messages-list`, `+messages-mget`, and `+threads-messages-list` accept `--download-resources` (**off by default** — no `resources` block and no extra requests when omitted). When set, eligible message resources (image/file/audio/video/media + post-embedded; **stickers excluded**) are downloaded into `./lark-im-resources/` and each message gains a `resources` array of `{message_id, key, type, local_path, size_bytes}`. Downloads are deduped by `(message_id, file_key)`, run with bounded concurrency, and isolate single-resource failures (`error: true` + stderr warning). **Scope:** requires `im:message:readonly` (already declared by the listing commands — no extra scope); works under both user and bot identity. For one-off downloads use [`+messages-resources-download`](references/lark-im-messages-resources-download.md). Full contract: [`references/lark-im-message-enrichment.md`](references/lark-im-message-enrichment.md).
|
||||
|
||||
### Card Messages (Interactive)
|
||||
|
||||
Card messages (`interactive` type) are not yet supported for compact conversion in event subscriptions. The raw event data will be returned instead, with a hint printed to stderr.
|
||||
The four message-pulling shortcuts auto-attach `reactions` (+ `update_time` for edited messages) — no separate `reactions.batch_query` (needs `im:message.reactions:read`); `--no-reactions` opts out. `+chat-messages-list` / `+messages-mget` / `+threads-messages-list` also accept `--download-resources` (opt-in, off by default) to fetch message binaries into `./lark-im-resources/`. Contract: [`references/lark-im-message-enrichment.md`](references/lark-im-message-enrichment.md).
|
||||
|
||||
### Flag Types
|
||||
|
||||
Flags support two layers:
|
||||
|
||||
- **Message-layer flag**: `(ItemTypeDefault, FlagTypeMessage)` — regular message bookmark
|
||||
- **Feed-layer flag**: `(ItemTypeThread/ItemTypeMsgThread, FlagTypeFeed)` — thread as feed-layer bookmark
|
||||
|
||||
Item types for feed-layer flags:
|
||||
- **ItemTypeThread** (4) = thread in a topic-style chat
|
||||
- **ItemTypeMsgThread** (11) = thread in a regular chat
|
||||
Two layers (item_type auto-detected from chat mode — rarely set by hand):
|
||||
- **Message-layer** `(ItemTypeDefault, FlagTypeMessage)` — regular message bookmark.
|
||||
- **Feed-layer** `(ItemType{Thread|MsgThread}, FlagTypeFeed)` — thread bookmarked at feed level:
|
||||
- **ItemTypeThread** (4) = a topic in a topic-style chat (an entry in the group's Thread tab).
|
||||
- **ItemTypeMsgThread** (11) = a reply thread under a single message in a regular group.
|
||||
|
||||
### Feed Shortcut
|
||||
|
||||
Feed shortcuts add chats to the **current user's** feed sidebar. They are distinct from flags:
|
||||
Pins a chat to the **current user's** feed sidebar. Limits: **CHAT-type only** (oc_xxx); **user-identity only**; **10 per call** for create/remove; list uses opaque `page_token`.
|
||||
|
||||
- **Flag** = bookmark on a message/thread, scoped to the user's bookmark list.
|
||||
- **Feed shortcut** = entry in the user's feed sidebar (currently only chats).
|
||||
## 不在本 skill 范围
|
||||
|
||||
Key limits:
|
||||
- Only **CHAT-type** (`feed_card_id` is `oc_xxx`) is exposed via OpenAPI; doc/app/subscription shortcuts exist internally but are not yet whitelisted.
|
||||
- All three operations (create/remove/list) are **user-identity only** — they sign with `user_access_token`.
|
||||
- Batch size is **10 per call** for create/remove; list is a one-page wrapper with opaque `page_token` pagination.
|
||||
- 邮件 → [`lark-mail`](../lark-mail/SKILL.md)|日程/会议 → [`lark-calendar`](../lark-calendar/SKILL.md)|会议回放/纪要 → [`lark-vc`](../lark-vc/SKILL.md)
|
||||
- 文档评论 → [`lark-drive`](../lark-drive/SKILL.md)|IM 事件订阅 → [`lark-event`](../lark-event/SKILL.md)|姓名解析 open_id → [`lark-contact`](../lark-contact/SKILL.md)
|
||||
|
||||
群禁言 / 管理员 / 角色 / 解散 / 转让 / 群设置 等群治理 lark-cli im 暂无命令:如实告知“暂不支持”、勿臆造,引导用户到飞书客户端群设置手动操作(高风险写操作,勿擅自走原生 API 代执行)。
|
||||
|
||||
@@ -30,12 +30,8 @@ lark-cli schema {{service}}.<resource>.<method> # 调用 API 前必须先查
|
||||
lark-cli {{service}} <resource> <method> [flags] # 调用 API
|
||||
```
|
||||
|
||||
> **重要**:使用原生 API 时,必须先运行 `schema` 查看 `--data` / `--params` 参数结构,不要猜测字段格式。
|
||||
{{schema_note_block}}
|
||||
|
||||
{{resource_sections}}
|
||||
## 权限表
|
||||
|
||||
| 方法 | 所需 scope |
|
||||
|------|-----------|
|
||||
{{permission_rows}}
|
||||
{{permission_block}}
|
||||
{{/actions}}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: lark-im
|
||||
version: 1.0.0
|
||||
description: "飞书即时通讯:收发消息和管理群聊。发送和回复消息、搜索聊天记录、管理群聊成员、上传下载图片和文件(支持大文件分片下载)、管理表情回复、发送应用内/短信/电话加急。当用户需要发消息、查看或搜索聊天记录、下载聊天中的文件、查看群成员、搜索群、创建群聊或话题群、管理标记数据、管理 Feed 置顶(添加/移除/查询置顶会话)、管理标签数据时使用。"
|
||||
description: "飞书即时通讯:收发消息和管理群聊。发送和回复消息、搜索聊天记录、管理群聊成员、上传下载图片和文件(支持大文件分片下载)、管理表情回复、发送应用内/短信/电话加急。当用户需要发消息、查看或搜索聊天记录、下载聊天中的文件、查看群成员、搜索群、创建群聊或话题群、管理标记数据、管理 Feed 置顶、管理标签数据时使用。不负责收发邮件(→ lark-mail)、日程与会议安排(→ lark-calendar)、会议回放与纪要(→ lark-vc)、IM 事件订阅(→ lark-event)。"
|
||||
metadata:
|
||||
requires:
|
||||
bins: ["lark-cli"]
|
||||
@@ -12,104 +12,79 @@ metadata:
|
||||
|
||||
**CRITICAL — 开始前 MUST 先用 Read 工具读取 [`../lark-shared/SKILL.md`](../lark-shared/SKILL.md),其中包含认证、权限处理**
|
||||
|
||||
## Core Concepts
|
||||
|
||||
- **Message**: A single message in a chat, identified by `message_id` (om_xxx). Supports types: text, post, image, file, audio, video, sticker, interactive (card), share_chat, share_user, merge_forward, etc.
|
||||
- **Chat**: A group chat or P2P conversation, identified by `chat_id` (oc_xxx).
|
||||
- **Thread**: A reply thread under a message, identified by `thread_id` (om_xxx or omt_xxx).
|
||||
- **Reaction**: An emoji reaction on a message.
|
||||
- **Flag**: A bookmark on a message or thread.
|
||||
- **Feed Shortcut**: A chat pinned to the current user's feed sidebar, identified by `feed_card_id` (an `oc_xxx` open_chat_id for CHAT type).
|
||||
- **Feed Group**: A tag that groups feed cards in the feed list, identified by `feed_group_id` (ofg_xxx). Members are feed cards, each identified by `feed_id` + `feed_type`. Two types: `normal` (members managed explicitly) and `rule` (members auto-derived from rules).
|
||||
|
||||
## Resource Relationships
|
||||
|
||||
```
|
||||
Chat (oc_xxx)
|
||||
├── Message (om_xxx)
|
||||
│ ├── Thread (reply thread)
|
||||
│ ├── Reaction (emoji)
|
||||
│ └── Resource (image / file / video / audio)
|
||||
└── Member (user / bot)
|
||||
```
|
||||
|
||||
## Important Notes
|
||||
|
||||
### Identity and Token Mapping
|
||||
|
||||
- `--as user` means **user identity** and uses `user_access_token`. Calls run as the authorized end user, so permissions depend on both the app scopes and that user's own access to the target chat/message/resource.
|
||||
- `--as bot` means **bot identity** and uses `tenant_access_token`. Calls run as the app bot, so behavior depends on the bot's membership, app visibility, availability range, and bot-specific scopes.
|
||||
- If an IM API says it supports both `user` and `bot`, the token type changes who the operator is. The same API can succeed with one identity and fail with the other because owner/admin status, chat membership, tenant boundary, or app availability are checked against the current caller.
|
||||
|
||||
### Sender Name Resolution with Bot Identity
|
||||
|
||||
When using bot identity (`--as bot`) to fetch messages (e.g. `+chat-messages-list`, `+threads-messages-list`, `+messages-mget`), sender names may not be resolved (shown as open_id instead of display name). This happens when the bot cannot access the user's contact info.
|
||||
|
||||
**Root cause**: The bot's app visibility settings do not include the message sender, so the contact API returns no name.
|
||||
|
||||
**Solution**: Check the app's visibility settings in the Lark Developer Console — ensure the app's visible range covers the users whose names need to be resolved. Alternatively, use `--as user` to fetch messages with user identity, which typically has broader contact access.
|
||||
|
||||
### Default message enrichment (reactions / update_time)
|
||||
|
||||
The four message-pulling shortcuts (`+messages-mget`, `+chat-messages-list`, `+messages-search`, `+threads-messages-list`) automatically attach a `reactions` block and (for edited messages) `update_time` to each returned message — no separate `im.reactions.batch_query` call is needed. Pass `--no-reactions` to opt out. For the full contract (output shape, the `im:message.reactions:read` scope requirement, and the "missing field ≠ fetch failure" data rules), read [`references/lark-im-message-enrichment.md`](references/lark-im-message-enrichment.md).
|
||||
|
||||
### Opt-in resource auto-download (`--download-resources`)
|
||||
|
||||
`+chat-messages-list`, `+messages-mget`, and `+threads-messages-list` accept `--download-resources` (**off by default** — no `resources` block and no extra requests when omitted). When set, eligible message resources (image/file/audio/video/media + post-embedded; **stickers excluded**) are downloaded into `./lark-im-resources/` and each message gains a `resources` array of `{message_id, key, type, local_path, size_bytes}`. Downloads are deduped by `(message_id, file_key)`, run with bounded concurrency, and isolate single-resource failures (`error: true` + stderr warning). **Scope:** requires `im:message:readonly` (already declared by the listing commands — no extra scope); works under both user and bot identity. For one-off downloads use [`+messages-resources-download`](references/lark-im-messages-resources-download.md). Full contract: [`references/lark-im-message-enrichment.md`](references/lark-im-message-enrichment.md).
|
||||
|
||||
### Card Messages (Interactive)
|
||||
|
||||
Card messages (`interactive` type) are not yet supported for compact conversion in event subscriptions. The raw event data will be returned instead, with a hint printed to stderr.
|
||||
|
||||
### Flag Types
|
||||
|
||||
Flags support two layers:
|
||||
|
||||
- **Message-layer flag**: `(ItemTypeDefault, FlagTypeMessage)` — regular message bookmark
|
||||
- **Feed-layer flag**: `(ItemTypeThread/ItemTypeMsgThread, FlagTypeFeed)` — thread as feed-layer bookmark
|
||||
|
||||
Item types for feed-layer flags:
|
||||
- **ItemTypeThread** (4) = thread in a topic-style chat
|
||||
- **ItemTypeMsgThread** (11) = thread in a regular chat
|
||||
|
||||
### Feed Shortcut
|
||||
|
||||
Feed shortcuts add chats to the current user's feed sidebar. They are distinct from flags:
|
||||
|
||||
- **Flag** = bookmark on a message/thread, scoped to the user's bookmark list.
|
||||
- **Feed shortcut** = entry in the user's feed sidebar (currently only chats).
|
||||
|
||||
Key limits:
|
||||
- Only **CHAT-type** (`feed_card_id` is `oc_xxx`) is exposed via OpenAPI; doc/app/subscription shortcuts exist internally but are not yet whitelisted.
|
||||
- All three operations (create/remove/list) are **user-identity only** — they sign with `user_access_token`.
|
||||
- Batch size is **10 per call** for create/remove; list is a one-page wrapper with opaque `page_token` pagination.
|
||||
|
||||
## Shortcuts(推荐优先使用)
|
||||
|
||||
Shortcut 是对常用操作的高级封装(`lark-cli im +<verb> [flags]`)。有 Shortcut 的操作优先使用。
|
||||
|
||||
| Shortcut | 说明 |
|
||||
|----------|------|
|
||||
| [`+chat-create`](references/lark-im-chat-create.md) | Create a group chat or topic chat; user/bot; --chat-mode group|topic; private/public; invites users/bots; optionally sets bot manager |
|
||||
| [`+chat-list`](references/lark-im-chat-list.md) | List chats the current user/bot is a member of; defaults to groups; pass --types=p2p,group to include p2p single chats (user-only); user/bot; supports sorting, pagination, --exclude-muted (user-only) |
|
||||
| [`+chat-messages-list`](references/lark-im-chat-messages-list.md) | List messages in a chat or P2P conversation; user/bot; accepts --chat-id or --user-id, resolves P2P chat_id, supports time range/sort/pagination |
|
||||
| [`+chat-search`](references/lark-im-chat-search.md) | Search visible group chats by --query keyword and/or --member-ids; user/bot; e.g. look up chat_id by group name; supports type filters, sorting, pagination, and --exclude-muted (user identity only) |
|
||||
| [`+chat-update`](references/lark-im-chat-update.md) | Update group chat name or description; user/bot; updates a chat's name or description |
|
||||
| [`+messages-mget`](references/lark-im-messages-mget.md) | Batch get messages by IDs; user/bot; fetches up to 50 om_ message IDs, formats sender names, expands thread replies |
|
||||
| [`+messages-reply`](references/lark-im-messages-reply.md) | Reply to a message (supports thread replies); user/bot; supports text/markdown/post/media replies, reply-in-thread, idempotency key |
|
||||
| [`+messages-resources-download`](references/lark-im-messages-resources-download.md) | Download images/files from a message; user/bot; supports automatic chunked download for large files (8MB chunks), auto-detects file extension from Content-Type |
|
||||
| [`+messages-search`](references/lark-im-messages-search.md) | Search messages across chats (supports keyword, sender, time range filters) with user identity; user-only; filters by chat/sender/attachment/time, supports auto-pagination via `--page-all` / `--page-limit`, enriches results via batched mget and chats batch_query |
|
||||
| [`+messages-send`](references/lark-im-messages-send.md) | Send a message to a chat or direct message; user/bot; sends to chat-id or user-id with text/markdown/post/media, supports idempotency key |
|
||||
| [`+threads-messages-list`](references/lark-im-threads-messages-list.md) | List messages in a thread; user/bot; accepts om_/omt_ input, resolves message IDs to thread_id, supports sort/pagination |
|
||||
| [`+flag-create`](references/lark-im-flag-create.md) | Create a bookmark on a message; user-only; defaults to message-layer flag; use --flag-type feed for feed-layer flag (item_type auto-detected from chat mode) |
|
||||
| [`+flag-cancel`](references/lark-im-flag-cancel.md) | Cancel (remove) a bookmark. When no --flag-type is given, best-effort double-cancel: removes message layer and (when chat_type is determinable) feed layer |
|
||||
| [`+flag-list`](references/lark-im-flag-list.md) | List bookmarks; user-only; auto-enriches feed-type thread entries with message content; supports `--page-all` auto-pagination |
|
||||
| [`+feed-shortcut-create`](references/lark-im-feed-shortcut-create.md) | Add chats to the user's feed shortcuts; user-only; oc_xxx chat IDs only; batch up to 10 per call; `--head`/`--tail` controls insertion order; partial failures return an `ok:false` ledger |
|
||||
| [`+feed-shortcut-remove`](references/lark-im-feed-shortcut-remove.md) | Remove chats from the user's feed shortcuts; user-only; batch up to 10 per call; removing an absent shortcut is idempotent success; real per-item failures return an `ok:false` ledger |
|
||||
| [`+feed-shortcut-list`](references/lark-im-feed-shortcut-list.md) | List one page of the user's feed shortcuts; user-only; omit `--page-token` for the first page; default output enriches CHAT entries under `detail`; pass `--no-detail` to skip the extra lookup and `im:chat:read` scope |
|
||||
| [`+feed-group-list`](references/lark-im-feed-group-list.md) | List the caller's feed groups (tags); user-only; supports `--page-all` auto-pagination |
|
||||
| [`+feed-group-list-item`](references/lark-im-feed-group-list-item.md) | List feed cards in a feed group (tag); user-only; enriches each item with chat_name resolved from feed_id; supports --page-all auto-pagination |
|
||||
| [`+feed-group-query-item`](references/lark-im-feed-group-query-item.md) | Look up specific feed cards in a feed group (tag) by ID; user-only; enriches each item with chat_name resolved from feed_id |
|
||||
| [`+chat-create`](references/lark-im-chat-create.md) | Create a group chat or topic chat |
|
||||
| [`+chat-list`](references/lark-im-chat-list.md) | List chats the current user/bot is a member of |
|
||||
| [`+chat-messages-list`](references/lark-im-chat-messages-list.md) | List messages in a chat or P2P conversation |
|
||||
| [`+chat-search`](references/lark-im-chat-search.md) | Search visible group chats by --query keyword and/or --member-ids |
|
||||
| [`+chat-update`](references/lark-im-chat-update.md) | Update group chat name or description |
|
||||
| [`+messages-mget`](references/lark-im-messages-mget.md) | Batch get messages by IDs |
|
||||
| [`+messages-reply`](references/lark-im-messages-reply.md) | Reply to a message (supports thread replies) |
|
||||
| [`+messages-resources-download`](references/lark-im-messages-resources-download.md) | Download images/files from a message |
|
||||
| [`+messages-search`](references/lark-im-messages-search.md) | Search messages across chats (supports keyword, sender, time range filters) with user identity |
|
||||
| [`+messages-send`](references/lark-im-messages-send.md) | Send a message to a chat or direct message |
|
||||
| [`+threads-messages-list`](references/lark-im-threads-messages-list.md) | List messages in a thread |
|
||||
| [`+flag-create`](references/lark-im-flag-create.md) | Create a bookmark on a message |
|
||||
| [`+flag-cancel`](references/lark-im-flag-cancel.md) | Cancel (remove) a bookmark |
|
||||
| [`+flag-list`](references/lark-im-flag-list.md) | List bookmarks |
|
||||
| [`+feed-shortcut-create`](references/lark-im-feed-shortcut-create.md) | Add chats to the user's feed shortcuts |
|
||||
| [`+feed-shortcut-remove`](references/lark-im-feed-shortcut-remove.md) | Remove chats from the user's feed shortcuts |
|
||||
| [`+feed-shortcut-list`](references/lark-im-feed-shortcut-list.md) | List one page of the user's feed shortcuts |
|
||||
| [`+feed-group-list`](references/lark-im-feed-group-list.md) | List the caller's feed groups (tags) |
|
||||
| [`+feed-group-list-item`](references/lark-im-feed-group-list-item.md) | List feed cards in a feed group (tag) |
|
||||
| [`+feed-group-query-item`](references/lark-im-feed-group-query-item.md) | Look up specific feed cards in a feed group (tag) by ID |
|
||||
|
||||
## Core Concepts
|
||||
|
||||
- **Message** `message_id` (om_xxx) · **Chat** `chat_id` (oc_xxx, group or P2P) · **Thread** `thread_id` (om_xxx / omt_xxx).
|
||||
- **Flag** — bookmark on a message/thread (two layers, see below).
|
||||
- **Feed Shortcut** `feed_card_id` (oc_xxx) — a chat pinned to the user's feed sidebar.
|
||||
- **Feed Group** `feed_group_id` (ofg_xxx) — a tag grouping feed cards (`feed_id`+`feed_type`); `normal` (explicit) / `rule` (auto-derived).
|
||||
|
||||
## Important Notes
|
||||
|
||||
### Identity (user vs bot)
|
||||
|
||||
- `--as user` (`user_access_token`): runs as the authorized user; permission = app scopes + that user's own access to the target.
|
||||
- `--as bot` (`tenant_access_token`): runs as the app bot; depends on bot's chat membership, app visibility range, bot scopes.
|
||||
- When an API supports both, the token decides *who* operates — owner/admin, membership, tenant, visibility are checked against the caller, so the same API can pass on one identity and fail on the other.
|
||||
|
||||
### Sender name resolution
|
||||
|
||||
As **bot**, the sender may show as `open_id` (bot visibility range doesn't cover it); `--as user` gives real names.
|
||||
|
||||
```bash
|
||||
lark-cli im +chat-messages-list --chat-id oc_xxx --as bot # BAD: sender = open_id
|
||||
lark-cli im +chat-messages-list --chat-id oc_xxx --as user # GOOD: sender = real name
|
||||
```
|
||||
|
||||
### Default message enrichment
|
||||
|
||||
The four message-pulling shortcuts auto-attach `reactions` (+ `update_time` for edited messages) — no separate `reactions.batch_query` (needs `im:message.reactions:read`); `--no-reactions` opts out. `+chat-messages-list` / `+messages-mget` / `+threads-messages-list` also accept `--download-resources` (opt-in, off by default) to fetch message binaries into `./lark-im-resources/`. Contract: [`references/lark-im-message-enrichment.md`](references/lark-im-message-enrichment.md).
|
||||
|
||||
### Flag Types
|
||||
|
||||
Two layers (item_type auto-detected from chat mode — rarely set by hand):
|
||||
- **Message-layer** `(ItemTypeDefault, FlagTypeMessage)` — regular message bookmark.
|
||||
- **Feed-layer** `(ItemType{Thread|MsgThread}, FlagTypeFeed)` — thread bookmarked at feed level:
|
||||
- **ItemTypeThread** (4) = a topic in a topic-style chat (an entry in the group's Thread tab).
|
||||
- **ItemTypeMsgThread** (11) = a reply thread under a single message in a regular group.
|
||||
|
||||
### Feed Shortcut
|
||||
|
||||
Pins a chat to the **current user's** feed sidebar. Limits: **CHAT-type only** (oc_xxx); **user-identity only**; **10 per call** for create/remove; list uses opaque `page_token`.
|
||||
|
||||
## 不在本 skill 范围
|
||||
|
||||
- 邮件 → [`lark-mail`](../lark-mail/SKILL.md)|日程/会议 → [`lark-calendar`](../lark-calendar/SKILL.md)|会议回放/纪要 → [`lark-vc`](../lark-vc/SKILL.md)
|
||||
- 文档评论 → [`lark-drive`](../lark-drive/SKILL.md)|IM 事件订阅 → [`lark-event`](../lark-event/SKILL.md)|姓名解析 open_id → [`lark-contact`](../lark-contact/SKILL.md)
|
||||
|
||||
群禁言 / 管理员 / 角色 / 解散 / 转让 / 群设置 等群治理 lark-cli im 暂无命令:如实告知“暂不支持”、勿臆造,引导用户到飞书客户端群设置手动操作(高风险写操作,勿擅自走原生 API 代执行)。
|
||||
|
||||
## API Resources
|
||||
|
||||
@@ -118,114 +93,47 @@ lark-cli schema im.<resource>.<method> # 调用 API 前必须先查看参数
|
||||
lark-cli im <resource> <method> [flags] # 调用 API
|
||||
```
|
||||
|
||||
> **重要**:使用原生 API 时,必须先运行 `schema` 查看 `--data` / `--params` 参数结构,不要猜测字段格式。
|
||||
|
||||
### chats
|
||||
|
||||
- `create` — 创建群。Identity: `bot` only (`tenant_access_token`).
|
||||
- `get` — 获取群信息。Identity: supports `user` and `bot`; the caller must be in the target chat to get full details, and must belong to the same tenant for internal chats.
|
||||
- `link` — 获取群分享链接。Identity: supports `user` and `bot`; the caller must be in the target chat, must be an owner or admin when chat sharing is restricted to owners/admins, and must belong to the same tenant for internal chats.
|
||||
- `update` — 更新群信息。Identity: supports `user` and `bot`.
|
||||
`create`(bot) · `get`(user/bot) · `link`(user/bot) · `update`(user/bot)
|
||||
|
||||
### chat.members
|
||||
|
||||
- `bots` — 获取群内机器人列表。Identity: supports `user` and `bot`; the caller must be in the target chat and must belong to the same tenant for internal chats.
|
||||
- `create` — 将用户或机器人拉入群聊。Identity: supports `user` and `bot`; the caller must be in the target chat; for `bot` calls, added users must be within the app's availability; for internal chats the operator must belong to the same tenant; if only owners/admins can add members, the caller must be an owner/admin, or a chat-creator bot with `im:chat:operate_as_owner`.
|
||||
- `delete` — 将用户或机器人移出群聊。Identity: supports `user` and `bot`; only group owner, admin, or creator bot can remove others; max 50 users or 5 bots per request.
|
||||
- `get` — 获取群成员列表。Identity: supports `user` and `bot`; the caller must be in the target chat and must belong to the same tenant for internal chats.
|
||||
`bots`(user/bot) · `create`(user/bot) · `delete`(user/bot) · `get`(user/bot)
|
||||
|
||||
### chat.user_setting
|
||||
|
||||
- `batch_query` — 批量查询当前用户在群内的个人偏好设置 (e.g. `is_muted` mutes normal messages, `is_mute_at_all` mutes @all messages); up to 10 chats per request. Identity: `user` only (`user_access_token`); the caller must be in each target chat.
|
||||
- `batch_update` — 批量更新当前用户在群内的个人偏好设置 (e.g. `is_muted` mutes normal messages, `is_mute_at_all` mutes @all messages); up to 10 chats per request. Identity: `user` only (`user_access_token`); the caller must be in each target chat.
|
||||
`batch_query`(user) · `batch_update`(user)
|
||||
|
||||
### chat.managers
|
||||
|
||||
- `add_managers` — 指定群管理员。Identity: supports `user` and `bot`; only the group owner can add managers; max 10 managers per chat (20 for super-large chats), and at most 5 bots per request.
|
||||
- `delete_managers` — 删除群管理员。Identity: supports `user` and `bot`; only the group owner can remove managers; max 50 users or 5 bots per request.
|
||||
`add_managers`(user/bot) · `delete_managers`(user/bot)
|
||||
|
||||
### chat.moderation
|
||||
|
||||
- `get` — 获取群成员发言权限。Identity: supports `user` and `bot`; the caller must be in the target chat and belong to the same tenant.
|
||||
- `update` — 更新群发言权限。Identity: supports `user` and `bot`; only the group owner (or creator bot with `im:chat:operate_as_owner`) can update; the caller must be in the chat.
|
||||
`get`(user/bot) · `update`(user/bot)
|
||||
|
||||
### messages
|
||||
|
||||
- `delete` — 撤回消息。Identity: supports `user` and `bot`; for `bot` calls, the bot must be in the chat to revoke group messages; to revoke another user's group message, the bot must be the owner, an admin, or the creator; for user P2P recalls, the target user must be within the bot's availability.
|
||||
- `forward` — 转发消息。Identity: supports `user` and `bot`.
|
||||
- `merge_forward` — 合并转发消息。Identity: `bot` only (`tenant_access_token`).
|
||||
- `read_users` — 查询消息已读信息。Identity: `bot` only (`tenant_access_token`); the bot must be in the chat, and can only query read status for messages it sent within the last 7 days.
|
||||
- `urgent_app` — 发送应用内加急。Identity: `bot` only (`tenant_access_token`); the bot must be the message sender and must be in the conversation that contains the message.
|
||||
- `urgent_phone` — 发送电话加急。Identity: `bot` only (`tenant_access_token`); the bot must be the message sender and must be in the conversation that contains the message.
|
||||
- `urgent_sms` — 发送短信加急。Identity: `bot` only (`tenant_access_token`); the bot must be the message sender and must be in the conversation that contains the message.
|
||||
`delete`(user/bot) · `forward`(user/bot) · `merge_forward`(bot) · `read_users`(bot) · `urgent_app`(bot) · `urgent_phone`(bot) · `urgent_sms`(bot)
|
||||
|
||||
### reactions
|
||||
|
||||
- `batch_query` — 批量获取消息表情。Identity: supports `user` and `bot`.[Must-read](references/lark-im-reactions.md)
|
||||
- `create` — 添加消息表情回复。Identity: supports `user` and `bot`; the caller must be in the conversation that contains the message.[Must-read](references/lark-im-reactions.md)
|
||||
- `delete` — 删除消息表情回复。Identity: supports `user` and `bot`; the caller must be in the conversation that contains the message, and can only delete reactions added by itself.[Must-read](references/lark-im-reactions.md)
|
||||
- `list` — 获取消息表情回复。Identity: supports `user` and `bot`; the caller must be in the conversation that contains the message.[Must-read](references/lark-im-reactions.md)
|
||||
`batch_query`(user/bot) · `create`(user/bot) · `delete`(user/bot) · `list`(user/bot)
|
||||
|
||||
### threads
|
||||
|
||||
- `forward` — 转发话题。Identity: supports `user` and `bot`.
|
||||
`forward`(user/bot)
|
||||
|
||||
### images
|
||||
|
||||
- `create` — 上传图片。Identity: `bot` only (`tenant_access_token`).
|
||||
`create`(bot)
|
||||
|
||||
### pins
|
||||
|
||||
- `create` — Pin 消息。Identity: supports `user` and `bot`.
|
||||
- `delete` — 移除 Pin 消息。Identity: supports `user` and `bot`.
|
||||
- `list` — 获取群内 Pin 消息。Identity: supports `user` and `bot`.
|
||||
`create`(user/bot) · `delete`(user/bot) · `list`(user/bot)
|
||||
|
||||
### feed.groups
|
||||
|
||||
- `batch_add_item` — Batch add feed cards to a feed group. Identity: `user` only (`user_access_token`).[Must-read](references/lark-im-feed-groups.md)
|
||||
- `batch_query` — Batch query feed groups. Identity: `user` only (`user_access_token`).[Must-read](references/lark-im-feed-groups.md)
|
||||
- `batch_remove_item` — Batch remove feed cards from a feed group. Identity: `user` only (`user_access_token`).[Must-read](references/lark-im-feed-groups.md)
|
||||
- `create` — Create a feed group. Identity: `user` only (`user_access_token`).[Must-read](references/lark-im-feed-groups.md)
|
||||
- `delete` — Delete a feed group. Identity: `user` only (`user_access_token`).[Must-read](references/lark-im-feed-groups.md)
|
||||
- `update` — Update a feed group. Identity: `user` only (`user_access_token`).[Must-read](references/lark-im-feed-groups.md)
|
||||
`batch_add_item`(user) · `batch_query`(user) · `batch_remove_item`(user) · `create`(user) · `delete`(user) · `update`(user)
|
||||
|
||||
## 权限表
|
||||
|
||||
| 方法 | 所需 scope |
|
||||
|------|-----------|
|
||||
| `chats.create` | `im:chat:create` |
|
||||
| `chats.get` | `im:chat:read` |
|
||||
| `chats.link` | `im:chat:read` |
|
||||
| `chats.update` | `im:chat:update` |
|
||||
| `chat.members.bots` | `im:chat.members:read` |
|
||||
| `chat.members.create` | `im:chat.members:write_only` |
|
||||
| `chat.members.delete` | `im:chat.members:write_only` |
|
||||
| `chat.members.get` | `im:chat.members:read` |
|
||||
| `chat.user_setting.batch_query` | `im:chat.user_setting:read` |
|
||||
| `chat.user_setting.batch_update` | `im:chat.user_setting:write` |
|
||||
| `chat.managers.add_managers` | `im:chat.managers:write_only` |
|
||||
| `chat.managers.delete_managers` | `im:chat.managers:write_only` |
|
||||
| `chat.moderation.get` | `im:chat.moderation:read` |
|
||||
| `chat.moderation.update` | `im:chat:moderation:write_only` |
|
||||
| `messages.delete` | `im:message:recall` |
|
||||
| `messages.forward` | `im:message` |
|
||||
| `messages.merge_forward` | `im:message` |
|
||||
| `messages.read_users` | `im:message:readonly` |
|
||||
| `messages.urgent_app` | `im:message.urgent` |
|
||||
| `messages.urgent_phone` | `im:message.urgent:phone` |
|
||||
| `messages.urgent_sms` | `im:message.urgent:sms` |
|
||||
| `reactions.batch_query` | `im:message.reactions:read` |
|
||||
| `reactions.create` | `im:message.reactions:write_only` |
|
||||
| `reactions.delete` | `im:message.reactions:write_only` |
|
||||
| `reactions.list` | `im:message.reactions:read` |
|
||||
| `threads.forward` | `im:message` |
|
||||
| `images.create` | `im:resource` |
|
||||
| `pins.create` | `im:message.pins:write_only` |
|
||||
| `pins.delete` | `im:message.pins:write_only` |
|
||||
| `pins.list` | `im:message.pins:read` |
|
||||
| `feed.groups.batch_add_item` | `im:feed_group_v1:write` |
|
||||
| `feed.groups.batch_query` | `im:feed_group_v1:read` |
|
||||
| `feed.groups.batch_remove_item` | `im:feed_group_v1:write` |
|
||||
| `feed.groups.create` | `im:feed_group_v1:write` |
|
||||
| `feed.groups.delete` | `im:feed_group_v1:write` |
|
||||
| `feed.groups.update` | `im:feed_group_v1:write` |
|
||||
|
||||
@@ -1,162 +1,40 @@
|
||||
# im +chat-create
|
||||
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first to understand authentication, global parameters, and safety rules.
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first for authentication, global parameters, and safety rules.
|
||||
|
||||
Create a group chat. Supports both user identity (`--as user`) and bot identity (`--as bot`). You can specify the group name, description, members (users/bots), owner, chat type (private/public), and group mode. Set `--chat-mode topic` to create a topic chat.
|
||||
Maps to `lark-cli im +chat-create`. **Run `lark-cli im +chat-create --help` for the authoritative flags (`--name` / `--description` / `--users` / `--bots` / `--owner` / `--type` / `--chat-mode` / `--set-bot-manager` / `--as`), limits, and enums.** This file covers only what `--help` cannot.
|
||||
|
||||
This skill maps to the shortcut: `lark-cli im +chat-create` (internally calls `POST /open-apis/im/v1/chats`).
|
||||
Identity choice follows [Group Chat Identity Rules](lark-im-chat-identity.md). Scope: `--as bot` needs `im:chat:create`; `--as user` needs `im:chat:create_by_user`.
|
||||
|
||||
- `--as bot` requires the `im:chat:create` scope.
|
||||
- `--as user` requires the `im:chat:create_by_user` scope.
|
||||
## Gotchas
|
||||
|
||||
## Commands
|
||||
- **`--chat-mode topic` ≠ "normal group in topic-message mode".** `topic` makes the *entire* group a 话题群. A normal group (`chat_mode=group`) with `group_message_type=thread` is a different thing — and this CLI intentionally does NOT surface `group_message_type`. When the user asks for a topic chat, pass `--chat-mode topic` explicitly; never rely on the default.
|
||||
- **`--type public` defaults to `private`** — only pass `public` when the user explicitly asks for a discoverable group.
|
||||
- **`--set-bot-manager` is silently a no-op without `--as bot`** (effective only when the creating bot is also a member).
|
||||
|
||||
```bash
|
||||
# Create a private group (default)
|
||||
lark-cli im +chat-create --name "My Group"
|
||||
## The bot-invisible-members trap → create-then-add recipe
|
||||
|
||||
# Create a public group (name is required and must be at least 2 characters)
|
||||
lark-cli im +chat-create --name "Public Group" --type public
|
||||
A bot **cannot** invite users who are mutually invisible to it during creation — passing them in `--users` fails the *whole* request with **232043**. Do NOT pass arbitrary users in `--users` under `--as bot`. Instead:
|
||||
|
||||
# Create a topic chat
|
||||
lark-cli im +chat-create --name "Topic Group" --chat-mode topic
|
||||
|
||||
# Specify the group owner
|
||||
lark-cli im +chat-create --name "My Group" --owner ou_xxx
|
||||
|
||||
# Invite user members (comma-separated open_ids, up to 50)
|
||||
lark-cli im +chat-create --name "My Group" --users "ou_aaa,ou_bbb"
|
||||
|
||||
# Invite bot members (comma-separated app IDs, up to 5)
|
||||
lark-cli im +chat-create --name "My Group" --bots "cli_aaa,cli_bbb"
|
||||
|
||||
# Invite both users and bots
|
||||
lark-cli im +chat-create --name "My Group" --users "ou_aaa" --bots "cli_aaa"
|
||||
|
||||
# Make the creating bot a group manager (bot identity only)
|
||||
lark-cli im +chat-create --name "My Group" --set-bot-manager --as bot
|
||||
|
||||
# JSON output
|
||||
lark-cli im +chat-create --name "My Group" --format json
|
||||
|
||||
# Create a group with bot identity
|
||||
lark-cli im +chat-create --name "My Group" --users "ou_aaa" --as bot
|
||||
|
||||
# Create a group with user identity
|
||||
lark-cli im +chat-create --name "My Group" --users "ou_aaa,ou_bbb" --as user
|
||||
|
||||
# Preview the request without creating anything
|
||||
lark-cli im +chat-create --name "My Group" --dry-run
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Required | Limits | Description |
|
||||
|------|------|------|------|
|
||||
| `--name <name>` | Required for public groups | Max 60 characters; at least 2 characters for public groups | Group name (`"(no subject)"` for private groups if omitted) |
|
||||
| `--description <text>` | No | Max 100 characters | Group description |
|
||||
| `--users <ids>` | No | Up to 50, format `ou_xxx` | Comma-separated user open_ids |
|
||||
| `--bots <ids>` | No | Up to 5, format `cli_xxx` | Comma-separated bot app IDs |
|
||||
| `--owner <open_id>` | No | Format `ou_xxx` | Owner open_id (defaults to the bot when using `--as bot`, or the authorized user when using `--as user`) |
|
||||
| `--type <type>` | No | `private` (default) or `public` | Group type. Default to `private`; pass `public` only when the user explicitly asks for a discoverable/public group. |
|
||||
| `--chat-mode <mode>` | No | `group` (default) or `topic` | Group mode; `topic` creates a topic chat (not the same as `group_message_type=thread`). When the user asks for a topic chat, pass `topic` explicitly — do not rely on the default. |
|
||||
| `--set-bot-manager` | No | - | Set the creating bot as a group manager (only effective with `--as bot`) |
|
||||
| `--format json` | No | - | Output as JSON |
|
||||
| `--as <identity>` | No | `bot` or `user` | Identity type |
|
||||
| `--dry-run` | No | - | Preview the request without executing it |
|
||||
|
||||
> **`--chat-mode topic` vs "normal group with topic-message mode"**: `--chat-mode topic` here creates a 话题群 — the entire group is a topic chat. This is different from "normal group (`chat_mode=group`) + topic-message mode (`group_message_type=thread`)". This CLI exposes only `chat_mode`; `group_message_type` is intentionally not surfaced.
|
||||
|
||||
## AI Usage Guidance
|
||||
|
||||
### When using `--as bot`
|
||||
|
||||
Bot may fail to invite users who are mutually invisible to it during group creation (error 232043). To avoid this, use the **two-step flow** below instead of passing other users' open_ids in `--users`.
|
||||
|
||||
1. **Get the current user's open_id:** Run `lark-cli contact +search-user --query "<name or email>"` to retrieve it.
|
||||
2. **Create the group — by default include the current user:**
|
||||
1. Resolve the **current user's** open_id (`lark-cli contact +search-user --query "<name|email>"`).
|
||||
2. Create the group with the bot, including **only the current user** (omit `--users` entirely only if the user explicitly says "bot-only group" / "do not add me"):
|
||||
|
||||
```bash
|
||||
lark-cli im +chat-create --name "<group name>" \
|
||||
--users "<current user open_id>" --as bot
|
||||
lark-cli im +chat-create --name "<name>" --users "<current user open_id>" --as bot
|
||||
```
|
||||
|
||||
**Default behavior:** Always add the current user to the group, unless the user explicitly says "do not add me" or "bot-only group" — only then omit `--users`.
|
||||
|
||||
3. **Add other members via user identity** (requires the current user to be in the group):
|
||||
3. Add the remaining members with **user identity** (requires the current user to be in the group), tolerating unreachable IDs via `succeed_type=1`:
|
||||
|
||||
```bash
|
||||
lark-cli im chat.members create \
|
||||
--params '{"chat_id":"<chat_id from step 2>","member_id_type":"open_id","succeed_type":1}' \
|
||||
--data '{"id_list":["ou_aaa","ou_bbb"]}' \
|
||||
--as user
|
||||
--data '{"id_list":["ou_aaa","ou_bbb"]}' --as user
|
||||
```
|
||||
|
||||
`succeed_type=1` ensures reachable users are added successfully; unreachable ones are returned in `invalid_id_list` instead of failing the whole request.
|
||||
4. **Check `invalid_id_list`** in the response; report any members that could not be added.
|
||||
|
||||
4. **Check `invalid_id_list`** in the response. If non-empty, report to the user which members could not be added.
|
||||
Under `--as user` there is no visibility limit, so create + invite happens in one call; the authorized user is automatically creator and member.
|
||||
|
||||
### When using `--as user`
|
||||
## HELP-GAP — not yet in `--help`/schema; keep until CLI adds it
|
||||
|
||||
User identity does not have the bot visibility limitation, so you can create the group and invite members in one step:
|
||||
|
||||
```bash
|
||||
lark-cli im +chat-create --name "<group name>" --users "ou_aaa,ou_bbb" --as user
|
||||
```
|
||||
|
||||
The authorized user is automatically the group creator and member.
|
||||
|
||||
## Output Fields
|
||||
|
||||
| Field | Description |
|
||||
|------|------|
|
||||
| `chat_id` | The new group's ID (`oc_xxx` format) |
|
||||
| `name` | Group name |
|
||||
| `chat_type` | Group type (`private` / `public`) |
|
||||
| `owner_id` | Owner ID (may be empty when a bot creates the group and `--owner` is not specified) |
|
||||
| `external` | Whether the group is external |
|
||||
| `share_link` | Group share link (omitted if retrieval fails) |
|
||||
|
||||
## Usage Scenarios
|
||||
|
||||
### Scenario 1: Create a group and specify the owner
|
||||
|
||||
```bash
|
||||
lark-cli im +chat-create --name "Project Discussion Group" --owner ou_xxx
|
||||
```
|
||||
|
||||
### Scenario 2: Create a group and invite users and a bot
|
||||
|
||||
```bash
|
||||
lark-cli im +chat-create --name "Project Discussion Group" \
|
||||
--owner ou_xxx \
|
||||
--users "ou_aaa,ou_bbb" \
|
||||
--bots "cli_aaa"
|
||||
```
|
||||
|
||||
### Scenario 3: Create a group and send a welcome message
|
||||
|
||||
```bash
|
||||
CHAT_ID=$(lark-cli im +chat-create --name "New Group" --format json | jq -r '.data.chat_id')
|
||||
lark-cli im +messages-send --chat-id "$CHAT_ID" --text "Welcome, everyone!"
|
||||
```
|
||||
|
||||
## Common Errors and Troubleshooting
|
||||
|
||||
| Symptom | Root Cause | Solution |
|
||||
|---------|---------|---------|
|
||||
| Permission denied (99991672) | The app does not have `im:chat:create` (bot) or `im:chat:create_by_user` (user) permission enabled | Enable the required permission for the app in the Open Platform console |
|
||||
| `--name is required for public groups and must be at least 2 characters` | A public group was created without a name or with a name shorter than 2 characters | Provide a name with at least 2 characters |
|
||||
| `--name exceeds the maximum of 60 characters` | The group name is too long | Shorten the name to 60 characters or fewer |
|
||||
| `--description exceeds the maximum of 100 characters` | The group description is too long | Shorten the description to 100 characters or fewer |
|
||||
| `--users exceeds the maximum of 50` | Too many user members were provided | Split the operation into batches and add more members later |
|
||||
| `--bots exceeds the maximum of 5` | Too many bot members were provided | Invite at most 5 bots at once |
|
||||
| `invalid user id: expected open_id (ou_xxx)` | Invalid user ID format | Use the `ou_xxx` format for users |
|
||||
| `invalid bot id: expected app ID (cli_xxx)` | Invalid bot ID format | Use the `cli_xxx` format for bots |
|
||||
| `invalid --owner: expected open_id (ou_xxx)` | Invalid owner ID format | Use the `ou_xxx` format for the owner |
|
||||
| `bot is invisible to user` (232043) | The bot and target users are mutually invisible | Follow the two-step flow in AI Usage Guidance above — do not pass other users in `--users` during creation |
|
||||
|
||||
## References
|
||||
|
||||
- [lark-im](../SKILL.md) - all IM commands
|
||||
- [lark-shared](../../lark-shared/SKILL.md) - authentication and global parameters
|
||||
- **Output fields**: `chat_id` (oc_xxx) · `name` · `chat_type` (`private` / `public`) · `owner_id` (**may be empty** when a bot creates the group and `--owner` is unset) · `external` · `share_link` (**omitted if retrieval fails**).
|
||||
|
||||
@@ -1,55 +1,26 @@
|
||||
# Group Chat Identity Rules
|
||||
|
||||
> Warning: The most common source of failure in group operations is choosing the wrong identity. Confirm the identity before performing the action.
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first for authentication, global parameters, and safety rules.
|
||||
|
||||
Group-chat operations support both `--as user` (UAT user identity) and `--as bot` (TAT bot identity). Choosing the correct identity is critical for success.
|
||||
Concept doc (no direct command). These cross-cutting identity/ownership rules apply to [`+chat-create`](lark-im-chat-create.md), [`+chat-update`](lark-im-chat-update.md), and the member-management flow (`im chat.members ...`). **The most common cause of group-operation failure is choosing the wrong identity** — decide it before acting.
|
||||
|
||||
## Basic Principles
|
||||
## Gotchas
|
||||
|
||||
- **If the user explicitly specifies an identity:** use exactly what the user requested (`--as user` or `--as bot`) without guessing.
|
||||
- **If the user does not specify an identity:** infer the correct identity from context instead of relying on the default.
|
||||
- **If the user names an identity, use it verbatim** (`--as user` / `--as bot`) — do not second-guess. Only infer when the user is silent; never just take the default.
|
||||
- **Adding members → prefer `--as user`.** Bot visibility is limited and fails when the target is mutually invisible to the bot (**232024** for member-add, **232043** during create). See the create-then-add recipe in [`+chat-create`](lark-im-chat-create.md); do not retry the bot blindly.
|
||||
- **Owner-level actions need owner/admin identity** (rename/permissions: **232016 / 232002 / 232017** if under-privileged; **232011** if not in the group).
|
||||
|
||||
## Identity Selection by Operation
|
||||
## Inferring the owner (when an owner-level action is needed and the owner is unknown)
|
||||
|
||||
| Operation | Recommended Identity | Why |
|
||||
|------|---------|-----------------------------------|
|
||||
| Create group (`+chat-create`) | Depends on the scenario | Infer from context |
|
||||
| Add members (member-management flow) | `--as user` | Bot visibility is limited and often fails when the target user is mutually invisible to the bot (232024) |
|
||||
| Update group (`+chat-update`) | Owner identity | Permission changes require owner/admin privileges; owner transfer requires owner identity |
|
||||
1. Bot created the group, `--owner` **unset** → owner is the bot (`--as bot`).
|
||||
2. Bot created the group, `--owner ou_xxx` **set** → owner is that user (`--as user`).
|
||||
3. User created the group, `--owner` **unset** → owner is the current user (`--as user`).
|
||||
4. Still unclear → **ask** before any owner-level change; don't guess.
|
||||
|
||||
## Inferring the Owner
|
||||
## When the owner is a third party (neither current user nor bot)
|
||||
|
||||
When an owner-level action is needed and the owner is unknown, infer in this order:
|
||||
The current identity has no owner privileges. Then:
|
||||
|
||||
1. A bot created the group and `--owner` was **not** specified -> the owner is the bot (`--as bot`)
|
||||
2. A bot created the group and `--owner ou_xxx` **was** specified -> the owner is that user (`--as user`)
|
||||
3. A user created the group and `--owner` was **not** specified -> the owner is the current user (`--as user`)
|
||||
4. Still unclear -> ask the user to confirm who owns the group before making owner-level changes
|
||||
|
||||
### When the Owner Is Neither the Current User Nor the Bot
|
||||
|
||||
If the query shows that the owner is a third-party user (`owner_id` is neither the currently authorized user nor the bot), the current identity does not have owner privileges. In that case:
|
||||
|
||||
- **Permission/setting changes:** if the bot is an admin of the group, `--as bot` can still perform admin-level operations such as renaming the group or changing permissions.
|
||||
- **Owner-only actions such as owner transfer:** require the actual owner to complete UAT authorization via `lark-cli auth login`, then perform the action as that owner.
|
||||
- Explain the limitation clearly to the user instead of retrying blindly.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
### Inviting Members During Group Creation
|
||||
|
||||
If a bot creates a group and `--users` includes users who are mutually invisible to the bot, the entire request fails with 232043. Use two steps instead:
|
||||
|
||||
1. Create the group with the bot first, excluding invisible users: `lark-cli im +chat-create --name "Group Name"`
|
||||
2. Add users later with a user-identity member-management flow
|
||||
|
||||
### Insufficient Privileges
|
||||
|
||||
- **232016 / 232002 / 232017:** the current identity is not the owner or an admin -> switch to the owner identity
|
||||
- **232011:** the current user is not in the group -> use a group-member identity, or join the group first
|
||||
- **232024:** the bot and the target user are mutually invisible -> switch to `--as user`
|
||||
|
||||
## References
|
||||
|
||||
- [lark-im](../SKILL.md) - all IM commands
|
||||
- [lark-shared](../../lark-shared/SKILL.md) - authentication and global parameters
|
||||
- **Rename / permission / setting changes:** if the bot is a group **admin**, `--as bot` can still perform these admin-level operations.
|
||||
- **Owner-only actions (e.g. owner transfer):** the actual owner must complete UAT auth via `lark-cli auth login` first, then act as that owner. There is no bot workaround.
|
||||
- Explain the limitation to the user instead of retrying blindly.
|
||||
|
||||
@@ -1,166 +1,30 @@
|
||||
# im +chat-list
|
||||
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first to understand authentication, global parameters, and safety rules.
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first for authentication, global parameters, and safety rules.
|
||||
|
||||
List chats the current user (or bot, with `--as bot`) is a member of. **Not a search API — there is no `--query` parameter; the call always returns the full member list, paginated.** For keyword-based lookup (e.g. find a group by name or by member), use [`+chat-search`](lark-im-chat-search.md) instead.
|
||||
Maps to `lark-cli im +chat-list`. **Run `lark-cli im +chat-list --help` for the authoritative flags (`--types` / `--sort` / `--page-size` / `--page-token` / `--exclude-muted` / `--user-id-type` / `--as`), enums, and defaults.** This file covers only what `--help` cannot.
|
||||
|
||||
**Defaults to groups only**; pass `--types=p2p,group` (or `--types p2p --types group`) to also include p2p single chats (user identity only — see ["Bot identity and p2p"](#bot-identity-and-p2p)). Supports pagination, sort order, and (user identity only) muted-chat filtering.
|
||||
## Gotchas
|
||||
|
||||
This skill maps to the shortcut: `lark-cli im +chat-list` (internally calls `GET /open-apis/im/v1/chats`).
|
||||
- **Not a search API — there is no `--query`.** It always returns the full member list, paginated. For keyword/name/member lookup use [`+chat-search`](lark-im-chat-search.md); do NOT loop `+chat-list` to find a named group.
|
||||
- **Defaults to groups only.** p2p single chats appear only when you pass `--types` to include `p2p`, and **only under `--as user`** (see below).
|
||||
- **`--exclude-muted` is user-only.** Under `--as bot` the mute API is UAT-only, so the filter is silently skipped and all chats come back unfiltered — don't trust it under bot identity.
|
||||
|
||||
## Commands
|
||||
### Bot identity + p2p (silent-downgrade trap)
|
||||
|
||||
```bash
|
||||
# List the user's chats (default sort: create_time, ascending)
|
||||
lark-cli im +chat-list
|
||||
`tenant_access_token` cannot enumerate p2p chats (user-privacy protection). Under `--as bot`:
|
||||
|
||||
# Sort by recent activity (most recently active first)
|
||||
lark-cli im +chat-list --sort active_time
|
||||
- `--types=p2p` alone → **rejected at validation** with an actionable error; no request sent.
|
||||
- `--types=p2p,group` → CLI **silently strips `p2p`** and sends `group` only. The strip surfaces two ways (so neither humans nor agents miss it): a `warning: bot_strip_p2p: ...` line on **stderr**, and a structured entry in the top-level **`notices`** array of the stdout JSON (`{"code":"bot_strip_p2p","message":"..."}`). DryRun emits the same stderr warning.
|
||||
|
||||
# Limit page size
|
||||
lark-cli im +chat-list --page-size 50
|
||||
To include p2p, switch to `--as user --types=p2p,group`.
|
||||
|
||||
# Pagination
|
||||
lark-cli im +chat-list --page-token "xxx"
|
||||
### `--exclude-muted` output envelope
|
||||
|
||||
# Drop muted chats (user identity only)
|
||||
lark-cli im +chat-list --exclude-muted
|
||||
When set, the JSON gains a top-level `filter` sub-object (absent otherwise, so existing consumers are unaffected). Invariant **`fetched_count == returned_count + filtered_count`** always holds. Filtering is **per page, client-side** (the page's chat_ids are batched through the mute-status API after the list call), so a filtered page can return fewer than `--page-size` rows — keep paginating via `page_token`. `filter` and `notices` are separate top-level keys and never collide.
|
||||
|
||||
# JSON output
|
||||
lark-cli im +chat-list --format json
|
||||
## HELP-GAP — not yet in `--help`/schema; keep until CLI adds it
|
||||
|
||||
# Preview the request without executing it
|
||||
lark-cli im +chat-list --dry-run
|
||||
|
||||
# Include p2p single chats (user identity only) — comma form
|
||||
lark-cli im +chat-list --as user --types p2p,group
|
||||
|
||||
# Same, using repeat flag instead of CSV
|
||||
lark-cli im +chat-list --as user --types p2p --types group
|
||||
|
||||
# Only p2p single chats (user identity only)
|
||||
lark-cli im +chat-list --as user --types p2p
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Required | Limits | Description |
|
||||
|------|------|------|------|
|
||||
| `--user-id-type <type>` | No | `open_id` (default), `union_id`, `user_id` | ID type used for `owner_id` in the response |
|
||||
| `--types <strings>` | No | `group`, `p2p` (comma-separated or repeated) | Chat types to include. Omitted = groups only (backward compatible). `p2p` requires user identity (`--as user`); under `--as bot`, `--types=p2p` alone is rejected and `--types=p2p,group` is silently downgraded to `group` |
|
||||
| `--sort <field>` | No | `create_time` (default, ascending), `active_time` (descending) | Result ordering |
|
||||
| `--page-size <n>` | No | 1-100, default 20 | Number of results per page |
|
||||
| `--page-token <token>` | No | - | Pagination token from the previous response |
|
||||
| `--exclude-muted` | No | User identity only | Drop chats the current user has muted (do-not-disturb). Under `--as bot`, the flag is silently inactive; see "Filtering muted chats" below |
|
||||
| `--format json` | No | - | Output as JSON |
|
||||
| `--dry-run` | No | - | Preview the request without executing it |
|
||||
|
||||
> **Note:** Supports both `--as user` (default) and `--as bot`. When using bot identity, the app must have bot capability enabled.
|
||||
|
||||
## Output Fields
|
||||
|
||||
| Field | Description |
|
||||
|------|------|
|
||||
| `chat_id` | Chat ID (`oc_xxx` format) |
|
||||
| `name` | Chat name |
|
||||
| `description` | Chat description |
|
||||
| `owner_id` | Owner ID (type controlled by `--user-id-type`) |
|
||||
| `external` | Whether the chat is external |
|
||||
| `chat_status` | Chat status (`normal` / `dissolved` / `dissolved_save`) |
|
||||
| `chat_mode` | Chat mode discriminator: `group` (regular) / `topic` (topic group) / `p2p` (single chat) |
|
||||
| `p2p_target_type` | Peer type, e.g., `user` |
|
||||
| `p2p_target_id` | Peer ID (type controlled by `--user-id-type`) |
|
||||
|
||||
## Including p2p single chats
|
||||
|
||||
Default behavior lists groups only — same as before this feature. To include p2p, pass `--types`:
|
||||
|
||||
| User intent | Call | Identity |
|
||||
|---|---|---|
|
||||
| "list my groups" / 我的群 / 我加入了哪些群 | (default, omit `--types`) | user or bot |
|
||||
| "list my p2p chats" / 我的单聊 / 我跟谁有 1v1 | `--types p2p` | **user only** |
|
||||
| "all my chats" / 全部聊天 / 所有会话 (ambiguous) | `--types p2p,group` | **user only** |
|
||||
|
||||
For p2p rows in the response: `name` is the peer's display name, `owner_id` follows group semantics, `chat_mode = "p2p"`, and `p2p_target_type` / `p2p_target_id` identify the peer.
|
||||
|
||||
## Bot identity and p2p
|
||||
|
||||
`tenant_access_token` cannot list p2p chats — to protect user privacy, bot identity is not permitted to enumerate p2p single chats. Behavior under `--as bot`:
|
||||
|
||||
- `--as bot --types=p2p` → rejected at validation time with an actionable error; no request is sent.
|
||||
- `--as bot --types=p2p,group` → CLI strips `p2p` and sends `types=group`. Request proceeds; only groups are returned. The strip is a **request-level adjustment**, surfaced two ways so neither humans nor agents miss it:
|
||||
- **stderr**: `warning: bot_strip_p2p: To protect user privacy, bot identity cannot list p2p chats; --types=p2p,group was sent as types=group. Use --as user to include p2p.` (matches the `warning: <code>: <message>` convention in `shortcuts/common/runner.go`)
|
||||
- **stdout JSON**: a top-level `notices` array gains a structured entry:
|
||||
```json
|
||||
{
|
||||
"chats": [...],
|
||||
"notices": [
|
||||
{ "code": "bot_strip_p2p", "message": "To protect user privacy, bot identity cannot list p2p chats; …" }
|
||||
]
|
||||
}
|
||||
```
|
||||
- The `filter` slot stays scoped to `--exclude-muted`; `notices` is a separate top-level key, so the two never collide and no priority is needed when both fire.
|
||||
- DryRun emits the same stderr warning so a previewed request truthfully reflects what Execute will send (parity with `shortcuts/drive/drive_search.go`).
|
||||
- `--as bot --types=group` → accepted, returns groups normally.
|
||||
- `--as bot` (no `--types`) → unchanged, returns groups.
|
||||
|
||||
To include p2p single chats, switch to user identity: `--as user --types=p2p,group`.
|
||||
|
||||
## Filtering muted chats
|
||||
|
||||
`--exclude-muted` (user identity only) drops chats the current user has set to do-not-disturb. After the list call, the CLI batches the page's chat_ids through `POST /open-apis/im/v1/chat_user_setting/batch_get_mute_status` and filters client-side. Under `--as bot`, the mute API is UAT-only and the filter is silently skipped.
|
||||
|
||||
When the flag is set, the JSON envelope gains a `filter` sub-object (absent otherwise, so existing consumers are unaffected); `fetched_count == returned_count + filtered_count` always holds:
|
||||
|
||||
```json
|
||||
{
|
||||
"chats": [...],
|
||||
"filter": {
|
||||
"applied": "exclude_muted",
|
||||
"fetched_count": 20,
|
||||
"returned_count": 17,
|
||||
"filtered_count": 3,
|
||||
"hint": "Filtered out 3 muted chat(s) on this page (17 remaining); use --page-token to fetch more."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Usage Scenarios
|
||||
|
||||
### Scenario 1: List my recent chats
|
||||
|
||||
```bash
|
||||
lark-cli im +chat-list --sort active_time --page-size 10
|
||||
```
|
||||
|
||||
### Scenario 2: List my non-muted chats sorted by activity
|
||||
|
||||
```bash
|
||||
lark-cli im +chat-list --sort active_time --exclude-muted
|
||||
```
|
||||
|
||||
### Scenario 3: Iterate all my chats programmatically
|
||||
|
||||
```bash
|
||||
TOKEN=""
|
||||
while :; do
|
||||
RESP=$(lark-cli im +chat-list --page-size 100 --page-token "$TOKEN" --format json)
|
||||
echo "$RESP" | jq -r '.data.chats[].chat_id'
|
||||
HAS_MORE=$(echo "$RESP" | jq -r '.data.has_more')
|
||||
[ "$HAS_MORE" = "true" ] || break
|
||||
TOKEN=$(echo "$RESP" | jq -r '.data.page_token')
|
||||
done
|
||||
```
|
||||
|
||||
## Common Errors and Troubleshooting
|
||||
|
||||
| Symptom | Root Cause | Solution |
|
||||
|---------|---------|---------|
|
||||
| `--page-size must be an integer between 1 and 100` | page-size is out of range or not an integer | Use an integer between 1 and 100 |
|
||||
| Permission denied (99991672) | The bot app does not have `im:chat:read` TAT permission enabled | Enable the permission for the app in the Open Platform console |
|
||||
| Permission denied (99991679) with `--as user` | UAT is not authorized for `im:chat:read` | Run `lark-cli auth login --scope "im:chat:read"` |
|
||||
| `Bot ability is not activated` (232025) | The app does not have bot capability enabled | Enable bot capability in the Open Platform console |
|
||||
| `--exclude-muted` returns all chats unfiltered and `hint` says "no effect under bot identity" | Running under `--as bot` (mute API is UAT-only) | Switch to `--as user` for mute filtering |
|
||||
| `--types=p2p (single chats) is only supported with user identity` | `--as bot` + `--types=p2p` (single-value only; mixed `--types=p2p,group` is downgraded to `group` and surfaces a `bot_strip_p2p` notice via stderr + `outData["notices"]` — see "Bot identity and p2p") | Use `--as user`, or include `group` in `--types` (the bot proceeds with `group` only and emits the `bot_strip_p2p` notice) |
|
||||
|
||||
> Full error message of the row above: `--types=p2p (single chats) is only supported with user identity (--as user). To protect user privacy, bot identity cannot list p2p chats. Use --as user, or include "group" in --types.`
|
||||
- **Output fields**: `chat_id` (oc_xxx) · `name` · `description` · `owner_id` (type per `--user-id-type`) · `external` · `chat_status` (`normal` / `dissolved` / `dissolved_save`) · `chat_mode` (`group` / `topic` / `p2p`) · `p2p_target_type` (e.g. `user`) · `p2p_target_id`. For p2p rows, `name` is the peer's display name and `p2p_target_*` identify the peer.
|
||||
- **`filter` envelope shape**: `{"applied":"exclude_muted","fetched_count","returned_count","filtered_count","hint"}`.
|
||||
- **Pagination fields** (`--format json`): `has_more` / `page_token` live under `.data`.
|
||||
|
||||
@@ -1,157 +1,34 @@
|
||||
# im +chat-messages-list
|
||||
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first to understand authentication, global parameters, and safety rules.
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first for authentication, global parameters, and safety rules.
|
||||
|
||||
Fetch the message list for a conversation. Supports both group chats and direct messages.
|
||||
Maps to `lark-cli im +chat-messages-list`. **Run `lark-cli im +chat-messages-list --help` for the authoritative flags (`--chat-id` / `--user-id` / `--start` / `--end` / `--order` / `--page-size` / `--page-token` / `--no-reactions` / `--download-resources` / `--as`), enums, time format (ISO 8601 or date-only), and the `--chat-id`/`--user-id` mutual-exclusion.** This file covers only what `--help` cannot.
|
||||
|
||||
By default the response carries a `reactions` block (counts + details from `im.reactions.batch_query`) on every message that has reactions, and `update_time` on messages that were actually edited. Thread replies expanded via auto-`thread_replies` participate in the same batched enrichment. Pass `--no-reactions` to skip the extra round-trip. Pass `--download-resources` to additionally download message resources (image/file/audio/video/media + post-embedded, excluding stickers) into `./lark-im-resources/` and attach a `resources` block — off by default. See [message enrichment](lark-im-message-enrichment.md) for the full contract.
|
||||
Supports both `--as user` (default) and `--as bot`. Auto-resolves the p2p `chat_id`, auto-expands `thread_replies`, and enriches with reactions / `update_time` per [message enrichment](lark-im-message-enrichment.md).
|
||||
|
||||
This skill maps to the shortcut: `lark-cli im +chat-messages-list` (internally calls `GET /open-apis/im/v1/messages`, and automatically resolves the p2p chat_id when needed).
|
||||
## Gotchas
|
||||
|
||||
## Commands
|
||||
- **`--user-id` (DM by open_id) is user-identity only — and the constraint is silent until you hit it.** The p2p-resolution endpoint requires user identity; with `--as bot` it errors. For bot identity, look up the p2p `chat_id` yourself and pass `--chat-id`.
|
||||
- **`P2P chat not found for this user`** means no DM exists *for the current identity* with that user — not a bad ID. Confirm the DM relationship under the identity you're calling as.
|
||||
- **Resolve a chat name → `chat_id` via [`+chat-search`](lark-im-chat-search.md) first**, then pass `--chat-id`. **Do NOT use `im chats search` or `+chat-list`** — those are not search APIs and won't locate the target.
|
||||
- **`--order` defaults to `desc`** (newest first); pass `--order asc` for chronological reading. (Note: the flag is `--order`, not `--sort`.) It is the **only** sort axis — messages are always ordered by creation time. There is no field sort: `--sort sender` (or any field) is rejected. If asked to group/sort by sender, fetch with `--order` and aggregate client-side, and say so (local post-processing, not a CLI/API sort).
|
||||
- **Image content is a placeholder, not bytes**: images render as `[Image: img_xxx]`; files/audio/video carry resource keys. Nothing downloads unless you pass `--download-resources` (writes to `./lark-im-resources/`) or use [`im +messages-resources-download`](lark-im-messages-resources-download.md).
|
||||
|
||||
## Thread expansion (`thread_id`)
|
||||
|
||||
A message carrying `thread_id` (`omt_xxx`) has thread replies. Auto-expansion attaches them as `thread_replies` (see enrichment doc); to drive the thread yourself use [`im +threads-messages-list --thread <id>`](lark-im-threads-messages-list.md) — `--order desc --page-size 10` for recent replies, `--order asc --page-size 50` (then paginate) for the full discussion, skip it for an overview.
|
||||
|
||||
## Bot identity + named-group history (cross-command recipe)
|
||||
|
||||
When the user says "以 bot 身份 / use application identity" and wants historical messages for a *named* group, use bot identity for **both** steps:
|
||||
|
||||
```bash
|
||||
# Get group chat messages (json output by default)
|
||||
lark-cli im +chat-messages-list --chat-id oc_xxx
|
||||
|
||||
# Get direct messages with a user (pass open_id and resolve p2p chat_id automatically)
|
||||
lark-cli im +chat-messages-list --user-id ou_xxx
|
||||
|
||||
# Specify a time range (ISO 8601)
|
||||
lark-cli im +chat-messages-list --chat-id oc_xxx --start "2026-03-10T00:00:00+08:00" --end "2026-03-11T00:00:00+08:00"
|
||||
|
||||
# Specify a time range (date only)
|
||||
lark-cli im +chat-messages-list --chat-id oc_xxx --start 2026-03-10 --end 2026-03-11
|
||||
|
||||
# Control sort order and page size (max 50)
|
||||
lark-cli im +chat-messages-list --chat-id oc_xxx --order asc --page-size 20
|
||||
|
||||
# Pagination
|
||||
lark-cli im +chat-messages-list --chat-id oc_xxx --page-token "xxx"
|
||||
|
||||
# JSON output
|
||||
lark-cli im +chat-messages-list --chat-id oc_xxx --format json
|
||||
lark-cli im +chat-search --as bot --query "<chat name keyword>" --format json
|
||||
lark-cli im +chat-messages-list --as bot --chat-id <chat_id> --page-size 50 --format json
|
||||
```
|
||||
|
||||
## Parameters
|
||||
**Do NOT reach for `im +messages-search --as bot`** — that command is user-only. Continue with `--page-token` while `has_more=true`.
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|------|------|------|
|
||||
| `--chat-id <id>` | One of two | Specify the conversation by its chat_id directly (e.g., group chat `oc_xxx`) |
|
||||
| `--user-id <id>` | One of two | Specify a DM conversation by the other user's open_id (`ou_xxx`); p2p chat_id is resolved automatically. Requires user identity (`--as user`); not supported with bot identity |
|
||||
| `--start <time>` | No | Start time (ISO 8601 or date only) |
|
||||
| `--end <time>` | No | End time (ISO 8601 or date only) |
|
||||
| `--order <order>` | No | Sort order: `asc` / `desc` (default `desc`) |
|
||||
| `--page-size <n>` | No | Page size (default 50, max 50) |
|
||||
| `--page-token <token>` | No | Pagination token |
|
||||
| `--no-reactions` | No | Skip auto-fetching the `reactions` block |
|
||||
| `--download-resources` | No | Download message resources (image/file/audio/video/media + post-embedded, excluding stickers) into `./lark-im-resources/` and attach a `resources` block. Off by default; no extra requests when omitted |
|
||||
## HELP-GAP — not yet in `--help`/schema; keep until CLI adds it
|
||||
|
||||
> Rule: `--chat-id` and `--user-id` are mutually exclusive. You must provide exactly one of them.
|
||||
|
||||
> **CAUTION:** `--order` is the only sort axis — messages are always ordered by creation time, `asc` or `desc`. There is no field axis: the command cannot sort by sender or any other field, so do **not** attempt `--sort sender` or similar (it is rejected). If the user asks to group or sort by sender, fetch with `--order` and aggregate client-side, and tell them this is local post-processing, not a CLI/API sort capability.
|
||||
|
||||
## Resource Rendering
|
||||
|
||||
Messages are rendered into human-readable text for inspection. Image messages are shown as placeholders such as `[Image: img_xxx]`; files, audio, and videos are rendered with resource keys in the content (e.g. `<audio key="file_xxx" duration="Xs"/>`). By default resource binaries are **not** downloaded.
|
||||
|
||||
Two ways to get the binaries:
|
||||
- **In one pass:** add `--download-resources` to this command — every eligible resource (image/file/audio/video/media + post-embedded, excluding stickers) is downloaded into `./lark-im-resources/` and a `resources` block (`{message_id, key, type, local_path, size_bytes}`) is attached to each message. See [message enrichment](lark-im-message-enrichment.md#resource-auto-download---download-resources-opt-in).
|
||||
- **One at a time:** use [lark-im-messages-resources-download](lark-im-messages-resources-download.md).
|
||||
|
||||
| Resource Type | Marker in Content | Behavior |
|
||||
|---------|-------------|------|
|
||||
| Image | `[Image: img_xxx]` | `--download-resources`, or manually `im +messages-resources-download --type image` |
|
||||
| File | `<file key="file_xxx" .../>` | `--download-resources`, or manually `im +messages-resources-download --type file` |
|
||||
| Audio | `<audio key="file_xxx" duration="Xs"/>` | `--download-resources`, or manually `im +messages-resources-download --type file` |
|
||||
| Video | `<video key="file_xxx" .../>` | `--download-resources`, or manually `im +messages-resources-download --type file` |
|
||||
| Sticker | `[Sticker]` | Not downloadable (Feishu does not support fetching sticker resources) |
|
||||
|
||||
## Thread Expansion (`thread_id`)
|
||||
|
||||
In JSON output, a message may contain a `thread_id` (`omt_xxx`) field, which means the message has replies in a thread. Use [`im +threads-messages-list`](lark-im-threads-messages-list.md) to inspect replies in that thread:
|
||||
|
||||
```bash
|
||||
lark-cli im +threads-messages-list --thread omt_xxx
|
||||
```
|
||||
|
||||
| Scenario | Recommendation |
|
||||
|------|------|
|
||||
| You need context | Call `im +threads-messages-list --order desc --page-size 10` for the discovered thread_id to inspect recent replies |
|
||||
| The user asks for the "full discussion" | Use `im +threads-messages-list --order asc --page-size 50`, then paginate if needed |
|
||||
| You only need an overview | Skip thread expansion |
|
||||
|
||||
## Output Fields
|
||||
|
||||
| Field | Description |
|
||||
|------|------|
|
||||
| `messages` | Message array |
|
||||
| `total` | Number of messages in the current page |
|
||||
| `has_more` | Whether additional pages are available |
|
||||
| `page_token` | Pagination token for the next page |
|
||||
|
||||
Each message contains:
|
||||
|
||||
| Field | Description |
|
||||
|------|------|
|
||||
| `message_id` | Message ID |
|
||||
| `msg_type` | Message type: `text`, `image`, `file`, `interactive`, `post`, `audio`, `video`, `system`, etc. |
|
||||
| `create_time` | Creation time |
|
||||
| `sender` | Sender information (includes `name` for user senders) |
|
||||
| `content` | Message content |
|
||||
| `deleted` | Whether the message has been recalled (always present, `true` = recalled) |
|
||||
| `updated` | Whether the message has been edited after sending |
|
||||
| `mentions` | Array of @mentions in the message; each item contains `{id, key, name}`. Present only when the message contains @mentions |
|
||||
| `thread_id` | Thread ID (`omt_xxx`) if the message has replies in a thread. Present only when replies exist |
|
||||
|
||||
## Pagination (`has_more` / `page_token`)
|
||||
|
||||
`im +chat-messages-list` returns `has_more` and `page_token` when more data is available. Use `--page-token` to continue:
|
||||
|
||||
```bash
|
||||
lark-cli im +chat-messages-list --chat-id oc_xxx --page-token <PAGE_TOKEN>
|
||||
```
|
||||
|
||||
You can also fall back to the generic API:
|
||||
|
||||
```bash
|
||||
lark-cli api GET /open-apis/im/v1/messages \
|
||||
--params 'container_id_type=chat&container_id=oc_xxx&page_size=50&page_token=<PAGE_TOKEN>'
|
||||
```
|
||||
|
||||
## Common Errors and Troubleshooting
|
||||
|
||||
| Symptom | Root Cause | Solution |
|
||||
|---------|---------|---------|
|
||||
| `specify --chat-id <chat_id> or --user-id <open_id>` | Neither `--chat-id` nor `--user-id` was provided | You must provide exactly one |
|
||||
| `--chat-id and --user-id cannot be specified together` | Both parameters were provided | Use only one |
|
||||
| `--user-id requires user identity (--as user); use --chat-id when calling with bot identity` | `--user-id` was used with bot identity | The p2p resolution endpoint requires user identity. Either pass `--as user` or look up the p2p `chat_id` separately and pass it via `--chat-id` |
|
||||
| `P2P chat not found for this user` | `--user-id` was used but no p2p chat exists for the current identity and that user | Confirm the target direct-message relationship exists for the current identity |
|
||||
| `--start: invalid time format` | Invalid time format | Use ISO 8601 or date-only format such as `2026-03-10` |
|
||||
| Permission denied | Message read permissions are missing | Ensure the app has `im:message:readonly` and `im:chat:read` enabled |
|
||||
|
||||
## AI Usage Guidance
|
||||
|
||||
1. **Resolving chat_id from a chat name:** When the user refers to a chat by name and you don't have the `chat_id`, use [`+chat-search`](lark-im-chat-search.md) first:
|
||||
```bash
|
||||
# Find chat_id by name, then list messages
|
||||
lark-cli im +chat-search --query "<chat name keyword>" --format json
|
||||
lark-cli im +chat-messages-list --chat-id <chat_id>
|
||||
```
|
||||
**Do not use `im chats search` or `+chat-list` — always use the `+chat-search` shortcut.**
|
||||
2. **Prefer `--chat-id` when available:** if the chat_id is already known, use it directly to avoid extra API calls.
|
||||
3. **For direct messages:** use `--user-id` to resolve the p2p chat automatically instead of looking it up manually. This requires user identity (`--as user`); with bot identity, resolve the p2p `chat_id` yourself and pass it via `--chat-id`.
|
||||
4. **For time ranges:** both ISO 8601 and date-only inputs are supported. Date-only is usually simpler.
|
||||
5. **For full content:** table output truncates content. Use `--format json` when you need the complete message body.
|
||||
6. **For sender info:** the command already resolves sender names, so you do not need a separate lookup.
|
||||
7. **Application/bot identity + named group history:** If the user says "使用应用身份/以 bot 身份" and asks to list or read historical messages for a named group, use bot identity for both steps:
|
||||
```bash
|
||||
lark-cli im +chat-search --as bot --query "<chat name keyword>" --format json
|
||||
lark-cli im +chat-messages-list --as bot --chat-id <chat_id> --page-size 50 --format json
|
||||
```
|
||||
Do not use `im +messages-search --as bot`; `+messages-search` is user-only. Continue with `--page-token` if `has_more=true`.
|
||||
|
||||
## References
|
||||
|
||||
- [lark-im](../SKILL.md) - all IM commands
|
||||
- [lark-shared](../../lark-shared/SKILL.md) - authentication and global parameters
|
||||
- **Per-message fields** (JSON): `deleted` (always present; `true` = recalled) · `updated` (edited after send) · `mentions` `[{id,key,name}]` **present only when @mentions exist** · `thread_id` (omt_xxx) **present only when replies exist**. Page envelope carries `has_more` / `page_token`.
|
||||
|
||||
@@ -1,142 +1,29 @@
|
||||
# im +chat-search
|
||||
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first to understand authentication, global parameters, and safety rules.
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first for authentication, global parameters, and safety rules.
|
||||
|
||||
Search the list of group chats visible to a user or bot, including chats the user or bot belongs to and public chats visible to them. Supports keyword matching on chat names and member names, including pinyin and prefix fuzzy search.
|
||||
Maps to `lark-cli im +chat-search`. **Run `lark-cli im +chat-search --help` for the authoritative flag list (`--query` / `--member-ids` / `--search-types` / `--chat-modes` / `--sort` / `--page-size` / `--page-token` / `--is-manager` / `--exclude-muted` / `--disable-search-by-user`), limits, and enums.** This file covers only what `--help` cannot.
|
||||
|
||||
This skill maps to the shortcut: `lark-cli im +chat-search` (internally calls `POST /open-apis/im/v2/chats/search`).
|
||||
## Gotchas
|
||||
|
||||
## Commands
|
||||
- **Visibility-scoped, not global**: only finds chats visible to the current identity (joined chats + visible public chats). A chat the caller can't see won't appear.
|
||||
- **At least one of `--query` / `--member-ids`** must be given (either alone, or combined).
|
||||
- `--query` containing `-` is auto-wrapped in quotes.
|
||||
- **On empty results, do NOT fall back to `+chat-list` / `GET /chats`** — list is not a search API (returns all chats unfiltered, won't locate the target). Refine the keyword or check visibility under the current identity instead.
|
||||
- Supports `--as user` (default) and `--as bot` (bot needs bot capability enabled).
|
||||
- **`--sort` is always descending** (`create_time` / `update_time` / `member_count`, high→low). There is no ascending option. If the user asks for "fewest first / ascending / 从少到多", tell them the search API doesn't support ascending and re-sort the fetched page client-side — do **NOT** invent `member_count_asc` or pass `asc` (rejected).
|
||||
|
||||
```bash
|
||||
# Search chats by keyword
|
||||
lark-cli im +chat-search --query "project"
|
||||
## `--exclude-muted` (user identity only)
|
||||
|
||||
# Restrict by search types
|
||||
lark-cli im +chat-search --query "project" --search-types "private,public_joined"
|
||||
Drops do-not-disturb chats; under `--as bot` the filter is silently inactive (mute is per-user / UAT-only). When set, the JSON envelope gains a `filter` sub-object (absent otherwise, so existing consumers are unaffected); the invariant **`fetched_count == returned_count + filtered_count`** always holds. Only confirmed-muted chats count toward `filtered_count`; non-member public groups are retained and surfaced in `hint`. For strict member-only results, combine with `--search-types "private,public_joined,external"`.
|
||||
|
||||
# Filter by chat mode (group = regular group, topic = topic/thread group)
|
||||
lark-cli im +chat-search --query "project" --chat-modes "topic"
|
||||
## Error → remediation
|
||||
|
||||
# Filter by member open_ids (with keyword)
|
||||
lark-cli im +chat-search --query "project" --member-ids "ou_xxx,ou_yyy"
|
||||
- `99991679` (`--as user`): UAT not authorized for `im:chat:read` → `lark-cli auth login --scope "im:chat:read"`.
|
||||
- `99991672` (`--as bot`): app lacks `im:chat:read` TAT → enable in the Open Platform console.
|
||||
- `232025`: bot capability not activated → enable in the console.
|
||||
|
||||
# Search by member open_ids only
|
||||
lark-cli im +chat-search --member-ids "ou_xxx,ou_yyy"
|
||||
## HELP-GAP — not yet in `--help`/schema; keep until CLI adds it
|
||||
|
||||
# Only show chats you created or manage
|
||||
lark-cli im +chat-search --query "project" --is-manager
|
||||
|
||||
# Set page size
|
||||
lark-cli im +chat-search --query "project" --page-size 10
|
||||
|
||||
# Pagination
|
||||
lark-cli im +chat-search --query "project" --page-token "xxx"
|
||||
|
||||
# JSON output
|
||||
lark-cli im +chat-search --query "project" --format json
|
||||
|
||||
# Preview the request without executing it
|
||||
lark-cli im +chat-search --query "project" --dry-run
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Required | Limits | Description |
|
||||
|------|------|------|------|
|
||||
| `--query <keyword>` | No (at least one of `--query` / `--member-ids` required) | Max 64 characters | Search keyword. Supports matching localized chat names, member names, multilingual search, pinyin, and prefix fuzzy search. If the query contains `-`, it is automatically wrapped in quotes |
|
||||
| `--search-types <types>` | No | Comma-separated: `private`, `external`, `public_joined`, `public_not_joined` | Restrict the visible chat types returned by search |
|
||||
| `--chat-modes <modes>` | No | Comma-separated: `group`, `topic` | Filter by chat mode (server-side): `group` = regular group, `topic` = topic/thread group |
|
||||
| `--member-ids <ids>` | No (at least one of `--query` / `--member-ids` required) | Up to 50, format `ou_xxx` | Filter by member open_ids; can be used alone or combined with `--query` |
|
||||
| `--is-manager` | No | - | Only show chats you created or manage |
|
||||
| `--disable-search-by-user` | No | - | Disable member-name-based matching and search by group name only |
|
||||
| `--sort <field>` | No | `create_time`, `update_time`, `member_count` | Sort field (always descending) |
|
||||
| `--page-size <n>` | No | 1-100, default 20 | Number of results per page |
|
||||
| `--page-token <token>` | No | - | Pagination token from the previous response |
|
||||
| `--exclude-muted` | No | User identity only | Drop chats the current user has muted (do-not-disturb). Under `--as bot`, the flag is silently inactive (mute is a per-user setting); see "Filtering muted chats" below |
|
||||
| `--format json` | No | - | Output as JSON |
|
||||
| `--dry-run` | No | - | Preview the request without executing it |
|
||||
|
||||
> **Note:** Supports both `--as user` (default) and `--as bot`. When using bot identity, the app must have bot capability enabled.
|
||||
|
||||
> **CAUTION:** `--sort` is **always descending** — the search API only ranks the chosen field high-to-low (e.g. `member_count` = most members first). There is no ascending option. If the user asks for "fewest first / ascending / 从少到多", tell them the search API does not support ascending order; any low-to-high view requires re-sorting the fetched page client-side and is not an upstream sort. Do **not** invent values like `member_count_asc` or pass `asc` (they are rejected).
|
||||
|
||||
## Output Fields
|
||||
|
||||
| Field | Description |
|
||||
|------|------|
|
||||
| `chat_id` | Chat ID (`oc_xxx` format) |
|
||||
| `name` | Chat name |
|
||||
| `description` | Chat description |
|
||||
| `owner_id` | Owner ID |
|
||||
| `external` | Whether the chat is external |
|
||||
| `chat_status` | Chat status (`normal` / `dissolved` / `dissolved_save`) |
|
||||
|
||||
## Filtering muted chats
|
||||
|
||||
`--exclude-muted` (user identity only) drops chats the current user has set to do-not-disturb. After the search call, the CLI batches the page's chat_ids through `POST /open-apis/im/v1/chat_user_setting/batch_get_mute_status` and filters client-side. Under `--as bot`, the mute API is UAT-only and the filter is silently skipped.
|
||||
|
||||
When the flag is set, the JSON envelope gains a `filter` sub-object (absent otherwise, so existing consumers are unaffected); `fetched_count == returned_count + filtered_count` always holds:
|
||||
|
||||
```json
|
||||
{
|
||||
"chats": [...],
|
||||
"filter": {
|
||||
"applied": "exclude_muted",
|
||||
"fetched_count": 20,
|
||||
"returned_count": 19,
|
||||
"filtered_count": 1,
|
||||
"hint": "Filtered out 1 muted chat(s) on this page (19 remaining, including 2 non-member public group(s)); use --page-token to fetch more."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note: only confirmed-muted chats count toward `filtered_count`; non-member public groups are retained and surfaced in `hint`. For strict member-only results, combine with `--search-types "private,public_joined,external"`.
|
||||
|
||||
## Usage Scenarios
|
||||
|
||||
### Scenario 1: Search chats that contain a keyword
|
||||
|
||||
```bash
|
||||
lark-cli im +chat-search --query "design review"
|
||||
```
|
||||
|
||||
### Scenario 2: Search a chat and list recent messages
|
||||
|
||||
```bash
|
||||
CHAT_ID=$(lark-cli im +chat-search --query "project" --format json | jq -r '.data.chats[0].chat_id')
|
||||
lark-cli im +chat-messages-list --chat-id "$CHAT_ID"
|
||||
```
|
||||
|
||||
### Scenario 3: Search a chat and send a message
|
||||
|
||||
```bash
|
||||
CHAT_ID=$(lark-cli im +chat-search --query "daily report" --format json | jq -r '.data.chats[0].chat_id')
|
||||
lark-cli im +messages-send --chat-id "$CHAT_ID" --text "Today's progress update"
|
||||
```
|
||||
|
||||
## Common Errors and Troubleshooting
|
||||
|
||||
| Symptom | Root Cause | Solution |
|
||||
|---------|---------|---------|
|
||||
| `--query and --member-ids cannot both be empty` | Both were omitted | Provide at least `--query` or `--member-ids` |
|
||||
| Empty results | No visible chats matched the keyword or filters | Relax the keyword or filters and try again |
|
||||
| `--page-size must be an integer between 1 and 100` | page-size is out of range or not an integer | Use an integer between 1 and 100 |
|
||||
| Permission denied (99991672) | The bot app does not have `im:chat:read` TAT permission enabled | Enable the permission for the app in the Open Platform console |
|
||||
| Permission denied (99991679) with `--as user` | UAT is not authorized for `im:chat:read` | Run `lark-cli auth login --scope "im:chat:read"` |
|
||||
| `Bot ability is not activated` (232025) | The app does not have bot capability enabled | Enable bot capability in the Open Platform console |
|
||||
|
||||
## AI Usage Guidance
|
||||
|
||||
When the user asks to search chats, follow these rules:
|
||||
|
||||
1. **At least one filter required:** `--query` and `--member-ids` cannot both be empty. Either alone or combined together are valid.
|
||||
2. **Search scope is limited:** only chats visible to the current user or bot can be found (joined chats plus public chats). This is not a global search over all chats.
|
||||
3. **Control result volume:** the result set may be large. Use `--page-size` deliberately.
|
||||
4. **Suggest follow-up actions:** after finding a chat, common next steps include listing recent messages (`im +chat-messages-list`) or sending a message (`im +messages-send`).
|
||||
5. **NEVER fall back to chats list:** If `+chat-search` returns empty results, do NOT attempt to use `+chat-list` or `GET /open-apis/im/v1/chats` as a fallback. The list API is not a search API — it returns all chats without keyword filtering and will not help locate the target chat. Instead, ask the user to refine the keyword or check whether the chat is visible to the current identity.
|
||||
|
||||
## References
|
||||
|
||||
- [lark-im](../SKILL.md) - all IM commands
|
||||
- [lark-shared](../../lark-shared/SKILL.md) - authentication and global parameters
|
||||
- **Output fields**: `chat_id` (oc_xxx) · `name` · `description` · `owner_id` · `external` · `chat_status` (`normal` / `dissolved` / `dissolved_save`).
|
||||
- `--member-ids`: up to 50, format `ou_xxx` (`--help` only says "comma-separated").
|
||||
|
||||
@@ -1,84 +1,11 @@
|
||||
# im +chat-update
|
||||
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first to understand authentication, global parameters, and safety rules.
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first for authentication, global parameters, and safety rules.
|
||||
|
||||
Update a group's name or description. Supports both **TAT (bot)** and **UAT (user)** identity.
|
||||
Maps to `lark-cli im +chat-update`. **Run `lark-cli im +chat-update --help` for the authoritative flags (`--chat-id` / `--name` / `--description` / `--as`), limits, and the "at least one field" rule.** This file covers only what `--help` cannot.
|
||||
|
||||
This skill maps to the shortcut: `lark-cli im +chat-update` (internally calls `PUT /open-apis/im/v1/chats/:chat_id`).
|
||||
## Gotchas
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Update the group name
|
||||
lark-cli im +chat-update --chat-id oc_xxx --name "New Group Name"
|
||||
|
||||
# Update the group description
|
||||
lark-cli im +chat-update --chat-id oc_xxx --description "Updated group description"
|
||||
|
||||
# Update multiple fields at once
|
||||
lark-cli im +chat-update --chat-id oc_xxx \
|
||||
--name "Q2 Project Team" \
|
||||
--description "Owns Q2 goal tracking"
|
||||
|
||||
# Preview the request without executing it
|
||||
lark-cli im +chat-update --chat-id oc_xxx --name "Test" --dry-run
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
### Required
|
||||
|
||||
| Parameter | Description |
|
||||
|------|------|
|
||||
| `--chat-id <oc_xxx>` | Group ID |
|
||||
|
||||
### Optional Fields
|
||||
|
||||
| Parameter | Limits | Description |
|
||||
|------|------|------|
|
||||
| `--name <name>` | Max 60 characters | Group name |
|
||||
| `--description <text>` | Max 100 characters | Group description |
|
||||
|
||||
### Global Parameters
|
||||
|
||||
| Parameter | Description |
|
||||
|------|------|
|
||||
| `--format json` | Output as JSON (default) |
|
||||
| `--dry-run` | Preview the request without executing it |
|
||||
|
||||
## Usage Scenarios
|
||||
|
||||
### Scenario 1: Rename a group and update its description
|
||||
|
||||
```bash
|
||||
lark-cli im +chat-update --chat-id oc_xxx \
|
||||
--name "Q2 Project Team" \
|
||||
--description "Owns Q2 goal tracking"
|
||||
```
|
||||
|
||||
## Common Errors and Troubleshooting
|
||||
|
||||
| Symptom | Root Cause | Solution |
|
||||
|---------|---------|---------|
|
||||
| `invalid --chat-id: expected chat ID (oc_xxx)` | Invalid chat_id format | Use a valid `oc_xxx` chat ID |
|
||||
| `--name exceeds the maximum of 60 characters` | Group name too long | Shorten the name to 60 characters or fewer |
|
||||
| `--description exceeds the maximum of 100 characters` | Group description too long | Shorten the description to 100 characters or fewer |
|
||||
| `at least one field must be specified to update` | No update field was provided | Specify at least one field to update |
|
||||
| Permission denied (99991679) | Missing `im:chat:update` permission | Run `lark-cli auth login --scope "im:chat:update"` |
|
||||
| Non-owner/admin cannot update (232016/232002/232017) | Current identity is not the owner/admin | Try switching identity with `--as bot` or `--as user` |
|
||||
| Not in the group (232011) | The current user is not a member of the group | Use a member identity (`--as bot`) or join the group first |
|
||||
|
||||
## AI Usage Guidance
|
||||
|
||||
### Identity Selection
|
||||
|
||||
`+chat-update` supports both user and bot identity (`--as user` / `--as bot`).
|
||||
|
||||
Infer the group owner from context whenever possible (for example, if a bot just created the group, the owner is the bot) and use the matching identity directly. If ownership is unclear, query the group first and confirm `owner_id`.
|
||||
|
||||
Identity choice should follow [Group Chat Identity Rules](lark-im-chat-identity.md): if the user explicitly specifies an identity, use it directly; otherwise infer the owner identity from context.
|
||||
|
||||
## References
|
||||
|
||||
- [lark-im](../SKILL.md) - all IM commands
|
||||
- [lark-shared](../../lark-shared/SKILL.md) - authentication and global parameters
|
||||
- **Updating requires owner/admin privileges.** A non-owner/non-admin identity fails with **232016 / 232002 / 232017**; an identity that isn't even in the group fails with **232011**. `--help` won't tell you this — pick the identity *before* you call.
|
||||
- **Infer the owner identity from context** rather than the default (per [Group Chat Identity Rules](lark-im-chat-identity.md)): if the user names an identity, use it; if a bot just created the group, the owner is the bot; otherwise query the group first and confirm `owner_id` before choosing `--as user` / `--as bot`.
|
||||
- **A bot that is an admin (not owner) can still rename / change settings** under `--as bot` — owner-only actions (e.g. owner transfer) are not exposed here and need the real owner's UAT auth.
|
||||
|
||||
@@ -1,68 +1,25 @@
|
||||
# +feed-group-list-item
|
||||
|
||||
> Shortcut for `lark-cli im +feed-group-list-item`. List the feed cards inside one feed group (tag), enriched with a readable `chat_name`.
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first for authentication, global parameters, and safety rules.
|
||||
|
||||
`+feed-group-list-item` is the only CLI surface for the `feed.groups.list_item` read API — there is no raw `feed.groups list_item` command. It resolves a human-readable `chat_name` for every feed card it returns: a v1 feed card's `feed_id` is always a chat ID (`oc_xxx`), so the shortcut issues a follow-up `POST /open-apis/im/v1/chats/batch_query` and injects `chat_name` into each entry of both `items[]` and `deleted_items[]`.
|
||||
Maps to `lark-cli im +feed-group-list-item`. **Run `lark-cli im +feed-group-list-item --help` for the authoritative flags (`--feed-group-id` / `--page-size` / `--page-token` / `--page-all` / `--page-limit` / `--start-time` / `--end-time` / `--as`), the page-size range, and the time format.** This file covers only what `--help` cannot.
|
||||
|
||||
## Identity
|
||||
**User identity only** (`--as user`); bot/tenant tokens are rejected. This is the **only** CLI surface for `feed.groups.list_item` — there is no raw command.
|
||||
|
||||
User-only. Run with `--as user`.
|
||||
## Gotchas
|
||||
|
||||
## Scopes
|
||||
- **`chat_name` enrichment is unconditional → needs a second scope.** A v1 feed card's `feed_id` is always a chat ID (`oc_xxx`), so the shortcut always issues a follow-up `chats/batch_query` and injects `chat_name` into each entry of **both** `items[]` and `deleted_items[]`. There is no single-scope, un-enriched path — so this needs `im:chat:read` **in addition to** `im:feed_group_v1:read` (vs. `+feed-group-list`, which needs only the read scope).
|
||||
- **Unresolvable cards silently omit `chat_name`** — a soft-deleted chat or one you can't see just lacks the field; the command still exits 0. Do not treat a missing `chat_name` as an error. **p2p (direct) cards also omit it** (the server returns an empty `name`; clients show the partner's display name instead) — if you need a label, fetch the chat via `chats/batch_query`, read `p2p_target_id`, and resolve it with a contact lookup.
|
||||
- **Dual-list response.** Like `+feed-group-list`, results split into `items[]` (live) and `deleted_items[]` (soft-deleted); `--page-all` merges both. Incremental-sync consumers must read both.
|
||||
- **`--page-token` wins over `--page-all`** when both are set — you get exactly that one page.
|
||||
|
||||
Because chat-name resolution always runs, this shortcut needs **two** user scopes unconditionally:
|
||||
## HELP-GAP — not yet in `--help`/schema; keep until CLI adds it
|
||||
|
||||
- `im:feed_group_v1:read` — to read the items
|
||||
- `im:chat:read` — to resolve names
|
||||
|
||||
`chat_name` resolution always runs, so there is no single-scope, un-enriched path. For the other raw `feed.groups.*` methods, see [lark-im-feed-groups.md](lark-im-feed-groups.md).
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# First page, enriched with chat names
|
||||
lark-cli im +feed-group-list-item --as user --feed-group-id ofg_xxx
|
||||
|
||||
# Auto-paginate through everything within a time window
|
||||
lark-cli im +feed-group-list-item --as user --feed-group-id ofg_xxx \
|
||||
--page-all --start-time 1767196800000 --end-time 1767200000000
|
||||
```
|
||||
|
||||
## Flags
|
||||
|
||||
| Flag | Required | Description |
|
||||
|---|---|---|
|
||||
| `--feed-group-id` | Yes | Feed group ID (`ofg_xxx`); path parameter |
|
||||
| `--page-size` | No | Records per page, 1–50 (default 50) |
|
||||
| `--page-token` | No | Continuation token for a specific page |
|
||||
| `--page-all` | No | Auto-paginate and merge all pages |
|
||||
| `--page-limit` | No | Max pages when `--page-all` is set, 1–1000 (default 20) |
|
||||
| `--start-time` | No | Update-time window start (Unix milliseconds as a decimal string) |
|
||||
| `--end-time` | No | Update-time window end (Unix milliseconds as a decimal string) |
|
||||
|
||||
When `--page-token` is set explicitly, it wins over `--page-all` (you get exactly that page).
|
||||
|
||||
## Output
|
||||
|
||||
JSON keeps the raw envelope and adds `chat_name` to each resolvable item:
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [
|
||||
{ "feed_id": "oc_abc", "feed_type": "chat", "update_time": "1767196800000", "chat_name": "Release Team" }
|
||||
],
|
||||
"deleted_items": [
|
||||
{ "feed_id": "oc_def", "feed_type": "chat", "update_time": "1767196800000", "chat_name": "Old Channel" }
|
||||
],
|
||||
"page_token": "",
|
||||
"has_more": false
|
||||
}
|
||||
```
|
||||
|
||||
A feed card whose chat cannot be resolved (soft-deleted or no permission) simply omits `chat_name` — the command still exits 0. p2p (direct) chats also omit `chat_name`: the server returns an empty `name` for them (the client UI shows the partner's display name instead); if a label is needed, fetch the chat via `chats/batch_query`, read `p2p_target_id`, and resolve it with a contact lookup.
|
||||
- **Required scopes**: `im:feed_group_v1:read` **plus** `im:chat:read` (always, because enrichment always runs).
|
||||
- **Output fields** (raw envelope): `items[]` / `deleted_items[]`, each `{feed_id (oc_xxx), feed_type (chat), update_time, chat_name (when resolvable)}` · `page_token` · `has_more`.
|
||||
|
||||
## See also
|
||||
|
||||
- [lark-im-feed-groups.md](lark-im-feed-groups.md) — raw `feed.groups.*` APIs, enums, and rule guidance
|
||||
- [lark-im-feed-groups.md](lark-im-feed-groups.md) — raw `feed.groups.*` write APIs, enums, and rule guidance
|
||||
- [lark-im-feed-group-list.md](lark-im-feed-group-list.md) — list your feed groups
|
||||
- [lark-im-feed-group-query-item.md](lark-im-feed-group-query-item.md) — look up specific feed cards by ID
|
||||
|
||||
@@ -1,65 +1,24 @@
|
||||
# +feed-group-list
|
||||
|
||||
> Shortcut for `lark-cli im +feed-group-list`. List the caller's feed groups (tags) with auto-pagination that correctly merges both the live and soft-deleted lists.
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first for authentication, global parameters, and safety rules.
|
||||
|
||||
`+feed-group-list` is the only CLI surface for listing feed groups — there is no raw `feed.groups list` command. The list response carries two parallel arrays — `groups` (live) and `deleted_groups` (soft-deleted). The shortcut paginates this dual-list response correctly: its `--page-all` merges **both** arrays across pages (a naive single-array pager would silently drop one list's later pages). It adds no enrichment.
|
||||
Maps to `lark-cli im +feed-group-list`. **Run `lark-cli im +feed-group-list --help` for the authoritative flags (`--page-size` / `--page-token` / `--page-all` / `--page-limit` / `--start-time` / `--end-time` / `--as`), the page-size range, and the time format.** This file covers only what `--help` cannot.
|
||||
|
||||
## Identity
|
||||
**User identity only** (`--as user`); bot/tenant tokens are rejected by the server. This is the **only** CLI surface for listing feed groups — there is no raw `feed.groups list` command.
|
||||
|
||||
User-only. Run with `--as user`.
|
||||
## Gotchas
|
||||
|
||||
## Scopes
|
||||
- **Dual-list response, merged by `--page-all`.** The response carries two parallel arrays — `groups` (live) and `deleted_groups` (soft-deleted). `--page-all` merges **both** across pages; a naive single-array pager would silently drop one list's later pages. Incremental-sync consumers must read both arrays. Adds no enrichment.
|
||||
- **`--page-token` wins over `--page-all`** when both are set — you get exactly that one page, not a full sweep.
|
||||
- **Never infer completeness from counts.** `--page-size` caps `groups` + `deleted_groups` *combined*, so a page may hold fewer live groups than the size suggests, and per-page counts can be smaller still when entries are filtered. Pagination is governed solely by `has_more`.
|
||||
|
||||
- `im:feed_group_v1:read`
|
||||
## HELP-GAP — not yet in `--help`/schema; keep until CLI adds it
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# First page
|
||||
lark-cli im +feed-group-list --as user
|
||||
|
||||
# Auto-paginate through all your feed groups (both live and deleted)
|
||||
lark-cli im +feed-group-list --as user --page-all
|
||||
|
||||
# Within an update-time window
|
||||
lark-cli im +feed-group-list --as user --page-all \
|
||||
--start-time 1767196800000 --end-time 1767200000000
|
||||
```
|
||||
|
||||
## Flags
|
||||
|
||||
| Flag | Required | Description |
|
||||
|---|---|---|
|
||||
| `--page-size` | No | Records per page, 1–50 (default 50). Caps the combined `groups` + `deleted_groups` count, so a page may hold fewer live groups than the size suggests |
|
||||
| `--page-token` | No | Continuation token for a specific page |
|
||||
| `--page-all` | No | Auto-paginate and merge all pages (both lists) |
|
||||
| `--page-limit` | No | Max pages when `--page-all` is set, 1–1000 (default 20) |
|
||||
| `--start-time` | No | Update-time window start (Unix milliseconds as a decimal string) |
|
||||
| `--end-time` | No | Update-time window end (Unix milliseconds as a decimal string) |
|
||||
|
||||
When `--page-token` is set explicitly, it wins over `--page-all` (you get exactly that page).
|
||||
|
||||
## Output
|
||||
|
||||
JSON keeps the raw envelope; with `--page-all` both lists are returned fully merged:
|
||||
|
||||
```json
|
||||
{
|
||||
"groups": [
|
||||
{ "group_id": "ofg_xxx", "type": "normal", "name": "Releases", "rules": { "rules": [] } }
|
||||
],
|
||||
"deleted_groups": [
|
||||
{ "group_id": "ofg_yyy", "type": "rule", "name": "Old", "rules": { "rules": [] } }
|
||||
],
|
||||
"page_token": "",
|
||||
"has_more": false
|
||||
}
|
||||
```
|
||||
|
||||
> `page_size` counts live and deleted groups together, and the per-page count can be smaller still when entries are filtered — so never infer completeness from counts. Pagination is governed solely by `has_more`.
|
||||
- **Required scope**: `im:feed_group_v1:read`.
|
||||
- **Output fields** (raw envelope): `groups[]` / `deleted_groups[]`, each `{group_id (ofg_xxx), type (normal|rule), name, rules{rules[]}}` · `page_token` · `has_more`. The `rules` shape is documented in [lark-im-feed-groups.md](lark-im-feed-groups.md).
|
||||
|
||||
## See also
|
||||
|
||||
- [lark-im-feed-groups.md](lark-im-feed-groups.md) — raw `feed.groups.*` APIs, enums, and rule guidance
|
||||
- [lark-im-feed-groups.md](lark-im-feed-groups.md) — raw `feed.groups.*` write APIs, enums, and rule guidance
|
||||
- [lark-im-feed-group-list-item.md](lark-im-feed-group-list-item.md) — list the feed cards inside one group
|
||||
- [lark-im-feed-group-query-item.md](lark-im-feed-group-query-item.md) — look up specific feed cards by ID
|
||||
|
||||
@@ -1,44 +1,24 @@
|
||||
# +feed-group-query-item
|
||||
|
||||
> Shortcut for `lark-cli im +feed-group-query-item`. Look up specific feed cards inside one feed group (tag) by ID, enriched with a readable `chat_name`.
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first for authentication, global parameters, and safety rules.
|
||||
|
||||
`+feed-group-query-item` is the only CLI surface for the `feed.groups.batch_query_item` read API — there is no raw `feed.groups batch_query_item` command. It resolves a human-readable `chat_name` for every feed card it returns: a v1 feed card's `feed_id` is always a chat ID (`oc_xxx`), so the shortcut issues a follow-up `POST /open-apis/im/v1/chats/batch_query` and injects `chat_name` into each entry of both `items[]` and `deleted_items[]`.
|
||||
Maps to `lark-cli im +feed-group-query-item`. **Run `lark-cli im +feed-group-query-item --help` for the authoritative flags (`--feed-group-id` / `--feed-id` / `--as`); `--feed-id` is a comma-separated list of chat IDs and `feed_type` is fixed to `chat`.** This file covers only what `--help` cannot.
|
||||
|
||||
## Identity
|
||||
**User identity only** (`--as user`); bot/tenant tokens are rejected. This is the **only** CLI surface for `feed.groups.batch_query_item` — there is no raw command.
|
||||
|
||||
User-only. Run with `--as user`.
|
||||
## Gotchas
|
||||
|
||||
## Scopes
|
||||
- **Lightweight ID lookup — prefer it over the list methods when you already hold the IDs.** Use this when you have the `feed_id`s (the `oc_xxx` you passed to `batch_add_item`); reserve `+feed-group-list-item` (paginated, heavier) for discovering IDs you don't have. **No pagination** for this method.
|
||||
- **`chat_name` enrichment is unconditional → needs a second scope.** Resolves `chat_name` for each card exactly as `+feed-group-list-item` does (follow-up `chats/batch_query`, injected into both `items[]` and `deleted_items[]`). So this needs `im:chat:read` **in addition to** `im:feed_group_v1:read`; there is no un-enriched path.
|
||||
- **Unresolvable cards silently omit `chat_name`** — soft-deleted or no-permission chats just lack the field; the command still exits 0. **p2p (direct) cards also omit it** (server returns an empty `name`); to label one, fetch the chat via `chats/batch_query`, read `p2p_target_id`, and resolve it with a contact lookup.
|
||||
|
||||
Because chat-name resolution always runs, this shortcut needs **two** user scopes unconditionally:
|
||||
## HELP-GAP — not yet in `--help`/schema; keep until CLI adds it
|
||||
|
||||
- `im:feed_group_v1:read` — to read the items
|
||||
- `im:chat:read` — to resolve names
|
||||
|
||||
`chat_name` resolution always runs, so there is no single-scope, un-enriched path. For the other raw `feed.groups.*` methods, see [lark-im-feed-groups.md](lark-im-feed-groups.md).
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
lark-cli im +feed-group-query-item --as user \
|
||||
--feed-group-id ofg_xxx --feed-id oc_a,oc_b
|
||||
```
|
||||
|
||||
## Flags
|
||||
|
||||
| Flag | Required | Description |
|
||||
|---|---|---|
|
||||
| `--feed-group-id` | Yes | Feed group ID (`ofg_xxx`); path parameter |
|
||||
| `--feed-id` | Yes | Comma-separated chat IDs (`oc_xxx`); `feed_type` is fixed to `chat` |
|
||||
|
||||
## Output
|
||||
|
||||
The command sends `{"items":[{"feed_id":"oc_a","feed_type":"chat"},{"feed_id":"oc_b","feed_type":"chat"}]}`, then enriches the response (`items[]` and `deleted_items[]`) with `chat_name` exactly as `+feed-group-list-item` does. There is no pagination for this method.
|
||||
|
||||
A feed card whose chat cannot be resolved (soft-deleted or no permission) simply omits `chat_name` — the command still exits 0. p2p (direct) chats also omit `chat_name`: the server returns an empty `name` for them (the client UI shows the partner's display name instead); if a label is needed, fetch the chat via `chats/batch_query`, read `p2p_target_id`, and resolve it with a contact lookup.
|
||||
- **Required scopes**: `im:feed_group_v1:read` **plus** `im:chat:read` (always).
|
||||
- **Output fields** (raw envelope): `items[]` (live) / `deleted_items[]` (soft-deleted), each `{feed_id (oc_xxx), feed_type (chat), update_time, chat_name (when resolvable)}`.
|
||||
|
||||
## See also
|
||||
|
||||
- [lark-im-feed-groups.md](lark-im-feed-groups.md) — raw `feed.groups.*` APIs, enums, and rule guidance
|
||||
- [lark-im-feed-groups.md](lark-im-feed-groups.md) — raw `feed.groups.*` write APIs, enums, and rule guidance
|
||||
- [lark-im-feed-group-list.md](lark-im-feed-group-list.md) — list your feed groups
|
||||
- [lark-im-feed-group-list-item.md](lark-im-feed-group-list-item.md) — list all feed cards in a group (paginated)
|
||||
|
||||
@@ -2,70 +2,46 @@
|
||||
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first to understand authentication, global parameters, and safety rules.
|
||||
|
||||
This reference is the shared annotation target for the IM feed-group (tag) APIs: it documents what each method does, the `--params` / `--data` request and response shapes, and the enum surface used in payloads. The full method list is in [Command Overview](#command-overview) below.
|
||||
This reference covers the IM feed-group (tag) APIs. **There is no resolvable `--help` or `schema` for the six raw `feed.groups.*` write methods** (see [Schema gap](#schema-gap)), so the request/response shapes, enums, and rule guidance below are the only source of truth and are kept in full. The three read methods are exposed only as typed `+` shortcuts — for those, see the sibling references and their `--help` rather than this file.
|
||||
|
||||
> **Important:** The six raw commands (`create`, `update`, `delete`, `batch_query`, `batch_add_item`, `batch_remove_item`) take structured input through `--params '<json>'` and `--data '<json>'` rather than typed flags. The three read methods (`list`, `list_item`, `batch_query_item`) are exposed only as typed `+` shortcut wrappers — see [Shortcuts](#shortcuts). All methods are user-only; see [Common Notes](#common-notes).
|
||||
> **All `feed.groups.*` methods are user-only** — they require `user_access_token`. Run with `--as user`; bot/tenant tokens are rejected.
|
||||
|
||||
> **Picking a read method:** `batch_query` / `+feed-group-query-item` are lightweight ID lookups; `+feed-group-list` / `+feed-group-list-item` paginate the whole set and are much heavier. When you already hold the IDs (`group_id` from `create`, the `feed_id`s you passed to `batch_add_item`), prefer the lightweight lookup. Reserve the list methods for when you actually need to discover IDs you don't have.
|
||||
## Schema gap
|
||||
|
||||
## Command Overview
|
||||
> Report this when you hit it: `lark-cli schema im.feed.groups.<method>` does **not** resolve — there is no `feed` service and no `im.feed*` resource in the schema registry (`lark-cli schema im` lists only `chat.members, chats, images, messages, pins, reactions, threads`). Do **not** send users to `schema` for feed-group methods. Until the CLI registers these methods, this reference is the authority for the six raw methods, and the three shortcuts' `--help` is the authority for the read methods.
|
||||
|
||||
| Method | Purpose |
|
||||
|---|---|
|
||||
| `feed.groups.create` | Create a new feed group (tag) |
|
||||
| `feed.groups.update` | Update a feed group's name and/or rules |
|
||||
| `feed.groups.delete` | Delete one feed group |
|
||||
| `feed.groups.batch_query` | Look up feed groups by ID list |
|
||||
| `feed.groups.list` | List the caller's feed groups with optional time-range filter — **CLI: only via `+feed-group-list` shortcut** |
|
||||
| `feed.groups.batch_add_item` | Add feed cards (chats) into a feed group |
|
||||
| `feed.groups.batch_remove_item` | Remove feed cards from a feed group |
|
||||
| `feed.groups.batch_query_item` | Look up feed cards inside a group by ID list — **CLI: only via `+feed-group-query-item` shortcut** |
|
||||
| `feed.groups.list_item` | List feed cards inside one feed group — **CLI: only via `+feed-group-list-item` shortcut** |
|
||||
## Routing: pick the right method
|
||||
|
||||
> HTTP method and path are not duplicated here. For the six raw methods, inspect them with `lark-cli schema im.feed.groups.<method>` when needed; the three shortcut-only read methods (`list`, `list_item`, `batch_query_item`) use typed flags (see their `--help`).
|
||||
- **Read paths are shortcut-only** — `list`, `list_item`, `batch_query_item` have **no** raw command. Use the typed `+` shortcuts:
|
||||
- [`+feed-group-list`](lark-im-feed-group-list.md) — list your feed groups (`--page-all` merges live + soft-deleted). No enrichment. Scope: `im:feed_group_v1:read`.
|
||||
- [`+feed-group-list-item`](lark-im-feed-group-list-item.md) — list feed cards in a group, enriched with `chat_name`. Scopes: `im:feed_group_v1:read` + `im:chat:read`.
|
||||
- [`+feed-group-query-item`](lark-im-feed-group-query-item.md) — look up feed cards by ID, enriched with `chat_name`. Scopes: `im:feed_group_v1:read` + `im:chat:read`.
|
||||
- **Lightweight lookup vs. heavy list**: when you already hold the IDs (`group_id` from `create`, the `feed_id`s you passed to `batch_add_item`), prefer the lightweight ID lookups (`batch_query` / `+feed-group-query-item`) over the paginated list methods (`+feed-group-list` / `+feed-group-list-item`), which are much heavier. Reserve the list methods for discovering IDs you don't have.
|
||||
- **`type=normal` vs `type=rule`**: a `normal` group's membership is managed explicitly via `batch_add_item` / `batch_remove_item`; a `rule` group auto-populates from `feed_group_creator.rules`. See [rule guidance](#choosing-a-group-shape).
|
||||
|
||||
## Shortcuts
|
||||
## Choosing a group shape
|
||||
|
||||
Three typed `+` shortcuts cover the feed-group read paths. All are user-only.
|
||||
|
||||
| Shortcut | Purpose | Notes |
|
||||
|---|---|---|
|
||||
| [`+feed-group-list`](lark-im-feed-group-list.md) | List your feed groups | Its `--page-all` correctly merges the live and soft-deleted lists. No enrichment |
|
||||
| [`+feed-group-list-item`](lark-im-feed-group-list-item.md) | List the feed cards inside a group | Enriches each card with `chat_name` |
|
||||
| [`+feed-group-query-item`](lark-im-feed-group-query-item.md) | Look up feed cards in a group by ID | Enriches each card with `chat_name` |
|
||||
|
||||
The two `*-item` shortcuts resolve `chat_name` via a follow-up `chats/batch_query`, so they need `im:chat:read` in addition to `im:feed_group_v1:read`; `+feed-group-list` needs only `im:feed_group_v1:read`. All three are the **only** CLI surface for their methods — `list`, `list_item`, and `batch_query_item` have no raw command; full flags and response shapes live in the shortcut docs linked above.
|
||||
|
||||
## Common Notes
|
||||
|
||||
- `feed_group_id` is the feed-group identifier returned by `create`, typically formatted as `ofg_xxx`. It is an opaque string — the group's stable ID.
|
||||
- `feed_id` is the identifier of one feed card inside a group. In v1 only the `chat` feed card type is supported (see `feed_card_type` below), so `feed_id` is currently a chat ID such as `oc_xxx`.
|
||||
- All `feed.groups.*` methods require `user_access_token`. Run with `--as user`; bot/tenant tokens are rejected.
|
||||
- Read APIs (`batch_query`, `list`, `batch_query_item`, `list_item`) return **two parallel lists**: a live list (`groups[]` or `items[]`) and a soft-deleted list (`deleted_groups[]` or `deleted_items[]`). Consumers tracking incremental sync should consume both.
|
||||
- Time-range fields (`start_time`, `end_time`, `update_time`) are Unix timestamps **in milliseconds**, encoded as decimal strings (e.g. `1767196800000`).
|
||||
- Rule-based feed groups (`type=rule`) auto-populate from the rules declared in `feed_group_creator.rules`. Normal feed groups (`type=normal`) are managed explicitly via `batch_add_item` / `batch_remove_item`.
|
||||
|
||||
> **Choose the simplest group that fits** — it keeps `create` / `update` fast and predictable. Apply these in order:
|
||||
> **Choose the simplest group that fits** — it keeps `create` / `update` fast and predictable. Apply in order:
|
||||
> 1. **Prefer `type=normal`.** When the target chats are known up front, set membership explicitly with `batch_add_item` / `batch_remove_item`. Use `type=rule` only when membership must be derived automatically.
|
||||
> 2. **Keep the rule set smallest.** Use the fewest `rules[]` and `condition_items[]` that express the intent (one condition is ideal). This outranks the style rules below — never split a rule or add conditions just to satisfy them (e.g. one `match_any` rule beats two single-condition rules for "A or B").
|
||||
> 2. **Keep the rule set smallest.** Use the fewest `rules[]` and `condition_items[]` that express the intent (one condition is ideal). This outranks the precision rule below — never split a rule or add conditions just to satisfy it (e.g. one `match_any` rule beats two single-condition rules for "A or B").
|
||||
> 3. **Within that, make each condition precise.** Prefer positive, specific conditions (`is`, or `contain` with a distinctive keyword) over exclusion (`is_not`, `not_contain`) or broad keywords, which capture more than intended. For a multi-condition rule, prefer `match_all` (narrower) over `match_any` (wider).
|
||||
|
||||
## Inspect Schema
|
||||
## Identity / ID conventions (shared)
|
||||
|
||||
```bash
|
||||
lark-cli schema im.feed.groups
|
||||
lark-cli schema im.feed.groups.create --format pretty
|
||||
lark-cli schema im.feed.groups.batch_add_item --format pretty
|
||||
```
|
||||
- `feed_group_id` — the feed-group identifier returned by `create`, formatted as `ofg_xxx`.
|
||||
- `feed_id` — the identifier of one feed card inside a group. In v1 only the `chat` feed card type is supported, so `feed_id` is currently a chat ID such as `oc_xxx`.
|
||||
- **Read APIs return two parallel lists** — a live list (`groups[]` or `items[]`) and a soft-deleted list (`deleted_groups[]` or `deleted_items[]`). Consumers tracking incremental sync must consume both.
|
||||
|
||||
> `list`, `list_item`, and `batch_query_item` have no raw method schema (they are shortcut-only). Inspect their flags with `lark-cli im +feed-group-list --help` / `+feed-group-list-item --help` / `+feed-group-query-item --help` instead.
|
||||
---
|
||||
|
||||
# HELP-GAP — raw `feed.groups.*` write methods (no `--help`/schema; keep until CLI adds it)
|
||||
|
||||
> Everything from here down documents the six raw methods that take `--params '<json>'` / `--data '<json>'`. None of it is expressible via `--help` or `schema` today (the methods are unregistered — see [Schema gap](#schema-gap)). Once the CLI registers these methods, replace this section with a pointer to schema.
|
||||
|
||||
## create
|
||||
|
||||
Create a new feed group. Returns the new `group_id` on success.
|
||||
|
||||
> **Prefer `type=normal`.** Use `type=rule` only when membership must be derived automatically, and keep the rule set small and precise — see the guidance under [Common Notes](#common-notes).
|
||||
|
||||
```bash
|
||||
# Normal (empty) group
|
||||
lark-cli im feed.groups create --as user \
|
||||
@@ -95,35 +71,15 @@ lark-cli im feed.groups create --as user \
|
||||
}'
|
||||
```
|
||||
|
||||
### Request
|
||||
|
||||
#### `--params`
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|---|---|---|
|
||||
| `user_id_type` | No | ID type used when the request body contains `user_id` references inside rules. One of `open_id`, `union_id`, `user_id` |
|
||||
|
||||
#### `--data`
|
||||
|
||||
| Field | Required | Description |
|
||||
|---|---|---|
|
||||
| `feed_group_creator.type` | Yes | `normal` (empty group) or `rule` (auto-populated by rules) |
|
||||
| `feed_group_creator.name` | Yes | Display name, e.g. `"标签名称测试"` |
|
||||
| `feed_group_creator.rules` | No | Rule object (required when `type=rule`). See `feed_group_rules` section below |
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"group_id": "ofg_xxx"
|
||||
}
|
||||
```
|
||||
- `--params`: `user_id_type` (optional) — `open_id` | `union_id` | `user_id`; used when the body contains `user_id` references inside rules.
|
||||
- `--data`: `feed_group_creator.type` (required: `normal` | `rule`) · `feed_group_creator.name` (required) · `feed_group_creator.rules` (required when `type=rule`; see [feed_group_rules](#feed_group_rules)).
|
||||
- Response: `{"group_id":"ofg_xxx"}`.
|
||||
|
||||
## update
|
||||
|
||||
Update a feed group's name and/or rules. The `update_fields` array tells the server which fields are being updated.
|
||||
Update a feed group's name and/or rules. The `update_fields` array tells the server which fields to apply.
|
||||
|
||||
> **Scope each update to what actually changed.** If you only need to rename, pass `update_fields:[1]` so the rules are left untouched. When you do change rules, the same guidance under [Common Notes](#common-notes) applies to the resulting set.
|
||||
> **Scope each update to what actually changed.** To rename only, pass `update_fields:[1]` so rules are left untouched. When you do change rules, the [group-shape guidance](#choosing-a-group-shape) applies to the resulting set.
|
||||
|
||||
```bash
|
||||
# Rename only
|
||||
@@ -131,60 +87,29 @@ lark-cli im feed.groups update --as user \
|
||||
--params '{"feed_group_id":"ofg_xxx"}' \
|
||||
--data '{"feed_group_updater":{"name":"测试标签名称","update_fields":[1]}}'
|
||||
|
||||
# Replace rules only (rules array uses the feed_group_rules shape — see that section)
|
||||
# Replace rules only
|
||||
lark-cli im feed.groups update --as user \
|
||||
--params '{"feed_group_id":"ofg_xxx"}' \
|
||||
--data '{
|
||||
"feed_group_updater":{
|
||||
"rules":{"rules":[]},
|
||||
"update_fields":[2]
|
||||
}
|
||||
}'
|
||||
--data '{"feed_group_updater":{"rules":{"rules":[]},"update_fields":[2]}}'
|
||||
```
|
||||
|
||||
### Request
|
||||
- `--params`: `feed_group_id` (required, path) · `user_id_type` (optional, for `user_id` fields inside `rules`).
|
||||
- `--data`: `feed_group_updater.name` (optional) · `feed_group_updater.rules` (optional; same shape as `create`) · `feed_group_updater.update_fields` (optional integer markers: `1`=name, `2`=rules — server applies only the listed fields).
|
||||
- Response: empty body on success — inspect the CLI exit code for status.
|
||||
|
||||
#### `--params`
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|---|---|---|
|
||||
| `feed_group_id` | Yes | Path parameter — the feed group to update |
|
||||
| `user_id_type` | No | ID type for any `user_id` fields inside `rules` |
|
||||
|
||||
#### `--data`
|
||||
|
||||
| Field | Required | Description |
|
||||
|---|---|---|
|
||||
| `feed_group_updater.name` | No | New display name |
|
||||
| `feed_group_updater.rules` | No | Replacement rule object. Same structure as `create.feed_group_creator.rules` |
|
||||
| `feed_group_updater.update_fields` | No | Array of integer update markers: `1` = name, `2` = rules. Server applies only the listed fields |
|
||||
|
||||
### Response
|
||||
|
||||
Empty body on success. Inspect the CLI exit code for status.
|
||||
> **`update_fields` takes integers, not strings.** The server rejects the lowercase string forms (`"name"`, `"rules"`) with `9499 Invalid parameter value`. Use `[1]` / `[2]`. Omit the array (or pass `[]`) to make no field updates.
|
||||
|
||||
## delete
|
||||
|
||||
Delete one feed group.
|
||||
|
||||
```bash
|
||||
lark-cli im feed.groups delete --as user \
|
||||
--params '{"feed_group_id":"ofg_xxx"}'
|
||||
lark-cli im feed.groups delete --as user --params '{"feed_group_id":"ofg_xxx"}'
|
||||
```
|
||||
|
||||
### Request
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|---|---|---|
|
||||
| `feed_group_id` | Yes | Path parameter — the feed group to delete |
|
||||
|
||||
### Response
|
||||
|
||||
Empty body on success.
|
||||
- `--params`: `feed_group_id` (required, path). Response: empty body on success.
|
||||
|
||||
## batch_query
|
||||
|
||||
Look up feed groups by an explicit list of IDs. Returns both live and soft-deleted matches.
|
||||
Look up feed groups by an explicit ID list. Returns both live and soft-deleted matches.
|
||||
|
||||
```bash
|
||||
lark-cli im feed.groups batch_query --as user \
|
||||
@@ -192,57 +117,9 @@ lark-cli im feed.groups batch_query --as user \
|
||||
--data '{"group_ids":["ofg_xxx","ofg_yyy"]}'
|
||||
```
|
||||
|
||||
### Request
|
||||
|
||||
#### `--params`
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|---|---|---|
|
||||
| `user_id_type` | No | ID type used when the response includes `user_id` references inside `groups[].rules` |
|
||||
|
||||
#### `--data`
|
||||
|
||||
| Field | Required | Description |
|
||||
|---|---|---|
|
||||
| `group_ids` | Yes | Array of feed group IDs to look up |
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"groups": [
|
||||
{
|
||||
"group_id": "ofg_xxx",
|
||||
"type": "normal",
|
||||
"name": "test",
|
||||
"rules": { "rules": [] }
|
||||
}
|
||||
],
|
||||
"deleted_groups": [
|
||||
{
|
||||
"group_id": "ofg_yyy",
|
||||
"type": "rule",
|
||||
"name": "test",
|
||||
"rules": { "rules": [] }
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Each `rules.rules[]` element follows the `feed_group_rules` shape — see that section for the full structure.
|
||||
|
||||
### Top-Level Fields
|
||||
|
||||
| Field | Type | Meaning |
|
||||
|---|---|---|
|
||||
| `groups` | `array<object>` | Live feed groups for the requested IDs |
|
||||
| `deleted_groups` | `array<object>` | Soft-deleted matches, returned for incremental-sync clients |
|
||||
|
||||
Each element carries `group_id`, `type`, `name`, and (when defined) `rules`.
|
||||
|
||||
## list
|
||||
|
||||
Shortcut-only: [`+feed-group-list`](lark-im-feed-group-list.md). Lists the caller's feed groups, optionally filtered by an update-time window. Its `--page-all` correctly merges the live (`groups`) and soft-deleted (`deleted_groups`) lists across pages. There is no raw command — flags and response shape are in the linked shortcut doc.
|
||||
- `--params`: `user_id_type` (optional) — interprets `user_id` references inside `groups[].rules`.
|
||||
- `--data`: `group_ids` (required array).
|
||||
- Response: `{"groups":[...],"deleted_groups":[...]}`; each element carries `group_id`, `type`, `name`, and (when defined) `rules` (the [feed_group_rules](#feed_group_rules) shape). `deleted_groups[]` is the soft-deleted list returned for incremental-sync clients.
|
||||
|
||||
## batch_add_item
|
||||
|
||||
@@ -251,159 +128,41 @@ Add feed cards (chats) into one feed group. Partial failures are reported in `fa
|
||||
```bash
|
||||
lark-cli im feed.groups batch_add_item --as user \
|
||||
--params '{"feed_group_id":"ofg_xxx"}' \
|
||||
--data '{
|
||||
"items":[
|
||||
{"feed_id":"oc_xxx","feed_type":"chat"},
|
||||
{"feed_id":"oc_yyy","feed_type":"chat"}
|
||||
]
|
||||
}'
|
||||
--data '{"items":[{"feed_id":"oc_xxx","feed_type":"chat"},{"feed_id":"oc_yyy","feed_type":"chat"}]}'
|
||||
```
|
||||
|
||||
### Request
|
||||
- `--params`: `feed_group_id` (required, path).
|
||||
- `--data`: `items[]` (required array). Each item: `feed_id` (the chat ID, e.g. `oc_xxx`) and `feed_type` (`"chat"` only).
|
||||
- Response: `{"failed_items":[{"item":{...},"error_code":<int>,"error_message":"..."}]}`. **`failed_items` absent or empty means all succeeded** — check it for partial failure; it lists the original `{feed_id, feed_type}` plus a numeric `error_code` and human-readable `error_message`.
|
||||
|
||||
| Source | Field | Required | Description |
|
||||
|---|---|---|---|
|
||||
| `--params` | `feed_group_id` | Yes | Path parameter — the target feed group |
|
||||
| `--data` | `items[]` | Yes | Array of feed cards to add |
|
||||
| `--data` | `items[].feed_id` | No | The chat ID to add (e.g. `oc_xxx`) |
|
||||
| `--data` | `items[].feed_type` | Yes (`"chat"` only) | Wire-typed as an open string. v1 OAPI service accepts only `chat`; anything else is rejected at runtime. See the Enums section. |
|
||||
|
||||
> Note: `items[].feed_id` is not marked as required in the API schema, but every element of `items` must set it — a missing field yields an unusable entry. Always pass `{"feed_id": "oc_xxx", "feed_type": "chat"}` per item.
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"failed_items": [
|
||||
{
|
||||
"item": { "feed_id": "oc_xxx", "feed_type": "chat" },
|
||||
"error_code": 240001,
|
||||
"error_message": "feed_id is invalid"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Meaning |
|
||||
|---|---|---|
|
||||
| `failed_items` | `array<object>` | Items that failed; absent or empty means all succeeded |
|
||||
| `failed_items[].item` | `object` | The original `{feed_id, feed_type}` element |
|
||||
| `failed_items[].error_code` | `integer` | Numeric error code |
|
||||
| `failed_items[].error_message` | `string` | Human-readable failure reason |
|
||||
> **`items[].feed_id` is a trap.** Although the meta marks it `Required: No`, every element of `items` must set it — a missing `feed_id` yields an unusable entry. Always pass `{"feed_id":"oc_xxx","feed_type":"chat"}` per item.
|
||||
|
||||
## batch_remove_item
|
||||
|
||||
Remove feed cards from one feed group. Same request and response shape as `batch_add_item`.
|
||||
Remove feed cards from one feed group. **Same request and response shape as `batch_add_item`** — same `feed_group_id` path param, same `items[]` body, same `failed_items[]` response, and the same `feed_id`-required-in-practice caveat.
|
||||
|
||||
```bash
|
||||
lark-cli im feed.groups batch_remove_item --as user \
|
||||
--params '{"feed_group_id":"ofg_xxx"}' \
|
||||
--data '{
|
||||
"items":[
|
||||
{"feed_id":"oc_xxx","feed_type":"chat"}
|
||||
]
|
||||
}'
|
||||
--data '{"items":[{"feed_id":"oc_xxx","feed_type":"chat"}]}'
|
||||
```
|
||||
|
||||
### Request
|
||||
## Enums (raw methods)
|
||||
|
||||
| Source | Field | Required | Description |
|
||||
|---|---|---|---|
|
||||
| `--params` | `feed_group_id` | Yes | Path parameter — the target feed group |
|
||||
| `--data` | `items[]` | Yes | Array of feed cards to remove |
|
||||
| `--data` | `items[].feed_id` | No | The chat ID to remove |
|
||||
| `--data` | `items[].feed_type` | Yes (`"chat"` only) | Wire-typed as an open string. v1 OAPI service accepts only `chat`; anything else is rejected at runtime. See the Enums section. |
|
||||
Sourced from the internal datasync IDL (`lark.im.datasync.open.thrift`). Values listed are exhaustive.
|
||||
|
||||
> Note: same caveat as `batch_add_item` — `items[].feed_id` is optional per the API schema but must be present in practice.
|
||||
|
||||
### Response
|
||||
|
||||
Identical shape to `batch_add_item` — `failed_items[]` lists rows that did not remove cleanly.
|
||||
|
||||
## batch_query_item
|
||||
|
||||
Shortcut-only: [`+feed-group-query-item`](lark-im-feed-group-query-item.md). Looks up feed cards in a group by an explicit ID list and enriches each with `chat_name`. There is no raw command — flags and response shape are in the linked shortcut doc.
|
||||
|
||||
## list_item
|
||||
|
||||
Shortcut-only: [`+feed-group-list-item`](lark-im-feed-group-list-item.md). Lists the feed cards inside a group (paginated, `--page-all` supported) and enriches each with `chat_name`. There is no raw command — flags and response shape are in the linked shortcut doc.
|
||||
|
||||
## Enums
|
||||
|
||||
All enum values listed here are exhaustive.
|
||||
|
||||
### `feed_group_type`
|
||||
|
||||
Used in `feed_group_creator.type` and the response `groups[].type`.
|
||||
|
||||
- `normal` — empty group; members managed explicitly via `batch_add_item` / `batch_remove_item`.
|
||||
- `rule` — auto-populated; `feed_group_creator.rules` must be supplied.
|
||||
|
||||
### `feed_card_type`
|
||||
|
||||
Used in `items[].feed_type` everywhere a feed card appears. Wire type is an open string.
|
||||
|
||||
- `chat` — the only value the v1 OAPI service accepts. `feed_id` is therefore a chat ID such as `oc_xxx`.
|
||||
|
||||
The CLI does not pre-validate this field — passing anything other than `chat` reaches the server and is rejected at runtime. Treat `chat` as effectively required.
|
||||
|
||||
### `feed_group_rule_action`
|
||||
|
||||
Used inside `feed_group_rules.rules[].action`.
|
||||
|
||||
- `add` — when the condition matches, add the matching feed into this group.
|
||||
- `remove` — when the condition matches, remove the matching feed from this group.
|
||||
|
||||
### `feed_group_rule_cond_match_type`
|
||||
|
||||
Used inside `feed_group_rules.rules[].condition.match_type`.
|
||||
|
||||
- `match_all` — every condition item must match.
|
||||
- `match_any` — at least one condition item must match.
|
||||
|
||||
### `feed_group_rule_cond_item_type`
|
||||
|
||||
Used inside `feed_group_rules.rules[].condition.condition_items[].type`. Determines which sibling field of the item is consulted.
|
||||
|
||||
- `keyword` — match against a keyword; consult the `keyword` field.
|
||||
- `chatter` — match against a user; consult the `user_id` field (interpreted per the request's `user_id_type`).
|
||||
- `chat_type` — match against a chat type; consult the `chat_type` field.
|
||||
|
||||
### `feed_group_rule_cond_item_operator`
|
||||
|
||||
Used inside `feed_group_rules.rules[].condition.condition_items[].operator`. Typically paired with the relevant `type`:
|
||||
|
||||
- `contain` — substring match; typically paired with `keyword`.
|
||||
- `not_contain` — substring non-match; typically paired with `keyword`.
|
||||
- `is` — equality; typically paired with `chatter` or `chat_type`.
|
||||
- `is_not` — non-equality; typically paired with `chatter` or `chat_type`.
|
||||
|
||||
### `feed_group_rule_cond_item_chat_type`
|
||||
|
||||
Used inside `feed_group_rules.rules[].condition.condition_items[].chat_type` when `type=chat_type`.
|
||||
|
||||
- `p2p`
|
||||
- `group`
|
||||
- `thread_group`
|
||||
- `helpdesk`
|
||||
- `bot`
|
||||
- `mute`
|
||||
- `flag`
|
||||
- `cross_tenant`
|
||||
- `any`
|
||||
|
||||
### `update_fields`
|
||||
|
||||
Used inside `feed_group_updater.update_fields`. Multiple values may be listed.
|
||||
|
||||
- `1` — update name only.
|
||||
- `2` — update rules only.
|
||||
|
||||
Wire form: integers (`1` = name, `2` = rules). The server rejects the lowercase string forms (`"name"`, `"rules"`) with `9499 Invalid parameter value`. Omit the array (or pass an empty array) to make no field updates.
|
||||
- **`feed_group_type`** (`feed_group_creator.type`, response `groups[].type`): `normal` (empty; managed via `batch_add_item`/`batch_remove_item`) · `rule` (auto-populated; requires `feed_group_creator.rules`).
|
||||
- **`feed_card_type`** (`items[].feed_type`; wire alias `FeedCardTypeV1`): `chat` is the **only** value the v1 OAPI service accepts, so `feed_id` is a chat ID (`oc_xxx`). **The CLI does not pre-validate this** — anything other than `chat` reaches the server and is rejected at runtime. Treat `chat` as effectively required.
|
||||
- **`feed_group_rule_action`** (`rules[].action`): `add` · `remove`.
|
||||
- **`feed_group_rule_cond_match_type`** (`rules[].condition.match_type`): `match_all` (every item must match) · `match_any` (at least one).
|
||||
- **`feed_group_rule_cond_item_type`** (`condition_items[].type` — determines which sibling field is consulted): `keyword` (→ `keyword` field) · `chatter` (→ `user_id` field, interpreted per `user_id_type`) · `chat_type` (→ `chat_type` field).
|
||||
- **`feed_group_rule_cond_item_operator`** (`condition_items[].operator`): `contain` / `not_contain` (substring, with `keyword`) · `is` / `is_not` (equality, with `chatter` or `chat_type`).
|
||||
- **`feed_group_rule_cond_item_chat_type`** (`condition_items[].chat_type` when `type=chat_type`): `p2p` · `group` · `thread_group` · `helpdesk` · `bot` · `mute` · `flag` · `cross_tenant` · `any`.
|
||||
- **`update_fields`** (`feed_group_updater.update_fields`): integers `1` = name, `2` = rules (multiple may be listed). See the string-vs-integer trap under [update](#update).
|
||||
|
||||
## feed_group_rules
|
||||
|
||||
The same nested object is used in `feed_group_creator.rules` (create), `feed_group_updater.rules` (update), and in read responses under `groups[].rules`. Shape:
|
||||
The same nested object is used in `feed_group_creator.rules` (create), `feed_group_updater.rules` (update), and read responses under `groups[].rules`:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -424,25 +183,15 @@ The same nested object is used in `feed_group_creator.rules` (create), `feed_gro
|
||||
|
||||
Per-`type` required-field legend:
|
||||
|
||||
- `type=keyword` → `keyword` is required; `user_id` and `chat_type` are ignored.
|
||||
- `type=chatter` → `user_id` is required; the request's `user_id_type` query parameter tells the server how to interpret it.
|
||||
- `type=chat_type` → `chat_type` is required.
|
||||
- `type=keyword` → `keyword` required; `user_id` and `chat_type` ignored.
|
||||
- `type=chatter` → `user_id` required; the request's `user_id_type` query parameter tells the server how to interpret it.
|
||||
- `type=chat_type` → `chat_type` required.
|
||||
|
||||
## Permissions
|
||||
## Permissions (raw write methods)
|
||||
|
||||
| Method | Scope |
|
||||
|---|---|
|
||||
| `feed.groups.create` | `im:feed_group_v1:write` |
|
||||
| `feed.groups.update` | `im:feed_group_v1:write` |
|
||||
| `feed.groups.delete` | `im:feed_group_v1:write` |
|
||||
| `feed.groups.batch_query` | `im:feed_group_v1:read` |
|
||||
| `feed.groups.batch_add_item` | `im:feed_group_v1:write` |
|
||||
| `feed.groups.batch_remove_item` | `im:feed_group_v1:write` |
|
||||
|
||||
The three read methods are shortcut-only:
|
||||
|
||||
- [`+feed-group-list`](lark-im-feed-group-list.md) — `im:feed_group_v1:read`
|
||||
- [`+feed-group-list-item`](lark-im-feed-group-list-item.md) / [`+feed-group-query-item`](lark-im-feed-group-query-item.md) — `im:feed_group_v1:read` **plus** `im:chat:read` (they always resolve `chat_name`)
|
||||
- `create` / `update` / `delete` / `batch_add_item` / `batch_remove_item` → `im:feed_group_v1:write`.
|
||||
- `batch_query` → `im:feed_group_v1:read`.
|
||||
- Read shortcuts: scopes listed under [Routing](#routing-pick-the-right-method) and in each shortcut doc.
|
||||
|
||||
If a required scope is missing, the CLI surfaces a hint such as `lark-cli auth login --scope "im:feed_group_v1:write"`.
|
||||
|
||||
|
||||
@@ -1,97 +1,18 @@
|
||||
# im +feed-shortcut-create
|
||||
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) for authentication, global parameters, and security rules.
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first for authentication, global parameters, and safety rules.
|
||||
|
||||
This skill maps to shortcut: `lark-cli im +feed-shortcut-create`. Underlying API: `POST /open-apis/im/v2/feed_shortcuts`.
|
||||
Maps to `lark-cli im +feed-shortcut-create`. **Run `lark-cli im +feed-shortcut-create --help` for the authoritative flags (`--chat-id` / `--head` / `--tail` / `--as` / `--dry-run` / `--format` / `-q`), the 10-ID batch limit, and `--head`/`--tail` mutual exclusion.** This file covers only what `--help` cannot.
|
||||
|
||||
## What it does
|
||||
## Gotchas
|
||||
|
||||
Adds one or more chats to the **current user's** feed shortcuts — equivalent to right-clicking a chat in the Feishu client and pinning it to the feed sidebar.
|
||||
- **Only CHAT-type shortcuts are supported** (`--chat-id` must be an `oc_xxx` open_chat_id). If you only know a group name, resolve its `oc_xxx` first with `im +chat-search`.
|
||||
- **Re-adding an existing shortcut is idempotent** — the server treats it as a no-op rather than an error; `ok:true`, `failure_count=0`.
|
||||
- **Partial failure exits non-zero**: any non-empty `failed_shortcuts` sets `ok:false` on stdout and exits `1`. The full batch ledger (`total`, `success_count`, `failure_count`, `succeeded_shortcuts`, `failed_shortcuts`) remains machine-readable on stdout even on partial failure — check exit code AND `ok` AND `failure_count` in scripts.
|
||||
- **`failed_shortcuts[].reason_label`** is a CLI-added human-readable label alongside the numeric `reason`. The server only returns the number; the CLI enriches it. Reason codes: `1=no_permission`, `2=invalid_item`, `3=has_pending_delete`, `4=type_not_support`, `5=internal_error`.
|
||||
- **User identity only** (`--as user`). The CLI rejects `--as bot` locally; the server also rejects it.
|
||||
|
||||
- Only **CHAT-type** shortcuts are exposed by the OpenAPI gateway right now (`feed_card_id` must be an `oc_xxx` open_chat_id).
|
||||
- Batch up to **10 chat IDs per call**; pass more by issuing multiple calls.
|
||||
- Currently only supports **user identity** (`--as user`); bot identity is not allowed by the server.
|
||||
- If you only know a group name, resolve its `oc_xxx` first with `im +chat-search` or `im +chat-list`.
|
||||
## HELP-GAP — not yet in `--help`/schema; keep until CLI adds it
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Add a single chat as a feed shortcut (defaults to head/top insertion)
|
||||
lark-cli im +feed-shortcut-create --as user --chat-id oc_xxx
|
||||
|
||||
# Add multiple chats; comma-separated or repeated flag both work
|
||||
lark-cli im +feed-shortcut-create --as user --chat-id oc_a,oc_b,oc_c
|
||||
lark-cli im +feed-shortcut-create --as user --chat-id oc_a --chat-id oc_b
|
||||
|
||||
# Append at the bottom of the shortcut list instead of the top
|
||||
lark-cli im +feed-shortcut-create --as user --chat-id oc_xxx --tail
|
||||
|
||||
# Preview the request without sending
|
||||
lark-cli im +feed-shortcut-create --as user --chat-id oc_xxx --dry-run
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Default | Description |
|
||||
|------|------|------|
|
||||
| `--chat-id <oc_xxx>` | required | open_chat_id to add as a feed shortcut; repeatable or comma-separated; **max 10 per call** |
|
||||
| `--head` | true (implied) | Insert at the top of the shortcut list; mutually exclusive with `--tail` |
|
||||
| `--tail` | false | Append at the bottom of the shortcut list |
|
||||
| `--as user` | required | Server only accepts user_access_token for this API |
|
||||
|
||||
## Response
|
||||
|
||||
The response is a batch ledger. A full success exits `0` with `ok:true`. Any non-empty `failed_shortcuts` is a partial failure: the process exits non-zero (currently exit `1`), stdout carries `ok:false`, and the full ledger remains machine-readable:
|
||||
|
||||
| Field | Meaning |
|
||||
|------|------|
|
||||
| `total` | Number of requested shortcuts |
|
||||
| `success_count` | Number of requested shortcuts not reported in `failed_shortcuts` |
|
||||
| `failure_count` | Number of requested shortcuts reported as failed; `failed_shortcuts` preserves the raw server failure list |
|
||||
| `succeeded_shortcuts` | Requested shortcut entries that succeeded |
|
||||
| `failed_shortcuts` | Per-item failures returned by the server, enriched with `reason_label` |
|
||||
|
||||
The shortcut adds a `reason_label` field next to each numeric `reason`:
|
||||
|
||||
| `reason` | `reason_label` | Meaning |
|
||||
|---:|------|------|
|
||||
| 1 | `no_permission` | User has no permission on the feed card |
|
||||
| 2 | `invalid_item` | `feed_card_id` is invalid or type doesn't match |
|
||||
| 3 | `has_pending_delete` | The chat is being deleted |
|
||||
| 4 | `type_not_support` | Type is not whitelisted (only CHAT is open now) |
|
||||
| 5 | `internal_error` | Server internal error |
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": false,
|
||||
"data": {
|
||||
"total": 2,
|
||||
"success_count": 1,
|
||||
"failure_count": 1,
|
||||
"succeeded_shortcuts": [
|
||||
{ "feed_card_id": "oc_good", "type": 1 }
|
||||
],
|
||||
"failed_shortcuts": [
|
||||
{
|
||||
"shortcut": { "feed_card_id": "oc_bad", "type": 1 },
|
||||
"reason": 2,
|
||||
"reason_label": "invalid_item"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Permissions
|
||||
|
||||
- Required scope: `im:feed.shortcut:write`
|
||||
- Only available with user identity (`--as user`). The CLI will reject `--as bot` for this shortcut.
|
||||
|
||||
## Note
|
||||
|
||||
- The shortcut list is **per user**: the call adds shortcuts for the currently authenticated user only.
|
||||
- Adding the same chat twice is **idempotent at the user level** (re-adding an existing shortcut is a no-op rather than an error).
|
||||
- Scripts should check the process exit code, top-level `ok`, and ledger counts. Partial failures intentionally keep machine-readable success and failure details on stdout.
|
||||
- To inspect the current shortcut list, use [`+feed-shortcut-list`](lark-im-feed-shortcut-list.md). To remove a shortcut, use [`+feed-shortcut-remove`](lark-im-feed-shortcut-remove.md).
|
||||
- **Required scope**: `im:feed.shortcut:write`.
|
||||
- **Response ledger fields**: `total` · `success_count` · `failure_count` · `succeeded_shortcuts[]{feed_card_id, type}` · `failed_shortcuts[]{shortcut{feed_card_id, type}, reason, reason_label}`.
|
||||
|
||||
@@ -1,103 +1,20 @@
|
||||
# im +feed-shortcut-list
|
||||
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) for authentication, global parameters, and security rules.
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first for authentication, global parameters, and safety rules.
|
||||
|
||||
This skill maps to shortcut: `lark-cli im +feed-shortcut-list`. Underlying API: `GET /open-apis/im/v2/feed_shortcuts`.
|
||||
Maps to `lark-cli im +feed-shortcut-list`. **Run `lark-cli im +feed-shortcut-list --help` for the authoritative flags (`--page-token` / `--no-detail` / `--as` / `--dry-run` / `--format` / `-q`), and pagination restart behavior.** This file covers only what `--help` cannot.
|
||||
|
||||
## What it does
|
||||
## Gotchas
|
||||
|
||||
Lists **one page** of the **current user's** feed shortcuts.
|
||||
- **Only CHAT-type shortcuts are returned** — other shortcut types exist in the API IDL but are not whitelisted by the server today. Do not assume `type` will ever be non-1.
|
||||
- **No built-in auto-pagination.** Drive the loop yourself: read `data.page_token` and pass it back until `has_more=false`. The shortcut intentionally stays one-page-at-a-time so callers decide what to do when a token is rejected.
|
||||
- **Detail enrichment calls `im.chats.batch_query` in batches of 50**, requires `im:chat:read`, and attaches the full chat object under `detail`. Pass `--no-detail` to avoid the extra scope and network call when only `feed_card_id` values are needed.
|
||||
- **P2P chats return an empty `name`** — the Feishu client renders the partner's display name client-side. Use `p2p_target_id` to resolve the partner via `+contact-search` if a display title is needed.
|
||||
- **Enrichment failure is silent on stdout**: if `im:chat:read` is missing or the batch_query errors, the list still returns successfully; a warning goes to stderr and the data payload gains a `_notice` field (`"detail enrichment skipped: ..."`). Affected entries simply lack the `detail` field. Check `_notice` to distinguish "enrichment skipped" from "nothing to enrich."
|
||||
- **`detail` shape is dispatched per `type`** — switch on `type` before parsing `detail`; future shortcut types may attach a different object shape.
|
||||
|
||||
- Only **CHAT-type** shortcuts are exposed via OpenAPI today (others in the IDL are not yet whitelisted).
|
||||
- The shortcut is a **thin one-page wrapper** — there is no built-in auto-pagination. Callers drive their own loop when they actually need to paginate.
|
||||
- Server-side page size is controlled by the service; in normal use one page usually covers the list.
|
||||
- Pagination tokens are opaque. If a token is rejected because the shortcut list changed, restart by omitting `--page-token`.
|
||||
## HELP-GAP — not yet in `--help`/schema; keep until CLI adds it
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# First page (the only call most users ever need — --page-token omitted)
|
||||
lark-cli im +feed-shortcut-list --as user
|
||||
|
||||
# Continue from the previous response's page_token
|
||||
lark-cli im +feed-shortcut-list --as user --page-token <token-from-previous-response>
|
||||
|
||||
# Skip detail enrichment when only IDs are needed; avoids the extra im:chat:read lookup
|
||||
lark-cli im +feed-shortcut-list --as user --no-detail -q '.data.shortcuts[].feed_card_id'
|
||||
```
|
||||
|
||||
> If you need to walk every page, write the loop yourself: read `data.page_token` from each response and pass it back in until `has_more=false`. The shortcut intentionally does not auto-walk because page-token errors require the caller to decide whether to restart from the first page.
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|------|------|------|
|
||||
| `--page-token <token>` | no | Opaque pagination token from the previous response. **Omit it for the first page.** |
|
||||
| `--no-detail` | no (default `false`) | Skip fetching each entry's full info object. By default enrichment is enabled: CHAT-type entries call `im.chats.batch_query`, need `im:chat:read`, and attach the object under the `detail` field. Pass `--no-detail` to skip the extra call and scope. |
|
||||
| `--as user` | yes | Server only accepts user_access_token for this API |
|
||||
|
||||
## Response Structure
|
||||
|
||||
| Field | Type | Description |
|
||||
|------|------|------|
|
||||
| `shortcuts` | array | Feed shortcut entries; each has `feed_card_id` (oc_xxx) and `type` (1=CHAT). By default (without `--no-detail`), each entry also has a `detail` field with the full per-type info object. |
|
||||
| `has_more` | boolean | Whether more pages exist |
|
||||
| `page_token` | string | Opaque token to pass to the next call when continuing pagination |
|
||||
|
||||
Example (with detail enrichment, CHAT type):
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"shortcuts": [
|
||||
{
|
||||
"feed_card_id": "oc_092f0100fe59c35995727db1039777a8",
|
||||
"type": 1,
|
||||
"detail": {
|
||||
"chat_id": "oc_092f0100fe59c35995727db1039777a8",
|
||||
"chat_mode": "group",
|
||||
"name": "Engineering",
|
||||
"avatar": "https://...",
|
||||
"description": "",
|
||||
"external": false,
|
||||
"owner_id": "ou_xxx",
|
||||
"owner_id_type": "open_id",
|
||||
"tenant_key": "..."
|
||||
}
|
||||
},
|
||||
{
|
||||
"feed_card_id": "oc_c82061d126a06635aa3569587b134bb1",
|
||||
"type": 1,
|
||||
"detail": {
|
||||
"chat_id": "oc_c82061d126a06635aa3569587b134bb1",
|
||||
"chat_mode": "p2p",
|
||||
"name": "",
|
||||
"p2p_target_id": "ou_xxx",
|
||||
"p2p_target_type": "user",
|
||||
"avatar": "",
|
||||
"description": "",
|
||||
"external": false,
|
||||
"tenant_key": "..."
|
||||
}
|
||||
}
|
||||
],
|
||||
"has_more": false,
|
||||
"page_token": "v1.example-opaque-token"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Detail Enrichment
|
||||
|
||||
The `detail` payload is dispatched **per `type`**. Today only CHAT is wired in; future shortcut types can attach different object shapes. Callers should `switch` on `type` before parsing `detail`. For CHAT (`type=1`):
|
||||
|
||||
- **Source**: `POST /open-apis/im/v1/chats/batch_query` (50 ids per call, server limit).
|
||||
- **Payload**: the **full chat object** is passed through verbatim — `chat_id`, `chat_mode` (`group` / `p2p` / `topic`), `name`, `avatar`, `description`, `external`, `tenant_key`, plus type-specific fields (`owner_id*` for groups, `p2p_target_*` for p2p).
|
||||
- **P2P chats** return an empty `name` because the Feishu client renders the partner's display name there. The rest of the object (especially `p2p_target_id`) still flows through, so callers can resolve the partner via `+contact-search` if a display title is needed.
|
||||
- **Lookup failure** (missing scope, network error) → the list still returns successfully; a warning is printed to stderr, the data payload carries a `_notice` field (`"detail enrichment skipped: ..."`), and affected entries simply lack the `detail` field. Check `_notice` to tell "enrichment skipped" from "nothing to enrich".
|
||||
|
||||
## Permissions
|
||||
|
||||
- Required scope: `im:feed.shortcut:read`
|
||||
- Conditional scope (default detail path only): `im:chat:read`; pass `--no-detail` to avoid this extra scope and lookup.
|
||||
- Only available with user identity (`--as user`).
|
||||
- **Output fields**: `shortcuts[].feed_card_id` (oc_xxx) · `shortcuts[].type` (1=CHAT) · `shortcuts[].detail` (full chat object; absent when `--no-detail` or enrichment fails) · `has_more` · `page_token`.
|
||||
- **`detail` for CHAT**: `chat_id` · `chat_mode` (`group`/`p2p`/`topic`) · `name` · `avatar` · `description` · `external` · `tenant_key`; groups add `owner_id`/`owner_id_type`; p2p adds `p2p_target_id`/`p2p_target_type`.
|
||||
- **Required scopes**: `im:feed.shortcut:read` always; `im:chat:read` conditionally (default detail path only).
|
||||
|
||||
@@ -1,48 +1,16 @@
|
||||
# im +feed-shortcut-remove
|
||||
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) for authentication, global parameters, and security rules.
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first for authentication, global parameters, and safety rules.
|
||||
|
||||
This skill maps to shortcut: `lark-cli im +feed-shortcut-remove`. Underlying API: `POST /open-apis/im/v2/feed_shortcuts/remove`.
|
||||
Maps to `lark-cli im +feed-shortcut-remove`. **Run `lark-cli im +feed-shortcut-remove --help` for the authoritative flags (`--chat-id` / `--as` / `--dry-run` / `--format` / `-q`) and the 10-ID batch limit.** This file covers only what `--help` cannot.
|
||||
|
||||
## What it does
|
||||
## Gotchas
|
||||
|
||||
Removes one or more chats from the **current user's** feed shortcuts.
|
||||
- **Removing a chat that is not in the shortcut list is idempotent** — the server returns `ok:true`, `failure_count=0`, no `failed_shortcuts` entry. This means you cannot distinguish "removed" from "was not present."
|
||||
- **Partial failure exits non-zero**: any non-empty `failed_shortcuts` sets `ok:false` on stdout and exits `1`. The full batch ledger remains on stdout — check exit code AND `ok` in scripts. See [`+feed-shortcut-create`](lark-im-feed-shortcut-create.md) for the shared ledger field definitions and `reason_label` codes.
|
||||
- **User identity only** (`--as user`). The server does not accept bot identity.
|
||||
- **To inspect the list before removing**, run `+feed-shortcut-list --no-detail` (avoids the extra `im:chat:read` call when only `feed_card_id` values are needed).
|
||||
|
||||
- Only **CHAT-type** shortcuts are supported (`feed_card_id` must be an `oc_xxx`).
|
||||
- Batch up to **10 chat IDs per call**.
|
||||
- Currently only supports **user identity** (`--as user`).
|
||||
- Removing a chat that is not currently in the shortcut list is idempotent success: the call returns `ok:true`, `failure_count=0`, and no `failed_shortcuts` entry for that chat.
|
||||
## HELP-GAP — not yet in `--help`/schema; keep until CLI adds it
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Remove a single feed shortcut
|
||||
lark-cli im +feed-shortcut-remove --as user --chat-id oc_xxx
|
||||
|
||||
# Remove multiple feed shortcuts in one call
|
||||
lark-cli im +feed-shortcut-remove --as user --chat-id oc_a,oc_b
|
||||
lark-cli im +feed-shortcut-remove --as user --chat-id oc_a --chat-id oc_b
|
||||
|
||||
# Preview the request
|
||||
lark-cli im +feed-shortcut-remove --as user --chat-id oc_xxx --dry-run
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|------|------|------|
|
||||
| `--chat-id <oc_xxx>` | yes | open_chat_id to remove from feed shortcuts; repeatable or comma-separated; max 10 per call |
|
||||
| `--as user` | yes | Server only accepts user_access_token for this API |
|
||||
|
||||
## Response
|
||||
|
||||
The response uses the same batch ledger as [`+feed-shortcut-create`](lark-im-feed-shortcut-create.md#response): `total`, `success_count`, `failure_count`, `succeeded_shortcuts`, and `failed_shortcuts`. A non-empty `failed_shortcuts` is a partial failure: stdout carries `ok:false` with the full ledger and the process exits non-zero (currently exit `1`).
|
||||
|
||||
## Permissions
|
||||
|
||||
- Required scope: `im:feed.shortcut:write`
|
||||
- Only available with user identity (`--as user`).
|
||||
|
||||
## Note
|
||||
|
||||
- To see what is currently in the shortcut list before removing, run [`+feed-shortcut-list`](lark-im-feed-shortcut-list.md). Use `--no-detail` when you only need the `feed_card_id` values.
|
||||
- **Required scope**: `im:feed.shortcut:write`.
|
||||
|
||||
@@ -2,66 +2,13 @@
|
||||
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) for authentication, global parameters, and security rules.
|
||||
|
||||
This skill maps to shortcut: `lark-cli im +flag-cancel`. Underlying API: `POST /open-apis/im/v1/flags/cancel`.
|
||||
Maps to `lark-cli im +flag-cancel`. **Run `lark-cli im +flag-cancel --help` for the authoritative flags (`--message-id` / `--flag-type` / `--item-type`), defaults, and enums.** This file covers only what `--help` cannot.
|
||||
|
||||
## Double-Cancel Behavior (Important)
|
||||
## Gotchas
|
||||
|
||||
A message can have flags on both layers simultaneously:
|
||||
- Message layer: `(default, message)`
|
||||
- Feed layer: `(thread, feed)` or `(msg_thread, feed)` depending on chat type
|
||||
|
||||
**When no `--flag-type` is specified, the shortcut performs best-effort double-cancel**: the message-layer flag is always removed; the feed-layer flag is also removed when the chat type can be determined (otherwise a warning is printed on stderr and the feed layer is skipped). The server handles cancel requests for non-existent flags idempotently, so this is safe.
|
||||
|
||||
**Feed layer item_type is determined by chat_mode**:
|
||||
- Topic-style chat (`chat_mode=topic`) → `item_type=thread`
|
||||
- Regular chat (`chat_mode=group`) → `item_type=msg_thread`
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Double-cancel both layers (recommended default)
|
||||
lark-cli im +flag-cancel --as user --message-id om_xxx
|
||||
|
||||
# Only cancel message layer
|
||||
lark-cli im +flag-cancel --as user --message-id om_xxx --flag-type message
|
||||
|
||||
# Only cancel feed layer (need to specify item-type)
|
||||
lark-cli im +flag-cancel --as user --message-id om_xxx --item-type thread --flag-type feed
|
||||
|
||||
# Preview request
|
||||
lark-cli im +flag-cancel --as user --message-id om_xxx --dry-run
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|------|------|------|
|
||||
| `--message-id <om_xxx>` | Required | Message ID |
|
||||
| `--flag-type <name>` | No | `message` or `feed`; **when omitted, best-effort double-cancel of both layers** |
|
||||
| `--item-type <name>` | No | `default\|thread\|msg_thread`; required when `--flag-type feed` |
|
||||
| `--as user` | Required | Currently only supports user identity |
|
||||
|
||||
## Idempotency
|
||||
|
||||
The server doesn't return an error for cancel requests when the flag doesn't exist, so repeated `+cancel` calls are idempotent.
|
||||
|
||||
## Permissions
|
||||
|
||||
- Required scopes: `im:feed.flag:write`, `im:message.group_msg:get_as_user`, `im:message.p2p_msg:get_as_user`, `im:chat:read`
|
||||
- The message/chat read scopes are used by the default double-cancel path to auto-detect the feed-layer item type.
|
||||
|
||||
## Note
|
||||
|
||||
- **Do not call +flag-list for verification**: If the cancel API returns success, the flag is removed. Calling +flag-list to verify is expensive (requires full pagination) and unnecessary.
|
||||
|
||||
## Finding Message ID Efficiently
|
||||
|
||||
If you have message content but not the message ID:
|
||||
|
||||
1. **Use `+messages-search`** to find the message by content, then extract `message_id` from the result
|
||||
2. **Do NOT use `+flag-list`** to find the message — it requires full pagination and is very inefficient
|
||||
|
||||
```bash
|
||||
# Search by message content to find message_id
|
||||
lark-cli im +messages-search --as user --query "message content here" -q '.data.items[0].message_id'
|
||||
```
|
||||
- **Default is double-cancel (both layers)**: omitting `--flag-type` best-effort cancels both the message-layer flag `(default, message)` and the feed-layer flag `(thread, feed)` or `(msg_thread, feed)`. The server treats cancel of a non-existent flag idempotently — no error — so double-cancel is safe even when only one layer has a flag.
|
||||
- **Feed-layer `item_type` is determined by `chat_mode`**: `topic` chat → `item_type=thread`; regular `group` chat → `item_type=msg_thread`. The double-cancel path auto-detects this (requires `im:chat:read`). **Best-effort, not all-or-nothing**: the message layer is always removed; the feed layer is removed only when the chat type can be determined — otherwise a warning is printed on stderr and the feed layer is silently skipped (the command still exits 0). When calling single-layer feed cancel with `--flag-type feed`, you must supply `--item-type` explicitly.
|
||||
- **Idempotent**: repeated cancel calls on an already-unflagged message succeed silently.
|
||||
- **Do NOT call `+flag-list` for verification**: a success response means the flag was removed. Paginating `+flag-list` to confirm is expensive and unnecessary.
|
||||
- **Finding a message ID**: use `+messages-search --query "<keywords>"` to locate the message and extract `message_id`. Do NOT use `+flag-list` — it requires full pagination and won't reliably locate the message.
|
||||
- **User identity only** — `--as bot` is not supported.
|
||||
|
||||
@@ -2,66 +2,13 @@
|
||||
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) for authentication, global parameters, and security rules.
|
||||
|
||||
This skill maps to shortcut: `lark-cli im +flag-create`. Underlying API: `POST /open-apis/im/v1/flags`.
|
||||
Maps to `lark-cli im +flag-create`. **Run `lark-cli im +flag-create --help` for the authoritative flags (`--message-id` / `--flag-type` / `--item-type`), defaults, and enums.** This file covers only what `--help` cannot.
|
||||
|
||||
## Default Behavior
|
||||
## Gotchas
|
||||
|
||||
- **Message-layer flag** (default): `item_type=default, flag_type=message`
|
||||
- **Feed-layer flag**: Use `--flag-type feed` — automatically detects chat type to determine `item_type`:
|
||||
- Topic-style chat (`chat_mode=topic`) → `item_type=thread`
|
||||
- Regular chat (`chat_mode=group`) → `item_type=msg_thread`
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Flag a message (default: message-layer)
|
||||
lark-cli im +flag-create --as user --message-id om_xxx
|
||||
|
||||
# Create feed-layer flag (auto-detects chat type)
|
||||
lark-cli im +flag-create --as user --message-id om_xxx --flag-type feed
|
||||
|
||||
# Explicit item-type override (rarely needed)
|
||||
lark-cli im +flag-create --as user --message-id om_xxx --item-type thread --flag-type feed
|
||||
|
||||
# Preview request (dry-run, doesn't send)
|
||||
lark-cli im +flag-create --as user --message-id om_xxx --dry-run
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|------|------|------|
|
||||
| `--message-id <om_xxx>` | Required | Message ID |
|
||||
| `--flag-type <name>` | No | `message` (default) or `feed` |
|
||||
| `--item-type <name>` | No | Override auto-detection: `default\|thread\|msg_thread` (rarely needed) |
|
||||
| `--as user` | Required | Currently only supports user identity |
|
||||
|
||||
## Valid Combinations
|
||||
|
||||
The server only accepts these `(item_type, flag_type)` pairs:
|
||||
|
||||
- `(default, message)` — regular message flag
|
||||
- `(thread, feed)` — feed flag in topic-style chat
|
||||
- `(msg_thread, feed)` — feed flag in regular chat
|
||||
|
||||
## Permissions
|
||||
|
||||
- Required scopes: `im:feed.flag:write`, `im:message.group_msg:get_as_user`, `im:message.p2p_msg:get_as_user`, `im:chat:read`
|
||||
- The message/chat read scopes are used when `--flag-type feed` is used without explicit `--item-type` so the CLI can auto-detect chat type.
|
||||
- If missing, CLI will prompt with `lark-cli auth login --scope "..."`
|
||||
|
||||
## Note
|
||||
|
||||
- **Do not call +flag-list for verification**: If the create API returns success, the flag is created. Calling +flag-list to verify is expensive (requires full pagination) and unnecessary.
|
||||
|
||||
## Finding Message ID Efficiently
|
||||
|
||||
If you have message content but not the message ID:
|
||||
|
||||
1. **Use `+messages-search`** to find the message by content, then extract `message_id` from the result
|
||||
2. **Do NOT use `+flag-list`** to find the message — it requires full pagination and is very inefficient
|
||||
|
||||
```bash
|
||||
# Search by message content to find message_id
|
||||
lark-cli im +messages-search --as user --query "message content here" -q '.data.items[0].message_id'
|
||||
```
|
||||
- **Message-layer vs feed-layer are distinct**: default (`--flag-type message`) creates a `(default, message)` flag visible in message history. `--flag-type feed` creates a feed-layer flag visible in the Feed tab. A message can carry both simultaneously — they are independent.
|
||||
- **Feed-layer auto-detection requires extra scopes**: `--flag-type feed` without `--item-type` calls the chat API to determine `chat_mode`; this needs `im:message.group_msg:get_as_user` / `im:message.p2p_msg:get_as_user` / `im:chat:read`. If you already know the chat type, pass `--item-type` explicitly to skip the lookup.
|
||||
- **Only three valid `(item_type, flag_type)` pairs**: `(default, message)`, `(thread, feed)`, `(msg_thread, feed)`. Any other combination is rejected by the server.
|
||||
- **Do NOT call `+flag-list` for verification**: a success response means the flag was created. Full pagination of `+flag-list` to confirm is expensive and unnecessary.
|
||||
- **Finding a message ID**: use `+messages-search --query "<keywords>"` to locate the message and extract `message_id`. Do NOT use `+flag-list` — it requires full pagination and won't reliably locate the message.
|
||||
- **User identity only** — `--as bot` is not supported.
|
||||
|
||||
@@ -2,99 +2,16 @@
|
||||
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) for authentication, global parameters, and security rules.
|
||||
|
||||
This skill maps to shortcut: `lark-cli im +flag-list`. Underlying API: `GET /open-apis/im/v1/flags`.
|
||||
Maps to `lark-cli im +flag-list`. **Run `lark-cli im +flag-list --help` for the authoritative flags (`--page-size` / `--page-token` / `--page-all` / `--page-limit` / `--enrich-feed-thread`), limits, and enums.** This file covers only what `--help` cannot.
|
||||
|
||||
## Sorting Rules (Important)
|
||||
## Gotchas
|
||||
|
||||
The API returns data sorted by `update_time` in **ascending order**, meaning **oldest first, newest last**. When `has_more=true`, you cannot simply take the first page's items as the latest flags — you must paginate through all pages and take the last item on the last page as the newest.
|
||||
- **Sort order is ascending (oldest first)**: the API returns `flag_items` sorted by `update_time` ascending. When `has_more=true`, the first page's items are the oldest, not the newest. To get the latest flag, paginate all pages (`--page-all`) and take the last item: `-q '.data.flag_items[-1]'`.
|
||||
- **`delete_flag_items` are NOT enriched**: message content is fetched only for active flags (`flag_items`). To get message content for a canceled flag, query separately via `+messages-mget --message-ids <item_id>`.
|
||||
- **`(thread, feed)` / `(msg_thread, feed)` entries are enriched automatically**: the shortcut calls `messages/mget` for feed-type thread entries and writes the result into each entry's `message` field. Disable with `--enrich-feed-thread=false` to avoid the extra scope requirement.
|
||||
- **User identity only** — `--as bot` is not supported.
|
||||
|
||||
Recommended: use `--page-all` for auto-pagination to get the complete list, then use `-q '.data.flag_items[-1]'` to get the latest item.
|
||||
## HELP-GAP — not yet in `--help`/schema; keep until CLI adds it
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Fetch first page (default page-size=50)
|
||||
lark-cli im +flag-list --as user
|
||||
|
||||
# Manual pagination with custom page size
|
||||
lark-cli im +flag-list --as user --page-size 30 --page-token <page_token>
|
||||
|
||||
# Auto-paginate to get all flags (recommended)
|
||||
lark-cli im +flag-list --as user --page-all
|
||||
|
||||
# Auto-paginate + get the latest flag
|
||||
lark-cli im +flag-list --as user --page-all -q '.data.flag_items[-1]'
|
||||
|
||||
# Auto-paginate + get only item_id list
|
||||
lark-cli im +flag-list --as user --page-all -q '.data.flag_items[].item_id'
|
||||
|
||||
# Disable auto-enrichment of message content (enabled by default)
|
||||
lark-cli im +flag-list --as user --page-all --enrich-feed-thread=false
|
||||
|
||||
# Limit max pages (default 20, max 1000)
|
||||
lark-cli im +flag-list --as user --page-all --page-limit 10
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Default | Description |
|
||||
|------|------|------|
|
||||
| `--page-size <n>` | 50 | Range 1-50 (server max is 50) |
|
||||
| `--page-token <token>` | empty | Pagination token from previous page; empty string must still be provided |
|
||||
| `--page-all` | false | Auto-paginate to fetch all pages and merge results |
|
||||
| `--page-limit <n>` | 20 | Max pages in `--page-all` mode (max 1000) |
|
||||
| `--enrich-feed-thread` | true | Auto-enrich feed-layer thread entries with message content (calls `im.messages.mget`) |
|
||||
| `--as user` | Required | Currently only supports user identity |
|
||||
|
||||
## Response Structure
|
||||
|
||||
The response has `data` as the main body, with fields described below:
|
||||
|
||||
| Field | Type | Description |
|
||||
|------|------|------|
|
||||
| `flag_items` | array | List of currently existing (not canceled) flags, sorted by `update_time` ascending |
|
||||
| `delete_flag_items` | array | List of previously canceled flags, sorted by `update_time` ascending |
|
||||
| `messages` | array | Message content inlined by the server for `(default, message)` type flags |
|
||||
| `has_more` | boolean | Whether there's a next page |
|
||||
| `page_token` | string | Pagination token for the next page |
|
||||
|
||||
Note: `(thread, feed)` / `(msg_thread, feed)` entries are automatically enriched via `mget` by the shortcut, and written to the corresponding entry's `message` field.
|
||||
|
||||
## Limitations
|
||||
|
||||
- **delete_flag_items are not enriched**: Message content is only fetched for active flags (`flag_items`), not canceled flags (`delete_flag_items`). If you need message content for a canceled flag, query the message separately using `+messages-mget --message-ids <item_id>`.
|
||||
|
||||
## Response Example (Sanitized)
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"delete_flag_items": [
|
||||
{
|
||||
"create_time": "xxx",
|
||||
"flag_type": "xxx",
|
||||
"item_id": "xxx",
|
||||
"item_type": "xxx",
|
||||
"update_time": "xxx"
|
||||
}
|
||||
],
|
||||
"flag_items": [
|
||||
{
|
||||
"create_time": "xxx",
|
||||
"flag_type": "xxx",
|
||||
"item_id": "xxx",
|
||||
"item_type": "xxx",
|
||||
"update_time": "xxx"
|
||||
}
|
||||
],
|
||||
"has_more": false,
|
||||
"messages": [],
|
||||
"page_token": "xxx"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Permissions
|
||||
|
||||
- Base scope: `im:feed.flag:read`
|
||||
- Additional scopes only when `--enrich-feed-thread=true` needs to fetch missing message content: `im:message.group_msg:get_as_user`, `im:message.p2p_msg:get_as_user`
|
||||
- **Output fields**: `flag_items` (active flags, ascending `update_time`) · `delete_flag_items` (canceled flags, ascending `update_time`, unenriched) · `messages` (server-inlined content for `(default, message)` flags) · `has_more` · `page_token`.
|
||||
- **Enrichment write target**: for `(thread, feed)` / `(msg_thread, feed)` entries, enriched message content is written to the entry's `message` field (not a top-level `messages` array).
|
||||
|
||||
@@ -1,54 +1,32 @@
|
||||
# im default message enrichment (reactions / update_time)
|
||||
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first to understand authentication, global parameters, and safety rules.
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first for authentication, global parameters, and safety rules.
|
||||
|
||||
This is the single source of truth for the automatic message-enrichment contract shared by the four message-pulling shortcuts — [`+messages-mget`](lark-im-messages-mget.md), [`+chat-messages-list`](lark-im-chat-messages-list.md), [`+messages-search`](lark-im-messages-search.md), [`+threads-messages-list`](lark-im-threads-messages-list.md). They automatically attach `reactions` and `update_time` to each returned message, so callers do **not** need to invoke the raw [`im.reactions.batch_query`](lark-im-reactions.md) API separately.
|
||||
Cross-cutting behavior shared by the message-pulling shortcuts. `--help` documents only the `--no-reactions` opt-out; the field semantics, caps, and failure contract below are not in any `--help` or schema.
|
||||
|
||||
- **`reactions`** — populated from `im.reactions.batch_query` as `{counts, details}`. The field is only attached when the server actually returns data; messages with no reactions omit it. Replies inside `thread_replies` are enriched alongside their parent (collected into the same id set), so outer and inner messages follow identical semantics. The id set is split into batches of <= 20 (server-side cap) and the batches are dispatched with bounded concurrency (up to 4 in flight), so high-N pulls — e.g. page 50 + ~500 expanded thread replies = 550 ids → ⌈550 / 20⌉ = **28 batches** — finish in a few round-trips instead of serializing into tens of seconds.
|
||||
- **`update_time`** — emitted only when `updated == true` (message was actually edited). The server echoes `update_time == create_time` for unedited messages too, but the CLI gates that output away so consumers don't misread every message as "edited".
|
||||
- **Opt-out** — each shortcut accepts `--no-reactions` to skip the extra round-trip when the caller only needs message bodies.
|
||||
**Relied on by:** [`+messages-mget`](lark-im-messages-mget.md) · [`+chat-messages-list`](lark-im-chat-messages-list.md) · [`+messages-search`](lark-im-messages-search.md) · [`+threads-messages-list`](lark-im-threads-messages-list.md). All four attach `reactions` + `update_time` so callers do **not** need the raw [`im.reactions.batch_query`](lark-im-reactions.md) API. **Only `+messages-mget` and `+chat-messages-list` additionally auto-expand `thread_replies`**; `+messages-search` and `+threads-messages-list` do reactions/`update_time` only.
|
||||
|
||||
## Thread replies expansion
|
||||
## Gotchas
|
||||
|
||||
`+messages-mget` and `+chat-messages-list` also auto-expand thread replies: any returned message that carries a `thread_id` triggers a fetch of that thread's replies, which are attached as a `thread_replies` array on the host. Fetches across distinct threads run with bounded concurrency (up to 4 in flight). Two caps gate the result:
|
||||
|
||||
- **`perThread` (default 50)** — max replies fetched for any single thread.
|
||||
- **`totalLimit` (default 500)** — max cumulative replies across all threads on the page.
|
||||
|
||||
`totalLimit` is enforced **post-fetch against actual returned reply counts**, not against the planned per-thread ceiling — so a chat with many short threads (e.g. 12 threads × 3 actual replies = 36 ≪ 500) attaches every thread, even though the planned sum (12 × 50 = 600) would exceed the budget. When a thread's actual replies push the running total across `totalLimit`, that thread is truncated to fit the remaining budget and its host is flagged with `thread_has_more: true` so consumers know the server has more.
|
||||
|
||||
On per-thread fetch failure the host gets `thread_replies_error: true` (mirrors the reactions data contract); budget-truncated or budget-skipped threads do NOT carry that flag.
|
||||
- **Missing field ≠ fetch failure.** `reactions` is attached only when the server returns data — a message with no reactions *omits* the field (not `{}`, not `null`). To decide "has the user already reacted?", branch on **presence of `reactions` + its `counts` contents**, never on `null`. Fetch failure is signaled separately on stderr (`warning: reactions_batch_query_failed` for a whole batch; `warning: reactions_partial_failed: N message(s) failed` for some IDs).
|
||||
- **`update_time` is gated, not raw.** It's emitted only when `updated == true`. The server echoes `update_time == create_time` for unedited messages, but the CLI suppresses that so you don't misread every message as edited.
|
||||
- **Requires `im:message.reactions:read`.** Declared in each shortcut's scopes, so the pre-flight surfaces `missing_scope` before sending. Bots registered before this scope existed need an incremental authorization in the developer console (a user re-login picking up the scope is enough for user identity).
|
||||
- **High-N pulls are batched, not serialized.** Reaction lookups split into batches of ≤20 ids (server cap) dispatched with bounded concurrency (≤4 in flight), so e.g. 550 ids → 28 batches finish in a few round-trips, not tens of seconds. Don't add your own throttling/looping on top.
|
||||
|
||||
## Resource auto-download (`--download-resources`, opt-in)
|
||||
|
||||
`+chat-messages-list`, `+messages-mget`, and `+threads-messages-list` accept an **opt-in** `--download-resources` flag. It is **off by default** — when omitted, output and the request count are identical to before (no `resources` block, no extra round-trips).
|
||||
`+chat-messages-list` / `+messages-mget` / `+threads-messages-list` accept `--download-resources` (**off by default** — omitted = no `resources` block and zero extra requests). When set, eligible resources (`image`/`file`/`audio`/`video`/`media` + post-embedded; **stickers excluded** — Feishu can't fetch them) download into `./lark-im-resources/`, and each message gains a `resources` array of `{message_id, key, type, local_path, size_bytes}`.
|
||||
|
||||
When enabled:
|
||||
- **`merge_forward` trap**: for a resource inside a forwarded message, `message_id` is the **top-level container** id, not the sub-item's — the download endpoint rejects sub-item ids with `234003 File not in msg`. Thread replies each get their own block.
|
||||
- **Fail-silent isolation**: one resource that fails to download is flagged `"error": true` + a single stderr `warning: resource_download_failed: …`; the message and other resources are unaffected (exit 0).
|
||||
- Deduped by `(message_id, file_key)`, bounded concurrency (≤3), output confined to `./lark-im-resources/` (path-separator / `..` / absolute `file_key` rejected).
|
||||
- **No extra scope**: uses `im:message:readonly`, already declared by the listing commands; works under user and bot identity. For one-off fetches use [`+messages-resources-download`](lark-im-messages-resources-download.md).
|
||||
|
||||
- Each message that carries downloadable resources gets a `resources` array. Eligible types: `image`, `file`, `audio`, `video`, `media`, and post-embedded `img` / `media`. **Stickers are excluded** (Feishu does not support fetching sticker resources).
|
||||
- Each ref is `{message_id, key, type, local_path, size_bytes}` — `type` is `image` or `file`; `message_id` is the id used to fetch the resource. For a standalone message that is its own id; for a resource inside a **merge_forward** it is the **top-level container** `message_id`, not the sub-item's own id (the download endpoint rejects sub-item ids with `234003 File not in msg` and can only fetch a forwarded resource through the container). Thread replies each get their own block.
|
||||
- Files download into `./lark-im-resources/` under the current working directory. Each distinct `(message_id, file_key)` is downloaded once (deduped) with bounded concurrency (up to 3 in flight).
|
||||
- **Fail-silent isolation**: a single resource that fails to download is flagged `"error": true` with one stderr line (`warning: resource_download_failed: <message_id>/<key>: ...`); the main message and the other resources are unaffected.
|
||||
- Output paths are confined to `./lark-im-resources/` by the same guards as [`+messages-resources-download`](lark-im-messages-resources-download.md) (abnormal `file_key` with path separators / `..` / absolute paths is rejected).
|
||||
- **Scope**: the download uses `GET /open-apis/im/v1/messages/:message_id/resources/:file_key`, which requires `im:message:readonly` — already declared in each listing command's `Scopes`, so `--download-resources` needs **no extra scope** beyond what's required to read the messages (user identity also needs `im:message.group_msg:get_as_user` / `im:message.p2p_msg:get_as_user`; bot identity needs `im:message.group_msg` / `im:message.p2p_msg:readonly`, all already declared). Works under both user and bot identity. If a bot was registered before `im:message:readonly` was granted, a single resource will fail-silently (`error: true` + stderr warning) rather than aborting the pull.
|
||||
## Thread-replies expansion caps (mget / chat-messages-list only)
|
||||
|
||||
Use `--download-resources` when you want the binaries on disk in one pass; otherwise the message content keeps the inline resource markers (e.g. `[Image: img_xxx]`, `<file .../>`, `<audio key="..." duration="Xs"/>`) and you can fetch individual resources later with [`+messages-resources-download`](lark-im-messages-resources-download.md).
|
||||
Any returned message carrying a `thread_id` triggers a fetch of that thread's replies, attached as a `thread_replies` array on the host (distinct threads fetched with ≤4 concurrent). Two caps gate it:
|
||||
|
||||
## Scope requirement
|
||||
- **`perThread` (default 50)** — max replies per single thread.
|
||||
- **`totalLimit` (default 500)** — max cumulative replies across all threads on the page.
|
||||
|
||||
The default enrichment requires `im:message.reactions:read`, already declared in each shortcut's `UserScopes` / `BotScopes` (or `Scopes` for the user-only search command), so the framework's pre-flight check surfaces a `missing_scope` error before the request is sent. Bots that were registered before this scope was added need an incremental authorization in the Feishu developer console; users can run:
|
||||
|
||||
```bash
|
||||
lark-cli auth login --scope "im:message.reactions:read"
|
||||
```
|
||||
|
||||
## Data contract — missing field ≠ fetch failure
|
||||
|
||||
| Situation | Output |
|
||||
|---|---|
|
||||
| Message has no reactions | `reactions` field is omitted (not `{}`, not an empty list) |
|
||||
| Message was never edited | `update_time` field is omitted |
|
||||
| Whole batch failed | Messages in that batch carry no `reactions`; one line on stderr: `warning: reactions_batch_query_failed: ...` |
|
||||
| Some message IDs failed | Failed IDs go to stderr: `warning: reactions_partial_failed: N message(s) failed (...)` |
|
||||
|
||||
When deciding "has the user already reacted?", branch on the **presence of the `reactions` field plus its `counts` contents**, not on whether a value is `null` — the field's absence means "no data attached" (which usually means "no reactions exist"), not "fetch failed".
|
||||
**`totalLimit` is enforced post-fetch against *actual* returned counts, not the planned per-thread ceiling.** So 12 threads × 3 real replies = 36 all attach, even though the planned sum (12 × 50 = 600) would blow the budget. When a thread's real replies push the running total past `totalLimit`, that thread is truncated to fit and its host is flagged **`thread_has_more: true`**. A per-thread fetch *failure* instead flags the host **`thread_replies_error: true`** — budget-truncated/skipped threads do NOT carry that flag (so the two cases are distinguishable).
|
||||
|
||||
@@ -1,99 +1,18 @@
|
||||
# im +messages-mget
|
||||
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first to understand authentication, global parameters, and safety rules.
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first for authentication, global parameters, and safety rules.
|
||||
|
||||
Fetch message details in batch. Given a list of message IDs, this returns the full content for multiple messages in one call and automatically resolves sender names.
|
||||
Maps to `lark-cli im +messages-mget`. **Run `lark-cli im +messages-mget --help` for the authoritative flags (`--message-ids` / `--no-reactions` / `--download-resources` / `--as` / `--format`), the `om_xxx` ID format, and the max-50 batch cap.** This file covers only what `--help` cannot.
|
||||
|
||||
By default the response also carries a `reactions` block (counts + details from `im.reactions.batch_query`) on every message that has reactions, and `update_time` on messages that were actually edited. Replies inside `thread_replies` participate in the same batched enrichment. Pass `--no-reactions` to skip the extra round-trip. Pass `--download-resources` to additionally download message resources (image/file/audio/video/media + post-embedded, excluding stickers) into `./lark-im-resources/` and attach a `resources` block — off by default, no extra requests when omitted. See [message enrichment](lark-im-message-enrichment.md) for the full contract.
|
||||
Supports both `--as user` (default) and `--as bot`. Auto-resolves sender names and auto-expands `thread_replies`, both with reactions / `update_time` per [message enrichment](lark-im-message-enrichment.md).
|
||||
|
||||
> **Supports both `--as user` (default) and `--as bot`.**
|
||||
## Gotchas
|
||||
|
||||
This skill maps to the shortcut: `lark-cli im +messages-mget` (internally calls `GET /open-apis/im/v1/messages/mget`).
|
||||
- **Batch, don't loop**: one call with comma-separated IDs is one request (up to 50); calling per-ID wastes round-trips.
|
||||
- **Image content is a placeholder, not bytes**: image messages render as `[Image: img_xxx]`. Use `--download-resources` to write them to `./lark-im-resources/`, or [`im +messages-resources-download`](lark-im-messages-resources-download.md) for a single resource.
|
||||
- **Use `--format json` for full bodies** — non-JSON formats truncate `content`.
|
||||
- Permission denied → needs `im:message:readonly` + `contact:user.base:readonly` (sender-name resolution requires the contact scope, which the message scope alone won't satisfy).
|
||||
|
||||
## Commands
|
||||
## HELP-GAP — not yet in `--help`/schema; keep until CLI adds it
|
||||
|
||||
```bash
|
||||
# Fetch a single message
|
||||
lark-cli im +messages-mget --message-ids om_xxx
|
||||
|
||||
# Fetch multiple messages in batch (comma-separated)
|
||||
lark-cli im +messages-mget --message-ids "om_aaa,om_bbb,om_ccc"
|
||||
|
||||
# JSON output
|
||||
lark-cli im +messages-mget --message-ids "om_aaa,om_bbb" --format json
|
||||
|
||||
# Preview the request without executing it
|
||||
lark-cli im +messages-mget --message-ids "om_aaa" --dry-run
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Required | Limits | Description |
|
||||
|------|------|------|------|
|
||||
| `--message-ids <ids>` | Yes | At least one, max 50, `om_xxx` format, comma-separated | Message ID list |
|
||||
| `--no-reactions` | No | — | Skip auto-fetching the `reactions` block |
|
||||
| `--download-resources` | No | — | Download message resources (image/file/audio/video/media + post-embedded, excluding stickers) into `./lark-im-resources/` and attach a `resources` block. Off by default |
|
||||
|
||||
## Output Fields
|
||||
|
||||
| Field | Description |
|
||||
|------|------|
|
||||
| `messages` | Message array |
|
||||
| `total` | Number of messages returned |
|
||||
|
||||
Each message contains:
|
||||
|
||||
| Field | Description |
|
||||
|------|------|
|
||||
| `message_id` | Message ID |
|
||||
| `msg_type` | Message type (`text`, `image`, `file`, etc.) |
|
||||
| `create_time` | Creation time |
|
||||
| `sender` | Sender information (includes `name`) |
|
||||
| `content` | Message content |
|
||||
|
||||
## Usage Scenarios
|
||||
|
||||
### Scenario 1: Fetch the full content of a specific message
|
||||
|
||||
```bash
|
||||
lark-cli im +messages-mget --message-ids om_xxx --format json
|
||||
```
|
||||
|
||||
### Scenario 2: Fetch multiple messages in one batch
|
||||
|
||||
```bash
|
||||
lark-cli im +messages-mget --message-ids "om_aaa,om_bbb,om_ccc"
|
||||
```
|
||||
|
||||
### Scenario 3: Use together with the message list command
|
||||
|
||||
First get message IDs via `+chat-messages-list`, then fetch full content via `+messages-mget`:
|
||||
|
||||
```bash
|
||||
# Get the message list
|
||||
lark-cli im +chat-messages-list --chat-id oc_xxx --format json
|
||||
|
||||
# Fetch specific message details
|
||||
lark-cli im +messages-mget --message-ids "om_aaa,om_bbb"
|
||||
```
|
||||
|
||||
## Common Errors and Troubleshooting
|
||||
|
||||
| Symptom | Root Cause | Solution |
|
||||
|---------|---------|---------|
|
||||
| `--message-ids requires at least one message ID` | No message ID was provided | Provide at least one message ID |
|
||||
| `invalid message ID: must start with om_` | Invalid message ID format | Message IDs must start with `om_` |
|
||||
| Permission denied | Message read permission is missing | Ensure the app has `im:message:readonly` and `contact:user.base:readonly` enabled |
|
||||
| Empty result | Message IDs do not exist or are not accessible | Verify the IDs and access permissions |
|
||||
|
||||
## AI Usage Guidance
|
||||
|
||||
1. **Use JSON for full content:** table output truncates content. Use `--format json` when the full body matters.
|
||||
2. **Sender names are already enriched:** the command resolves sender names automatically, so no extra lookup is required.
|
||||
3. **Images are rendered as placeholders:** image messages appear as placeholders such as `[Image: img_xxx]`. Use `+messages-resources-download` when you need the binary resource.
|
||||
4. **Batching is more efficient:** fetching multiple IDs in one request is better than calling the API repeatedly.
|
||||
|
||||
## References
|
||||
|
||||
- [lark-im](../SKILL.md) - all IM commands
|
||||
- [lark-shared](../../lark-shared/SKILL.md) - authentication and global parameters
|
||||
- **Return shape**: `{messages:[...], total:N}`; each message has `message_id` / `msg_type` / `create_time` / `sender` (incl. resolved `name`) / `content`. Enrichment-added fields are documented in [message enrichment](lark-im-message-enrichment.md).
|
||||
|
||||
@@ -1,263 +1,14 @@
|
||||
# im +messages-reply
|
||||
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first to understand authentication, global parameters, and safety rules.
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first for authentication, global parameters, and safety rules.
|
||||
|
||||
Reply to a specific message. Supports both user identity (`--as user`) and bot identity (`--as bot`). Also supports thread replies.
|
||||
Maps to `lark-cli im +messages-reply`. **Run `lark-cli im +messages-reply --help` for the authoritative flags (`--message-id` / `--text` / `--markdown` / `--content` / `--msg-type` / `--image` / `--file` / `--video` / `--video-cover` / `--audio` / `--reply-in-thread` / `--idempotency-key` / `--as`), content/media flags, mutual-exclusion rules, msg-type inference, `--video`/`--video-cover` pairing, and the cwd-relative media path rules.** This file covers only what `--help` cannot.
|
||||
|
||||
This skill maps to the shortcut: `lark-cli im +messages-reply` (internally calls `POST /open-apis/im/v1/messages/:message_id/reply`).
|
||||
Safety: replies are visible to others — confirm the target message, content, and identity before sending (see lark-shared risk policy). `--as bot` requires the app to already be in the target chat.
|
||||
|
||||
## Safety Constraints
|
||||
**Shared with [`im +messages-send`](lark-im-messages-send.md)**: picking the content flag (`--markdown` vs `--text` vs `--content`), `--markdown` boundary rules, the `images.create` pre-upload step for local images in Markdown, `$'...'` for exact formatting, `@mention` syntax, the `--content` JSON shape per `msg_type`, the return value, and the `--idempotency-key` 1-hour window all behave identically — see that reference, do not re-derive here.
|
||||
|
||||
Replies sent by this tool are visible to other people. Before calling it, you **must** confirm with the user:
|
||||
## Gotchas
|
||||
|
||||
1. Which message to reply to
|
||||
2. The reply content
|
||||
3. Which identity to use (user or bot)
|
||||
|
||||
**Do not** send a reply without explicit user approval.
|
||||
|
||||
When using `--as bot`, the reply is sent in the app's name, so make sure the app has already been added to the target chat.
|
||||
|
||||
When using `--as user`, the reply is sent as the authorized end user and requires the `im:message.send_as_user` and `im:message` scopes.
|
||||
|
||||
## Choose The Right Content Flag
|
||||
|
||||
### Default Selection Rule For Agents
|
||||
|
||||
- Prefer `--markdown` for headings, lists, links, summaries, investigation notes, or Markdown-looking content.
|
||||
- Use `--text` for exact plain text: logs, code, indentation-sensitive text, or literal Markdown.
|
||||
- Use `--content` for exact `post` JSON, titles, multiple locales, cards, or unsupported structures.
|
||||
|
||||
| Need | Recommended flag | Why |
|
||||
|------|------|------|
|
||||
| Reply with headings, lists, links, summaries, or investigation notes | `--markdown` | Best default for lightweight formatting; converted to Feishu `post` JSON |
|
||||
| Reply with plain text exactly as written | `--text` | Preserves literal text; no Markdown conversion |
|
||||
| Precisely control the reply payload | `--content` | You provide the exact JSON |
|
||||
| Reply with media | `--image` / `--file` / `--video` / `--audio` | Shortcut uploads URLs, or cwd-relative local files automatically |
|
||||
|
||||
### `--text` vs `--markdown`
|
||||
|
||||
- Use `--markdown` for lightweight formatted replies.
|
||||
- Use `--text` for exact plain text, especially logs, code, indentation, or literal Markdown characters.
|
||||
- Use `--content` when you need exact `post` JSON, a card, a title, multiple locales, or any structure that `--markdown` cannot express reliably.
|
||||
|
||||
## What `--markdown` Really Does
|
||||
|
||||
`--markdown` accepts Markdown-like input and converts it to the Feishu `post` payload required by the reply API.
|
||||
|
||||
The shortcut:
|
||||
|
||||
1. Forces `msg_type=post`
|
||||
2. Resolves remote Markdown images like ``
|
||||
3. Normalizes the Markdown for Feishu post rendering
|
||||
4. Wraps the final content as:
|
||||
|
||||
```json
|
||||
{"zh_cn":{"content":[[{"tag":"md","text":"..."}]]}}
|
||||
```
|
||||
|
||||
This makes `--markdown` the simplest path for lightweight formatted replies.
|
||||
|
||||
### Markdown Boundaries
|
||||
|
||||
- It does **not** promise full CommonMark / GitHub Flavored Markdown support.
|
||||
- It always becomes a `post` payload with a single `zh_cn` locale.
|
||||
- It does **not** let you set a `post` title.
|
||||
- Headings are rewritten:
|
||||
- `# Title` becomes `#### Title`
|
||||
- `##` to `######` are normalized to `#####` when the content contains H1-H3
|
||||
- Consecutive headings are separated with blank lines after heading normalization.
|
||||
- Block spacing and line breaks may be normalized during conversion.
|
||||
- Code blocks are preserved as code blocks.
|
||||
- Excess blank lines are compressed.
|
||||
- Already-uploaded `img_xxx` image keys are the most reliable Markdown image input.
|
||||
- Local paths (e.g. ``) are **not** supported directly in `--markdown` and will not be auto-uploaded.
|
||||
- Remote URLs (`https://...`) will be auto-downloaded and uploaded at runtime; if the download or upload fails, the image is removed with a warning.
|
||||
|
||||
If you need a title, multiple locales, cards, unsupported rich structures, or byte-for-byte post JSON control, use `--msg-type post --content ...`.
|
||||
|
||||
### Image Constraint for `--markdown`
|
||||
|
||||
When using `--markdown` with images, prefer pre-uploading via `images.create` and referencing `` for predictable results. Remote URLs may work but are not guaranteed.
|
||||
|
||||
**Steps:**
|
||||
|
||||
```bash
|
||||
# 1. Upload image to get image_key
|
||||
lark-cli im images create --data '{"image_type":"message"}' --file ./diagram.png
|
||||
# Returns: {"image_key":"img_v3_xxxx"}
|
||||
|
||||
# 2. Use image_key in --markdown reply
|
||||
lark-cli im +messages-reply --message-id om_xxx --markdown $'## Result\n\n\n\nSee above for details.'
|
||||
```
|
||||
|
||||
## Preserving Formatting
|
||||
|
||||
If the reply contains multiple lines, code blocks, indentation, tabs, or a lot of escaping, prefer `$'...'` for either `--markdown` or `--text`.
|
||||
|
||||
### When formatting must be preserved
|
||||
|
||||
Use `--text` plus `$'...'`:
|
||||
|
||||
```bash
|
||||
lark-cli im +messages-reply --message-id om_xxx --text $'Received\nI will check this today.\nOwner: alice'
|
||||
```
|
||||
|
||||
```bash
|
||||
lark-cli im +messages-reply --message-id om_xxx --text $'```sql\nselect * from jobs;\n```'
|
||||
```
|
||||
|
||||
This keeps the reply as plain text instead of converting it to a `post`.
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Reply with a formatted update
|
||||
lark-cli im +messages-reply --message-id om_xxx --markdown $'## Reply\n\n- item 1\n- item 2'
|
||||
|
||||
# Reply with a plain one-line message
|
||||
lark-cli im +messages-reply --message-id om_xxx --text "Received"
|
||||
|
||||
# Equivalent manual JSON
|
||||
lark-cli im +messages-reply --message-id om_xxx --content '{"text":"Received"}'
|
||||
|
||||
# Reply as a bot
|
||||
lark-cli im +messages-reply --message-id om_xxx --text "bot reply" --as bot
|
||||
|
||||
# Reply with preserved multi-line text
|
||||
lark-cli im +messages-reply --message-id om_xxx --text $'Line 1\nLine 2\n indented line'
|
||||
|
||||
# Reply inside the thread (message appears in the target thread)
|
||||
lark-cli im +messages-reply --message-id om_xxx --text "Let's discuss this" --reply-in-thread
|
||||
|
||||
# Reply with Markdown containing an image (must pre-upload via images.create)
|
||||
lark-cli im images create --data '{"image_type":"message"}' --file ./screenshot.png
|
||||
# Use the returned image_key
|
||||
lark-cli im +messages-reply --message-id om_xxx --markdown $'## Screenshot\n\n\n\nConfirmed.'
|
||||
|
||||
# If you need exact post structure, send JSON directly
|
||||
lark-cli im +messages-reply --message-id om_xxx --msg-type post --content '{"zh_cn":{"title":"Reply","content":[[{"tag":"text","text":"Detailed content"}]]}}'
|
||||
|
||||
# Reply with a local image (uploaded automatically before sending)
|
||||
lark-cli im +messages-reply --message-id om_xxx --image ./photo.png
|
||||
|
||||
# Reply with a local file (uploaded automatically before sending)
|
||||
lark-cli im +messages-reply --message-id om_xxx --file ./report.pdf
|
||||
|
||||
# Reply with a local video (--video-cover is required as the video cover)
|
||||
lark-cli im +messages-reply --message-id om_xxx --video ./demo.mp4 --video-cover ./cover.png
|
||||
|
||||
# With an idempotency key
|
||||
lark-cli im +messages-reply --message-id om_xxx --text "Received" --idempotency-key my-unique-id
|
||||
|
||||
# Preview the request without executing it
|
||||
lark-cli im +messages-reply --message-id om_xxx --markdown $'## Test\n\nhello' --dry-run
|
||||
```
|
||||
|
||||
## Media Input Rules
|
||||
|
||||
- Media flags accept an existing key (`img_xxx` / `file_xxx`), an `http://` or `https://` URL, or a local file path.
|
||||
- Local paths must be relative to the current working directory and stay within it after resolving `..` and symlinks.
|
||||
- Absolute paths such as `/tmp/photo.png` are rejected. Run the command from the file's directory and pass `./photo.png`, or copy the file into the current directory first.
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|------|------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `--message-id <id>` | Yes | ID of the message being replied to (`om_xxx`) |
|
||||
| `--msg-type <type>` | No | Message type (default `text`). If you use `--text` / `--markdown` / media flags, the effective type is inferred automatically. Explicitly setting a conflicting `--msg-type` fails validation |
|
||||
| `--content <json>` | One content option | Exact reply content as JSON. The JSON must match the effective `--msg-type` |
|
||||
| `--text <string>` | One content option | Plain text reply. Use when exact text and formatting preservation matter |
|
||||
| `--markdown <string>` | One content option | Best default for lightweight formatted replies such as headings, lists, links, summaries, and investigation notes. Internally converted to `post` JSON with Feishu-specific normalization |
|
||||
| `--image <path\|url\|key>` | One content option | Cwd-relative local image path, URL, or `image_key` (`img_xxx`) |
|
||||
| `--file <path\|url\|key>` | One content option | Cwd-relative local file path, URL, or `file_key` (`file_xxx`) |
|
||||
| `--video <path\|url\|key>` | One content option | Cwd-relative local video path, URL, or `file_key` (`file_xxx`); **must be used together with `--video-cover`** |
|
||||
| `--video-cover <path\|url\|key>` | **Required with `--video`** | Cwd-relative local cover image path, URL, or `image_key` (`img_xxx`) |
|
||||
| `--audio <path\|url\|key>` | One content option | Cwd-relative local audio path, URL, or `file_key` (`file_xxx`) |
|
||||
| `--reply-in-thread` | No | Reply inside the thread. The reply appears in the target message's thread instead of the main chat stream |
|
||||
| `--idempotency-key <key>` | No | Idempotency key; the same key sends only one reply within 1 hour |
|
||||
| `--as <identity>` | No | Identity type: `bot` or `user` (default `bot`) |
|
||||
| `--dry-run` | No | Print the request only, do not execute it |
|
||||
|
||||
> **Mutual exclusivity rule:** `--text`, `--markdown`, `--content`, and `--image`/`--file`/`--video`/`--audio` cannot be used together. Media flags are also mutually exclusive with each other.
|
||||
>
|
||||
> **Video cover rule:** `--video` **must** be accompanied by `--video-cover`. Omitting `--video-cover` when using `--video` will fail validation. `--video-cover` cannot be used without `--video`.
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
- Choosing `--text` for headings, lists, links, summaries, or investigation notes. Use `--markdown`.
|
||||
- Choosing `--markdown` when you actually need exact plain text. If exact line breaks, spacing, logs, code, or literal Markdown characters matter, use `--text`, usually with `$'...'`.
|
||||
- Assuming `--markdown` supports every Markdown feature. It is converted into a Feishu `post` payload and normalized first.
|
||||
- Putting local image paths inside Markdown like ``. `--markdown` does not auto-upload those paths.
|
||||
- **Using local file paths inside Markdown image syntax** (e.g. ``) with `--markdown`. Local paths are not auto-uploaded and will not render as an image. Pre-upload via `images.create` to get an `image_key` instead.
|
||||
- Using `--content` without making the JSON match the effective `--msg-type`.
|
||||
- Explicitly setting `--msg-type` to something that conflicts with `--text`, `--markdown`, or media flags.
|
||||
- Mixing `--text`, `--markdown`, or `--content` with media flags in one command.
|
||||
|
||||
## Return Value
|
||||
|
||||
```json
|
||||
{
|
||||
"message_id": "om_xxx",
|
||||
"chat_id": "oc_xxx",
|
||||
"create_time": "1234567890"
|
||||
}
|
||||
```
|
||||
|
||||
## Usage Scenarios
|
||||
|
||||
### Scenario 1: Reply in the main chat stream
|
||||
|
||||
```bash
|
||||
lark-cli im +messages-reply --message-id om_xxx --text "OK, I will handle it"
|
||||
```
|
||||
|
||||
The reply appears in the main chat stream and references the target message.
|
||||
|
||||
### Scenario 2: Reply inside a thread
|
||||
|
||||
```bash
|
||||
lark-cli im +messages-reply --message-id om_xxx --text "Let me take a look at this" --reply-in-thread
|
||||
```
|
||||
|
||||
The reply appears in the target message's thread and does not show up in the main chat stream.
|
||||
|
||||
## @Mention Format
|
||||
|
||||
The `<at>` syntax differs by message type. The shortcut only normalizes mentions for `text` and `post`; `interactive` card content is passed through verbatim, so cards must use the card-native syntax below.
|
||||
|
||||
### `text`
|
||||
|
||||
- `<at user_id="ou_xxx">name</at>` — the inner text is the mentioned user's display name and is optional (`<at user_id="ou_xxx"></at>` also works)
|
||||
- @all: `<at user_id="all"></at>`
|
||||
|
||||
### `post`
|
||||
|
||||
- Inside a `text` or `md` element, the same inline form as `text` works: `<at user_id="ou_xxx">name</at>`
|
||||
- Or use a dedicated `at` element node: `{"tag":"at","user_id":"ou_xxx"}` (use `"all"` to mention everyone)
|
||||
|
||||
### `interactive` (card)
|
||||
|
||||
Card content is **not** normalized — use the card-native `<at>` syntax inside a `lark_md` / `markdown` element:
|
||||
|
||||
- single user by open_id: `<at id=ou_xxx></at>`
|
||||
- multiple users: `<at ids=ou_xxx1,ou_xxx2></at>`
|
||||
- by email: `<at email=user@example.com></at>`
|
||||
|
||||
## Notes
|
||||
|
||||
- `--message-id` must be a valid message ID in `om_xxx` format
|
||||
- `--content` must be valid JSON
|
||||
- When using `--content`, you are responsible for making the JSON structure match the effective `msg_type`
|
||||
- `--reply-in-thread` adds `reply_in_thread=true` to the API request
|
||||
- `--reply-in-thread` is mainly meaningful in chats that support thread replies
|
||||
- `--image`/`--file`/`--video`/`--audio`/`--video-cover` support existing keys, URLs, and cwd-relative local file paths; the shortcut uploads local paths and URLs first, then sends the reply; both the upload and send steps use the same identity (UAT when `--as user`, TAT when `--as bot`)
|
||||
- If the provided media value starts with `img_` or `file_`, it is treated as an existing key and used directly
|
||||
- `--markdown` always sends `msg_type=post`
|
||||
- If you explicitly set `--msg-type` and it conflicts with the chosen content flag, validation fails
|
||||
- When using `--video`, `--video-cover` is required as the video cover
|
||||
- `--dry-run` uses placeholder image keys for remote Markdown images and placeholder media keys for local uploads
|
||||
- Failures return error codes and messages
|
||||
- `--as user` uses a user access token (UAT) and requires the `im:message.send_as_user` and `im:message` scopes; the reply is sent as the authorized end user
|
||||
- `--as bot` uses a tenant access token (TAT), and requires the `im:message:send_as_bot` scope
|
||||
- When using `--markdown` with images, pre-uploading via `images.create` to obtain an `image_key` is recommended for reliability; remote URLs may be auto-resolved at runtime, but if download/upload fails the image is removed with a warning; local paths are not supported
|
||||
- **`--reply-in-thread` posts into the target message's thread** (thread stream), not the main chat stream; without it the reply appears inline in the main stream referencing the target. Only meaningful in chats that support thread replies — in a non-thread chat the flag is a no-op.
|
||||
- **`--message-id` is the message being replied to** (`om_xxx`), not a chat id. To reply by named group, resolve the message first via `im +chat-search` → `im +chat-messages-list`, then take its `message_id`.
|
||||
|
||||
@@ -1,94 +1,16 @@
|
||||
# im +messages-resources-download
|
||||
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first to understand authentication, global parameters, and safety rules.
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first for authentication, global parameters, and safety rules.
|
||||
|
||||
Download image or file resources from a message. Supports **automatic chunked download for large files** using HTTP Range requests. Resources are identified by the combination of `message_id` + `file_key`, both of which come directly from message content returned by `im +chat-messages-list`.
|
||||
Maps to `lark-cli im +messages-resources-download`. **Run `lark-cli im +messages-resources-download --help` for the authoritative flags (`--message-id` / `--file-key` / `--type` / `--output` / `--as`), the `image`/`file` type enum, and the `--output` behavior (relative-only / no `..`, Content-Disposition filename fallback, extension inference).** This file covers only what `--help` cannot.
|
||||
|
||||
> **Note:** read-only message commands render resource keys in message content, but they do not download binaries automatically. Use this command whenever you need to fetch the actual image/file bytes or save them to a specific path.
|
||||
## Gotchas
|
||||
|
||||
This skill maps to the shortcut: `lark-cli im +messages-resources-download` (internally calls `GET /open-apis/im/v1/messages/{message_id}/resources/{file_key}`).
|
||||
- **A resource is identified by `message_id` + `file_key` together** — the `file_key` must come from *that* message's own content (returned by `im +chat-messages-list`). A `file_key` from a different message fails to download even if both look valid. Read-only message commands render the key in content but never fetch the bytes; this command is the only way to get them.
|
||||
- **`--type` must match the marker, not the original media kind**: `img_xxx` → `--type image`; `file_xxx` → `--type file`. Audio and video both surface as `file_xxx`, so they download with `--type file` (not `audio`/`video`).
|
||||
- **`234002` / `14005` are permission/state errors, NOT missing scope** — no access to the chat, or the file was deleted. Do not retry and do not re-run `auth login`; return the error to the user. (`im:message:readonly` not authorized is a *different* failure → `auth login --scope "im:message:readonly"`.)
|
||||
- **Large files auto-chunk via HTTP Range** (128 KB size-probe, then sequential 8 MB chunks, up to 2 retries w/ backoff, post-download size-integrity check). A "file size mismatch" or "Content-Range" error is transient network instability — just retry the command.
|
||||
|
||||
## Commands
|
||||
## HELP-GAP — not yet in `--help`/schema; keep until CLI adds it
|
||||
|
||||
```bash
|
||||
# Download an image (save to the current directory)
|
||||
lark-cli im +messages-resources-download --message-id om_xxx --file-key img_v3_xxx --type image
|
||||
|
||||
# Download a file
|
||||
lark-cli im +messages-resources-download --message-id om_xxx --file-key file_v3_xxx --type file
|
||||
|
||||
# Specify the output path
|
||||
lark-cli im +messages-resources-download --message-id om_xxx --file-key img_v3_xxx --type image --output ./photo.png
|
||||
|
||||
# Download as a bot
|
||||
lark-cli im +messages-resources-download --message-id om_xxx --file-key img_v3_xxx --type image --as bot
|
||||
|
||||
# Preview the request without executing it
|
||||
lark-cli im +messages-resources-download --message-id om_xxx --file-key img_v3_xxx --type image --dry-run
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|------|------|------|
|
||||
| `--message-id <id>` | Yes | Message ID (`om_xxx` format) |
|
||||
| `--file-key <key>` | Yes | Resource key (`img_xxx` or `file_xxx`) |
|
||||
| `--type <type>` | Yes | Resource type: `image` or `file` |
|
||||
| `--output <path>` | No | Output path (relative paths only; `..` traversal is not allowed). When omitted, the server's original filename from `Content-Disposition` is used if available; otherwise defaults to `file_key`. File extension is automatically inferred from `Content-Disposition` or `Content-Type` if not provided |
|
||||
| `--as <identity>` | No | Identity type: `user` (default) or `bot` |
|
||||
| `--dry-run` | No | Print the request only, do not execute it |
|
||||
|
||||
## Large File Download (Auto Chunking)
|
||||
|
||||
When downloading large files, the command automatically uses **HTTP Range requests** for reliable chunked downloading:
|
||||
|
||||
| Behavior | Details |
|
||||
|----------|---------|
|
||||
| Probe chunk | First 128 KB to detect file size and Content-Type |
|
||||
| Chunk size | 8 MB per subsequent request |
|
||||
| Workers | Single-threaded sequential download (ensures reliability) |
|
||||
| Retries | Up to 2 retries for transient request failures, with exponential backoff |
|
||||
|
||||
**Benefits:**
|
||||
- Reduces the impact of transient request failures during large downloads
|
||||
- Preserves the server's original filename via `Content-Disposition` (supports RFC 5987 UTF-8 encoding); falls back to `Content-Type`-based extension inference
|
||||
- Validates file size integrity after download completion
|
||||
|
||||
## `file_key` Sources
|
||||
|
||||
Different resource markers in message content correspond to different `file_key` and `type` values:
|
||||
|
||||
| Message Type | Marker in Content | `file_key` Format | `--type` |
|
||||
|---------|-------------|---------------|--------|
|
||||
| Image | `img_xxx` | `img_xxx` | `image` |
|
||||
| File | `file_xxx` | `file_xxx` | `file` |
|
||||
| Audio | `file_xxx` | `file_xxx` | `file` |
|
||||
| Video | `file_xxx` | `file_xxx` | `file` |
|
||||
|
||||
## Usage Scenario
|
||||
|
||||
### Scenario: Extract and download an image from a message
|
||||
|
||||
```bash
|
||||
# Step 1: Fetch messages and find one containing an image
|
||||
lark-cli im +chat-messages-list --chat-id oc_xxx
|
||||
# In the response you see: { "msg_type": "image", "content": "{\"image_key\":\"img_v3_xxx\"}" }
|
||||
|
||||
# Step 2: Download the image
|
||||
lark-cli im +messages-resources-download --message-id om_xxx --file-key img_v3_xxx --type image
|
||||
```
|
||||
|
||||
## Common Errors and Troubleshooting
|
||||
|
||||
| Symptom | Root Cause | Solution |
|
||||
|---------|---------|---------|
|
||||
| Download failed | `file_key` does not match the `message_id` | Make sure the `file_key` came from that message's content |
|
||||
| Hit error code 234002 or 14005 | No permission, **not** missing API scope | no access to this chat or file was deleted — do not retry, return the error to the user |
|
||||
| Permission denied | `im:message:readonly` is not authorized | Run `auth login --scope "im:message:readonly"` |
|
||||
| File size mismatch | Chunked download integrity check failed | Network instability during download; retry the command |
|
||||
| Content-Range error | Server returned invalid range header | Transient API issue; retry the command |
|
||||
|
||||
## References
|
||||
|
||||
- [lark-im](../SKILL.md) - all message-related commands
|
||||
- [lark-shared](../../lark-shared/SKILL.md) - authentication and global parameters
|
||||
- The chunked-download internals above (probe size, 8 MB chunk, sequential single-worker, 2 retries, integrity check) are runtime behavior not surfaced by `--help`.
|
||||
|
||||
@@ -1,234 +1,32 @@
|
||||
# im +messages-search
|
||||
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first to understand authentication, global parameters, and safety rules.
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first for authentication, global parameters, and safety rules.
|
||||
|
||||
Search Feishu messages across conversations. This shortcut automatically performs a multi-step workflow: search for message IDs, batch fetch message details, then enrich the results with chat context.
|
||||
Maps to `lark-cli im +messages-search`. **Run `lark-cli im +messages-search --help` for the authoritative flags (`--query` / `--chat-id` / `--sender` / `--include-attachment-type` / `--chat-type` / `--sender-type` / `--exclude-sender-type` / `--is-at-me` / `--at-chatter-ids` / `--start` / `--end` / `--page-size` / `--page-all` / `--page-limit`), enums, time format (ISO 8601 + offset), and `--no-reactions`.** This file covers only what `--help` cannot.
|
||||
|
||||
By default each result message also carries a `reactions` block (counts + details from `im.reactions.batch_query`) when the server has reactions for it, and `update_time` for messages that were actually edited. With `--page-all`, every page is enriched; pass `--no-reactions` to skip the extra round-trip. See [message enrichment](lark-im-message-enrichment.md) for the full contract.
|
||||
**User identity only** (`--as user`); bot not supported. Auto-orchestrates: search → batch mget → batch chat-context enrich (plus reactions / update_time per [message enrichment](lark-im-message-enrichment.md)).
|
||||
|
||||
> **User identity only** (`--as user`). Bot identity is not supported.
|
||||
## Gotchas
|
||||
|
||||
This skill maps to the shortcut: `lark-cli im +messages-search` (internally calls `POST /open-apis/im/v1/messages/search` + batched `GET /open-apis/im/v1/messages/mget`, then batch-fetches chat context).
|
||||
- **Identity is user-only — do NOT try `--as bot`.** For bot identity with a named group + history intent, resolve via `im +chat-search --as bot`, then `im +chat-messages-list --as bot --chat-id <id>`.
|
||||
- **Resolving a chat by name**: use `im +chat-search` first to get `chat_id`, then pass `--chat-id`. **Do not use `im chats search` or `+chat-list`.**
|
||||
- `--at-chatter-ids` results also include messages that `@all`.
|
||||
- **Resources not downloaded**: images render as `[Image: img_xxx]` placeholders; use `im +messages-resources-download` for the bytes.
|
||||
|
||||
## Commands
|
||||
## Query construction (high-signal vs over-constrained)
|
||||
|
||||
```bash
|
||||
# Search by keyword
|
||||
lark-cli im +messages-search --query "project progress"
|
||||
- Use `--query` only for **real message keywords**. For activity review ("最近一周和哪些 Bot 交互"、"整理和某人的聊天记录"), keep `--query ""` and rely on `--sender` / `--sender-type` / `--chat-id` / `--start` / `--end`. Do NOT put instruction words ("看看"、"总结"、"聊天记录") into `--query` — they over-constrain and hide the relevant messages.
|
||||
- **Relative time**: compute start/end from the current day at execution time; never copy date literals from this file ("最近一周" = compute the actual range, don't hardcode).
|
||||
- For activity summaries, retain each item's `message_id` / sender / chat / create_time as recall targets; don't rely on a high-level keyword match alone.
|
||||
|
||||
# Restrict search to a specific group chat
|
||||
lark-cli im +messages-search --query "weekly report" --chat-id oc_xxx
|
||||
## Work summary / report generation
|
||||
|
||||
# Filter by sender (comma-separated)
|
||||
lark-cli im +messages-search --query "requirement" --sender ou_xxx,ou_yyy
|
||||
- Narrow first (`--chat-id` / `--sender` / `--start` / `--end`), then **paginate exhaustively** — one page (20–50) is rarely enough.
|
||||
- Default to `--page-all --format json` (JSON carries `has_more` / `page_token`); use `--page-limit <n>` to bound the run; resume via `--page-token` if a bounded run still returns `has_more=true`.
|
||||
- **Accumulate all pages, then summarize** — don't summarize after page 1. Group by topic/thread, not chronology.
|
||||
- If no time range is given, default to the current week (Mon→today), or ask.
|
||||
|
||||
# Filter by attachment type
|
||||
lark-cli im +messages-search --query "report" --include-attachment-type file
|
||||
## HELP-GAP — not yet in `--help`/schema; keep until CLI adds it
|
||||
|
||||
# Filter by chat type (group / p2p)
|
||||
lark-cli im +messages-search --query "progress" --chat-type group
|
||||
|
||||
# Filter by sender type (user / bot)
|
||||
lark-cli im +messages-search --query "reminder" --sender-type bot
|
||||
|
||||
# Exclude bot senders
|
||||
lark-cli im +messages-search --query "reminder" --exclude-sender-type bot
|
||||
|
||||
# Only messages that @me
|
||||
lark-cli im +messages-search --query "announcement" --is-at-me
|
||||
|
||||
# Only messages that @mention specific users (results also include messages that @all)
|
||||
lark-cli im +messages-search --query "release" --at-chatter-ids ou_xxx,ou_yyy
|
||||
|
||||
# Combined filters + time range
|
||||
lark-cli im +messages-search --query "meeting" --sender ou_xxx --chat-type group --start "2026-03-13T00:00:00+08:00" --end "2026-03-20T23:59:59+08:00"
|
||||
|
||||
# Specific time range (ISO 8601)
|
||||
lark-cli im +messages-search --query "release" --start "2026-03-01T00:00:00+08:00" --end "2026-03-10T00:00:00+08:00"
|
||||
|
||||
# Output format options
|
||||
lark-cli im +messages-search --query "test" --format pretty
|
||||
lark-cli im +messages-search --query "test" --format table
|
||||
lark-cli im +messages-search --query "test" --format csv
|
||||
|
||||
# Pagination
|
||||
lark-cli im +messages-search --query "test" --page-token <PAGE_TOKEN>
|
||||
|
||||
# Auto-pagination across multiple pages
|
||||
lark-cli im +messages-search --query "test" --page-all --format json
|
||||
|
||||
# Auto-pagination with an explicit page cap
|
||||
lark-cli im +messages-search --query "test" --page-limit 5 --format json
|
||||
|
||||
# Preview the request without executing it
|
||||
lark-cli im +messages-search --query "test" --dry-run
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|------|------|------|
|
||||
| `--query <text>` | No | Search keyword (may be empty when used with other filters) |
|
||||
| `--chat-id <id>` | No | Restrict to chat IDs, comma-separated (`oc_xxx,oc_yyy`) |
|
||||
| `--sender <ids>` | No | Sender open_ids, comma-separated (`ou_xxx`) |
|
||||
| `--include-attachment-type <type>` | No | Attachment filter: `file` / `image` / `video` / `link` |
|
||||
| `--chat-type <type>` | No | Chat type: `group` / `p2p` |
|
||||
| `--sender-type <type>` | No | Sender type: `user` / `bot` |
|
||||
| `--exclude-sender-type <type>` | No | Exclude messages from `user` or `bot` senders |
|
||||
| `--is-at-me` | No | Only return messages that mention `@me` |
|
||||
| `--at-chatter-ids <ids>` | No | Filter by @mentioned user open_ids, comma-separated (`ou_xxx,ou_yyy`). Matched results also include messages that `@all` |
|
||||
| `--start <time>` | No | Start time with local timezone offset required (e.g. `2026-03-24T00:00:00+08:00`) |
|
||||
| `--end <time>` | No | End time with local timezone offset required (e.g. `2026-03-25T23:59:59+08:00`) |
|
||||
| `--page-size <n>` | No | Page size (default 20, range 1-50) |
|
||||
| `--page-token <token>` | No | Pagination token for the next page |
|
||||
| `--page-all` | No | Automatically paginate through all result pages (up to 40 pages) |
|
||||
| `--page-limit <n>` | No | Max pages to fetch when auto-pagination is enabled (default 20, max 40). Setting it explicitly also enables auto-pagination |
|
||||
| `--format <fmt>` | No | Output format: `json` (default) / `pretty` / `table` / `ndjson` / `csv` |
|
||||
| `--as <identity>` | No | Identity type (defaults to and only supports `user`) |
|
||||
| `--dry-run` | No | Print the request only, do not execute it |
|
||||
|
||||
## Core Constraints
|
||||
|
||||
### 1. Provide at least one filter whenever possible
|
||||
|
||||
All parameters are optional, but you should usually provide at least one filter (`--query`, `--sender`, `--chat-id`, etc.). Otherwise the search scope may be too broad and return low-signal results.
|
||||
|
||||
### 2. Two-step orchestration is automatic
|
||||
|
||||
The shortcut automatically performs:
|
||||
|
||||
1. The **search API** returns matching `message_id` values
|
||||
2. The **mget API** fetches full message content for those message IDs in batch
|
||||
3. Chat context lookup is fetched in batch and attached to each message
|
||||
|
||||
The user does not need to manage the orchestration manually. When search results span multiple pages, the shortcut can also paginate automatically with `--page-all` or `--page-limit`.
|
||||
|
||||
### 3. Conversation context is enriched automatically
|
||||
|
||||
In JSON output, each message automatically includes conversation context:
|
||||
|
||||
| Field | Description |
|
||||
|------|------|
|
||||
| `chat_type` | Conversation type: `p2p` / `group` |
|
||||
| `chat_name` | Group name (for groups) or the other participant's name (for p2p chats) |
|
||||
| `chat_partner` | For p2p only: the other participant's `open_id` and `name` |
|
||||
|
||||
In pretty output, the `chat` column shows the chat name for groups, or `"p2p"` for direct messages.
|
||||
|
||||
Each message in JSON output contains:
|
||||
|
||||
| Field | Description |
|
||||
|------|------|
|
||||
| `message_id` | Message ID |
|
||||
| `msg_type` | Message type: `text`, `image`, `file`, `interactive`, `post`, `audio`, `video`, `system`, etc. |
|
||||
| `create_time` | Creation time |
|
||||
| `sender` | Sender information (includes `name` for user senders) |
|
||||
| `content` | Message content |
|
||||
| `chat_id` | ID of the conversation the message belongs to |
|
||||
| `deleted` | Whether the message has been recalled (`true` = recalled) |
|
||||
| `updated` | Whether the message has been edited after sending |
|
||||
| `mentions` | Array of @mentions in the message; each item contains `{id, key, name}`. Present only when the message contains @mentions |
|
||||
| `thread_id` | Thread ID (`omt_xxx`) if the message has replies in a thread. Present only when replies exist |
|
||||
|
||||
### 4. Pagination behavior
|
||||
|
||||
- Default behavior is still **single-page**.
|
||||
- `--page-token` is the manual continuation mechanism when you already have a token from a previous response.
|
||||
- `--page-all` enables auto-pagination and uses a default cap of **40 pages**.
|
||||
- `--page-limit <n>` enables auto-pagination with an explicit cap. If you pass `--page-limit` without `--page-all`, auto-pagination is still enabled.
|
||||
- When auto-pagination stops because of the configured page cap, the response still includes the last `has_more` / `page_token` so you can continue manually.
|
||||
|
||||
### 5. Search results contain follow-up clues
|
||||
|
||||
In JSON output, each message includes `chat_id` and `thread_id` (when present). Use them with other shortcuts for deeper inspection:
|
||||
|
||||
```bash
|
||||
# View the full message stream for the conversation that contains the search result
|
||||
lark-cli im +chat-messages-list --chat-id <chat_id>
|
||||
|
||||
# View replies in the thread that contains the search result
|
||||
lark-cli im +threads-messages-list --thread <thread_id>
|
||||
```
|
||||
|
||||
## Resource Rendering
|
||||
|
||||
Search results reuse the same content formatter as other read commands. Image messages are rendered as placeholders such as `[Image: img_xxx]`; resource binaries are **not** downloaded automatically.
|
||||
|
||||
Use `im +messages-resources-download` if you need to fetch the underlying image or file bytes from a specific message.
|
||||
|
||||
## AI Usage Guidance
|
||||
|
||||
### Query boundary for activity review
|
||||
|
||||
Use `--query` only for real message keywords. If the user asks for activity review such as "最近一周我和哪些 Bot 有过交互" or "整理我和某人的聊天记录", and the useful constraints are sender type, chat, person, or time range, keep `--query ""` and rely on those filters. Do not put generic instruction words such as "看看", "总结", "交互内容", or "聊天记录" into `--query`; those words often over-constrain message search and hide the relevant messages.
|
||||
|
||||
This guidance applies only when using user identity. `im +messages-search` is user-only; if the user explicitly asks for application/bot identity, do not try `--as bot`. For bot identity with a named group and history/listing intent, resolve the group with `im +chat-search --as bot`, then list messages with `im +chat-messages-list --as bot --chat-id <chat_id>`.
|
||||
|
||||
```bash
|
||||
# Review recent bot interactions without forcing a keyword
|
||||
lark-cli im +messages-search --query "" --sender-type bot --start "<YYYY-MM-DDT00:00:00+08:00>" --end "<YYYY-MM-DDT23:59:59+08:00>" --page-all --format json
|
||||
```
|
||||
|
||||
Replace the time placeholders at execution time. For example, "最近一周" means computing the start date and end date from the current day before running the command; do not copy date literals from this reference into answers for relative requests.
|
||||
|
||||
For activity summaries, validate evidence by message IDs and chat context. The final answer should cite or retain the `message_id`, sender, chat, and create time for each important item. If the row's source data contains concrete `om_...` message IDs or `ou_...` user IDs, treat those IDs as strong recall targets during verification; do not rely only on a high-level keyword match.
|
||||
|
||||
### Resolving chat_id from a chat name
|
||||
|
||||
When the user refers to a chat by name and you need its `chat_id` for the `--chat-id` filter, use [`+chat-search`](lark-im-chat-search.md) first:
|
||||
|
||||
```bash
|
||||
# Step 1: Find the chat_id by name
|
||||
lark-cli im +chat-search --query "<chat name keyword>" --format json
|
||||
|
||||
# Step 2: Use the chat_id to narrow down message search
|
||||
lark-cli im +messages-search --query "keyword" --chat-id <chat_id>
|
||||
```
|
||||
|
||||
**Do not use `im chats search` or `+chat-list` — always use the `+chat-search` shortcut.**
|
||||
|
||||
## Work Summary / Report Generation
|
||||
|
||||
When the user asks you to summarize work, generate a weekly report, or compile activity from chat messages, you should **paginate through all available results** to get a complete picture. A single page is rarely enough for thorough summarization.
|
||||
|
||||
### Strategy
|
||||
|
||||
1. **Start with targeted filters** — use `--chat-id`, `--sender`, `--start`, `--end` to narrow the scope as much as possible before paginating.
|
||||
2. **Prefer auto-pagination** — for report and summary tasks, use `--page-all --format json` by default. If you need a bounded run, use `--page-limit <n> --format json`.
|
||||
3. **Accumulate before summarizing** — collect all pages of messages first, then analyze and summarize. Do not summarize after the first page alone — you will miss important context.
|
||||
4. **Fall back to `--page-token` when resuming** — if auto-pagination hits the configured page cap and the response still has `has_more=true`, continue from the returned `page_token`.
|
||||
5. **Use `--format json`** — JSON output includes `has_more` and `page_token` fields needed for pagination. `pretty` and `table` formats are useful for reading but not for resuming pagination reliably.
|
||||
|
||||
### Example: Weekly work summary from a project chat
|
||||
|
||||
```bash
|
||||
# Preferred: fetch automatically
|
||||
lark-cli im +messages-search --query "" --chat-id oc_xxx --sender ou_me --start "2026-03-18T00:00:00+08:00" --end "2026-03-25T23:59:59+08:00" --page-size 50 --page-all --format json
|
||||
|
||||
# If you need to cap the run explicitly
|
||||
lark-cli im +messages-search --query "" --chat-id oc_xxx --sender ou_me --start "2026-03-18T00:00:00+08:00" --end "2026-03-25T23:59:59+08:00" --page-size 50 --page-limit 5 --format json
|
||||
|
||||
# If the bounded run still returns has_more=true, continue manually
|
||||
lark-cli im +messages-search --query "" --chat-id oc_xxx --sender ou_me --start "2026-03-18T00:00:00+08:00" --end "2026-03-25T23:59:59+08:00" --page-size 50 --page-token <token_from_previous_run> --format json
|
||||
```
|
||||
|
||||
### Key points
|
||||
|
||||
- **Always paginate exhaustively** for summary tasks. A single page of 20-50 messages is usually insufficient for a meaningful work summary.
|
||||
- Prefer `--page-all`; use `--page-limit` only when you need to bound runtime or output volume.
|
||||
- If the user does not specify a time range, default to the current week (Monday to today) for weekly reports, or ask for clarification.
|
||||
- When summarizing, group messages by topic/thread rather than by chronological order for better readability.
|
||||
|
||||
## Common Errors and Troubleshooting
|
||||
|
||||
| Symptom | Root Cause | Solution |
|
||||
|---------|---------|---------|
|
||||
| Too few results | The time range is too narrow or the keyword is too specific | Expand the time range and try broader keywords |
|
||||
| No results | Missing permission or no match | Confirm `search:message` is authorized and relax the filters |
|
||||
| Permission denied | Search scope not authorized | Run `auth login --scope "search:message"` |
|
||||
|
||||
## References
|
||||
|
||||
- [lark-im](../SKILL.md) - all message-related commands
|
||||
- [lark-im-threads-messages-list](lark-im-threads-messages-list.md) - inspect thread replies
|
||||
- [lark-shared](../../lark-shared/SKILL.md) - authentication and global parameters
|
||||
- **Auto-enrich fields** (JSON): conversation `chat_type` (p2p/group) · `chat_name` · `chat_partner` (p2p only: other party `open_id`+`name`). Per message: `deleted` (true=recalled) · `updated` (edited after send) · `mentions` `[{id,key,name}]` **present only when @mentions exist** · `thread_id` (omt_xxx) **present only when replies exist**.
|
||||
- **Follow-up clues**: each result carries `chat_id` (+ `thread_id` when present) → `im +chat-messages-list --chat-id <id>` / `im +threads-messages-list --thread <id>`.
|
||||
|
||||
@@ -1,264 +1,53 @@
|
||||
# im +messages-send
|
||||
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first to understand authentication, global parameters, and safety rules.
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first for authentication, global parameters, and safety rules.
|
||||
|
||||
Send a message to a group chat or a direct message conversation. Supports both user identity (`--as user`) and bot identity (`--as bot`).
|
||||
Maps to `lark-cli im +messages-send`. **Run `lark-cli im +messages-send --help` for the authoritative flag list, content/media flags, mutual-exclusion rules, msg-type inference, `--video`/`--video-cover` pairing, and the cwd-relative media path rules.** This file only covers what `--help` cannot.
|
||||
|
||||
This skill maps to the shortcut: `lark-cli im +messages-send` (internally calls `POST /open-apis/im/v1/messages`).
|
||||
Safety: sent messages are visible to others — confirm recipient, content, and identity before sending (see lark-shared risk policy). `--as bot` requires the app to already be in the target chat.
|
||||
|
||||
## Safety Constraints
|
||||
## Picking the content flag (when to use which)
|
||||
|
||||
Messages sent by this tool are visible to other people. Before calling it, you **must** confirm with the user:
|
||||
- `--markdown` — headings / lists / links / summaries / reports. **Converted to a Feishu `post` payload (forces `msg_type=post`), single `zh_cn` locale.**
|
||||
- `--text` — exact plain text: logs, code, indentation, or literal Markdown that must **not** render.
|
||||
- `--content` — exact JSON when you need a post title, multiple locales, cards, or unsupported structures.
|
||||
|
||||
1. The recipient (which person or which group)
|
||||
2. The message content
|
||||
3. The sending identity (user or bot)
|
||||
## `--markdown` boundaries (non-obvious)
|
||||
|
||||
**Do not** send messages without explicit user approval.
|
||||
- Not full CommonMark/GFM. Always a single-`zh_cn` `post`; **cannot set a post title** — use `--content` for a title.
|
||||
- Headings are rewritten: `# Title` → `#### Title`; `##`–`######` → `#####` when the content has H1–H3.
|
||||
- **Local image paths in `` are NOT auto-uploaded** and will not render. Pre-upload first to get an `img_xxx` key:
|
||||
```bash
|
||||
lark-cli im images create --data '{"image_type":"message"}' --file ./diagram.png # → {"image_key":"img_v3_xxxx"}
|
||||
lark-cli im +messages-send --chat-id oc_xxx --markdown $'## Report\n\n'
|
||||
```
|
||||
- Remote `https://` images are auto-downloaded + uploaded at runtime; on download/upload failure the image is silently dropped with a warning.
|
||||
|
||||
When using `--as bot`, the message is sent in the app's name, so make sure the app has already been added to the target chat.
|
||||
## Preserve exact formatting → `--text` with `$'...'`
|
||||
|
||||
When using `--as user`, the message is sent as the authorized end user and requires the `im:message.send_as_user` and `im:message` scopes.
|
||||
|
||||
## Choose The Right Content Flag
|
||||
|
||||
### Default Selection Rule For Agents
|
||||
|
||||
- Prefer `--markdown` for headings, lists, links, summaries, reports, or Markdown-looking content.
|
||||
- Use `--text` for exact plain text: logs, code, indentation-sensitive text, or literal Markdown.
|
||||
- Use `--content` for exact `post` JSON, titles, multiple locales, cards, or unsupported structures.
|
||||
|
||||
| Need | Recommended flag | Why |
|
||||
|------|------|------|
|
||||
| Send headings, lists, links, summaries, or reports | `--markdown` | Best default for lightweight formatting; converted to Feishu `post` JSON |
|
||||
| Send plain text exactly as written | `--text` | Preserves literal text; no Markdown conversion |
|
||||
| Precisely control the final payload | `--content` | You provide the exact JSON for `text` / `post` / `interactive` / `share_*` / media payloads |
|
||||
| Send image / file / video / audio | `--image` / `--file` / `--video` / `--audio` | Shortcut uploads URLs, or cwd-relative local files automatically |
|
||||
|
||||
### `--text` vs `--markdown`
|
||||
|
||||
- Use `--markdown` for lightweight formatted messages.
|
||||
- Use `--text` for exact plain text, especially logs, code, indentation, or Markdown characters that should **not** render.
|
||||
- Use `--content` when `--markdown` is not enough, especially if you need exact `post` JSON, a title, multiple locales, cards, or unsupported rich structures.
|
||||
|
||||
## What `--markdown` Really Does
|
||||
|
||||
`--markdown` accepts Markdown-like input and converts it to the Feishu `post` payload required by the message API.
|
||||
|
||||
The shortcut does all of the following before sending:
|
||||
|
||||
1. Forces `msg_type=post`
|
||||
2. Resolves remote Markdown images like `` by downloading and uploading them first
|
||||
3. Normalizes the Markdown for Feishu post rendering
|
||||
4. Wraps the result as:
|
||||
|
||||
```json
|
||||
{"zh_cn":{"content":[[{"tag":"md","text":"..."}]]}}
|
||||
```
|
||||
|
||||
This makes `--markdown` the simplest path for lightweight formatted messages.
|
||||
|
||||
### Markdown Boundaries
|
||||
|
||||
- It does **not** promise full CommonMark / GitHub Flavored Markdown support.
|
||||
- It always becomes a `post` payload with a single `zh_cn` locale.
|
||||
- It does **not** let you set a `post` title. If you need a title, use `--msg-type post --content ...`.
|
||||
- Headings are rewritten:
|
||||
- `# Title` becomes `#### Title`
|
||||
- `##` to `######` are normalized to `#####` when the content contains H1-H3
|
||||
- Consecutive headings are separated with blank lines after heading normalization.
|
||||
- Block spacing and line breaks may be normalized during conversion.
|
||||
- Code blocks are preserved as code blocks.
|
||||
- Excess blank lines are compressed.
|
||||
- Already-uploaded `img_xxx` image keys are the most reliable Markdown image input.
|
||||
- Local paths in Markdown image syntax like `` are **not** supported and will not be auto-uploaded.
|
||||
- Remote URLs (`https://...`) will be auto-downloaded and uploaded at runtime; if the download or upload fails, the image is removed with a warning.
|
||||
|
||||
If you need a title, multiple locales, cards, unsupported rich structures, or byte-for-byte post JSON control, use `--content` and provide the final JSON yourself.
|
||||
|
||||
### Image Constraint for `--markdown`
|
||||
|
||||
When using `--markdown` with images, prefer pre-uploading via `images.create` and referencing `` for predictable results. Remote URLs may work but are not guaranteed.
|
||||
|
||||
**Steps:**
|
||||
For multi-line text, indentation, code blocks, or literal backslashes, use shell ANSI-C quoting so `\n` is honored literally instead of relying on the shell:
|
||||
|
||||
```bash
|
||||
# 1. Upload image to get image_key
|
||||
lark-cli im images create --data '{"image_type":"message"}' --file ./diagram.png
|
||||
# Returns: {"image_key":"img_v3_xxxx"}
|
||||
|
||||
# 2. Use image_key in --markdown
|
||||
lark-cli im +messages-send --chat-id oc_xxx --markdown $'## Report\n\n\n\nSee above for details.'
|
||||
lark-cli im +messages-send --chat-id oc_xxx --text $'Build failed\nBranch: feature/x\nAction: check logs'
|
||||
```
|
||||
|
||||
## Preserving Formatting
|
||||
## @Mention format (differs by message type)
|
||||
|
||||
If the message has multiple lines, indentation, code blocks, tabs, or many quotes/backslashes, prefer shell ANSI-C quoting with `$'...'` for either `--markdown` or `--text`.
|
||||
The shortcut only normalizes mentions for `text` and `post`; **`interactive` card content is passed through verbatim**, so cards must use the card-native syntax — this asymmetry is the trap.
|
||||
|
||||
This is especially useful in `zsh` / `bash` because it lets you write `\n` explicitly instead of relying on the shell to preserve literal newlines.
|
||||
- **`text`**: `<at user_id="ou_xxx">name</at>` (inner name optional); @all: `<at user_id="all"></at>`. The shortcut also normalizes `<at id=...>` / `<at open_id=...>` into `user_id`, but author examples with `user_id`.
|
||||
- **`post`**: same inline form inside a `text`/`md` element, or a dedicated node `{"tag":"at","user_id":"ou_xxx"}` (`"all"` for everyone).
|
||||
- **`interactive` (card)**: NOT normalized — use card-native `<at>` inside a `lark_md`/`markdown` element: single `<at id=ou_xxx></at>`, multiple `<at ids=ou_xxx1,ou_xxx2></at>`, by email `<at email=user@example.com></at>`.
|
||||
|
||||
### When formatting must be preserved
|
||||
## Common mistakes (the non-obvious ones)
|
||||
|
||||
Use `--text` plus `$'...'`:
|
||||
- `--text` for headings/lists/reports → use `--markdown`. `--markdown` when exact line breaks / logs / literal Markdown matter → use `--text` with `$'...'`.
|
||||
- Local image paths inside `--markdown` (``) — pre-upload via `images.create` instead.
|
||||
|
||||
```bash
|
||||
lark-cli im +messages-send --chat-id oc_xxx --text $'Build failed\nBranch: feature/im-docs\nAction: please check logs'
|
||||
```
|
||||
## HELP-GAP — not yet in `--help`/schema; keep here until CLI adds it
|
||||
|
||||
```bash
|
||||
lark-cli im +messages-send --chat-id oc_xxx --text $'```bash\nmake test\nmake lint\n```'
|
||||
```
|
||||
> These are USAGE that belongs in schema but isn't there yet. Once CLI adds them, delete this section.
|
||||
|
||||
Use this path when you want the receiver to see the text exactly as entered, not a converted Markdown post.
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Send a formatted update
|
||||
lark-cli im +messages-send --chat-id oc_xxx --markdown $'## Update\n\n- item 1\n- item 2'
|
||||
|
||||
# Send a plain one-line message
|
||||
lark-cli im +messages-send --chat-id oc_xxx --text "Hello"
|
||||
|
||||
# Equivalent manual JSON
|
||||
lark-cli im +messages-send --chat-id oc_xxx --content '{"text":"Hello"}'
|
||||
|
||||
# Send to a direct message (pass open_id)
|
||||
lark-cli im +messages-send --user-id ou_xxx --text "Hello"
|
||||
|
||||
# Send multi-line text while preserving formatting
|
||||
lark-cli im +messages-send --chat-id oc_xxx --text $'Line 1\nLine 2\n indented line'
|
||||
|
||||
# Send Markdown with an image (must pre-upload via images.create)
|
||||
lark-cli im images create --data '{"image_type":"message"}' --file ./screenshot.png
|
||||
# Use the returned image_key in the markdown content
|
||||
lark-cli im +messages-send --chat-id oc_xxx --markdown $'## Status\n\n\n\nDone.'
|
||||
|
||||
# If you need exact post structure, send JSON directly
|
||||
lark-cli im +messages-send --chat-id oc_xxx --msg-type post --content '{"zh_cn":{"title":"Title","content":[[{"tag":"text","text":"Body"}]]}}'
|
||||
|
||||
# Send a local image (uploaded automatically before sending)
|
||||
lark-cli im +messages-send --chat-id oc_xxx --image ./photo.png
|
||||
|
||||
# Or send directly with an existing image_key
|
||||
lark-cli im +messages-send --chat-id oc_xxx --image img_xxx
|
||||
|
||||
# Send a local file (uploaded automatically before sending)
|
||||
lark-cli im +messages-send --chat-id oc_xxx --file ./report.pdf
|
||||
|
||||
# Send a video (--video-cover is required as the cover)
|
||||
lark-cli im +messages-send --chat-id oc_xxx --video ./demo.mp4 --video-cover ./cover.png
|
||||
lark-cli im +messages-send --chat-id oc_xxx --video ./demo.mp4 --video-cover img_xxx
|
||||
|
||||
# Send audio
|
||||
lark-cli im +messages-send --chat-id oc_xxx --audio ./voice.opus
|
||||
|
||||
# Use an idempotency key (same key sends only once within 1 hour)
|
||||
lark-cli im +messages-send --chat-id oc_xxx --text "Hello" --idempotency-key my-unique-id
|
||||
|
||||
# Preview the request without executing it
|
||||
lark-cli im +messages-send --chat-id oc_xxx --markdown $'## Test\n\nhello' --dry-run
|
||||
```
|
||||
|
||||
## Media Input Rules
|
||||
|
||||
- Media flags accept an existing key (`img_xxx` / `file_xxx`), an `http://` or `https://` URL, or a local file path.
|
||||
- Local paths must be relative to the current working directory and stay within it after resolving `..` and symlinks.
|
||||
- Absolute paths such as `/tmp/photo.png` are rejected. Run the command from the file's directory and pass `./photo.png`, or copy the file into the current directory first.
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|------|------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `--chat-id <id>` | One of two | Group chat ID (`oc_xxx`) |
|
||||
| `--user-id <id>` | One of two | User open_id (`ou_xxx`) for direct messages |
|
||||
| `--text <string>` | One content option | Plain text message. Use when exact text and formatting preservation matter. Automatically wrapped as `{"text":"..."}` |
|
||||
| `--markdown <string>` | One content option | Best default for lightweight formatted messages such as headings, lists, links, summaries, and reports. Internally converted to `post` JSON with Feishu-specific normalization |
|
||||
| `--content <json>` | One content option | Exact message content JSON string; use this when you need full control over `msg_type` and payload. The JSON must match the effective `--msg-type` |
|
||||
| `--image <path\|url\|key>` | One content option | Cwd-relative local image path, URL, or `image_key` (`img_xxx`). Local paths and URLs are uploaded automatically |
|
||||
| `--file <path\|url\|key>` | One content option | Cwd-relative local file path, URL, or `file_key` (`file_xxx`). Local paths and URLs are uploaded automatically |
|
||||
| `--video <path\|url\|key>` | One content option | Cwd-relative local video path, URL, or `file_key` (`file_xxx`). Local paths and URLs are uploaded automatically. **Must be paired with `--video-cover`** |
|
||||
| `--video-cover <path\|url\|key>` | **Required with `--video`** | Cwd-relative local cover image path, URL, or `image_key` (`img_xxx`). Local paths and URLs are uploaded automatically |
|
||||
| `--audio <path\|url\|key>` | One content option | Cwd-relative local audio path, URL, or `file_key` (`file_xxx`). Local paths and URLs are uploaded automatically |
|
||||
| `--msg-type <type>` | No | Message type (default `text`). If you use `--text` / `--markdown` / media flags, the effective type is inferred automatically. Explicitly setting a conflicting `--msg-type` fails validation |
|
||||
| `--idempotency-key <key>` | No | Idempotency key; the same key sends only one message within 1 hour |
|
||||
| `--as <identity>` | No | Identity type: `bot` or `user` (default `bot`) |
|
||||
| `--dry-run` | No | Print the request only, do not execute it |
|
||||
|
||||
> **Mutual exclusivity rule:** `--text`, `--markdown`, `--content`, and `--image`/`--file`/`--video`/`--audio` cannot be used together. Media flags are also mutually exclusive with each other.
|
||||
>
|
||||
> **Video cover rule:** `--video` **must** be accompanied by `--video-cover`. Omitting `--video-cover` when using `--video` will fail validation. `--video-cover` cannot be used without `--video`.
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
- Choosing `--text` for headings, lists, links, summaries, or reports. Use `--markdown`.
|
||||
- Choosing `--markdown` when you actually need exact plain text. If exact line breaks, spacing, logs, code, or literal Markdown characters matter, use `--text`, usually with `$'...'`.
|
||||
- Assuming `--markdown` supports every Markdown feature. It is converted into a Feishu `post` payload and normalized first.
|
||||
- Putting local image paths inside Markdown like ``. `--markdown` does not auto-upload those paths.
|
||||
- **Using local file paths inside Markdown image syntax** (e.g. ``) with `--markdown`. Local paths are not auto-uploaded and will not render as an image. Pre-upload via `images.create` to get an `image_key` instead.
|
||||
- Using `--content` without making the JSON match the effective `--msg-type`.
|
||||
- Explicitly setting `--msg-type` to something that conflicts with `--text`, `--markdown`, or media flags.
|
||||
- Mixing `--text`, `--markdown`, or `--content` with media flags in one command.
|
||||
|
||||
## `content` Format Reference
|
||||
|
||||
| `msg_type` | Example `content` |
|
||||
|----------|-------------|
|
||||
| `text` | `{"text":"Hello <at user_id=\"ou_xxx\">name</at>"}` |
|
||||
| `post` | `{"zh_cn":{"title":"Title","content":[[{"tag":"text","text":"Body"}]]}}` |
|
||||
| `image` | `{"image_key":"img_xxx"}` |
|
||||
| `file` | `{"file_key":"file_xxx"}` |
|
||||
| `audio` | `{"file_key":"file_xxx"}` |
|
||||
| `media` | `{"file_key":"file_xxx","image_key":"img_xxx"}` (video; `image_key` is the cover from `--video-cover` — **required**) |
|
||||
| `share_chat` | `{"chat_id":"oc_xxx"}` |
|
||||
| `share_user` | `{"user_id":"ou_xxx"}` |
|
||||
| `interactive` | Card JSON (see Feishu interactive card documentation) |
|
||||
|
||||
## Return Value
|
||||
|
||||
```json
|
||||
{
|
||||
"message_id": "om_xxx",
|
||||
"chat_id": "oc_xxx",
|
||||
"create_time": "1234567890"
|
||||
}
|
||||
```
|
||||
|
||||
## @Mention Format
|
||||
|
||||
The `<at>` syntax differs by message type. The shortcut only normalizes mentions for `text` and `post`; `interactive` card content is passed through verbatim, so cards must use the card-native syntax below.
|
||||
|
||||
### `text`
|
||||
|
||||
- `<at user_id="ou_xxx">name</at>` — the inner text is the mentioned user's display name and is optional (`<at user_id="ou_xxx"></at>` also works)
|
||||
- @all: `<at user_id="all"></at>`
|
||||
|
||||
### `post`
|
||||
|
||||
- Inside a `text` or `md` element, the same inline form as `text` works: `<at user_id="ou_xxx">name</at>`
|
||||
- Or use a dedicated `at` element node: `{"tag":"at","user_id":"ou_xxx"}` (use `"all"` to mention everyone)
|
||||
|
||||
### `interactive` (card)
|
||||
|
||||
Card content is **not** normalized — use the card-native `<at>` syntax inside a `lark_md` / `markdown` element:
|
||||
|
||||
- single user by open_id: `<at id=ou_xxx></at>`
|
||||
- multiple users: `<at ids=ou_xxx1,ou_xxx2></at>`
|
||||
- by email: `<at email=user@example.com></at>`
|
||||
|
||||
## Notes
|
||||
|
||||
- `--chat-id` and `--user-id` are mutually exclusive; you must provide exactly one
|
||||
- `--content` must be valid JSON
|
||||
- When using `--content`, you are responsible for making the JSON structure match the effective `msg_type`
|
||||
- `--image`/`--file`/`--video`/`--audio` support existing keys, URLs, and cwd-relative local file paths; the shortcut uploads local paths and URLs first, then sends the message; both the upload and send steps use the same identity (UAT when `--as user`, TAT when `--as bot`)
|
||||
- If the provided media value starts with `img_` or `file_`, it is treated as an existing key and used directly
|
||||
- `--markdown` always sends `msg_type=post`, even if you do not explicitly set `--msg-type post`
|
||||
- If you explicitly set `--msg-type` and it conflicts with the chosen content flag, validation fails
|
||||
- When using `--video`, `--video-cover` is required as the video cover
|
||||
- `--dry-run` uses placeholder image keys for remote Markdown images and placeholder media keys for local uploads
|
||||
- Failures return an error code and message
|
||||
- `--as user` uses a user access token (UAT) and requires the `im:message.send_as_user` and `im:message` scopes; the message is sent as the authorized end user
|
||||
- `--as bot` uses a tenant access token (TAT) and requires the `im:message:send_as_bot` scope
|
||||
- When sending as a bot, the app must already be in the target group or already have a direct-message relationship with the target user
|
||||
- When using `--markdown` with images, pre-uploading via `images.create` to obtain an `image_key` is recommended for reliability; remote URLs may be auto-resolved at runtime, but if download/upload fails the image is removed with a warning; local paths are not supported
|
||||
- **`--content` JSON shape per `msg_type`**: `text` `{"text":"..."}` · `post` `{"zh_cn":{"title":"...","content":[[{"tag":"text","text":"..."}]]}}` · `image` `{"image_key":"img_xxx"}` · `file`/`audio` `{"file_key":"file_xxx"}` · `media` `{"file_key":"...","image_key":"<cover, required>"}` · `share_chat` `{"chat_id":"oc_xxx"}` · `share_user` `{"user_id":"ou_xxx"}` · `interactive` = card JSON.
|
||||
- **Return value**: `{"message_id":"om_xxx","chat_id":"oc_xxx","create_time":"..."}`.
|
||||
- **`--idempotency-key`**: the same key sends only one message within 1 hour.
|
||||
|
||||
@@ -1,299 +1,23 @@
|
||||
# im reactions
|
||||
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first to understand authentication, global parameters, and safety rules.
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first for authentication, global parameters, and safety rules.
|
||||
|
||||
> **Heads-up — don't reach for `batch_query` by default.** The four message-pulling shortcuts (`+messages-mget`, `+chat-messages-list`, `+messages-search`, `+threads-messages-list`) already call `im.reactions.batch_query` automatically and attach the result as a `reactions` block on each message (replies inside `thread_replies` included). Use those shortcuts for any "read reactions of messages I'm already pulling" task. Reach for the raw `batch_query` API only when you have a standalone `message_id` outside that pull flow. See the main [message enrichment](lark-im-message-enrichment.md) for the contract.
|
||||
Maps to `lark-cli schema im.reactions.*` (no typed shortcut). **Run `lark-cli schema im.reactions.create|list|delete|batch_query --format pretty` for the authoritative parameters, the `reaction_type` emoji-key set, and field shapes.** This file covers only what `schema` cannot.
|
||||
|
||||
This reference is the shared annotation target for the IM reaction APIs:
|
||||
## Gotchas
|
||||
|
||||
- `im.reactions.create`
|
||||
- `im.reactions.list`
|
||||
- `im.reactions.delete`
|
||||
- `im.reactions.batch_query`
|
||||
- **Caller must be in the conversation** for `create`, `list`, and `delete`. `batch_query` has no this constraint — it is the only reactions method where bot or user can query messages from conversations they are not currently in.
|
||||
- **`delete` can only remove reactions added by itself** (the calling identity). You cannot delete another user's or another bot's reaction, even as bot admin.
|
||||
- **`operator_id` is the `app_id` when `operator_type=app`**, not an `ou_xxx` open_id. The type of the user-operator ID is controlled by `user_id_type` (default: `open_id`).
|
||||
- **Don't call `batch_query` directly for messages you are already pulling.** The four message shortcuts (`+messages-mget`, `+chat-messages-list`, `+messages-search`, `+threads-messages-list`) call `batch_query` automatically and attach the result as a `reactions` block on each message (including replies inside `thread_replies`). Use the raw `batch_query` only for standalone `message_id`s outside that pull flow. See [message enrichment](lark-im-message-enrichment.md) for the contract.
|
||||
- **`batch_query` pagination is per-message, not global.** Each entry in `queries[]` carries its own `page_token`. To page a specific message, set `queries[].page_token` for that entry; other entries in the same request start fresh. `success_msg_reaction_details[].has_more` indicates whether a given message has more pages.
|
||||
- **`batch_query` `page_size_per_message` is capped at 10** (range 1..10 per schema). For high-reaction messages, expect multiple round trips per message.
|
||||
- **`list` `page_size` is capped at 50** (schema: `range: <=50`, default 20).
|
||||
- **`reaction_type` filter naming asymmetry across methods:**
|
||||
- `create` / `delete` response: nested as `reaction_type.emoji_type`
|
||||
- `list` request filter: flat string param `reaction_type`; response: nested `reaction_type.emoji_type`
|
||||
- `batch_query` request filter: flat string `reaction_type`; detail items: `message_reaction_items[].emoji_type` (flat, not nested); aggregated counts: `reaction_count[].reaction_type` (flat)
|
||||
|
||||
It focuses on:
|
||||
## HELP-GAP — not yet in `--help`/schema; keep until CLI adds it
|
||||
|
||||
- What each reaction method does
|
||||
- The request/response shape you need when calling the raw API commands
|
||||
- The complete `emoji_type` list used in reaction payloads and filters
|
||||
|
||||
> **Important:** These raw API commands accept structured input through `--params '<json>'` and `--data '<json>'`. They do not expose typed flags such as `--message-id` or `--reaction-type` directly.
|
||||
|
||||
## Command Overview
|
||||
|
||||
| Method | HTTP | Path | Purpose |
|
||||
|---|---|---|---|
|
||||
| `im.reactions.create` | `POST` | `/open-apis/im/v1/messages/{message_id}/reactions` | Add a reaction to one message |
|
||||
| `im.reactions.list` | `GET` | `/open-apis/im/v1/messages/{message_id}/reactions` | List reaction records on one message |
|
||||
| `im.reactions.delete` | `DELETE` | `/open-apis/im/v1/messages/{message_id}/reactions/{reaction_id}` | Delete one specific reaction record |
|
||||
| `im.reactions.batch_query` | `POST` | `/open-apis/im/v1/messages/reactions/batch_query` | Query reactions for multiple messages in one request |
|
||||
|
||||
## Common Notes
|
||||
|
||||
- `message_id` is always an IM message ID such as `om_xxx`
|
||||
- `reaction_id` is the unique record ID returned after a reaction is added
|
||||
- `reaction_type.emoji_type` is the enum-like emoji identifier used by both write and read APIs
|
||||
- Reaction APIs return **reaction records**, not only aggregated counts
|
||||
- When the operator is a human user, the returned ID type may depend on `user_id_type`
|
||||
|
||||
## Inspect Schema
|
||||
|
||||
```bash
|
||||
lark-cli schema im.reactions
|
||||
lark-cli schema im.reactions.create --format pretty
|
||||
lark-cli schema im.reactions.list --format pretty
|
||||
lark-cli schema im.reactions.delete --format pretty
|
||||
```
|
||||
|
||||
If your local build has already exposed the batch API in `schema`, also check:
|
||||
|
||||
```bash
|
||||
lark-cli schema im.reactions.batch_query --format pretty
|
||||
```
|
||||
|
||||
## create
|
||||
|
||||
Add a reaction to one message.
|
||||
|
||||
```bash
|
||||
lark-cli im reactions create \
|
||||
--params '{"message_id":"om_xxx"}' \
|
||||
--data '{"reaction_type":{"emoji_type":"SMILE"}}'
|
||||
```
|
||||
|
||||
### Request
|
||||
|
||||
- `--params.message_id`: required message ID
|
||||
- `--data.reaction_type.emoji_type`: required emoji type
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"reaction_id": "ZCaCIjUBVVWSrm5L-3ZTw_xxx",
|
||||
"operator": {
|
||||
"operator_id": "ou_xxx",
|
||||
"operator_type": "user"
|
||||
},
|
||||
"action_time": "1663054162546",
|
||||
"reaction_type": {
|
||||
"emoji_type": "SMILE"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## list
|
||||
|
||||
List reaction records on one message.
|
||||
|
||||
```bash
|
||||
lark-cli im reactions list --params '{"message_id":"om_xxx"}'
|
||||
lark-cli im reactions list --params '{"message_id":"om_xxx","reaction_type":"SMILE"}'
|
||||
lark-cli im reactions list --params '{"message_id":"om_xxx","page_size":50}'
|
||||
lark-cli im reactions list --params '{"message_id":"om_xxx","page_token":"<PAGE_TOKEN>"}'
|
||||
lark-cli im reactions list --params '{"message_id":"om_xxx","user_id_type":"open_id"}'
|
||||
```
|
||||
|
||||
### Request Parameters (`--params`)
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|---|---|---|
|
||||
| `message_id` | Yes | Message ID (`om_xxx`) |
|
||||
| `reaction_type` | No | Filter by one emoji type such as `SMILE` or `LAUGH` |
|
||||
| `page_size` | No | Number of records per page. Default is 20 |
|
||||
| `page_token` | No | Pagination token from the previous page |
|
||||
| `user_id_type` | No | Returned operator ID type when `operator_type=user`: `open_id`, `union_id`, or `user_id` |
|
||||
|
||||
### Response Shape
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"reaction_id": "ZCaCIjUBVVWSrm5L-3ZTw_xxx",
|
||||
"operator": {
|
||||
"operator_id": "ou_xxx",
|
||||
"operator_type": "user"
|
||||
},
|
||||
"action_time": "1663054162546",
|
||||
"reaction_type": {
|
||||
"emoji_type": "SMILE"
|
||||
}
|
||||
}
|
||||
],
|
||||
"has_more": true,
|
||||
"page_token": "YhljsPiGfUgnVAg9urvRFd-BvSqRLxxxx"
|
||||
}
|
||||
```
|
||||
|
||||
### Top-Level Fields
|
||||
|
||||
| Field | Type | Meaning |
|
||||
|---|---|---|
|
||||
| `items` | `array<object>` | Reaction records for the current page |
|
||||
| `has_more` | `boolean` | Whether more pages are available |
|
||||
| `page_token` | `string` | Token for the next page when `has_more=true` |
|
||||
|
||||
### `items[]` Fields
|
||||
|
||||
| Field | Type | Meaning |
|
||||
|---|---|---|
|
||||
| `reaction_id` | `string` | Unique ID of this reaction record |
|
||||
| `operator` | `object` | Identity of the user or app that added the reaction |
|
||||
| `action_time` | `string` | Unix timestamp in milliseconds |
|
||||
| `reaction_type` | `object` | Reaction payload. The key field is `emoji_type` |
|
||||
|
||||
### `operator` Fields
|
||||
|
||||
| Field | Type | Meaning |
|
||||
|---|---|---|
|
||||
| `operator.operator_id` | `string` | Operator ID. If `operator_type=user`, the returned ID type follows `user_id_type`; if `operator_type=app`, this is the app ID |
|
||||
| `operator.operator_type` | `string` | `user` or `app` |
|
||||
|
||||
## delete
|
||||
|
||||
Delete one specific reaction record from one message.
|
||||
|
||||
```bash
|
||||
lark-cli im reactions delete \
|
||||
--params '{"message_id":"om_xxx","reaction_id":"ZCaCIjUBVVWSrm5L-3ZTw_xxx"}'
|
||||
```
|
||||
|
||||
### Request
|
||||
|
||||
- `--params.message_id`: required message ID
|
||||
- `--params.reaction_id`: required reaction record ID
|
||||
|
||||
### Response
|
||||
|
||||
The response shape is similar to `create`, and usually echoes:
|
||||
|
||||
- `reaction_id`
|
||||
- `operator`
|
||||
- `action_time`
|
||||
- `reaction_type.emoji_type`
|
||||
|
||||
## batch_query
|
||||
|
||||
Query reactions for multiple messages in one request.
|
||||
|
||||
```bash
|
||||
lark-cli im reactions batch_query \
|
||||
--params '{"user_id_type":"open_id"}' \
|
||||
--data '{
|
||||
"queries":[
|
||||
{"message_id":"om_xxx"},
|
||||
{"message_id":"om_yyy","page_token":"<PAGE_TOKEN>"}
|
||||
],
|
||||
"page_size_per_message":10,
|
||||
"reaction_type":"LAUGH"
|
||||
}'
|
||||
```
|
||||
|
||||
### Request
|
||||
|
||||
#### `--params`
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|---|---|---|
|
||||
| `user_id_type` | No | Returned user ID type in operator info: `open_id`, `union_id`, or `user_id` |
|
||||
|
||||
#### `--data`
|
||||
|
||||
| Field | Required | Description |
|
||||
|---|---|---|
|
||||
| `queries` | Yes | Array of target messages |
|
||||
| `queries[].message_id` | No | Message ID to query |
|
||||
| `queries[].page_token` | No | Continuation token for that message |
|
||||
| `page_size_per_message` | No | Max reactions returned per message |
|
||||
| `reaction_type` | No | Filter by one emoji type |
|
||||
|
||||
### Response
|
||||
|
||||
The meta definition contains three top-level result groups:
|
||||
|
||||
| Field | Meaning |
|
||||
|---|---|
|
||||
| `success_msg_reaction_details` | Per-message reaction detail records |
|
||||
| `success_msg_reaction_counts` | Per-message aggregated reaction counts |
|
||||
| `fail_msg_reaction_details` | Query failures for individual messages |
|
||||
|
||||
#### `success_msg_reaction_details`
|
||||
|
||||
Each `message_reaction_items[]` element includes:
|
||||
|
||||
- `reaction_id`
|
||||
- `operator`
|
||||
- `action_time`
|
||||
- `emoji_type`
|
||||
|
||||
#### `success_msg_reaction_counts`
|
||||
|
||||
Each aggregated count record includes:
|
||||
|
||||
- `message_id`
|
||||
- `reaction_count[].reaction_type`
|
||||
- `reaction_count[].count`
|
||||
|
||||
#### `fail_msg_reaction_details`
|
||||
|
||||
Each failed message record includes:
|
||||
|
||||
- `message_id`
|
||||
- `fail_reason`
|
||||
|
||||
Supported `fail_reason` values from meta:
|
||||
|
||||
- `invalid`
|
||||
- `invalid_page_token`
|
||||
- `no_permission`
|
||||
|
||||
## `emoji_type` Field
|
||||
|
||||
Reaction emoji identifiers are used in slightly different field names across the APIs:
|
||||
|
||||
- `im.reactions.create`: request and response use `reaction_type.emoji_type`
|
||||
- `im.reactions.list`: request filter uses `reaction_type`, response uses `reaction_type.emoji_type`
|
||||
- `im.reactions.delete`: response uses `reaction_type.emoji_type`
|
||||
- `im.reactions.batch_query`: request filter uses top-level `reaction_type`, detail results use `message_reaction_items[].emoji_type`, aggregated results use `reaction_count[].reaction_type`
|
||||
|
||||
## Complete `emoji_type` List
|
||||
|
||||
The following list is synchronized from the official Feishu reaction emoji documentation:
|
||||
|
||||
- Source page: `https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message-reaction/emojis-introduce`
|
||||
- Markdown source: `https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message-reaction/emojis-introduce.md`
|
||||
|
||||
Current count in the fetched source: `185`.
|
||||
|
||||
```text
|
||||
OK, THUMBSUP, THANKS, MUSCLE, FINGERHEART, APPLAUSE, FISTBUMP, JIAYI
|
||||
DONE, SMILE, BLUSH, LAUGH, SMIRK, LOL, FACEPALM, LOVE
|
||||
WINK, PROUD, WITTY, SMART, SCOWL, THINKING, SOB, CRY
|
||||
ERROR, NOSEPICK, HAUGHTY, SLAP, SPITBLOOD, TOASTED, GLANCE, DULL
|
||||
INNOCENTSMILE, JOYFUL, WOW, TRICK, YEAH, ENOUGH, TEARS, EMBARRASSED
|
||||
KISS, SMOOCH, DROOL, OBSESSED, MONEY, TEASE, SHOWOFF, COMFORT
|
||||
CLAP, PRAISE, STRIVE, XBLUSH, SILENT, WAVE, WHAT, FROWN
|
||||
SHY, DIZZY, LOOKDOWN, CHUCKLE, WAIL, CRAZY, WHIMPER, HUG
|
||||
BLUBBER, WRONGED, HUSKY, SHHH, SMUG, ANGRY, HAMMER, SHOCKED
|
||||
TERROR, PETRIFIED, SKULL, SWEAT, SPEECHLESS, SLEEP, DROWSY, YAWN
|
||||
SICK, PUKE, BETRAYED, HEADSET, EatingFood, MeMeMe, Sigh, Typing
|
||||
Lemon, Get, LGTM, OnIt, OneSecond, VRHeadset, YouAreTheBest, SALUTE
|
||||
SHAKE, HIGHFIVE, UPPERLEFT, ThumbsDown, SLIGHT, TONGUE, EYESCLOSED, RoarForYou
|
||||
CALF, BEAR, BULL, RAINBOWPUKE, ROSE, HEART, PARTY, LIPS
|
||||
BEER, CAKE, GIFT, CUCUMBER, Drumstick, Pepper, CANDIEDHAWS, BubbleTea
|
||||
Coffee, Yes, No, OKR, CheckMark, CrossMark, MinusOne, Hundred
|
||||
AWESOMEN, Pin, Alarm, Loudspeaker, Trophy, Fire, BOMB, Music
|
||||
XmasTree, Snowman, XmasHat, FIREWORKS, 2022, REDPACKET, FORTUNE, LUCK
|
||||
FIRECRACKER, StickyRiceBalls, HEARTBROKEN, POOP, StatusFlashOfInspiration, 18X, CLEAVER, Soccer
|
||||
Basketball, GeneralDoNotDisturb, Status_PrivateMessage, GeneralInMeetingBusy, StatusReading, StatusInFlight, GeneralBusinessTrip, GeneralWorkFromHome
|
||||
StatusEnjoyLife, GeneralTravellingCar, StatusBus, GeneralSun, GeneralMoonRest, MoonRabbit, Mooncake, JubilantRabbit
|
||||
TV, Movie, Pumpkin, BeamingFace, Delighted, ColdSweat, FullMoonFace, Partying
|
||||
GoGoGo, ThanksFace, SaluteFace, Shrug, ClownFace, HappyDragon
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [lark-im](../SKILL.md) - all IM commands
|
||||
- [lark-shared](../../lark-shared/SKILL.md) - authentication and global parameters
|
||||
- Official emoji doc: `https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message-reaction/emojis-introduce`
|
||||
- **Full `emoji_type` key list (185 values):** schema only links to the Feishu docs page (`https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/im-v1/message-reaction/emojis-introduce`); it does not enumerate the values inline. Keys include: `OK THUMBSUP THANKS MUSCLE FINGERHEART APPLAUSE FISTBUMP JIAYI DONE SMILE BLUSH LAUGH SMIRK LOL FACEPALM LOVE WINK PROUD WITTY SMART SCOWL THINKING SOB CRY ERROR NOSEPICK HAUGHTY SLAP SPITBLOOD TOASTED GLANCE DULL INNOCENTSMILE JOYFUL WOW TRICK YEAH ENOUGH TEARS EMBARRASSED KISS SMOOCH DROOL OBSESSED MONEY TEASE SHOWOFF COMFORT CLAP PRAISE STRIVE XBLUSH SILENT WAVE WHAT FROWN SHY DIZZY LOOKDOWN CHUCKLE WAIL CRAZY WHIMPER HUG BLUBBER WRONGED HUSKY SHHH SMUG ANGRY HAMMER SHOCKED TERROR PETRIFIED SKULL SWEAT SPEECHLESS SLEEP DROWSY YAWN SICK PUKE BETRAYED HEADSET EatingFood MeMeMe Sigh Typing Lemon Get LGTM OnIt OneSecond VRHeadset YouAreTheBest SALUTE SHAKE HIGHFIVE UPPERLEFT ThumbsDown SLIGHT TONGUE EYESCLOSED RoarForYou CALF BEAR BULL RAINBOWPUKE ROSE HEART PARTY LIPS BEER CAKE GIFT CUCUMBER Drumstick Pepper CANDIEDHAWS BubbleTea Coffee Yes No OKR CheckMark CrossMark MinusOne Hundred AWESOMEN Pin Alarm Loudspeaker Trophy Fire BOMB Music XmasTree Snowman XmasHat FIREWORKS 2022 REDPACKET FORTUNE LUCK FIRECRACKER StickyRiceBalls HEARTBROKEN POOP StatusFlashOfInspiration 18X CLEAVER Soccer Basketball GeneralDoNotDisturb Status_PrivateMessage GeneralInMeetingBusy StatusReading StatusInFlight GeneralBusinessTrip GeneralWorkFromHome StatusEnjoyLife GeneralTravellingCar StatusBus GeneralSun GeneralMoonRest MoonRabbit Mooncake JubilantRabbit TV Movie Pumpkin BeamingFace Delighted ColdSweat FullMoonFace Partying GoGoGo ThanksFace SaluteFace Shrug ClownFace HappyDragon`
|
||||
|
||||
@@ -1,115 +1,15 @@
|
||||
# im +threads-messages-list
|
||||
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first to understand authentication, global parameters, and safety rules.
|
||||
> **Prerequisite:** Read [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) first for authentication, global parameters, and safety rules.
|
||||
|
||||
Fetch the reply message list inside a thread. When `im +chat-messages-list` returns messages that include a `thread_id` field, use this command to inspect all replies in that thread.
|
||||
Maps to `lark-cli im +threads-messages-list`. **Run `lark-cli im +threads-messages-list --help` for the authoritative flags (`--thread` / `--order` / `--page-size` / `--page-token` / `--no-reactions` / `--download-resources` / `--as` / `--format`), the accepted `om_xxx`/`omt_xxx` input, and page-size range (1-500).** This file covers only what `--help` cannot.
|
||||
|
||||
By default each reply also carries a `reactions` block (counts + details from `im.reactions.batch_query`) when the server has reactions for it, and `update_time` for messages that were actually edited. Pass `--no-reactions` to skip the extra round-trip. Pass `--download-resources` to additionally download message resources (image/file/audio/video/media + post-embedded, excluding stickers) into `./lark-im-resources/` and attach a `resources` block — off by default, no extra requests when omitted. See [message enrichment](lark-im-message-enrichment.md) for the full contract.
|
||||
Supports both `--as user` (default) and `--as bot`. Enriches replies with reactions / `update_time` per [message enrichment](lark-im-message-enrichment.md).
|
||||
|
||||
This skill maps to the shortcut: `lark-cli im +threads-messages-list` (internally calls `GET /open-apis/im/v1/messages` with `container_id_type=thread` to fetch thread messages).
|
||||
## Gotchas
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Get thread replies (ascending by time by default, table output)
|
||||
lark-cli im +threads-messages-list --thread omt_xxx
|
||||
|
||||
# Reverse chronological order (latest first)
|
||||
lark-cli im +threads-messages-list --thread omt_xxx --order desc
|
||||
|
||||
# Control page size
|
||||
lark-cli im +threads-messages-list --thread omt_xxx --page-size 20
|
||||
|
||||
# Pagination
|
||||
lark-cli im +threads-messages-list --thread omt_xxx --page-token <PAGE_TOKEN>
|
||||
|
||||
# Output format options
|
||||
lark-cli im +threads-messages-list --thread omt_xxx --format pretty
|
||||
lark-cli im +threads-messages-list --thread omt_xxx --format table
|
||||
lark-cli im +threads-messages-list --thread omt_xxx --format csv
|
||||
|
||||
# View as a bot
|
||||
lark-cli im +threads-messages-list --thread omt_xxx --as bot
|
||||
|
||||
# Preview the request without executing it
|
||||
lark-cli im +threads-messages-list --thread omt_xxx --dry-run
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|------|------|------|
|
||||
| `--thread <id>` | Yes | Thread ID (`om_xxx` or `omt_xxx` format) |
|
||||
| `--no-reactions` | No | Skip auto-fetching the `reactions` block |
|
||||
| `--download-resources` | No | Download message resources (image/file/audio/video/media + post-embedded, excluding stickers) into `./lark-im-resources/` and attach a `resources` block. Off by default |
|
||||
| `--order <order>` | No | Sort order: `asc` (default) / `desc` |
|
||||
| `--page-size <n>` | No | Number of items per page (default 50, range 1-500) |
|
||||
| `--page-token <token>` | No | Pagination token for the next page |
|
||||
| `--format <fmt>` | No | Output format: `json` (default) / `pretty` / `table` / `ndjson` / `csv` |
|
||||
| `--as <identity>` | No | Identity type: `user` (default) / `bot` |
|
||||
| `--dry-run` | No | Print the request only, do not execute it |
|
||||
|
||||
## Core Constraints
|
||||
|
||||
### 1. Source of `thread_id`
|
||||
|
||||
`thread_id` (`omt_xxx` or `om_xxx`) comes from the `thread_id` field in results returned by `im +chat-messages-list` or `im +messages-search`. Do not guess a thread ID. Fetch messages first and use the returned value.
|
||||
|
||||
### 2. No time filtering support
|
||||
|
||||
Thread messages do not support `start_time` / `end_time` filtering because of Feishu API limitations. Use pagination and sort order to control the scope.
|
||||
|
||||
### 3. Pagination (`has_more` / `page_token`)
|
||||
|
||||
- When the result includes `has_more=true`, use `page_token` to fetch the next page
|
||||
- If you need the complete thread, keep paginating; if you only need an overview, the first page is often enough
|
||||
|
||||
### 4. Recommended expansion strategy
|
||||
|
||||
| Scenario | Recommended Parameters |
|
||||
|------|---------|
|
||||
| Quickly inspect recent replies | `--order desc --page-size 10` |
|
||||
| Read the full thread in chronological order | `--order asc --page-size 50`, then paginate as needed |
|
||||
| Just confirm whether replies exist | `--order desc --page-size 1` |
|
||||
|
||||
## Usage Scenarios
|
||||
|
||||
### Scenario 1: Expand a thread discovered in group messages
|
||||
|
||||
```bash
|
||||
# Step 1: Fetch group messages and find one that contains thread_id
|
||||
lark-cli im +chat-messages-list --chat-id oc_xxx
|
||||
|
||||
# Step 2: Extract thread_id from the JSON output and fetch thread replies
|
||||
lark-cli im +threads-messages-list --thread omt_xxx
|
||||
```
|
||||
|
||||
### Scenario 2: Paginate through a long thread
|
||||
|
||||
```bash
|
||||
# First page
|
||||
lark-cli im +threads-messages-list --thread omt_xxx
|
||||
|
||||
# If has_more=true is returned, continue with page_token
|
||||
lark-cli im +threads-messages-list --thread omt_xxx --page-token <PAGE_TOKEN>
|
||||
```
|
||||
|
||||
## Resource Rendering
|
||||
|
||||
Thread replies are rendered into human-readable text. Image messages appear as placeholders such as `[Image: img_xxx]`; by default resource binaries are **not** downloaded.
|
||||
|
||||
Pass `--download-resources` to download every eligible resource (image/file/audio/video/media + post-embedded, excluding stickers) into `./lark-im-resources/` in one pass and attach a `resources` block to each reply (see [message enrichment](lark-im-message-enrichment.md#resource-auto-download---download-resources-opt-in)). Otherwise download individual resources manually through `im +messages-resources-download` (see [lark-im-messages-resources-download](lark-im-messages-resources-download.md)).
|
||||
|
||||
## Common Errors and Troubleshooting
|
||||
|
||||
| Symptom | Root Cause | Solution |
|
||||
|---------|---------|---------|
|
||||
| "Invalid thread ID format" | `thread_id` does not start with `om_` or `omt_` | Use a valid `om_xxx` or `omt_xxx` value |
|
||||
| Empty thread result | Wrong thread_id or no replies in the thread | Confirm the thread_id came from `im +chat-messages-list` output |
|
||||
| Permission denied | The user is not authorized or is not a conversation member | Make sure OAuth authorization is complete and the identity is a chat member |
|
||||
|
||||
## References
|
||||
|
||||
- [lark-im](../SKILL.md) - all message-related commands
|
||||
- [lark-im-chat-messages-list](lark-im-chat-messages-list.md) - fetch conversation messages (source of `thread_id`)
|
||||
- [lark-shared](../../lark-shared/SKILL.md) - authentication and global parameters
|
||||
- **Don't guess a `thread_id`.** It comes from the `thread_id` field of [`im +chat-messages-list`](lark-im-chat-messages-list.md) or [`im +messages-search`](lark-im-messages-search.md) output. Passing a plain `om_` message ID also works — the CLI resolves it to that message's thread — but an invented ID returns empty, not an error, so an empty result usually means a wrong/stale ID rather than "no replies".
|
||||
- **No time filtering** — unlike `+chat-messages-list`, threads accept no `--start`/`--end` (Feishu API limitation). Scope the read with `--order` + pagination only: `--order desc --page-size 1` to just confirm replies exist, `--order desc --page-size 10` for recent, `--order asc --page-size 50` (then paginate) for the full thread in order.
|
||||
- **`--order` defaults to `asc`** here (opposite of `+chat-messages-list`'s `desc`). (Note: the flag is `--order`, not `--sort`.)
|
||||
- **Image content is a placeholder, not bytes**: replies render images as `[Image: img_xxx]`; files/audio/video stay as resource keys. Nothing downloads unless you pass `--download-resources` (writes to `./lark-im-resources/`) or use [`im +messages-resources-download`](lark-im-messages-resources-download.md).
|
||||
- Permission denied here usually means the calling identity is **not a member of the parent chat** — thread access is gated by chat membership, not just OAuth scope.
|
||||
|
||||
Reference in New Issue
Block a user