fix(feishu): preserve group context without sender name

This commit is contained in:
ntduc committed 2026-07-20 15:05:54 +07:00
1 parent aa27354d16
commit e4e4bb33fb
2 files changed
+145 -3

No files matched your search

+10 -3
View File
@@ -101,6 +101,10 @@ func (c *Channel) handleMessageEventFrom(ctx context.Context, event *MessageEven
// 3. Resolve sender name (cached)
senderName := c.resolveSenderName(ctx, mc.SenderID)
senderLabel := senderName
if senderLabel == "" {
senderLabel = mc.SenderID
}
// 4. Resolve media BEFORE mention gate so non-mentioned messages
// also have their files downloaded and stored in pending history.
@@ -166,7 +170,7 @@ func (c *Channel) handleMessageEventFrom(ctx context.Context, event *MessageEven
historyKey = fmt.Sprintf("%s:topic:%s", mc.ChatID, mc.RootID)
}
c.GroupHistory().Record(historyKey, channels.HistoryEntry{
Sender: senderName,
Sender: senderLabel,
SenderID: mc.SenderID,
Body: mc.Content,
Media: earlyMediaPaths,
@@ -307,9 +311,12 @@ func (c *Channel) handleMessageEventFrom(ctx context.Context, event *MessageEven
}
// Annotate content with sender identity so the agent knows who is messaging.
if senderName != "" {
if mc.ChatType == "group" || senderName != "" {
if mc.ChatType == "group" {
annotated := fmt.Sprintf("[From: %s]\n%s", senderName, content)
annotated := content
if senderLabel != "" {
annotated = fmt.Sprintf("[From: %s]\n%s", senderLabel, content)
}
if c.HistoryLimit() > 0 {
content = c.GroupHistory().BuildContext(chatID, annotated, c.HistoryLimit())
} else {
+135
View File
@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"errors"
"strings"
"testing"
"time"
@@ -80,6 +81,140 @@ func TestCheckGroupPolicy_Pairing_GroupAllowListBypasses(t *testing.T) {
}
}
func TestHandleMessageEvent_GroupContextFallsBackToSenderIDWhenNameLookupFails(t *testing.T) {
requireMention := true
msgBus := bus.New()
defer msgBus.Close()
ch, err := New(config.FeishuConfig{
AppID: "app",
AppSecret: "secret",
GroupPolicy: "open",
RequireMention: &requireMention,
}, msgBus, nil, nil, nil)
if err != nil {
t.Fatalf("New: %v", err)
}
ch.SetName("feishu-test")
ch.botOpenID = "ou_target_bot"
srv := newSimpleMockServer(t, `{"code":50000,"msg":"name lookup failed"}`)
ch.client = NewLarkClient("app", "secret", srv.URL)
first := feishuGroupTextEvent(t, "om_context", "ou_duc", "oc_group", "Send AGENTS.md again", nil)
ch.handleMessageEvent(context.Background(), first)
assertNoFeishuInbound(t, msgBus)
entries := ch.GroupHistory().GetEntries("oc_group")
if len(entries) != 1 {
t.Fatalf("history entries = %d, want 1", len(entries))
}
if entries[0].Sender != "ou_duc" {
t.Fatalf("history sender = %q, want sender ID fallback", entries[0].Sender)
}
second := feishuGroupTextEvent(t, "om_mention", "ou_duc", "oc_group", "@_user_1", []EventMention{
feishuMention("@_user_1", "ou_target_bot", "Techlead"),
})
ch.handleMessageEvent(context.Background(), second)
msg := assertFeishuInbound(t, msgBus)
for _, want := range []string{
"[From: ou_duc]",
"[empty message]",
"Send AGENTS.md again",
} {
if !strings.Contains(msg.Content, want) {
t.Errorf("content missing %q:\n%s", want, msg.Content)
}
}
if got := ch.GroupHistory().GetEntries("oc_group"); len(got) != 0 {
t.Fatalf("history entries after publish = %d, want 0", len(got))
}
}
func TestHandleMessageEvent_GroupContextPreservesResolvedSenderName(t *testing.T) {
requireMention := true
msgBus := bus.New()
defer msgBus.Close()
ch, err := New(config.FeishuConfig{
AppID: "app",
AppSecret: "secret",
GroupPolicy: "open",
RequireMention: &requireMention,
}, msgBus, nil, nil, nil)
if err != nil {
t.Fatalf("New: %v", err)
}
ch.SetName("feishu-test")
ch.botOpenID = "ou_target_bot"
srv := newSimpleMockServer(t, `{"code":0,"msg":"ok","data":{"user":{"name":"Duc Nguyen"}}}`)
ch.client = NewLarkClient("app", "secret", srv.URL)
event := feishuGroupTextEvent(t, "om_named", "ou_duc", "oc_group", "@_user_1 hello", []EventMention{
feishuMention("@_user_1", "ou_target_bot", "Techlead"),
})
ch.handleMessageEvent(context.Background(), event)
msg := assertFeishuInbound(t, msgBus)
if msg.Content != "[From: Duc Nguyen]\nhello" {
t.Fatalf("content = %q, want resolved sender name", msg.Content)
}
if msg.Metadata["sender_name"] != "Duc Nguyen" {
t.Fatalf("sender_name metadata = %q", msg.Metadata["sender_name"])
}
}
func TestHandleMessageEvent_P2PContextRemainsNameBased(t *testing.T) {
msgBus := bus.New()
defer msgBus.Close()
ch, err := New(config.FeishuConfig{AppID: "app", AppSecret: "secret", DMPolicy: "open"}, msgBus, nil, nil, nil)
if err != nil {
t.Fatalf("New: %v", err)
}
ch.SetName("feishu-test")
srv := newSimpleMockServer(t, `{"code":0,"msg":"ok","data":{"user":{"name":"Duc Nguyen"}}}`)
ch.client = NewLarkClient("app", "secret", srv.URL)
event := feishuGroupTextEvent(t, "om_p2p", "ou_duc", "oc_p2p", "hello", nil)
event.Event.Message.ChatType = "p2p"
ch.handleMessageEvent(context.Background(), event)
msg := assertFeishuInbound(t, msgBus)
if msg.Content != "[From: Duc Nguyen]\nhello" {
t.Fatalf("content = %q, want unchanged p2p name context", msg.Content)
}
}
func TestHandleMessageEvent_GroupContextDoesNotEmitEmptySenderLabel(t *testing.T) {
requireMention := true
msgBus := bus.New()
defer msgBus.Close()
ch, err := New(config.FeishuConfig{
AppID: "app",
AppSecret: "secret",
GroupPolicy: "open",
RequireMention: &requireMention,
}, msgBus, nil, nil, nil)
if err != nil {
t.Fatalf("New: %v", err)
}
ch.SetName("feishu-test")
ch.botOpenID = "ou_target_bot"
event := feishuGroupTextEvent(t, "om_no_sender", "", "oc_group", "@_user_1", []EventMention{
feishuMention("@_user_1", "ou_target_bot", "Techlead"),
})
ch.handleMessageEvent(context.Background(), event)
msg := assertFeishuInbound(t, msgBus)
if msg.Content != "[empty message]" {
t.Fatalf("content = %q, want no empty sender annotation", msg.Content)
}
}
type feishuPolicyPairingStore struct {
requests int
lastSenderID string