Merge PR #1327
This commit is contained in:
commit
89237781c0
4 changed files with 182 additions and 6 deletions
|
|
@ -62,6 +62,61 @@ func extractJSONStringField(content, field string) string {
|
||||||
// Format: {"image_key": "img_xxx"}
|
// Format: {"image_key": "img_xxx"}
|
||||||
func extractImageKey(content string) string { return extractJSONStringField(content, "image_key") }
|
func extractImageKey(content string) string { return extractJSONStringField(content, "image_key") }
|
||||||
|
|
||||||
|
// extractPostImageKeys extracts all image_key values from a Feishu post (rich text) message.
|
||||||
|
// Post content format: {"title":"...","content":[[{"tag":"img","image_key":"..."},...],...]}.
|
||||||
|
func extractPostImageKeys(rawContent string) []string {
|
||||||
|
var post struct {
|
||||||
|
Content [][]struct {
|
||||||
|
Tag string `json:"tag"`
|
||||||
|
ImageKey string `json:"image_key"`
|
||||||
|
} `json:"content"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal([]byte(rawContent), &post); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var keys []string
|
||||||
|
for _, paragraph := range post.Content {
|
||||||
|
for _, elem := range paragraph {
|
||||||
|
if elem.Tag == "img" && elem.ImageKey != "" {
|
||||||
|
keys = append(keys, elem.ImageKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractPostText extracts plain text from a Feishu post (rich text) message.
|
||||||
|
// Returns title and text elements joined by newlines. Falls back to rawContent on parse error.
|
||||||
|
func extractPostText(rawContent string) string {
|
||||||
|
var post struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
Content [][]struct {
|
||||||
|
Tag string `json:"tag"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
} `json:"content"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal([]byte(rawContent), &post); err != nil {
|
||||||
|
return rawContent
|
||||||
|
}
|
||||||
|
|
||||||
|
var parts []string
|
||||||
|
if post.Title != "" {
|
||||||
|
parts = append(parts, post.Title)
|
||||||
|
}
|
||||||
|
for _, paragraph := range post.Content {
|
||||||
|
for _, elem := range paragraph {
|
||||||
|
if elem.Tag == "text" && elem.Text != "" {
|
||||||
|
parts = append(parts, elem.Text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(parts) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.Join(parts, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
// extractFileKey extracts the file_key from a Feishu file/audio message content JSON.
|
// extractFileKey extracts the file_key from a Feishu file/audio message content JSON.
|
||||||
// Format: {"file_key": "file_xxx", "file_name": "...", ...}
|
// Format: {"file_key": "file_xxx", "file_name": "...", ...}
|
||||||
func extractFileKey(content string) string { return extractJSONStringField(content, "file_key") }
|
func extractFileKey(content string) string { return extractJSONStringField(content, "file_key") }
|
||||||
|
|
|
||||||
|
|
@ -134,6 +134,93 @@ func TestExtractFileKey(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestExtractPostImageKeys(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
rawContent string
|
||||||
|
want []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "text and image",
|
||||||
|
rawContent: `{"title":"","content":[[{"tag":"img","image_key":"img_v3_abc","width":100,"height":100}],[{"tag":"text","text":"hello","style":[]}]]}`,
|
||||||
|
want: []string{"img_v3_abc"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple images",
|
||||||
|
rawContent: `{"title":"","content":[[{"tag":"img","image_key":"img_1","width":100,"height":100},{"tag":"img","image_key":"img_2","width":100,"height":100}]]}`,
|
||||||
|
want: []string{"img_1", "img_2"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no images",
|
||||||
|
rawContent: `{"title":"","content":[[{"tag":"text","text":"hello","style":[]}]]}`,
|
||||||
|
want: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid JSON",
|
||||||
|
rawContent: `not json`,
|
||||||
|
want: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty content",
|
||||||
|
rawContent: `{"title":"","content":[]}`,
|
||||||
|
want: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := extractPostImageKeys(tt.rawContent)
|
||||||
|
if len(got) != len(tt.want) {
|
||||||
|
t.Errorf("extractPostImageKeys() = %v, want %v", got, tt.want)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := range got {
|
||||||
|
if got[i] != tt.want[i] {
|
||||||
|
t.Errorf("extractPostImageKeys()[%d] = %q, want %q", i, got[i], tt.want[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtractPostText(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
rawContent string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "text and image",
|
||||||
|
rawContent: `{"title":"","content":[[{"tag":"img","image_key":"img_v3_abc","width":100,"height":100}],[{"tag":"text","text":"图里有啥","style":[]}]]}`,
|
||||||
|
want: "图里有啥",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with title",
|
||||||
|
rawContent: `{"title":"My Post","content":[[{"tag":"text","text":"body text","style":[]}]]}`,
|
||||||
|
want: "My Post\nbody text",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only images returns empty",
|
||||||
|
rawContent: `{"title":"","content":[[{"tag":"img","image_key":"img_v3_abc","width":100,"height":100}]]}`,
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid JSON returns raw",
|
||||||
|
rawContent: `not json`,
|
||||||
|
want: "not json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := extractPostText(tt.rawContent)
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("extractPostText(%q) = %q, want %q", tt.rawContent, got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestExtractFileName(t *testing.T) {
|
func TestExtractFileName(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
|
|
||||||
|
|
@ -504,8 +504,8 @@ func extractContent(messageType, rawContent string) string {
|
||||||
return rawContent
|
return rawContent
|
||||||
|
|
||||||
case larkim.MsgTypePost:
|
case larkim.MsgTypePost:
|
||||||
// Pass raw JSON to LLM — structured rich text is more informative than flattened plain text
|
// Extract plain text from post; images are handled separately via downloadInboundMedia.
|
||||||
return rawContent
|
return extractPostText(rawContent)
|
||||||
|
|
||||||
case larkim.MsgTypeImage:
|
case larkim.MsgTypeImage:
|
||||||
// Image messages don't have text content
|
// Image messages don't have text content
|
||||||
|
|
@ -544,6 +544,14 @@ func (c *FeishuChannel) downloadInboundMedia(
|
||||||
refs = append(refs, ref)
|
refs = append(refs, ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case larkim.MsgTypePost:
|
||||||
|
for _, imageKey := range extractPostImageKeys(rawContent) {
|
||||||
|
ref := c.downloadResource(ctx, messageID, imageKey, "image", ".jpg", store, scope)
|
||||||
|
if ref != "" {
|
||||||
|
refs = append(refs, ref)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case larkim.MsgTypeFile, larkim.MsgTypeAudio, larkim.MsgTypeMedia:
|
case larkim.MsgTypeFile, larkim.MsgTypeAudio, larkim.MsgTypeMedia:
|
||||||
fileKey := extractFileKey(rawContent)
|
fileKey := extractFileKey(rawContent)
|
||||||
if fileKey == "" {
|
if fileKey == "" {
|
||||||
|
|
@ -670,7 +678,7 @@ func appendMediaTags(content, messageType string, mediaRefs []string) string {
|
||||||
|
|
||||||
var tag string
|
var tag string
|
||||||
switch messageType {
|
switch messageType {
|
||||||
case larkim.MsgTypeImage:
|
case larkim.MsgTypeImage, larkim.MsgTypePost:
|
||||||
tag = "[image: photo]"
|
tag = "[image: photo]"
|
||||||
case larkim.MsgTypeAudio:
|
case larkim.MsgTypeAudio:
|
||||||
tag = "[audio]"
|
tag = "[audio]"
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,22 @@ func TestExtractContent(t *testing.T) {
|
||||||
want: "not json",
|
want: "not json",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "post message returns raw JSON",
|
name: "post message extracts text",
|
||||||
messageType: "post",
|
messageType: "post",
|
||||||
rawContent: `{"title": "test post"}`,
|
rawContent: `{"title":"","content":[[{"tag":"img","image_key":"img_v3_xxx","width":100,"height":100}],[{"tag":"text","text":"图里有啥","style":[]}]]}`,
|
||||||
want: `{"title": "test post"}`,
|
want: "图里有啥",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "post message with title",
|
||||||
|
messageType: "post",
|
||||||
|
rawContent: `{"title":"test post","content":[[{"tag":"text","text":"hello","style":[]}]]}`,
|
||||||
|
want: "test post\nhello",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "post message only images returns empty",
|
||||||
|
messageType: "post",
|
||||||
|
rawContent: `{"title":"","content":[[{"tag":"img","image_key":"img_v3_xxx","width":100,"height":100}]]}`,
|
||||||
|
want: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "image message returns empty",
|
name: "image message returns empty",
|
||||||
|
|
@ -144,6 +156,20 @@ func TestAppendMediaTags(t *testing.T) {
|
||||||
mediaRefs: []string{"ref1"},
|
mediaRefs: []string{"ref1"},
|
||||||
want: "report.pdf [file]",
|
want: "report.pdf [file]",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "post with image refs",
|
||||||
|
content: "图里有啥",
|
||||||
|
messageType: "post",
|
||||||
|
mediaRefs: []string{"ref1"},
|
||||||
|
want: "图里有啥 [image: photo]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "post empty content with image refs",
|
||||||
|
content: "",
|
||||||
|
messageType: "post",
|
||||||
|
mediaRefs: []string{"ref1"},
|
||||||
|
want: "[image: photo]",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "unknown type",
|
name: "unknown type",
|
||||||
content: "something",
|
content: "something",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue