mirror of
https://github.com/larksuite/cli.git
synced 2026-07-03 14:02:43 +08:00
docs(event): slim lark-event references to recipes + gotchas; CLI owns structure
- references (im/vc/minutes/whiteboard): drop key catalogs, field/scope tables and other content already emitted by `event list`/`event schema`; keep only jq recipes, domain semantic gotchas, required params, and per-key identity. ~55% token cut across the four references (4343 -> 1940, cl100k_base). - vc: document the 3 previously-undocumented recording keys, then replace the hardcoded key catalog with a pointer to `event list` to stop doc-vs-code drift. - SKILL.md Topic index: reword rows to advertise recipes/gotchas and route structure lookups to the CLI, matching the slimmed references. Validated by a command-correctness eval (skill+CLI available): 21/21 across the four domains, identity correct per key, whiteboard required `-p whiteboard_id` preserved; the VC recording-transcript key (previously unanswerable from the skill alone) now resolves. Change-Id: I666e9706ae6ef3e2bbcd56a3fb70c4f8be94182c
This commit is contained in:
committed by
liangshuo-1
parent
5a806febc8
commit
cbd729757b
@@ -147,7 +147,7 @@ Lark-defined semantic tags (**not** JSON Schema's standard `format`). Common val
|
||||
|
||||
| Topic | Reference | Coverage |
|
||||
|------------|------------------------------------------------------------------------------|---|
|
||||
| IM | [`references/lark-event-im.md`](references/lark-event-im.md) | Catalog of 11 IM EventKeys + shape notes (flat vs V2 envelope) + `im.message.receive_v1` field gotchas (`sender_id` is open_id only; `.content` is plain text except for `interactive` cards) + common jq recipes (filter by chat_type / message_type / sender) |
|
||||
| VC | [`references/lark-event-vc.md`](references/lark-event-vc.md) | Catalog of 2 VC EventKeys (`vc.meeting.participant_meeting_ended_v1`, `vc.note.generated_v1`) + field reference + source type semantics (meeting only) |
|
||||
| Minutes | [`references/lark-event-minutes.md`](references/lark-event-minutes.md) | Catalog of 1 Minutes EventKey (`minutes.minute.generated_v1`) + field reference + source type semantics (meeting only) |
|
||||
| Whiteboard | [`references/lark-event-whiteboard.md`](references/lark-event-whiteboard.md) | Catalog of 1 Board EventKey (`board.whiteboard.updated_v1`) + per-whiteboard subscription model (requires `-p whiteboard_id=<token>`) + payload field reference (whiteboard_id / operator_ids triple-id) |
|
||||
| IM | [`references/lark-event-im.md`](references/lark-event-im.md) | jq recipes (filter by chat_type / message_type / sender) + payload gotchas (`.content` is pre-rendered text — don't `fromjson`; `sender_id` is open_id; flat vs `.event` envelope) |
|
||||
| VC | [`references/lark-event-vc.md`](references/lark-event-vc.md) | jq recipes (meeting-ended / note / transcript) + behavior gotchas (time conversion, note_source meeting-only, recording batches). All VC keys need `--as user` |
|
||||
| Minutes | [`references/lark-event-minutes.md`](references/lark-event-minutes.md) | jq recipes + enrichment gotcha (`title` may be empty on detail-API failure; `minute_source` meeting-only). `--as user` |
|
||||
| Whiteboard | [`references/lark-event-whiteboard.md`](references/lark-event-whiteboard.md) | jq recipes + subscription gotcha (**required** `-p whiteboard_id`, needs manage access or 403). `--as user\|bot` |
|
||||
|
||||
@@ -2,85 +2,45 @@
|
||||
|
||||
> **Prerequisite:** Read [`../SKILL.md`](../SKILL.md) first for the `event consume` essentials (commands, subprocess contract, jq usage).
|
||||
>
|
||||
> **Heads-up for AI agents**: this key's `.content` is **NOT** the raw OAPI payload shape your training data may suggest. `lark-cli` runs a Process hook (`convertlib`) that flattens the V2 envelope and **pre-renders** `.content` to human-readable text for `text` / `post` / `image` / `file` / `audio` / etc. Only `interactive` (cards) keeps the raw JSON string. Don't blindly `fromjson`.
|
||||
> **The catalog lives in the CLI, not here.** `lark-cli event list` lists all 11 IM EventKeys; `lark-cli event schema <key>` gives any key's fields / types / enums. This file only covers what the schema can't: payload-shape gotchas and ready-to-use jq recipes.
|
||||
|
||||
## Key catalog (11)
|
||||
## Shape: flat vs enveloped
|
||||
|
||||
| EventKey | Purpose |
|
||||
|---|---|
|
||||
| `im.message.receive_v1` | Receive IM messages |
|
||||
| `im.message.message_read_v1` | User read a bot's **p2p** message (group messages don't fire this) |
|
||||
| `im.message.reaction.created_v1` | Reaction added to a message |
|
||||
| `im.message.reaction.deleted_v1` | Reaction removed from a message |
|
||||
| `im.chat.updated_v1` | Chat settings changed (owner, avatar, name, permissions, etc.) |
|
||||
| `im.chat.disbanded_v1` | Chat disbanded |
|
||||
| `im.chat.member.bot.added_v1` | Bot added to a chat |
|
||||
| `im.chat.member.bot.deleted_v1` | Bot removed from a chat |
|
||||
| `im.chat.member.user.added_v1` | User joined a chat (including topic chats) |
|
||||
| `im.chat.member.user.deleted_v1` | User left voluntarily **or** was removed |
|
||||
| `im.chat.member.user.withdrawn_v1` | Pending chat invite withdrawn (inviter canceled; user never actually joined) |
|
||||
`im.message.receive_v1` is the only **flat** key (fields at `.xxx`). The other 10 IM keys are **V2-enveloped** — fields live at `.event.xxx` (e.g. `.event.chat_id`). `event schema <key>` confirms it (its Output Schema nests everything under `event`).
|
||||
|
||||
> **Shape**: `im.message.receive_v1` is the only flat key (fields at `.xxx`); the other 10 are V2-enveloped (fields at `.event.xxx`).
|
||||
## `.content` is pre-rendered — do NOT blindly `fromjson` (`im.message.receive_v1`)
|
||||
|
||||
## Gotchas (`im.message.receive_v1`)
|
||||
`lark-cli` runs a Process hook that **pre-renders `.content` to human-readable text** for every `message_type` except `interactive` (`@mentions` resolved to display names). Only `interactive` (cards) keeps the raw JSON string.
|
||||
|
||||
**sender_id is open_id only**: the event payload carries no display name. Call the contact API separately if you need the sender's name.
|
||||
|
||||
**`.content` shape depends on `message_type`** (this key uses a flat Custom schema; see [`events/im/message_receive.go`](../../../events/im/message_receive.go)):
|
||||
|
||||
| message_type | `.content` shape | How to read |
|
||||
| message_type | `.content` | How to read |
|
||||
|---|---|---|
|
||||
| `text` / `post` / `image` / `file` / `audio` / `sticker` / `share_chat` / `share_user` / `media` / `system` | Human-readable text (convertlib-processed; `@mentions` resolved to display names) | Use `.content` directly |
|
||||
| `interactive` (card) | Raw card JSON string (structured actions can't be losslessly flattened) | `.content \| fromjson` to get the card object |
|
||||
| everything except `interactive` | plain text | use `.content` directly |
|
||||
| `interactive` (card) | raw card JSON string | `.content \| fromjson` |
|
||||
|
||||
**Do not blindly `fromjson`** — for non-interactive messages it fails with `jq: fromjson cannot be applied to "hello"` because `.content` isn't JSON-encoded.
|
||||
Applying `fromjson` to a non-interactive message errors per event (`jq: fromjson cannot be applied to "hello"`) and the consumer **silently drops** it — looks alive, emits nothing.
|
||||
|
||||
**`sender_id` is `open_id` only** — the payload carries no display name; resolve via the contact API if you need one.
|
||||
|
||||
## jq recipes (`im.message.receive_v1`)
|
||||
|
||||
> Default = no `--jq` (stream every message). Use these only when asked to narrow the stream.
|
||||
|
||||
```bash
|
||||
# text: .content is plain text — no fromjson needed
|
||||
lark-cli event consume im.message.receive_v1 --as bot \
|
||||
--jq 'select(.message_type=="text") | .content'
|
||||
|
||||
# interactive: .content is a JSON string — fromjson to parse
|
||||
lark-cli event consume im.message.receive_v1 --as bot \
|
||||
--jq 'select(.message_type=="interactive") | .content | fromjson'
|
||||
```
|
||||
|
||||
## On-demand filter recipes
|
||||
|
||||
> **Default = no `--jq`.** Run `lark-cli event consume im.message.receive_v1 --as bot` to see every message. The recipes below are only for cases where the user has asked to narrow the stream.
|
||||
|
||||
### 1. Filter by chat type (p2p vs group)
|
||||
|
||||
`chat_type` is an enum with values `p2p` / `group`.
|
||||
|
||||
```bash
|
||||
# p2p only (direct messages)
|
||||
lark-cli event consume im.message.receive_v1 --as bot \
|
||||
--jq 'select(.chat_type=="p2p") | {from: .sender_id, msg: .content}'
|
||||
|
||||
# group only
|
||||
# group chats only (chat_type enum: p2p | group)
|
||||
lark-cli event consume im.message.receive_v1 --as bot \
|
||||
--jq 'select(.chat_type=="group") | {chat: .chat_id, from: .sender_id, msg: .content}'
|
||||
```
|
||||
|
||||
### 2. Filter by message type
|
||||
|
||||
```bash
|
||||
# text only — content is plain human-readable text
|
||||
# text messages only — .content is plain text
|
||||
lark-cli event consume im.message.receive_v1 --as bot \
|
||||
--jq 'select(.message_type=="text") | .content'
|
||||
|
||||
# interactive (card) only — parse the card body
|
||||
# interactive cards only — parse the card body
|
||||
lark-cli event consume im.message.receive_v1 --as bot \
|
||||
--jq 'select(.message_type=="interactive") | .content | fromjson'
|
||||
|
||||
# one sender's messages only
|
||||
lark-cli event consume im.message.receive_v1 --as bot \
|
||||
--jq 'select(.sender_id=="ou_xxxx") | {msg_id: .message_id, text: .content}'
|
||||
```
|
||||
|
||||
### 3. Filter by sender (only one user's messages)
|
||||
|
||||
```bash
|
||||
# example: only messages from the given open_id
|
||||
lark-cli event consume im.message.receive_v1 --as bot\
|
||||
--jq 'select(.sender_id=="ou_xxxxxxxxxxxxxxxxxxxxxxxxxx") | {msg_id: .message_id, text: .content}'
|
||||
```
|
||||
|
||||
Get your own open_id via `lark-cli contact +get-user --as user`; other users' via `lark-cli contact +search-user`.
|
||||
Get your own open_id via `lark-cli contact +get-user --as user`; others' via `lark-cli contact +search-user`.
|
||||
|
||||
@@ -1,54 +1,25 @@
|
||||
# Minutes Events
|
||||
|
||||
> **Prerequisite:** Read [`../SKILL.md`](../SKILL.md) first for the `event consume` essentials (commands, subprocess contract, jq usage).
|
||||
>
|
||||
> Catalog & fields live in the CLI: `event list` (the one key `minutes.minute.generated_v1`) and `event schema minutes.minute.generated_v1`. This file only covers what the schema can't: enrichment behavior and recipes. **Requires `--as user`.** Flat output (fields at `.xxx`).
|
||||
|
||||
## Key catalog (1)
|
||||
## Enrichment & degradation (the gotcha)
|
||||
|
||||
| EventKey | Purpose |
|
||||
|---|---|
|
||||
| `minutes.minute.generated_v1` | A minute (妙记) has been generated |
|
||||
The Process hook calls the minutes detail API to enrich `title`. **If that call fails, `title` is left empty** — the base fields (`type`, `event_id`, `timestamp`, `minute_token`, `minute_source`) are always present. So filter on `.title != ""` if you only want enriched events.
|
||||
|
||||
This key uses a **Custom schema** (flat output at `.xxx`) and carries a **PreConsume hook** that auto-subscribes / unsubscribes via OAPI on first / last consumer.
|
||||
`minute_source` comes from the payload directly (survives enrichment failure) and is **only present when the minute originates from a meeting** (`source_type == "meeting"`); for recording / local-upload sources it is absent.
|
||||
|
||||
## Scopes & auth
|
||||
|
||||
| EventKey | Scope | Auth |
|
||||
|---|---|---|
|
||||
| `minutes.minute.generated_v1` | `minutes:minutes.basic:read` | user |
|
||||
|
||||
Requires `--as user`.
|
||||
|
||||
## `minutes.minute.generated_v1`
|
||||
|
||||
### Output fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|---|---|---|
|
||||
| `type` | string | Event type; always `minutes.minute.generated_v1` |
|
||||
| `event_id` | string | Globally unique event ID; safe for deduplication |
|
||||
| `timestamp` | string (timestamp_ms) | Event delivery time (ms timestamp string) |
|
||||
| `minute_token` | string | Minute token |
|
||||
| `title` | string | Minute title (enriched via detail API) |
|
||||
| `minute_source` | object | Minute source metadata; only present when the source is a meeting |
|
||||
| `minute_source.source_type` | string | Source type; only present when the source is a meeting (value: `meeting`) |
|
||||
| `minute_source.source_entity_id` | string | Source entity ID (meeting ID); only present when the source is a meeting |
|
||||
|
||||
### Enrichment & degradation
|
||||
|
||||
The Process hook calls `GET /open-apis/minutes/v1/minutes/{minute_token}` to enrich `title`. If the detail API fails, this field is left empty — the base fields (`type`, `event_id`, `timestamp`, `minute_token`, `minute_source`) are always present.
|
||||
|
||||
`minute_source` is populated from the event payload directly (not the detail API), so it survives enrichment failures. Note: `minute_source` is only present when the minute originates from a meeting; for other sources (e.g. recording, local upload) this field is absent.
|
||||
|
||||
### Example
|
||||
## jq recipes
|
||||
|
||||
```bash
|
||||
lark-cli event consume minutes.minute.generated_v1 --as user
|
||||
|
||||
# Project title and token only (skip events where enrichment failed)
|
||||
# title + token, skipping events where enrichment failed
|
||||
lark-cli event consume minutes.minute.generated_v1 --as user \
|
||||
--jq 'select(.title != "") | {minute_token, title}'
|
||||
|
||||
# Filter by source type
|
||||
# meeting-sourced minutes only
|
||||
lark-cli event consume minutes.minute.generated_v1 --as user \
|
||||
--jq 'select(.minute_source.source_type == "meeting") | {minute_token, title}'
|
||||
```
|
||||
|
||||
@@ -1,94 +1,27 @@
|
||||
# VC Events
|
||||
|
||||
> **Prerequisite:** Read [`../SKILL.md`](../SKILL.md) first for the `event consume` essentials (commands, subprocess contract, jq usage).
|
||||
>
|
||||
> Catalog & fields live in the CLI: `event list` shows the VC keys (meeting-ended, note-generated, recording started / ended / transcript-generated); `event schema <key>` shows each one's fields. This file only covers what the schema can't: behavior gotchas and recipes. **All VC keys require `--as user`.** Flat output (fields at `.xxx`).
|
||||
|
||||
## Key catalog (2)
|
||||
## Behavior gotchas
|
||||
|
||||
| EventKey | Purpose |
|
||||
|---|---|
|
||||
| `vc.meeting.participant_meeting_ended_v1` | A meeting the current user participates in has ended |
|
||||
| `vc.note.generated_v1` | A note has been generated (meeting, recording, upload, etc.) |
|
||||
- **`participant_meeting_ended_v1`**: `start_time` / `end_time` are **not** raw unix seconds — the Process hook converts them to local-timezone RFC3339. If the raw value is empty/non-numeric, the field is left empty. No detail API call; all fields come from the payload.
|
||||
- **`note.generated_v1`**: fires for meetings *and* recordings/uploads. `note_token` / `verbatim_token` may be empty if detail isn't ready yet. `note_source` (and `note_source.source_entity_id` = meeting ID) is **only present when `source_type == "meeting"`**.
|
||||
- **`recording.*`**: only fire on Feishu-connected software. `recording_started`/`recording_ended` share `unique_key` (pairs a start with its end). `recording_transcript_generated` carries `transcript_items` as an **array, delivered in batches** — expect multiple events per recording.
|
||||
|
||||
Both keys use a **Custom schema** (flat output) and carry a **PreConsume hook** that auto-subscribes / unsubscribes via OAPI on first / last consumer. Both require `--as user`.
|
||||
|
||||
## Scopes & auth
|
||||
|
||||
| EventKey | Scope | Auth |
|
||||
|---|---|---|
|
||||
| `vc.meeting.participant_meeting_ended_v1` | `vc:meeting.meetingevent:read` | user |
|
||||
| `vc.note.generated_v1` | `vc:note:read` | user |
|
||||
|
||||
---
|
||||
|
||||
## `vc.meeting.participant_meeting_ended_v1`
|
||||
|
||||
### Output fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|---|---|---|
|
||||
| `type` | string | Event type; always `vc.meeting.participant_meeting_ended_v1` |
|
||||
| `event_id` | string | Globally unique event ID; safe for deduplication |
|
||||
| `timestamp` | string (timestamp_ms) | Event delivery time (ms timestamp string) |
|
||||
| `meeting_id` | string | Meeting ID |
|
||||
| `topic` | string | Meeting topic |
|
||||
| `meeting_no` | string | Meeting number |
|
||||
| `start_time` | string | Meeting start time in RFC3339, converted to the local timezone |
|
||||
| `end_time` | string | Meeting end time in RFC3339, converted to the local timezone |
|
||||
| `calendar_event_id` | string | Calendar event ID associated with the meeting |
|
||||
|
||||
### Gotchas
|
||||
|
||||
- `start_time` / `end_time` are **not** the raw unix-seconds from OAPI — the Process hook converts them to local-timezone RFC3339. If the raw value is empty or non-numeric, the field is left empty.
|
||||
- No detail API call is made; all fields come from the event payload itself.
|
||||
|
||||
### Example
|
||||
## jq recipes
|
||||
|
||||
```bash
|
||||
lark-cli event consume vc.meeting.participant_meeting_ended_v1 --as user
|
||||
|
||||
# Project meeting topic and end time only
|
||||
# meeting ended: topic + end time
|
||||
lark-cli event consume vc.meeting.participant_meeting_ended_v1 --as user \
|
||||
--jq '{meeting: .meeting_id, topic: .topic, ended: .end_time}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `vc.note.generated_v1`
|
||||
|
||||
Fires when a note is generated — not just from meetings, but also from realtime recordings and local file uploads.
|
||||
|
||||
### Output fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|---|---|---|
|
||||
| `type` | string | Event type; always `vc.note.generated_v1` |
|
||||
| `event_id` | string | Globally unique event ID; safe for deduplication |
|
||||
| `timestamp` | string (timestamp_ms) | Event delivery time (ms timestamp string) |
|
||||
| `note_id` | string | Note ID |
|
||||
| `note_token` | string | Note document token; may be empty if detail is not yet available |
|
||||
| `verbatim_token` | string | Verbatim document token; may be empty if detail is not yet available |
|
||||
| `note_source` | object | Source metadata; only present when source is a meeting |
|
||||
| `note_source.source_type` | string | Source type; only present when source is a meeting (value: `meeting`) |
|
||||
| `note_source.source_entity_id` | string | Source entity ID (meeting ID); only present when source is a meeting |
|
||||
|
||||
### Source type semantics
|
||||
|
||||
| `source_type` | Trigger |
|
||||
|---|---|
|
||||
| `meeting` | Note generated from a meeting |
|
||||
|
||||
`note_source` (and its sub-fields) are only populated when `source_type` is `meeting`. For other sources the field is absent.
|
||||
|
||||
### Example
|
||||
|
||||
```bash
|
||||
lark-cli event consume vc.note.generated_v1 --as user
|
||||
|
||||
# Only notes with enriched tokens, skip incomplete ones
|
||||
# notes: meeting-sourced only, with enriched tokens
|
||||
lark-cli event consume vc.note.generated_v1 --as user \
|
||||
--jq 'select(.note_token != "") | {note_id, note_token, verbatim_token}'
|
||||
--jq 'select(.note_source.source_type == "meeting" and .note_token != "") | {note_id, note_token, meeting_id: .note_source.source_entity_id}'
|
||||
|
||||
# Filter to meeting-sourced notes only
|
||||
lark-cli event consume vc.note.generated_v1 --as user \
|
||||
--jq 'select(.note_source.source_type == "meeting") | {note_id, meeting_id: .note_source.source_entity_id}'
|
||||
# recording transcript: stream speaker + text per line
|
||||
lark-cli event consume vc.recording.recording_transcript_generated_v1 --as user \
|
||||
--jq '.transcript_items[] | {speaker: .speaker_name, text}'
|
||||
```
|
||||
|
||||
@@ -1,67 +1,27 @@
|
||||
# Whiteboard Events
|
||||
|
||||
> **Prerequisite:** Read [`../SKILL.md`](../SKILL.md) first for the `event consume` essentials (commands, subprocess contract, jq usage).
|
||||
>
|
||||
> One key: `board.whiteboard.updated_v1` (run `event schema` for fields). Supports `--as user` **or** `--as bot`. Output is V2-enveloped — fields at `.event.xxx`.
|
||||
|
||||
## Key catalog (1)
|
||||
## Per-whiteboard subscription (the gotcha)
|
||||
|
||||
| EventKey | Purpose |
|
||||
|---|---|
|
||||
| `board.whiteboard.updated_v1` | A whiteboard has been edited |
|
||||
Unlike global keys, this one subscribes **per whiteboard**. **Required param: `-p whiteboard_id=<whiteboard_token>`** — omitting it fails param validation up-front (`required param "whiteboard_id" missing ...`) before any subscription.
|
||||
|
||||
This key uses a **Native schema** (V2 envelope; output rooted at `.event`) and carries a **PreConsume hook** that auto-subscribes / unsubscribes via OAPI on first / last consumer.
|
||||
- Get the token via the docs OAPI [list document blocks](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/document-docx/docx-v1/document-block/list): the block with `block_type=43` is a whiteboard; its `block.token` is the whiteboard token.
|
||||
- The caller must have **manage** access to that whiteboard, otherwise the subscribe OAPI returns 403 and `event consume` exits with an auth error **before** listening.
|
||||
- `.event.operator_ids` is an **array** — multiple collaborators editing in one tick collapse into a single event with multiple entries.
|
||||
|
||||
## Scopes & auth
|
||||
|
||||
| EventKey | Scope | Auth |
|
||||
|---|---|---|
|
||||
| `board.whiteboard.updated_v1` | `board:whiteboard:node:read` | user, bot |
|
||||
|
||||
Supports `--as user` or `--as bot`. The caller must have **manage** access to the target whiteboard, otherwise the subscribe OAPI returns 403 and `event consume` exits with an auth error before listening.
|
||||
|
||||
## `board.whiteboard.updated_v1`
|
||||
|
||||
### Per-whiteboard subscription
|
||||
|
||||
Unlike global event keys (e.g. minutes / im), this key subscribes **per whiteboard**: `event consume` calls `POST /open-apis/board/v1/whiteboards/{whiteboard_id}/subscribe` on startup with the `whiteboard_id` you pass via `-p`. **Required parameter**: `-p whiteboard_id=<whiteboard_token>`. Missing this param fails param validation up-front with `required param "whiteboard_id" missing for EventKey board.whiteboard.updated_v1` before any subscription happens.
|
||||
|
||||
Whiteboard token can be obtained via the docs OAPI [list document blocks](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/document-docx/docx-v1/document-block/list): the block whose `block_type=43` is a whiteboard, and `block.token` is the whiteboard token.
|
||||
|
||||
### Output fields (V2 envelope; root path `.event`)
|
||||
|
||||
| Field | Type | Description |
|
||||
|---|---|---|
|
||||
| `.event.whiteboard_id` | string (kind=whiteboard_id) | Whiteboard token |
|
||||
| `.event.operator_ids[].open_id` | string (kind=open_id) | Editor's open_id (`ou_` prefix) |
|
||||
| `.event.operator_ids[].union_id` | string (kind=union_id) | Editor's union_id |
|
||||
| `.event.operator_ids[].user_id` | string (kind=user_id) | Editor's user_id (only present when the caller's app has the user_id-related contact scope granted by the OAPI side) |
|
||||
|
||||
`operator_ids` is an array — multi-user collaborative editing within one tick collapses into a single event with multiple entries.
|
||||
|
||||
### Subscription lifecycle
|
||||
|
||||
| Phase | Behavior |
|
||||
|---|---|
|
||||
| Startup | `event consume` calls `subscribe` OAPI; on success stderr emits `[event] consuming as ...`, `[event] running pre-consume setup...`, `[event] listening for events (key=board.whiteboard.updated_v1)...`, then the AI-facing ready marker `[event] ready event_key=board.whiteboard.updated_v1` |
|
||||
| Running | Edits to the whiteboard stream as NDJSON to stdout |
|
||||
| Graceful exit (Ctrl+C / SIGTERM / `--max-events` / `--timeout` / stdin EOF) | `event consume` calls `unsubscribe` OAPI |
|
||||
| `kill -9` | **Skips unsubscribe → server-side subscription leaks**, may cause `subscription already exists` or duplicate delivery on next consume. See SKILL.md "Never `kill -9`". |
|
||||
|
||||
### Example
|
||||
## jq recipes
|
||||
|
||||
```bash
|
||||
# Stream every edit on whiteboard <token> until Ctrl+C
|
||||
lark-cli event consume board.whiteboard.updated_v1 \
|
||||
-p whiteboard_id=<whiteboard_token> \
|
||||
--as user
|
||||
# stream every edit on the whiteboard until Ctrl+C
|
||||
lark-cli event consume board.whiteboard.updated_v1 -p whiteboard_id=<whiteboard_token> --as user
|
||||
|
||||
# Sample one event for payload inspection
|
||||
lark-cli event consume board.whiteboard.updated_v1 \
|
||||
-p whiteboard_id=<whiteboard_token> \
|
||||
--as user --max-events 1 --timeout 2m
|
||||
# sample one event to inspect the payload
|
||||
lark-cli event consume board.whiteboard.updated_v1 -p whiteboard_id=<whiteboard_token> --as user --max-events 1 --timeout 2m
|
||||
|
||||
# Project to "edit summary": who edited which whiteboard
|
||||
lark-cli event consume board.whiteboard.updated_v1 \
|
||||
-p whiteboard_id=<whiteboard_token> \
|
||||
--as user \
|
||||
--jq '{whiteboard: .event.whiteboard_id, editors: (.event.operator_ids | map(.open_id))}'
|
||||
# edit summary: who edited which whiteboard
|
||||
lark-cli event consume board.whiteboard.updated_v1 -p whiteboard_id=<whiteboard_token> --as user \
|
||||
--jq '{whiteboard: .event.whiteboard_id, editors: (.event.operator_ids | map(.open_id))}'
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user