mirror of
https://github.com/larksuite/cli.git
synced 2026-07-03 22:24:31 +08:00
Compare commits
1 Commits
codex/html
...
docs/opt-l
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bffdd6fdd0 |
@@ -110,122 +110,16 @@ Shortcut 是对常用操作的高级封装(`lark-cli im +<verb> [flags]`)。
|
||||
| [`+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 |
|
||||
| `reactions.*` (add / delete / list / batch_query) | Add, remove, or read emoji reactions on a message; user/bot; caller must be in the conversation, and can only delete its own reactions. Read first: [`lark-im-reactions.md`](references/lark-im-reactions.md) |
|
||||
| `feed.groups.*` (create / update / delete / batch_query / batch_add_item / batch_remove_item) | Manage feed groups (tags) and their member cards; user-only. Read first: [`lark-im-feed-groups.md`](references/lark-im-feed-groups.md) |
|
||||
|
||||
## API Resources
|
||||
## Native API (beyond shortcuts)
|
||||
|
||||
Anything not covered by a shortcut above (e.g. `chats.*`, `chat.members.*`, `chat.managers.*`, `chat.moderation.*`, `chat.user_setting.*`, `messages.delete|forward|merge_forward|read_users|urgent_*`, `threads.forward`, `images.create`, `pins.*`) is callable as a raw API:
|
||||
|
||||
```bash
|
||||
lark-cli schema im.<resource>.<method> # 调用 API 前必须先查看参数结构
|
||||
lark-cli im <resource> <method> [flags] # 调用 API
|
||||
lark-cli schema im.<resource>.<method> # MUST run first — gives params, identity (user/bot/tenant), and required scope
|
||||
lark-cli im <resource> <method> [flags] # then call
|
||||
```
|
||||
|
||||
> **重要**:使用原生 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`.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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)
|
||||
|
||||
### threads
|
||||
|
||||
- `forward` — 转发话题。Identity: supports `user` and `bot`.
|
||||
|
||||
### images
|
||||
|
||||
- `create` — 上传图片。Identity: `bot` only (`tenant_access_token`).
|
||||
|
||||
### pins
|
||||
|
||||
- `create` — Pin 消息。Identity: supports `user` and `bot`.
|
||||
- `delete` — 移除 Pin 消息。Identity: supports `user` and `bot`.
|
||||
- `list` — 获取群内 Pin 消息。Identity: supports `user` and `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)
|
||||
|
||||
## 权限表
|
||||
|
||||
| 方法 | 所需 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` |
|
||||
> **MUST** run `schema` before any native call: it is the live source for the `--data` / `--params` structure, the supported identity (`--as user` vs `--as bot`), owner/admin/tenant constraints, and the required `im:*` scope — do not guess. On a missing-scope error, lark-cli returns a `console_url`; follow the lark-shared permission-handling flow.
|
||||
|
||||
@@ -12,43 +12,24 @@ This skill maps to the shortcut: `lark-cli im +chat-create` (internally calls `P
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Create a private group (default)
|
||||
# Private group (default)
|
||||
lark-cli im +chat-create --name "My Group"
|
||||
|
||||
# Create a public group (name is required and must be at least 2 characters)
|
||||
# Public group (--name required, min 2 chars)
|
||||
lark-cli im +chat-create --name "Public Group" --type public
|
||||
|
||||
# Create a topic chat
|
||||
# Topic chat (a 话题群; see note under Parameters)
|
||||
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 members and set owner (users: up to 50 ou_xxx; bots: up to 5 cli_xxx)
|
||||
lark-cli im +chat-create --name "My Group" --owner ou_xxx --users "ou_aaa,ou_bbb" --bots "cli_aaa"
|
||||
|
||||
# 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
|
||||
# Bot identity, making the creating bot a manager
|
||||
lark-cli im +chat-create --name "My Group" --users "ou_aaa" --as bot --set-bot-manager
|
||||
```
|
||||
|
||||
Run `lark-cli im +chat-create --help` for the full flag list, limits, and types. Single-flag variations (`--as user`, `--description`, `--format json`, `--dry-run` preview, etc.) follow the Parameters table below — `--dry-run` previews the request without creating anything.
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Required | Limits | Description |
|
||||
@@ -106,6 +87,13 @@ 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.
|
||||
|
||||
### Create a group, then 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!"
|
||||
```
|
||||
|
||||
## Output Fields
|
||||
|
||||
| Field | Description |
|
||||
@@ -117,43 +105,13 @@ The authorized user is automatically the group creator and member.
|
||||
| `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
|
||||
|
||||
Format/limit validation (`--name`/`--description`/`--users`/`--bots`/`--owner` length, count, and `ou_xxx`/`cli_xxx` format) is enforced by the CLI and reported verbatim with the fix — see the Parameters table for limits. The two errors needing extra action:
|
||||
|
||||
| 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
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
# 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`).
|
||||
|
||||
This skill maps to the shortcut: `lark-cli im +messages-send` (internally calls `POST /open-apis/im/v1/messages`).
|
||||
Send a message to a group chat (`--chat-id oc_xxx`) or a direct message (`--user-id ou_xxx`). One step, supports `--as user` and `--as bot` (default `bot`). Maps to shortcut `lark-cli im +messages-send` (`POST /open-apis/im/v1/messages`).
|
||||
|
||||
## Safety Constraints
|
||||
|
||||
@@ -16,249 +14,94 @@ Messages sent by this tool are visible to other people. Before calling it, you *
|
||||
|
||||
**Do not** send messages without explicit user approval.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
- `--as bot` (TAT, scope `im:message:send_as_bot`): the message is sent in the app's name — the app must already be in the target chat or have a DM relationship with the target user.
|
||||
- `--as user` (UAT, scopes `im:message.send_as_user` + `im:message`): the message is sent as the authorized end user.
|
||||
|
||||
## Choose The Right Content Flag
|
||||
|
||||
### Default Selection Rule For Agents
|
||||
| Content | Flag | Why |
|
||||
|---|---|---|
|
||||
| Headings, lists, links, summaries, reports (lightweight formatting) | `--markdown` | Best default; converted to Feishu `post` JSON |
|
||||
| Exact plain text — logs, code, indentation, literal Markdown chars that must **not** render | `--text` | Preserves literal text; no conversion |
|
||||
| Exact `post` JSON, a `post` title, multiple locales, cards (`interactive`), `share_*`, or unsupported structures | `--content` | You provide the final JSON; it must match the effective `--msg-type` |
|
||||
| Image / file / video / audio | `--image` / `--file` / `--video` / `--audio` | Uploads URLs or cwd-relative local files automatically |
|
||||
|
||||
- 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.
|
||||
These content flags (and the media flags) are **mutually exclusive** — pass exactly one. Media flags are also mutually exclusive with each other.
|
||||
|
||||
| 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 |
|
||||
## `--markdown` Gotchas
|
||||
|
||||
### `--text` vs `--markdown`
|
||||
`--markdown` always forces `msg_type=post` (single `zh_cn` locale) and normalizes input for Feishu post rendering. Key boundaries (not full CommonMark/GFM):
|
||||
|
||||
- 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.
|
||||
- **No `post` title** — if you need one, use `--content` with `post` JSON.
|
||||
- **Headings rewritten**: `# Title` → `#### Title`; `##`–`######` normalized to `#####` when content has H1–H3. Code blocks preserved; excess blank lines compressed.
|
||||
- **Images**: pre-upload via `im images create` and reference `` for reliable results. Remote `https://` URLs are auto-downloaded+uploaded at runtime (removed with a warning if that fails). Local paths in `` are **not** supported and will not auto-upload.
|
||||
|
||||
## What `--markdown` Really Does
|
||||
## Preserving Exact Formatting
|
||||
|
||||
`--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, tabs, or many backslashes/quotes, use shell ANSI-C quoting `$'...'` so `\n` is written explicitly. Use `--text` + `$'...'` when the receiver must see the text exactly as entered:
|
||||
|
||||
```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
|
||||
|
||||
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`.
|
||||
|
||||
This is especially useful in `zsh` / `bash` because it lets you write `\n` explicitly instead of relying on the shell to preserve literal newlines.
|
||||
|
||||
### When formatting must be preserved
|
||||
|
||||
Use `--text` plus `$'...'`:
|
||||
|
||||
```bash
|
||||
lark-cli im +messages-send --chat-id oc_xxx --text $'Build failed\nBranch: feature/im-docs\nAction: please check logs'
|
||||
```
|
||||
|
||||
```bash
|
||||
lark-cli im +messages-send --chat-id oc_xxx --text $'```bash\nmake test\nmake lint\n```'
|
||||
```
|
||||
|
||||
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
|
||||
# Formatted update (Markdown → post)
|
||||
lark-cli im +messages-send --chat-id oc_xxx --markdown $'## Update\n\n- item 1\n- item 2'
|
||||
|
||||
# Send a plain one-line message
|
||||
# Plain one-line text
|
||||
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)
|
||||
# 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
|
||||
# Exact post structure with a title
|
||||
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)
|
||||
# Markdown with an image (pre-upload first)
|
||||
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\n\nDone.'
|
||||
|
||||
# Media (local files uploaded automatically; --video requires --video-cover)
|
||||
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
|
||||
# Idempotency (same key sends only once within 1 hour) / preview without sending
|
||||
lark-cli im +messages-send --chat-id oc_xxx --text "Hi" --idempotency-key my-id
|
||||
lark-cli im +messages-send --chat-id oc_xxx --markdown $'## Test\n\nhi' --dry-run
|
||||
```
|
||||
|
||||
## Media Input Rules
|
||||
Run `lark-cli im +messages-send --help` for the full flag list and types. Load-bearing rules that `--help` may not make obvious:
|
||||
|
||||
- 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.
|
||||
- **Media paths** accept an existing key (`img_xxx`/`file_xxx`), an `http(s)://` URL, or a **cwd-relative** local path. Absolute paths (e.g. `/tmp/x.png`) are rejected — run from the file's directory and pass `./x.png`. Upload and send use the same identity.
|
||||
- **`--video` must be paired with `--video-cover`** (image key/URL/local path); `--video-cover` cannot be used alone.
|
||||
- **`--msg-type`** is inferred from `--text`/`--markdown`/media flags; explicitly setting a conflicting type fails validation.
|
||||
|
||||
## 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
|
||||
## `content` Format Reference (for `--content`)
|
||||
|
||||
| `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) |
|
||||
| `image` / `file` / `audio` | `{"image_key":"img_xxx"}` / `{"file_key":"file_xxx"}` / `{"file_key":"file_xxx"}` |
|
||||
| `media` (video) | `{"file_key":"file_xxx","image_key":"img_xxx"}` (`image_key` is the **required** cover) |
|
||||
| `share_chat` / `share_user` | `{"chat_id":"oc_xxx"}` / `{"user_id":"ou_xxx"}` |
|
||||
| `interactive` (card) | Card JSON (see Feishu interactive card docs) |
|
||||
|
||||
When using `--content`, you are responsible for making the JSON match the effective `msg_type`.
|
||||
|
||||
## @Mention Format
|
||||
|
||||
The `<at>` syntax differs by message type; the shortcut normalizes mentions for `text` and `post` only — `interactive` cards are passed through verbatim.
|
||||
|
||||
- **`text`** / inside a `post` `text`/`md` element: `<at user_id="ou_xxx">name</at>` (inner name optional); @all: `<at user_id="all"></at>`. In `post` you may also use a node: `{"tag":"at","user_id":"ou_xxx"}` (`"all"` for everyone).
|
||||
- **`interactive` (card)** — card-native syntax inside a `lark_md`/`markdown` element: `<at id=ou_xxx></at>`, multiple `<at ids=ou_1,ou_2></at>`, by email `<at email=user@example.com></at>`.
|
||||
|
||||
## Return Value
|
||||
|
||||
```json
|
||||
{
|
||||
"message_id": "om_xxx",
|
||||
"chat_id": "oc_xxx",
|
||||
"create_time": "1234567890"
|
||||
}
|
||||
{"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
|
||||
|
||||
Reference in New Issue
Block a user