mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-13 10:15:48 +00:00
feat: Add support for sending and receiving media attachments in the Feishu channel.
This commit is contained in:
1 parent
278a98ba74
commit
50c32a4d01
3 files changed
+88
-30
No files matched your search
@@ -145,8 +145,15 @@ func (c *Channel) handleMessageEvent(ctx context.Context, event *MessageEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
// 10. Publish to bus
|
||||
c.HandleMessage(mc.SenderID, chatID, content, nil, metadata, peerKind)
|
||||
// 10. Resolve inbound media (image, file, audio, video, sticker)
|
||||
var mediaPaths []string
|
||||
switch mc.ContentType {
|
||||
case "image", "file", "audio", "video", "sticker":
|
||||
mediaPaths = c.resolveMediaFromMessage(ctx, mc.MessageID, mc.ContentType, msg.Content)
|
||||
}
|
||||
|
||||
// 11. Publish to bus
|
||||
c.HandleMessage(mc.SenderID, chatID, content, mediaPaths, metadata, peerKind)
|
||||
|
||||
// Clear pending history after sending to agent.
|
||||
if mc.ChatType == "group" {
|
||||
|
||||
@@ -136,39 +136,50 @@ func (c *Channel) Send(ctx context.Context, msg bus.OutboundMessage) error {
|
||||
return fmt.Errorf("empty chat ID for feishu send")
|
||||
}
|
||||
|
||||
text := msg.Content
|
||||
if text == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Resolve render mode
|
||||
renderMode := c.cfg.RenderMode
|
||||
if renderMode == "" {
|
||||
renderMode = "auto"
|
||||
}
|
||||
|
||||
useCard := false
|
||||
switch renderMode {
|
||||
case "card":
|
||||
useCard = true
|
||||
case "auto":
|
||||
useCard = shouldUseCard(text)
|
||||
}
|
||||
|
||||
chunkLimit := c.cfg.TextChunkLimit
|
||||
if chunkLimit <= 0 {
|
||||
chunkLimit = defaultTextChunkLimit
|
||||
}
|
||||
|
||||
// Determine receive_id_type
|
||||
receiveIDType := resolveReceiveIDType(chatID)
|
||||
|
||||
// Send as card or text
|
||||
if useCard {
|
||||
return c.sendMarkdownCard(ctx, chatID, receiveIDType, text, nil)
|
||||
// Send text content
|
||||
text := msg.Content
|
||||
if text != "" {
|
||||
// Resolve render mode
|
||||
renderMode := c.cfg.RenderMode
|
||||
if renderMode == "" {
|
||||
renderMode = "auto"
|
||||
}
|
||||
|
||||
useCard := false
|
||||
switch renderMode {
|
||||
case "card":
|
||||
useCard = true
|
||||
case "auto":
|
||||
useCard = shouldUseCard(text)
|
||||
}
|
||||
|
||||
chunkLimit := c.cfg.TextChunkLimit
|
||||
if chunkLimit <= 0 {
|
||||
chunkLimit = defaultTextChunkLimit
|
||||
}
|
||||
|
||||
if useCard {
|
||||
if err := c.sendMarkdownCard(ctx, chatID, receiveIDType, text, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := c.sendChunkedText(ctx, chatID, receiveIDType, text, chunkLimit); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return c.sendChunkedText(ctx, chatID, receiveIDType, text, chunkLimit)
|
||||
// Send media attachments
|
||||
for _, media := range msg.Media {
|
||||
if err := c.sendMediaAttachment(ctx, chatID, receiveIDType, media); err != nil {
|
||||
slog.Warn("feishu send media failed", "url", media.URL, "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// --- Connection modes ---
|
||||
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/nextlevelbuilder/goclaw/internal/bus"
|
||||
)
|
||||
|
||||
// --- Image download ---
|
||||
@@ -65,6 +67,44 @@ func (c *Channel) sendFile(ctx context.Context, chatID, receiveIDType, fileKey,
|
||||
return nil
|
||||
}
|
||||
|
||||
// --- Outbound media ---
|
||||
|
||||
// sendMediaAttachment uploads and sends a media attachment (image or file).
|
||||
func (c *Channel) sendMediaAttachment(ctx context.Context, chatID, receiveIDType string, media bus.MediaAttachment) error {
|
||||
filePath := media.URL
|
||||
if filePath == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
f, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open media file %s: %w", filePath, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
ct := media.ContentType
|
||||
if isImageContentType(ct) {
|
||||
imageKey, err := c.uploadImage(ctx, f)
|
||||
if err != nil {
|
||||
return fmt.Errorf("upload image: %w", err)
|
||||
}
|
||||
return c.sendImage(ctx, chatID, receiveIDType, imageKey)
|
||||
}
|
||||
|
||||
// Everything else: upload as file
|
||||
fileName := filepath.Base(filePath)
|
||||
fileType := detectFileType(fileName)
|
||||
fileKey, err := c.uploadFile(ctx, f, fileName, fileType, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("upload file: %w", err)
|
||||
}
|
||||
return c.sendFile(ctx, chatID, receiveIDType, fileKey, "file")
|
||||
}
|
||||
|
||||
func isImageContentType(ct string) bool {
|
||||
return strings.HasPrefix(ct, "image/") || ct == "image"
|
||||
}
|
||||
|
||||
// --- Media helpers ---
|
||||
|
||||
// saveMediaToTemp writes media bytes to a temp file and returns the path.
|
||||
|
||||
Reference in new issue
Block a user