diff options
author | bodqhrohro <bodqhrohro@gmail.com> | 2019-11-29 03:51:41 +0300 |
---|---|---|
committer | bodqhrohro <bodqhrohro@gmail.com> | 2019-11-29 03:51:41 +0300 |
commit | dbe87fafa8fb3c38d6cb22ac335cc76b70b607a6 (patch) | |
tree | 784f16c7e27444c03b865e1f4c03aadac1f18bf8 /telegram | |
parent | bcf222b53db2199cb6c784ac8c5b7105d794b6c9 (diff) |
Handle updates of user status
Diffstat (limited to 'telegram')
-rw-r--r-- | telegram/client.go | 35 | ||||
-rw-r--r-- | telegram/connect.go | 11 | ||||
-rw-r--r-- | telegram/handlers.go | 38 | ||||
-rw-r--r-- | telegram/utils.go | 170 |
4 files changed, 232 insertions, 22 deletions
diff --git a/telegram/client.go b/telegram/client.go index 6a738af..e8643ce 100644 --- a/telegram/client.go +++ b/telegram/client.go @@ -1,10 +1,10 @@ package telegram import ( - "fmt" "github.com/pkg/errors" "path/filepath" "strconv" + "sync" "dev.narayana.im/narayana/telegabber/config" "dev.narayana.im/narayana/telegabber/persistence" @@ -23,6 +23,16 @@ var logConstants = map[string]int32{ ":all": 1023, } +type cacheStruct struct { + chats map[int64]*client.Chat + users map[int32]*client.User +} + +var cache = cacheStruct{ + chats: map[int64]*client.Chat{}, + users: map[int32]*client.User{}, +} + func stringToLogConstant(c string) int32 { level, ok := logConstants[c] if !ok { @@ -44,10 +54,14 @@ type Client struct { jid string Session *persistence.Session - ready chan bool + locks clientLocks online bool } +type clientLocks struct { + authorizationReady sync.WaitGroup +} + // NewClient instantiates a Telegram App func NewClient(conf config.TelegramConfig, jid string, component *xmpp.Component, session *persistence.Session) (*Client, error) { logVerbosity := client.WithLogVerbosity(&client.SetLogVerbosityLevelRequest{ @@ -88,21 +102,6 @@ func NewClient(conf config.TelegramConfig, jid string, component *xmpp.Component jid: jid, Session: session, logVerbosity: logVerbosity, - ready: make(chan bool), + locks: clientLocks{}, }, nil } - -func updateHandler(tdlibClient *client.Client) { - listener := tdlibClient.GetListener() - defer listener.Close() - - for update := range listener.Updates { - if update.GetClass() == client.ClassUpdate { - fmt.Printf("%#v\n", update) - } - } -} - -func (c *Client) ProcessOutgoingMessage(chatID int, text string, messageID int) { - // TODO -} diff --git a/telegram/connect.go b/telegram/connect.go index 4fe262c..f1b3dc5 100644 --- a/telegram/connect.go +++ b/telegram/connect.go @@ -100,6 +100,8 @@ func (c *Client) Connect() error { Password: make(chan string, 1), } + c.locks.authorizationReady.Add(1) + go c.interactor() c.authorizer.TdlibParameters <- c.parameters @@ -111,9 +113,9 @@ func (c *Client) Connect() error { c.client = tdlibClient c.online = true - c.ready <- true + c.locks.authorizationReady.Done() - go updateHandler(c.client) + go c.updateHandler() return nil } @@ -141,6 +143,7 @@ func (c *Client) interactor() { stateType := state.AuthorizationStateType() log.Infof("Telegram authorization state: %#v", stateType) + log.Debugf("%#v", state) switch stateType { case client.TypeAuthorizationStateWaitPhoneNumber: @@ -159,7 +162,7 @@ func (c *Client) interactor() { case client.TypeAuthorizationStateReady: var err error - <-c.ready + c.locks.authorizationReady.Wait() log.Warn("Authorization successful!") @@ -178,7 +181,7 @@ func (c *Client) interactor() { log.Error("Could not retrieve chats") } - gateway.SendPresence(c.xmpp, nil, c.jid, gateway.SPStatus("Logged in "+c.Session.Login)) + gateway.SendPresence(c.xmpp, c.jid, gateway.SPStatus("Logged in "+c.Session.Login)) return } diff --git a/telegram/handlers.go b/telegram/handlers.go new file mode 100644 index 0000000..1195831 --- /dev/null +++ b/telegram/handlers.go @@ -0,0 +1,38 @@ +package telegram + +import ( + log "github.com/sirupsen/logrus" + "github.com/zelenin/go-tdlib/client" +) + +func uhOh() { + log.Fatal("Update type mismatch") +} + +func (c *Client) updateHandler() { + listener := c.client.GetListener() + defer listener.Close() + + for update := range listener.Updates { + if update.GetClass() == client.ClassUpdate { + switch update.GetType() { + case client.TypeUpdateUser: + typedUpdate, ok := update.(*client.UpdateUser) + if !ok { + uhOh() + } + c.updateUser(typedUpdate) + default: + // log only handled types + continue + } + + log.Debugf("%#v", update) + } + } +} + +func (c *Client) updateUser(update *client.UpdateUser) { + cache.users[update.User.Id] = update.User + c.processStatusUpdate(update.User.Id, &update.User.Status) +} diff --git a/telegram/utils.go b/telegram/utils.go new file mode 100644 index 0000000..7fb78d9 --- /dev/null +++ b/telegram/utils.go @@ -0,0 +1,170 @@ +package telegram + +import ( + "crypto/sha1" + "github.com/pkg/errors" + "io" + "os" + "strconv" + "time" + + "dev.narayana.im/narayana/telegabber/xmpp/gateway" + + log "github.com/sirupsen/logrus" + "github.com/soheilhy/args" + "github.com/zelenin/go-tdlib/client" +) + +var errOffline = errors.New("TDlib instance is offline") + +// GetContactByUsername resolves username to user id retrieves user and chat information +func (c *Client) GetContactByUsername(username string) (*client.Chat, *client.User, error) { + if !c.online { + return nil, nil, errOffline + } + + chat, err := c.client.SearchPublicChat(&client.SearchPublicChatRequest{ + Username: username, + }) + + if err != nil { + return nil, nil, err + } + + return c.GetContactByID(int32(chat.Id), chat) +} + +// GetContactByID gets user and chat information from cache (or tries to retrieve it, if missing) +func (c *Client) GetContactByID(id int32, chat *client.Chat) (*client.Chat, *client.User, error) { + if !c.online { + return nil, nil, errOffline + } + + var user *client.User + var cacheChat *client.Chat + var ok bool + var err error + + user, ok = cache.users[id] + if !ok && id > 0 { + user, err = c.client.GetUser(&client.GetUserRequest{ + UserId: id, + }) + if err != nil { + return nil, nil, err + } + + cache.users[id] = user + } + + chatID := int64(id) + cacheChat, ok = cache.chats[chatID] + if !ok { + if chat == nil { + cacheChat, err = c.client.GetChat(&client.GetChatRequest{ + ChatId: chatID, + }) + if err != nil { + return nil, nil, err + } + + cache.chats[chatID] = cacheChat + } else { + cache.chats[chatID] = chat + } + } + if chat == nil { + chat = cacheChat + } + + return chat, user, nil +} + +func (c *Client) processStatusUpdate(chatID int32, status *client.UserStatus, args ...args.V) error { + if !c.online { + return nil + } + + log.WithFields(log.Fields{ + "chat_id": chatID, + }).Info("Status update for") + + chat, user, err := c.GetContactByID(chatID, nil) + if err != nil { + return err + } + + var photo string + if chat != nil && chat.Photo != nil { + path := chat.Photo.Small.Local.Path + file, err := os.Open(path) + if err == nil { + defer file.Close() + + hash := sha1.New() + _, err = io.Copy(hash, file) + if err == nil { + photo = string(hash.Sum(nil)) + } else { + log.Errorf("Error calculating hash: %v", path) + } + } else if path != "" { + log.Errorf("Photo does not exist: %v", path) + } + } + + if status == nil && user != nil { + status = &user.Status + } + + var show, textStatus string + if status == nil { + show = "chat" + if chat.Title != "" { + textStatus = chat.Title + } + } else { + switch (*status).UserStatusType() { + case client.TypeUserStatusOnline: + textStatus = "Online" + case client.TypeUserStatusRecently: + show, textStatus = "dnd", "Last seen recently" + case client.TypeUserStatusLastWeek: + show, textStatus = "unavailable", "Last seen last week" + case client.TypeUserStatusLastMonth: + show, textStatus = "unavailable", "Last seen last month" + case client.TypeUserStatusEmpty: + show, textStatus = "unavailable", "Last seen a long time ago" + case client.TypeUserStatusOffline: + offlineStatus, ok := (*status).(*client.UserStatusOffline) + if !ok { + log.Fatal("Status type changed before conversion!") + } + // this will stop working in 2038 O\ + elapsed := time.Now().Unix() - int64(offlineStatus.WasOnline) + if elapsed < 3600 { + show = "away" + } else { + show = "xa" + } + // TODO: timezone + textStatus = time.Unix(int64(offlineStatus.WasOnline), 0).Format("Last seen at 15:03 02/01/2006") + } + } + + gateway.SendPresence( + c.xmpp, + c.jid, + gateway.SPFrom(strconv.Itoa(int(chatID))), + gateway.SPShow(show), + gateway.SPStatus(textStatus), + gateway.SPPhoto(photo), + gateway.SPImmed(gateway.SPImmed.Get(args)), + ) + + return nil +} + +func (c *Client) ProcessOutgoingMessage(chatID int, text string, messageID int) { + // TODO +} |