package telegram import ( "github.com/pkg/errors" "hash/maphash" "path/filepath" "strconv" "sync" "time" "dev.narayana.im/narayana/telegabber/config" "dev.narayana.im/narayana/telegabber/persistence" "dev.narayana.im/narayana/telegabber/telegram/cache" "github.com/zelenin/go-tdlib/client" "gosrc.io/xmpp" ) var logConstants = map[string]int32{ ":fatal": 0, ":error": 1, ":warn": 2, ":info": 3, ":debug": 4, ":verbose": 5, ":all": 1023, } func stringToLogConstant(c string) int32 { level, ok := logConstants[c] if !ok { level = 0 } return level } // DelayedStatus describes an online status expiring on timeout type DelayedStatus struct { TimestampOnline int64 TimestampExpired int64 } // MUCState holds MUC metadata type MUCState struct { Resources map[string]bool Members map[int64]*MUCMember } // MUCMember represents a MUC member type MUCMember struct { Nickname string Affiliation string } func NewMUCState() *MUCState { return &MUCState{ Resources: make(map[string]bool), Members: make(map[int64]*MUCMember), } } // Client stores the metadata for lazily invoked TDlib instance type Client struct { client *client.Client authorizer *clientAuthorizer parameters *client.SetTdlibParametersRequest options []client.Option me *client.User xmpp *xmpp.Component jid string Session *persistence.Session resources map[string]bool outbox map[string]string content *config.TelegramContentConfig cache *cache.Cache online bool DelayedStatuses map[int64]*DelayedStatus DelayedStatusesLock sync.Mutex lastMsgHashes map[int64]uint64 msgHashSeed maphash.Seed mucCache map[int64]*MUCState locks clientLocks SendMessageLock sync.Mutex } type clientLocks struct { authorizationReady sync.Mutex chatMessageLocks map[int64]*sync.Mutex resourcesLock sync.Mutex outboxLock sync.Mutex mucCacheLock sync.Mutex lastMsgHashesLock sync.Mutex authorizerReadLock sync.Mutex authorizerWriteLock sync.Mutex } // NewClient instantiates a Telegram App func NewClient(conf config.TelegramConfig, jid string, component *xmpp.Component, session *persistence.Session) (*Client, error) { var options []client.Option options = append(options, client.WithLogVerbosity(&client.SetLogVerbosityLevelRequest{ NewVerbosityLevel: stringToLogConstant(conf.Loglevel), })) if conf.Tdlib.Client.CatchTimeout != 0 { options = append(options, client.WithCatchTimeout( time.Duration(conf.Tdlib.Client.CatchTimeout)*time.Second, )) } apiID, err := strconv.ParseInt(conf.Tdlib.Client.APIID, 10, 32) if err != nil { return &Client{}, errors.Wrap(err, "Wrong api_id") } datadir := conf.Tdlib.Datadir if datadir == "" { datadir = "./sessions/" // ye olde defaute } parameters := client.SetTdlibParametersRequest{ UseTestDc: false, DatabaseDirectory: filepath.Join(datadir, jid), FilesDirectory: filepath.Join(datadir, jid, "/files/"), UseFileDatabase: true, UseChatInfoDatabase: conf.Tdlib.Client.UseChatInfoDatabase, UseMessageDatabase: true, UseSecretChats: conf.Tdlib.Client.UseSecretChats, ApiId: int32(apiID), ApiHash: conf.Tdlib.Client.APIHash, SystemLanguageCode: "en", DeviceModel: conf.Tdlib.Client.DeviceModel, SystemVersion: "1.0.0", ApplicationVersion: conf.Tdlib.Client.ApplicationVersion, EnableStorageOptimizer: true, IgnoreFileNames: false, } return &Client{ parameters: ¶meters, xmpp: component, jid: jid, Session: session, resources: make(map[string]bool), outbox: make(map[string]string), mucCache: make(map[int64]*MUCState), content: &conf.Content, cache: cache.NewCache(), options: options, DelayedStatuses: make(map[int64]*DelayedStatus), lastMsgHashes: make(map[int64]uint64), msgHashSeed: maphash.MakeSeed(), locks: clientLocks{ chatMessageLocks: make(map[int64]*sync.Mutex), }, }, nil }