fix: sync birthday updates and system sticker sets

This commit is contained in:
A 2026-07-05 15:15:32 +08:00
parent c38dd73fdd
commit ddcf2d31ca
10 changed files with 431 additions and 35 deletions

View file

@ -1292,8 +1292,8 @@ func (r *Router) onAccountUpdateUsername(ctx context.Context, username string) (
}
// onAccountUpdateBirthday 持久化资料页生日account.updateBirthday。birthday 缺省即清除;
// 月/日/年非法返回 BIRTHDAY_INVALID。生日落在 userFull按隐私 PrivacyKeyBirthday 对外裁剪)
// 故只需失效本人 userFull 投影缓存,客户端重拉 getFullUser 即见最新值
// 月/日/年非法返回 BIRTHDAY_INVALID。生日落在 userFull按隐私 PrivacyKeyBirthday 对外裁剪)
// 写入后推 updateUser 信号给本人其它在线 session促使已加载 full profile 的客户端重拉
func (r *Router) onAccountUpdateBirthday(ctx context.Context, req *tg.AccountUpdateBirthdayRequest) (bool, error) {
userID, _, err := r.currentUserID(ctx)
if err != nil {
@ -1318,6 +1318,7 @@ func (r *Router) onAccountUpdateBirthday(ctx context.Context, req *tg.AccountUpd
return false, internalErr()
}
r.invalidateRPCProjectionForUser(u.ID)
r.pushSelfUserChangedUpdate(ctx, u)
return true, nil
}
@ -1509,6 +1510,17 @@ func (r *Router) pushUsernameUpdate(ctx context.Context, u domain.User) {
})
}
func (r *Router) pushSelfUserChangedUpdate(ctx context.Context, u domain.User) {
if u.ID == 0 {
return
}
r.pushUserUpdates(ctx, u.ID, &tg.Updates{
Updates: []tg.UpdateClass{&tg.UpdateUser{UserID: u.ID}},
Users: []tg.UserClass{r.tgSelfUser(u)},
Date: int(r.clock.Now().Unix()),
})
}
func tgAuthorization(a domain.Authorization, currentAuthKeyID [8]byte, now int) tg.Authorization {
created := int(a.CreatedAt.Unix())
if created == 0 {

View file

@ -0,0 +1,84 @@
package rpc
import (
"context"
"testing"
"github.com/gotd/td/clock"
"github.com/gotd/td/tg"
"go.uber.org/zap/zaptest"
appusers "telesrv/internal/app/users"
"telesrv/internal/domain"
"telesrv/internal/store/memory"
)
func TestAccountUpdateBirthdayPersistsFullUserAndPushesRefresh(t *testing.T) {
ctx := context.Background()
userStore := memory.NewUserStore()
owner, err := userStore.Create(ctx, domain.User{AccessHash: 11, Phone: "15550003301", FirstName: "Owner"})
if err != nil {
t.Fatalf("create owner: %v", err)
}
sessions := &captureSessions{}
router := New(Config{}, Deps{
Users: appusers.NewService(userStore),
Sessions: sessions,
}, zaptest.NewLogger(t), clock.System)
birthday := tg.Birthday{Day: 14, Month: 2}
birthday.SetYear(1990)
req := &tg.AccountUpdateBirthdayRequest{}
req.SetBirthday(birthday)
callCtx := WithSessionID(WithUserID(ctx, owner.ID), 4242)
ok, err := router.onAccountUpdateBirthday(callCtx, req)
if err != nil || !ok {
t.Fatalf("update birthday = ok %v err %v, want true/nil", ok, err)
}
saved, found, err := userStore.ByID(ctx, owner.ID)
if err != nil || !found {
t.Fatalf("load saved user found=%v err=%v", found, err)
}
if saved.Birthday != (domain.Birthday{Day: 14, Month: 2, Year: 1990}) {
t.Fatalf("saved birthday = %+v, want 14/2/1990", saved.Birthday)
}
full, err := router.onUsersGetFullUser(WithUserID(ctx, owner.ID), &tg.InputUserSelf{})
if err != nil {
t.Fatalf("get full user: %v", err)
}
gotBirthday, ok := full.FullUser.GetBirthday()
if !ok {
t.Fatal("full user missing birthday")
}
gotYear, gotYearOK := gotBirthday.GetYear()
if gotBirthday.Day != 14 || gotBirthday.Month != 2 || !gotYearOK || gotYear != 1990 {
t.Fatalf("full user birthday = %+v yearOK=%v, want 14/2/1990", gotBirthday, gotYearOK)
}
snap := sessions.snapshot()
if snap.userID != owner.ID || snap.sessionID != 4242 {
t.Fatalf("push target user/session = %d/%d, want %d/4242", snap.userID, snap.sessionID, owner.ID)
}
updates, ok := snap.message.(*tg.Updates)
if !ok {
t.Fatalf("pushed message = %T, want *tg.Updates", snap.message)
}
hasUserUpdate := false
for _, update := range updates.Updates {
if u, ok := update.(*tg.UpdateUser); ok && u.UserID == owner.ID {
hasUserUpdate = true
}
}
if !hasUserUpdate {
t.Fatalf("updates = %+v, want UpdateUser for self", updates.Updates)
}
if len(updates.Users) != 1 {
t.Fatalf("pushed users = %d, want 1 self user", len(updates.Users))
}
pushedUser, ok := updates.Users[0].(*tg.User)
if !ok || pushedUser.ID != owner.ID {
t.Fatalf("pushed user = %T %+v, want self user", updates.Users[0], updates.Users[0])
}
}

View file

@ -411,6 +411,14 @@ func tgInputStickerSetFromSystemKey(systemKey string) (tg.InputStickerSetClass,
return &tg.InputStickerSetAnimatedEmojiAnimations{}, true
case "emoji_generic_animations":
return &tg.InputStickerSetEmojiGenericAnimations{}, true
case domain.StickerSetSystemKeyEmojiDefaultStatuses:
return &tg.InputStickerSetEmojiDefaultStatuses{}, true
case domain.StickerSetSystemKeyEmojiDefaultTopicIcons:
return &tg.InputStickerSetEmojiDefaultTopicIcons{}, true
case domain.StickerSetSystemKeyPremiumGifts:
return &tg.InputStickerSetPremiumGifts{}, true
case domain.StickerSetSystemKeyTonGifts:
return &tg.InputStickerSetTonGifts{}, true
default:
if strings.HasPrefix(systemKey, "dice:") {
return &tg.InputStickerSetDice{Emoticon: strings.TrimPrefix(systemKey, "dice:")}, true
@ -705,6 +713,8 @@ func stickerSetRefFromInput(input tg.InputStickerSetClass) (domain.StickerSetRef
return domain.StickerSetRef{Kind: domain.StickerSetRefBySystem, SystemKey: domain.StickerSetSystemKeyEmojiDefaultTopicIcons}, true
case *tg.InputStickerSetPremiumGifts:
return domain.StickerSetRef{Kind: domain.StickerSetRefBySystem, SystemKey: domain.StickerSetSystemKeyPremiumGifts}, true
case *tg.InputStickerSetTonGifts:
return domain.StickerSetRef{Kind: domain.StickerSetRefBySystem, SystemKey: domain.StickerSetSystemKeyTonGifts}, true
case *tg.InputStickerSetDice:
return domain.StickerSetRef{Kind: domain.StickerSetRefBySystem, SystemKey: "dice:" + in.Emoticon}, true
default:

View file

@ -285,6 +285,7 @@ func TestStickerSetRefFromSystemInputs(t *testing.T) {
{"emoji channel default statuses", &tg.InputStickerSetEmojiChannelDefaultStatuses{}, domain.StickerSetSystemKeyEmojiDefaultStatuses},
{"emoji default topic icons", &tg.InputStickerSetEmojiDefaultTopicIcons{}, domain.StickerSetSystemKeyEmojiDefaultTopicIcons},
{"premium gifts", &tg.InputStickerSetPremiumGifts{}, domain.StickerSetSystemKeyPremiumGifts},
{"ton gifts", &tg.InputStickerSetTonGifts{}, domain.StickerSetSystemKeyTonGifts},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {