From 9627846e5b7a39fcfc6c025ef1f97ef8ba100b38 Mon Sep 17 00:00:00 2001 From: ywj <138745068+yangwenjie1231@users.noreply.github.com> Date: Tue, 31 Mar 2026 12:57:12 +0800 Subject: [PATCH] fix(feishu): add message cache for fetchMessageByID to avoid repeated downloads - Add messageCache (sync.Map) to FeishuChannel struct - Cache fetched messages with 30s TTL to avoid re-downloading attachments when multiple users reply to the same parent message in a thread - Cleanup expired entries on read access (no background goroutine needed) --- pkg/channels/feishu/feishu_64.go | 9 ++++++++- pkg/channels/feishu/feishu_reply.go | 14 +++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/pkg/channels/feishu/feishu_64.go b/pkg/channels/feishu/feishu_64.go index 061cef9d7..ac10504ea 100644 --- a/pkg/channels/feishu/feishu_64.go +++ b/pkg/channels/feishu/feishu_64.go @@ -19,6 +19,7 @@ import ( "strings" "sync" "sync/atomic" + "time" "github.com/sipeed/picoclaw/pkg/bus" "github.com/sipeed/picoclaw/pkg/channels" @@ -41,12 +42,18 @@ type FeishuChannel struct { wsClient *larkws.Client tokenCache *tokenCache // custom cache that supports invalidation - botOpenID atomic.Value // stores string; populated lazily for @mention detection + botOpenID atomic.Value // stores string; populated lazily for @mention detection + messageCache sync.Map // caches fetched messages (messageID -> *larkim.Message) mu sync.Mutex cancel context.CancelFunc } +type cachedMessage struct { + msg *larkim.Message + expiry time.Time +} + func NewFeishuChannel(cfg config.FeishuConfig, bus *bus.MessageBus) (*FeishuChannel, error) { base := channels.NewBaseChannel("feishu", cfg, bus, cfg.AllowFrom, channels.WithGroupTrigger(cfg.GroupTrigger), diff --git a/pkg/channels/feishu/feishu_reply.go b/pkg/channels/feishu/feishu_reply.go index febd6cd33..98dd89690 100644 --- a/pkg/channels/feishu/feishu_reply.go +++ b/pkg/channels/feishu/feishu_reply.go @@ -14,6 +14,8 @@ import ( "github.com/sipeed/picoclaw/pkg/utils" ) +const messageCacheTTL = 30 * time.Second + const ( maxReplyContextLen = 600 ) @@ -122,6 +124,14 @@ func (c *FeishuChannel) resolveReplyTargetMessageID(ctx context.Context, message } func (c *FeishuChannel) fetchMessageByID(ctx context.Context, messageID string) (*larkim.Message, error) { + if cached, ok := c.messageCache.Load(messageID); ok { + cm := cached.(*cachedMessage) + if time.Now().Before(cm.expiry) { + return cm.msg, nil + } + c.messageCache.Delete(messageID) + } + req := larkim.NewGetMessageReqBuilder(). MessageId(messageID). Build() @@ -138,7 +148,9 @@ func (c *FeishuChannel) fetchMessageByID(ctx context.Context, messageID string) return nil, fmt.Errorf("feishu get message: empty response") } - return resp.Data.Items[0], nil + msg := resp.Data.Items[0] + c.messageCache.Store(messageID, &cachedMessage{msg: msg, expiry: time.Now().Add(messageCacheTTL)}) + return msg, nil } func replyTargetID(message *larkim.EventMessage) string {