diff --git a/pkg/channels/dingtalk/dingtalk.go b/pkg/channels/dingtalk/dingtalk.go index b23975d1a..9f382ec39 100644 --- a/pkg/channels/dingtalk/dingtalk.go +++ b/pkg/channels/dingtalk/dingtalk.go @@ -190,8 +190,12 @@ func (c *DingTalkChannel) onChatBotMessageReceived( peer = bus.Peer{Kind: "direct", ID: peerID} } else { peer = bus.Peer{Kind: "group", ID: data.ConversationId} + isMentioned := data.IsInAtList + if isMentioned { + content = stripLeadingAtMentions(content) + } // In group chats, apply unified group trigger filtering - respond, cleaned := c.ShouldRespondInGroup(data.IsInAtList, content) + respond, cleaned := c.ShouldRespondInGroup(isMentioned, content) if !respond { return nil, nil } @@ -253,3 +257,19 @@ func (c *DingTalkChannel) SendDirectReply(ctx context.Context, sessionWebhook, c return nil } + +func stripLeadingAtMentions(content string) string { + fields := strings.Fields(content) + if len(fields) == 0 { + return "" + } + + i := 0 + for i < len(fields) && strings.HasPrefix(fields[i], "@") { + i++ + } + if i == 0 { + return strings.TrimSpace(content) + } + return strings.Join(fields[i:], " ") +} diff --git a/pkg/channels/dingtalk/dingtalk_test.go b/pkg/channels/dingtalk/dingtalk_test.go index d2ab6a3cc..3b517aef4 100644 --- a/pkg/channels/dingtalk/dingtalk_test.go +++ b/pkg/channels/dingtalk/dingtalk_test.go @@ -40,13 +40,13 @@ func mustReceiveInbound(t *testing.T, msgBus *bus.MessageBus) bus.InboundMessage } } -func TestOnChatBotMessageReceived_GroupMentionOnlyUsesIsInAtList(t *testing.T) { +func TestOnChatBotMessageReceived_GroupMentionOnlyUsesIsInAtListAndStripsMention(t *testing.T) { ch, msgBus := newTestDingTalkChannel(t, config.DingTalkConfig{ GroupTrigger: config.GroupTriggerConfig{MentionOnly: true}, }) _, err := ch.onChatBotMessageReceived(context.Background(), &chatbot.BotCallbackDataModel{ - Text: chatbot.BotCallbackDataTextModel{Content: " @bot hello "}, + Text: chatbot.BotCallbackDataTextModel{Content: " @bot /help "}, SenderStaffId: "staff-123", SenderNick: "Alice", ConversationType: "2", @@ -68,7 +68,7 @@ func TestOnChatBotMessageReceived_GroupMentionOnlyUsesIsInAtList(t *testing.T) { if inbound.Peer.Kind != "group" || inbound.Peer.ID != "group-abc" { t.Fatalf("peer=%+v", inbound.Peer) } - if inbound.Content != "@bot hello" { + if inbound.Content != "/help" { t.Fatalf("content=%q", inbound.Content) } } @@ -107,3 +107,25 @@ func TestOnChatBotMessageReceived_DirectFallbackSenderIDUsesConversationID(t *te t.Fatal("unexpected empty chat_id webhook key") } } + +func TestStripLeadingAtMentions(t *testing.T) { + tests := []struct { + name string + input string + wantOut string + }{ + {name: "single mention and command", input: "@bot /help", wantOut: "/help"}, + {name: "multiple mentions", input: "@bot @alice /new", wantOut: "/new"}, + {name: "no mention", input: "/help", wantOut: "/help"}, + {name: "mention only", input: "@bot", wantOut: ""}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := stripLeadingAtMentions(tt.input) + if got != tt.wantOut { + t.Fatalf("stripLeadingAtMentions(%q)=%q want=%q", tt.input, got, tt.wantOut) + } + }) + } +}