diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 52c045df..59060543 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -34,6 +34,34 @@ type TelegramClient struct { var _ bridgev2.NetworkAPI = (*TelegramClient)(nil) +type UpdateDispatcher struct { + tg.UpdateDispatcher + EntityHandler func(context.Context, tg.Entities) error +} + +func (u UpdateDispatcher) Handle(ctx context.Context, updates tg.UpdatesClass) error { + var ( + e tg.Entities + ) + switch u := updates.(type) { + case *tg.Updates: + e.Users = u.MapUsers().NotEmptyToMap() + chats := u.MapChats() + e.Chats = chats.ChatToMap() + e.Channels = chats.ChannelToMap() + case *tg.UpdatesCombined: + e.Users = u.MapUsers().NotEmptyToMap() + chats := u.MapChats() + e.Chats = chats.ChatToMap() + e.Channels = chats.ChannelToMap() + } + if u.EntityHandler != nil { + u.EntityHandler(ctx, e) + } + + return u.UpdateDispatcher.Handle(ctx, updates) +} + func NewTelegramClient(ctx context.Context, tc *TelegramConnector, login *bridgev2.UserLogin) (*TelegramClient, error) { loginID, err := strconv.ParseInt(string(login.ID), 10, 64) if err != nil { @@ -52,9 +80,13 @@ func NewTelegramClient(ctx context.Context, tc *TelegramConnector, login *bridge loginID: loginID, userLogin: login, } - dispatcher := tg.NewUpdateDispatcher() + dispatcher := UpdateDispatcher{ + UpdateDispatcher: tg.NewUpdateDispatcher(), + EntityHandler: client.onEntityUpdate, + } dispatcher.OnNewMessage(client.onUpdateNewMessage) dispatcher.OnNewChannelMessage(client.onUpdateNewChannelMessage) + dispatcher.OnUserName(client.onUserName) store := tc.store.GetScopedStore(loginID) @@ -120,6 +152,7 @@ func connectTelegramClient(ctx context.Context, client *telegram.Client) (contex } func (t *TelegramClient) onUpdateNewMessage(ctx context.Context, e tg.Entities, update *tg.UpdateNewMessage) error { + fmt.Printf("onupdatenewmessage %+v\n", e) log := zerolog.Ctx(ctx) switch msg := update.GetMessage().(type) { case *tg.Message: @@ -241,6 +274,34 @@ func (t *TelegramClient) onUpdateNewChannelMessage(ctx context.Context, e tg.Ent return nil } +func (t *TelegramClient) onUserName(ctx context.Context, e tg.Entities, update *tg.UpdateUserName) error { + ghost, err := t.main.Bridge.GetGhostByID(ctx, ids.MakeUserID(update.UserID)) + if err != nil { + return err + } + + name := util.FormatFullName(update.FirstName, update.LastName) + + // TODO update identifiers? + ghost.UpdateInfo(ctx, &bridgev2.UserInfo{Name: &name}) + return nil +} + +func (t *TelegramClient) onEntityUpdate(ctx context.Context, e tg.Entities) error { + for userID, user := range e.Users { + ghost, err := t.main.Bridge.GetGhostByID(ctx, ids.MakeUserID(userID)) + if err != nil { + return err + } + userInfo, err := t.getUserInfoFromTelegramUser(ctx, user) + if err != nil { + return err + } + ghost.UpdateInfo(ctx, userInfo) + } + return nil +} + func (t *TelegramClient) Connect(ctx context.Context) (err error) { t.clientCancel, err = connectTelegramClient(ctx, t.client) return @@ -336,26 +397,44 @@ func (t *TelegramClient) GetUserInfo(ctx context.Context, ghost *bridgev2.Ghost) if user, ok := users[0].(*tg.User); !ok { return nil, fmt.Errorf("returned user is not *tg.User") } else { - var identifiers []string - - if username, ok := user.GetUsername(); ok { - identifiers = append(identifiers, fmt.Sprintf("telegram:%s", username)) - } - if phone, ok := user.GetPhone(); ok { - identifiers = append(identifiers, fmt.Sprintf("tel:+%s", strings.TrimPrefix(phone, "+"))) - } - - name := util.FormatFullName(user.FirstName, user.LastName) - return &bridgev2.UserInfo{ - IsBot: &user.Bot, - Name: &name, - // TODO - // Avatar *Avatar - Identifiers: identifiers, - }, nil + return t.getUserInfoFromTelegramUser(ctx, user) } } +func (t *TelegramClient) getUserInfoFromTelegramUser(ctx context.Context, user *tg.User) (*bridgev2.UserInfo, error) { + var identifiers []string + for _, username := range user.Usernames { + identifiers = append(identifiers, fmt.Sprintf("telegram:%s", username.Username)) + } + if phone, ok := user.GetPhone(); ok { + identifiers = append(identifiers, fmt.Sprintf("tel:+%s", strings.TrimPrefix(phone, "+"))) + } + + var avatar *bridgev2.Avatar + if p, ok := user.GetPhoto(); ok && p.TypeID() == tg.UserProfilePhotoTypeID { + photo := p.(*tg.UserProfilePhoto) + avatar = &bridgev2.Avatar{ + ID: ids.MakeAvatarID(photo.PhotoID), + Get: func(ctx context.Context) (data []byte, err error) { + data, _, err = download.DownloadPhotoFileLocation(ctx, t.client.API(), &tg.InputPeerPhotoFileLocation{ + Peer: &tg.InputPeerUser{UserID: user.ID}, + PhotoID: photo.PhotoID, + Big: true, + }) + return + }, + } + } + + name := util.FormatFullName(user.FirstName, user.LastName) + return &bridgev2.UserInfo{ + IsBot: &user.Bot, + Name: &name, + Avatar: avatar, + Identifiers: identifiers, + }, nil +} + func (t *TelegramClient) IsLoggedIn() bool { _, err := t.client.Self(context.TODO()) return err == nil diff --git a/pkg/connector/download/photo.go b/pkg/connector/download/photo.go index 775a7c3d..81181543 100644 --- a/pkg/connector/download/photo.go +++ b/pkg/connector/download/photo.go @@ -35,18 +35,10 @@ func GetLargestPhotoSize(sizes []tg.PhotoSizeClass) (largest tg.PhotoSizeClass) return } -func DownloadPhoto(ctx context.Context, client downloader.Client, photo *tg.Photo) (data []byte, mimeType string, err error) { - largest := GetLargestPhotoSize(photo.GetSizes()) - file := tg.InputPhotoFileLocation{ - ID: photo.GetID(), - AccessHash: photo.GetAccessHash(), - FileReference: photo.GetFileReference(), - ThumbSize: largest.GetType(), - } - +func DownloadPhotoFileLocation(ctx context.Context, client downloader.Client, file tg.InputFileLocationClass) (data []byte, mimeType string, err error) { // TODO convert to streaming? var buf bytes.Buffer - storageFileTypeClass, err := downloader.NewDownloader().Download(client, &file).Stream(ctx, &buf) + storageFileTypeClass, err := downloader.NewDownloader().Download(client, file).Stream(ctx, &buf) if err != nil { return nil, "", err } @@ -73,6 +65,15 @@ func DownloadPhoto(ctx context.Context, client downloader.Client, photo *tg.Phot return buf.Bytes(), mimeType, nil } +func DownloadPhoto(ctx context.Context, client downloader.Client, photo *tg.Photo) (data []byte, mimeType string, err error) { + return DownloadPhotoFileLocation(ctx, client, &tg.InputPhotoFileLocation{ + ID: photo.GetID(), + AccessHash: photo.GetAccessHash(), + FileReference: photo.GetFileReference(), + ThumbSize: GetLargestPhotoSize(photo.GetSizes()).GetType(), + }) +} + func DownloadPhotoMedia(ctx context.Context, client downloader.Client, media *tg.MessageMediaPhoto) (data []byte, mimeType string, err error) { p, ok := media.GetPhoto() if !ok {