mirror of
https://github.com/larksuite/cli.git
synced 2026-07-08 10:08:02 +08:00
fix(vc): keep bot activity meeting per item
This commit is contained in:
@@ -19,7 +19,6 @@ type VCBotEventOutput struct {
|
||||
Timestamp string `json:"timestamp,omitempty" desc:"Event delivery time (ms timestamp string); taken from header.create_time when present" kind:"timestamp_ms"`
|
||||
CallID string `json:"call_id,omitempty" desc:"Bot invitation call ID; pass through to vc agent join when present"`
|
||||
MeetingNo string `json:"meeting_no,omitempty" desc:"Meeting number from the bot event's declared meeting field"`
|
||||
Meeting map[string]interface{} `json:"meeting,omitempty" desc:"Meeting object extracted from the first activity item that carries one"`
|
||||
MeetingActivityItems []map[string]interface{} `json:"meeting_activity_items,omitempty" desc:"event.meeting_activity_items extracted one level up for direct consumption; nested user id objects keep id.open_id"`
|
||||
Payload json.RawMessage `json:"payload,omitempty" desc:"Original bot event body without schema/header envelope for non-activity events or malformed activity payloads"`
|
||||
}
|
||||
@@ -98,7 +97,6 @@ func fillBotEventOutput(eventType string, data json.RawMessage, out *VCBotEventO
|
||||
return
|
||||
}
|
||||
out.MeetingNo = botActivityMeetingNo(items)
|
||||
out.Meeting = normalizeBotActivityMeeting(items)
|
||||
meetingActivityItems := normalizeBotActivityItems(items)
|
||||
if len(meetingActivityItems) == 0 {
|
||||
out.Payload = cloneRawMessage(data)
|
||||
@@ -159,19 +157,6 @@ func botActivityMeetingNo(items []interface{}) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func normalizeBotActivityMeeting(items []interface{}) map[string]interface{} {
|
||||
for _, raw := range items {
|
||||
item, _ := raw.(map[string]interface{})
|
||||
meeting, _ := item["meeting"].(map[string]interface{})
|
||||
if len(meeting) == 0 {
|
||||
continue
|
||||
}
|
||||
normalized, _ := normalizeBotActivityValue(meeting).(map[string]interface{})
|
||||
return normalized
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func normalizeBotActivityItems(items []interface{}) []map[string]interface{} {
|
||||
normalized := make([]map[string]interface{}, 0, len(items))
|
||||
for _, raw := range items {
|
||||
@@ -181,7 +166,6 @@ func normalizeBotActivityItems(items []interface{}) []map[string]interface{} {
|
||||
}
|
||||
normalizedItem, _ := normalizeBotActivityValue(item).(map[string]interface{})
|
||||
if normalizedItem != nil {
|
||||
delete(normalizedItem, "meeting")
|
||||
normalized = append(normalized, normalizedItem)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,9 +216,8 @@ func TestProcessVCBotEvents_StableFields(t *testing.T) {
|
||||
if _, ok := row["payload"]; ok {
|
||||
t.Fatalf("meeting activity output should lift meeting_activity_items out of payload: %s", string(got))
|
||||
}
|
||||
meeting, ok := row["meeting"].(map[string]any)
|
||||
if !ok || meeting["meeting_no"] != tc.want.MeetingNo {
|
||||
t.Fatalf("meeting activity output should include top-level meeting: %s", string(got))
|
||||
if _, ok := row["meeting"]; ok {
|
||||
t.Fatalf("meeting activity output should not include top-level meeting: %s", string(got))
|
||||
}
|
||||
items, ok := row["meeting_activity_items"].([]any)
|
||||
if !ok || len(items) == 0 {
|
||||
@@ -226,8 +225,12 @@ func TestProcessVCBotEvents_StableFields(t *testing.T) {
|
||||
}
|
||||
for _, rawItem := range items {
|
||||
item, _ := rawItem.(map[string]any)
|
||||
if _, ok := item["meeting"]; ok {
|
||||
t.Fatalf("meeting_activity_items should not repeat meeting: %s", string(got))
|
||||
meeting, ok := item["meeting"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("meeting_activity_items should keep meeting on each item: %s", string(got))
|
||||
}
|
||||
if meeting["meeting_no"] == nil {
|
||||
t.Fatalf("meeting_activity_items meeting should keep meeting_no: %s", string(got))
|
||||
}
|
||||
}
|
||||
if tc.name == "meeting activity ignores nested reaction details" {
|
||||
|
||||
@@ -149,6 +149,6 @@ Lark-defined semantic tags (**not** JSON Schema's standard `format`). Common val
|
||||
|------------|------------------------------------------------------------------------------|---|
|
||||
| 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) | VC user events plus bot-observed EventKeys (`vc.bot.meeting_invited_v1`, `vc.bot.meeting_activity_v1`, `vc.bot.meeting_ended_v1`) + compact stable fields, event-body payload reference, and meeting-events forwarding guidance |
|
||||
| VC | [`references/lark-event-vc.md`](references/lark-event-vc.md) | VC user events plus bot-observed EventKeys (`vc.bot.meeting_invited_v1`, `vc.bot.meeting_activity_v1`, `vc.bot.meeting_ended_v1`) + compact stable fields, activity `meeting_activity_items`, fallback payload semantics, and meeting-events forwarding guidance |
|
||||
| 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) |
|
||||
|
||||
@@ -141,12 +141,11 @@ These keys model what the bot observes. Do not treat them as aliases for:
|
||||
| `event_id` | string | Globally unique event ID; safe for deduplication |
|
||||
| `timestamp` | string (timestamp_ms) | Event delivery time from `header.create_time` when present |
|
||||
| `call_id` | string | Invitation call ID; pass through to VC agent join when present |
|
||||
| `meeting_no` | string | Meeting number from the bot event's declared meeting field |
|
||||
| `meeting` | object | First `event.meeting_activity_items[].meeting` object extracted one level up for `vc.bot.meeting_activity_v1` |
|
||||
| `meeting_activity_items` | object[] | `event.meeting_activity_items` extracted one level up for `vc.bot.meeting_activity_v1`; item-level duplicate `meeting` is removed, each item carries its own `activity_event_type`, and nested user `id` objects keep `id.open_id` |
|
||||
| `payload` | object | Original bot event body without the `schema` / `header` envelope for non-activity events or malformed activity payloads |
|
||||
| `meeting_no` | string | Stable top-level meeting number. For `vc.bot.meeting_activity_v1`, this mirrors the first activity item's `meeting.meeting_no`; for invited/ended events, it comes from the event body directly. Prefer this field for routing and joins across `vc.bot.*` keys |
|
||||
| `meeting_activity_items` | object[] | Only emitted for normal `vc.bot.meeting_activity_v1` output. This is `event.meeting_activity_items` lifted one level up; each item keeps its own `activity_event_type` and `meeting`, and nested user `id` objects keep only `id.open_id` |
|
||||
| `payload` | object | Fallback/event-body field for non-activity bot events (`vc.bot.meeting_invited_v1`, `vc.bot.meeting_ended_v1`) or malformed activity payloads. Normal `vc.bot.meeting_activity_v1` output does not include `payload` |
|
||||
|
||||
Normal bot-event output intentionally does not include the full raw envelope. Use stable top-level fields for routing; for `vc.bot.meeting_activity_v1`, consume `meeting_activity_items[]` directly and branch on each item's `activity_event_type`. Do not assume one delivered bot event contains only one activity type. The repeated item-level `meeting` object is lifted to top-level `meeting` to avoid duplicating meeting metadata in every item. For richer cross-event meeting analysis or IM forwarding, prefer `vc +meeting-events --format json`, which normalizes actors, chat/reaction payloads, subtitles, and magic-share events. If the top-level event envelope cannot be parsed at all, `event consume` still passes the raw payload through for diagnostics.
|
||||
Normal bot-event output intentionally does not include the full raw envelope. Use stable top-level fields for routing. For `vc.bot.meeting_activity_v1`, consume `meeting_activity_items[]` directly and branch on each item's `activity_event_type`; do not read activity content from `payload`, because normal activity output does not emit it. Do not assume one delivered bot event contains only one activity type. Meeting metadata stays on each activity item under `meeting`; there is no top-level `meeting` field. `meeting_no` remains top-level even when item-level `meeting.meeting_no` is also present so agents can use the same scalar field across invited, activity, and ended bot events. For richer cross-event meeting analysis or IM forwarding, prefer `vc +meeting-events --format json`, which normalizes actors, chat/reaction payloads, subtitles, and magic-share events. If the top-level event envelope cannot be parsed at all, `event consume` still passes the raw payload through for diagnostics.
|
||||
|
||||
### Forwarding meeting activity to IM
|
||||
|
||||
|
||||
Reference in New Issue
Block a user