diff --git a/pkg/connector/chatinfo.go b/pkg/connector/chatinfo.go index f631418a..215aac1b 100644 --- a/pkg/connector/chatinfo.go +++ b/pkg/connector/chatinfo.go @@ -2,6 +2,7 @@ package connector import ( "context" + "crypto/sha256" "fmt" "time" @@ -73,29 +74,28 @@ func adminRightsToPowerLevel(rights tg.ChatAdminRights) *int { return otherPowerLevel } -func (t *TelegramClient) getDMChatInfo(userID int64) (*bridgev2.ChatInfo, error) { - networkUserID := ids.MakeUserID(userID) +func (t *TelegramClient) getDMChatInfo(userID int64) *bridgev2.ChatInfo { chatInfo := bridgev2.ChatInfo{ Type: ptr.Ptr(database.RoomTypeDM), Members: &bridgev2.ChatMemberList{ - IsFull: true, - MemberMap: map[networkid.UserID]bridgev2.ChatMember{ - networkUserID: { - EventSender: bridgev2.EventSender{ - SenderLogin: ids.MakeUserLoginID(userID), - Sender: networkUserID, - }, - }, - t.userID: {EventSender: t.mySender()}, - }, + IsFull: true, + MemberMap: map[networkid.UserID]bridgev2.ChatMember{}, }, CanBackfill: true, } + chatInfo.Members.MemberMap[ids.MakeUserID(userID)] = bridgev2.ChatMember{EventSender: t.senderForUserID(userID)} + chatInfo.Members.MemberMap[t.userID] = bridgev2.ChatMember{EventSender: t.mySender()} if userID == t.telegramUserID { - // TODO also hardcode the avatar used by telegram? - chatInfo.Name = ptr.Ptr("Saved Messages") + chatInfo.Avatar = &bridgev2.Avatar{ + ID: networkid.AvatarID(t.main.Config.SavedMessagesAvatar), + Remove: len(t.main.Config.SavedMessagesAvatar) == 0, + MXC: t.main.Config.SavedMessagesAvatar, + Hash: sha256.Sum256([]byte(t.main.Config.SavedMessagesAvatar)), + } + chatInfo.Name = ptr.Ptr("Telegram Saved Messages") + chatInfo.Topic = ptr.Ptr("Your Telegram cloud storage chat") } - return &chatInfo, nil + return &chatInfo } func (t *TelegramClient) getGroupChatInfo(fullChat *tg.MessagesChatFull, chatID int64) (*bridgev2.ChatInfo, bool, error) { @@ -198,7 +198,7 @@ func (t *TelegramClient) GetChatInfo(ctx context.Context, portal *bridgev2.Porta switch peerType { case ids.PeerTypeUser: - return t.getDMChatInfo(id) + return t.getDMChatInfo(id), nil case ids.PeerTypeChat: fullChat, err := APICallWithUpdates(ctx, t, func() (*tg.MessagesChatFull, error) { return t.client.API().MessagesGetFullChat(ctx, id) diff --git a/pkg/connector/client.go b/pkg/connector/client.go index 79a73da5..34b93c85 100644 --- a/pkg/connector/client.go +++ b/pkg/connector/client.go @@ -465,6 +465,16 @@ func (t *TelegramClient) Connect(ctx context.Context) error { } else { t.userLogin.BridgeState.Send(status.BridgeState{StateEvent: status.StateConnected}) } + + // Fix the "Telegram Saved Messages" chat + t.main.Bridge.QueueRemoteEvent(t.userLogin, &simplevent.ChatResync{ + ChatInfo: t.getDMChatInfo(t.telegramUserID), + EventMeta: simplevent.EventMeta{ + Type: bridgev2.RemoteEventChatResync, + PortalKey: t.makePortalKeyFromID(ids.PeerTypeUser, t.telegramUserID), + CreatePortal: false, // Do not create the portal if it doesn't already exist + }, + }) return nil } diff --git a/pkg/connector/config.go b/pkg/connector/config.go index 54090ce4..a689c600 100644 --- a/pkg/connector/config.go +++ b/pkg/connector/config.go @@ -68,6 +68,8 @@ type TelegramConfig struct { } `yaml:"sync"` AlwaysCustomEmojiReaction bool `yaml:"always_custom_emoji_reaction"` + + SavedMessagesAvatar id.ContentURIString `yaml:"saved_message_avatar"` } func (c TelegramConfig) ShouldBridge(participantCount int) bool { @@ -104,6 +106,7 @@ func upgradeConfig(helper up.Helper) { helper.Copy(up.Int, "sync", "create_limit") helper.Copy(up.Bool, "sync", "direct_chats") helper.Copy(up.Bool, "always_custom_emoji_reaction") + helper.Copy(up.Str, "saved_message_avatar") } func (tg *TelegramConnector) GetConfig() (example string, data any, upgrader up.Upgrader) { diff --git a/pkg/connector/example-config.yaml b/pkg/connector/example-config.yaml index 0cf16f92..056af4c8 100644 --- a/pkg/connector/example-config.yaml +++ b/pkg/connector/example-config.yaml @@ -81,3 +81,6 @@ sync: # Telegram? By default, the bridge only uses custom emojis for unicode emojis # that aren't allowed in reactions. always_custom_emoji_reaction: false + +# The avatar to use for the Telegram Saved Messages chat +saved_message_avatar: mxc://nevarro.space/ZzzyOSDZfyGjKRRbyDPTweAA diff --git a/pkg/connector/sync.go b/pkg/connector/sync.go index d6fc6f53..95db1cc8 100644 --- a/pkg/connector/sync.go +++ b/pkg/connector/sync.go @@ -105,19 +105,8 @@ func (t *TelegramClient) handleDialogs(ctx context.Context, dialogs tg.ModifiedM continue } } - chatInfo := bridgev2.ChatInfo{ - CanBackfill: true, - UserLocal: &bridgev2.UserLocalPortalInfo{}, - Members: &bridgev2.ChatMemberList{ - MemberMap: map[networkid.UserID]bridgev2.ChatMember{ - t.userID: { - EventSender: t.mySender(), - Membership: event.MembershipJoin, - }, - }, - }, - } + var chatInfo *bridgev2.ChatInfo switch peer := dialog.Peer.(type) { case *tg.PeerUser: userID := ids.MakeUserID(peer.UserID) @@ -125,10 +114,7 @@ func (t *TelegramClient) handleDialogs(ctx context.Context, dialogs tg.ModifiedM log.Debug().Int64("user_id", peer.UserID).Msg("Not syncing portal because user is deleted") continue } - chatInfo.Members.MemberMap[userID] = bridgev2.ChatMember{ - EventSender: t.senderForUserID(peer.UserID), - Membership: event.MembershipJoin, - } + chatInfo = t.getDMChatInfo(peer.UserID) case *tg.PeerChat: chat := chats[peer.ChatID] if chat.TypeID() == tg.ChatForbiddenTypeID { @@ -143,8 +129,19 @@ func (t *TelegramClient) handleDialogs(ctx context.Context, dialogs tg.ModifiedM Msg("Not syncing portal because chat type is unsupported") continue } - chatInfo.Members.PowerLevels = t.getGroupChatPowerLevels(ctx, chat) - chatInfo.Name = &chat.(*tg.Chat).Title + chatInfo = &bridgev2.ChatInfo{ + CanBackfill: true, + Name: &chat.(*tg.Chat).Title, + Members: &bridgev2.ChatMemberList{ + PowerLevels: t.getGroupChatPowerLevels(ctx, chat), + MemberMap: map[networkid.UserID]bridgev2.ChatMember{ + t.userID: { + EventSender: t.mySender(), + Membership: event.MembershipJoin, + }, + }, + }, + } case *tg.PeerChannel: channel := chats[peer.ChannelID] if channel.TypeID() == tg.ChannelForbiddenTypeID { @@ -159,8 +156,19 @@ func (t *TelegramClient) handleDialogs(ctx context.Context, dialogs tg.ModifiedM Msg("Not syncing portal because channel type is unsupported") continue } - chatInfo.Name = &channel.(*tg.Channel).Title - chatInfo.Members.PowerLevels = t.getGroupChatPowerLevels(ctx, channel) + chatInfo = &bridgev2.ChatInfo{ + CanBackfill: true, + Name: &channel.(*tg.Channel).Title, + Members: &bridgev2.ChatMemberList{ + PowerLevels: t.getGroupChatPowerLevels(ctx, channel), + MemberMap: map[networkid.UserID]bridgev2.ChatMember{ + t.userID: { + EventSender: t.mySender(), + Membership: event.MembershipJoin, + }, + }, + }, + } if !portal.Metadata.(*PortalMetadata).IsSuperGroup { // Add the channel user sender := ids.MakeChannelUserID(peer.ChannelID) @@ -170,7 +178,6 @@ func (t *TelegramClient) handleDialogs(ctx context.Context, dialogs tg.ModifiedM PowerLevel: superadminPowerLevel, } } - } if portal == nil || portal.MXID == "" { @@ -191,16 +198,16 @@ func (t *TelegramClient) handleDialogs(ctx context.Context, dialogs tg.ModifiedM } if mu, ok := dialog.NotifySettings.GetMuteUntil(); ok { - chatInfo.UserLocal.MutedUntil = ptr.Ptr(time.Unix(int64(mu), 0)) + chatInfo.UserLocal = &bridgev2.UserLocalPortalInfo{MutedUntil: ptr.Ptr(time.Unix(int64(mu), 0))} } else { - chatInfo.UserLocal.MutedUntil = &bridgev2.Unmuted + chatInfo.UserLocal = &bridgev2.UserLocalPortalInfo{MutedUntil: &bridgev2.Unmuted} } if dialog.Pinned { chatInfo.UserLocal.Tag = ptr.Ptr(event.RoomTagFavourite) } t.main.Bridge.QueueRemoteEvent(t.userLogin, &simplevent.ChatResync{ - ChatInfo: &chatInfo, + ChatInfo: chatInfo, EventMeta: simplevent.EventMeta{ Type: bridgev2.RemoteEventChatResync, LogContext: func(c zerolog.Context) zerolog.Context { diff --git a/pkg/connector/telegram.go b/pkg/connector/telegram.go index f03662ce..31042cfd 100644 --- a/pkg/connector/telegram.go +++ b/pkg/connector/telegram.go @@ -506,10 +506,7 @@ func (t *TelegramClient) getEventSender(msg interface { func (t *TelegramClient) getPeerSender(peer tg.PeerClass) bridgev2.EventSender { switch from := peer.(type) { case *tg.PeerUser: - return bridgev2.EventSender{ - SenderLogin: ids.MakeUserLoginID(from.UserID), - Sender: ids.MakeUserID(from.UserID), - } + return t.senderForUserID(from.UserID) case *tg.PeerChannel: return bridgev2.EventSender{ Sender: ids.MakeChannelUserID(from.ChannelID),