@@ -7,11 +7,12 @@ import (
77
88// Poller implements long-polling for Telegram updates.
99type Poller struct {
10- Bot * Bot
11- Offset int
12- Interval time.Duration
13- Timeout int
14- log Logger
10+ Bot * Bot
11+ Offset int
12+ Interval time.Duration
13+ Timeout int
14+ consecutiveErrors int
15+ log Logger
1516}
1617
1718// NewPoller creates a new Poller with sensible defaults.
@@ -35,6 +36,22 @@ func (p *Poller) SetLogger(l Logger) {
3536 p .log = l
3637}
3738
39+ // backoffDuration returns the sleep duration after consecutive poll errors.
40+ // Formula: interval * 2^errors, capped at 60 * interval.
41+ // Returns 0 for 0 errors (no backoff needed).
42+ func (p * Poller ) backoffDuration (errors int ) time.Duration {
43+ if errors <= 0 {
44+ return 0
45+ }
46+ shift := 1 << errors // 2^errors
47+ d := p .Interval * time .Duration (shift )
48+ max := 60 * p .Interval
49+ if d > max {
50+ return max
51+ }
52+ return d
53+ }
54+
3855// Poll performs a single long-poll cycle.
3956// It calls GetUpdates with the current offset and timeout,
4057// advances the offset past the highest update ID received,
@@ -68,7 +85,9 @@ func (p *Poller) Poll(ctx context.Context) ([]Update, error) {
6885// Start begins the infinite long-polling loop.
6986// It calls Poll() repeatedly, sending each received update to the channel.
7087// On empty result (timeout), sleeps for Interval then retries.
71- // On error, sleeps for 5*Interval (backoff), logs to stderr, but continues.
88+ // On error, sleeps with exponential backoff (interval * 2^consecutiveErrors,
89+ // capped at 60 * interval), logs to the logger, but continues.
90+ // Backoff resets to zero after a successful poll.
7291// When ctx is cancelled, closes the channel and returns ctx.Err().
7392func (p * Poller ) Start (ctx context.Context , updates chan <- Update ) error {
7493 defer close (updates )
@@ -88,9 +107,10 @@ func (p *Poller) Start(ctx context.Context, updates chan<- Update) error {
88107 default :
89108 }
90109
91- p .log .Error ("poll error" , "error" , err )
110+ p .consecutiveErrors ++
111+ backoff := p .backoffDuration (p .consecutiveErrors )
112+ p .log .Error ("poll error" , "error" , err , "consecutive_errors" , p .consecutiveErrors , "backoff" , backoff )
92113
93- backoff := 5 * p .Interval
94114 select {
95115 case <- ctx .Done ():
96116 return ctx .Err ()
@@ -99,6 +119,9 @@ func (p *Poller) Start(ctx context.Context, updates chan<- Update) error {
99119 continue
100120 }
101121
122+ // Successful poll — reset error counter.
123+ p .consecutiveErrors = 0
124+
102125 if len (result ) == 0 {
103126 // Timeout — no updates, sleep for Interval and retry.
104127 select {
0 commit comments