fix for muting an account
This commit is contained in:
parent
aeaf3f4596
commit
87cebec9bd
12 changed files with 503 additions and 65 deletions
|
|
@ -94,7 +94,22 @@ func (r *Router) onAccountUpdateNotifySettings(ctx context.Context, req *tg.Acco
|
|||
}
|
||||
r.notifySettings.Delete(userID)
|
||||
}
|
||||
// 推 updateNotifySettings 给本人其它在线设备(多设备静音同步)。
|
||||
// 推 updateNotifySettings 给本人其它在线设备(多设备静音同步)。NotifyScopePeer(静音/
|
||||
// 取消静音某一具体会话,最常见场景)走 durable outbox:对方另一台设备当时不在线也能
|
||||
// 在重连后经 getDifference 追上最新静音状态,而不是停留在旧设置继续弹通知直到重启
|
||||
// app 才刷新。其余全局作用域(NotifyUsers/Chats/Broadcasts)仍走旧的 best-effort 推送。
|
||||
if scope.Kind == domain.NotifyScopePeer && r.deps.Updates != nil {
|
||||
authKeyID, _ := AuthKeyIDFrom(ctx)
|
||||
sessionID, _ := SessionIDFrom(ctx)
|
||||
event, _, err := r.deps.Updates.RecordNotifySettings(ctx, authKeyID, userID, scope.Peer, scope.TopicID, settings, rawAuthKeyIDForOrigin(ctx), sessionID)
|
||||
if err != nil {
|
||||
return false, internalErr()
|
||||
}
|
||||
if sessionID != 0 {
|
||||
r.bookkeepAuxPtsForCurrentSession(ctx, event)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
r.pushUserUpdates(ctx, userID, &tg.Updates{
|
||||
Updates: []tg.UpdateClass{&tg.UpdateNotifySettings{
|
||||
Peer: tgNotifyPeer(scope),
|
||||
|
|
|
|||
|
|
@ -406,6 +406,15 @@ func tgOtherUpdateFromEvent(event domain.UpdateEvent) tg.UpdateClass {
|
|||
return nil
|
||||
}
|
||||
return &tg.UpdatePeerSettings{Peer: peer, Settings: tgPeerSettings(event.Settings)}
|
||||
case domain.UpdateEventNotifySettings:
|
||||
if event.Peer.Type != domain.PeerTypeCommunity && tgPeer(event.Peer) == nil {
|
||||
return nil
|
||||
}
|
||||
if event.NotifyPeerSettings == nil {
|
||||
return nil
|
||||
}
|
||||
scope := domain.NotifyScope{Kind: domain.NotifyScopePeer, Peer: event.Peer, TopicID: event.TopMsgID}
|
||||
return &tg.UpdateNotifySettings{Peer: tgNotifyPeer(scope), NotifySettings: *tgPeerNotifySettings(event.NotifyPeerSettings)}
|
||||
case domain.UpdateEventPeerStoryBlocked:
|
||||
peer := tgPeer(event.Peer)
|
||||
if peer == nil {
|
||||
|
|
|
|||
|
|
@ -585,6 +585,7 @@ type UpdatesService interface {
|
|||
RecordPinnedSavedDialogs(ctx context.Context, stateAuthKeyID [8]byte, userID int64, order []domain.Peer, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
|
||||
RecordDialogUnreadMark(ctx context.Context, stateAuthKeyID [8]byte, userID int64, peer domain.Peer, unread bool, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
|
||||
RecordPeerSettings(ctx context.Context, stateAuthKeyID [8]byte, userID int64, peer domain.Peer, settings domain.PeerSettings, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
|
||||
RecordNotifySettings(ctx context.Context, stateAuthKeyID [8]byte, userID int64, peer domain.Peer, topicID int, settings domain.PeerNotifySettings, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
|
||||
RecordPeerStoryBlocked(ctx context.Context, stateAuthKeyID [8]byte, userID int64, peer domain.Peer, blocked bool, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
|
||||
RecordDialogFilter(ctx context.Context, stateAuthKeyID [8]byte, userID int64, folderID int, folder *domain.DialogFolder, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
|
||||
RecordDialogFilterOrder(ctx context.Context, stateAuthKeyID [8]byte, userID int64, order []int, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
|
||||
|
|
|
|||
101
internal/rpc/notify_settings_cross_device_repro_test.go
Normal file
101
internal/rpc/notify_settings_cross_device_repro_test.go
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/iamxvbaba/td/clock"
|
||||
"github.com/iamxvbaba/td/tg"
|
||||
"go.uber.org/zap/zaptest"
|
||||
|
||||
appaccount "telesrv/internal/app/account"
|
||||
appupdates "telesrv/internal/app/updates"
|
||||
appusers "telesrv/internal/app/users"
|
||||
"telesrv/internal/domain"
|
||||
"telesrv/internal/store/memory"
|
||||
)
|
||||
|
||||
// TestNotifySettingsMuteFromOneDeviceReachesAnotherViaDifference reproduces the reported
|
||||
// symptom: muting a chat from one device (PC) does not apply on another device of the SAME
|
||||
// account (phone), even after a full app restart (which does updates.getDifference, not a
|
||||
// live push). This test drives the real durable pipeline end to end (real appupdates.Service +
|
||||
// appaccount.Service, in-memory stores) instead of the captureUpdates fake, to catch anything
|
||||
// the fake's simplistic recording hides.
|
||||
func TestNotifySettingsMuteFromOneDeviceReachesAnotherViaDifference(t *testing.T) {
|
||||
passwordStore := memory.NewPasswordStore()
|
||||
updateStateStore := memory.NewUpdateStateStore()
|
||||
updateEventStore := memory.NewUpdateEventStore()
|
||||
userStore := memory.NewUserStore()
|
||||
r := New(Config{}, Deps{
|
||||
Account: appaccount.NewService(passwordStore, appaccount.WithNotifySettings(passwordStore)),
|
||||
Updates: appupdates.NewService(updateStateStore, updateEventStore),
|
||||
Users: appusers.NewService(userStore),
|
||||
}, zaptest.NewLogger(t), clock.System)
|
||||
|
||||
peerUser, err := userStore.Create(context.Background(), domain.User{AccessHash: 44, Phone: "15550009001", FirstName: "Peer"})
|
||||
if err != nil {
|
||||
t.Fatalf("create peer user: %v", err)
|
||||
}
|
||||
ownerUser, err := userStore.Create(context.Background(), domain.User{AccessHash: 45, Phone: "15550009002", FirstName: "Owner"})
|
||||
if err != nil {
|
||||
t.Fatalf("create owner user: %v", err)
|
||||
}
|
||||
peerID := peerUser.ID
|
||||
owner := ownerUser.ID
|
||||
|
||||
// Phone is "at" pts=0 (fresh install / just opened), before the PC mutes anything.
|
||||
phoneCtx := WithAuthKeyID(WithUserID(context.Background(), owner), [8]byte{2, 2, 2})
|
||||
baseline, err := r.onUpdatesGetDifference(phoneCtx, &tg.UpdatesGetDifferenceRequest{Pts: 0})
|
||||
if err != nil {
|
||||
t.Fatalf("phone baseline getDifference: %v", err)
|
||||
}
|
||||
t.Logf("phone baseline = %#v", baseline)
|
||||
var baselinePts int
|
||||
switch d := baseline.(type) {
|
||||
case *tg.UpdatesDifference:
|
||||
baselinePts = d.State.Pts
|
||||
case *tg.UpdatesDifferenceEmpty:
|
||||
baselinePts = 0
|
||||
case *tg.UpdatesDifferenceSlice:
|
||||
baselinePts = d.IntermediateState.Pts
|
||||
default:
|
||||
t.Fatalf("unexpected baseline type %T", baseline)
|
||||
}
|
||||
|
||||
// PC (different session, same user) mutes a specific peer.
|
||||
pcCtx := WithSessionID(WithAuthKeyID(WithUserID(context.Background(), owner), [8]byte{1, 1, 1}), 77)
|
||||
in := tg.InputPeerNotifySettings{}
|
||||
in.SetMuteUntil(2000000000)
|
||||
peerInput := &tg.InputNotifyPeer{Peer: &tg.InputPeerUser{UserID: peerID}}
|
||||
if ok, err := r.onAccountUpdateNotifySettings(pcCtx, &tg.AccountUpdateNotifySettingsRequest{Peer: peerInput, Settings: in}); err != nil || !ok {
|
||||
t.Fatalf("PC mute = ok %v err %v", ok, err)
|
||||
}
|
||||
|
||||
// Phone reconnects / restarts and asks for what changed since its baseline.
|
||||
diff, err := r.onUpdatesGetDifference(phoneCtx, &tg.UpdatesGetDifferenceRequest{Pts: baselinePts})
|
||||
if err != nil {
|
||||
t.Fatalf("phone catch-up getDifference: %v", err)
|
||||
}
|
||||
t.Logf("phone catch-up diff = %#v", diff)
|
||||
d, ok := diff.(*tg.UpdatesDifference)
|
||||
if !ok {
|
||||
t.Fatalf("phone catch-up diff type = %T, want *tg.UpdatesDifference containing the mute", diff)
|
||||
}
|
||||
t.Logf("other updates = %#v", d.OtherUpdates)
|
||||
found := false
|
||||
for _, u := range d.OtherUpdates {
|
||||
if ns, ok := u.(*tg.UpdateNotifySettings); ok {
|
||||
t.Logf("found UpdateNotifySettings: %#v peer=%#v settings=%#v", ns, ns.Peer, ns.NotifySettings)
|
||||
if np, ok := ns.Peer.(*tg.NotifyPeer); ok {
|
||||
if pu, ok := np.Peer.(*tg.PeerUser); ok && pu.UserID == peerID {
|
||||
if mu, hasMu := ns.NotifySettings.GetMuteUntil(); hasMu && mu == 2000000000 {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("phone's getDifference does not contain the PC's mute of peer %d -- this is the reported bug", peerID)
|
||||
}
|
||||
}
|
||||
|
|
@ -259,6 +259,12 @@ func (s *captureUpdates) RecordPeerSettings(_ context.Context, authKeyID [8]byte
|
|||
return s.recordCapturedEvent(authKeyID, userID, domain.UpdateEvent{Type: domain.UpdateEventPeerSettings, Peer: peer, Settings: settings})
|
||||
}
|
||||
|
||||
func (s *captureUpdates) RecordNotifySettings(_ context.Context, authKeyID [8]byte, userID int64, peer domain.Peer, topicID int, settings domain.PeerNotifySettings, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error) {
|
||||
s.captureExclude(excludeAuthKeyID, excludeSessionID)
|
||||
sc := settings.Clone()
|
||||
return s.recordCapturedEvent(authKeyID, userID, domain.UpdateEvent{Type: domain.UpdateEventNotifySettings, Peer: peer, TopMsgID: topicID, NotifyPeerSettings: &sc})
|
||||
}
|
||||
|
||||
func (s *captureUpdates) RecordPeerStoryBlocked(_ context.Context, authKeyID [8]byte, userID int64, peer domain.Peer, blocked bool, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error) {
|
||||
s.captureExclude(excludeAuthKeyID, excludeSessionID)
|
||||
return s.recordCapturedEvent(authKeyID, userID, domain.UpdateEvent{Type: domain.UpdateEventPeerStoryBlocked, Peer: peer, Bool: blocked})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue