SuperAPI AI Directory

feishu-cli-event

Office & productivity

View repository

Updated 10-16 03:04

About this skill

Feishu real-time event subscription skill (WebSocket)

Via the feishu-cli event subcommand family, subscribe to Feishu Open Platform events, receive events over a WebSocket long connection, and output them to stdout as NDJSON. Suitable for AI Agent scenarios such as real-time bot responses, group message monitoring, and approval callback consumption.

feishu-cli: if not yet installed, go to riba2534/feishu-cli to get the installation method.

Send a message? Please use the feishu-cli-msg skill. This skill focuses on event subscription (receivingapp events) and is not responsible for sending.

Core concepts

Process model = 1 EventKey, 1 consume process

event consume <EventKey>
   │
   ├─ 启动 WebSocket 长连接(飞书 SDK ws.Client + AutoReconnect)
   ├─ 注册到 bus.json(PID / EventKey / 启动时间 / max-events / timeout)
   ├─ stderr 输出 [event] ready event_key=<key>
   ├─ 接收事件 → 写 stdout(NDJSON,每条一行 JSON)
   ├─ 可选:dump 每条事件为 <event_id>.json 文件
   ├─ 退出条件:--max-events / --timeout / SIGTERM / Ctrl-C / stdin EOF / pipe broken
   └─ 退出时自动 unregister bus.json

Differences from lark-cli event: lark-cli uses a Unix domain socket to run an independent bus daemon for event fan-out; feishu-cli simplifies this to "each consume directly connects one WebSocket" and does no event distribution—enough to cover the mainline AI Agent scenario of single-EventKey subscription.

State files and cross-process mutual exclusion

Path Purpose
~/.feishu-cli/events/<app_id>/bus.json List of active consumers (PID/EventKey/start time/parameters)
~/.feishu-cli/events/<app_id>/bus.lock flock file lock; bus.json reads and writes are serialized, and the fd is automatically released when closed

One subdirectory per AppID, so different apps do not interfere with each other.event status The query actively removes entries for PIDs that are no longer alive (kill -9 / crash leftovers). bus.json uses tmp + rename atomic writes to prevent partial writes.

Output protocol (NDJSON + ready marker)

Stream Content
stdout One line of JSON per event (NDJSON), suitable for jq / script pipelines
stderr Diagnostic logs; at startup, one line [event] ready event_key=<key> (init complete; WS handshake in progress)

AI Agent recommendation: the parent process runs consume in the background (run_in_background=true), first blocks on stderr until the [event] ready line, then starts reading stdout.Note: the ready marker only indicates that process initialization is complete; the WS handshake is performed asynchronously in the background; after the parent process sees the marker,it still needs to wait an additional 1-3s for the WS handshake to actually complete. In production, it is recommended that the parent process send a self-check event + wait for the echo round trip to confirm the link is working.

Exit codes and exit reason

Exit code Meaning
0 Normal exit (reached --max-events / --timeout / SIGTERM / Ctrl-C / stdin EOF)
Non-0 startup failure / unrecoverable WebSocket error / parameter error

At the end of stderr, [event] exited — elapsed=<d> reason=<r>is output; there are 4 reasons:

  • limit — reached --max-events
  • timeout — reached --timeout
  • signal — context canceled (Ctrl-C / SIGTERM / stdin EOF / downstream pipe broken)
  • error — WebSocket connection keeps failing

Command quick reference

feishu-cli event list [--json]                       # 1. 列所有支持的 EventKey
feishu-cli event schema <event_key> [--json]         # 2. 看某 key 的 EventType / scope / payload schema
feishu-cli event consume <event_key> [flags]         # 3. 启动订阅(阻塞)
feishu-cli event status [--json]                     # 4. 看本机活跃 consume 进程
feishu-cli event stop {--pid N | --event-key K | --all} [--force] [--json]  # 5. 停 consume

1. event list: list supported EventKeys

Display the currently supported 22+ EventKeys grouped by domain (im / contact / calendar / drive / approval / vc).

# 表格视图(默认)
feishu-cli event list

# JSON 输出,jq 提取 IM 域所有 EventKey
feishu-cli event list --json | jq -r '.[] | select(.domain=="im") | .key'

Output fields(JSON mode):key / event_type / description / domain / scopes[] / payload_schema。

2. event schema: view payload schema and scope

feishu-cli event schema im.message.receive_v1
feishu-cli event schema im.message.receive_v1 --json

Output 4 parts:Key / Event Type / Domain / Description / Scopes + optional Payload Schema (示例)。The payload schema is manually curated; after subscribing, the actual payload is subject to the Feishu Open Platform documentation.

3. event consume:Start WebSocket subscription (blocking)

# 基础订阅,Ctrl-C 退出
feishu-cli event consume im.message.receive_v1

# 调试:抓 5 条消息,最多跑 60s
feishu-cli event consume im.message.receive_v1 --max-events 5 --timeout 60s

