Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions server/internal/integrations/lark/feishu_channel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,23 @@ func TestChannelMessageFromLark_NormalizesAndStashesRaw(t *testing.T) {
}
}

func TestChannelMessageFromLark_ImageKeepsPlaceholder(t *testing.T) {
cm := channelMessageFromLark(InboundMessage{
MessageID: "om_image",
ChatID: "oc",
ChatType: ChatTypeP2P,
Body: "[Image]",
MessageType: "image",
})

if cm.Type != channel.MsgTypeImage {
t.Fatalf("image must normalize to image, got %q", cm.Type)
}
if cm.Text != "[Image]" {
t.Fatalf("image placeholder lost during normalization: %q", cm.Text)
}
}

func TestChannelMsgType(t *testing.T) {
cases := map[string]channel.MsgType{
"": channel.MsgTypeText,
Expand Down
16 changes: 10 additions & 6 deletions server/internal/integrations/lark/ws_frame_decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,20 @@ func (d *LarkJSONFrameDecoder) Decode(payload []byte, inst Installation) (Inboun
botUnionID = inst.BotUnionID.String
}

// text + post are flattened synchronously here (no external calls —
// the decoder must stay fast and dependency-free). merge_forward
// leaves Body empty: it needs an HTTP round-trip to expand and is
// handled downstream by the enricher, which keys off MessageType.
// Other types (image, file, …) also leave Body empty in this MVP;
// attachment ingestion is a separate issue.
// All directly renderable types are flattened synchronously here (no
// external calls — the decoder must stay fast and dependency-free).
// Media messages become stable placeholders such as [Image], so an
// image-only event never lands in chat history as an empty message.
// merge_forward alone leaves Body empty: it needs an HTTP round-trip to
// expand and is handled downstream by the enricher.
switch evt.Message.MessageType {
case "text", "post":
msg.Body = resolveMentions(flattenContent(evt.Message.MessageType, evt.Message.Content),
evt.Message.Mentions, inst.BotOpenID, botUnionID)
case "merge_forward":
// Expanded by InboundEnricher.
default:
msg.Body = flattenContent(evt.Message.MessageType, evt.Message.Content)
}

// Snapshot the user's own text as the command source BEFORE any
Expand Down
6 changes: 3 additions & 3 deletions server/internal/integrations/lark/ws_frame_decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ func TestLarkJSONFrameDecoderMessageContentEmptyOnInvalidContentJSON(t *testing.
}
}

func TestLarkJSONFrameDecoderNonTextMessageHasEmptyBody(t *testing.T) {
func TestLarkJSONFrameDecoderImageMessageHasPlaceholderBody(t *testing.T) {
t.Parallel()
raw := []byte(`{
"type":"event_callback",
Expand All @@ -443,8 +443,8 @@ func TestLarkJSONFrameDecoderNonTextMessageHasEmptyBody(t *testing.T) {
if err != nil || !ok {
t.Fatalf("ok=%v err=%v", ok, err)
}
if msg.Body != "" {
t.Errorf("Body = %q; non-text messages should have empty body in MVP", msg.Body)
if msg.Body != "[Image]" {
t.Errorf("Body = %q; want [Image]", msg.Body)
}
if msg.MessageID == "" {
t.Error("MessageID should still be populated for non-text events")
Expand Down