package telegram
import (
"context"
"io"
"sync"
"time"
"github.com/cenkalti/backoff/v4"
"go.opentelemetry.io/otel/trace"
"go.uber.org/atomic"
"golang.org/x/sync/singleflight"
"github.com/gotd/log"
"github.com/gotd/td/bin"
"github.com/gotd/td/clock"
"github.com/gotd/td/mtproto"
"github.com/gotd/td/oteltg"
"github.com/gotd/td/pool"
"github.com/gotd/td/session"
"github.com/gotd/td/tdsync"
"github.com/gotd/td/telegram/dcs"
"github.com/gotd/td/telegram/internal/manager"
"github.com/gotd/td/telegram/internal/version"
"github.com/gotd/td/tg"
)
type UpdateHandler interface {
Handle (ctx context .Context , u tg .UpdatesClass ) error
}
type UpdateHandlerFunc func (ctx context .Context , u tg .UpdatesClass ) error
func (f UpdateHandlerFunc ) Handle (ctx context .Context , u tg .UpdatesClass ) error {
return f (ctx , u )
}
type clientStorage interface {
Load (ctx context .Context ) (*session .Data , error )
Save (ctx context .Context , data *session .Data ) error
}
type clientConn interface {
Run (ctx context .Context ) error
Invoke (ctx context .Context , input bin .Encoder , output bin .Decoder ) error
Ping (ctx context .Context ) error
}
type Client struct {
connsCounter atomic .Int64
create connConstructor
resolver dcs .Resolver
onDead func (error )
newConnBackoff func () backoff .BackOff
defaultMode manager .ConnMode
onConnectionState func (ConnectionState )
migrationTimeout time .Duration
migration chan struct {}
tg *tg .Client
invoker tg .Invoker
mw []Middleware
device DeviceConfig
opts mtproto .Options
domains map [int ]string
testDC bool
session *pool .SyncSession
cfg *manager .AtomicConfig
conn clientConn
connChanged chan struct {}
connBackoff atomic .Pointer [backoff .BackOff ]
connMux sync .Mutex
restart chan struct {}
subConns map [int ]CloseInvoker
subConnsMux sync .Mutex
cdnPools cdnPoolManager
sessions map [int ]*pool .SyncSession
cdnSessions map [int ]*pool .SyncSession
sessionsMux sync .Mutex
cdnKeys []PublicKey
cdnKeysByDC map [int ][]PublicKey
cdnKeysSet bool
cdnKeysGen uint64
cdnKeysMux sync .Mutex
cdnKeysLoad singleflight .Group
rand io .Reader
log log .Helper
clock clock .Clock
ctx context .Context
cancel context .CancelFunc
appID int
appHash string
allowCDN bool
storage clientStorage
ready *tdsync .ResetReady
updateHandler UpdateHandler
noUpdatesMode bool
tracer trace .Tracer
onTransfer AuthTransferHandler
onSelfError func (ctx context .Context , err error ) error
onSelfSuccess func (self *tg .User )
}
func NewClient (appID int , appHash string , opt Options ) *Client {
opt .setDefaults ()
mode := manager .ConnModeUpdates
if opt .NoUpdates {
mode = manager .ConnModeData
}
client := &Client {
rand : opt .Random ,
log : log .For (opt .Logger ),
appID : appID ,
appHash : appHash ,
allowCDN : opt .AllowCDN ,
updateHandler : opt .UpdateHandler ,
session : pool .NewSyncSession (pool .Session {
DC : opt .DC ,
}),
domains : opt .DCList .Domains ,
testDC : opt .DCList .Test ,
cfg : manager .NewAtomicConfig (tg .Config {
DCOptions : opt .DCList .Options ,
}),
create : defaultConstructor (),
resolver : opt .Resolver ,
defaultMode : mode ,
newConnBackoff : opt .ReconnectionBackoff ,
onDead : opt .OnDead ,
onConnectionState : opt .OnConnectionState ,
clock : opt .Clock ,
device : opt .Device ,
migrationTimeout : opt .MigrationTimeout ,
noUpdatesMode : opt .NoUpdates ,
mw : opt .Middlewares ,
onTransfer : opt .OnTransfer ,
onSelfError : opt .OnSelfError ,
onSelfSuccess : opt .OnSelfSuccess ,
}
if opt .TracerProvider != nil {
client .tracer = opt .TracerProvider .Tracer (oteltg .Name )
}
client .init ()
if v := version .GetVersion (); v != "" {
client .log = client .log .With (log .String ("v" , v ))
}
if opt .SessionStorage != nil {
client .storage = &session .Loader {
Storage : opt .SessionStorage ,
}
}
client .opts = mtproto .Options {
PublicKeys : opt .PublicKeys ,
Random : opt .Random ,
Logger : opt .Logger ,
AckBatchSize : opt .AckBatchSize ,
AckInterval : opt .AckInterval ,
RetryInterval : opt .RetryInterval ,
MaxRetries : opt .MaxRetries ,
CompressThreshold : opt .CompressThreshold ,
MessageID : opt .MessageID ,
ExchangeTimeout : opt .ExchangeTimeout ,
DialTimeout : opt .DialTimeout ,
EnablePFS : opt .EnablePFS ,
TempKeyTTL : opt .TempKeyTTL ,
Clock : opt .Clock ,
Types : getTypesMapping (),
Tracer : client .tracer ,
}
client .conn = client .createPrimaryConn (nil )
return client
}
func (c *Client ) replaceConn (conn clientConn ) {
c .conn = conn
close (c .connChanged )
c .connChanged = make (chan struct {})
}
func (c *Client ) init () {
if c .domains == nil {
c .domains = map [int ]string {}
}
if c .cfg == nil {
c .cfg = manager .NewAtomicConfig (tg .Config {})
}
c .ready = tdsync .NewResetReady ()
c .connChanged = make (chan struct {})
c .restart = make (chan struct {})
c .migration = make (chan struct {}, 1 )
c .sessions = map [int ]*pool .SyncSession {}
c .cdnSessions = map [int ]*pool .SyncSession {}
c .subConns = map [int ]CloseInvoker {}
c .cdnPools = newCDNPoolManager ()
c .cdnKeys = nil
c .cdnKeysByDC = nil
c .cdnKeysSet = false
c .cdnKeysGen = 0
c .cdnKeysLoad = singleflight .Group {}
c .invoker = chainMiddlewares (InvokeFunc (c .invokeDirect ), c .mw ...)
c .tg = tg .NewClient (c .invoker )
}
The pages are generated with Golds v0.8.4 . (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 @zigo_101 (reachable from the left QR code) to get the latest news of Golds .