aboutsummaryrefslogblamecommitdiff
path: root/telegram/connect.go
blob: 3a5a91f8b85b93b579520a5ec5ac530a9a5056e6 (plain) (tree)
1
2
3
4
5
6
7
8
9



                               
              
                 
 

                                                          
                                        


                                            

                            







































































                                                                                                       

                                  


                                                                     
                       


                          

                                                     






                                                                          
 

                                         
                         
 
                                                    
 
                                                                        
                       
                                                                                         


                              
                                              
                                         
                       
 
                            





                                    
                               
                        

                      
 

                                                          








                                                                  
 

                                  
                                                                                   
                              
         

 



                                                 
                                                               




                                                                         
                                        
 














                                                                                                                            

                                     
                                                         
 
                                                             















                                                                             
                                                                                                           

                              

                 
 
 
                               

                            
                          

 

                                                 
                                                                    
 
package telegram

import (
	"github.com/pkg/errors"
	"math"
	"strconv"

	"dev.narayana.im/narayana/telegabber/xmpp/gateway"

	log "github.com/sirupsen/logrus"
	"github.com/zelenin/go-tdlib/client"
)

const chatsLimit int32 = 999

type clientAuthorizer struct {
	TdlibParameters chan *client.TdlibParameters
	PhoneNumber     chan string
	Code            chan string
	State           chan client.AuthorizationState
	Password        chan string
}

func (stateHandler *clientAuthorizer) Handle(c *client.Client, state client.AuthorizationState) error {
	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{})
		return err

	case client.TypeAuthorizationStateWaitPhoneNumber:
		_, err := c.SetAuthenticationPhoneNumber(&client.SetAuthenticationPhoneNumberRequest{
			PhoneNumber: <-stateHandler.PhoneNumber,
			Settings: &client.PhoneNumberAuthenticationSettings{
				AllowFlashCall:       false,
				IsCurrentPhoneNumber: false,
				AllowSmsRetrieverApi: false,
			},
		})
		return err

	case client.TypeAuthorizationStateWaitCode:
		_, err := c.CheckAuthenticationCode(&client.CheckAuthenticationCodeRequest{
			Code: <-stateHandler.Code,
		})
		return err

	case client.TypeAuthorizationStateWaitRegistration:
		return client.ErrNotSupportedAuthorizationState

	case client.TypeAuthorizationStateWaitPassword:
		_, err := c.CheckAuthenticationPassword(&client.CheckAuthenticationPasswordRequest{
			Password: <-stateHandler.Password,
		})
		return err

	case client.TypeAuthorizationStateReady:
		return nil

	case client.TypeAuthorizationStateLoggingOut:
		return client.ErrNotSupportedAuthorizationState

	case client.TypeAuthorizationStateClosing:
		return client.ErrNotSupportedAuthorizationState

	case client.TypeAuthorizationStateClosed:
		return client.ErrNotSupportedAuthorizationState
	}

	return client.ErrNotSupportedAuthorizationState
}

func (stateHandler *clientAuthorizer) Close() {
	close(stateHandler.TdlibParameters)
	close(stateHandler.PhoneNumber)
	close(stateHandler.Code)
	close(stateHandler.State)
	close(stateHandler.Password)
}

// Connect starts TDlib connection
func (c *Client) Connect() error {
	// avoid conflict if another authorization is pending already
	c.locks.authorizationReady.Wait()

	if c.Online() {
		return nil
	}

	log.Warn("Connecting to Telegram network...")

	c.authorizer = &clientAuthorizer{
		TdlibParameters: make(chan *client.TdlibParameters, 1),
		PhoneNumber:     make(chan string, 1),
		Code:            make(chan string, 1),
		State:           make(chan client.AuthorizationState, 10),
		Password:        make(chan string, 1),
	}

	c.locks.authorizationReady.Add(1)

	go c.interactor()

	c.authorizer.TdlibParameters <- c.parameters

	tdlibClient, err := client.NewClient(c.authorizer, c.options...)
	if err != nil {
		return errors.Wrap(err, "Couldn't initialize a Telegram client instance")
	}

	c.client = tdlibClient
	c.listener = tdlibClient.GetListener()
	c.locks.authorizationReady.Done()
	c.online = true

	go c.updateHandler()

	return nil
}

// Disconnect drops TDlib connection
func (c *Client) Disconnect() {
	// already disconnected
	if !c.Online() {
		return
	}

	log.Warn("Disconnecting from Telegram network...")

	// we're offline (unsubscribe if logout)
	for id := range c.cache.chats {
		gateway.SendPresence(
			c.xmpp,
			c.jid,
			gateway.SPFrom(strconv.FormatInt(id, 10)),
			gateway.SPType("unavailable"),
		)
	}

	_, err := c.client.Close()
	if err != nil {
		log.Errorf("Couldn't close the Telegram instance: %v; %#v", err, c)
		c.forceClose()
	}
}

func (c *Client) interactor() {
	for {
		state, ok := <-c.authorizer.State
		if !ok {
			log.Error("Interactor is disconnected")
			return
		}

		stateType := state.AuthorizationStateType()
		log.Infof("Telegram authorization state: %#v", stateType)
		log.Debugf("%#v", state)

		switch stateType {
		case client.TypeAuthorizationStateWaitPhoneNumber:
			log.Warn("Logging in...")
			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)
			}
		case client.TypeAuthorizationStateWaitCode:
			log.Warn("Waiting for authorization code...")
			gateway.SendMessage(c.jid, "", "Please, enter authorization code via /code 12345", c.xmpp)
		case client.TypeAuthorizationStateWaitPassword:
			log.Warn("Waiting for 2FA password...")
			gateway.SendMessage(c.jid, "", "Please, enter 2FA passphrase via /password 12345", c.xmpp)
		case client.TypeAuthorizationStateReady:
			var err error

			c.locks.authorizationReady.Wait()

			log.Warn("Authorization successful!")

			c.me, err = c.client.GetMe()
			if err != nil {
				log.Error("Could not retrieve me info")
			} else if c.Session.Login == "" {
				c.Session.Login = c.me.PhoneNumber
			}

			_, err = c.client.GetChats(&client.GetChatsRequest{
				OffsetOrder: client.JsonInt64(math.MaxInt64),
				Limit:       chatsLimit,
			})
			if err != nil {
				log.Error("Could not retrieve chats")
			}

			gateway.SendPresence(c.xmpp, c.jid, gateway.SPStatus("Logged in "+c.Session.Login))

			return
		}
	}
}

func (c *Client) forceClose() {
	//c.listener.Close()
	c.online = false
	c.authorizer = nil
}

// Online checks if the updates listener is alive
func (c *Client) Online() bool {
	return c.listener != nil && c.online //c.listener.IsActive()
}