package telegram
import (
"context"
"time"
"github.com/cenkalti/backoff/v4"
"github.com/go-faster/errors"
"go.uber.org/multierr"
"go.uber.org/zap"
"github.com/gotd/td/internal/exchange"
"github.com/gotd/td/internal/tdsync"
"github.com/gotd/td/telegram/auth"
)
func (c *Client ) runUntilRestart (ctx context .Context ) error {
g := tdsync .NewCancellableGroup (ctx )
g .Go (c .conn .Run )
if !c .noUpdatesMode {
g .Go (func (ctx context .Context ) error {
self , err := c .Self (ctx )
if err != nil {
if !auth .IsUnauthorized (err ) {
c .log .Warn ("Got error on self" , zap .Error (err ))
}
return nil
}
c .log .Info ("Got self" , zap .String ("username" , self .Username ))
return nil
})
}
g .Go (func (ctx context .Context ) error {
select {
case <- ctx .Done ():
return ctx .Err ()
case <- c .restart :
c .log .Debug ("Restart triggered" )
g .Cancel ()
return nil
}
})
return g .Wait ()
}
func (c *Client ) isPermanentError (err error ) bool {
return errors .Is (err , exchange .ErrKeyFingerprintNotFound )
}
func (c *Client ) reconnectUntilClosed (ctx context .Context ) error {
b := tdsync .SyncBackoff (backoff .WithContext (c .connBackoff (), ctx ))
return backoff .RetryNotify (func () error {
if err := c .runUntilRestart (ctx ); err != nil {
if c .isPermanentError (err ) {
return backoff .Permanent (err )
}
return err
}
return nil
}, b , func (err error , timeout time .Duration ) {
c .log .Info ("Restarting connection" , zap .Error (err ), zap .Duration ("backoff" , timeout ))
c .connMux .Lock ()
c .conn = c .createPrimaryConn (nil )
c .connMux .Unlock ()
})
}
func (c *Client ) onReady () {
c .log .Debug ("Ready" )
c .ready .Signal ()
}
func (c *Client ) resetReady () {
c .ready .Reset ()
}
func (c *Client ) Run (ctx context .Context , f func (ctx context .Context ) error ) (err error ) {
if c .ctx != nil {
select {
case <- c .ctx .Done ():
return errors .Wrap (c .ctx .Err (), "client already closed" )
default :
}
}
c .ctx , c .cancel = context .WithCancel (ctx )
c .log .Info ("Starting" )
defer c .log .Info ("Closed" )
defer c .cancel ()
defer func () {
c .subConnsMux .Lock ()
defer c .subConnsMux .Unlock ()
for _ , conn := range c .subConns {
if closeErr := conn .Close (); !errors .Is (closeErr , context .Canceled ) {
multierr .AppendInto (&err , closeErr )
}
}
}()
c .resetReady ()
if err := c .restoreConnection (ctx ); err != nil {
return err
}
g := tdsync .NewCancellableGroup (ctx )
g .Go (c .reconnectUntilClosed )
g .Go (func (ctx context .Context ) error {
select {
case <- ctx .Done ():
c .cancel ()
return ctx .Err ()
case <- c .ctx .Done ():
return c .ctx .Err ()
}
})
g .Go (func (ctx context .Context ) error {
select {
case <- ctx .Done ():
return ctx .Err ()
case <- c .ready .Ready ():
if err := f (ctx ); err != nil {
return errors .Wrap (err , "callback" )
}
c .log .Debug ("Callback returned, stopping" )
g .Cancel ()
return nil
}
})
if err := g .Wait (); !errors .Is (err , context .Canceled ) {
return err
}
return nil
}
The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .