From a9cfbf2f3979687b3510f4a5204f44dff56fddde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=82=AB=E3=83=B3?= Date: Thu, 4 Jun 2026 13:55:30 +0900 Subject: [PATCH] fix(tgbot): ignore commands for other bots Telegram group chats can contain multiple bots. Commands addressed to another bot, such as /status@other_bot, should not be handled by the 3x-ui bot. Closes #4893 --- web/service/tgbot.go | 20 ++++++++++++++++++++ web/service/tgbot_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/web/service/tgbot.go b/web/service/tgbot.go index dcb6cb2c..81a0de4e 100644 --- a/web/service/tgbot.go +++ b/web/service/tgbot.go @@ -495,6 +495,10 @@ func (t *Tgbot) OnReceive() { }, th.TextEqual(t.I18nBot("tgbot.buttons.closeKeyboard"))) h.HandleMessage(func(ctx *th.Context, message telego.Message) error { + if !t.isCommandForCurrentBot(&message) { + return nil + } + // Use goroutine with worker pool for concurrent command processing go func() { messageWorkerPool <- struct{}{} // Acquire worker @@ -684,6 +688,22 @@ func (t *Tgbot) answerCommand(message *telego.Message, chatId int64, isAdmin boo } } +func (t *Tgbot) isCommandForCurrentBot(message *telego.Message) bool { + return isCommandForBot(message.Text, botUsername()) +} + +func botUsername() string { + if bot == nil { + return "" + } + return bot.Username() +} + +func isCommandForBot(text string, username string) bool { + _, commandUsername, _ := tu.ParseCommand(text) + return commandUsername == "" || username == "" || strings.EqualFold(commandUsername, username) +} + // sendResponse sends the response message based on the onlyMessage flag. func (t *Tgbot) sendResponse(chatId int64, msg string, onlyMessage, isAdmin bool) { if onlyMessage { diff --git a/web/service/tgbot_test.go b/web/service/tgbot_test.go index 36e17e78..a3c346b0 100644 --- a/web/service/tgbot_test.go +++ b/web/service/tgbot_test.go @@ -99,3 +99,27 @@ func TestTgbotProxyDialerNoneWhenEmpty(t *testing.T) { t.Fatal("Dial must be nil when no proxy is configured") } } + +func TestIsCommandForBotAllowsUntargetedCommand(t *testing.T) { + if !isCommandForBot("/status", "panel_bot") { + t.Fatal("untargeted commands must remain accepted") + } +} + +func TestIsCommandForBotAllowsMatchingUsername(t *testing.T) { + if !isCommandForBot("/status@panel_bot", "Panel_Bot") { + t.Fatal("commands targeted to this bot must be accepted") + } +} + +func TestIsCommandForBotRejectsOtherUsername(t *testing.T) { + if isCommandForBot("/status@other_bot", "panel_bot") { + t.Fatal("commands targeted to another bot must be ignored") + } +} + +func TestIsCommandForBotKeepsLegacyBehaviorWhenUsernameUnavailable(t *testing.T) { + if !isCommandForBot("/status@panel_bot", "") { + t.Fatal("commands must remain accepted when the current bot username is unavailable") + } +}