From 8af92fb4462b67ecf1b085b7da20b5be3f542de4 Mon Sep 17 00:00:00 2001 From: review Date: Thu, 30 Jul 2026 18:02:21 +0800 Subject: [PATCH 01/11] docs(im): trim messages-send reference to non-derivable content --- .../references/lark-im-messages-send.md | 253 +++--------------- 1 file changed, 40 insertions(+), 213 deletions(-) diff --git a/skills/lark-im/references/lark-im-messages-send.md b/skills/lark-im/references/lark-im-messages-send.md index 3554e6df71..5bdfdc49a6 100644 --- a/skills/lark-im/references/lark-im-messages-send.md +++ b/skills/lark-im/references/lark-im-messages-send.md @@ -2,9 +2,7 @@ > **Prerequisite:** Before executing this command, ensure [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) has been read once in the current task for authentication, global parameters, and safety rules. Do not reread it if already loaded. -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`). +**Run `lark-cli im +messages-send --help` for the authoritative flags, defaults, enums, mutual-exclusion rules, and examples.** This file covers only what `--help` cannot. ## Safety Constraints @@ -21,203 +19,57 @@ When using `--as user`, the message is sent as the authorized end user and requi ## 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 `![x](https://...)` 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 +`--help` lists what each flag accepts; it does not say which to pick. The decision rule: -- 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 ...`. -- H1 through H6 are preserved outside fenced code blocks. -- Consecutive headings are separated with blank lines during 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 `![x](./a.png)` 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. +- `--markdown` — headings, lists, links, summaries, reports, or Markdown-looking content. +- `--text` — exact plain text: logs, code, indentation-sensitive text, or literal Markdown that must **not** render. +- `--content` — exact `post` JSON, a `post` title, multiple locales, cards, or structures the other flags cannot express. -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. +## Markdown Boundaries -### Image Constraint for `--markdown` +`--markdown` is not a general Markdown renderer. It converts to a Feishu `post` payload, so: -When using `--markdown` with images, prefer pre-uploading via `images.create` and referencing `![alt](img_xxx)` for predictable results. Remote URLs may work but are not guaranteed. - -**Steps:** +- It does **not** promise full CommonMark / GFM support. +- The result always carries a single `zh_cn` locale, and there is **no way to set a `post` title** — use `--msg-type post --content` when you need either. +- H1–H6 survive outside fenced code blocks; fenced code blocks are preserved. +- **Local paths in Markdown image syntax (`![x](./a.png)`) are silently unsupported** — they are not uploaded and will not render. Pre-upload with `im images create` and reference the returned `img_xxx` key instead: ```bash -# 1. Upload image to get image_key lark-cli im images create --data '{"image_type":"message"}' --file ./diagram.png --as bot -# 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![diagram](img_v3_xxxx)\n\nSee above for details.' --as bot +lark-cli im +messages-send --chat-id oc_EXAMPLE_CHAT_ID --markdown $'## Report\n\n![diagram](img_EXAMPLE_KEY)' --as bot ``` -## 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 +- Remote `https://` images are downloaded and uploaded at runtime; **if that fails the image is dropped and only a warning is emitted** — the message still sends without it. -Use `--text` plus `$'...'`: +## Preserving Formatting -```bash -lark-cli im +messages-send --chat-id oc_xxx --text $'Build failed\nBranch: feature/im-docs\nAction: please check logs' --as bot -``` +For multi-line content, indentation, code blocks, tabs, or many quotes/backslashes, use shell ANSI-C quoting `$'...'` so `\n` is written explicitly rather than relying on the shell to carry literal newlines: ```bash -lark-cli im +messages-send --chat-id oc_xxx --text $'```bash\nmake test\nmake lint\n```' --as bot +lark-cli im +messages-send --chat-id oc_EXAMPLE_CHAT_ID --text $'Build failed\nBranch: feature/x\nAction: check logs' --as bot ``` -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' --as bot - -# Send a plain one-line message -lark-cli im +messages-send --chat-id oc_xxx --text "Hello" --as bot - -# Equivalent manual JSON -lark-cli im +messages-send --chat-id oc_xxx --content '{"text":"Hello"}' --as bot - -# Send to a direct message (pass open_id) -lark-cli im +messages-send --user-id ou_xxx --text "Hello" --as bot - -# Send text and add a structured user mention (repeat or comma-separate --mention) -lark-cli im +messages-send --chat-id oc_xxx --text "Please review" --mention ou_xxx --as bot - -# Mention all members with a structured at node -lark-cli im +messages-send --chat-id oc_xxx --text "Release started" --mention-all --as bot - -# Send multi-line text while preserving formatting -lark-cli im +messages-send --chat-id oc_xxx --text $'Line 1\nLine 2\n indented line' --as bot - -# Send Markdown with an image (must pre-upload via images.create) -lark-cli im images create --data '{"image_type":"message"}' --file ./screenshot.png --as bot -# Use the returned image_key in the markdown content -lark-cli im +messages-send --chat-id oc_xxx --markdown $'## Status\n\n![screenshot](img_v3_xxxx)\n\nDone.' --as bot - -# 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"}]]}}' --as bot - -# Send a local image (uploaded automatically before sending) -lark-cli im +messages-send --chat-id oc_xxx --image ./photo.png --as bot - -# Or send directly with an existing image_key -lark-cli im +messages-send --chat-id oc_xxx --image img_xxx --as bot - -# Send a local file (uploaded automatically before sending) -lark-cli im +messages-send --chat-id oc_xxx --file ./report.pdf --as bot - -# 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 --as bot -lark-cli im +messages-send --chat-id oc_xxx --video ./demo.mp4 --video-cover img_xxx --as bot - -# Send a voice message -lark-cli im +messages-send --chat-id oc_xxx --audio ./voice.opus --as bot - -# 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 --as bot - -# Preview the request without executing it -lark-cli im +messages-send --chat-id oc_xxx --markdown $'## Test\n\nhello' --dry-run --as bot - -# ===== Interactive Card ===== -# 🚫 STOP — before constructing ANY interactive card JSON, you MUST read -# card/lark-im-card-create.md and follow its workflow. Do NOT -# hand-write or copy a card payload from the examples below. The JSON passed -# to --content must be the OUTPUT of that workflow. This is non-negotiable. - -# Once the workflow has produced the card JSON, send it: -lark-cli im +messages-send --chat-id oc_xxx --msg-type interactive --content '' --as bot -``` +Use `--text` (not `--markdown`) whenever the receiver must see the bytes exactly as entered. ## 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. -- `--audio` sends a voice message and accepts only Opus audio (`.opus` or Ogg Opus `.ogg`) for local paths and URLs. For `mp3`, `wav`, or other non-Opus audio, convert to `.opus` before using `--audio`, or use `--file` to send the original audio as an attachment. - -## Parameters - -| Parameter | Required | Description | -|------|------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `--chat-id ` | One of two | Group chat ID (`oc_xxx`) | -| `--user-id ` | One of two | User open_id (`ou_xxx`) for direct messages | -| `--text ` | One content option | Plain text message. Use when exact text and formatting preservation matter. Automatically wrapped as `{"text":"..."}` | -| `--markdown ` | 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 ` | 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 ` | One content option | Cwd-relative local image path, URL, or `image_key` (`img_xxx`). Local paths and URLs are uploaded automatically | -| `--file ` | One content option | Cwd-relative local file path, URL, or `file_key` (`file_xxx`). Local paths and URLs are uploaded automatically | -| `--video ` | 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 ` | **Required with `--video`** | Cwd-relative local cover image path, URL, or `image_key` (`img_xxx`). Local paths and URLs are uploaded automatically | -| `--audio ` | One content option | Voice-message audio key, URL, or cwd-relative local path. Local paths and URLs must be Opus (`.opus` or Ogg Opus `.ogg`) | -| `--mention ` | No | Add a structured user mention to a text/post message. Repeat the flag or pass comma-separated IDs; IDs are sent unchanged. | -| `--mention-all` | No | Add a structured @all node to a text/post message. May be combined with `--mention`; do not pass `all` through `--mention`. | -| `--msg-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 ` | No | Idempotency key, max 50 characters; the same key sends only one message within 1 hour | -| `--as ` | 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`. +- Local paths must resolve to a location **inside** the current working directory after `..` **and symlinks** are resolved. When a file sits elsewhere, run the command from that file's directory or copy it in first — there is no flag that relaxes this. +- Upload and send use the **same identity** (UAT for `--as user`, TAT for `--as bot`), so a bot that cannot post to the chat also cannot pre-upload for it. +- **If an upload fails, nothing is sent.** The CLI never silently downgrades content (it will not swap a failed image for a text link). Any degraded form must be shown to the user and re-sent only after their approval. +- `--dry-run` prints **placeholder** image/media keys for remote Markdown images and local uploads — never treat a dry-run key as real. ## 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 `![x](./a.png)`. `--markdown` does not auto-upload those paths. -- **Using local file paths inside Markdown image syntax** (e.g. `![x](./a.png)`) 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. -- Hand-writing `` in text/post content. Pass targets with `--mention` / `--mention-all`; those flags are supported only for text and post messages. +- Choosing `--text` for headings, lists, links, summaries, or reports — use `--markdown`. +- Choosing `--markdown` when exact line breaks, spacing, logs, code, or literal Markdown characters matter — use `--text` with `$'...'`. +- Assuming `--markdown` supports every Markdown feature; it is normalized into a `post` payload first. +- Putting local image paths inside Markdown image syntax — they are not auto-uploaded. +- Hand-writing `` in text/post content. Pass targets with `--mention` / `--mention-all`, which are accepted **only** for text and post messages. ## `content` Format Reference +Needed only with `--content`, which requires you to build JSON matching the effective `msg_type`: + | `msg_type` | Example `content` | |----------|-------------| | `text` | `{"text":"Hello"}`; add mentions through `--mention` / `--mention-all` | @@ -228,51 +80,26 @@ lark-cli im +messages-send --chat-id oc_xxx --msg-type interactive --content ' **`post` vs `interactive`:** `post` is a static rich-text message (title, paragraphs, @mentions, links, inline images) — content is fixed once sent. `interactive` is a card with structured layout and UI components (buttons, forms, selects, date pickers, charts) — content can be updated after sending and supports user-action callbacks. Use `post` for read-only content; use `interactive` when the message needs user interaction or dynamic updates. - -`interactive` cards support callback events (`card.action.trigger`) — see [`lark-im-card-action-reply.md`](lark-im-card-action-reply.md). +| `interactive` | Card JSON — see the gate below | -## Return Value +> **`post` vs `interactive`:** `post` is static rich text (title, paragraphs, @mentions, links, inline images), fixed once sent. `interactive` is a card with UI components (buttons, forms, selects, pickers, charts) that can be updated after sending and supports action callbacks. Use `post` for read-only content; `interactive` only when the message needs interaction or later updates. -```json -{ - "message_id": "om_xxx", - "chat_id": "oc_xxx", - "create_time": "1234567890" -} -``` +**🚫 Interactive cards are gated.** Before constructing ANY card JSON you MUST read [`card/lark-im-card-create.md`](card/lark-im-card-create.md) and follow its workflow. The JSON passed to `--msg-type interactive --content` must be that workflow's output — never hand-written, never copied from an example. This applies every time. Callback handling: [`lark-im-card-action-reply.md`](lark-im-card-action-reply.md). ## Structured @Mentions -- For `--text`, `--markdown`, or `--msg-type post --content`, pass each user ID through `--mention`; the flag is repeatable and also accepts comma-separated IDs. Values are sent unchanged, so do not convert between `user_id` and `open_id`. -- Use `--mention-all` for @all. It may be combined with individual `--mention` values. -- Do not hand-write `` inside text/post content when using these shortcuts. Mention flags reject non-text/post message types. +`--help` documents the flags; it does not document what the response tells you about whether the mentions landed. + - A returned `mention_result.status` of `complete` means all requested individual mention results were attributed. `accepted_unverified` means the service accepted the message/@all node but delivery is not verified. `partial` or `partial_unattributed` means the message may already exist but mention completion is not fully proven. -- Follow `data.mention_result.retry_scope`. When it is `none`, do not resend the original message to repair or verify mentions; an extra remedial message is a new user-approved business action. For `partial_unattributed`, do not guess which user failed. +- Follow `data.mention_result.retry_scope`. When it is `none`, **do not resend the original message** to repair or verify mentions — an extra remedial message is a new user-approved business action. For `partial_unattributed`, do not guess which user failed. +- IDs are sent unchanged, so do not convert between `user_id` and `open_id`. -Interactive cards do not use the shortcut mention flags. Use the card-native `` syntax inside a `lark_md` / `markdown` element: +Interactive cards ignore the shortcut mention flags. Use card-native `` inside a `lark_md` / `markdown` element: -- single user by open_id: `` +- single user: `` - multiple users: `` - by email: `` -## 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 an upload fails (URL media or a markdown image), **nothing is sent** — the command fails with a recovery hint. The CLI never downgrades content on its own (e.g. replacing a failed image with a text link); any degraded form must be shown to the user and re-sent explicitly after their approval -- 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 -- **Interactive cards are gated:** you MUST read and follow the [`card/lark-im-card-create.md`](card/lark-im-card-create.md) workflow to produce the card JSON *before* sending. Do not hand-write or copy a card payload — the JSON given to `--msg-type interactive --content` must be the workflow's output. This applies every time, with no exception +## HELP-GAP — not yet in `--help`/schema; keep until CLI adds it + +- **Required scope per identity:** `--as bot` needs `im:message:send_as_bot`; `--as user` needs `im:message.send_as_user` plus `im:message`. `--help` prints no scope information at all. From b7bc918807cdc1da3c5819a8bbf90da4526b7663 Mon Sep 17 00:00:00 2001 From: review Date: Thu, 30 Jul 2026 18:04:44 +0800 Subject: [PATCH 02/11] docs(im): trim messages-reply reference to non-derivable content --- .../references/lark-im-messages-reply.md | 275 ++++-------------- 1 file changed, 56 insertions(+), 219 deletions(-) diff --git a/skills/lark-im/references/lark-im-messages-reply.md b/skills/lark-im/references/lark-im-messages-reply.md index 969e3695a7..d5b6faeb10 100644 --- a/skills/lark-im/references/lark-im-messages-reply.md +++ b/skills/lark-im/references/lark-im-messages-reply.md @@ -2,9 +2,7 @@ > **Prerequisite:** Before executing this command, ensure [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) has been read once in the current task for authentication, global parameters, and safety rules. Do not reread it if already loaded. -Reply to a specific message. Supports both user identity (`--as user`) and bot identity (`--as bot`). Also supports thread replies. - -This skill maps to the shortcut: `lark-cli im +messages-reply` (internally calls `POST /open-apis/im/v1/messages/:message_id/reply`). +**Run `lark-cli im +messages-reply --help` for the authoritative flags, defaults, enums, mutual-exclusion rules, and examples.** This file covers only what `--help` cannot. ## Safety Constraints @@ -22,256 +20,95 @@ When using `--as user`, the reply is sent as the authorized end user and require ## 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 `![x](https://...)` -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. +`--help` lists what each flag accepts; it does not say which to pick. The decision rule: -### Markdown Boundaries +- `--markdown` — headings, lists, links, summaries, reports, or Markdown-looking content. +- `--text` — exact plain text: logs, code, indentation-sensitive text, or literal Markdown that must **not** render. +- `--content` — exact `post` JSON, a `post` title, multiple locales, cards, or structures the other flags cannot express. -- 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. -- H1 through H6 are preserved outside fenced code blocks. -- Consecutive headings are separated with blank lines during 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. `![x](./a.png)`) 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. +## Markdown Boundaries -If you need a title, multiple locales, cards, unsupported rich structures, or byte-for-byte post JSON control, use `--msg-type post --content ...`. +`--markdown` is not a general Markdown renderer. It converts to a Feishu `post` payload, so: -### Image Constraint for `--markdown` - -When using `--markdown` with images, prefer pre-uploading via `images.create` and referencing `![alt](img_xxx)` for predictable results. Remote URLs may work but are not guaranteed. - -**Steps:** +- It does **not** promise full CommonMark / GFM support. +- The result always carries a single `zh_cn` locale, and there is **no way to set a `post` title** — use `--msg-type post --content` when you need either. +- H1–H6 survive outside fenced code blocks; fenced code blocks are preserved. +- **Local paths in Markdown image syntax (`![x](./a.png)`) are silently unsupported** — they are not uploaded and will not render. Pre-upload with `im images create` and reference the returned `img_xxx` key instead: ```bash -# 1. Upload image to get image_key lark-cli im images create --data '{"image_type":"message"}' --file ./diagram.png --as bot -# 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![diagram](img_v3_xxxx)\n\nSee above for details.' --as bot +lark-cli im +messages-reply --message-id om_EXAMPLE_MESSAGE_ID --markdown $'## Report\n\n![diagram](img_EXAMPLE_KEY)' --as bot ``` -## 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 +- Remote `https://` images are downloaded and uploaded at runtime; **if that fails the image is dropped and only a warning is emitted** — the reply still sends without it. -Use `--text` plus `$'...'`: +## Preserving Formatting -```bash -lark-cli im +messages-reply --message-id om_xxx --text $'Received\nI will check this today.\nOwner: alice' --as bot -``` +For multi-line content, indentation, code blocks, tabs, or many quotes/backslashes, use shell ANSI-C quoting `$'...'` so `\n` is written explicitly rather than relying on the shell to carry literal newlines: ```bash -lark-cli im +messages-reply --message-id om_xxx --text $'```sql\nselect * from jobs;\n```' --as bot +lark-cli im +messages-reply --message-id om_EXAMPLE_MESSAGE_ID --text $'Checked\nBranch: feature/x\nResult: passing' --as bot ``` -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' --as bot - -# Reply with a plain one-line message -lark-cli im +messages-reply --message-id om_xxx --text "Received" --as bot - -# Reply and add a structured user mention -lark-cli im +messages-reply --message-id om_xxx --text "Please review" --mention ou_xxx --as bot - -# Reply and mention all members -lark-cli im +messages-reply --message-id om_xxx --text "Incident update" --mention-all --as bot - -# Equivalent manual JSON -lark-cli im +messages-reply --message-id om_xxx --content '{"text":"Received"}' --as bot - -# 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' --as bot - -# 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 --as bot - -# Reply with Markdown containing an image (must pre-upload via images.create) -lark-cli im images create --data '{"image_type":"message"}' --file ./screenshot.png --as bot -# Use the returned image_key -lark-cli im +messages-reply --message-id om_xxx --markdown $'## Screenshot\n\n![screenshot](img_v3_xxxx)\n\nConfirmed.' --as bot - -# 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"}]]}}' --as bot - -# Reply with a local image (uploaded automatically before sending) -lark-cli im +messages-reply --message-id om_xxx --image ./photo.png --as bot - -# Reply with a local file (uploaded automatically before sending) -lark-cli im +messages-reply --message-id om_xxx --file ./report.pdf --as bot - -# 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 --as bot +Use `--text` (not `--markdown`) whenever the receiver must see the bytes exactly as entered. -# Reply with a voice message -lark-cli im +messages-reply --message-id om_xxx --audio ./voice.opus --as bot +## Thread vs Main Stream -# With an idempotency key -lark-cli im +messages-reply --message-id om_xxx --text "Received" --idempotency-key --as bot +`--reply-in-thread` changes where the reply lands, and that choice is not recoverable after sending: -# Preview the request without executing it -lark-cli im +messages-reply --message-id om_xxx --markdown $'## Test\n\nhello' --dry-run --as bot +- Without it, the reply appears in the **main chat stream** and references the target message — visible to everyone reading the chat. +- With it, the reply appears **only in the target message's thread** and does not surface in the main stream. +- It is meaningful only in chats that support thread replies. In a chat without thread support the flag has no useful effect. -# ===== Interactive Card ===== -# 🚫 STOP — before constructing ANY interactive card JSON, you MUST read -# card/lark-im-card-create.md and follow its workflow. Do NOT -# hand-write or copy a card payload. The JSON passed to --content must be -# the OUTPUT of that workflow. This is non-negotiable. - -# Once the workflow has produced the card JSON, reply with it: -lark-cli im +messages-reply --message-id om_xxx --msg-type interactive --content '' --as bot -``` +When the user says "reply in the thread" / "回复到话题里", pass the flag. When they just say "reply", do not — the main stream is the default and the more visible choice. ## 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. -- `--audio` sends a voice message and accepts only Opus audio (`.opus` or Ogg Opus `.ogg`) for local paths and URLs. For `mp3`, `wav`, or other non-Opus audio, convert to `.opus` before using `--audio`, or use `--file` to send the original audio as an attachment. - -## Parameters - -| Parameter | Required | Description | -|------|------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `--message-id ` | Yes | ID of the message being replied to (`om_xxx`) | -| `--msg-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 ` | One content option | Exact reply content as JSON. The JSON must match the effective `--msg-type` | -| `--text ` | One content option | Plain text reply. Use when exact text and formatting preservation matter | -| `--markdown ` | 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 ` | One content option | Cwd-relative local image path, URL, or `image_key` (`img_xxx`) | -| `--file ` | One content option | Cwd-relative local file path, URL, or `file_key` (`file_xxx`) | -| `--video ` | One content option | Cwd-relative local video path, URL, or `file_key` (`file_xxx`); **must be used together with `--video-cover`** | -| `--video-cover ` | **Required with `--video`** | Cwd-relative local cover image path, URL, or `image_key` (`img_xxx`) | -| `--audio ` | One content option | Voice-message audio key, URL, or cwd-relative local path. Local paths and URLs must be Opus (`.opus` or Ogg Opus `.ogg`) | -| `--mention ` | No | Add a structured user mention to a text/post reply. Repeat the flag or pass comma-separated IDs; IDs are sent unchanged. | -| `--mention-all` | No | Add a structured @all node to a text/post reply. May be combined with `--mention`; do not pass `all` through `--mention`. | -| `--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 ` | No | Idempotency key, max 50 characters; the same key sends only one reply within 1 hour | -| `--as ` | 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`. +- Local paths must resolve to a location **inside** the current working directory after `..` **and symlinks** are resolved. When a file sits elsewhere, run the command from that file's directory or copy it in first — there is no flag that relaxes this. +- Upload and send use the **same identity** (UAT for `--as user`, TAT for `--as bot`), so a bot that cannot post to the chat also cannot pre-upload for it. +- **If an upload fails, nothing is sent.** The CLI never silently downgrades content (it will not swap a failed image for a text link). Any degraded form must be shown to the user and re-sent only after their approval. +- `--dry-run` prints **placeholder** image/media keys for remote Markdown images and local uploads — never treat a dry-run key as real. ## 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 `![x](./a.png)`. `--markdown` does not auto-upload those paths. -- **Using local file paths inside Markdown image syntax** (e.g. `![x](./a.png)`) 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. -- Hand-writing `` in text/post content. Pass targets with `--mention` / `--mention-all`; those flags are supported only for text and post replies. - -## 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" --as bot -``` +- Choosing `--text` for headings, lists, links, summaries, or reports — use `--markdown`. +- Choosing `--markdown` when exact line breaks, spacing, logs, code, or literal Markdown characters matter — use `--text` with `$'...'`. +- Assuming `--markdown` supports every Markdown feature; it is normalized into a `post` payload first. +- Putting local image paths inside Markdown image syntax — they are not auto-uploaded. +- Hand-writing `` in text/post content. Pass targets with `--mention` / `--mention-all`, which are accepted **only** for text and post messages. -The reply appears in the main chat stream and references the target message. +## `content` Format Reference -### Scenario 2: Reply inside a thread +Needed only with `--content`, which requires you to build JSON matching the effective `msg_type`: -```bash -lark-cli im +messages-reply --message-id om_xxx --text "Let me take a look at this" --reply-in-thread --as bot -``` +| `msg_type` | Example `content` | +|----------|-------------| +| `text` | `{"text":"Hello"}`; add mentions through `--mention` / `--mention-all` | +| `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 the gate below | -The reply appears in the target message's thread and does not show up in the main chat stream. +**🚫 Interactive cards are gated.** Before constructing ANY card JSON you MUST read [`card/lark-im-card-create.md`](card/lark-im-card-create.md) and follow its workflow. The JSON passed to `--msg-type interactive --content` must be that workflow's output — never hand-written, never copied from an example. This applies every time. Callback handling: [`lark-im-card-action-reply.md`](lark-im-card-action-reply.md). ## Structured @Mentions -- For `--text`, `--markdown`, or `--msg-type post --content`, pass each user ID through `--mention`; the flag is repeatable and also accepts comma-separated IDs. Values are sent unchanged, so do not convert between `user_id` and `open_id`. -- Use `--mention-all` for @all. It may be combined with individual `--mention` values. -- Do not hand-write `` inside text/post content when using these shortcuts. Mention flags reject non-text/post message types. -- A returned `mention_result.status` of `complete` means all requested individual mention results were attributed. `accepted_unverified` means the service accepted the reply/@all node but delivery is not verified. `partial` or `partial_unattributed` means the reply may already exist but mention completion is not fully proven. -- Follow `data.mention_result.retry_scope`. When it is `none`, do not resend the original reply to repair or verify mentions; an extra remedial message is a new user-approved business action. For `partial_unattributed`, do not guess which user failed. +`--help` documents the flags; it does not document what the response tells you about whether the mentions landed. + +- A returned `mention_result.status` of `complete` means all requested individual mention results were attributed. `accepted_unverified` means the service accepted the message/@all node but delivery is not verified. `partial` or `partial_unattributed` means the reply may already exist but mention completion is not fully proven. +- Follow `data.mention_result.retry_scope`. When it is `none`, **do not resend the original reply** to repair or verify mentions — an extra remedial message is a new user-approved business action. For `partial_unattributed`, do not guess which user failed. +- IDs are sent unchanged, so do not convert between `user_id` and `open_id`. -Interactive cards do not use the shortcut mention flags. Use the card-native `` syntax inside a `lark_md` / `markdown` element: +Interactive cards ignore the shortcut mention flags. Use card-native `` inside a `lark_md` / `markdown` element: -- single user by open_id: `` +- single user: `` - multiple users: `` - by email: `` -## 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 an upload fails (URL media or a markdown image), **nothing is sent** — the command fails with a recovery hint. The CLI never downgrades content on its own (e.g. replacing a failed image with a text link); any degraded form must be shown to the user and re-sent explicitly after their approval -- 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 -- **Interactive cards are gated:** you MUST read and follow the [`card/lark-im-card-create.md`](card/lark-im-card-create.md) workflow to produce the card JSON *before* replying. Do not hand-write or copy a card payload — the JSON given to `--msg-type interactive --content` must be the workflow's output. This applies every time, with no exception +## HELP-GAP — not yet in `--help`/schema; keep until CLI adds it + +- **Required scope per identity:** `--as bot` needs `im:message:send_as_bot`; `--as user` needs `im:message.send_as_user` plus `im:message`. `--help` prints no scope information at all. From a8270bc89beb02a6c63102903d3c7875e5e0b409 Mon Sep 17 00:00:00 2001 From: review Date: Thu, 30 Jul 2026 18:07:30 +0800 Subject: [PATCH 03/11] docs(im): trim feed-groups reference to non-derivable content --- .../lark-im/references/lark-im-feed-groups.md | 418 +++--------------- 1 file changed, 50 insertions(+), 368 deletions(-) diff --git a/skills/lark-im/references/lark-im-feed-groups.md b/skills/lark-im/references/lark-im-feed-groups.md index e50711a40a..092ca8cd2b 100644 --- a/skills/lark-im/references/lark-im-feed-groups.md +++ b/skills/lark-im/references/lark-im-feed-groups.md @@ -2,385 +2,105 @@ > **Prerequisite:** Before executing this command, ensure [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) has been read once in the current task for authentication, global parameters, and safety rules. Do not reread it if already loaded. -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. +Maps to `lark-cli im feed.groups ` (six raw methods, structured input via `--params` / `--data`) plus three typed read shortcuts. **Run `lark-cli schema im.feed.groups. --format pretty` for the authoritative parameters and field shapes, and `lark-cli im +feed-group-list --help` (also `+feed-group-list-item`, `+feed-group-query-item`) for the shortcut flags.** This file covers only what those cannot. -> **Important:** The six raw commands (`create`, `update`, `delete`, `batch_query`, `batch_add_item`, `batch_remove_item`) take structured input through `--params ''` and `--data ''` 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). +## CLI surface split -> **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. +Six methods are raw-only, three are shortcut-only — and the shortcut-only ones have **no raw schema at all**, so `lark-cli schema im.feed.groups.list` fails rather than returning a definition: -## Command Overview - -| Method | Purpose | +| Method | CLI surface | |---|---| -| `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** | - -> HTTP method and path are not duplicated here. For the six raw methods, inspect them with `lark-cli schema im.feed.groups.` when needed; the three shortcut-only read methods (`list`, `list_item`, `batch_query_item`) use typed flags (see their `--help`). - -## Shortcuts - -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 | Preserves and merges both 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` | +| `create` `update` `delete` `batch_query` `batch_add_item` `batch_remove_item` | raw command + `schema` | +| `list` | **only** [`+feed-group-list`](lark-im-feed-group-list.md) | +| `list_item` / `feed.groups.list_item` | **only** [`+feed-group-list-item`](lark-im-feed-group-list-item.md) | +| `batch_query_item` / `feed.groups.batch_query_item` | **only** [`+feed-group-query-item`](lark-im-feed-group-query-item.md) | -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. +**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 discovering IDs you don't have. ## 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`. +- `feed_id` is the identifier of one feed card inside a group. Because only the `chat` feed card type exists in v1 (see `feed_card_type` below), `feed_id` is currently always a chat ID such as `oc_xxx` — there is no separate feed-card ID space to look up. +- Read methods 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** — reading only the live list silently misses removals. +- Rule-based groups (`type=rule`) auto-populate from `feed_group_creator.rules`. Normal 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: > 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"). > 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 - -```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 -``` - -> `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. - -## 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 \ - --data '{"feed_group_creator":{"type":"normal","name":"Releases"}}' - -# Rule-based group: auto-add p2p chats with "release" in their name -lark-cli im feed.groups create --as user \ - --data '{ - "feed_group_creator":{ - "type":"rule", - "name":"Auto: release chats", - "rules":{ - "rules":[ - { - "condition":{ - "match_type":"match_all", - "condition_items":[ - {"type":"chat_type","operator":"is","chat_type":"p2p"}, - {"type":"keyword","operator":"contain","keyword":"release"} - ] - }, - "action":"add" - } - ] - } - } - }' -``` - -### 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" -} -``` - -## update - -Update a feed group's name and/or rules. The `update_fields` array tells the server which fields are being updated. - -> **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. - -```bash -# Rename only -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) -lark-cli im feed.groups update --as user \ - --params '{"feed_group_id":"ofg_xxx"}' \ - --data '{ - "feed_group_updater":{ - "rules":{"rules":[]}, - "update_fields":[2] - } - }' -``` - -### Request - -#### `--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. - -## delete - -Delete one feed group. - -```bash -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. - -## batch_query - -Look up feed groups by an explicit list of IDs. Returns both live and soft-deleted matches. - -```bash -lark-cli im feed.groups batch_query --as user \ - --params '{"user_id_type":"open_id"}' \ - --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. +## Gotchas -### Top-Level Fields +- **`items[].feed_id` is required in practice but not in the schema.** The API definition leaves it optional; every element of `items` must still set it, or the entry is unusable. Always pass `{"feed_id":"oc_xxx","feed_type":"chat"}` per item — for both `batch_add_item` and `batch_remove_item`. +- **`feed_type` is not pre-validated by the CLI.** It is wire-typed as an open string, so a wrong value reaches the server and is rejected at runtime rather than failing locally. +- **`batch_add_item` / `batch_remove_item` fail partially.** Success is not all-or-nothing — check `failed_items[]`; absent or empty means everything succeeded. +- **`update_fields` takes integers, not names.** See the enum below; the string forms are rejected by the server, not by the CLI. -| Field | Type | Meaning | -|---|---|---| -| `groups` | `array` | Live feed groups for the requested IDs | -| `deleted_groups` | `array` | Soft-deleted matches, returned for incremental-sync clients | +## `feed_group_rules` -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, and 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. - -## batch_add_item - -Add feed cards (chats) into one feed group. Partial failures are reported in `failed_items`. - -```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"} - ] - }' -``` - -### Request - -| 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 +The same nested object is used in `feed_group_creator.rules` (create), `feed_group_updater.rules` (update), and read responses under `groups[].rules`: ```json { - "failed_items": [ + "rules": [ { - "item": { "feed_id": "oc_xxx", "feed_type": "chat" }, - "error_code": 240001, - "error_message": "feed_id is invalid" + "condition": { + "match_type": "match_all", + "condition_items": [ + { "type": "chat_type", "operator": "is", "chat_type": "group" }, + { "type": "keyword", "operator": "contain", "keyword": "release" } + ] + }, + "action": "add" } ] } ``` -| Field | Type | Meaning | -|---|---|---| -| `failed_items` | `array` | 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 | - -## batch_remove_item - -Remove feed cards from one feed group. Same request and response shape as `batch_add_item`. - -```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"} - ] - }' -``` - -### Request - -| 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. | - -> 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. +Per-`type` required-field legend — the sibling field consulted depends on `type`, and the others are ignored: -## 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 and enriches each with `chat_name`. There is no raw command — flags and response shape are in the linked shortcut doc. +- `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. -## Enums +## HELP-GAP — not yet in `--help`/schema; keep until CLI adds it -All enum values listed here are exhaustive. +### Enum values -### `feed_group_type` +All values below are exhaustive. None appear in `--help` or `schema` output. -Used in `feed_group_creator.type` and the response `groups[].type`. +**`feed_group_type`** — `feed_group_creator.type` and 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. +**`feed_card_type`** — `items[].feed_type` everywhere a feed card appears: - `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`. +**`feed_group_rule_action`** — `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`. +**`feed_group_rule_cond_match_type`** — `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. +**`feed_group_rule_cond_item_type`** — `condition_items[].type`; determines which sibling field 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`: +**`feed_group_rule_cond_item_operator`** — `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`. +**`feed_group_rule_cond_item_chat_type`** — `condition_items[].chat_type` when `type=chat_type`: - `p2p` - `group` @@ -392,61 +112,23 @@ Used inside `feed_group_rules.rules[].condition.condition_items[].chat_type` whe - `cross_tenant` - `any` -### `update_fields` - -Used inside `feed_group_updater.update_fields`. Multiple values may be listed. +**`update_fields`** — `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_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: - -```json -{ - "rules": [ - { - "condition": { - "match_type": "match_all", - "condition_items": [ - { "type": "chat_type", "operator": "is", "chat_type": "group" }, - { "type": "keyword", "operator": "contain", "keyword": "release" } - ] - }, - "action": "add" - } - ] -} -``` - -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. - -## Permissions +Wire form is integers. 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. -| 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` | +### Scope requirements -The three read methods are shortcut-only: +`--help` prints no scope information, and the two enriching shortcuts need a second scope that is declared only in Go source: -- [`+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`) +- `+feed-group-list` — `im:feed_group_v1:read` +- `+feed-group-list-item` / `+feed-group-query-item` — `im:feed_group_v1:read` **plus** `im:chat:read`, because they always resolve `chat_name` through a follow-up `chats/batch_query` +- The six raw methods — `im:feed_group_v1:write` for `create` / `update` / `delete` / `batch_add_item` / `batch_remove_item`, `im:feed_group_v1:read` for `batch_query` -If a required scope is missing, the CLI surfaces a hint such as `lark-cli auth login --scope "im:feed_group_v1:write"`. +When a scope is missing the CLI surfaces a hint such as `lark-cli auth login --scope "im:feed_group_v1:write"`. ## References - [lark-im](../SKILL.md) — all IM commands -- [lark-shared](../../lark-shared/SKILL.md) — authentication and global parameters From bcd0c492f637a8f4fd4158d6363e27ef4c811550 Mon Sep 17 00:00:00 2001 From: review Date: Thu, 30 Jul 2026 18:09:22 +0800 Subject: [PATCH 04/11] docs(im): trim reactions reference and mark emoji enum as help-gap --- .../lark-im/references/lark-im-reactions.md | 268 ++---------------- 1 file changed, 16 insertions(+), 252 deletions(-) diff --git a/skills/lark-im/references/lark-im-reactions.md b/skills/lark-im/references/lark-im-reactions.md index 9f2b2a6e0e..2fed67adbd 100644 --- a/skills/lark-im/references/lark-im-reactions.md +++ b/skills/lark-im/references/lark-im-reactions.md @@ -2,264 +2,30 @@ > **Prerequisite:** Before executing this command, ensure [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) has been read once in the current task for authentication, global parameters, and safety rules. Do not reread it if already loaded. -> **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. - -This reference is the shared annotation target for the IM reaction APIs: - -- `im.reactions.create` -- `im.reactions.list` -- `im.reactions.delete` -- `im.reactions.batch_query` - -It focuses on: - -- 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 ''` and `--data ''`. 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":""}' -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` | 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` | +Maps to `lark-cli im reactions ` (`create` / `list` / `delete` / `batch_query`) — raw API only, no typed shortcut. **Run `lark-cli schema im.reactions. --format pretty` for the authoritative parameters, field shapes, and value ranges.** This file covers only what `schema` cannot. -### `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. - -`batch_query` covers only the reaction fragments returned for each query. When complete reactions for one message are required, use `im reactions list` and exhaust its pagination instead of treating an empty or partial batch fragment as complete. - -```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_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` +> **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. -Each failed message record includes: +## Gotchas -- `message_id` -- `fail_reason` +- **Reaction APIs return reaction *records*, not just aggregated counts.** `list` yields one entry per reaction event (who, when, which emoji); aggregate counts only appear in `batch_query`'s `success_msg_reaction_counts`. Do not expect a count-only response from `list`. +- **`batch_query` returns fragments, not complete reaction sets.** It covers only the reaction fragment returned for each query. When the *complete* reaction list for one message is required, use `im reactions list` and exhaust its pagination — never treat an empty or partial batch fragment as complete. +- **`operator.operator_id` is not always a user ID.** When `operator_type=app` the value is the **app ID**, not an `ou_xxx` open_id. Only when `operator_type=user` does it follow the request's `user_id_type`. -Supported `fail_reason` values from meta: +## `emoji_type` Field -- `invalid` -- `invalid_page_token` -- `no_permission` +The same emoji identifier appears under **different field names and nesting depths** across the four methods — the single most common source of malformed reaction payloads: -## `emoji_type` Field +- `im.reactions.create` — request and response use `reaction_type.emoji_type` +- `im.reactions.list` — request filter uses flat `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` -Reaction emoji identifiers are used in slightly different field names across the APIs: +## HELP-GAP — not yet in `--help`/schema; keep until CLI adds it -- `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` +`schema` links to the official emoji documentation page but does **not** enumerate the values inline, so the +list below is the only machine-readable copy available offline. Keys are case-sensitive as written. -## Complete `emoji_type` List The following list is synchronized from the official Feishu reaction emoji documentation: @@ -296,6 +62,4 @@ 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` +- [lark-im](../SKILL.md) — all IM commands From 7bf173ec59a1492c064cd3d8f4447cac395890ba Mon Sep 17 00:00:00 2001 From: review Date: Thu, 30 Jul 2026 18:11:09 +0800 Subject: [PATCH 05/11] docs(im): trim messages-search reference to non-derivable content --- .../references/lark-im-messages-search.md | 208 +++--------------- 1 file changed, 36 insertions(+), 172 deletions(-) diff --git a/skills/lark-im/references/lark-im-messages-search.md b/skills/lark-im/references/lark-im-messages-search.md index 3e7d67b2a0..12fdf0920d 100644 --- a/skills/lark-im/references/lark-im-messages-search.md +++ b/skills/lark-im/references/lark-im-messages-search.md @@ -2,143 +2,24 @@ > **Prerequisite:** Before executing this command, ensure [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) has been read once in the current task for authentication, global parameters, and safety rules. Do not reread it if already loaded. -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. +**Run `lark-cli im +messages-search --help` for the authoritative flags, defaults, filter enums, and pagination controls.** 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. Every fetched page is enriched; pass `--no-reactions` to skip the extra round-trip. See [message enrichment](lark-im-message-enrichment.md) for the full contract. +## What the shortcut does for you -> **User identity only** (`--as user`). Bot identity is not supported. +`--help` says it "enriches results via mget and chats batch_query" but not what that means for your next step. Three API calls happen behind one command: -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). +1. search returns matching `message_id` values +2. mget batch-fetches full content for those IDs +3. chat context is batch-looked-up and attached to each message -## Commands +**Consequence:** results are already materialized. Do **not** follow a search with `+messages-mget` to "get the details" — that work is done. -```bash -# Search by keyword -lark-cli im +messages-search --query "project progress" +## Completeness and recovery -# Restrict search to a specific group chat -lark-cli im +messages-search --query "weekly report" --chat-id oc_xxx +`--help` states that only `meta.complete=true` proves completion. What it does not state is what to do when it is false: -# Filter by sender (comma-separated) -lark-cli im +messages-search --query "requirement" --sender ou_xxx,ou_yyy - -# Filter by attachment type -lark-cli im +messages-search --query "report" --include-attachment-type file - -# 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 - -# Preview the request without executing it -lark-cli im +messages-search --query "test" --dry-run -``` - -## Parameters - -| Parameter | Required | Description | -|------|------|------| -| `--query ` | No | Search keyword (may be empty when used with other filters) | -| `--chat-id ` | No | Restrict to chat IDs, comma-separated (`oc_xxx,oc_yyy`) | -| `--sender ` | No | Sender open_ids, comma-separated (`ou_xxx`) | -| `--include-attachment-type ` | No | Attachment filter: `file` / `image` / `video` / `link` | -| `--chat-type ` | No | Chat type: `group` / `p2p` | -| `--sender-type ` | No | Sender type: `user` / `bot` | -| `--exclude-sender-type ` | No | Exclude messages from `user` or `bot` senders | -| `--is-at-me` | No | Only return messages that mention `@me` | -| `--at-chatter-ids ` | No | Filter by @mentioned user open_ids, comma-separated (`ou_xxx,ou_yyy`). Matched results also include messages that `@all` | -| `--start