From c71b17b772cdd99245c31e90cb4fe8a8eabd253b Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Sun, 19 Jul 2026 20:09:16 +0700 Subject: [PATCH] feat(modules): match commands case-insensitively Mobile keyboards autocapitalize, so a typed /Ping silently did nothing and read as the bot being broken. Registered names are lowercase-only per validateCommand, so folding case cannot make two commands collide, and the canonical Command.Name still drives stats, metrics, hooks, and logs. --- internal/modules/dispatcher.go | 8 +++++++- internal/modules/dispatcher_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/internal/modules/dispatcher.go b/internal/modules/dispatcher.go index 655ae35..aa9b606 100644 --- a/internal/modules/dispatcher.go +++ b/internal/modules/dispatcher.go @@ -121,6 +121,12 @@ func logCommand(name string, update *models.Update, err error) { // // Telegram routes /cmd@otherbot only to otherbot, so an @suffix present in // the entity addresses *this* bot — no need to verify against our username. +// +// Matching is case-insensitive so /PING and /Ping reach the same handler as +// /ping — mobile keyboards autocapitalize, and a typed command that silently +// does nothing reads as the bot being broken. Registered names are lowercase by +// validateCommand, so folding case can never make two commands collide, and the +// canonical Command.Name still drives stats, metrics, hooks, and logs. func matchCommand(name string, update *models.Update) bool { if update == nil || update.Message == nil { return false @@ -141,7 +147,7 @@ func matchCommand(name string, update *models.Update) bool { if i := strings.IndexByte(tok, '@'); i >= 0 { tok = tok[:i] } - if tok == name { + if strings.EqualFold(tok, name) { return true } } diff --git a/internal/modules/dispatcher_test.go b/internal/modules/dispatcher_test.go index 1e571e7..70ca8b0 100644 --- a/internal/modules/dispatcher_test.go +++ b/internal/modules/dispatcher_test.go @@ -116,6 +116,30 @@ func TestMatchCommand(t *testing.T) { update: mkUpdate("hi /help", cmd(3, 5)), expect: true, }, + { + name: "uppercase command matches", + want: "help", + update: mkUpdate("/HELP", cmd(0, 5)), + expect: true, + }, + { + name: "mixed-case command matches", + want: "help", + update: mkUpdate("/Help", cmd(0, 5)), + expect: true, + }, + { + name: "uppercase command with botname matches", + want: "help", + update: mkUpdate("/HELP@miti99bot", cmd(0, 15)), + expect: true, + }, + { + name: "case folding does not match a different command", + want: "help", + update: mkUpdate("/INFO", cmd(0, 5)), + expect: false, + }, { name: "nil update", want: "help",