NicoNex / echotron

An elegant and concurrent library for the Telegram bot API in Go.
https://t.me/s/echotronnews
GNU Lesser General Public License v3.0
363 stars 26 forks source link

How to run a bot on two or more servers #46

Closed GMELUM closed 3 months ago

GMELUM commented 3 months ago

I have 2 server instances behind a balancer. I need to use Webhook on both servers and receive notifications on one of them.

I use this:

for u := range echotron.WebhookUpdates("https://example.com/bot", token) {
    if u.PreCheckoutQuery != nil && u.PreCheckoutQuery.Currency == "XTR" {
        println(u.PreCheckoutQuery.InvoicePayload)
    }
}

but I get a 429 error

NicoNex commented 3 months ago

To avoid the error 429 make sure you're using the latest Echotron update that implements a rate limiter on the requests. Additionally you might want to set the limiter duration to 1/2 of the current values since you're running it on two machines. Then you can stetup a custom server for both instances and use this function:

// HandleWebhook is the http.HandlerFunc for the webhook URL.
// Useful if you've already a http server running and want to handle the request yourself.
func (d *Dispatcher) HandleWebhook(w http.ResponseWriter, r *http.Request)
GMELUM commented 3 months ago

panic is caused when trying to call setWebhook. But one of the instances must call it.

NicoNex commented 3 months ago

I think what you could do is to call the SetWebhook on your balancer or a third service and then have the two instances instantiate a new dispatcher and create a webserver that calls HandleWebhook.

This way the third service (or the balacer itself) is the one receiving the updates from Telegram and then proceeds to route them to the right address.

NicoNex commented 3 months ago

What I would do for the two instances is this:

func main() {
    dsp := echotron.NewDispatcher()

    http.HandleFunc("/", dsp.HandleWebhook)
    http.ListenAndServe(":8080", nil)
}

Then all you have to do is set the actual webhook to Telegram in a middle man that calls one of the two services according to the balancing logic.

GMELUM commented 3 months ago

theoretically I can call setWebhook on each of the servers since all servers have the same url? Just handle the error and not cause panic.

NicoNex commented 3 months ago

Yes you can do that, but you can't use dispatcher.ListenWebhook since it tries to set the webhook and if there's an error it returns without listening. What you can do is what I showed you in the last snippet which is actually what the ListenWebhook does aside setting the webhook to Telegram, this way you'll be actually listening for updates.

GMELUM commented 3 months ago

Thank you, everything worked for me. Only now I'm seeing another problem. The same code with a webhook works differently on different bots. On one bot everything works correctly, but on the second I wait a very, very long time for an answer. Some requests don't arrive at all. Perhaps you have a theory of what the problem might be?

NicoNex commented 3 months ago

Yes, it could be due to the request limiters, you should try setting different values for echotron.SetChatRequestLimit and echotron.SetGlobalRequestLimit.

The current defaults are time.Minute / 20 for the chat limiter and time.Second / 30 for the global limiter.

NicoNex commented 3 months ago

Maybe in the next release I should disable them by default since I'm seeing that the guidelines provided by telegram are too strict and people generally don't like those high limits.

GMELUM commented 3 months ago

My problem doesn't appear over time. I just receive a request from Telegram after 5-20 seconds, but on another bot it doesn’t take even 1 second. The code is the same, only the tokens are different.

GMELUM commented 3 months ago

The problem was solved as soon as I re-issued a new token for the bot. Thanks for the help again

NicoNex commented 3 months ago

No problem, I'm closing this issue but if you have more questions feel free to text in the telegram group dedicated to Echotron, you can find the link to it in the readme ;).