From 3b6af95976ea4906a51ed6f6ce9e2bae46b5e73e Mon Sep 17 00:00:00 2001 From: Sumner Evans Date: Tue, 3 Sep 2024 12:36:28 -0600 Subject: [PATCH] connector: support messages sent by a channel Signed-off-by: Sumner Evans Co-authored-by: Tulir Asokan --- pkg/connector/api.go | 2 +- pkg/connector/chatinfo.go | 9 +++++ pkg/connector/config.go | 1 + pkg/connector/media/transfer.go | 14 ++++++++ pkg/connector/telegram.go | 62 ++++++++++++++++++++++++++++----- 5 files changed, 78 insertions(+), 10 deletions(-) diff --git a/pkg/connector/api.go b/pkg/connector/api.go index bf2868cf..d23ee48b 100644 --- a/pkg/connector/api.go +++ b/pkg/connector/api.go @@ -45,7 +45,7 @@ func APICallWithUpdates[U hasUpdates](ctx context.Context, t *TelegramClient, fn for _, c := range resp.GetChats() { if channel, ok := c.(*tg.Channel); ok { - if err := t.ScopedStore.SetAccessHash(ctx, channel.ID, channel.AccessHash); err != nil { + if err := t.updateChannel(ctx, channel); err != nil { return *new(U), err } } diff --git a/pkg/connector/chatinfo.go b/pkg/connector/chatinfo.go index 2cc75f5c..15438c42 100644 --- a/pkg/connector/chatinfo.go +++ b/pkg/connector/chatinfo.go @@ -216,6 +216,15 @@ func (t *TelegramClient) GetChatInfo(ctx context.Context, portal *bridgev2.Porta // TODO save emojiset? chatInfo.Members.IsFull = false + if !portal.Metadata.(*PortalMetadata).IsSuperGroup { + // Add the channel user + chatInfo.Members.MemberMap[ids.MakeUserID(id)] = bridgev2.ChatMember{ + EventSender: bridgev2.EventSender{ + SenderLogin: ids.MakeUserLoginID(id), + Sender: ids.MakeUserID(id), + }, + } + } // Just return the current user as a member if we can't view the // participants or the max initial sync is 0. diff --git a/pkg/connector/config.go b/pkg/connector/config.go index 819f605b..b5c39dfc 100644 --- a/pkg/connector/config.go +++ b/pkg/connector/config.go @@ -137,6 +137,7 @@ func (tg *TelegramConnector) GetDBMetaTypes() database.MetaTypes { type GhostMetadata struct { IsPremium bool `json:"is_premium,omitempty"` IsBot bool `json:"is_bot,omitempty"` + IsChannel bool `json:"is_channel,omitempty"` } type PortalMetadata struct { diff --git a/pkg/connector/media/transfer.go b/pkg/connector/media/transfer.go index 4b50c5ee..114083d4 100644 --- a/pkg/connector/media/transfer.go +++ b/pkg/connector/media/transfer.go @@ -209,6 +209,20 @@ func (t *Transferer) WithUserPhoto(ctx context.Context, store *store.ScopedStore } } +// WithChannelPhoto transforms a [Transferer] to a [ReadyTransferer] by setting +// the given chat photo as the location that will be downloaded by the +// [ReadyTransferer]. +func (t *Transferer) WithChannelPhoto(channelID, accessHash, photoID int64) *ReadyTransferer { + return &ReadyTransferer{ + inner: t, + loc: &tg.InputPeerPhotoFileLocation{ + Peer: &tg.InputPeerChannel{ChannelID: channelID, AccessHash: accessHash}, + PhotoID: photoID, + Big: true, + }, + } +} + // Transfer downloads the media from Telegram and uploads it to Matrix. // // If the file is already in the database, the MXC URI will be reused. The diff --git a/pkg/connector/telegram.go b/pkg/connector/telegram.go index 7a48ad84..f768adcd 100644 --- a/pkg/connector/telegram.go +++ b/pkg/connector/telegram.go @@ -181,19 +181,25 @@ func (t *TelegramClient) getEventSender(msg interface { }) bridgev2.EventSender { if msg.GetOut() { return t.mySender() - } else if f, ok := msg.GetFromID(); ok && f.TypeID() == tg.PeerUserTypeID { - from := f.(*tg.PeerUser) + } + + peer, ok := msg.GetFromID() + if !ok { + peer = msg.GetPeerID() + } + switch from := peer.(type) { + case *tg.PeerUser: return bridgev2.EventSender{ SenderLogin: ids.MakeUserLoginID(from.UserID), Sender: ids.MakeUserID(from.UserID), } - } else if peer, ok := msg.GetPeerID().(*tg.PeerUser); ok { + case *tg.PeerChannel: return bridgev2.EventSender{ - SenderLogin: ids.MakeUserLoginID(peer.UserID), - Sender: ids.MakeUserID(peer.UserID), + Sender: ids.MakeUserID(from.ChannelID), } - } else { - panic(fmt.Sprintf("couldn't determine sender (from: %+v) (peer: %+v)", f, msg.GetPeerID())) + default: + fromID, _ := msg.GetFromID() + panic(fmt.Sprintf("couldn't determine sender (from: %+v) (peer: %+v)", fromID, msg.GetPeerID())) } } @@ -318,14 +324,52 @@ func (t *TelegramClient) updateGhost(ctx context.Context, userID int64, user *tg return userInfo, t.maybeUpdateRemoteProfile(ctx, ghost, user) } +func (t *TelegramClient) updateChannel(ctx context.Context, channel *tg.Channel) error { + if err := t.ScopedStore.SetAccessHash(ctx, channel.ID, channel.AccessHash); err != nil { + return err + } + + if !channel.Broadcast { + return nil + } + + // Update the channel ghost if this is a broadcast channel. + ghost, err := t.main.Bridge.GetGhostByID(ctx, ids.MakeUserID(channel.ID)) + if err != nil { + return err + } + + var avatar *bridgev2.Avatar + if photo, ok := channel.GetPhoto().(*tg.ChatPhoto); ok { + avatar = &bridgev2.Avatar{ + ID: ids.MakeAvatarID(photo.PhotoID), + Get: func(ctx context.Context) (data []byte, err error) { + data, _, err = media.NewTransferer(t.client.API()).WithChannelPhoto(channel.ID, channel.AccessHash, photo.PhotoID).Download(ctx) + return + }, + } + } + + ghost.UpdateInfo(ctx, &bridgev2.UserInfo{ + Name: &channel.Title, + Avatar: avatar, + ExtraUpdates: func(ctx context.Context, g *bridgev2.Ghost) bool { + updated := !g.Metadata.(*GhostMetadata).IsChannel + g.Metadata.(*GhostMetadata).IsChannel = true + return updated + }, + }) + return nil +} + func (t *TelegramClient) onEntityUpdate(ctx context.Context, e tg.Entities) error { for userID, user := range e.Users { if _, err := t.updateGhost(ctx, userID, user); err != nil { return err } } - for channelID, channel := range e.Channels { - if err := t.ScopedStore.SetAccessHash(ctx, channelID, channel.AccessHash); err != nil { + for _, channel := range e.Channels { + if err := t.updateChannel(ctx, channel); err != nil { return err } }