go-telegram / bot

Telegram Bot API Go framework
MIT License
654 stars 57 forks source link

[Bug] `RegisterHandlerMatchFunc` Not Triggering Correct Handler for CallbackQuery #106

Closed oanhnn closed 3 days ago

oanhnn commented 3 weeks ago

Reproduction Steps:

  1. Add a handler using the RegisterHandlerMatchFunc method to handle the scenario when a user clicks on the “Play Game” button.
  2. When the user clicks on the “Play Game” button (which triggers an update with CallbackQuery).

Expected Behavior:

The match function should return true, and the handler added using RegisterHandlerMatchFunc should be executed.

Actual Behavior:

The match function is not executed, and the default handler is triggered instead.

Test Case to Reproduce the Issue:

// file process_update_test.go

func TestProcessUpdate_WithMatchTypeFunc(t *testing.T) {
    var called string
    h1 := func(ctx context.Context, bot *Bot, update *models.Update) {
        called = "h1"
    }
    h2 := func(ctx context.Context, bot *Bot, update *models.Update) {
        called = "h2"
    }
    m := func(update *models.Update) bool {
        return update.CallbackQuery.GameShortName == "game"
    }

    bot := &Bot{
        defaultHandlerFunc: h1,
        handlersMx:         &sync.RWMutex{},
        handlers:           map[string]handler{},
    }

    bot.RegisterHandlerMatchFunc(m, h2)

    ctx := context.Background()
    upd := &models.Update{ID: 42, CallbackQuery: &models.CallbackQuery{ID: "test", GameShortName: "game"}}

    bot.ProcessUpdate(ctx, upd)
    if called != "h2" {
        t.Fatalf("Expected h2 handler to be called but %s handler was called", called)
    }
}

Solution

diff --git a/process_update.go b/process_update.go
index 6bdcc1f..b24eb2e 100644
--- a/process_update.go
+++ b/process_update.go
@@ -26,24 +26,22 @@ func (b *Bot) ProcessUpdate(ctx context.Context, upd *models.Update) {
        }()

        if upd.Message != nil {
-               h = b.findHandler(HandlerTypeMessageText, upd)
+               h = b.findHandler(upd)
                return
        }
        if upd.CallbackQuery != nil {
-               h = b.findHandler(HandlerTypeCallbackQueryData, upd)
+               h = b.findHandler(upd)
                return
        }
 }

-func (b *Bot) findHandler(handlerType HandlerType, upd *models.Update) HandlerFunc {
+func (b *Bot) findHandler(upd *models.Update) HandlerFunc {
        b.handlersMx.RLock()
        defer b.handlersMx.RUnlock()

        for _, h := range b.handlers {
-               if h.handlerType == handlerType {
-                       if h.match(upd) {
-                               return h.handler
-                       }
+               if h.match(upd) {
+                       return h.handler
                }
        }
oanhnn commented 2 weeks ago

I make PR #107 to fix this issue. Please check soon @negasus

NiJeTi commented 4 days ago

Just tested on v1.7.3 I have a universal update router registered via WithDefaultHandler and it still does not catch any CallbackQuery updates.

negasus commented 3 days ago

@NiJeTi I showed my case in the chat, everything seemed to work. Can you provide your example?

https://t.me/gotelegrambotui/2179

NiJeTi commented 3 days ago

@negasus What I have is pretty simple configuration:

// func main()
tgBot := telegram.Configure(
    cfg.Telegram,
    tgPkg.WithErrorsHandler(tgRouter.ErrorHandle),
    tgPkg.WithDefaultHandler(tgRouter.Handle),
)

// telegram.Configure()
func Configure(cfg Config, opts ...tgPkg.Option) *tgPkg.Bot {
    bot, err := tgPkg.New(cfg.Token, opts...)
    if err != nil {
        log.Fatalln("failed to connect to telegram:", err)
    }

    return bot
}

// telegram.Router.Handle()
func (r *Router) Handle(ctx context.Context, bot *tgPkg.Bot, update *tgModels.Update) {
    log.Println("update received")
}

And unfortunately router's method Handle never triggers if I press an inline keyboard button.

negasus commented 3 days ago

@NiJeTi

Its work for me

func main() {
    ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
    defer cancel()

    opts := []bot.Option{
        bot.WithDefaultHandler(handler),
    }

    b, _ := bot.New(os.Getenv("EXAMPLE_TELEGRAM_BOT_TOKEN"), opts...)
    b.Start(ctx)
}

func handler(ctx context.Context, b *bot.Bot, update *models.Update) {
    if update.Message != nil {
        kb := &models.InlineKeyboardMarkup{InlineKeyboard: [][]models.InlineKeyboardButton{
            {
                {Text: "button", CallbackData: "button"},
            },
        }}
        b.SendMessage(ctx, &bot.SendMessageParams{
            ChatID:      update.Message.Chat.ID,
            Text:        update.Message.Text,
            ReplyMarkup: kb,
        })

        return
    }

    log.Printf("update %v", update)
}
NiJeTi commented 3 days ago

Sorry for confusion. Yes, you are right. After a brief investigation it became clear that it was some sort of (Telegram's ?) bug. I reissued token and set up a proper log out and now everything seems to work just fine. Thanks for the help!

P.S. If you have any explanation why should either logOut or close methods be called directly when not using webhooks at all it would be very helpful.

negasus commented 3 days ago

I don't use logOut )

NiJeTi commented 3 days ago

I don't use logOut )

Neither did I, but it seems that my approach with placing bot.Start in goroutine may caused such behavior.