event skill
This commit is contained in:
@@ -60,7 +60,7 @@ cli_version: ">=1.0.15"
|
||||
| `sheet` | 在线电子表格(axls):工作表 CRUD/区域读写/CSV 批量写入/行列增删/合并/查找替换/筛选视图/全局筛选/排序/下拉列表/浮动图片/导出(两步) | [sheet.md](./references/products/sheet.md) |
|
||||
| `todo` | 待办:创建(含优先级/截止时间/循环)/查询/修改/标记完成/删除 | [todo.md](./references/products/todo.md) |
|
||||
| `wiki` | 知识库:空间创建/详情/列表/搜索 + 成员管理 | [wiki.md](./references/products/wiki.md) |
|
||||
| `event` | 事件订阅:Stream 长连接订阅消息/审批/通讯录/日历事件,NDJSON 输出(实时驱动 Agent)| [event.md](./references/products/event.md) |
|
||||
| `event` | 个人单聊事件:监听当前用户与指定用户的单聊消息,NDJSON 输出(实时驱动 Agent)| [event.md](./references/products/event.md) |
|
||||
|
||||
## 核心流程(每次请求必须执行,不得跳过)
|
||||
|
||||
@@ -113,7 +113,7 @@ cli_version: ">=1.0.15"
|
||||
用户提到"在线电子表格/钉钉表格/axls/工作表/单元格读写/合并单元格/筛选视图/导出 xlsx" → `sheet`
|
||||
用户提到"待办/TODO/任务提醒/循环待办" → `todo`
|
||||
用户提到"创建知识库/知识库列表/搜索知识库空间/wiki/团队空间/知识库成员管理/我的文档个人空间" → `wiki`
|
||||
用户提到"实时监听/订阅事件/事件流/event consume/接收新消息/驱动 Agent/聊天机器人监听/审批触发/通讯录变更" → `event`
|
||||
用户提到"监听我和某人的单聊消息/订阅个人单聊事件/实时接收某个用户发给我的消息/个人消息事件流/event consume user_im_message_receive_o2o/驱动 Agent 处理单聊消息" → `event`
|
||||
|
||||
关键区分: **dev(创建/配置/建联机器人)** vs **chat(查询/发消息已有机器人)**。`dws chat bot search/find` 只查询机器人;**建号**(创建钉钉智能体机器人)走 `dws dev app robot submit`;**建联**(把机器人接到本地 agent 的 Stream)走 `dws dev connect`。凡是"创建机器人""建机器人""接入 agent""建联"一律路由到 `dev`,禁止走 `chat`。
|
||||
关键区分: aitable(数据表格) vs todo(待办任务)
|
||||
|
||||
@@ -1,51 +1,58 @@
|
||||
# `dws event` — 事件订阅
|
||||
# `dws event` — 个人单聊事件
|
||||
|
||||
通过 DingTalk Stream 长连接实时订阅事件,NDJSON 输出到 stdout,用于驱动事件触发的 Agent。
|
||||
通过个人 Stream 长连接监听当前用户与指定用户的单聊消息,NDJSON 输出到 stdout,用于驱动事件触发的 Agent。
|
||||
|
||||
当前只暴露 `user_im_message_receive_o2o`。
|
||||
|
||||
## 当用户说……
|
||||
|
||||
| 用户说 | 命令 |
|
||||
|--------|------|
|
||||
| "监听消息" / "实时接收消息" | `dws event consume --event-types im.message.receive_v1 --compact --quiet` |
|
||||
| "审批触发时执行" | `dws event consume --event-types approval.instance.status_changed --compact --quiet` |
|
||||
| "看 bus 状态" | `dws event status` |
|
||||
| "列出消费者" | `dws event list --all` |
|
||||
| "停止订阅" | `dws event stop` |
|
||||
| "监听我和 507971 的单聊消息" | `dws event consume`,事件码 `user_im_message_receive_o2o`,参数 `--peer-user-id 507971 -f ndjson` |
|
||||
| "订阅某个 unionId 的单聊事件" | `dws event consume`,事件码 `user_im_message_receive_o2o`,参数 `--peer-union-id <unionId> -f ndjson` |
|
||||
| "查看个人单聊事件 schema" | `dws event schema`,事件码 `user_im_message_receive_o2o` |
|
||||
| "看个人单聊订阅状态" | `dws event status --event user_im_message_receive_o2o` |
|
||||
| "停止这个个人事件订阅" | `dws event stop <subscribe_id>` |
|
||||
|
||||
## 子命令
|
||||
## 核心规则
|
||||
|
||||
- `dws event consume [flags]` — 主命令,订阅 + 输出
|
||||
- `dws event list [--all] [--all-editions]` — 列消费者
|
||||
- `dws event status [--all] [--fail-on-orphan]` — bus 健康状态
|
||||
- `dws event stop` — 优雅停 bus
|
||||
- `user` 是默认身份,不要加 `--as user`。
|
||||
- 必须提供对端身份:优先 `--peer-user-id`,没有时使用 `--peer-union-id`。
|
||||
- 缺少对端身份时先追问,不要猜测。
|
||||
- 不要主动运行 `dws event list` 作为能力菜单;本参考只承认 `user_im_message_receive_o2o`。
|
||||
- 当前 event 参考不暴露应用事件。用户明确询问应用事件时,回答“当前 event skill 暂不暴露应用事件”。
|
||||
|
||||
## 凭证
|
||||
## 常用命令
|
||||
|
||||
bot-only,需要 ClientID + ClientSecret:
|
||||
- env:`DWS_CLIENT_ID` + `DWS_CLIENT_SECRET`(成组)
|
||||
- 或:`dws config init` 写入 keychain
|
||||
```bash
|
||||
dws event schema user_im_message_receive_o2o
|
||||
```
|
||||
|
||||
```bash
|
||||
dws event consume user_im_message_receive_o2o \
|
||||
--peer-user-id 507971 \
|
||||
-f ndjson
|
||||
```
|
||||
|
||||
```bash
|
||||
dws event consume user_im_message_receive_o2o \
|
||||
--peer-user-id 507971 \
|
||||
--duration 10m \
|
||||
-f ndjson
|
||||
```
|
||||
|
||||
```bash
|
||||
dws event status --event user_im_message_receive_o2o
|
||||
dws event stop <subscribe_id>
|
||||
```
|
||||
|
||||
## 输出格式
|
||||
|
||||
- `-f ndjson`(默认):一行一对象
|
||||
- `-f compact`:扁平化 + 解析嵌套(**Agent 强推荐**)
|
||||
- `-f json/pretty`:美化 JSON(必须 `--max-events` 或 `--duration`)
|
||||
- `-f raw`:SDK 原始 payload
|
||||
|
||||
## 关键 Flag
|
||||
|
||||
| Flag | 说明 |
|
||||
|------|------|
|
||||
| `--event-types <list>` | 限定事件类型,下推到 bus |
|
||||
| `--filter <regex>` | 正则过滤事件类型 |
|
||||
| `--max-events <n>` | 收 N 条后退出 |
|
||||
| `--duration <duration>` | 运行时长上限 (30s/5m) |
|
||||
| `--output-dir <dir>` | 每事件写一个文件 |
|
||||
| `--route '<regex>=dir:<path>'` | 按正则路由事件到目录 |
|
||||
| `--foreground` | 不 fork daemon(systemd/k8s 友好)|
|
||||
| `--dry-run` | 仅打印配置 |
|
||||
- 推荐 `-f ndjson`:一行一个事件 JSON,适合 Agent 管道读取。
|
||||
- 人工取样可用 `-f json --max-events 1`。
|
||||
- `--debug-raw-events` 仅用于服务端联调,正常消费不要使用。
|
||||
|
||||
## 完整文档
|
||||
|
||||
详细见 multi 模式:`skills/multi/dingtalk-event/SKILL.md`
|
||||
端到端冒烟手册:`skills/multi/dingtalk-event/references/runbook.md`
|
||||
自测流程:`skills/multi/dingtalk-event/references/runbook.md`
|
||||
|
||||
@@ -1,183 +1,69 @@
|
||||
---
|
||||
name: dingtalk-event
|
||||
description: 钉钉事件订阅。Use when 用户提到 实时监听消息/订阅事件/事件流/event consume/接收新消息/聊天机器人监听/审批事件/通讯录变更/日历事件/构建事件驱动 Agent。命令前缀:dws event。
|
||||
cli_version: ">=1.0.31"
|
||||
metadata:
|
||||
category: product
|
||||
stability: experimental
|
||||
requires:
|
||||
bins:
|
||||
- dws
|
||||
description: 钉钉个人单聊事件监听、订阅与消费。Use when 用户提到 监听我和某人的单聊消息、订阅个人单聊事件、实时接收某个用户发给我的消息、dws event consume user_im_message_receive_o2o、个人消息事件流、用事件驱动 Agent 处理单聊消息。命令前缀:dws event。
|
||||
---
|
||||
|
||||
# 钉钉事件订阅 Skill
|
||||
# 钉钉个人单聊事件 Skill
|
||||
|
||||
> 🧪 **EXPERIMENTAL** — DingTalk Stream 事件订阅 (v1)。提供 daemon + 多消费者本地 IPC,对齐 lark-cli 风格。
|
||||
本 skill 只暴露个人事件里已经实现的单聊事件:`user_im_message_receive_o2o`。
|
||||
|
||||
> **PREREQUISITE:** 用 `dws config init` 配置 ClientID/ClientSecret,或通过 env var `DWS_CLIENT_ID` + `DWS_CLIENT_SECRET`(成组覆盖)。bot-only:不需要 `auth login`。
|
||||
不要暴露其它事件类型、身份模式或应用级事件能力。用户明确询问应用事件时,回答:当前 event skill 暂不暴露应用事件。
|
||||
|
||||
> 命令参考:[dingtalk-event-consume.md](references/dingtalk-event-consume.md) · 端到端示例:[runbook.md](references/runbook.md)
|
||||
## 前置条件
|
||||
|
||||
## 意图表
|
||||
- 使用当前用户 OAuth 登录态;需要时先让用户执行 `dws auth login`。
|
||||
- 订阅单聊事件必须知道对端身份:优先 `peer-user-id`,没有时使用 `peer-union-id`。
|
||||
- 缺少对端身份时先追问,不要猜测 ID。
|
||||
|
||||
| 用户说 | 命令 |
|
||||
|--------|------|
|
||||
| "监听新消息" | `dws event consume --event-types im.message.receive_v1 --compact --quiet` |
|
||||
| "订阅审批状态变化" | `dws event consume --event-types approval.instance.status_changed` |
|
||||
| "看 bus 状态" | `dws event status` |
|
||||
| "列活跃消费者" | `dws event list --all` |
|
||||
| "停 bus" | `dws event stop` |
|
||||
| "调试 SDK 原始 payload" | `dws event consume -f raw --max-events 1` |
|
||||
| "事件持久化到文件" | `dws event consume --output-dir ./events` |
|
||||
| "按事件类型路由到不同目录" | `dws event consume --route '^im\.=dir:./im/' --route '^approval\.=dir:./approval/'` |
|
||||
## 核心命令
|
||||
|
||||
## 架构(一图)
|
||||
| 意图 | 命令 |
|
||||
|------|------|
|
||||
| 查看单聊事件说明 | `dws event schema`,事件码 `user_im_message_receive_o2o` |
|
||||
| 监听与指定 userId 的单聊消息 | `dws event consume`,事件码 `user_im_message_receive_o2o`,参数 `--peer-user-id <userId> -f ndjson` |
|
||||
| 监听与指定 unionId 的单聊消息 | `dws event consume`,事件码 `user_im_message_receive_o2o`,参数 `--peer-union-id <unionId> -f ndjson` |
|
||||
| 查看个人单聊订阅状态 | `dws event status --event user_im_message_receive_o2o` |
|
||||
| 停止指定个人订阅 | `dws event stop <subscribe_id>` |
|
||||
| 停止当前身份下所有本地记录的个人订阅 | `dws event stop --all` |
|
||||
|
||||
```
|
||||
钉钉 Stream Gateway (WebSocket)
|
||||
│ (一个 ClientID 一条云端连接)
|
||||
▼
|
||||
┌───────────────────────────────────────────────────┐
|
||||
│ dws event _bus (per ClientID, auto-forked) │
|
||||
│ Source (SDK 长连接) → Dedup (event_id LRU) │
|
||||
│ → Hub (Hello pushdown 按订阅过滤) │
|
||||
│ → UDS (Unix) / Named Pipe (Windows) │
|
||||
└───────────────────────┬───────────────────────────┘
|
||||
│ NDJSON frames
|
||||
┌───────────────┼───────────────┐
|
||||
▼ ▼ ▼
|
||||
dws event dws event dws event
|
||||
consume (A) consume (B) status/list
|
||||
im.* 订阅 approval.* ad-hoc RPC
|
||||
```
|
||||
`user` 是 `dws event` 默认身份,不要额外加 `--as user`。
|
||||
|
||||
**关键不变量:**
|
||||
1. **emit 非阻塞** — SDK 回调里立即 ACK;慢消费者只丢自己(drop-oldest)
|
||||
2. **dedup 必须有** — Stream 重连会重投递在途事件
|
||||
3. **单 ClientID 单 bus** — `<ConfigDir>/events/<edition>/<clientIDHash>/bus.lock` 强制
|
||||
4. **上游永远全订阅** — `--event-types` 只影响 bus → consumer 这一段;开放平台后台未勾选的事件类型即使设置 `--event-types` 也收不到
|
||||
5. **consumer EOF 自动清理** — SIGKILL/崩溃后 bus 通过 socket EOF 立即 unregister
|
||||
## 常用模式
|
||||
|
||||
## 子命令总览
|
||||
|
||||
| 子命令 | 用途 |
|
||||
|--------|------|
|
||||
| `dws event consume` | 订阅事件并 NDJSON 输出(主命令)|
|
||||
| `dws event list` | 列出当前 / 全 edition 的消费者 |
|
||||
| `dws event status` | 显示 bus 健康状态 + per-event-type 计数 |
|
||||
| `dws event stop` | 优雅停 bus(SIGTERM)|
|
||||
| `dws event _bus` | (隐藏)daemon 进程入口,consume 按需 fork |
|
||||
|
||||
## 凭证解析顺序
|
||||
|
||||
1. **env var 覆盖**(CI/容器友好):`DWS_CLIENT_ID` + `DWS_CLIENT_SECRET` **同时设置** → 整组使用,跳过 keychain;任一缺失则不启用 env 通道(避免半套配置)
|
||||
2. **keychain**:`dws config init` 配置后存储于系统 keychain
|
||||
3. **明文 config**(不推荐):app config 文件中 `clientSecret` 字段直接为 plain string
|
||||
|
||||
`status` / HelloAck 会显示 `credentials_source` (env / keychain / app_config / plain_config),方便用户验证实际生效的来源。
|
||||
|
||||
## 4 个 env var 调参(不需要新增 flag)
|
||||
|
||||
| Env var | 默认 | 用途 |
|
||||
|---------|------|------|
|
||||
| `DWS_EVENT_BUS_IDLE_TIMEOUT` | `5m` | bus 在 0 consumer 后自停时间 |
|
||||
| `DWS_EVENT_CONSUMER_BUFFER` | `100` | 每 consumer sendCh 容量 |
|
||||
| `DWS_EVENT_DEDUP_LRU` | `8192` | event_id LRU 容量 |
|
||||
| `DWS_EVENT_DROP_WARN_PCT` | `5` | 单事件类型 drop 比超阈值时写 bus.log WARN |
|
||||
|
||||
## 常见 Agent pipeline
|
||||
|
||||
### 收消息 → Claude 回复
|
||||
### 监听指定用户的单聊消息
|
||||
|
||||
```bash
|
||||
DWS_CLIENT_ID=ding_xxx DWS_CLIENT_SECRET=*** \
|
||||
dws event consume \
|
||||
--event-types im.message.receive_v1 \
|
||||
--compact --quiet \
|
||||
| while IFS= read -r ev; do
|
||||
content=$(echo "$ev" | jq -r '.content // empty')
|
||||
msg_id=$(echo "$ev" | jq -r '.message_id // empty')
|
||||
[[ -z "$content" ]] && continue
|
||||
reply=$(claude -p "用中文简短回复:$content")
|
||||
dws chat message reply --message-id "$msg_id" --text "$reply"
|
||||
done
|
||||
dws event consume user_im_message_receive_o2o \
|
||||
--peer-user-id 507971 \
|
||||
-f ndjson
|
||||
```
|
||||
|
||||
### 审批 → 写入飞书文档
|
||||
### 有界自测
|
||||
|
||||
```bash
|
||||
dws event consume \
|
||||
--event-types approval.instance.status_changed \
|
||||
--compact --quiet \
|
||||
| while IFS= read -r ev; do
|
||||
title=$(echo "$ev" | jq -r '.title')
|
||||
status=$(echo "$ev" | jq -r '.to_status')
|
||||
lark-cli docs +update --doc "DOC_URL" --mode append \
|
||||
--markdown "- $(date '+%H:%M') 审批 [$title] → $status"
|
||||
done
|
||||
dws event consume user_im_message_receive_o2o \
|
||||
--peer-user-id 507971 \
|
||||
--duration 10m \
|
||||
-f ndjson
|
||||
```
|
||||
|
||||
### 事件持久化(审计/回放)
|
||||
### 抓一条样本
|
||||
|
||||
```bash
|
||||
# 每事件落一个 JSON 文件,按事件类型分目录
|
||||
dws event consume \
|
||||
--route '^im\.=dir:./events/im/' \
|
||||
--route '^approval\.=dir:./events/approval/' \
|
||||
--route '^contact\.=dir:./events/contact/' \
|
||||
--output-dir ./events/other/
|
||||
dws event consume user_im_message_receive_o2o \
|
||||
--peer-user-id 507971 \
|
||||
--max-events 1 \
|
||||
-f json
|
||||
```
|
||||
|
||||
## 关键陷阱速查
|
||||
## 输出处理
|
||||
|
||||
| 陷阱 | 症状 | 解决 |
|
||||
|------|------|------|
|
||||
| 开放平台后台没勾事件 | consume 跑着但收不到事件 | 钉钉开发者后台 → 应用 → 事件订阅 → 长连接 → 勾对应事件类型 |
|
||||
| `--force` 不带 `--foreground` | validation error | daemon 模式不能 force(会导致多 bus 抢同 socket);要重启 bus 用 `event stop && event consume` |
|
||||
| `--format json` 无界流 | validation error | json 必须配 `--max-events` 或 `--duration`;流式用 `ndjson`(默认)|
|
||||
| CI/容器没 keychain | "ClientSecret resolution failed" | 用 env var:`DWS_CLIENT_ID + DWS_CLIENT_SECRET` 成组设置 |
|
||||
| orphan bus 残留 | 上次 `kill -9` 后 status 显示 orphan | 下次 `dws event consume` 会自动清理 stale lock;手动可 `dws event stop` 或 `rm -rf` workdir |
|
||||
| 多副本(K8s)只 1 个能跑 | 单 ClientID 单 bus 设计 | 每副本用独立 ClientID;不要共享 |
|
||||
| stdout 被污染 | NDJSON 流里混入 daemon 日志 | bus daemon stdio 已 detach;日志在 `<workDir>/bus.log`;如果污染必是 bug 请 report |
|
||||
|
||||
## 输出格式(事件流默认 ndjson)
|
||||
|
||||
| `-f` 值 | 输出 | 用途 |
|
||||
|---------|------|------|
|
||||
| `ndjson` (默认) | 一行一个对象 | 管道 / jq / Agent |
|
||||
| `json` | 多行美化 JSON | 人工调试(要求 bounded:必须 `--max-events` 或 `--duration`)|
|
||||
| `pretty` | 同 json | 同上 |
|
||||
| `raw` | 仅 SDK 原始 payload | 调试 SDK |
|
||||
| `compact` | 扁平化 + 解析嵌套 + 语义字段 | **Agent 强推荐**(IM/审批/通讯录/日历/考勤都有处理器)|
|
||||
| `table` / `csv` | fallback 到 ndjson + stderr WARN | 事件流无意义 |
|
||||
|
||||
`-f compact` 已注册 8 个处理器:im.message.receive_v1 / im.message.read_v1 / approval.instance.status_changed / approval.task.created / contact.user.{created,updated,deleted}_v3 / cal.event.{created,updated,deleted}_v1 / attendance.check_v1。未注册类型走 generic processor(payload JSON 字段扁平化到顶层)。
|
||||
|
||||
## 与 lark-cli event 对齐和超越
|
||||
|
||||
| 维度 | lark-cli | dws event |
|
||||
|------|---------|-----------|
|
||||
| 用户命令 | `consume <EventKey>` + skill `+subscribe` 套娃 | **`consume [--event-types ...]` 一层搞定** |
|
||||
| `--compact/--filter/--route` | 在 `+subscribe` skill 里 | **全部下放到 `consume`** |
|
||||
| 隐藏 daemon | `_bus.go` 按需 fork ✅ | 同 |
|
||||
| event_id 去重 | ✅ | 同 |
|
||||
| 连接状态 | 日志正则反推 | **inferred state machine + state_source 字段** |
|
||||
| `status` 视图 | per-consumer 计数 | + **per-event-type 总计 + drop-rate WARN** |
|
||||
| Identity | `--as user/bot/auto` | **bot-only**(命令面更干净)|
|
||||
| 前台模式 | 强 daemon | + **`--foreground`**(systemd/k8s 友好)|
|
||||
| Hello 下推过滤 | EventKey + 基础 | **+ filter regex / event_types 下推到 bus,减少 IPC 量** |
|
||||
| 跨平台 | Unix Socket + Windows Named Pipe ✅ | 同 |
|
||||
|
||||
## 实施细节
|
||||
|
||||
- v1 仅 `RegisterAllEventRouter`(generic events);ChatBot @-消息和卡片回调 v2 加(需要同步 ACK 业务响应体)
|
||||
- v1 stop 走 SIGTERM;Windows graceful 是 v2 todo
|
||||
- catch-all 列表 v1 为空(= bus 上游全订阅);待 P0 真测 DingTalk event_type 字符串后填具体清单
|
||||
- workdir:`<ConfigDir>/events/<edition>/<clientIDHash>/`,clientIDHash = `sha256(clientID)[:16]`
|
||||
- 默认推荐 `-f ndjson`:stdout 每行一个事件 JSON,适合 Agent 管道读取。
|
||||
- 人工查看单条样本可用 `-f json --max-events 1`。
|
||||
- 长时间监听时用 `--duration` 或外部进程管理控制生命周期。
|
||||
- `--debug-raw-events` 只用于和服务端联调,正常 Agent 消费不要使用。
|
||||
|
||||
## 参考
|
||||
|
||||
- 详细命令参考:[references/dingtalk-event-consume.md](references/dingtalk-event-consume.md)
|
||||
- 端到端冒烟手册:[references/runbook.md](references/runbook.md)
|
||||
- 实施计划:`plans/2026-05-28_event_capability_v1.plan.md`
|
||||
- P0 SDK 验证记录:`plans/2026-05-28_event_capability_v1.p0-sdk-probe.md`
|
||||
- 详细消费参数:[references/dingtalk-event-consume.md](references/dingtalk-event-consume.md)
|
||||
- 自测流程:[references/runbook.md](references/runbook.md)
|
||||
|
||||
@@ -1,137 +1,105 @@
|
||||
# `dws event consume` 详细参考
|
||||
# `dws event consume` 个人单聊参考
|
||||
|
||||
订阅 DingTalk Stream 事件并将每条事件以 NDJSON 输出到 stdout。
|
||||
本参考只覆盖个人单聊事件 `user_im_message_receive_o2o`。
|
||||
|
||||
## Synopsis
|
||||
|
||||
```
|
||||
dws event consume [flags]
|
||||
```bash
|
||||
dws event consume user_im_message_receive_o2o [flags]
|
||||
```
|
||||
|
||||
## Flags
|
||||
## 必填参数
|
||||
|
||||
| Flag | 默认 | 说明 |
|
||||
|------|------|------|
|
||||
| `--event-types <list>` | catch-all | 逗号分隔事件类型,下推到 bus;省略 = 接收 bus 上游所有事件 |
|
||||
| `--filter <regex>` | — | 客户端正则过滤事件类型(下推到 bus)|
|
||||
| `--compact` | false | 提示 bus 期望 compact 渲染(语义透传)|
|
||||
| `-f, --format <format>` | `ndjson` | 输出格式:`ndjson` (默认) / `json` / `pretty` / `raw` / `compact`;`table/csv` fallback 到 ndjson |
|
||||
| `--output-dir <dir>` | stdout | 每事件写一个文件 `{type}_{id}_{ts}.json`;与 stdout 互斥 |
|
||||
| `--route <spec>` | — | `<regex>=dir:<path>`,可重复;未命中走 stdout/--output-dir |
|
||||
| `--max-events <n>` | 0 | 收到 N 条后退出 (0 = 不限) |
|
||||
| `--duration <duration>` | 不限 | Go duration (30s/5m),事件流专用 |
|
||||
| `--quiet` | false | 抑制 stderr 状态信息 |
|
||||
| `--force` | false | 仅 `--foreground` 模式有意义;daemon 模式 → validation error |
|
||||
| `--dry-run` | false | 仅打印解析后的配置,不连接 bus / 云端 |
|
||||
| `--foreground` | false | 不 fork daemon,当前进程跑 bus (systemd/k8s 友好) |
|
||||
必须二选一:
|
||||
|
||||
## 凭证
|
||||
| 参数 | 说明 |
|
||||
|------|------|
|
||||
| `--peer-user-id <userId>` | 对端用户的 userId,优先使用 |
|
||||
| `--peer-union-id <unionId>` | 对端用户的 unionId |
|
||||
|
||||
- env var (覆盖式优先):`DWS_CLIENT_ID` + `DWS_CLIENT_SECRET` 同时设置即生效
|
||||
- keychain(默认):`dws config init` 写入 OS keychain
|
||||
- bot-only:不需要 `dws auth login`
|
||||
缺少对端身份时,先向用户追问。
|
||||
|
||||
## 输出契约
|
||||
## 常用参数
|
||||
|
||||
- 默认 `-f ndjson`:每事件一行 JSON。字段:`type/seq/event_id/event_born_time/event_corp_id/event_type/event_unified_app_id/data/headers/received_at_unix_ms`
|
||||
- `-f compact`:经 processor 扁平化后的 map。常见字段(IM):`type/event_id/timestamp/corp_id/app_id/message_id/chat_id/chat_type/message_type/content/sender_id`
|
||||
- `-f raw`:仅 `event.Data`(SDK 原始 payload string),无 dws 封装
|
||||
- `-f json/pretty`:每事件多行美化 JSON;**必须** 配 `--max-events` 或 `--duration`,否则 validation error
|
||||
- stderr:默认输出连接 banner `connected bus pid=N source=... state=...`;`--quiet` 抑制
|
||||
| 参数 | 说明 |
|
||||
|------|------|
|
||||
| `-f, --format ndjson` | 推荐输出,一行一个事件 JSON |
|
||||
| `--max-events <n>` | 收到 N 条后退出 |
|
||||
| `--duration <duration>` | 到时退出,如 `30s`、`10m` |
|
||||
| `--output-dir <dir>` | 每个事件写入一个文件 |
|
||||
| `--route '<regex>=dir:<path>'` | 按事件类型路由到目录 |
|
||||
| `--subscribe-id <id>` | 复用已有个人订阅 |
|
||||
| `--personal-event-base-url <url>` | 联调环境覆盖控制面 base URL |
|
||||
| `--stream-ticket-url <url>` | 联调环境覆盖取票 URL |
|
||||
| `--stream-source-id <id>` | 联调环境覆盖 sourceId |
|
||||
| `--debug-raw-events` | 联调用:输出当前 personal stream bus 收到的全部可解析事件 |
|
||||
|
||||
## Sink 选择
|
||||
不要使用未在本参考中列出的事件选择参数;本 skill 只消费 `user_im_message_receive_o2o`。
|
||||
|
||||
| Flag 组合 | 行为 |
|
||||
|----------|------|
|
||||
| (默认) | stdout NDJSON |
|
||||
| `--output-dir <dir>` | 每事件写一个文件到该目录 |
|
||||
| `--route '<regex>=dir:<path>'` | 匹配事件路由到该目录,未匹配走 stdout |
|
||||
| `--route ... --output-dir <fallback>` | 匹配事件路由,未匹配走 fallback 目录 |
|
||||
## 输出
|
||||
|
||||
`--output-dir` / `--route` 与全局隐藏 `-o/--output`(单文件捕获)互斥;同时出现 → validation error。
|
||||
`-f ndjson` 每行是一个事件对象,常见字段:
|
||||
|
||||
## 退出码
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `event_type` | 应为 `user_im_message_receive_o2o` |
|
||||
| `subscribe_id` | 个人订阅 ID |
|
||||
| `source_id` | 当前 sourceId |
|
||||
| `data` | 服务端业务 payload 原文 |
|
||||
| `headers` | Stream 帧 headers |
|
||||
| `received_at_unix_ms` | 本地接收时间 |
|
||||
|
||||
| 场景 | 退出码 |
|
||||
|------|--------|
|
||||
| 优雅退出(ctx cancel / max-events / duration / bye)| 0 |
|
||||
| 下游 stdout 管道关(SIGPIPE)| 0(不被视为错误)|
|
||||
| validation error(flag 互斥、`--force` 不带 `--foreground` 等)| 5 |
|
||||
| 凭证缺失 | 5 |
|
||||
| bus 连接失败(discover deadline)| 5 |
|
||||
|
||||
## 信号
|
||||
|
||||
- `SIGINT` / `SIGTERM`:优雅退出,向 bus 发 Bye,返回 0
|
||||
- 下游 EPIPE:检测到 stdout 关后立即退出 0
|
||||
|
||||
## 跨平台
|
||||
|
||||
- Unix:`<ConfigDir>/events/<edition>/<clientIDHash>/bus.sock`(权限 0600)
|
||||
- Windows:Named Pipe `\\.\pipe\dws-event-<edition>-<clientIDHash>`(只允许当前用户)
|
||||
业务消息内容通常在 `data` 内部 payload 中,读取前先查看实际样本。
|
||||
|
||||
## 示例
|
||||
|
||||
### 基础订阅(catch-all 到 stdout)
|
||||
### 持续监听
|
||||
|
||||
```bash
|
||||
dws event consume
|
||||
dws event consume user_im_message_receive_o2o \
|
||||
--peer-user-id 507971 \
|
||||
-f ndjson
|
||||
```
|
||||
|
||||
### Agent 友好(推荐)
|
||||
### 监听 10 分钟
|
||||
|
||||
```bash
|
||||
dws event consume --event-types im.message.receive_v1 --compact --quiet
|
||||
dws event consume user_im_message_receive_o2o \
|
||||
--peer-user-id 507971 \
|
||||
--duration 10m \
|
||||
-f ndjson
|
||||
```
|
||||
|
||||
### 多类型订阅 + 过滤
|
||||
### 获取一条 JSON 样本
|
||||
|
||||
```bash
|
||||
dws event consume \
|
||||
--event-types im.message.receive_v1,im.message.at_v1,approval.instance.status_changed \
|
||||
--filter '^im\.' \
|
||||
--max-events 10
|
||||
dws event consume user_im_message_receive_o2o \
|
||||
--peer-user-id 507971 \
|
||||
--max-events 1 \
|
||||
-f json
|
||||
```
|
||||
|
||||
### 调试单条 SDK payload
|
||||
### 联调预发环境
|
||||
|
||||
```bash
|
||||
dws event consume -f raw --max-events 1
|
||||
dws event consume user_im_message_receive_o2o \
|
||||
--peer-user-id 507971 \
|
||||
--personal-event-base-url https://pre-mcp.dingtalk.com/dws \
|
||||
--stream-ticket-mode normal \
|
||||
--stream-source-id pre_open_source \
|
||||
--stream-ticket-url https://pre-mcp.dingtalk.com/stream/connections/ticket \
|
||||
-f ndjson
|
||||
```
|
||||
|
||||
### 持久化 + 路由
|
||||
## 停止
|
||||
|
||||
```bash
|
||||
dws event consume \
|
||||
--route '^im\.=dir:./events/im/' \
|
||||
--route '^approval\.=dir:./events/approval/' \
|
||||
--output-dir ./events/other/
|
||||
dws event status --event user_im_message_receive_o2o
|
||||
dws event stop <subscribe_id>
|
||||
```
|
||||
|
||||
### 前台模式(systemd / k8s)
|
||||
如果只想清理当前身份下本地记录的所有个人订阅:
|
||||
|
||||
```bash
|
||||
# Unit file 直接调用,不 fork daemon
|
||||
ExecStart=/usr/local/bin/dws event consume --foreground --quiet
|
||||
dws event stop --all
|
||||
```
|
||||
|
||||
### 一次性运行(CI / 测试)
|
||||
|
||||
```bash
|
||||
DWS_CLIENT_ID=$CI_DING_ID DWS_CLIENT_SECRET=$CI_DING_SECRET \
|
||||
dws event consume --max-events 1 --duration 30s --dry-run
|
||||
```
|
||||
|
||||
## 错误消息和恢复指引
|
||||
|
||||
- `--force is only meaningful with --foreground (...) To restart the bus: dws event stop && dws event consume`
|
||||
- `--format json requires --max-events or --duration (...) Use --format ndjson for unbounded streams.`
|
||||
- `app config missing: run \`dws config init\` or set DWS_CLIENT_ID/DWS_CLIENT_SECRET env vars`
|
||||
- `ClientSecret resolution failed (keychain unavailable?); try DWS_CLIENT_ID/DWS_CLIENT_SECRET env vars`
|
||||
- `WARN: only one of DWS_CLIENT_ID/DWS_CLIENT_SECRET is set; env fallback disabled, using keychain/app config`(半套 env 警告)
|
||||
|
||||
## See Also
|
||||
|
||||
- [`dws event status`](runbook.md#status)
|
||||
- [`dws event list`](runbook.md#list)
|
||||
- [`dws event stop`](runbook.md#stop)
|
||||
|
||||
@@ -1,171 +1,81 @@
|
||||
# 端到端冒烟手册 (Runbook)
|
||||
# 个人单聊事件自测流程
|
||||
|
||||
从零到看见第一条事件的全流程。
|
||||
## 1. 确认登录
|
||||
|
||||
## 1. 钉钉开发者后台配置
|
||||
|
||||
1. 访问 https://open.dingtalk.com/app → 创建应用(或选择已有应用)
|
||||
2. **应用能力** → 添加 **机器人**(如果做 IM 监听)
|
||||
3. **权限管理** → 申请:
|
||||
- `im:message`(接收消息)
|
||||
- `im:message.group_at_msg`(群里被 @)
|
||||
- `im:message.p2p_msg`(私聊消息)
|
||||
- `im:message:send_as_bot`(发送消息,可选)
|
||||
4. **事件订阅** → 选 "使用 **Stream 模式** 接收事件"(非 HTTP 推送)→ 勾选要订阅的事件类型,例如 `im.message.receive_v1`
|
||||
5. 在应用基本信息页拿 **AppKey** (= ClientID) 和 **AppSecret** (= ClientSecret)
|
||||
|
||||
## 2. 本机配置凭证
|
||||
|
||||
### 方式 A:keychain(macOS/Linux desktop 推荐)
|
||||
个人事件使用当前用户 OAuth 登录态。未登录或 token 失效时,先执行:
|
||||
|
||||
```bash
|
||||
dws config init
|
||||
# 按提示输入 AppKey 和 AppSecret,存入 OS keychain
|
||||
dws auth login
|
||||
```
|
||||
|
||||
### 方式 B:env var(CI/容器/SSH 推荐)
|
||||
## 2. 查看单聊事件说明
|
||||
|
||||
```bash
|
||||
export DWS_CLIENT_ID=ding_abcdef123
|
||||
export DWS_CLIENT_SECRET=YOUR_APP_SECRET
|
||||
dws event schema user_im_message_receive_o2o
|
||||
```
|
||||
|
||||
**注意**:两个 env var 必须**同时设置**才生效;只设一个会触发 stderr WARN 并回退到 keychain。
|
||||
确认事件规则是 `singleChat`,必填参数是 `peer-user-id` 或 `peer-union-id`。
|
||||
|
||||
## 3. 第一条消息
|
||||
## 3. 启动监听
|
||||
|
||||
终端 1:
|
||||
优先用对端 `userId`:
|
||||
|
||||
```bash
|
||||
dws event consume --event-types im.message.receive_v1 --compact --quiet
|
||||
dws event consume user_im_message_receive_o2o \
|
||||
--peer-user-id 507971 \
|
||||
--duration 10m \
|
||||
-f ndjson
|
||||
```
|
||||
|
||||
终端 2:用钉钉客户端给应用机器人发一条消息 "hello"
|
||||
|
||||
终端 1 应该看到一行:
|
||||
|
||||
```json
|
||||
{"type":"im.message.receive_v1","event_id":"ev_xxx","message_id":"om_xxx","chat_id":"oc_xxx","content":"hello","sender_id":"ou_xxx","timestamp":1700000000123}
|
||||
```
|
||||
|
||||
按 `Ctrl+C` 退出。
|
||||
|
||||
## 4. 查看 bus 状态
|
||||
如果只有 `unionId`:
|
||||
|
||||
```bash
|
||||
dws event status
|
||||
dws event consume user_im_message_receive_o2o \
|
||||
--peer-union-id <unionId> \
|
||||
--duration 10m \
|
||||
-f ndjson
|
||||
```
|
||||
|
||||
输出(运行中):
|
||||
## 4. 触发事件
|
||||
|
||||
```
|
||||
ClientID: ding_abcdef123
|
||||
Edition : open
|
||||
Workdir : /Users/me/.dws/events/open/e1092ee444e129c8...
|
||||
Bus : running pid=12345 uptime=15m20s
|
||||
Source : state=connected state_source=inferred reconnects=0
|
||||
Consumers: 1 active
|
||||
Per-event-type counters (since bus start):
|
||||
im.message.receive_v1 received=3 dropped=0
|
||||
Consumers:
|
||||
PID EVENT KEYS RECEIVED DROPPED
|
||||
12350 im.message.receive_v1 3 0
|
||||
```
|
||||
让对端用户给当前登录用户发送一条单聊消息。stdout 应输出一行事件 JSON。
|
||||
|
||||
orphan 状态(bus 进程被 `kill -9` 后):
|
||||
如果没有输出:
|
||||
|
||||
```
|
||||
Bus : orphan (last_pid=12345 not alive)
|
||||
Action : run `dws event consume` to force-restart, or rm -rf the workdir
|
||||
```
|
||||
1. 确认对端身份参数正确。
|
||||
2. 用 `dws event status --event user_im_message_receive_o2o` 查看订阅和本地连接状态。
|
||||
3. 联调服务端时临时加 `--debug --debug-raw-events`,观察当前 personal stream bus 是否收到服务端推送。
|
||||
|
||||
## 5. 列出所有 consumer
|
||||
## 5. 停止监听
|
||||
|
||||
先查订阅 ID:
|
||||
|
||||
```bash
|
||||
# 当前 ClientID
|
||||
dws event list
|
||||
|
||||
# 当前 edition 下所有 ClientID
|
||||
dws event list --all
|
||||
|
||||
# 跨 edition(调试用)
|
||||
dws event list --all-editions
|
||||
dws event status --event user_im_message_receive_o2o
|
||||
```
|
||||
|
||||
## 6. 优雅停止 bus
|
||||
停止指定订阅:
|
||||
|
||||
```bash
|
||||
dws event stop
|
||||
# bus stopped
|
||||
dws event stop <subscribe_id>
|
||||
```
|
||||
|
||||
工作机制:读 `bus.lock` 内 PID → SIGTERM → 等进程退出(默认 5s timeout)。
|
||||
|
||||
## 7. 健康检查(CI/监控)
|
||||
清理当前身份下本地记录的全部个人订阅:
|
||||
|
||||
```bash
|
||||
# orphan 时退出码 5(适合 cron / k8s liveness)
|
||||
dws event status --fail-on-orphan
|
||||
echo $? # 0 if healthy, 5 if orphan present
|
||||
|
||||
# JSON 输出供脚本消费
|
||||
dws event status -f json | jq '.[0].entry.state'
|
||||
# "running" | "orphan" | "not_running"
|
||||
dws event stop --all
|
||||
```
|
||||
|
||||
## 8. systemd 部署
|
||||
## 6. 安装到 Agent
|
||||
|
||||
`/etc/systemd/system/dws-event.service`:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=DingTalk Event Consumer
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=dws
|
||||
Environment=DWS_CLIENT_ID=ding_abcdef123
|
||||
Environment=DWS_CLIENT_SECRET=YOUR_SECRET
|
||||
Environment=DWS_EVENT_BUS_IDLE_TIMEOUT=24h
|
||||
ExecStart=/usr/local/bin/dws event consume \
|
||||
--foreground \
|
||||
--event-types im.message.receive_v1 \
|
||||
--compact --quiet \
|
||||
--output-dir /var/log/dws-events/
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
启用:
|
||||
安装 multi skill 的 event 子 skill:
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now dws-event
|
||||
sudo journalctl -u dws-event -f
|
||||
dws skill setup --mode multi -s event --target codex --source <repo> --yes
|
||||
```
|
||||
|
||||
## 9. 故障排查
|
||||
安装 mono skill:
|
||||
|
||||
| 现象 | 排查步骤 |
|
||||
|------|---------|
|
||||
| 跑 `consume` 立刻报 `app config missing` | 检查 `~/.dws/app.json` 是否存在;或设 env var |
|
||||
| `consume` 跑着但收不到事件 | (1) 开放平台后台确认事件订阅模式是 **Stream 长连接** 不是 HTTP;(2) 确认对应事件类型已勾选;(3) `dws event status` 看 source state |
|
||||
| `dws event status` 显示 orphan | bus 进程死了但 lock 没清。下次 `consume` 会自动清理;或 `dws event stop` 强制 |
|
||||
| stderr 频繁 `WARN: event type backpressure` | drop rate 超阈值。consumer 处理太慢。增大 `DWS_EVENT_CONSUMER_BUFFER` 或加快消费 |
|
||||
| `WARN: only one of DWS_CLIENT_ID/DWS_CLIENT_SECRET is set` | 两个 env var 必须同时设置;要么都设,要么都 unset |
|
||||
| `dws event status --all-editions` 显示意外的 wukong/* 条目 | 历史遗留(之前用过 wukong overlay)。安全:`rm -rf <ConfigDir>/events/wukong/` |
|
||||
|
||||
## 10. 数据位置
|
||||
|
||||
| 路径 | 用途 |
|
||||
|------|------|
|
||||
| `~/.dws/events/<edition>/<clientIDHash>/bus.lock` | 单文件锁 + PID |
|
||||
| `~/.dws/events/<edition>/<clientIDHash>/bus.meta` | 元数据(原 ClientID/edition/started_at)|
|
||||
| `~/.dws/events/<edition>/<clientIDHash>/bus.sock` | Unix Socket(Linux/macOS)|
|
||||
| `~/.dws/events/<edition>/<clientIDHash>/bus.log` | daemon slog 日志(含 drop WARN)|
|
||||
|
||||
Windows 用 `\\.\pipe\dws-event-<edition>-<clientIDHash>` 代替 sock。
|
||||
```bash
|
||||
dws skill setup --mode mono --target codex --source <repo> --yes
|
||||
```
|
||||
|
||||
@@ -47,6 +47,7 @@ Agent 安装 dws skill 后,仅依据 skill 提供的参考文档,将自然
|
||||
| `devdoc` | `references/products/simple.md` | 2 | 7 |
|
||||
| `dev` | `skills/multi/dingtalk-dev/SKILL.md` | 19 | 25 |
|
||||
| `ding` | `references/products/ding.md` | 2 | 5 |
|
||||
| `event` | `references/products/event.md` | 1 | 3 |
|
||||
| `report` | `references/products/report.md` | 6 | 26 |
|
||||
| `routing` | `references/intent-guide.md` | 1 | 3 |
|
||||
| `todo` | `references/products/todo.md` | 6 | 30 |
|
||||
@@ -993,6 +994,26 @@ Agent 安装 dws skill 后,仅依据 skill 提供的参考文档,将自然
|
||||
|
||||
---
|
||||
|
||||
### event(3 条)
|
||||
|
||||
#### `dws event consume user_im_message_receive_o2o`
|
||||
|
||||
**event_event_consume_o2o_001**
|
||||
- Prompt: 监听我和 userId 507971 的单聊消息
|
||||
- Expected: `dws event consume user_im_message_receive_o2o --peer-user-id 507971 -f ndjson`
|
||||
- Flags: `--peer-user-id` = `507971`
|
||||
|
||||
**event_event_consume_o2o_002**
|
||||
- Prompt: 订阅 unionId union123 给我发来的单聊消息
|
||||
- Expected: `dws event consume user_im_message_receive_o2o --peer-union-id union123 -f ndjson`
|
||||
- Flags: `--peer-union-id` = `union123`
|
||||
|
||||
**event_event_consume_o2o_003** `[ASK_USER]`
|
||||
- Prompt: 监听我的个人单聊消息
|
||||
- Expected: `dws event consume user_im_message_receive_o2o -f ndjson`
|
||||
|
||||
---
|
||||
|
||||
### ding(5 条)
|
||||
|
||||
#### `dws ding message recall`
|
||||
|
||||
Reference in New Issue
Block a user