From eda635e72c1aad0d430418492f8e633c4cb7948f Mon Sep 17 00:00:00 2001 From: ScuroNeko Date: Wed, 25 Mar 2026 13:17:44 +0300 Subject: [PATCH] fix: enforce required command arg positions --- cmd_generator.go | 2 +- plugins.go | 8 ++++---- plugins_test.go | 16 ++++++++++++++++ tgapi/bot_methods.go | 2 +- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/cmd_generator.go b/cmd_generator.go index a5b0d40..4658e88 100644 --- a/cmd_generator.go +++ b/cmd_generator.go @@ -10,7 +10,7 @@ import ( ) // CmdRegexp matches command names allowed for Telegram command registration. -var CmdRegexp = regexp.MustCompile("^[a-zA-Z0-9]+$") +var CmdRegexp = regexp.MustCompile("^[_a-z0-9]+$") // ErrTooManyCommands is returned when the total number of registered commands // exceeds Telegram's limit of 100 bot commands per bot. diff --git a/plugins.go b/plugins.go index 903718f..793ef23 100644 --- a/plugins.go +++ b/plugins.go @@ -127,10 +127,10 @@ func (c *Command[T]) SkipCommandAutoGen() *Command[T] { // Returns ErrCmdArgCountMismatch if too few arguments are provided. // Returns ErrCmdArgRegexpMismatch if any argument fails regex validation. func (c *Command[T]) validateArgs(args []string) error { - // Count required args - requiredCount := c.args.Filter(func(a CommandArg) bool { return a.required }).Len() - if len(args) < requiredCount { - return ErrCmdArgCountMismatch + for i := range c.args.Len() { + if i >= len(args) && c.args.Get(i).required { + return ErrCmdArgCountMismatch + } } // Validate each argument against its regex diff --git a/plugins_test.go b/plugins_test.go index a8fd856..7c7507d 100644 --- a/plugins_test.go +++ b/plugins_test.go @@ -22,3 +22,19 @@ func TestValidateArgsRequiresFullMatch(t *testing.T) { t.Fatalf("expected ErrCmdArgRegexpMismatch for partial bool match, got %v", err) } } + +func TestValidateArgsEnforcesRequiredArgIndex(t *testing.T) { + cmd := NewCommand[NoDB]( + func(ctx *MsgContext, db *NoDB) {}, + "mixed", + *NewCommandArg("optional"), + *NewCommandArg("required").SetRequired(), + ) + + if err := cmd.validateArgs([]string{"only-optional"}); !errors.Is(err, ErrCmdArgCountMismatch) { + t.Fatalf("expected ErrCmdArgCountMismatch when required second arg is missing, got %v", err) + } + if err := cmd.validateArgs([]string{"optional", "required"}); err != nil { + t.Fatalf("expected both args to validate, got %v", err) + } +} diff --git a/tgapi/bot_methods.go b/tgapi/bot_methods.go index 5956755..8531613 100644 --- a/tgapi/bot_methods.go +++ b/tgapi/bot_methods.go @@ -244,7 +244,7 @@ func (api *API) RemoveMyProfilePhotoWithContext(ctx context.Context) (bool, erro // SetChatMenuButtonP holds parameters for the setChatMenuButton method. // See https://core.telegram.org/bots/api#setchatmenubutton type SetChatMenuButtonP struct { - ChatID int64 `json:"chat_id"` + ChatID int64 `json:"chat_id,omitempty"` MenuButton MenuButtonType `json:"menu_button"` }