diff --git a/internal/app/event_personal_output_test.go b/internal/app/event_personal_output_test.go index 47ea00f5..3a5fb515 100644 --- a/internal/app/event_personal_output_test.go +++ b/internal/app/event_personal_output_test.go @@ -18,7 +18,9 @@ import ( "encoding/json" "strings" "testing" + "time" + authpkg "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/auth" "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/event/personal" "github.com/spf13/cobra" ) @@ -43,10 +45,8 @@ func TestPersonalEventListHidesSchemaIDs(t *testing.T) { } got := out.String() assertPersonalOutputHidesSchemaIDs(t, got) - if strings.Contains(got, personal.EventFromUser) { - t.Fatalf("list output exposed hidden event %s: %s", personal.EventFromUser, got) - } for _, eventKey := range []string{ + personal.EventFromUser, personal.EventReadO2O, personal.EventReadGroup, personal.EventRecallO2O, @@ -76,8 +76,8 @@ func TestEventListDefaultsToUser(t *testing.T) { if !strings.Contains(got, personal.EventSingleChat) || !strings.Contains(got, "EVENT_KEY") { t.Fatalf("list output = %s, want personal event catalog", got) } - if strings.Contains(got, personal.EventFromUser) { - t.Fatalf("list output exposed hidden event %s: %s", personal.EventFromUser, got) + if !strings.Contains(got, personal.EventFromUser) { + t.Fatalf("list output missing public event %s: %s", personal.EventFromUser, got) } if strings.Contains(got, "CLIENT_ID") || strings.Contains(got, "ClientSecret") { t.Fatalf("list default appears to use legacy application output: %s", got) @@ -338,37 +338,54 @@ func TestEventSchemaDefaultsToUser(t *testing.T) { } } -func TestPersonalEventFromUserIsNotPubliclyAvailable(t *testing.T) { - for _, tc := range []struct { - name string - cmd *cobra.Command - args []string - }{ - { - name: "schema", - cmd: newEventSchemaCommand(), - args: []string{personal.EventFromUser}, - }, - { - name: "consume", - cmd: newEventConsumeCommand(), - args: []string{personal.EventFromUser, "--user", "507971", "--dry-run"}, - }, - { - name: "status", - cmd: newEventStatusCommand(), - args: []string{"--event", personal.EventFromUser}, - }, - } { - t.Run(tc.name, func(t *testing.T) { - tc.cmd.SilenceUsage = true - tc.cmd.SilenceErrors = true - tc.cmd.SetArgs(tc.args) - err := tc.cmd.Execute() - if err == nil || !strings.Contains(err.Error(), "event "+personal.EventFromUser+" is not publicly available yet") { - t.Fatalf("Execute() error = %v, want not publicly available", err) - } - }) +func TestPersonalEventFromUserIsPubliclyAvailable(t *testing.T) { + if err := ensurePublicPersonalEvent(personal.EventFromUser); err != nil { + t.Fatalf("ensurePublicPersonalEvent() error = %v", err) + } + + schemaCmd := newEventSchemaCommand() + schemaCmd.SilenceUsage = true + schemaCmd.SilenceErrors = true + var schemaOut bytes.Buffer + schemaCmd.SetOut(&schemaOut) + schemaCmd.SetArgs([]string{personal.EventFromUser}) + if err := schemaCmd.Execute(); err != nil { + t.Fatalf("schema Execute() error = %v", err) + } + var doc map[string]any + if err := json.Unmarshal(schemaOut.Bytes(), &doc); err != nil { + t.Fatalf("schema output is not JSON: %v\n%s", err, schemaOut.String()) + } + if doc["event_key"] != personal.EventFromUser || doc["rule_type"] != "sender" { + t.Fatalf("schema document = %#v", doc) + } + + configDir := setupPersonalIdentityToken(t, &authpkg.TokenData{ + AccessToken: "access-1", + RefreshToken: "refresh-1", + ExpiresAt: time.Now().Add(time.Hour), + RefreshExpAt: time.Now().Add(24 * time.Hour), + CorpID: "corp-1", + UserID: "user-1", + ClientID: "client-1", + }) + t.Setenv("DWS_CONFIG_DIR", configDir) + + consumeCmd := newEventConsumeCommand() + consumeCmd.SilenceUsage = true + consumeCmd.SilenceErrors = true + consumeCmd.SetArgs([]string{personal.EventFromUser, "--user", "507971", "--dry-run"}) + if err := consumeCmd.Execute(); err != nil { + t.Fatalf("consume dry-run Execute() error = %v", err) + } + + missingUserCmd := newEventConsumeCommand() + missingUserCmd.SilenceUsage = true + missingUserCmd.SilenceErrors = true + missingUserCmd.SetArgs([]string{personal.EventFromUser}) + err := missingUserCmd.Execute() + if err == nil || !strings.Contains(err.Error(), "--user is required for "+personal.EventFromUser) { + t.Fatalf("missing --user error = %v", err) } } diff --git a/internal/event/personal/client_test.go b/internal/event/personal/client_test.go index dc64ff86..bf084231 100644 --- a/internal/event/personal/client_test.go +++ b/internal/event/personal/client_test.go @@ -108,7 +108,7 @@ func TestClientCreateSubscriptionDWSRequestAndArrayResponse(t *testing.T) { } } -func TestClientCreateActionSubscriptionsUsesDocumentedRuleParam(t *testing.T) { +func TestClientCreateRuleBasedSubscriptionsUsesDocumentedRuleParam(t *testing.T) { tests := []struct { eventKey string opts RuleOptions @@ -117,6 +117,7 @@ func TestClientCreateActionSubscriptionsUsesDocumentedRuleParam(t *testing.T) { {EventReadO2O, RuleOptions{UserID: "staff-1"}, map[string]any{"targetUid": "staff-1", "targetUidType": "staffId"}}, {EventRecallO2O, RuleOptions{UserID: "staff-1"}, map[string]any{"targetUid": "staff-1", "targetUidType": "staffId"}}, {EventReactionO2O, RuleOptions{UserID: "staff-1"}, map[string]any{"targetUid": "staff-1", "targetUidType": "staffId"}}, + {EventFromUser, RuleOptions{UserID: "staff-1"}, map[string]any{"targetUid": "staff-1", "targetUidType": "staffId"}}, {EventReadGroup, RuleOptions{GroupID: "cid-1"}, map[string]any{"openConversationId": "cid-1"}}, {EventRecallGroup, RuleOptions{GroupID: "cid-1"}, map[string]any{"openConversationId": "cid-1"}}, {EventReactionGroup, RuleOptions{GroupID: "cid-1"}, map[string]any{"openConversationId": "cid-1"}}, @@ -132,7 +133,7 @@ func TestClientCreateActionSubscriptionsUsesDocumentedRuleParam(t *testing.T) { w.WriteHeader(http.StatusBadRequest) return } - _ = json.NewEncoder(w).Encode(map[string]any{"success": true, "result": []string{"sub-action"}}) + _ = json.NewEncoder(w).Encode(map[string]any{"success": true, "result": []string{"sub-rule"}}) })) defer srv.Close() diff --git a/internal/event/personal/registry.go b/internal/event/personal/registry.go index 84680121..9814562b 100644 --- a/internal/event/personal/registry.go +++ b/internal/event/personal/registry.go @@ -111,6 +111,17 @@ var definitions = []Definition{ Auth: map[string]any{"identity": "user"}, Public: true, }, + { + EventKey: EventFromUser, + DisplayName: "指定发送人消息", + Description: "当前用户收到的指定用户发送的消息", + Category: "im", + RuleType: "sender", + Status: StatusEnabled, + RequiredParams: []string{"user"}, + Auth: map[string]any{"identity": "user"}, + Public: true, + }, { EventKey: EventReadO2O, DisplayName: "指定单聊消息已读", @@ -177,17 +188,6 @@ var definitions = []Definition{ Auth: map[string]any{"identity": "user"}, Public: true, }, - { - EventKey: EventFromUser, - DisplayName: "指定发送人消息", - Description: "当前用户收到的特别关注用户的消息", - Category: "im", - RuleType: "sender", - Status: StatusEnabled, - RequiredParams: []string{"user"}, - Auth: map[string]any{"identity": "user"}, - Public: false, - }, } func Definitions() []Definition { diff --git a/internal/event/personal/registry_test.go b/internal/event/personal/registry_test.go index 92b0addc..6cbb46f1 100644 --- a/internal/event/personal/registry_test.go +++ b/internal/event/personal/registry_test.go @@ -33,6 +33,7 @@ func TestCatalogEnabledEvents(t *testing.T) { EventMention, EventSingleChat, EventInChat, + EventFromUser, EventReadO2O, EventReadGroup, EventRecallO2O, @@ -45,12 +46,12 @@ func TestCatalogEnabledEvents(t *testing.T) { } } -func TestEventFromUserRemainsInternalButNotPublic(t *testing.T) { +func TestEventFromUserIsPublic(t *testing.T) { if _, ok := Lookup(EventFromUser); !ok { - t.Fatalf("Lookup(%q) failed, want internal definition retained", EventFromUser) + t.Fatalf("Lookup(%q) failed", EventFromUser) } - if IsPublic(EventFromUser) { - t.Fatalf("IsPublic(%q) = true, want hidden", EventFromUser) + if !IsPublic(EventFromUser) { + t.Fatalf("IsPublic(%q) = false, want public", EventFromUser) } } diff --git a/internal/event/source/personal_test.go b/internal/event/source/personal_test.go index 59c67bf7..7effddd2 100644 --- a/internal/event/source/personal_test.go +++ b/internal/event/source/personal_test.go @@ -649,6 +649,44 @@ func TestPersonalSourceActionEventPassesNormalBusFilter(t *testing.T) { } } +func TestPersonalSourceSenderEventPassesNormalBusFilter(t *testing.T) { + const ( + eventKey = "user_im_message_receive_user" + subscribeID = "sub-receive-user" + ) + src := personalSourceForRawEventTests() + raw := src.rawEventFromDataFrame(&payload.DataFrame{ + Headers: payload.DataFrameHeader{ + "EVENT_TYPE": eventKey, + "SUB_ID": subscribeID, + }, + Data: `{"eventKey":"user_im_message_receive_user","subId":"sub-receive-user","payload":{}}`, + }) + h := bus.NewHub(10) + consumer, err := h.Register(transport.Hello{ + EventTypes: []string{eventKey}, + SubscribeID: subscribeID, + }) + if err != nil { + t.Fatal(err) + } + + h.Deliver(raw) + + select { + case frame := <-consumer.SendCh: + eventFrame, ok := frame.(transport.Event) + if !ok { + t.Fatalf("frame = %T, want transport.Event", frame) + } + if eventFrame.EventType != eventKey || eventFrame.SubscribeID != subscribeID { + t.Fatalf("event = %#v", eventFrame) + } + case <-time.After(time.Second): + t.Fatal("timed out waiting for sender event") + } +} + func personalSourceForRawEventTests() *PersonalSource { return &PersonalSource{cfg: PersonalConfig{ SourceID: "fallback_source", diff --git a/skills/mono/SKILL.md b/skills/mono/SKILL.md index e711e8a1..0a898a16 100644 --- a/skills/mono/SKILL.md +++ b/skills/mono/SKILL.md @@ -47,7 +47,7 @@ cli_version: ">=1.0.15" | `sheet` | 在线电子表格(axls):工作表 CRUD/区域读写/CSV 批量写入/行列增删/合并/查找替换/筛选视图/全局筛选/排序/下拉列表/条件格式/浮动图片/浮动图表/模板/导出 xlsx(单命令一站式) | [sheet.md](./references/products/sheet.md) | | `todo` | 待办:创建(含优先级/截止时间/循环)/查询/修改/标记完成/删除 | [todo.md](./references/products/todo.md) | | `wiki` | 知识库:空间创建/详情/列表/搜索 + 成员管理 | [wiki.md](./references/products/wiki.md) | -| `event` | 个人 IM 事件:监听消息接收、已读、撤回、表情回应,NDJSON 输出(实时驱动 Agent)| [event.md](./references/products/event.md) | +| `event` | 个人 IM 事件:监听消息接收、指定发送人、已读、撤回、表情回应,NDJSON 输出(实时驱动 Agent)| [event.md](./references/products/event.md) | ## 意图判断决策树 @@ -70,7 +70,7 @@ cli_version: ">=1.0.15" 用户提到"在线电子表格/钉钉表格/axls/工作表/单元格读写/合并单元格/筛选视图/导出 xlsx" → `sheet` 用户提到"待办/TODO/任务提醒/循环待办" → `todo` 用户提到"创建知识库/知识库列表/搜索知识库空间/wiki/团队空间/知识库成员管理/我的文档个人空间" → `wiki` -用户提到"监听有人@我/监听单聊或群消息/监听消息已读/监听消息撤回/监听消息贴表情或表情回应/订阅个人 IM 事件/实时接收钉钉事件/个人事件流/event consume user_im_message_*/监听并自动回复消息/驱动 Agent 处理消息" → `event` +用户提到"监听有人@我/监听单聊或群消息/监听某人发送的消息/监听消息已读/监听消息撤回/监听消息贴表情或表情回应/订阅个人 IM 事件/实时接收钉钉事件/个人事件流/event consume user_im_message_*/监听并自动回复消息/驱动 Agent 处理消息" → `event` 关键区分: aitable(数据表格) vs todo(待办任务) 关键区分: report(钉钉日志/日报周报) vs todo(待办任务) diff --git a/skills/mono/references/products/event.md b/skills/mono/references/products/event.md index 5c03a0d2..db0b45b5 100644 --- a/skills/mono/references/products/event.md +++ b/skills/mono/references/products/event.md @@ -19,6 +19,7 @@ | `user_im_message_receive_at` | 当前用户被 @ 的消息 | 无 | | `user_im_message_receive_o2o` | 当前用户与指定用户的单聊消息 | `--user` | | `user_im_message_receive_group` | 当前用户所在指定群聊/会话的消息 | `--group` | +| `user_im_message_receive_user` | 当前用户收到的指定用户发送的消息(单聊和群聊) | `--user` | | `user_im_message_read_o2o` | 指定单聊中当前用户发送的消息被已读 | `--user` | | `user_im_message_read_group` | 指定群聊中当前用户发送的消息被已读 | `--group` | | `user_im_message_recall_o2o` | 指定单聊中的消息被撤回 | `--user` | @@ -26,7 +27,7 @@ | `user_im_message_reaction_o2o` | 指定单聊中的消息收到表情回应 | `--user` | | `user_im_message_reaction_group` | 指定群聊中的消息收到表情回应 | `--group` | -只承认上表 9 个事件码。默认身份就是当前用户,不要额外加身份切换 flag。 +只承认上表 10 个事件码。默认身份就是当前用户,不要额外加身份切换 flag。 ## Intent mapping @@ -35,6 +36,7 @@ | "监听有人 @ 我的消息" | `event consume`,事件码 `user_im_message_receive_at`,参数 `-f ndjson` | | "监听我和 userId 507971 的单聊消息" | `event consume`,事件码 `user_im_message_receive_o2o`,参数 `--user 507971 -f ndjson` | | "监听 XX 群消息" | 先 `dws chat search --query "XX" --format json`,确认后 consume group | +| "监听 userId 507971 发给我的消息" | `event consume`,事件码 `user_im_message_receive_user`,参数 `--user 507971 -f ndjson` | | "监听我发给 userId 507971 的消息是否已读" | `event consume`,事件码 `user_im_message_read_o2o`,参数 `--user 507971 -f ndjson` | | "监听 XX 群消息已读" | 先解析群 ID,再 consume `user_im_message_read_group --group ` | | "监听我和 userId 507971 的消息撤回" | `event consume`,事件码 `user_im_message_recall_o2o`,参数 `--user 507971 -f ndjson` | @@ -48,7 +50,7 @@ 多候选必须让用户确认。缺少必填 ID 且无法解析时先追问,不要猜测。 -用户要求执行“撤回消息”时走 `dws chat`;只有“监听/订阅消息撤回”才走 `dws event`。“贴标签”表示给消息贴表情时,对应 `emotion` 表情回应事件。 +“我和某人的单聊”使用 `receive_o2o`;“某人发给我的消息/某人发送的消息”使用 `receive_user`,后者覆盖该发送人的单聊和群聊消息。用户要求执行“撤回消息”时走 `dws chat`;只有“监听/订阅消息撤回”才走 `dws event`。“贴标签”表示给消息贴表情时,对应 `reaction` 表情回应事件。 ## Call flow @@ -65,6 +67,7 @@ dws event schema user_im_message_receive_at dws event schema user_im_message_receive_o2o dws event schema user_im_message_receive_group +dws event schema user_im_message_receive_user dws event schema user_im_message_read_o2o dws event schema user_im_message_read_group dws event schema user_im_message_recall_o2o @@ -89,6 +92,12 @@ dws event consume user_im_message_receive_group \ -f ndjson ``` +```bash +dws event consume user_im_message_receive_user \ + --user 507971 \ + -f ndjson +``` + ```bash dws event consume user_im_message_read_o2o --user 507971 -f ndjson dws event consume user_im_message_read_group --group -f ndjson @@ -102,6 +111,7 @@ dws event consume user_im_message_reaction_group --group -f dws event status --event user_im_message_receive_at dws event status --event user_im_message_receive_o2o dws event status --event user_im_message_receive_group +dws event status --event user_im_message_receive_user dws event stop ``` diff --git a/skills/multi/dingtalk-event/SKILL.md b/skills/multi/dingtalk-event/SKILL.md index 10859df3..8468e5f6 100644 --- a/skills/multi/dingtalk-event/SKILL.md +++ b/skills/multi/dingtalk-event/SKILL.md @@ -1,6 +1,6 @@ --- name: dingtalk-event -description: 钉钉个人 IM 事件长连接监听、订阅与消费,覆盖消息接收、已读、撤回和表情回应,输出 NDJSON 到 stdout。Use when 用户提到 监听个人消息事件、被@消息、监听单聊或群消息、监听消息已读、监听消息撤回、监听消息贴表情或表情回应、实时接收钉钉事件、用事件驱动 Agent。命令前缀:dws event。 +description: 钉钉个人 IM 事件长连接监听、订阅与消费,覆盖消息接收、指定发送人、已读、撤回和表情回应,输出 NDJSON 到 stdout。Use when 用户提到 监听个人消息事件、被@消息、监听单聊或群消息、监听某人发送的消息、监听消息已读、监听消息撤回、监听消息贴表情或表情回应、实时接收钉钉事件、用事件驱动 Agent。命令前缀:dws event。 --- # 钉钉个人 IM 事件 @@ -25,6 +25,7 @@ description: 钉钉个人 IM 事件长连接监听、订阅与消费,覆盖消 | `user_im_message_receive_at` | 当前用户被 @ 的消息 | 无 | | `user_im_message_receive_o2o` | 当前用户与指定用户的单聊消息 | `--user` | | `user_im_message_receive_group` | 当前用户所在指定群聊/会话的消息 | `--group` | +| `user_im_message_receive_user` | 当前用户收到的指定用户发送的消息(单聊和群聊) | `--user` | | `user_im_message_read_o2o` | 指定单聊中当前用户发送的消息被已读 | `--user` | | `user_im_message_read_group` | 指定群聊中当前用户发送的消息被已读 | `--group` | | `user_im_message_recall_o2o` | 指定单聊中的消息被撤回 | `--user` | @@ -32,7 +33,7 @@ description: 钉钉个人 IM 事件长连接监听、订阅与消费,覆盖消 | `user_im_message_reaction_o2o` | 指定单聊中的消息收到表情回应 | `--user` | | `user_im_message_reaction_group` | 指定群聊中的消息收到表情回应 | `--group` | -只承认上表 9 个事件码。其它身份模式、应用凭证模式、非个人 IM 事件不在本 skill 范围内。 +只承认上表 10 个事件码。其它身份模式、应用凭证模式、非个人 IM 事件不在本 skill 范围内。 ## Command rules @@ -41,9 +42,10 @@ description: 钉钉个人 IM 事件长连接监听、订阅与消费,覆盖消 - 不主动运行 `dws event list` 作为能力菜单;按用户意图直接选择上表事件。 - 缺少必填 ID 时先解析或追问,不要猜测 ID。 - 用户只给单聊对端人名时,先运行 `dws aisearch person --keyword "" --dimension name --format json` 解析 userId;多候选必须让用户确认。 +- “监听我和某人的单聊”使用 `user_im_message_receive_o2o`;“监听某人发给我的消息/监听某人发送的消息”使用 `user_im_message_receive_user`,后者覆盖该发送人的单聊和群聊消息。 - 用户只给群名时,先运行 `dws chat search --query "" --format json` 解析 openConversationId;多候选必须让用户确认。 - 用户要求执行“撤回消息”时使用 `dws chat`;只有“监听/订阅消息撤回”才使用 `dws event consume user_im_message_recall_*`。 -- 用户说“贴标签”且语义是给消息贴表情时,按消息表情回应事件处理,event key 使用 `emotion`。 +- 用户说“贴标签”且语义是给消息贴表情时,按消息表情回应事件处理,event key 使用 `reaction`。 - 正常 Agent 消费使用 `-f ndjson`。抓一条样本可用 `--max-events 1 -f json`。 - `--debug-raw-events` 只用于联调确认服务端推送是否到达本地连接;正常任务不要使用。 @@ -81,6 +83,11 @@ dws event consume user_im_message_receive_group \ --group cidxxxxxxxx \ -f ndjson +# 指定发送人的消息(单聊和群聊) +dws event consume user_im_message_receive_user \ + --user 507971 \ + -f ndjson + # 指定单聊消息已读 dws event consume user_im_message_read_o2o \ --user 507971 \ @@ -120,4 +127,4 @@ dws event consume user_im_message_receive_o2o \ | Topic | Reference | Coverage | |---|---|---| -| IM | [references/event-im.md](references/event-im.md) | 九类个人 IM 事件命令、参数、生命周期、输出解析、自测和排障 | +| IM | [references/event-im.md](references/event-im.md) | 十类个人 IM 事件命令、参数、生命周期、输出解析、自测和排障 | diff --git a/skills/multi/dingtalk-event/references/event-im.md b/skills/multi/dingtalk-event/references/event-im.md index 2565b8ea..62a0b4ce 100644 --- a/skills/multi/dingtalk-event/references/event-im.md +++ b/skills/multi/dingtalk-event/references/event-im.md @@ -18,6 +18,7 @@ dws auth login dws event schema user_im_message_receive_at dws event schema user_im_message_receive_o2o dws event schema user_im_message_receive_group +dws event schema user_im_message_receive_user dws event schema user_im_message_read_o2o dws event schema user_im_message_read_group dws event schema user_im_message_recall_o2o @@ -35,6 +36,7 @@ schema 默认 JSON。业务字段说明在 `schema.properties`,当前业务 pa | `user_im_message_receive_at` | `at` | 当前用户被 @ 的消息 | 无 | | `user_im_message_receive_o2o` | `singleChat` | 当前用户与指定用户的单聊消息 | `--user` | | `user_im_message_receive_group` | `group` | 当前用户所在指定群聊/会话的消息 | `--group` | +| `user_im_message_receive_user` | `sender` | 当前用户收到的指定用户发送的消息(单聊和群聊) | `--user` | | `user_im_message_read_o2o` | `singleChat` | 指定单聊中当前用户发送的消息被已读 | `--user` | | `user_im_message_read_group` | `group` | 指定群聊中当前用户发送的消息被已读 | `--group` | | `user_im_message_recall_o2o` | `singleChat` | 指定单聊中的消息被撤回 | `--user` | @@ -47,11 +49,12 @@ schema 默认 JSON。业务字段说明在 `schema.properties`,当前业务 pa ## ID resolution - 人名 → `dws aisearch person --keyword "" --dimension name --format json`,确认后取 `userId`。 +- “我和某人的单聊”选择 `user_im_message_receive_o2o`;“某人发给我的消息/某人发送的消息”选择 `user_im_message_receive_user`。 - 群名 → `dws chat search --query "" --format json`,确认后取 `openConversationId`。 - 多候选 → 展示候选并让用户确认。 - 仍缺必填 ID → 先追问,不要编造。 - “撤回消息”表示执行操作时走 `dws chat`;“监听/订阅消息撤回”才走本事件能力。 -- “贴标签”表示给消息贴表情时,对应 `emotion` 表情回应事件。 +- “贴标签”表示给消息贴表情时,对应 `reaction` 表情回应事件。 ## Consume commands @@ -69,6 +72,11 @@ dws event consume user_im_message_receive_group \ --group cidxxxxxxxx \ -f ndjson +# 指定发送人的消息(单聊和群聊) +dws event consume user_im_message_receive_user \ + --user 507971 \ + -f ndjson + # 指定单聊已读事件 dws event consume user_im_message_read_o2o \ --user 507971 \ @@ -107,6 +115,7 @@ dws event consume user_im_message_reaction_group \ | `user_im_message_receive_at` | `--duration 10m -f ndjson` | 让任意可触达用户在群里 @ 当前登录用户 | | `user_im_message_receive_o2o` | `--user --duration 10m -f ndjson` | 让对端用户给当前登录用户发送单聊消息 | | `user_im_message_receive_group` | `--group --duration 10m -f ndjson` | 让任意用户在该群发送消息 | +| `user_im_message_receive_user` | `--user --duration 10m -f ndjson` | 让指定用户分别在单聊或共同群聊中发送消息 | | `user_im_message_read_o2o` | `--user --duration 10m -f ndjson` | 当前用户给对端发送单聊消息,再让对端打开并阅读 | | `user_im_message_read_group` | `--group --duration 10m -f ndjson` | 当前用户在群内发送消息,再让群成员打开并阅读 | | `user_im_message_recall_o2o` | `--user --duration 10m -f ndjson` | 在指定单聊中发送并撤回一条消息 | @@ -188,6 +197,7 @@ dws event consume user_im_message_receive_group \ dws event status --event user_im_message_receive_at dws event status --event user_im_message_receive_o2o dws event status --event user_im_message_receive_group +dws event status --event user_im_message_receive_user dws event status --event user_im_message_read_o2o dws event status --event user_im_message_recall_group dws event status --event user_im_message_reaction_o2o diff --git a/test/skill_tests.md b/test/skill_tests.md index d6014f57..c4bbdb80 100644 --- a/test/skill_tests.md +++ b/test/skill_tests.md @@ -994,7 +994,7 @@ Agent 安装 dws skill 后,仅依据 skill 提供的参考文档,将自然 --- -### event(14 条) +### event(15 条) #### `dws event consume user_im_message_receive_at` @@ -1025,6 +1025,13 @@ Agent 安装 dws skill 后,仅依据 skill 提供的参考文档,将自然 - Expected: `dws event consume user_im_message_receive_group --group cid123 -f ndjson` - Flags: `--group` = `cid123` +#### `dws event consume user_im_message_receive_user` + +**event_event_consume_user_001** +- Prompt: 监听 userId 507971 发给我的消息,包括单聊和群聊 +- Expected: `dws event consume user_im_message_receive_user --user 507971 -f ndjson` +- Flags: `--user` = `507971` + #### `dws event consume user_im_message_read_o2o` **event_event_consume_read_o2o_001**