‹ 首页

lark-event

@larksuite · 收录于 1 周前 · 上游提交 今天

Lark/Feishu real-time event listening / subscribing / consuming: stream events as NDJSON via `lark-cli event consume <EventKey>` (covers IM messages/reactions/chat changes, Task updates, VC meeting started/joined/ended, Minutes generated, Whiteboard updated, etc.). Use for Lark bots, real-time message processing, long-running subscribers, streaming webhook/push handlers. Supports `--max-events` / `--timeout` bounded runs and a stderr ready-marker contract — designed for AI agents running as subprocesses.

适合你,如果需要用命令行或AI agent实时处理飞书事件

/ 下载安装
lark-event.skill双击,或拖进 Claude 桌面版 / Cowork,即完成安装↓ .skill↓ .zip
用别的 agent?下载 .zip 解压,把文件夹放进它的技能目录
Claude Code~/.claude/skills/(项目级 .claude/skills/)
Codex CLI~/.codex/skills/
Cursor自动读取上面两处目录
其他工具见其文档的「skills」目录;两个下载是同一份文件,只是名字不同
/ 通过 npx 安装 校验哈希
npx oh-my-skill add larksuite/cli/lark-event
/ 通过 bash 安装
curl -fsSL https://oh-my-skill.com/install.sh | bash -s -- larksuite/cli/lark-event
/ 已经装过?验证本机副本,不用重装
npx oh-my-skill verify larksuite/cli/lark-event
安装目标可用 --agent / --scope 或 --to 明确指定;省略时只会在唯一已存在的 agent 目录上自动选择,零命中或多命中会停止并提示。content_hash 缺失或不一致均拒装。
GitHub stars
~2.2K最小装载
~5.9K含声明引用
~5.9K文本包总量
镜像托管

怎么用

技能原文 SKILL.md作者撰写 · MIT · baf6050

Lark Events

Prerequisite: Read [../lark-shared/SKILL.md](../lark-shared/SKILL.md) first for authentication, --as user/bot switching, Permission denied handling, and safety rules.
Core commands

| Command | Purpose | |------|------| | lark-cli event list [--json] | List all subscribable EventKeys | | lark-cli event schema <EventKey> [--json] | Show an EventKey's params and output schema | | lark-cli event consume <EventKey> [flags] | Blocking consume; events → stdout NDJSON | | lark-cli event status [--json] [--fail-on-orphan] | Inspect the local bus daemon status | | lark-cli event stop [--all] [--force] | Stop the bus daemon |

Common flags

| Flag | Description | |---|---| | --param key=value / -p | Business params (repeatable; comma-separated for multi-value). Unknown keys fail with valid names listed inline | | --jq <expr> | jq expression to filter / transform each event; empty output skips the event | | --max-events N | Exit after N events. Default 0 = unlimited | | --timeout D | Exit after duration D (e.g. 30s, 2m). Default 0 = no timeout. Whichever of --max-events / --timeout fires first wins | | --output-dir <dir> | Write each event as a file (relative paths only; prevents traversal) | | --quiet | Suppress stderr diagnostics. AI should not use this — it silences the ready marker | | --as user\|bot\|auto | Identity for the session (see lark-shared) |

Examples
# Default: stream every event for the key (no filter, no projection)
lark-cli event consume im.message.receive_v1 --as bot

# Grab one sample event to inspect payload shape
lark-cli event consume im.message.receive_v1 --max-events 1 --timeout 30s --as bot

# Run for 10 minutes then auto-exit
lark-cli event consume im.message.receive_v1 --timeout 10m --as bot

# Consume multiple EventKeys concurrently (one shape per process, no dispatcher)
lark-cli event consume im.message.receive_v1          --as bot > receive.ndjson &
lark-cli event consume im.message.reaction.created_v1 --as bot > reaction.ndjson &
wait
Call flow
  1. lark-cli event list --json → pick a legal key
  2. lark-cli event schema <key> --json → read resolved_output_schema + jq_root_path to determine field paths
  3. lark-cli event consume <key> [--jq '<expr>'] → consume
Subprocess contract
Ready marker

event consume's stderr emits a fixed line [event] ready event_key=<key>. Parent processes should block on stderr until this line appears, then start reading stdout. Do not fall back to sleep.

stdin EOF = graceful exit

event consume treats stdin close as a shutdown signal (wired for AI subprocess callers). Bounded runs are exempt: when --max-events or --timeout is set (> 0), stdin EOF is ignored and the run exits only via its own bound, timeout, or SIGTERM. For unbounded runs, < /dev/null / nohup / systemd's default StandardInput=null will cause an immediate graceful exit (stderr reason: signal). To keep an unbounded run alive:

  • Feed stdin a source that never EOFs: < <(tail -f /dev/null)
  • Or run bounded: --max-events N / --timeout D
Exit codes & reason