# 落盘 + 静默
feishu-cli event consume im.message.receive_v1 --output-dir ./events --quiet

# 配合 jq 实时过滤群消息
feishu-cli event consume im.message.receive_v1 | jq 'select(.event.message.chat_type=="group")'

# 后台并发订阅多个 EventKey(每个 EventKey 一个进程)
feishu-cli event consume im.message.receive_v1     > receive.ndjson  2> receive.log  &
feishu-cli event consume im.message.reaction.created_v1 > reaction.ndjson 2> reaction.log &
feishu-cli event status

key flag:

Flag Default Description
--max-events N 0 (unlimited) Exit after receiving N events, reason=limit
--timeout <duration>(example 60s) 0 (unlimited) Exit after running for duration D, reason=timeout
--jq .event.xxx "" Minimaldot pathfiltering; full jq syntax is not supported (use pipe to external jq)
--output-dir ./events "" Additionally dump each event as <event_id>.json to disk (does not affect stdout)
--quiet false Suppress stderr diagnostics;AI Agents use with caution—it will suppress most stderr together, but the ready marker still goes through the real os.Stderr and is unaffected

--jq limitation:only recognizes .a.b.c map value access of the form (e.g. .event.message), does not support select / array indexing / pipes. For complex filtering, use feishu-cli event consume ... | jq '<expr>'。

--output-dir limitation:must be a safe relative path; no ~ expansion, does not accept absolute paths or .. path segments.

4. event status:View active consume processes on this machine

feishu-cli event status
feishu-cli event status --json | jq '.consumers[] | .pid'

Output:App ID / State file path / PID / EVENT_KEY / UPTIME / EXTRA(max-events / timeout / output-dir / jq)。

When querying, entries for PIDs that are no longer alive are proactively removed (cleaning up zombie records left by kill -9 / crashes).

5. event stop:Stop consume processes

feishu-cli event stop --pid 12345                          # 按 PID
feishu-cli event stop --event-key im.message.receive_v1    # 按 EventKey(所有订阅该 key 的进程)
feishu-cli event stop --all                                # 当前 AppID 下全部 consume
feishu-cli event stop --all --force                        # SIGKILL(紧急情况)

By default SIGTERM for graceful exit (the consume process will automatically unregister bus.json), wait up to 3s to verify the process has exited;--force escalates to SIGKILL, which will leave zombie entries in bus.json; next time event status will automatically clean them up.

EventKey Quick Reference (grouped by domain)

For the full list use feishu-cli event list. Common ones:

Domain EventKey Description
im im.message.receive_v1 Receive messages (users/group chats send to the Bot)
im im.message.message_read_v1 Message read receipt
im im.message.recalled_v1 Message recalled
im im.message.reaction.created_v1 / deleted_v1 Message emoji reaction added/removed
im im.chat.updated_v1 Group chat information updated
im im.chat.member.user.added_v1 / deleted_v1 User joins/leaves group
im im.chat.member.bot.added_v1 / deleted_v1 Bot is added to/removed from group
im im.chat.disbanded_v1 Group chat disbanded
contact contact.user.created_v3 / updated_v3 / deleted_v3 Employee onboarding/change/offboarding
calendar calendar.calendar.event.changed_v4 Calendar event changes (create/update/delete)
calendar calendar.calendar.acl.created_v4 Calendar permission changes
drive drive.file.title_updated_v1 Document title modified
drive drive.file.permission_member_added_v1 Document collaborator added
approval approval_instance Approval instance status changed
approval approval_task Approval task changed
vc vc.meeting.meeting_started_v1 / meeting_ended_v1 VC meeting started/ended

EventKey and EventType are usually the same;in the received payload, header.event_type equals the event_type field.

Permissions and Open Platform configuration

Default App Token, no need for auth login

Event subscription uses App identity (app_id + app_secret),does not require a user token. Configure ~/.feishu-cli/config.yaml or FEISHU_APP_ID / FEISHU_APP_SECRET environment variables and you're set.

Feishu Open Platform two-step configuration

In open.feishu.cn your app console:

  1. "Event Subscription - Receive Events via Long Connection" enable long connection mode (feishu-cli uses WebSocket,not webhook URL mode)
  2. "Events & Callbacks - Event Subscriptions" Select the target EventType (consistent with the event schema <key> output Event Type) andpublish version
  3. scope enablement: the scope required by each EventKey is shown in event schema <key> 's Scopes field; enable it on the "Permission Management" page.event domain has been added to the --domain event --recommend recommended list, allowing a one-time application for the union of common scopes IM/contact/calendar/drive/approval/vc

Common Errors

