refactor(server): delete webhook once on startup, no retry

The Bot API GET reliably clears the webhook, so the retry loop added for the
empty-body POST is no longer needed. One best-effort call; log on failure.
This commit is contained in:
2026-06-28 22:52:55 +07:00
parent 7d753a03cc
commit 2722db5429
+6 -37
View File
@@ -146,7 +146,12 @@ func main() {
// Clear any webhook left over from the AWS deployment at startup, before the
// owner DM and before polling. getUpdates (long polling, below) returns HTTP
// 409 while a webhook is set, so a stuck webhook silently breaks the bot.
clearWebhook(rootCtx, cfg.TelegramBotToken)
// Best-effort, one shot: a real failure here is logged, not retried.
if err := telegram.DeleteWebhook(rootCtx, cfg.TelegramBotToken); err != nil {
log.Warn("deleteWebhook failed; getUpdates may 409 if a webhook is set", "err", err)
} else {
log.Info("webhook cleared")
}
deploynotify.Run(rootCtx, deploynotify.Config{
Bot: b,
@@ -193,42 +198,6 @@ func main() {
}
}
// webhookDeleteAttempts bounds the startup webhook-clear retries, and
// webhookDeleteBackoff is the wait between them. Telegram's edge sometimes
// returns an empty body on the first request after a cold container start
// ("unexpected end of JSON input"); a short retry rides over that blip so long
// polling is not left blocked by a stale webhook.
const (
webhookDeleteAttempts = 5
webhookDeleteBackoff = 1 * time.Second
)
// clearWebhook deletes any configured webhook so getUpdates (long polling) does
// not 409. It is best-effort but retried so a flaky network does not leave the
// bot permanently unable to poll. Uses telegram.DeleteWebhook (a plain Bot API
// GET) rather than the go-telegram helper, whose empty-multipart POST comes back
// with an empty body in some environments. Pending updates are kept so the
// poller drains the buffered queue for a lossless cutover.
func clearWebhook(ctx context.Context, token string) {
for attempt := 1; attempt <= webhookDeleteAttempts; attempt++ {
err := telegram.DeleteWebhook(ctx, token)
if err == nil {
log.Info("webhook cleared", "attempt", attempt)
return
}
log.Warn("deleteWebhook failed", "attempt", attempt, "err", err)
if attempt == webhookDeleteAttempts {
break
}
select {
case <-ctx.Done():
return
case <-time.After(webhookDeleteBackoff):
}
}
log.Warn("deleteWebhook still failing after retries; getUpdates may 409 if a webhook is set")
}
// buildProvider picks the storage backend. Selection order:
// 1. Explicit KV_PROVIDER env (memory|mongodb) wins.
// 2. Auto-detect: MONGO_URL set → mongodb; otherwise memory.