chatinfo: ensure own member is always added

This commit is contained in:
Tulir Asokan
2025-12-03 17:44:18 +02:00
parent 8bef95e237
commit b7e5078053
2 changed files with 74 additions and 50 deletions
+65 -50
View File
@@ -20,6 +20,7 @@ import (
"context" "context"
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"iter"
"slices" "slices"
"time" "time"
@@ -106,8 +107,8 @@ func (t *TelegramClient) getDMChatInfo(ctx context.Context, userID int64) (*brid
CanBackfill: true, CanBackfill: true,
ExtraUpdates: updatePortalLastSyncAt, ExtraUpdates: updatePortalLastSyncAt,
} }
chatInfo.Members.MemberMap[ids.MakeUserID(userID)] = bridgev2.ChatMember{EventSender: t.senderForUserID(userID)} chatInfo.Members.MemberMap.Add(bridgev2.ChatMember{EventSender: t.mySender()})
chatInfo.Members.MemberMap[t.userID] = bridgev2.ChatMember{EventSender: t.mySender()} chatInfo.Members.MemberMap.Add(bridgev2.ChatMember{EventSender: t.senderForUserID(userID)})
if userID == t.telegramUserID { if userID == t.telegramUserID {
chatInfo.Avatar = &bridgev2.Avatar{ chatInfo.Avatar = &bridgev2.Avatar{
ID: networkid.AvatarID(t.main.Config.SavedMessagesAvatar), ID: networkid.AvatarID(t.main.Config.SavedMessagesAvatar),
@@ -123,17 +124,20 @@ func (t *TelegramClient) getDMChatInfo(ctx context.Context, userID int64) (*brid
func (t *TelegramClient) getGroupChatInfo(fullChat *tg.MessagesChatFull, chatID int64) (*bridgev2.ChatInfo, bool, error) { func (t *TelegramClient) getGroupChatInfo(fullChat *tg.MessagesChatFull, chatID int64) (*bridgev2.ChatInfo, bool, error) {
var name *string var name *string
var isBroadcastChannel, isMegagroup bool var isBroadcastChannel, isMegagroup, left, found bool
var participantsCount int var participantsCount int
for _, c := range fullChat.GetChats() { for _, c := range fullChat.GetChats() {
if c.GetID() == chatID { if c.GetID() == chatID {
found = true
switch chat := c.(type) { switch chat := c.(type) {
case *tg.Chat: case *tg.Chat:
name = &chat.Title name = &chat.Title
left = chat.Left
case *tg.Channel: case *tg.Channel:
name = &chat.Title name = &chat.Title
isBroadcastChannel = chat.Broadcast isBroadcastChannel = chat.Broadcast
isMegagroup = chat.Megagroup isMegagroup = chat.Megagroup
left = chat.Left
if value, ok := chat.GetParticipantsCount(); ok { if value, ok := chat.GetParticipantsCount(); ok {
participantsCount = value participantsCount = value
@@ -142,13 +146,16 @@ func (t *TelegramClient) getGroupChatInfo(fullChat *tg.MessagesChatFull, chatID
break break
} }
} }
if !found {
return nil, false, fmt.Errorf("chat ID %d not found in full chat", chatID)
}
chatInfo := bridgev2.ChatInfo{ chatInfo := bridgev2.ChatInfo{
Name: name, Name: name,
Type: ptr.Ptr(database.RoomTypeDefault), Type: ptr.Ptr(database.RoomTypeDefault),
Members: &bridgev2.ChatMemberList{ Members: &bridgev2.ChatMemberList{
IsFull: true, IsFull: true,
MemberMap: map[networkid.UserID]bridgev2.ChatMember{}, MemberMap: bridgev2.ChatMemberMap{},
ExcludeChangesFromTimeline: true, ExcludeChangesFromTimeline: true,
}, },
@@ -184,6 +191,9 @@ func (t *TelegramClient) getGroupChatInfo(fullChat *tg.MessagesChatFull, chatID
return true return true
}, },
} }
if !left {
chatInfo.Members.MemberMap.Add(bridgev2.ChatMember{EventSender: t.mySender()})
}
if ttl, ok := fullChat.FullChat.GetTTLPeriod(); ok { if ttl, ok := fullChat.FullChat.GetTTLPeriod(); ok {
chatInfo.Disappear = &database.DisappearingSetting{ chatInfo.Disappear = &database.DisappearingSetting{
@@ -215,35 +225,43 @@ func (t *TelegramClient) avatarFromPhoto(ctx context.Context, peerType ids.PeerT
return avatar return avatar
} }
func (t *TelegramClient) filterChannelParticipants(participants []tg.ChannelParticipantClass, limit int) (members []bridgev2.ChatMember) { func (t *TelegramClient) filterChannelParticipants(participants []tg.ChannelParticipantClass, limit int) iter.Seq[bridgev2.ChatMember] {
for _, u := range participants { return func(yield func(bridgev2.ChatMember) bool) {
var userID int64 for i, u := range participants {
var powerLevel *int var member bridgev2.ChatMember
switch participant := u.(type) { switch participant := u.(type) {
case *tg.ChannelParticipant: case *tg.ChannelParticipant:
userID = participant.GetUserID() member.EventSender = t.senderForUserID(participant.GetUserID())
case *tg.ChannelParticipantSelf: case *tg.ChannelParticipantSelf:
userID = participant.GetUserID() member.EventSender = t.senderForUserID(participant.GetUserID())
case *tg.ChannelParticipantCreator: case *tg.ChannelParticipantCreator:
userID = participant.GetUserID() member.EventSender = t.senderForUserID(participant.GetUserID())
powerLevel = creatorPowerLevel member.PowerLevel = creatorPowerLevel
case *tg.ChannelParticipantAdmin: case *tg.ChannelParticipantAdmin:
userID = participant.GetUserID() member.EventSender = t.senderForUserID(participant.GetUserID())
powerLevel = adminRightsToPowerLevel(participant.AdminRights) member.PowerLevel = adminRightsToPowerLevel(participant.AdminRights)
default: case *tg.ChannelParticipantBanned:
continue member.Membership = event.MembershipBan
} member.PrevMembership = event.MembershipJoin
member.EventSender = t.getPeerSender(participant.GetPeer())
member.MemberSender = t.senderForUserID(participant.GetKickedBy())
case *tg.ChannelParticipantLeft:
member.Membership = event.MembershipLeave
member.PrevMembership = event.MembershipJoin
member.EventSender = t.getPeerSender(participant.GetPeer())
default:
// TODO warning log?
continue
}
if i >= limit && member.Membership == "" && !member.EventSender.IsFromMe {
continue
}
members = append(members, bridgev2.ChatMember{ if !yield(member) {
EventSender: t.senderForUserID(userID), return
PowerLevel: powerLevel, }
})
if len(members) >= limit {
break
} }
} }
return
} }
func (t *TelegramClient) GetChatInfo(ctx context.Context, portal *bridgev2.Portal) (*bridgev2.ChatInfo, error) { func (t *TelegramClient) GetChatInfo(ctx context.Context, portal *bridgev2.Portal) (*bridgev2.ChatInfo, error) {
@@ -254,6 +272,7 @@ func (t *TelegramClient) GetChatInfo(ctx context.Context, portal *bridgev2.Porta
return nil, err return nil, err
} }
memberSyncLimit := t.main.Config.MemberList.NormalizedMaxInitialSync()
switch peerType { switch peerType {
case ids.PeerTypeUser: case ids.PeerTypeUser:
return t.getDMChatInfo(ctx, id) return t.getDMChatInfo(ctx, id)
@@ -288,24 +307,22 @@ func (t *TelegramClient) GetChatInfo(ctx context.Context, portal *bridgev2.Porta
} }
for _, user := range chatParticipants.GetParticipants() { for _, user := range chatParticipants.GetParticipants() {
if user.TypeID() == tg.ChannelParticipantBannedTypeID {
continue
}
var powerLevel *int var powerLevel *int
switch user.(type) { switch user.(type) {
case *tg.ChatParticipantCreator: case *tg.ChatParticipantCreator:
powerLevel = creatorPowerLevel powerLevel = creatorPowerLevel
case *tg.ChatParticipantAdmin: case *tg.ChatParticipantAdmin:
powerLevel = modPowerLevel powerLevel = modPowerLevel
default:
powerLevel = ptr.Ptr(0)
} }
chatInfo.Members.MemberMap[ids.MakeUserID(user.GetUserID())] = bridgev2.ChatMember{ chatInfo.Members.MemberMap.Set(bridgev2.ChatMember{
EventSender: t.senderForUserID(user.GetUserID()), EventSender: t.senderForUserID(user.GetUserID()),
PowerLevel: powerLevel, PowerLevel: powerLevel,
} })
if len(chatInfo.Members.MemberMap) >= t.main.Config.MemberList.NormalizedMaxInitialSync() { if len(chatInfo.Members.MemberMap) >= memberSyncLimit {
break break
} }
} }
@@ -348,11 +365,10 @@ func (t *TelegramClient) GetChatInfo(ctx context.Context, portal *bridgev2.Porta
chatInfo.Members.PowerLevels = t.getGroupChatPowerLevels(ctx, fullChat.GetChats()[0]) chatInfo.Members.PowerLevels = t.getGroupChatPowerLevels(ctx, fullChat.GetChats()[0])
if !portal.Metadata.(*PortalMetadata).IsSuperGroup { if !portal.Metadata.(*PortalMetadata).IsSuperGroup {
// Add the channel user // Add the channel user
sender := ids.MakeChannelUserID(id) chatInfo.Members.MemberMap.Set(bridgev2.ChatMember{
chatInfo.Members.MemberMap[sender] = bridgev2.ChatMember{ EventSender: bridgev2.EventSender{Sender: ids.MakeChannelUserID(id)},
EventSender: bridgev2.EventSender{Sender: sender},
PowerLevel: superadminPowerLevel, PowerLevel: superadminPowerLevel,
} })
} }
// Just return the current user as a member if we can't view the // Just return the current user as a member if we can't view the
@@ -368,13 +384,12 @@ func (t *TelegramClient) GetChatInfo(ctx context.Context, portal *bridgev2.Porta
return chatInfo, nil return chatInfo, nil
} }
limit := t.main.Config.MemberList.NormalizedMaxInitialSync() if memberSyncLimit <= 200 {
if limit <= 200 {
participants, err := APICallWithUpdates(ctx, t, func() (*tg.ChannelsChannelParticipants, error) { participants, err := APICallWithUpdates(ctx, t, func() (*tg.ChannelsChannelParticipants, error) {
p, err := t.client.API().ChannelsGetParticipants(ctx, &tg.ChannelsGetParticipantsRequest{ p, err := t.client.API().ChannelsGetParticipants(ctx, &tg.ChannelsGetParticipantsRequest{
Channel: inputChannel, Channel: inputChannel,
Filter: &tg.ChannelParticipantsRecent{}, Filter: &tg.ChannelParticipantsRecent{},
Limit: limit, Limit: memberSyncLimit,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@@ -389,12 +404,12 @@ func (t *TelegramClient) GetChatInfo(ctx context.Context, portal *bridgev2.Porta
if err != nil { if err != nil {
return nil, err return nil, err
} }
chatInfo.Members.IsFull = len(participants.Participants) < limit chatInfo.Members.IsFull = len(participants.Participants) < memberSyncLimit
for _, participant := range t.filterChannelParticipants(participants.Participants, limit) { for participant := range t.filterChannelParticipants(participants.Participants, memberSyncLimit) {
chatInfo.Members.MemberMap[participant.Sender] = participant chatInfo.Members.MemberMap.Set(participant)
} }
} else { } else {
remaining := t.main.Config.MemberList.NormalizedMaxInitialSync() remaining := memberSyncLimit
var offset int var offset int
for remaining > 0 { for remaining > 0 {
participants, err := APICallWithUpdates(ctx, t, func() (*tg.ChannelsChannelParticipants, error) { participants, err := APICallWithUpdates(ctx, t, func() (*tg.ChannelsChannelParticipants, error) {
@@ -422,8 +437,8 @@ func (t *TelegramClient) GetChatInfo(ctx context.Context, portal *bridgev2.Porta
break break
} }
for _, participant := range t.filterChannelParticipants(participants.Participants, limit) { for participant := range t.filterChannelParticipants(participants.Participants, remaining) {
chatInfo.Members.MemberMap[participant.Sender] = participant chatInfo.Members.MemberMap.Set(participant)
} }
offset += len(participants.Participants) offset += len(participants.Participants)
+9
View File
@@ -27,10 +27,16 @@ import (
) )
func MakeUserID(userID int64) networkid.UserID { func MakeUserID(userID int64) networkid.UserID {
if userID == 0 {
return ""
}
return networkid.UserID(strconv.FormatInt(userID, 10)) return networkid.UserID(strconv.FormatInt(userID, 10))
} }
func MakeChannelUserID(channelID int64) networkid.UserID { func MakeChannelUserID(channelID int64) networkid.UserID {
if channelID == 0 {
return ""
}
return networkid.UserID("channel-" + strconv.FormatInt(channelID, 10)) return networkid.UserID("channel-" + strconv.FormatInt(channelID, 10))
} }
@@ -50,6 +56,9 @@ func ParseUserLoginID(userID networkid.UserLoginID) (int64, error) {
} }
func MakeUserLoginID(userID int64) networkid.UserLoginID { func MakeUserLoginID(userID int64) networkid.UserLoginID {
if userID == 0 {
return ""
}
return networkid.UserLoginID(strconv.FormatInt(userID, 10)) return networkid.UserLoginID(strconv.FormatInt(userID, 10))
} }