Symptom Cause Solution
WS connection failed, stderr reports ws error Long connection mode not enabled Enable "Event Subscriptions - Receive events via long connection" in the Feishu Open Platform
After startup it shows ready, but no events are received The target EventType is not checked under "Event Subscriptions" / version not published Re-check + publish version
Events received but payload fields are missing The App is missing the corresponding scope (e.g. im:message.p2p_msg:readonly) event schema <key> Check Scopes, go to the Permission Management page to enable it, then re-subscribe
event consume exits immediately with reason=error Wrong App ID/Secret / network unreachable / the domain uses lark but BaseURL uses feishu Check config.yaml;lark International version requires --base-url https://open.larksuite.com or the corresponding configuration

Recommended usage for AI Agent backend subscription

Single EventKey backend subscription (run_in_background=true)

# 1. 后台启动 consume,stderr/stdout 各 redirect
task = Bash(
    command='feishu-cli event consume im.message.receive_v1 --output-dir ./events 2> consume.log',
    run_in_background=True,
)

# 2. tail consume.log 阻塞等 "[event] ready event_key=im.message.receive_v1"

# 3. 额外 sleep 1-3s 让 WS 握手完成

# 4. 业务逻辑:tail stdout / 读 ./events/*.json 处理新事件

# 5. 退出:feishu-cli event stop --event-key im.message.receive_v1
#         或父进程 kill 后台 Bash task(SIGTERM 触发 graceful shutdown + unregister)

subprocess stdin EOF protocol (non-TTY)

In non-TTY mode,closing stdin triggers graceful exit(reason=signal)。Python subprocess.Popen use stdin=subprocess.PIPE, after processing p.stdin.close() is more stable than SIGTERM—consume will finish the current event before exiting.

Limit single-run duration / event count

For debugging scenarios always first use --max-events N --timeout Ds, to avoid forgetting to stop and leaving a background process consuming API quota:

feishu-cli event consume im.message.receive_v1 --max-events 1 --timeout 30s
# 抓 1 条事件 demo / 30 秒超时双保险

Pitfalls and Notes

  • daemon process persistence:event consume blocks running until a signal/timeout/EOF;will not exit on its own. Running in the background as an AI Agent must be configured with --max-events / --timeout or explicit event stop, otherwise it will leave a long-running process
  • flock cross-process mutual exclusion: bus.json reads and writes both use flock, multiple event consume starting and registering at the same time is safe; butdo not edit manually bus.json
  • pipe broken auto-exit: downstream jq / tee closing stdout (typical scenario:event consume ... | head -1) triggers SIGPIPE, consume proactively cancels and exits with reason=signal, and will not hang waiting for Ctrl-C
  • --quiet does not affect the ready marker: the ready marker goes through the real os.Stderr bypassing --quiet redirection, so even if an AI Agent enables --quiet the parent process can still wait for the ready line; but other diagnostics (including [event] exited reason) will be silently
  • AutoReconnect infinite retry: oapi-sdk-go v3 ws.Client defaults to WithAutoReconnect(true), retrying infinitely after disconnection (interval 2 minutes + initial jitter). For long disconnection scenarios it is recommended to use --timeout to actively exit and have the outer daemon process pull it up, which is more controllable than infinite inner retry
  • status does not actively ping the process:event status used signal(0) for liveness checks; in PID reuse scenarios it could theoretically misjudge (extremely low probability).event stop --pid N is also syscall.Kill; hitting the wrong PID will fail with ESRCH, and will not kill the wrong process
  • Each event gets its own file:--output-dir mode, each event is written to disk <event_id>.json,Short-term high-frequency events may create a large number of small files; writing to disk is only for record-keeping, and stdout NDJSON is still recommended for business consumption
  • --jq only supports dot paths:--jq .event.message projects each event onto the subtree before output; events that do not matchwill be skipped(no blank lines are output). For complex filtering, always use external pipe jq
  • --output-dir only supports safe relative paths: passing ~/events、/tmp/events、../events will all error; use ./events or events/today

When to switch to other skills

Task Routing
SendMessages / replies / cards / notifications feishu-cli-msg
Construct interactive card JSON feishu-cli-card
Handle received message events → write to multi-dimensional table feishu-cli-bitable(after parsing the payload, call the record command)
Handle received approval events → query approval details feishu-cli-toolkit(approval subcommand)
After receiving group messages, query group info/members feishu-cli-chat
Webhook URL mode (HTTP callback, not a long connection) Not within the scope of this skill; use the Feishu Open Platform's "Request URL configuration" + a self-built HTTP server
Batch pull of historical messages (not real-time) feishu-cli-chat the msg history / msg list

Reference

Security — event_id filename sanitization

--output-dir When enabled, each event is dumped as <event_id>.json. The v1 PR adds sanitizeEventID defenses:

  • Only [A-Za-z0-9_-] characters are kept, truncated to 128 in length
  • ..、/, spaces, and special symbols are all discarded
  • If the sanitized result is an empty string → skip the dump (do not write a file with an empty filename)

Defense scenario: when the server payload is abnormal or maliciously crafted header.event_id = "../etc/passwd" -type payloads, writeFile will not escape --output-dir。