From 9b01eb6fc5b9889f36c1a0697ef307d57a7bc718 Mon Sep 17 00:00:00 2001 From: DBT Date: Fri, 27 Feb 2026 04:09:42 +0000 Subject: [PATCH] feishu: auto-select msg_type (text/post/interactive) by content --- pkg/channels/feishu.go | 87 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 5 deletions(-) diff --git a/pkg/channels/feishu.go b/pkg/channels/feishu.go index 43eedce..e1f8e31 100644 --- a/pkg/channels/feishu.go +++ b/pkg/channels/feishu.go @@ -110,18 +110,17 @@ func (c *FeishuChannel) Send(ctx context.Context, msg bus.OutboundMessage) error return fmt.Errorf("unsupported feishu action: %s", action) } - content := normalizeFeishuText(msg.Content) - payload, err := json.Marshal(map[string]string{"text": content}) + msgType, contentPayload, err := buildFeishuOutbound(msg) if err != nil { - return fmt.Errorf("failed to marshal feishu content: %w", err) + return err } req := larkim.NewCreateMessageReqBuilder(). ReceiveIdType(larkim.ReceiveIdTypeChatId). Body(larkim.NewCreateMessageReqBodyBuilder(). ReceiveId(msg.ChatID). - MsgType(larkim.MsgTypeText). - Content(string(payload)). + MsgType(msgType). + Content(contentPayload). Uuid(fmt.Sprintf("clawgo-%d", time.Now().UnixNano())). Build()). Build() @@ -243,6 +242,84 @@ func (c *FeishuChannel) shouldHandleGroupMessage(chatType, content string) bool return false } +func buildFeishuOutbound(msg bus.OutboundMessage) (string, string, error) { + content := strings.TrimSpace(msg.Content) + + // Support Feishu interactive card when content is raw card JSON. + if looksLikeFeishuCard(content) { + return larkim.MsgTypeInteractive, content, nil + } + + if looksLikeMarkdown(content) { + postPayload, err := buildFeishuPostContent(content) + if err != nil { + return "", "", fmt.Errorf("failed to marshal feishu post content: %w", err) + } + return larkim.MsgTypePost, postPayload, nil + } + + textPayload, err := json.Marshal(map[string]string{"text": normalizeFeishuText(content)}) + if err != nil { + return "", "", fmt.Errorf("failed to marshal feishu text content: %w", err) + } + return larkim.MsgTypeText, string(textPayload), nil +} + +func looksLikeFeishuCard(s string) bool { + s = strings.TrimSpace(s) + if s == "" || (!strings.HasPrefix(s, "{") && !strings.HasPrefix(s, "[")) { + return false + } + var obj map[string]interface{} + if err := json.Unmarshal([]byte(s), &obj); err != nil { + return false + } + _, hasElements := obj["elements"] + _, hasHeader := obj["header"] + _, hasConfig := obj["config"] + return hasElements || hasHeader || hasConfig +} + +func looksLikeMarkdown(s string) bool { + trimmed := strings.TrimSpace(s) + if trimmed == "" { + return false + } + markers := []string{"```", "# ", "## ", "- ", "* ", "**", "`", "[", "]("} + for _, m := range markers { + if strings.Contains(trimmed, m) { + return true + } + } + return false +} + +func buildFeishuPostContent(s string) (string, error) { + lines := strings.Split(strings.TrimSpace(s), "\n") + contentRows := make([][]map[string]string, 0, len(lines)) + for _, line := range lines { + n := normalizeFeishuText(line) + if strings.TrimSpace(n) == "" { + continue + } + contentRows = append(contentRows, []map[string]string{{"tag": "text", "text": n}}) + } + if len(contentRows) == 0 { + contentRows = append(contentRows, []map[string]string{{"tag": "text", "text": " "}}) + } + payload := map[string]interface{}{ + "zh_cn": map[string]interface{}{ + "title": "", + "content": contentRows, + }, + } + b, err := json.Marshal(payload) + if err != nil { + return "", err + } + return string(b), nil +} + func normalizeFeishuText(s string) string { s = strings.TrimSpace(s) if s == "" {