On exit, the last stderr line is [event] exited — received N event(s) in Xs (reason: ...).

| exit code | reason | Trigger | |---|---|---| | 0 | reason: limit | --max-events reached | | 0 | reason: timeout | --timeout reached | | 0 | reason: signal | Ctrl+C / SIGTERM / stdin EOF (stdin EOF applies to unbounded runs only) | | 1 | JSON error envelope on stderr | Lark API business failure during pre-consume setup (for example subscription create/delete) | | 2 | JSON error envelope on stderr (no exited line) | Validation failure (unknown EventKey, bad --param / --jq, another bus already connected) | | 3 | JSON error envelope on stderr | Auth failure (missing token, missing scopes) | | 4 / 5 | JSON error envelope on stderr | Network / internal failure (bus startup, handshake, file I/O) |

Startup and runtime failures emit a structured JSON envelope on stderr: {"ok":false,"error":{"type","subtype","param","message","hint",...}} (the envelope may also carry top-level identity / _notice siblings). Parse error.type / error.subtype to branch (e.g. missing_scope carries a missing_scopes list), error.param to find the offending flag, and error.hint for the recovery action — do not regex-match message text.

Orchestrators should treat reason: limit/timeout/signal (all exit 0) as "business completion" and non-zero as "failure".

Never kill -9

Avoid kill -9 on consume processes: for EventKeys with a PreConsume hook (those that register server-side subscriptions via OAPI), kill -9 skips the OAPI unsubscribe and leaks server-side subscriptions (symptoms: "subscription already exists" on restart, duplicate event delivery). Prefer SIGTERM or closing stdin.

One consume, one EventKey (multi-key = multi-shell)

The command takes exactly one positional argument; k1,k2 and wildcards are unsupported. Listening to N keys means N subprocesses — this is intentional:

  • One shape per process stdout; no dispatcher logic required in the AI
  • Fault isolation (one key failing doesn't affect others)
  • Independent --as / --jq / --max-events / --timeout per key

All N consumers share a single bus daemon (UDS local IPC), so the overhead is small

Writing jq via schema

event schema <key> --json is the source of truth for writing --jq. Four things to look at:

(1) Where fields start — see jq_root_path

  • Value "." → fields are at the top level, write .chat_id
  • Value ".event" → fields are inside a V2 envelope, write .event.chat_id

(2) Field list and types — see resolved_output_schema.properties.<name>

Each field carries type / description, and some also have format. Snippet (from event schema im.message.receive_v1 --json):

{
  "chat_id":     {"type":"string", "format":"chat_id",      "description":"Chat ID, prefixed with oc_"},
  "sender_id":   {"type":"string", "format":"open_id",      "description":"Sender open_id, prefixed with ou_"},
  "create_time": {"type":"string", "format":"timestamp_ms", "description":"Send time as ms-epoch string"}
}

(3) Field semantics — see the format tag

Lark-defined semantic tags (not JSON Schema's standard format). Common values: open_id / chat_id / message_id / timestamp_ms / email. Purpose: distinguish "same string type, different meanings" fields so you can reverse-lookup via API or convert formats.

(4) Decoded state — read the field's description

event consume runs Process hooks that may pre-decode some payload fields (flattening V2 envelopes, rendering .content to plain text, etc.) — behavior differs from raw OAPI. Always read the field's description before writing jq, especially for generic field names like content / data / body / payload.

Why it matters: blindly applying fromjson to an already-decoded text field makes jq error on every event and silently drop it — the consumer looks alive but emits nothing, with only a single WARN line buried on stderr. (This is the general behavior: any jq runtime error skips the event with a one-line WARN; the loop does not abort.)

Don't shortcut the schema: when projecting event schema --json with jq, do not strip .description from properties — that's the field that tells you whether a field is already decoded. Dump the full property objects, not just keys.


Aside: --param's valid parameters also live in the schema — the params section lists name / type / required / enum / default / description; section missing = this key accepts no --param.

Topic index

| Topic | Reference | Coverage | |------------|------------------------------------------------------------------------------|---| | IM | [references/lark-event-im.md](references/lark-event-im.md) | Catalog of 12 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); for card.action.trigger see also [../lark-im/references/lark-im-card-action-reply.md](../lark-im/references/lark-im-card-action-reply.md) | | Task | [references/lark-event-task.md](references/lark-event-task.md) | Catalog of 1 Task EventKey (task.task.update_user_access_v2) + Native V2 envelope shape + task commit types + user/bot subscription notes | | VC | [references/lark-event-vc.md](references/lark-event-vc.md) | Catalog of 4 VC EventKeys (vc.meeting.participant_meeting_started_v1, vc.meeting.participant_meeting_joined_v1, 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) |

按 MIT 许可原样转载,未经改动 · 在 GitHub 查看 →

评论

登录即可评论;带「已验证安装」的,是发布者名下有本店的安装或持有记录。