PaulSonOfLars / gotgbot

Autogenerated Go wrapper for the telegram API. Inspired by the python-telegram-bot library.
MIT License
469 stars 107 forks source link

make Dispatcher support middleware stack #115

Closed liuerfire closed 1 year ago

liuerfire commented 1 year ago

Currently if we want some middlewares for the dispatcher, we must write like this:


func middleware(next handlers.Response) handlers.Response {
    fn := func(b *gotgbot.Bot, ctx *ext.Context) error {
                fmt.Println("I'm in a middleware")
        return next(b, ctx)
    }
    return fn
}

dispatcher.AddHandler(handlers.NewCommand("start", middleware(start)))
dispatcher.AddHandler(handlers.NewCommand("help", middleware(help)))

If there're lots of middlewares, then this will be like:

dispatcher.AddHandler(handlers.NewCommand("start", middleware1(middleware2(start))))
dispatcher.AddHandler(handlers.NewCommand("help", middleware1(middleware2(help))))

and this is tedious.

How about add a help method to support middleware stack, something like:


dispatcher.Use(middleware1)
dispatcher.Use(middleware2)

dispatcher.AddHandler(handlers.NewCommand("start", start))

what do you think? If you're ok with this, I think I can make a PR.

PaulSonOfLars commented 1 year ago

Thanks for your suggestion! I think I understand where you're coming from, but I'm not sure I agree. It all depends on what people consider a "middleware", and/or what this "middleware" aims to do. And I believe each of the options is already covered in different ways.

Option 1, preprocessing data

This is for preprocessing that needs to happen for every incoming update. You can achieve this by adding a handler at the start of the group, and ending it with ext.continuegroups so other bits get processed. Or, it can be added to one of the previous groups.

This allows you to run an item once for the data to be reused across other handlers; eg, admin checks.

Option 2, changing the response signature

If you need to change the handlers.Response signature (eg, to add reference to a database, or to expose the command "args"), then this can be done by preparing a custom handler which accepts the custom signature. Or, it can be done with a pattern similar to the one you've described, using a function to "bridge" the two.

Both of these patterns can also be used to handle errors.

Option 3, creating a Handler set

This would create a "parent" handler, which stores a set of "child" handlers that collectively can be pre/post processed by some slightly different behaviour. For example, it could be used to disable all handlers of a specific "module" in your bot. Or to handle errors slightly differently.

This last option is something that I am still thinking about, and would love to have some feedback/suggestions on :)

Ultimately, I worry that a single dispatcher middleware would be too restrictive for most use cases and wouldn't be able to cover the many nuances that different bots could have. What do you think?

If you've got any specific examples that would work best with your middleware concept, I would love to hear about them :)

liuerfire commented 1 year ago

Oh, I didn't notice that there's group, which meets my needs. Thanks!

"Option 3" is also something I'm thinking about to re-organize my project. But I don't find a clean way to do this, either. I'll give some feedbacks asap, if I work it out :-)