From 5a10eda3e1a2facc8714534279ac0830cc7c995a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=27Kama=C5=9B=27=20Bruchal?= Date: Mon, 22 Jun 2026 19:58:13 +0200 Subject: [PATCH] fix: only answer unknown /commands, not ordinary messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The default handler fired for every update the command matchers didn't claim, so plain chatter, service messages, media, and replies to the bot all drew a "Nieznana komenda" response — spammy in group chats. Gate the fallback on a leading bot_command entity so only real (unrecognized) commands get the help reply. --- telegram.go | 19 +++++++++++++++++-- telegram_test.go | 31 ++++++++++++++++++++++++++----- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/telegram.go b/telegram.go index 9a5a290..cc30a7d 100644 --- a/telegram.go +++ b/telegram.go @@ -156,14 +156,29 @@ func (h *botHandlers) sync(ctx context.Context, b *bot.Bot, update *models.Updat "Zsynchronizowano: %d tygodni, %d osób.", len(rota.Weeks), len(rota.People))) } -// unknown is the fallback for any text the bot does not recognize. +// unknown is the fallback for any update the command matchers don't claim. It +// only answers messages that actually carry a bot_command entity (a mistyped or +// unsupported /command); plain chatter, service messages (joins/pins), media, +// and replies to the bot fall through here too but get no response, so the bot +// stays quiet in groups instead of "Nieznana komenda"-spamming every message. func (h *botHandlers) unknown(ctx context.Context, b *bot.Bot, update *models.Update) { - if update.Message == nil { + if update.Message == nil || !hasCommandEntity(update.Message.Entities) { return } h.reply(ctx, b, update, "Nieznana komenda. Użyj /kto_sprzata lub /sync.") } +// hasCommandEntity reports whether the message carries any bot_command entity, +// i.e. the user typed a /command (recognized or not) rather than plain text. +func hasCommandEntity(entities []models.MessageEntity) bool { + for _, e := range entities { + if e.Type == models.MessageEntityTypeBotCommand { + return true + } + } + return false +} + // reply sends text back to the originating chat, logging send failures. func (h *botHandlers) reply(ctx context.Context, b *bot.Bot, update *models.Update, text string) { if update.Message == nil { diff --git a/telegram_test.go b/telegram_test.go index e0d0b15..0d4dcbb 100644 --- a/telegram_test.go +++ b/telegram_test.go @@ -293,18 +293,39 @@ func TestSyncReplyFailure(t *testing.T) { } } -// TestUnknownReply checks the fallback handler answers unrecognized text. +// TestUnknownReply checks the fallback answers an unrecognized /command (a +// message carrying a bot_command entity that no matcher claimed). func TestUnknownReply(t *testing.T) { svc := testService(t, Rota{}, nil) b, sent := newTestBot(t) h := &botHandlers{svc: svc, now: fixedNow()} + cmd := &models.Update{Message: &models.Message{ + Text: "/foo", + Chat: models.Chat{ID: 123}, + Entities: cmdEntity("/foo"), + }} + h.unknown(context.Background(), b, cmd) + + got := sent() + want := "Nieznana komenda. Użyj /kto_sprzata lub /sync." + if len(got) != 1 || got[0] != want { + t.Fatalf("reply = %v, want [%q]", got, want) + } +} + +// TestUnknownPlainTextIgnored checks ordinary chatter (no bot_command entity) +// gets no reply, so the bot doesn't spam "Nieznana komenda" on every group +// message, service notice, or reply to its own messages. +func TestUnknownPlainTextIgnored(t *testing.T) { + svc := testService(t, Rota{}, nil) + b, sent := newTestBot(t) + h := &botHandlers{svc: svc, now: fixedNow()} + h.unknown(context.Background(), b, updateMsg(123, "siema")) - got := sent() - want := "Nieznana komenda. Użyj /kto_sprzata lub /sync." - if len(got) != 1 || got[0] != want { - t.Fatalf("reply = %v, want [%q]", got, want) + if got := sent(); len(got) != 0 { + t.Fatalf("plain text should not reply, got %v", got) } }