move gotd fork into repo. (#111)
- update to latest telegram layer - remove some references to fields in tg.Entities that don't exist in the schema - originally added here: https://github.com/beeper/td/commit/820929062a2ba0104397bc01235ab58a9cff780e - referenced here - https://github.com/mautrix/telegramgo/commit/124f0967ed195b5a380c9bd02e170ada9710dde3 - https://github.com/mautrix/telegramgo/commit/4205047aab2e0639217148b5d125bfaab668bd8e
This commit is contained in:
@@ -0,0 +1,262 @@
|
||||
package telegram
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.uber.org/atomic"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/bin"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/clock"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/mtproto"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/oteltg"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/pool"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/session"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/tdsync"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/telegram/dcs"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/telegram/internal/manager"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/telegram/internal/version"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
||||
)
|
||||
|
||||
// UpdateHandler will be called on received updates from Telegram.
|
||||
type UpdateHandler interface {
|
||||
Handle(ctx context.Context, u tg.UpdatesClass) error
|
||||
}
|
||||
|
||||
// UpdateHandlerFunc type is an adapter to allow the use of
|
||||
// ordinary function as update handler.
|
||||
//
|
||||
// UpdateHandlerFunc(f) is an UpdateHandler that calls f.
|
||||
type UpdateHandlerFunc func(ctx context.Context, u tg.UpdatesClass) error
|
||||
|
||||
// Handle calls f(ctx, u)
|
||||
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
|
||||
}
|
||||
|
||||
// Client represents a MTProto client to Telegram.
|
||||
type Client struct {
|
||||
// Put migration in the header of the structure to ensure 64-bit alignment,
|
||||
// otherwise it will cause the atomic operation of connsCounter to panic.
|
||||
// DO NOT change the order of members arbitrarily.
|
||||
// Ref: https://pkg.go.dev/sync/atomic#pkg-note-BUG
|
||||
|
||||
// Connection factory fields.
|
||||
connsCounter atomic.Int64
|
||||
create connConstructor // immutable
|
||||
resolver dcs.Resolver // immutable
|
||||
onDead func() // immutable
|
||||
onAuthError func(error) // immutable
|
||||
onConnected func() // immutable
|
||||
newConnBackoff func() backoff.BackOff // immutable
|
||||
defaultMode manager.ConnMode // immutable
|
||||
|
||||
// Migration state.
|
||||
migrationTimeout time.Duration // immutable
|
||||
migration chan struct{}
|
||||
|
||||
// tg provides RPC calls via Client. Uses invoker below.
|
||||
tg *tg.Client // immutable
|
||||
// invoker implements tg.Invoker on top of Client and mw.
|
||||
invoker tg.Invoker // immutable
|
||||
// mw is list of middlewares used in invoker, can be blank.
|
||||
mw []Middleware // immutable
|
||||
|
||||
// Telegram device information.
|
||||
device DeviceConfig // immutable
|
||||
|
||||
// MTProto options.
|
||||
opts mtproto.Options // immutable
|
||||
|
||||
// DCList state.
|
||||
// Domain list (for websocket)
|
||||
domains map[int]string // immutable
|
||||
// Denotes to use Test DCs.
|
||||
testDC bool // immutable
|
||||
|
||||
// Connection state. Guarded by connMux.
|
||||
session *pool.SyncSession
|
||||
cfg *manager.AtomicConfig
|
||||
conn clientConn
|
||||
connBackoff atomic.Pointer[backoff.BackOff]
|
||||
connMux sync.Mutex
|
||||
|
||||
// Restart signal channel.
|
||||
restart chan struct{} // immutable
|
||||
|
||||
// Connections to non-primary DC.
|
||||
subConns map[int]CloseInvoker
|
||||
subConnsMux sync.Mutex
|
||||
sessions map[int]*pool.SyncSession
|
||||
sessionsMux sync.Mutex
|
||||
|
||||
// Wrappers for external world, like logs or PRNG.
|
||||
rand io.Reader // immutable
|
||||
log *zap.Logger // immutable
|
||||
clock clock.Clock // immutable
|
||||
|
||||
// Client context. Will be canceled by Run on exit.
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
|
||||
// Client config.
|
||||
appID int // immutable
|
||||
appHash string // immutable
|
||||
// Session storage.
|
||||
storage clientStorage // immutable, nillable
|
||||
|
||||
// Ready signal channel, sends signal when client connection is ready.
|
||||
// Resets on reconnect.
|
||||
ready *tdsync.ResetReady // immutable
|
||||
|
||||
// Telegram updates handler.
|
||||
updateHandler UpdateHandler // immutable
|
||||
// Denotes that no update mode is enabled.
|
||||
noUpdatesMode bool // immutable
|
||||
|
||||
// Tracing.
|
||||
tracer trace.Tracer
|
||||
|
||||
// onTransfer is called in transfer.
|
||||
onTransfer AuthTransferHandler
|
||||
|
||||
// onSelfError is called on error calling Self().
|
||||
onSelfError func(ctx context.Context, err error) error
|
||||
}
|
||||
|
||||
// NewClient creates new unstarted client.
|
||||
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: opt.Logger,
|
||||
appID: appID,
|
||||
appHash: appHash,
|
||||
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,
|
||||
onAuthError: opt.OnAuthError,
|
||||
onConnected: opt.OnConnected,
|
||||
clock: opt.Clock,
|
||||
device: opt.Device,
|
||||
migrationTimeout: opt.MigrationTimeout,
|
||||
noUpdatesMode: opt.NoUpdates,
|
||||
mw: opt.Middlewares,
|
||||
onTransfer: opt.OnTransfer,
|
||||
onSelfError: opt.OnSelfError,
|
||||
}
|
||||
if opt.TracerProvider != nil {
|
||||
client.tracer = opt.TracerProvider.Tracer(oteltg.Name)
|
||||
}
|
||||
client.init()
|
||||
|
||||
// Including version into client logger to help with debugging.
|
||||
if v := version.GetVersion(); v != "" {
|
||||
client.log = client.log.With(zap.String("v", v))
|
||||
}
|
||||
|
||||
if opt.SessionStorage != nil {
|
||||
client.storage = &session.Loader{
|
||||
Storage: opt.SessionStorage,
|
||||
}
|
||||
}
|
||||
if opt.CustomSessionStorage != nil {
|
||||
client.storage = opt.CustomSessionStorage
|
||||
}
|
||||
|
||||
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,
|
||||
Clock: opt.Clock,
|
||||
PingInterval: opt.PingInterval,
|
||||
PingCallback: opt.PingCallback,
|
||||
PingTimeout: opt.PingTimeout,
|
||||
|
||||
Handler: SessionNotifierHandler{opt.OnSession},
|
||||
|
||||
Types: getTypesMapping(),
|
||||
|
||||
Tracer: client.tracer,
|
||||
}
|
||||
client.conn = client.createPrimaryConn(nil)
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
// SessionNotifierHandler is a handler which notifies when a session is
|
||||
// received.
|
||||
type SessionNotifierHandler struct {
|
||||
onSession func()
|
||||
}
|
||||
|
||||
// OnSession implements Handler.
|
||||
func (n SessionNotifierHandler) OnSession(s mtproto.Session) error {
|
||||
if n.onSession != nil {
|
||||
n.onSession()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnMessage implements Handler
|
||||
func (n SessionNotifierHandler) OnMessage(b *bin.Buffer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// init sets fields which needs explicit initialization, like maps or channels.
|
||||
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.restart = make(chan struct{})
|
||||
c.migration = make(chan struct{}, 1)
|
||||
c.sessions = map[int]*pool.SyncSession{}
|
||||
c.subConns = map[int]CloseInvoker{}
|
||||
c.invoker = chainMiddlewares(InvokeFunc(c.invokeDirect), c.mw...)
|
||||
c.tg = tg.NewClient(c.invoker)
|
||||
}
|
||||
Reference in New Issue
Block a user