mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-09-10 00:17:37 +00:00
fix(telegram): strip own @mention from inbound content
Agents previously saw their own Telegram handle (e.g. "@viet_super_bot") in user messages and mistook it for a different bot, replying NO_REPLY. The username was only used for the mention gate, never removed from the content passed to the LLM. Slack and Feishu already strip their own bot mentions (handlers_mention.go stripBotMention, bot_parse.go resolveMentions); Telegram was the odd one out. Implementation: - Add stripBotMention helper with leading/trailing word-boundary anchors so inline matches inside words (e.g. contact@viet_super_bot.com) are not falsely stripped. - Apply in handleMessage right after the mention gate, before pairing/media processing, so history recording for unmentioned messages keeps raw text. - Restore "[empty message]" placeholder when a message consisting only of "@botname" becomes empty after stripping.
This commit is contained in:
@@ -361,6 +361,15 @@ func (c *Channel) handleMessage(ctx context.Context, update telego.Update) {
|
||||
}
|
||||
}
|
||||
|
||||
// Strip bot's own @mention so the LLM sees clean content and does not
|
||||
// mistake itself for another bot (cross-channel parity with Slack/Feishu).
|
||||
// Re-check empty state: a message containing only "@botname" becomes empty
|
||||
// after stripping, so we restore the placeholder used for originally-empty inbounds.
|
||||
content = stripBotMention(content, c.bot.Username())
|
||||
if content == "" {
|
||||
content = "[empty message]"
|
||||
}
|
||||
|
||||
// --- Group pairing gate (only reached when bot is mentioned) ---
|
||||
if isGroup && topicCfg.groupPolicy == "pairing" && c.PairingService() != nil {
|
||||
if !c.IsGroupApproved(chatIDStr) {
|
||||
|
||||
@@ -1,11 +1,30 @@
|
||||
package telegram
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/mymmrac/telego"
|
||||
)
|
||||
|
||||
// stripBotMention removes @botUsername tokens from text (case-insensitive).
|
||||
// Applied after the mention gate passes so the LLM does not see its own Telegram handle
|
||||
// and mistake itself for another bot (e.g. persona "Tiểu Hổ" receiving "@viet_super_bot vẽ...").
|
||||
//
|
||||
// Boundary rules match valid Telegram mentions:
|
||||
// - Leading: start-of-string OR a non-word char (whitespace/punct). Prevents false strips
|
||||
// inside words like "contact@viet_super_bot.com".
|
||||
// - Trailing: \b (word-boundary). Prevents matching "@bot" inside "@bot_2".
|
||||
//
|
||||
// The leading non-word char is preserved via capture group $1.
|
||||
func stripBotMention(text, botUsername string) string {
|
||||
if botUsername == "" || text == "" {
|
||||
return text
|
||||
}
|
||||
pattern := `(?i)(^|[^\w])@` + regexp.QuoteMeta(botUsername) + `\b`
|
||||
return strings.TrimSpace(regexp.MustCompile(pattern).ReplaceAllString(text, "$1"))
|
||||
}
|
||||
|
||||
// detectMention checks if a Telegram message mentions the bot.
|
||||
// Checks both msg.Text/Entities (text messages) and msg.Caption/CaptionEntities (photo/media messages).
|
||||
func (c *Channel) detectMention(msg *telego.Message, botUsername string) bool {
|
||||
|
||||
@@ -208,6 +208,70 @@ func TestHasOtherMention_CaptionWithOtherMention(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// --- stripBotMention ---
|
||||
|
||||
func TestStripBotMention_RemovesMention(t *testing.T) {
|
||||
got := stripBotMention("@viet_super_bot vẽ ảnh minh họa", "viet_super_bot")
|
||||
want := "vẽ ảnh minh họa"
|
||||
if got != want {
|
||||
t.Errorf("stripBotMention = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStripBotMention_CaseInsensitive(t *testing.T) {
|
||||
got := stripBotMention("@Viet_Super_Bot hello", "viet_super_bot")
|
||||
if got != "hello" {
|
||||
t.Errorf("stripBotMention case-insensitive = %q, want %q", got, "hello")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStripBotMention_PreservesOtherMentions(t *testing.T) {
|
||||
got := stripBotMention("@viet_super_bot hỏi @alice về X", "viet_super_bot")
|
||||
want := "hỏi @alice về X"
|
||||
if got != want {
|
||||
t.Errorf("stripBotMention = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStripBotMention_WordBoundary(t *testing.T) {
|
||||
// @viet_super_bot2 must NOT match @viet_super_bot (different bot with similar prefix).
|
||||
got := stripBotMention("@viet_super_bot2 hello", "viet_super_bot")
|
||||
if got != "@viet_super_bot2 hello" {
|
||||
t.Errorf("stripBotMention should not match prefix; got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStripBotMention_EmptyUsername(t *testing.T) {
|
||||
text := "@anything else"
|
||||
if got := stripBotMention(text, ""); got != text {
|
||||
t.Errorf("stripBotMention with empty botUsername should return text unchanged; got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStripBotMention_MultipleOccurrences(t *testing.T) {
|
||||
got := stripBotMention("hey @viet_super_bot, @viet_super_bot help!", "viet_super_bot")
|
||||
// Both removed; internal spacing/punctuation preserved.
|
||||
want := "hey , help!"
|
||||
if got != want {
|
||||
t.Errorf("stripBotMention multi = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStripBotMention_PreservesEmailLike(t *testing.T) {
|
||||
// "@viet_super_bot" embedded inside a word (e.g. email/URL) must NOT be stripped.
|
||||
// Telegram mentions require a leading word-boundary, so inline matches are false positives.
|
||||
in := "contact@viet_super_bot.com please"
|
||||
if got := stripBotMention(in, "viet_super_bot"); got != in {
|
||||
t.Errorf("stripBotMention should not strip mention embedded in word; got %q, want %q", got, in)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStripBotMention_OnlyMentionBecomesEmpty(t *testing.T) {
|
||||
if got := stripBotMention("@viet_super_bot", "viet_super_bot"); got != "" {
|
||||
t.Errorf("mention-only input should become empty; got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
// --- isServiceMessage ---
|
||||
|
||||
func TestIsServiceMessage_WithText(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user