chatinfo: refactor processing group chat info
This commit is contained in:
+246
-245
@@ -122,91 +122,263 @@ func (t *TelegramClient) getDMChatInfo(ctx context.Context, userID int64) (*brid
|
|||||||
return &chatInfo, nil
|
return &chatInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TelegramClient) getGroupChatInfo(fullChat *tg.MessagesChatFull, chatID int64) (*bridgev2.ChatInfo, bool, error) {
|
func isBroadcastChannel(chat tg.ChatClass) bool {
|
||||||
var name *string
|
switch c := chat.(type) {
|
||||||
var isBroadcastChannel, isMegagroup, left, found bool
|
case *tg.Channel:
|
||||||
var participantsCount int
|
return c.Broadcast
|
||||||
for _, c := range fullChat.GetChats() {
|
default:
|
||||||
if c.GetID() == chatID {
|
return false
|
||||||
found = true
|
}
|
||||||
switch chat := c.(type) {
|
}
|
||||||
case *tg.Chat:
|
|
||||||
name = &chat.Title
|
|
||||||
left = chat.Left
|
|
||||||
case *tg.Channel:
|
|
||||||
name = &chat.Title
|
|
||||||
isBroadcastChannel = chat.Broadcast
|
|
||||||
isMegagroup = chat.Megagroup
|
|
||||||
left = chat.Left
|
|
||||||
|
|
||||||
if value, ok := chat.GetParticipantsCount(); ok {
|
type memberFetchMeta struct {
|
||||||
participantsCount = value
|
Input *tg.InputChannel
|
||||||
}
|
IsBroadcast bool
|
||||||
|
ParticipantsHidden bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TelegramClient) wrapChatInfo(rawChat tg.ChatClass) (*bridgev2.ChatInfo, *memberFetchMeta, error) {
|
||||||
|
info := bridgev2.ChatInfo{
|
||||||
|
Type: ptr.Ptr(database.RoomTypeDefault),
|
||||||
|
CanBackfill: true,
|
||||||
|
Members: &bridgev2.ChatMemberList{
|
||||||
|
ExcludeChangesFromTimeline: true,
|
||||||
|
MemberMap: bridgev2.ChatMemberMap{},
|
||||||
|
},
|
||||||
|
ExcludeChangesFromTimeline: true,
|
||||||
|
}
|
||||||
|
var isMegagroup, isBroadcast, left bool
|
||||||
|
var channelInput *tg.InputChannel
|
||||||
|
var avatarErr error
|
||||||
|
switch chat := rawChat.(type) {
|
||||||
|
case *tg.Chat:
|
||||||
|
info.Name = &chat.Title
|
||||||
|
info.Members.TotalMemberCount = chat.ParticipantsCount
|
||||||
|
info.Avatar, avatarErr = t.convertChatPhoto(chat.AsInputPeer(), chat.Photo)
|
||||||
|
info.Members.PowerLevels = t.getPowerLevelOverridesFromBannedRights(chat, chat.DefaultBannedRights)
|
||||||
|
left = chat.Left
|
||||||
|
case *tg.Channel:
|
||||||
|
channelInput = chat.AsInput()
|
||||||
|
info.Name = &chat.Title
|
||||||
|
info.Members.TotalMemberCount = chat.ParticipantsCount
|
||||||
|
isMegagroup = chat.Megagroup
|
||||||
|
isBroadcast = chat.Broadcast
|
||||||
|
info.Avatar, avatarErr = t.convertChatPhoto(chat.AsInputPeer(), chat.Photo)
|
||||||
|
info.Members.PowerLevels = t.getPowerLevelOverridesFromBannedRights(chat, chat.DefaultBannedRights)
|
||||||
|
left = chat.Left
|
||||||
|
if chat.Broadcast {
|
||||||
|
info.Members.MemberMap.Set(bridgev2.ChatMember{
|
||||||
|
EventSender: bridgev2.EventSender{Sender: ids.MakeChannelUserID(chat.GetID())},
|
||||||
|
PowerLevel: superadminPowerLevel,
|
||||||
|
})
|
||||||
|
} else if chat.Megagroup && !t.main.Config.ShouldBridge(chat.ParticipantsCount) {
|
||||||
|
// TODO change this to a better error whenever that is implemented in mautrix-go
|
||||||
|
return nil, nil, fmt.Errorf("too many participants (%d) in chat %d", chat.ParticipantsCount, chat.GetID())
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, nil, fmt.Errorf("unsupported chat type %T", rawChat)
|
||||||
|
}
|
||||||
|
if avatarErr != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to wrap chat avatar: %w", avatarErr)
|
||||||
|
}
|
||||||
|
if !left {
|
||||||
|
info.Members.MemberMap.Add(bridgev2.ChatMember{EventSender: t.mySender()})
|
||||||
|
}
|
||||||
|
info.ExtraUpdates = func(ctx context.Context, portal *bridgev2.Portal) bool {
|
||||||
|
meta := portal.Metadata.(*PortalMetadata)
|
||||||
|
_ = updatePortalLastSyncAt(ctx, portal)
|
||||||
|
changed := meta.SetIsSuperGroup(isMegagroup)
|
||||||
|
if info.Members.TotalMemberCount != 0 && meta.ParticipantsCount != info.Members.TotalMemberCount {
|
||||||
|
meta.ParticipantsCount = info.Members.TotalMemberCount
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
return changed
|
||||||
|
}
|
||||||
|
return &info, &memberFetchMeta{Input: channelInput, IsBroadcast: isBroadcast}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TelegramClient) getChannelParticipants(ctx context.Context, req *tg.ChannelsGetParticipantsRequest) (*tg.ChannelsChannelParticipants, error) {
|
||||||
|
return APICallWithUpdates(ctx, t, func() (*tg.ChannelsChannelParticipants, error) {
|
||||||
|
p, err := t.client.API().ChannelsGetParticipants(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
participants, _ := p.(*tg.ChannelsChannelParticipants)
|
||||||
|
return participants, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TelegramClient) fillChannelMembers(ctx context.Context, mfm *memberFetchMeta, info *bridgev2.ChatMemberList) error {
|
||||||
|
if mfm.Input == nil || mfm.ParticipantsHidden || (mfm.IsBroadcast && !t.main.Config.MemberList.SyncBroadcastChannels) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
memberSyncLimit := t.main.Config.MemberList.NormalizedMaxInitialSync()
|
||||||
|
|
||||||
|
if memberSyncLimit <= 200 {
|
||||||
|
participants, err := t.getChannelParticipants(ctx, &tg.ChannelsGetParticipantsRequest{
|
||||||
|
Channel: mfm.Input,
|
||||||
|
Filter: &tg.ChannelParticipantsRecent{},
|
||||||
|
Limit: memberSyncLimit,
|
||||||
|
})
|
||||||
|
if err != nil || participants == nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
info.IsFull = len(participants.Participants) < memberSyncLimit &&
|
||||||
|
len(participants.Participants) >= info.TotalMemberCount &&
|
||||||
|
info.TotalMemberCount > 0
|
||||||
|
for participant := range t.filterChannelParticipants(participants.Participants, memberSyncLimit) {
|
||||||
|
info.MemberMap.Set(participant)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
remaining := memberSyncLimit
|
||||||
|
var offset int
|
||||||
|
for remaining > 0 {
|
||||||
|
participants, err := t.getChannelParticipants(ctx, &tg.ChannelsGetParticipantsRequest{
|
||||||
|
Channel: mfm.Input,
|
||||||
|
Filter: &tg.ChannelParticipantsSearch{},
|
||||||
|
Limit: min(remaining, 200),
|
||||||
|
Offset: offset,
|
||||||
|
})
|
||||||
|
if err != nil || participants == nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
if len(participants.Participants) == 0 {
|
||||||
|
info.IsFull = len(info.MemberMap) >= info.TotalMemberCount &&
|
||||||
|
info.TotalMemberCount > 0
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
for participant := range t.filterChannelParticipants(participants.Participants, remaining) {
|
||||||
|
info.MemberMap.Set(participant)
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += len(participants.Participants)
|
||||||
|
remaining -= len(participants.Participants)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TelegramClient) fillUserLocalMeta(info *bridgev2.ChatInfo, dialog *tg.Dialog) {
|
||||||
|
info.UserLocal = &bridgev2.UserLocalPortalInfo{}
|
||||||
|
if mu, ok := dialog.NotifySettings.GetMuteUntil(); ok {
|
||||||
|
info.UserLocal.MutedUntil = ptr.Ptr(time.Unix(int64(mu), 0))
|
||||||
|
} else {
|
||||||
|
info.UserLocal.MutedUntil = &bridgev2.Unmuted
|
||||||
|
}
|
||||||
|
if dialog.Pinned {
|
||||||
|
info.UserLocal.Tag = ptr.Ptr(event.RoomTagFavourite)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TelegramClient) wrapFullChatInfo(fullChat *tg.MessagesChatFull) (*bridgev2.ChatInfo, *memberFetchMeta, error) {
|
||||||
|
var chat tg.ChatClass
|
||||||
|
for _, c := range fullChat.GetChats() {
|
||||||
|
if c.GetID() == fullChat.FullChat.GetID() {
|
||||||
|
chat = c
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
if chat == nil {
|
||||||
return nil, false, fmt.Errorf("chat ID %d not found in full chat", chatID)
|
return nil, nil, fmt.Errorf("chat ID %d not found in full chat", fullChat.FullChat.GetID())
|
||||||
}
|
}
|
||||||
|
|
||||||
chatInfo := bridgev2.ChatInfo{
|
info, mfm, err := t.wrapChatInfo(chat)
|
||||||
Name: name,
|
if err != nil {
|
||||||
Type: ptr.Ptr(database.RoomTypeDefault),
|
return nil, nil, err
|
||||||
Members: &bridgev2.ChatMemberList{
|
}
|
||||||
IsFull: true,
|
|
||||||
MemberMap: bridgev2.ChatMemberMap{},
|
|
||||||
|
|
||||||
ExcludeChangesFromTimeline: true,
|
var newAllowedReactions []string
|
||||||
},
|
if reactions, ok := fullChat.FullChat.GetAvailableReactions(); ok {
|
||||||
CanBackfill: true,
|
switch typedReactions := reactions.(type) {
|
||||||
ExcludeChangesFromTimeline: true,
|
case *tg.ChatReactionsAll:
|
||||||
ExtraUpdates: func(ctx context.Context, p *bridgev2.Portal) bool {
|
newAllowedReactions = nil
|
||||||
meta := p.Metadata.(*PortalMetadata)
|
case *tg.ChatReactionsNone:
|
||||||
_ = updatePortalLastSyncAt(ctx, p)
|
newAllowedReactions = []string{}
|
||||||
_ = meta.SetIsSuperGroup(isMegagroup)
|
case *tg.ChatReactionsSome:
|
||||||
meta.ParticipantsCount = participantsCount
|
newAllowedReactions = make([]string, 0, len(typedReactions.Reactions))
|
||||||
|
for _, react := range typedReactions.Reactions {
|
||||||
if reactions, ok := fullChat.FullChat.GetAvailableReactions(); ok {
|
emoji, ok := react.(*tg.ReactionEmoji)
|
||||||
switch typedReactions := reactions.(type) {
|
if ok {
|
||||||
case *tg.ChatReactionsAll:
|
newAllowedReactions = append(newAllowedReactions, emoji.Emoticon)
|
||||||
meta.AllowedReactions = nil
|
|
||||||
case *tg.ChatReactionsNone:
|
|
||||||
meta.AllowedReactions = []string{}
|
|
||||||
case *tg.ChatReactionsSome:
|
|
||||||
allowedReactions := make([]string, 0, len(typedReactions.Reactions))
|
|
||||||
for _, react := range typedReactions.Reactions {
|
|
||||||
emoji, ok := react.(*tg.ReactionEmoji)
|
|
||||||
if ok {
|
|
||||||
allowedReactions = append(allowedReactions, emoji.Emoticon)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
slices.Sort(allowedReactions)
|
|
||||||
if !slices.Equal(meta.AllowedReactions, allowedReactions) {
|
|
||||||
meta.AllowedReactions = allowedReactions
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
slices.Sort(newAllowedReactions)
|
||||||
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{
|
info.Disappear = &database.DisappearingSetting{
|
||||||
Type: event.DisappearingTypeAfterSend,
|
Type: event.DisappearingTypeAfterSend,
|
||||||
Timer: time.Duration(ttl) * time.Second,
|
Timer: time.Duration(ttl) * time.Second,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if about := fullChat.FullChat.GetAbout(); about != "" {
|
if about := fullChat.FullChat.GetAbout(); about != "" {
|
||||||
chatInfo.Topic = &about
|
info.Topic = &about
|
||||||
|
}
|
||||||
|
info.ExtraUpdates = bridgev2.MergeExtraUpdaters(
|
||||||
|
info.ExtraUpdates,
|
||||||
|
reactionUpdater(newAllowedReactions),
|
||||||
|
markFullSynced,
|
||||||
|
)
|
||||||
|
|
||||||
|
switch typedFullChat := fullChat.FullChat.(type) {
|
||||||
|
case *tg.ChatFull:
|
||||||
|
participants, _ := typedFullChat.GetParticipants().(*tg.ChatParticipants)
|
||||||
|
memberSyncLimit := t.main.Config.MemberList.NormalizedMaxInitialSync()
|
||||||
|
info.Members.IsFull = true
|
||||||
|
for i, user := range participants.GetParticipants() {
|
||||||
|
var powerLevel *int
|
||||||
|
switch user.(type) {
|
||||||
|
case *tg.ChatParticipantCreator:
|
||||||
|
powerLevel = creatorPowerLevel
|
||||||
|
case *tg.ChatParticipantAdmin:
|
||||||
|
powerLevel = modPowerLevel
|
||||||
|
default:
|
||||||
|
powerLevel = ptr.Ptr(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
info.Members.MemberMap.Set(bridgev2.ChatMember{
|
||||||
|
EventSender: t.senderForUserID(user.GetUserID()),
|
||||||
|
PowerLevel: powerLevel,
|
||||||
|
})
|
||||||
|
|
||||||
|
if i >= memberSyncLimit {
|
||||||
|
info.Members.IsFull = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *tg.ChannelFull:
|
||||||
|
mfm.ParticipantsHidden = !typedFullChat.CanViewParticipants || typedFullChat.ParticipantsHidden
|
||||||
}
|
}
|
||||||
|
|
||||||
return &chatInfo, isBroadcastChannel, nil
|
return info, mfm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func reactionUpdater(newAllowedReactions []string) bridgev2.ExtraUpdater[*bridgev2.Portal] {
|
||||||
|
return func(ctx context.Context, portal *bridgev2.Portal) bool {
|
||||||
|
meta := portal.Metadata.(*PortalMetadata)
|
||||||
|
if newAllowedReactions == nil {
|
||||||
|
if meta.AllowedReactions == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
meta.AllowedReactions = nil
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if meta.AllowedReactions == nil || !slices.Equal(newAllowedReactions, meta.AllowedReactions) {
|
||||||
|
meta.AllowedReactions = newAllowedReactions
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func markFullSynced(ctx context.Context, portal *bridgev2.Portal) bool {
|
||||||
|
meta := portal.Metadata.(*PortalMetadata)
|
||||||
|
if !meta.FullSynced {
|
||||||
|
meta.FullSynced = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TelegramClient) avatarFromPhoto(ctx context.Context, peerType ids.PeerType, peerID int64, photo tg.PhotoClass) *bridgev2.Avatar {
|
func (t *TelegramClient) avatarFromPhoto(ctx context.Context, peerType ids.PeerType, peerID int64, photo tg.PhotoClass) *bridgev2.Avatar {
|
||||||
@@ -265,14 +437,11 @@ func (t *TelegramClient) filterChannelParticipants(participants []tg.ChannelPart
|
|||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
// FIXME GetFullChat should be avoided. Using only bundled info should be preferred whenever possible
|
|
||||||
// (e.g. when syncing dialogs, only use the data in the dialog list, don't fetch each chat info separately).
|
|
||||||
peerType, id, err := ids.ParsePortalID(portal.ID)
|
peerType, id, err := ids.ParsePortalID(portal.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
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)
|
||||||
@@ -283,50 +452,8 @@ func (t *TelegramClient) GetChatInfo(ctx context.Context, portal *bridgev2.Porta
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
chatInfo, _, err := t.getGroupChatInfo(fullChat, id)
|
info, _, err := t.wrapFullChatInfo(fullChat)
|
||||||
if err != nil {
|
return info, err
|
||||||
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, peerType, id, chatFull.ChatPhoto)
|
|
||||||
chatInfo.Members.PowerLevels = t.getGroupChatPowerLevels(ctx, fullChat.GetChats()[0])
|
|
||||||
|
|
||||||
if chatFull.Participants.TypeID() == tg.ChatParticipantsForbiddenTypeID {
|
|
||||||
chatInfo.Members.IsFull = false
|
|
||||||
return chatInfo, nil
|
|
||||||
}
|
|
||||||
chatParticipants := chatFull.Participants.(*tg.ChatParticipants)
|
|
||||||
|
|
||||||
if !t.main.Config.ShouldBridge(len(chatParticipants.Participants)) {
|
|
||||||
// TODO change this to a better error whenever that is implemented in mautrix-go
|
|
||||||
return nil, fmt.Errorf("too many participants (%d) in chat %d", len(chatParticipants.Participants), id)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, user := range chatParticipants.GetParticipants() {
|
|
||||||
var powerLevel *int
|
|
||||||
switch user.(type) {
|
|
||||||
case *tg.ChatParticipantCreator:
|
|
||||||
powerLevel = creatorPowerLevel
|
|
||||||
case *tg.ChatParticipantAdmin:
|
|
||||||
powerLevel = modPowerLevel
|
|
||||||
default:
|
|
||||||
powerLevel = ptr.Ptr(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
chatInfo.Members.MemberMap.Set(bridgev2.ChatMember{
|
|
||||||
EventSender: t.senderForUserID(user.GetUserID()),
|
|
||||||
PowerLevel: powerLevel,
|
|
||||||
})
|
|
||||||
|
|
||||||
if len(chatInfo.Members.MemberMap) >= memberSyncLimit {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return chatInfo, nil
|
|
||||||
case ids.PeerTypeChannel:
|
case ids.PeerTypeChannel:
|
||||||
accessHash, err := t.ScopedStore.GetAccessHash(ctx, ids.PeerTypeChannel, id)
|
accessHash, err := t.ScopedStore.GetAccessHash(ctx, ids.PeerTypeChannel, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -339,115 +466,17 @@ func (t *TelegramClient) GetChatInfo(ctx context.Context, portal *bridgev2.Porta
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
info, mfm, err := t.wrapFullChatInfo(fullChat)
|
||||||
chatInfo, isBroadcastChannel, err := t.getGroupChatInfo(fullChat, id)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
err = t.fillChannelMembers(ctx, mfm, info.Members)
|
||||||
channelFull, ok := fullChat.FullChat.(*tg.ChannelFull)
|
if err != nil {
|
||||||
if !ok {
|
zerolog.Ctx(ctx).Err(err).Msg("Failed to get channel members")
|
||||||
return nil, fmt.Errorf("full chat is %T not *tg.ChannelFull", fullChat.FullChat)
|
|
||||||
}
|
}
|
||||||
|
return info, nil
|
||||||
if portal.Metadata.(*PortalMetadata).IsSuperGroup && !t.main.Config.ShouldBridge(channelFull.ParticipantsCount) {
|
|
||||||
// TODO change this to a better error whenever that is implemented in mautrix-go
|
|
||||||
return nil, fmt.Errorf("too many participants (%d) in chat %d", channelFull.ParticipantsCount, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
chatInfo.Avatar = t.avatarFromPhoto(ctx, peerType, id, channelFull.ChatPhoto)
|
|
||||||
|
|
||||||
// TODO save available reactions?
|
|
||||||
// TODO save reactions limit?
|
|
||||||
// TODO save emojiset?
|
|
||||||
|
|
||||||
chatInfo.Members.IsFull = false
|
|
||||||
chatInfo.Members.PowerLevels = t.getGroupChatPowerLevels(ctx, fullChat.GetChats()[0])
|
|
||||||
if !portal.Metadata.(*PortalMetadata).IsSuperGroup {
|
|
||||||
// Add the channel user
|
|
||||||
chatInfo.Members.MemberMap.Set(bridgev2.ChatMember{
|
|
||||||
EventSender: bridgev2.EventSender{Sender: ids.MakeChannelUserID(id)},
|
|
||||||
PowerLevel: superadminPowerLevel,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Just return the current user as a member if we can't view the
|
|
||||||
// participants or the max initial sync is 0.
|
|
||||||
if t.main.Config.MemberList.MaxInitialSync == 0 || !channelFull.CanViewParticipants || channelFull.ParticipantsHidden {
|
|
||||||
return chatInfo, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this is a broadcast channel and we're not syncing broadcast
|
|
||||||
// channels, just return the chat info without all of the participant
|
|
||||||
// info.
|
|
||||||
if isBroadcastChannel && !t.main.Config.MemberList.SyncBroadcastChannels {
|
|
||||||
return chatInfo, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if memberSyncLimit <= 200 {
|
|
||||||
participants, err := APICallWithUpdates(ctx, t, func() (*tg.ChannelsChannelParticipants, error) {
|
|
||||||
p, err := t.client.API().ChannelsGetParticipants(ctx, &tg.ChannelsGetParticipantsRequest{
|
|
||||||
Channel: inputChannel,
|
|
||||||
Filter: &tg.ChannelParticipantsRecent{},
|
|
||||||
Limit: memberSyncLimit,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
participants, ok := p.(*tg.ChannelsChannelParticipants)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("returned participants is %T not *tg.ChannelsChannelParticipants", p)
|
|
||||||
} else {
|
|
||||||
return participants, nil
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
chatInfo.Members.IsFull = len(participants.Participants) < memberSyncLimit
|
|
||||||
for participant := range t.filterChannelParticipants(participants.Participants, memberSyncLimit) {
|
|
||||||
chatInfo.Members.MemberMap.Set(participant)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
remaining := memberSyncLimit
|
|
||||||
var offset int
|
|
||||||
for remaining > 0 {
|
|
||||||
participants, err := APICallWithUpdates(ctx, t, func() (*tg.ChannelsChannelParticipants, error) {
|
|
||||||
p, err := t.client.API().ChannelsGetParticipants(ctx, &tg.ChannelsGetParticipantsRequest{
|
|
||||||
Channel: inputChannel,
|
|
||||||
Filter: &tg.ChannelParticipantsSearch{},
|
|
||||||
Limit: min(remaining, 200),
|
|
||||||
Offset: offset,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
participants, ok := p.(*tg.ChannelsChannelParticipants)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("returned participants is %T not *tg.ChannelsChannelParticipants", p)
|
|
||||||
} else {
|
|
||||||
return participants, nil
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(participants.Participants) == 0 {
|
|
||||||
chatInfo.Members.IsFull = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
for participant := range t.filterChannelParticipants(participants.Participants, remaining) {
|
|
||||||
chatInfo.Members.MemberMap.Set(participant)
|
|
||||||
}
|
|
||||||
|
|
||||||
offset += len(participants.Participants)
|
|
||||||
remaining -= len(participants.Participants)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return chatInfo, nil
|
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unsupported peer type %s", peerType))
|
return nil, fmt.Errorf("unsupported peer type %s", peerType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -469,34 +498,6 @@ func (t *TelegramClient) getDMPowerLevels(ghost *bridgev2.Ghost) *bridgev2.Power
|
|||||||
return &plo
|
return &plo
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TelegramClient) getGroupChatPowerLevels(ctx context.Context, entity tg.ChatClass) *bridgev2.PowerLevelOverrides {
|
|
||||||
log := zerolog.Ctx(ctx).With().
|
|
||||||
Str("action", "get_group_chat_power_levels").
|
|
||||||
Logger()
|
|
||||||
|
|
||||||
dbrAble, ok := entity.(interface {
|
|
||||||
GetDefaultBannedRights() (tg.ChatBannedRights, bool)
|
|
||||||
})
|
|
||||||
var dbr tg.ChatBannedRights
|
|
||||||
if ok {
|
|
||||||
dbr, ok = dbrAble.GetDefaultBannedRights()
|
|
||||||
if !ok {
|
|
||||||
dbr = tg.ChatBannedRights{
|
|
||||||
InviteUsers: true,
|
|
||||||
ChangeInfo: true,
|
|
||||||
PinMessages: true,
|
|
||||||
SendStickers: false,
|
|
||||||
SendMessages: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Error().
|
|
||||||
Type("entity_type", entity).
|
|
||||||
Msg("couldn't get default banned rights from entity, assuming you don't have any rights")
|
|
||||||
}
|
|
||||||
return t.getPowerLevelOverridesFromBannedRights(entity, dbr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TelegramClient) getPowerLevelOverridesFromBannedRights(entity tg.ChatClass, dbr tg.ChatBannedRights) *bridgev2.PowerLevelOverrides {
|
func (t *TelegramClient) getPowerLevelOverridesFromBannedRights(entity tg.ChatClass, dbr tg.ChatBannedRights) *bridgev2.PowerLevelOverrides {
|
||||||
var plo bridgev2.PowerLevelOverrides
|
var plo bridgev2.PowerLevelOverrides
|
||||||
plo.Ban = banUsersPowerLevel
|
plo.Ban = banUsersPowerLevel
|
||||||
|
|||||||
@@ -186,31 +186,12 @@ func (tc *TelegramConnector) Download(ctx context.Context, mediaID networkid.Med
|
|||||||
return nil, fmt.Errorf("failed to create user photo transferer: %w", err)
|
return nil, fmt.Errorf("failed to create user photo transferer: %w", err)
|
||||||
}
|
}
|
||||||
} else if info.PeerType == ids.PeerTypeChat {
|
} else if info.PeerType == ids.PeerTypeChat {
|
||||||
fullChat, err := APICallWithUpdates(ctx, client, func() (*tg.MessagesChatFull, error) {
|
readyTransferer = transferer.WithPeerPhoto(&tg.InputPeerChat{ChatID: info.PeerID}, info.ID)
|
||||||
return client.client.API().MessagesGetFullChat(ctx, info.PeerID)
|
} else if info.PeerType == ids.PeerTypeChannel {
|
||||||
})
|
readyTransferer, err = transferer.WithChannelPhoto(ctx, client.ScopedStore, info.PeerID, info.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
chatFull, ok := fullChat.FullChat.(*tg.ChatFull)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("full chat is %T not *tg.ChatFull", fullChat.FullChat)
|
|
||||||
} else if chatFull.ChatPhoto == nil {
|
|
||||||
// FIXME: these 2 are basically not found errors
|
|
||||||
return nil, fmt.Errorf("photo not found on chat")
|
|
||||||
} else if photoID := chatFull.ChatPhoto.GetID(); photoID != info.ID {
|
|
||||||
return nil, fmt.Errorf("photo id mismatch: %d != %d", photoID, info.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
readyTransferer = transferer.WithPhoto(chatFull.ChatPhoto)
|
|
||||||
} else if info.PeerType == ids.PeerTypeChannel {
|
|
||||||
accessHash, err := client.ScopedStore.GetAccessHash(ctx, ids.PeerTypeChannel, info.PeerID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get channel access hash: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
readyTransferer = transferer.WithChannelPhoto(info.PeerID, accessHash, info.ID)
|
|
||||||
} else if info.PeerType == ids.FakePeerTypeEmoji {
|
} else if info.PeerType == ids.FakePeerTypeEmoji {
|
||||||
customEmojiDocuments, err := client.client.API().MessagesGetCustomEmojiDocuments(ctx, []int64{info.ID})
|
customEmojiDocuments, err := client.client.API().MessagesGetCustomEmojiDocuments(ctx, []int64{info.ID})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ func (t *TelegramClient) HandleMatrixViewingChat(ctx context.Context, msg *bridg
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
meta := msg.Portal.Metadata.(*PortalMetadata)
|
meta := msg.Portal.Metadata.(*PortalMetadata)
|
||||||
if meta.LastSync.Add(24 * time.Hour).Before(time.Now()) {
|
if !meta.FullSynced || meta.LastSync.Add(24*time.Hour).Before(time.Now()) {
|
||||||
t.userLogin.QueueRemoteEvent(&simplevent.ChatResync{
|
t.userLogin.QueueRemoteEvent(&simplevent.ChatResync{
|
||||||
EventMeta: simplevent.EventMeta{
|
EventMeta: simplevent.EventMeta{
|
||||||
Type: bridgev2.RemoteEventChatResync,
|
Type: bridgev2.RemoteEventChatResync,
|
||||||
@@ -737,6 +737,17 @@ func (t *TelegramClient) HandleMatrixReadReceipt(ctx context.Context, msg *bridg
|
|||||||
reactionPollErr = t.pollForReactions(ctx, msg.Portal.PortalKey, inputPeer)
|
reactionPollErr = t.pollForReactions(ctx, msg.Portal.PortalKey, inputPeer)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
if peerType == ids.PeerTypeChannel && !msg.Portal.Metadata.(*PortalMetadata).FullSynced {
|
||||||
|
log.Debug().Msg("Scheduling chat resync on read receipt because channel has never got a full sync")
|
||||||
|
go t.userLogin.QueueRemoteEvent(&simplevent.ChatResync{
|
||||||
|
EventMeta: simplevent.EventMeta{
|
||||||
|
Type: bridgev2.RemoteEventChatResync,
|
||||||
|
PortalKey: msg.Portal.PortalKey,
|
||||||
|
},
|
||||||
|
GetChatInfoFunc: t.GetChatInfo,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
return errors.Join(readMentionsErr, readReactionsErr, readMessagesErr, reactionPollErr)
|
return errors.Join(readMentionsErr, readReactionsErr, readMessagesErr, reactionPollErr)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -798,6 +798,8 @@ func (t *TelegramClient) updateChannel(ctx context.Context, channel *tg.Channel)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO resync portal metadata?
|
||||||
|
|
||||||
if !channel.Broadcast {
|
if !channel.Broadcast {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -810,7 +812,7 @@ func (t *TelegramClient) updateChannel(ctx context.Context, channel *tg.Channel)
|
|||||||
|
|
||||||
var avatar *bridgev2.Avatar
|
var avatar *bridgev2.Avatar
|
||||||
if photo, ok := channel.GetPhoto().(*tg.ChatPhoto); ok {
|
if photo, ok := channel.GetPhoto().(*tg.ChatPhoto); ok {
|
||||||
avatar, err = t.convertChatPhoto(ctx, channel.ID, channel.AccessHash, photo)
|
avatar, err = t.convertChatPhoto(channel.AsInputPeer(), photo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -864,7 +866,10 @@ func (t *TelegramClient) onMessageEdit(ctx context.Context, update IGetMessage)
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
t.handleTelegramReactions(ctx, msg)
|
err := t.handleTelegramReactions(ctx, msg)
|
||||||
|
if err != nil {
|
||||||
|
zerolog.Ctx(ctx).Err(err).Msg("Failed to handle reactions on edited message")
|
||||||
|
}
|
||||||
|
|
||||||
portalKey := t.makePortalKeyFromPeer(msg.PeerID)
|
portalKey := t.makePortalKeyFromPeer(msg.PeerID)
|
||||||
portal, err := t.main.Bridge.GetPortalByKey(ctx, portalKey)
|
portal, err := t.main.Bridge.GetPortalByKey(ctx, portalKey)
|
||||||
|
|||||||
@@ -212,25 +212,29 @@ func (t *Transferer) WithUserPhoto(ctx context.Context, store *store.ScopedStore
|
|||||||
if accessHash, err := store.GetAccessHash(ctx, ids.PeerTypeUser, userID); err != nil {
|
if accessHash, err := store.GetAccessHash(ctx, ids.PeerTypeUser, userID); err != nil {
|
||||||
return nil, fmt.Errorf("failed to get user access hash for %d: %w", userID, err)
|
return nil, fmt.Errorf("failed to get user access hash for %d: %w", userID, err)
|
||||||
} else {
|
} else {
|
||||||
return &ReadyTransferer{
|
return t.WithPeerPhoto(&tg.InputPeerUser{UserID: userID, AccessHash: accessHash}, photoID), nil
|
||||||
inner: t,
|
|
||||||
loc: &tg.InputPeerPhotoFileLocation{
|
|
||||||
Peer: &tg.InputPeerUser{UserID: userID, AccessHash: accessHash},
|
|
||||||
PhotoID: photoID,
|
|
||||||
Big: true,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithChannelPhoto transforms a [Transferer] to a [ReadyTransferer] by setting
|
// WithChannelPhoto transforms a [Transferer] to a [ReadyTransferer] by setting
|
||||||
// the given chat photo as the location that will be downloaded by the
|
// the given channel's photo as the location that will be downloaded by the
|
||||||
// [ReadyTransferer].
|
// [ReadyTransferer].
|
||||||
func (t *Transferer) WithChannelPhoto(channelID, accessHash, photoID int64) *ReadyTransferer {
|
func (t *Transferer) WithChannelPhoto(ctx context.Context, store *store.ScopedStore, channelID int64, photoID int64) (*ReadyTransferer, error) {
|
||||||
|
if accessHash, err := store.GetAccessHash(ctx, ids.PeerTypeChannel, channelID); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get channel access hash for %d: %w", channelID, err)
|
||||||
|
} else {
|
||||||
|
return t.WithPeerPhoto(&tg.InputPeerChannel{ChannelID: channelID, AccessHash: accessHash}, photoID), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPeerPhoto transforms a [Transferer] to a [ReadyTransferer] by setting
|
||||||
|
// the given user, chat or channel photo as the location that will be downloaded by the
|
||||||
|
// [ReadyTransferer].
|
||||||
|
func (t *Transferer) WithPeerPhoto(peer tg.InputPeerClass, photoID int64) *ReadyTransferer {
|
||||||
return &ReadyTransferer{
|
return &ReadyTransferer{
|
||||||
inner: t,
|
inner: t,
|
||||||
loc: &tg.InputPeerPhotoFileLocation{
|
loc: &tg.InputPeerPhotoFileLocation{
|
||||||
Peer: &tg.InputPeerChannel{ChannelID: channelID, AccessHash: accessHash},
|
Peer: peer,
|
||||||
PhotoID: photoID,
|
PhotoID: photoID,
|
||||||
Big: true,
|
Big: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ type PortalMetadata struct {
|
|||||||
ReadUpTo int `json:"read_up_to,omitempty"`
|
ReadUpTo int `json:"read_up_to,omitempty"`
|
||||||
AllowedReactions []string `json:"allowed_reactions"`
|
AllowedReactions []string `json:"allowed_reactions"`
|
||||||
LastSync jsontime.Unix `json:"last_sync,omitempty"`
|
LastSync jsontime.Unix `json:"last_sync,omitempty"`
|
||||||
|
FullSynced bool `json:"full_synced,omitempty"`
|
||||||
ParticipantsCount int `json:"member_count,omitempty"`
|
ParticipantsCount int `json:"member_count,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-14
@@ -20,15 +20,12 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"go.mau.fi/util/ptr"
|
|
||||||
"maunium.net/go/mautrix/bridgev2"
|
"maunium.net/go/mautrix/bridgev2"
|
||||||
"maunium.net/go/mautrix/bridgev2/database"
|
"maunium.net/go/mautrix/bridgev2/database"
|
||||||
"maunium.net/go/mautrix/bridgev2/networkid"
|
"maunium.net/go/mautrix/bridgev2/networkid"
|
||||||
"maunium.net/go/mautrix/bridgev2/simplevent"
|
"maunium.net/go/mautrix/bridgev2/simplevent"
|
||||||
"maunium.net/go/mautrix/event"
|
|
||||||
|
|
||||||
"go.mau.fi/mautrix-telegram/pkg/connector/ids"
|
"go.mau.fi/mautrix-telegram/pkg/connector/ids"
|
||||||
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
||||||
@@ -97,10 +94,10 @@ func (t *TelegramClient) handleDialogs(ctx context.Context, dialogs tg.ModifiedM
|
|||||||
|
|
||||||
var created int
|
var created int
|
||||||
for _, d := range dialogs.GetDialogs() {
|
for _, d := range dialogs.GetDialogs() {
|
||||||
if d.TypeID() != tg.DialogTypeID {
|
dialog, ok := d.(*tg.Dialog)
|
||||||
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
dialog := d.(*tg.Dialog)
|
|
||||||
|
|
||||||
log := log.With().
|
log := log.With().
|
||||||
Stringer("peer", dialog.Peer).
|
Stringer("peer", dialog.Peer).
|
||||||
@@ -151,6 +148,7 @@ func (t *TelegramClient) handleDialogs(ctx context.Context, dialogs tg.ModifiedM
|
|||||||
Msg("Not syncing portal because chat type is unsupported")
|
Msg("Not syncing portal because chat type is unsupported")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
// Need to get full chat info to get the member list
|
||||||
chatInfo, err = t.GetChatInfo(ctx, portal)
|
chatInfo, err = t.GetChatInfo(ctx, portal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get chat info for %s: %w", portalKey, err)
|
return fmt.Errorf("failed to get chat info for %s: %w", portalKey, err)
|
||||||
@@ -169,10 +167,15 @@ func (t *TelegramClient) handleDialogs(ctx context.Context, dialogs tg.ModifiedM
|
|||||||
Msg("Not syncing portal because channel type is unsupported")
|
Msg("Not syncing portal because channel type is unsupported")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
chatInfo, err = t.GetChatInfo(ctx, portal)
|
var mfm *memberFetchMeta
|
||||||
|
chatInfo, mfm, err = t.wrapChatInfo(channel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get chat info for %s: %w", portalKey, err)
|
return fmt.Errorf("failed to get chat info for %s: %w", portalKey, err)
|
||||||
}
|
}
|
||||||
|
err = t.fillChannelMembers(ctx, mfm, chatInfo.Members)
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Msg("Failed to get channel members")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if portal == nil || portal.MXID == "" {
|
if portal == nil || portal.MXID == "" {
|
||||||
@@ -199,14 +202,7 @@ func (t *TelegramClient) handleDialogs(ctx context.Context, dialogs tg.ModifiedM
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if mu, ok := dialog.NotifySettings.GetMuteUntil(); ok {
|
t.fillUserLocalMeta(chatInfo, dialog)
|
||||||
chatInfo.UserLocal = &bridgev2.UserLocalPortalInfo{MutedUntil: ptr.Ptr(time.Unix(int64(mu), 0))}
|
|
||||||
} else {
|
|
||||||
chatInfo.UserLocal = &bridgev2.UserLocalPortalInfo{MutedUntil: &bridgev2.Unmuted}
|
|
||||||
}
|
|
||||||
if dialog.Pinned {
|
|
||||||
chatInfo.UserLocal.Tag = ptr.Ptr(event.RoomTagFavourite)
|
|
||||||
}
|
|
||||||
|
|
||||||
res := t.main.Bridge.QueueRemoteEvent(t.userLogin, &simplevent.ChatResync{
|
res := t.main.Bridge.QueueRemoteEvent(t.userLogin, &simplevent.ChatResync{
|
||||||
ChatInfo: chatInfo,
|
ChatInfo: chatInfo,
|
||||||
|
|||||||
@@ -809,15 +809,39 @@ func (c *TelegramClient) convertUserProfilePhoto(ctx context.Context, userID int
|
|||||||
return avatar, nil
|
return avatar, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TelegramClient) convertChatPhoto(ctx context.Context, channelID, accessHash int64, chatPhoto *tg.ChatPhoto) (*bridgev2.Avatar, error) {
|
func (c *TelegramClient) convertChatPhoto(chat tg.InputPeerClass, rawChatPhoto tg.ChatPhotoClass) (*bridgev2.Avatar, error) {
|
||||||
|
var chatPhoto *tg.ChatPhoto
|
||||||
|
switch typedChatPhoto := rawChatPhoto.(type) {
|
||||||
|
case *tg.ChatPhotoEmpty:
|
||||||
|
return &bridgev2.Avatar{Remove: true}, nil
|
||||||
|
case *tg.ChatPhoto:
|
||||||
|
chatPhoto = typedChatPhoto
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("not a chat photo: %T", rawChatPhoto)
|
||||||
|
}
|
||||||
avatar := &bridgev2.Avatar{
|
avatar := &bridgev2.Avatar{
|
||||||
ID: ids.MakeAvatarID(chatPhoto.PhotoID),
|
ID: ids.MakeAvatarID(chatPhoto.PhotoID),
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.main.useDirectMedia {
|
if c.main.useDirectMedia {
|
||||||
|
var peerID int64
|
||||||
|
var peerType ids.PeerType
|
||||||
|
switch typedChat := chat.(type) {
|
||||||
|
case *tg.InputPeerChannel:
|
||||||
|
peerID = typedChat.ChannelID
|
||||||
|
peerType = ids.PeerTypeChannel
|
||||||
|
case *tg.InputPeerChat:
|
||||||
|
peerID = typedChat.ChatID
|
||||||
|
peerType = ids.PeerTypeChat
|
||||||
|
case *tg.InputPeerUser:
|
||||||
|
peerID = typedChat.UserID
|
||||||
|
peerType = ids.PeerTypeUser
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported chat type for chat photo: %T", chat)
|
||||||
|
}
|
||||||
mediaID, err := ids.DirectMediaInfo{
|
mediaID, err := ids.DirectMediaInfo{
|
||||||
PeerType: ids.PeerTypeChannel,
|
PeerType: peerType,
|
||||||
PeerID: channelID,
|
PeerID: peerID,
|
||||||
UserID: c.telegramUserID,
|
UserID: c.telegramUserID,
|
||||||
ID: chatPhoto.PhotoID,
|
ID: chatPhoto.PhotoID,
|
||||||
}.AsMediaID()
|
}.AsMediaID()
|
||||||
@@ -825,13 +849,14 @@ func (c *TelegramClient) convertChatPhoto(ctx context.Context, channelID, access
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if avatar.MXC, err = c.main.Bridge.Matrix.GenerateContentURI(ctx, mediaID); err != nil {
|
todoRemove := c.main.Bridge.BackgroundCtx // TODO remove context parameter from GenerateContentURI
|
||||||
|
if avatar.MXC, err = c.main.Bridge.Matrix.GenerateContentURI(todoRemove, mediaID); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
avatar.Hash = ids.HashMediaID(mediaID)
|
avatar.Hash = ids.HashMediaID(mediaID)
|
||||||
} else {
|
} else {
|
||||||
avatar.Get = func(ctx context.Context) (data []byte, err error) {
|
avatar.Get = func(ctx context.Context) (data []byte, err error) {
|
||||||
return media.NewTransferer(c.client.API()).WithChannelPhoto(channelID, accessHash, chatPhoto.PhotoID).DownloadBytes(ctx)
|
return media.NewTransferer(c.client.API()).WithPeerPhoto(chat, chatPhoto.PhotoID).DownloadBytes(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user