channels: give kicked/banned/promoted/transferred users a real qts so their client applies it
updateChannelParticipant carries the account's qts per the MTProto spec, but the server always sent Qts: 0, so real clients silently discarded it as a stale duplicate -- the banned/kicked user's channel never vanished locally and no correct "removed by admin" message showed, even though the update was delivered successfully at the transport layer. Add a durable per-device qts queue (channel_participant_event_queue) sharing its qts number space with the existing secret-chat queue (one qts sequence per device, per spec), and use it to stamp a correct, monotonically increasing qts on the update for every device of the affected user -- for channel bans/kicks, admin promotion/demotion, and ownership transfer. A device offline when it happened can now recover the event via updates.getDifference instead of missing it permanently.
This commit is contained in:
parent
97711c9d2e
commit
206bde18e0
16 changed files with 482 additions and 42 deletions
|
|
@ -118,8 +118,9 @@ func (r *Router) onUpdatesGetDifference(ctx context.Context, req *tg.UpdatesGetD
|
|||
break
|
||||
}
|
||||
}
|
||||
// 密聊设备级 qts 消息(独立于账号级 pts 事件):按当前设备 req.Qts 补回。
|
||||
encMsgs, newQts, encryptedPartial, err := r.encryptedDifference(ctx, req.Qts)
|
||||
// 密聊设备级 qts 消息 + channel 成员关系自通知(共用同一设备 qts 序列,独立于
|
||||
// 账号级 pts 事件):按当前设备 req.Qts 补回。
|
||||
encMsgs, participantUpdates, participantPeerIDs, participantChannelIDs, newQts, encryptedPartial, err := r.encryptedDifference(ctx, req.Qts)
|
||||
if err != nil {
|
||||
r.log.Error("load secret chat qts difference", zap.Error(err))
|
||||
return nil, internalErr()
|
||||
|
|
@ -130,6 +131,8 @@ func (r *Router) onUpdatesGetDifference(ctx context.Context, req *tg.UpdatesGetD
|
|||
r.log.Error("load secret chat state difference", zap.Error(err))
|
||||
return nil, internalErr()
|
||||
}
|
||||
stateUpdates = append(stateUpdates, participantUpdates...)
|
||||
statePeerUserIDs = append(statePeerUserIDs, participantPeerIDs...)
|
||||
// 账号 pts、设备 qts 和无序号状态事件任一被截断,都必须返回 differenceSlice。
|
||||
st.Partial = st.Partial || encryptedPartial || encryptedStatePartial
|
||||
if !st.Partial && len(st.Events) == 0 && len(st.ChannelNudges) == 0 && len(encMsgs) == 0 && len(stateUpdates) == 0 {
|
||||
|
|
@ -158,12 +161,40 @@ func (r *Router) onUpdatesGetDifference(ctx context.Context, req *tg.UpdatesGetD
|
|||
zap.Error(err))
|
||||
return nil, internalErr()
|
||||
}
|
||||
diff = r.injectChannelParticipantEventChats(ctx, userID, diff, participantChannelIDs)
|
||||
returnedCursor := st.State
|
||||
returnedCursor.Qts = newQts
|
||||
r.stageUpdatesBaselineAfterDelivery(ctx, userID, &returnedCursor, domain.UpdateStateCommitDeliveredOnly, stateEventIDs, true)
|
||||
return diff, nil
|
||||
}
|
||||
|
||||
// injectChannelParticipantEventChats resolves each channel referenced by a
|
||||
// recovered channel-participant self-notification through GetChannels (which
|
||||
// already projects Forbidden for a kicked/banned viewer, see
|
||||
// tgChannelChatForView) and appends the resulting chat objects to diff --
|
||||
// the bare updateChannel nudge alone carries no chat payload, and the
|
||||
// receiving client needs the full projection to apply the removal.
|
||||
func (r *Router) injectChannelParticipantEventChats(ctx context.Context, userID int64, diff tg.UpdatesDifferenceClass, channelIDs []int64) tg.UpdatesDifferenceClass {
|
||||
if r.deps.Channels == nil || len(channelIDs) == 0 {
|
||||
return diff
|
||||
}
|
||||
views, err := r.deps.Channels.GetChannels(ctx, userID, channelIDs)
|
||||
if err != nil || len(views) == 0 {
|
||||
return diff
|
||||
}
|
||||
chats := make([]tg.ChatClass, 0, len(views))
|
||||
for _, view := range views {
|
||||
chats = append(chats, tgChannelChatForView(userID, view))
|
||||
}
|
||||
switch v := diff.(type) {
|
||||
case *tg.UpdatesDifference:
|
||||
v.Chats = appendUniqueTGChats(v.Chats, chats...)
|
||||
case *tg.UpdatesDifferenceSlice:
|
||||
v.Chats = appendUniqueTGChats(v.Chats, chats...)
|
||||
}
|
||||
return diff
|
||||
}
|
||||
|
||||
func (r *Router) accountChannelDifferenceNudges(ctx context.Context, userID int64, sinceDate int) []domain.ChannelDifferenceNudge {
|
||||
if r.deps.Channels == nil || userID == 0 || sinceDate <= 0 {
|
||||
return nil
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue