chore: refresh gramsrv public release

This commit is contained in:
A 2026-06-30 14:37:43 +08:00
parent 75cebe8dbf
commit 70b6820474
1274 changed files with 378751 additions and 59919 deletions

View file

@ -0,0 +1,113 @@
package rpc
import (
"context"
"github.com/gotd/td/proto"
"github.com/gotd/td/tg"
"go.uber.org/zap"
"telesrv/internal/domain"
)
// 私聊密聊已读回执与 typingP1。已读/ typing 都是定向投给【对端绑定设备】的 update。
// P1在线推送已读 durable 离线补偿见后续 encrypted_state_events。typing 是 transient。
// 设计见 docs/secret-chat-module.md §8。
// resolveSecretChatPeer 校验调用方是密聊参与者且 access_hash 匹配,返回密聊、对端 user、
// 对端绑定设备 auth_key。失败返回 CHAT_ID_INVALID。
func (r *Router) resolveSecretChatPeer(ctx context.Context, userID int64, peer tg.InputEncryptedChat) (domain.SecretChat, int64, int64, error) {
chat, ok, err := r.deps.SecretChats.GetSecretChat(ctx, peer.ChatID)
if err != nil {
return domain.SecretChat{}, 0, 0, internalErr()
}
if !ok || !chat.HasParticipant(userID) || chat.AccessHashFor(userID) != peer.AccessHash {
return domain.SecretChat{}, 0, 0, chatIDInvalidErr()
}
return chat, chat.PeerOf(userID), chat.PeerAuthKeyOf(userID), nil
}
// pushEncryptedPeerUpdate 把 update 定向投给对端绑定设备transient=true 走 best-effort
func (r *Router) pushEncryptedPeerUpdate(ctx context.Context, peerUserID, peerAuthKeyID int64, update tg.UpdateClass, transient bool, logMessage string) {
if peerUserID == 0 || peerAuthKeyID == 0 {
return
}
now := int(r.clock.Now().Unix())
upd := &tg.Updates{
Updates: []tg.UpdateClass{update},
Users: []tg.UserClass{},
Chats: []tg.ChatClass{},
Date: now,
Seq: 0,
}
if targeted, ok := r.deps.Sessions.(AuthKeyTargetedSessionBinder); ok {
devKey := deviceAuthKeyBytes(peerAuthKeyID)
if transient {
_, _ = targeted.PushToUserAuthKeyTransient(ctx, peerUserID, devKey, proto.MessageFromServer, upd, r.cfg.OutboundPushTimeout)
} else {
_, _ = targeted.PushToUserAuthKey(ctx, peerUserID, devKey, proto.MessageFromServer, upd)
}
return
}
if transient {
r.pushUserMessageTransient(ctx, peerUserID, logMessage, upd)
} else {
r.pushUserMessage(ctx, peerUserID, logMessage, upd)
}
}
func (r *Router) onMessagesReadEncryptedHistory(ctx context.Context, req *tg.MessagesReadEncryptedHistoryRequest) (bool, error) {
if req == nil {
return false, inputRequestInvalidErr()
}
if r.deps.SecretChats == nil {
return false, notImplementedErr()
}
userID, err := r.secretChatRequireUser(ctx)
if err != nil {
return false, err
}
if req.MaxDate <= 0 || req.MaxDate > int(r.clock.Now().Unix())+1 {
return false, maxDateInvalidErr()
}
chat, peerUserID, peerAuthKeyID, err := r.resolveSecretChatPeer(ctx, userID, req.Peer)
if err != nil {
return false, err
}
now := int(r.clock.Now().Unix())
// durable设备定向供离线对端 getDifference 补回已读回执。
if err := r.deps.SecretChats.RecordReadEvent(ctx, chat.ID, peerUserID, peerAuthKeyID, req.MaxDate, now); err != nil {
r.log.Debug("record read state event", zap.Error(err))
}
// 仅投对端绑定设备(发起方靠本地置 read_outbox
r.pushEncryptedPeerUpdate(ctx, peerUserID, peerAuthKeyID, &tg.UpdateEncryptedMessagesRead{
ChatID: chat.ID,
MaxDate: req.MaxDate,
Date: now,
}, false, "secret chat read")
return true, nil
}
func (r *Router) onMessagesSetEncryptedTyping(ctx context.Context, req *tg.MessagesSetEncryptedTypingRequest) (bool, error) {
if req == nil {
return false, inputRequestInvalidErr()
}
if r.deps.SecretChats == nil {
return false, notImplementedErr()
}
userID, err := r.secretChatRequireUser(ctx)
if err != nil {
return false, err
}
chat, peerUserID, peerAuthKeyID, err := r.resolveSecretChatPeer(ctx, userID, req.Peer)
if err != nil {
return false, err
}
// typing 是 transient停止 typingTyping=false无对应 update自动过期。
if req.Typing {
r.pushEncryptedPeerUpdate(ctx, peerUserID, peerAuthKeyID, &tg.UpdateEncryptedChatTyping{
ChatID: chat.ID,
}, true, "secret chat typing")
}
return true, nil
}