From ec56fb6b286f4d7fa2e3d58e45b9a45aaa7a79c0 Mon Sep 17 00:00:00 2001 From: Sumner Evans Date: Wed, 17 Jul 2024 15:26:34 -0600 Subject: [PATCH] metadata: refactor getting chat info Signed-off-by: Sumner Evans --- pkg/connector/client.go | 250 +++++++++++++++++++------------------- pkg/connector/config.go | 4 + pkg/connector/telegram.go | 9 ++ 3 files changed, 140 insertions(+), 123 deletions(-) diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 30f97c5d..c416cb44 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -200,42 +200,42 @@ func (t *TelegramClient) Disconnect() { t.clientCancel() } -func (t *TelegramClient) GetChatInfo(ctx context.Context, portal *bridgev2.Portal) (*bridgev2.ChatInfo, error) { - // fmt.Printf("get chat info %+v\n", portal) - peerType, id, err := ids.ParsePortalID(portal.ID) +func (t *TelegramClient) updateUsersFromResponse(ctx context.Context, resp interface{ GetUsers() []tg.UserClass }) error { + // TODO table for the access hashes? + for _, user := range resp.GetUsers() { + user, ok := user.(*tg.User) + if !ok { + return fmt.Errorf("user is %T not *tg.User", user) + } + err := t.updateGhost(ctx, user.ID, user) + if err != nil { + return err + } + } + return nil +} + +func (t *TelegramClient) getDMChatInfo(ctx context.Context, userID int64) (*bridgev2.ChatInfo, error) { + chatInfo := bridgev2.ChatInfo{ + Type: ptr.Ptr(database.RoomTypeDM), + Members: &bridgev2.ChatMemberList{IsFull: true}, + } + // TODO need access hash here + users, err := t.client.API().UsersGetUsers(ctx, []tg.InputUserClass{&tg.InputUser{UserID: userID}}) if err != nil { return nil, err - } - chatInfo := bridgev2.ChatInfo{ - Type: ptr.Ptr(database.RoomTypeDM), - Members: &bridgev2.ChatMemberList{ - IsFull: true, - }, - } - - switch peerType { - case ids.PeerTypeUser: - users, err := t.client.API().UsersGetUsers(ctx, []tg.InputUserClass{&tg.InputUser{UserID: id}}) - if err != nil { - return nil, err - } - if len(users) == 0 { - return nil, fmt.Errorf("failed to get user info for user %d", id) - } - user, ok := users[0].(*tg.User) - if !ok { - return nil, fmt.Errorf("returned user is not *tg.User") - } - userInfo, err := t.getUserInfoFromTelegramUser(user) - if err != nil { - return nil, err - } - + } else if len(users) == 0 { + return nil, fmt.Errorf("failed to get user info for user %d", userID) + } else if userInfo, err := t.getUserInfoFromTelegramUser(users[0]); err != nil { + return nil, err + } else if err = t.updateGhostWithUserInfo(ctx, userID, userInfo); err != nil { + return nil, err + } else { chatInfo.Members.Members = []bridgev2.ChatMember{ { EventSender: bridgev2.EventSender{ - SenderLogin: ids.MakeUserLoginID(id), - Sender: ids.MakeUserID(id), + SenderLogin: ids.MakeUserLoginID(userID), + Sender: ids.MakeUserID(userID), }, UserInfo: userInfo, }, @@ -247,64 +247,102 @@ func (t *TelegramClient) GetChatInfo(ctx context.Context, portal *bridgev2.Porta }, }, } + } + return &chatInfo, nil +} + +func (t *TelegramClient) getGroupChatInfo(ctx context.Context, fullChat *tg.MessagesChatFull, chatID int64) (*bridgev2.ChatInfo, error) { + if err := t.updateUsersFromResponse(ctx, fullChat); err != nil { + return nil, err + } + + chatInfo := bridgev2.ChatInfo{ + Type: ptr.Ptr(database.RoomTypeGroupDM), // TODO Is this correct for channels? + Members: &bridgev2.ChatMemberList{IsFull: true}, + } + for _, c := range fullChat.GetChats() { + if c.GetID() == chatID { + switch chat := c.(type) { + case *tg.Chat: + chatInfo.Name = &chat.Title + case *tg.Channel: + chatInfo.Name = &chat.Title + } + break + } + } + + if ttl, ok := fullChat.FullChat.GetTTLPeriod(); ok { + chatInfo.Disappear = &database.DisappearingSetting{ + Type: database.DisappearingTypeAfterSend, + Timer: time.Duration(ttl) * time.Second, + } + } + + if about := fullChat.FullChat.GetAbout(); about != "" { + chatInfo.Topic = &about + } + + return &chatInfo, nil +} + +func (t *TelegramClient) avatarFromPhoto(ctx context.Context, photo tg.PhotoClass) *bridgev2.Avatar { + if photo == nil || photo.TypeID() != tg.PhotoTypeID { + return nil + } + return &bridgev2.Avatar{ + ID: ids.MakeAvatarID(photo.GetID()), + Get: func(ctx context.Context) (data []byte, err error) { + data, _, err = media.NewTransferer(t.client.API()).WithPhoto(photo).Download(ctx) + return + }, + } +} + +func (t *TelegramClient) GetChatInfo(ctx context.Context, portal *bridgev2.Portal) (*bridgev2.ChatInfo, error) { + // fmt.Printf("get chat info %+v\n", portal) + peerType, id, err := ids.ParsePortalID(portal.ID) + if err != nil { + return nil, err + } + + switch peerType { + case ids.PeerTypeUser: + return t.getDMChatInfo(ctx, id) case ids.PeerTypeChat: - chatInfo.Type = ptr.Ptr(database.RoomTypeGroupDM) fullChat, err := t.client.API().MessagesGetFullChat(ctx, id) if err != nil { return nil, err } - for _, c := range fullChat.GetChats() { - if c.GetID() == id { - chatInfo.Name = &c.(*tg.Chat).Title - break - } + chatInfo, err := t.getGroupChatInfo(ctx, fullChat, id) + if err != nil { + return nil, err } chatFull, ok := fullChat.FullChat.(*tg.ChatFull) if !ok { return nil, fmt.Errorf("full chat is %T not *tg.ChatFull", fullChat.FullChat) } + chatInfo.Avatar = t.avatarFromPhoto(ctx, chatFull.ChatPhoto) - if photo, ok := chatFull.GetChatPhoto(); ok { - chatInfo.Avatar = &bridgev2.Avatar{ - ID: ids.MakeAvatarID(photo.GetID()), - Get: func(ctx context.Context) (data []byte, err error) { - data, _, err = media.NewTransferer(t.client.API()).WithPhoto(photo).Download(ctx) - return + participants, ok := chatFull.Participants.(*tg.ChatParticipants) + if !ok { + return nil, fmt.Errorf("full chat is %T not *tg.ChatParticipants", chatFull.Participants) + } + if !t.main.Config.ShouldBridge(len(participants.Participants)) { + return nil, fmt.Errorf("too many participants (%d) in chat %d", len(participants.Participants), id) + } + for _, user := range participants.Participants { + chatInfo.Members.Members = append(chatInfo.Members.Members, bridgev2.ChatMember{ + EventSender: bridgev2.EventSender{ + IsFromMe: user.GetUserID() == t.telegramUserID, + SenderLogin: ids.MakeUserLoginID(user.GetUserID()), + Sender: ids.MakeUserID(user.GetUserID()), }, - } - } - - if ttl, ok := chatFull.GetTTLPeriod(); ok { - chatInfo.Disappear = &database.DisappearingSetting{ - Type: database.DisappearingTypeAfterSend, - Timer: time.Duration(ttl) * time.Second, - } - } - - if about := chatFull.GetAbout(); about != "" { - chatInfo.Topic = &about - } - - for _, user := range fullChat.GetUsers() { - if user, ok := user.(*tg.User); ok { - t.updateGhost(ctx, id, user) - } - } - - if participants, ok := chatFull.Participants.(*tg.ChatParticipants); ok { - for _, user := range participants.Participants { - chatInfo.Members.Members = append(chatInfo.Members.Members, bridgev2.ChatMember{ - EventSender: bridgev2.EventSender{ - IsFromMe: user.GetUserID() == t.telegramUserID, - SenderLogin: ids.MakeUserLoginID(user.GetUserID()), - Sender: ids.MakeUserID(user.GetUserID()), - }, - }) - } + }) } + return chatInfo, nil case ids.PeerTypeChannel: - chatInfo.Type = ptr.Ptr(database.RoomTypeGroupDM) // TODO is this correct? accessHash, found, err := t.ScopedStore.GetChannelAccessHash(ctx, t.telegramUserID, id) if err != nil { return nil, fmt.Errorf("failed to get channel access hash: %w", err) @@ -316,43 +354,17 @@ func (t *TelegramClient) GetChatInfo(ctx context.Context, portal *bridgev2.Porta if err != nil { return nil, err } - for _, c := range fullChat.Chats { - if c.GetID() == id { - switch chat := c.(type) { - case *tg.Chat: - chatInfo.Name = &chat.Title - case *tg.Channel: - chatInfo.Name = &chat.Title - } - break - } + + chatInfo, err := t.getGroupChatInfo(ctx, fullChat, id) + if err != nil { + return nil, err } channelFull, ok := fullChat.FullChat.(*tg.ChannelFull) if !ok { return nil, fmt.Errorf("full chat is %T not *tg.ChannelFull", fullChat.FullChat) } - - if photo := channelFull.GetChatPhoto(); photo.TypeID() == tg.PhotoTypeID { - chatInfo.Avatar = &bridgev2.Avatar{ - ID: ids.MakeAvatarID(photo.GetID()), - Get: func(ctx context.Context) (data []byte, err error) { - data, _, err = media.NewTransferer(t.client.API()).WithPhoto(photo).Download(ctx) - return - }, - } - } - - if ttl, ok := channelFull.GetTTLPeriod(); ok { - chatInfo.Disappear = &database.DisappearingSetting{ - Type: database.DisappearingTypeAfterSend, - Timer: time.Duration(ttl) * time.Second, - } - } - - if about := channelFull.GetAbout(); about != "" { - chatInfo.Topic = &about - } + chatInfo.Avatar = t.avatarFromPhoto(ctx, channelFull.ChatPhoto) // TODO save available reactions? // TODO save reactions limit? @@ -362,12 +374,6 @@ func (t *TelegramClient) GetChatInfo(ctx context.Context, portal *bridgev2.Porta // TODO when do we have to make a request to get the participants? - for _, user := range fullChat.GetUsers() { - if user, ok := user.(*tg.User); ok { - t.updateGhost(ctx, id, user) - } - } - if channelFull.CanViewParticipants && !channelFull.ParticipantsHidden { // TODO paginate p, err := t.client.API().ChannelsGetParticipants(ctx, &tg.ChannelsGetParticipantsRequest{ @@ -383,11 +389,6 @@ func (t *TelegramClient) GetChatInfo(ctx context.Context, portal *bridgev2.Porta return nil, fmt.Errorf("returned participants is %T not *tg.ChannelsChannelParticipants", p) } for _, user := range participants.GetUsers() { - user, ok := user.(*tg.User) - if !ok { - return nil, fmt.Errorf("participant is %T not *tg.User", user) - } - userInfo, err := t.getUserInfoFromTelegramUser(user) if err != nil { return nil, err @@ -402,11 +403,10 @@ func (t *TelegramClient) GetChatInfo(ctx context.Context, portal *bridgev2.Porta }) } } + return chatInfo, nil default: panic(fmt.Sprintf("unsupported peer type %s", peerType)) } - - return &chatInfo, nil } func (t *TelegramClient) GetUserInfo(ctx context.Context, ghost *bridgev2.Ghost) (*bridgev2.UserInfo, error) { @@ -424,14 +424,18 @@ func (t *TelegramClient) GetUserInfo(ctx context.Context, ghost *bridgev2.Ghost) if len(users) == 0 { return nil, fmt.Errorf("failed to get user info for user %d", id) } - if user, ok := users[0].(*tg.User); !ok { - return nil, fmt.Errorf("returned user is not *tg.User") - } else { - return t.getUserInfoFromTelegramUser(user) + userInfo, err := t.getUserInfoFromTelegramUser(users[0]) + if err != nil { + return nil, err } + return userInfo, t.updateGhostWithUserInfo(ctx, id, userInfo) } -func (t *TelegramClient) getUserInfoFromTelegramUser(user *tg.User) (*bridgev2.UserInfo, error) { +func (t *TelegramClient) getUserInfoFromTelegramUser(u tg.UserClass) (*bridgev2.UserInfo, error) { + user, ok := u.(*tg.User) + if !ok { + return nil, fmt.Errorf("user is %T not *tg.User", user) + } var identifiers []string for _, username := range user.Usernames { identifiers = append(identifiers, fmt.Sprintf("telegram:%s", username.Username)) diff --git a/pkg/connector/config.go b/pkg/connector/config.go index 52f0dad0..ad3e77bf 100644 --- a/pkg/connector/config.go +++ b/pkg/connector/config.go @@ -29,6 +29,10 @@ type TelegramConfig struct { MaxMemberCount int `yaml:"max_member_count"` } +func (c TelegramConfig) ShouldBridge(participantCount int) bool { + return c.MaxMemberCount < 0 || participantCount <= c.MaxMemberCount +} + //go:embed example-config.yaml var ExampleConfig string diff --git a/pkg/connector/telegram.go b/pkg/connector/telegram.go index a22d47c9..83064c3f 100644 --- a/pkg/connector/telegram.go +++ b/pkg/connector/telegram.go @@ -199,6 +199,15 @@ func (t *TelegramClient) updateGhost(ctx context.Context, userID int64, user *tg return nil } +func (t *TelegramClient) updateGhostWithUserInfo(ctx context.Context, userID int64, userInfo *bridgev2.UserInfo) error { + ghost, err := t.main.Bridge.GetGhostByID(ctx, ids.MakeUserID(userID)) + if err != nil { + return err + } + ghost.UpdateInfo(ctx, userInfo) + return nil +} + func (t *TelegramClient) onEntityUpdate(ctx context.Context, e tg.Entities) error { for userID, user := range e.Users { t.updateGhost(ctx, userID, user)