diff options
Diffstat (limited to 'telegram/connect.go')
-rw-r--r-- | telegram/connect.go | 142 |
1 files changed, 117 insertions, 25 deletions
diff --git a/telegram/connect.go b/telegram/connect.go index 37f719e..b1b8b10 100644 --- a/telegram/connect.go +++ b/telegram/connect.go @@ -3,6 +3,7 @@ package telegram import ( "github.com/pkg/errors" "strconv" + "time" "dev.narayana.im/narayana/telegabber/xmpp/gateway" @@ -13,25 +14,25 @@ import ( const chatsLimit int32 = 999 type clientAuthorizer struct { - TdlibParameters chan *client.TdlibParameters + TdlibParameters chan *client.SetTdlibParametersRequest PhoneNumber chan string Code chan string State chan client.AuthorizationState Password chan string + FirstName chan string + LastName chan string + isClosed bool } func (stateHandler *clientAuthorizer) Handle(c *client.Client, state client.AuthorizationState) error { + if stateHandler.isClosed { + return errors.New("Channel is closed") + } stateHandler.State <- state switch state.AuthorizationStateType() { case client.TypeAuthorizationStateWaitTdlibParameters: - _, err := c.SetTdlibParameters(&client.SetTdlibParametersRequest{ - Parameters: <-stateHandler.TdlibParameters, - }) - return err - - case client.TypeAuthorizationStateWaitEncryptionKey: - _, err := c.CheckDatabaseEncryptionKey(&client.CheckDatabaseEncryptionKeyRequest{}) + _, err := c.SetTdlibParameters(<-stateHandler.TdlibParameters) return err case client.TypeAuthorizationStateWaitPhoneNumber: @@ -52,7 +53,11 @@ func (stateHandler *clientAuthorizer) Handle(c *client.Client, state client.Auth return err case client.TypeAuthorizationStateWaitRegistration: - return client.ErrNotSupportedAuthorizationState + _, err := c.RegisterUser(&client.RegisterUserRequest{ + FirstName: <-stateHandler.FirstName, + LastName: <-stateHandler.LastName, + }) + return err case client.TypeAuthorizationStateWaitPassword: _, err := c.CheckAuthenticationPassword(&client.CheckAuthenticationPasswordRequest{ @@ -77,42 +82,54 @@ func (stateHandler *clientAuthorizer) Handle(c *client.Client, state client.Auth } func (stateHandler *clientAuthorizer) Close() { + if stateHandler.isClosed { + return + } + stateHandler.isClosed = true close(stateHandler.TdlibParameters) close(stateHandler.PhoneNumber) close(stateHandler.Code) close(stateHandler.State) close(stateHandler.Password) + close(stateHandler.FirstName) + close(stateHandler.LastName) } // Connect starts TDlib connection func (c *Client) Connect(resource string) error { + log.Warn("Attempting to connect to Telegram network...") + // avoid conflict if another authorization is pending already - c.locks.authorizationReady.Wait() + c.locks.authorizationReady.Lock() if c.Online() { c.roster(resource) + c.locks.authorizationReady.Unlock() return nil } log.Warn("Connecting to Telegram network...") + c.locks.authorizerWriteLock.Lock() c.authorizer = &clientAuthorizer{ - TdlibParameters: make(chan *client.TdlibParameters, 1), + TdlibParameters: make(chan *client.SetTdlibParametersRequest, 1), PhoneNumber: make(chan string, 1), Code: make(chan string, 1), State: make(chan client.AuthorizationState, 10), Password: make(chan string, 1), + FirstName: make(chan string, 1), + LastName: make(chan string, 1), } - c.locks.authorizationReady.Add(1) - go c.interactor() + log.Warn("Interactor launched") c.authorizer.TdlibParameters <- c.parameters + c.locks.authorizerWriteLock.Unlock() tdlibClient, err := client.NewClient(c.authorizer, c.options...) if err != nil { - c.locks.authorizationReady.Done() + c.locks.authorizationReady.Unlock() return errors.Wrap(err, "Couldn't initialize a Telegram client instance") } @@ -130,7 +147,7 @@ func (c *Client) Connect(resource string) error { go c.updateHandler() c.online = true - c.locks.authorizationReady.Done() + c.locks.authorizationReady.Unlock() c.addResource(resource) go func() { @@ -141,14 +158,55 @@ func (c *Client) Connect(resource string) error { log.Errorf("Could not retrieve chats: %v", err) } - gateway.SendPresence(c.xmpp, c.jid, gateway.SPType("subscribe")) - gateway.SendPresence(c.xmpp, c.jid, gateway.SPType("subscribed")) + gateway.SubscribeToTransport(c.xmpp, c.jid) gateway.SendPresence(c.xmpp, c.jid, gateway.SPStatus("Logged in as: "+c.Session.Login)) }() return nil } +func (c *Client) TryLogin(resource string, login string) error { + wasSessionLoginEmpty := c.Session.Login == "" + c.Session.Login = login + + if wasSessionLoginEmpty && c.authorizer == nil { + go func() { + err := c.Connect(resource) + if err != nil { + log.Error(errors.Wrap(err, "TDlib connection failure")) + } + }() + // a quirk for authorizer to become ready. If it's still not, + // nothing bad: just re-login again + time.Sleep(1e5) + } + + c.locks.authorizerWriteLock.Lock() + defer c.locks.authorizerWriteLock.Unlock() + + if c.authorizer == nil { + return errors.New(TelegramNotInitialized) + } + + if c.authorizer.isClosed { + return errors.New(TelegramAuthDone) + } + + return nil +} + +func (c *Client) SetPhoneNumber(login string) error { + c.locks.authorizerWriteLock.Lock() + defer c.locks.authorizerWriteLock.Unlock() + + if c.authorizer == nil || c.authorizer.isClosed { + return errors.New("Authorization not needed") + } + + c.authorizer.PhoneNumber <- login + return nil +} + // Disconnect drops TDlib connection and // returns the flag indicating if disconnecting is permitted func (c *Client) Disconnect(resource string, quit bool) bool { @@ -178,20 +236,23 @@ func (c *Client) Disconnect(resource string, quit bool) bool { ) } - _, err := c.client.Close() - if err != nil { - log.Errorf("Couldn't close the Telegram instance: %v; %#v", err, c) - } - c.forceClose() + c.close() return true } func (c *Client) interactor() { for { + c.locks.authorizerReadLock.Lock() + if c.authorizer == nil { + log.Warn("Authorizer is lost, halting the interactor") + c.locks.authorizerReadLock.Unlock() + return + } state, ok := <-c.authorizer.State if !ok { log.Warn("Interactor is disconnected") + c.locks.authorizerReadLock.Unlock() return } @@ -206,25 +267,56 @@ func (c *Client) interactor() { if c.Session.Login != "" { c.authorizer.PhoneNumber <- c.Session.Login } else { - gateway.SendMessage(c.jid, "", "Please, enter your Telegram login via /login 12345", c.xmpp) + gateway.SendServiceMessage(c.jid, "Please, enter your Telegram login via /login 12345", c.xmpp) } // stage 1: wait for auth code case client.TypeAuthorizationStateWaitCode: log.Warn("Waiting for authorization code...") - gateway.SendMessage(c.jid, "", "Please, enter authorization code via /code 12345", c.xmpp) + gateway.SendServiceMessage(c.jid, "Please, enter authorization code via /code 12345", c.xmpp) + // stage 1b: wait for registration + case client.TypeAuthorizationStateWaitRegistration: + log.Warn("Waiting for full name...") + gateway.SendServiceMessage(c.jid, "This number is not registered yet! Please, enter your name via /setname John Doe", c.xmpp) // stage 2: wait for 2fa case client.TypeAuthorizationStateWaitPassword: log.Warn("Waiting for 2FA password...") - gateway.SendMessage(c.jid, "", "Please, enter 2FA passphrase via /password 12345", c.xmpp) + gateway.SendServiceMessage(c.jid, "Please, enter 2FA passphrase via /password 12345", c.xmpp) } + c.locks.authorizerReadLock.Unlock() } } func (c *Client) forceClose() { + c.locks.authorizerReadLock.Lock() + c.locks.authorizerWriteLock.Lock() + defer c.locks.authorizerReadLock.Unlock() + defer c.locks.authorizerWriteLock.Unlock() + c.online = false c.authorizer = nil } +func (c *Client) close() { + c.locks.authorizerWriteLock.Lock() + if c.authorizer != nil && !c.authorizer.isClosed { + c.authorizer.Close() + } + c.locks.authorizerWriteLock.Unlock() + + if c.client != nil { + _, err := c.client.Close() + if err != nil { + log.Errorf("Couldn't close the Telegram instance: %v; %#v", err, c) + } + } + c.forceClose() +} + +func (c *Client) cancelAuth() { + c.close() + c.Session.Login = "" +} + // Online checks if the updates listener is alive func (c *Client) Online() bool { return c.online |