aboutsummaryrefslogtreecommitdiff
path: root/telegram
diff options
context:
space:
mode:
authorbodqhrohro <bodqhrohro@gmail.com>2019-11-29 03:51:41 +0300
committerbodqhrohro <bodqhrohro@gmail.com>2019-11-29 03:51:41 +0300
commitdbe87fafa8fb3c38d6cb22ac335cc76b70b607a6 (patch)
tree784f16c7e27444c03b865e1f4c03aadac1f18bf8 /telegram
parentbcf222b53db2199cb6c784ac8c5b7105d794b6c9 (diff)
Handle updates of user status
Diffstat (limited to 'telegram')
-rw-r--r--telegram/client.go35
-rw-r--r--telegram/connect.go11
-rw-r--r--telegram/handlers.go38
-rw-r--r--telegram/utils.go170
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
+}