refactor: sync sparse tlprofile runtime

This commit is contained in:
A 2026-07-16 21:36:32 +08:00
parent 5ecf4e912d
commit 25ab04a254
93 changed files with 2983 additions and 1615 deletions

View file

@ -7,8 +7,8 @@ practical community server.
The protocol stack is built on the published
[`github.com/iamxvbaba/td`](https://github.com/iamxvbaba/td) module
(`v1.0.0`), using a canonical Layer 228 schema with exact Layer 225-228
compatibility profiles.
(`v1.1.0`), using a canonical Layer 228 schema with sparse `tlprofile`
exact Layer 225-228 compatibility profiles.
If you are looking for a **Telegram server**, **MTProto server**,
**Telegram backend**, **Telegram clone server**, or **self-hosted
@ -45,7 +45,7 @@ codebase.
| Status | Feature | What works today |
|---|---|---|
| ✅ | MTProto server edge | TCP transport, RSA key exchange, auth keys, encrypted sessions, salts, ack/resend, bad messages, RPC dispatch, canonical Layer 228, and exact Layer 225-228 compatibility profiles. |
| ✅ | MTProto server edge | TCP transport, RSA key exchange, auth keys, encrypted sessions, salts, ack/resend, bad messages, RPC dispatch, canonical Layer 228, and sparse exact Layer 225-228 compatibility profiles. |
| ✅ | Login and accounts | Development login code, sign-in, sign-up, log-out, authorizations, account settings, SRP/password state, email/passkey-oriented paths. |
| ✅ | Users and contacts | User profiles, usernames, profile photos, contact import/search, blocked/privacy state, presence, and last-seen style status. |
| ✅ | Dialogs and sync | Dialog list, pinned dialogs, manual unread, folders/filters, drafts, read boundaries, durable updates, online fan-out, and offline difference recovery. |

View file

@ -6,8 +6,8 @@
协议栈基于已发布的
[`github.com/iamxvbaba/td`](https://github.com/iamxvbaba/td) module
`v1.0.0`),使用 canonical Layer 228 schema并提供 exact Layer 225-228
compatibility profiles。
`v1.1.0`),使用 canonical Layer 228 schema并通过 sparse `tlprofile`
提供 exact Layer 225-228 compatibility profiles。
如果你正在搜索 **Telegram server 实现**、**MTProto server 实现**、
**Telegram 后端**、**Telegram clone server**、**自建 Telegram-like 聊天服务器**
@ -42,7 +42,7 @@ https://github.com/user-attachments/assets/25e651dc-a022-4d60-8b9b-ca3e8bfe216c
| 状态 | 功能 | 当前已实现 |
|---|---|---|
| ✅ | MTProto server 接入层 | TCP transport、RSA key exchange、auth key、加密 session、salt、ack/resend、bad message、RPC dispatch、canonical Layer 228以及 exact Layer 225-228 compatibility profiles。 |
| ✅ | MTProto server 接入层 | TCP transport、RSA key exchange、auth key、加密 session、salt、ack/resend、bad message、RPC dispatch、canonical Layer 228以及 sparse exact Layer 225-228 compatibility profiles。 |
| ✅ | 登录与账号 | 开发验证码登录、sign-in、sign-up、log-out、授权设备、账号设置、SRP/password 状态、email/passkey 相关路径。 |
| ✅ | 用户与联系人 | 用户资料、username、头像、联系人导入/搜索、block/privacy 状态、presence、last seen。 |
| ✅ | 会话与同步 | dialog list、置顶、手动未读、folders/filters、草稿、read boundary、durable updates、在线 fan-out、离线 difference 恢复。 |

2
go.mod
View file

@ -8,7 +8,7 @@ require (
github.com/golang-migrate/migrate/v4 v4.19.1
github.com/gotd/ige v0.2.2
github.com/gotd/log/logzap v0.1.1
github.com/iamxvbaba/td v1.0.0
github.com/iamxvbaba/td v1.1.0
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa
github.com/jackc/pgx/v5 v5.9.2
github.com/pion/datachannel v1.6.2

4
go.sum
View file

@ -78,8 +78,8 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/iamxvbaba/td v1.0.0 h1:dgwQPxZCRWEZIDY9qNeZgIe6jvF7KAdI9NM1mxhnl6w=
github.com/iamxvbaba/td v1.0.0/go.mod h1:oG/fu7sqGC7NznoBD8f3fmTy9NFR42+DMNtdCPStX04=
github.com/iamxvbaba/td v1.1.0 h1:6Ddxi8sOuxOioGs3vyDGWlC6q53j4AQ2hFJ4AirJvFk=
github.com/iamxvbaba/td v1.1.0/go.mod h1:oG/fu7sqGC7NznoBD8f3fmTy9NFR42+DMNtdCPStX04=
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa h1:s+4MhCQ6YrzisK6hFJUX53drDT4UsSW3DEhKn0ifuHw=
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=

View file

@ -4,7 +4,7 @@ import (
"errors"
"github.com/iamxvbaba/td/bin"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
)
var ErrPrivateLayerRPCInvalid = errors.New("android private layer RPC is invalid")
@ -12,13 +12,13 @@ var ErrPrivateLayerRPCInvalid = errors.New("android private layer RPC is invalid
// AdaptPrivateLayerRPC invokes the provenance-locked static gotdgen overlay
// from the generated unknown-method view. Nested values decode with the exact
// connection profile and the canonical request is re-profiled by gotd core.
func AdaptPrivateLayerRPC(view tg.LayerRPCUnknownMethodView) (tg.LayerOutboundCall, bool, error) {
outbound, handled, err := view.AdaptClientRPCOverlay(tg.LayerClientRPCOverlayDrkloAndroid)
func AdaptPrivateLayerRPC(view tlprofile.UnknownMethodView) (tlprofile.OutboundCall, bool, error) {
outbound, handled, err := view.AdaptClientRPCOverlay(tlprofile.ClientRPCOverlayDrkloAndroid)
if err == nil && !handled {
outbound, handled, err = view.AdaptClientRPCOverlay(tg.LayerClientRPCOverlayDrkloAndroidTheme)
outbound, handled, err = view.AdaptClientRPCOverlay(tlprofile.ClientRPCOverlayDrkloAndroidTheme)
}
if err != nil {
return tg.LayerOutboundCall{}, handled, errors.Join(ErrPrivateLayerRPCInvalid, err)
return tlprofile.OutboundCall{}, handled, errors.Join(ErrPrivateLayerRPCInvalid, err)
}
return outbound, handled, nil
}
@ -26,10 +26,10 @@ func AdaptPrivateLayerRPC(view tg.LayerRPCUnknownMethodView) (tg.LayerOutboundCa
// UpgradePrivateLayerRPC is retained only for Router.Dispatch's legacy test
// seam. Production admission uses AdaptPrivateLayerRPC above so its decode
// shares the outer generated request budget.
func UpgradePrivateLayerRPC(profile tg.LayerProfile, in *bin.Buffer, limits tg.LayerDecodeLimits) (*bin.Buffer, bool, error) {
upgraded, handled, err := tg.AdaptClientRPCOverlayWithLimits(profile, tg.LayerClientRPCOverlayDrkloAndroid, in, limits)
func UpgradePrivateLayerRPC(profile tlprofile.Profile, in *bin.Buffer, limits tlprofile.Limits) (*bin.Buffer, bool, error) {
upgraded, handled, err := tlprofile.AdaptClientRPCOverlayWithLimits(profile, tlprofile.ClientRPCOverlayDrkloAndroid, in, limits)
if err == nil && !handled {
upgraded, handled, err = tg.AdaptClientRPCOverlayWithLimits(profile, tg.LayerClientRPCOverlayDrkloAndroidTheme, in, limits)
upgraded, handled, err = tlprofile.AdaptClientRPCOverlayWithLimits(profile, tlprofile.ClientRPCOverlayDrkloAndroidTheme, in, limits)
}
if err != nil {
return nil, handled, errors.Join(ErrPrivateLayerRPCInvalid, err)

View file

@ -5,7 +5,7 @@ import (
"testing"
"github.com/iamxvbaba/td/bin"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
)
func TestUpgradePrivateLayerRPCOnlyAcceptsAuditedAndroidConstructors(t *testing.T) {
@ -20,7 +20,7 @@ func TestUpgradePrivateLayerRPCOnlyAcceptsAuditedAndroidConstructors(t *testing.
private.PutID(0x7f3b18ea) // inputPeerEmpty
in := &bin.Buffer{Buf: private.Copy()}
upgraded, ok, err := UpgradePrivateLayerRPC(tg.LayerProfileCanonical, in, tg.LayerDecodeLimits{})
upgraded, ok, err := UpgradePrivateLayerRPC(tlprofile.ProfileCanonical, in, tlprofile.Limits{})
if err != nil || !ok {
t.Fatalf("upgrade private method = ok:%v err:%v", ok, err)
}
@ -33,13 +33,13 @@ func TestUpgradePrivateLayerRPCOnlyAcceptsAuditedAndroidConstructors(t *testing.
official := bin.Buffer{}
official.PutID(0xb921bd04) // arbitrary non-private/official constructor
if value, handled, err := UpgradePrivateLayerRPC(tg.LayerProfileCanonical, &official, tg.LayerDecodeLimits{}); value != nil || handled || err != nil {
if value, handled, err := UpgradePrivateLayerRPC(tlprofile.ProfileCanonical, &official, tlprofile.Limits{}); value != nil || handled || err != nil {
t.Fatalf("non-private method = value:%v handled:%v err:%v", value, handled, err)
}
}
func TestGeneratedPrivateLayerRPCOverlayHasAllAuditedMethods(t *testing.T) {
if got, want := tg.LayerClientRPCOverlayMethodCount(tg.LayerClientRPCOverlayDrkloAndroid), 15; got != want {
if got, want := tlprofile.ClientRPCOverlayMethodCount(tlprofile.ClientRPCOverlayDrkloAndroid), 15; got != want {
t.Fatalf("generated DrKLO method count = %d, want %d", got, want)
}
}
@ -47,7 +47,7 @@ func TestGeneratedPrivateLayerRPCOverlayHasAllAuditedMethods(t *testing.T) {
func TestUpgradePrivateLayerRPCRejectsMalformedBody(t *testing.T) {
malformed := bin.Buffer{}
malformed.PutID(0x41d41ade)
_, ok, err := UpgradePrivateLayerRPC(tg.LayerProfileCanonical, &malformed, tg.LayerDecodeLimits{})
_, ok, err := UpgradePrivateLayerRPC(tlprofile.ProfileCanonical, &malformed, tlprofile.Limits{})
if !ok || !errors.Is(err, ErrPrivateLayerRPCInvalid) {
t.Fatalf("malformed private method = ok:%v err:%v", ok, err)
}

View file

@ -12,7 +12,7 @@ import (
"github.com/iamxvbaba/td/bin"
"github.com/iamxvbaba/td/crypto"
"github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"github.com/iamxvbaba/td/transport"
)
@ -279,7 +279,7 @@ func (c *Conn) isPhysicalTransportCurrentOpen() bool {
// LayerProfile returns the exact TL profile currently selected for this
// connection. ok is false until admission or an inherited auth-key default
// supplies a supported generated profile.
func (c *Conn) LayerProfile() (profile tg.LayerProfile, ok bool) {
func (c *Conn) LayerProfile() (profile tlprofile.Profile, ok bool) {
state := c.LayerProfileState()
return state.Profile, state.Origin != LayerProfileUnknown
}
@ -288,7 +288,7 @@ func (c *Conn) LayerProfile() (profile tg.LayerProfile, ok bool) {
// admission. Repeating the same value is idempotent. A later well-formed
// invokeWithLayer may replace either an inherited default or older explicit
// evidence; already-admitted requests retain their own immutable profile.
func (c *Conn) FreezeLayerProfile(profile tg.LayerProfile) error {
func (c *Conn) FreezeLayerProfile(profile tlprofile.Profile) error {
_, err := c.setLayerProfile(profile, LayerProfileExplicit, true)
return err
}
@ -298,14 +298,14 @@ func (c *Conn) FreezeLayerProfile(profile tg.LayerProfile) error {
// msg_id carrying another Layer is a protocol conflict. Advancing the evidence
// cursor at an unchanged Layer does not rotate the outbound epoch because the
// wire profile itself did not change.
func (c *Conn) FreezeLayerProfileAt(profile tg.LayerProfile, msgID int64) (bool, error) {
func (c *Conn) FreezeLayerProfileAt(profile tlprofile.Profile, msgID int64) (bool, error) {
return c.freezeLayerProfileAt(profile, msgID)
}
// SeedLayerProfile restores explicit evidence previously proven for this exact
// logical session. It is kept as the compatible same-session restore API;
// auth-key-wide metadata must use SeedInheritedLayerProfile instead.
func (c *Conn) SeedLayerProfile(profile tg.LayerProfile) error {
func (c *Conn) SeedLayerProfile(profile tlprofile.Profile) error {
_, err := c.setLayerProfile(profile, LayerProfileExplicit, true)
return err
}
@ -313,7 +313,7 @@ func (c *Conn) SeedLayerProfile(profile tg.LayerProfile) error {
// SeedInheritedLayerProfile installs an auth-key-wide default only while the
// connection is still unknown. It never overwrites explicit evidence or an
// already selected inherited default; client protocol evidence owns correction.
func (c *Conn) SeedInheritedLayerProfile(profile tg.LayerProfile) error {
func (c *Conn) SeedInheritedLayerProfile(profile tlprofile.Profile) error {
_, err := c.setLayerProfile(profile, LayerProfileInherited, false)
return err
}

View file

@ -5,7 +5,7 @@ import (
"fmt"
"math"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"go.uber.org/zap"
)
@ -24,7 +24,7 @@ const (
// profile. Epoch advances on every effective correction, including promotion
// from inherited to explicit evidence at the same numeric layer.
type LayerProfileSnapshot struct {
Profile tg.LayerProfile
Profile tlprofile.Profile
Origin LayerProfileOrigin
Epoch uint32
}
@ -49,7 +49,7 @@ func unpackLayerProfileState(raw uint64) LayerProfileSnapshot {
return LayerProfileSnapshot{}
}
return LayerProfileSnapshot{
Profile: tg.LayerProfile(raw & layerProfileValueMask),
Profile: tlprofile.Profile(raw & layerProfileValueMask),
Origin: LayerProfileOrigin((raw >> layerProfileOriginShift) & layerProfileOriginMask),
Epoch: uint32(raw >> layerProfileEpochShift),
}
@ -63,7 +63,7 @@ func (c *Conn) LayerProfileState() LayerProfileSnapshot {
return unpackLayerProfileState(c.layerProfileState.Load())
}
func (c *Conn) setLayerProfile(profile tg.LayerProfile, origin LayerProfileOrigin, replace bool) (bool, error) {
func (c *Conn) setLayerProfile(profile tlprofile.Profile, origin LayerProfileOrigin, replace bool) (bool, error) {
if err := validateLayerProfile(profile); err != nil {
return false, err
}
@ -104,8 +104,8 @@ func (c *Conn) setLayerProfile(profile tg.LayerProfile, origin LayerProfileOrigi
}
}
func validateLayerProfile(profile tg.LayerProfile) error {
resolved, ok := tg.ResolveLayerProfile(int(profile))
func validateLayerProfile(profile tlprofile.Profile) error {
resolved, ok := tlprofile.ResolveProfile(int(profile))
if !ok || resolved != profile || uint64(profile) > layerProfileValueMask {
return fmt.Errorf("%w: %d", ErrLayerProfileUnsupported, profile)
}
@ -138,7 +138,7 @@ func (c *Conn) layerProfileRawEvidenceState() (LayerProfileSnapshot, int, int64)
// freezeLayerProfileAt is the production explicit-evidence transition. The
// positive client msg_id is the protocol ordering authority across TCP
// reconnects and cached request replays.
func (c *Conn) freezeLayerProfileAt(profile tg.LayerProfile, msgID int64) (bool, error) {
func (c *Conn) freezeLayerProfileAt(profile tlprofile.Profile, msgID int64) (bool, error) {
if c == nil {
return false, fmt.Errorf("nil connection layer profile")
}
@ -158,7 +158,7 @@ func (c *Conn) freezeRawLayerProfileAt(layer int, msgID int64) (bool, error) {
if layer <= 0 || msgID <= 0 {
return false, fmt.Errorf("invalid raw layer evidence layer=%d msg_id=%d", layer, msgID)
}
profile, supported := tg.ResolveLayerProfile(layer)
profile, supported := tlprofile.ResolveProfile(layer)
c.layerProfileMu.Lock()
defer c.layerProfileMu.Unlock()
@ -206,7 +206,7 @@ func (c *Conn) freezeRawLayerProfileAt(layer int, msgID int64) (bool, error) {
// seedOrderedLayerProfile restores exact-session evidence atomically before
// any request on a replacement physical connection is admitted.
func (c *Conn) seedOrderedLayerProfile(profile tg.LayerProfile, msgID int64) error {
func (c *Conn) seedOrderedLayerProfile(profile tlprofile.Profile, msgID int64) error {
if c == nil {
return nil
}
@ -248,7 +248,7 @@ func (c *Conn) seedRawLayerEvidence(layer int, msgID int64) error {
// auth.bindTempAuthKey: once a raw temporary key is resolved to its permanent
// key, the permanent key's default supersedes an older raw-key shadow. Explicit
// evidence on the concrete session is never overwritten.
func (c *Conn) refreshInheritedLayerProfile(profile tg.LayerProfile) (bool, error) {
func (c *Conn) refreshInheritedLayerProfile(profile tlprofile.Profile) (bool, error) {
if c == nil {
return false, nil
}
@ -339,7 +339,7 @@ func (s *Server) seedInitialLayerProfile(
// Older in-process exact-session registries did not retain a message
// watermark. Keep that compatibility-only seed usable without treating
// it as durable ordered evidence; real durable stores never persist zero.
profile, supported := tg.ResolveLayerProfile(layer)
profile, supported := tlprofile.ResolveProfile(layer)
if !supported {
return nil
}
@ -353,7 +353,7 @@ func (s *Server) seedInitialLayerProfile(
if msgID > 0 {
return c.seedRawLayerEvidence(layer, msgID)
}
profile, supported := tg.ResolveLayerProfile(layer)
profile, supported := tlprofile.ResolveProfile(layer)
if !supported {
return nil
}
@ -361,7 +361,7 @@ func (s *Server) seedInitialLayerProfile(
}
} else if resolver, ok := s.layerRPC.(LayerRPCSessionProfileResolver); ok {
if layer, found := resolver.NegotiatedSessionLayer(c.authKeyID, c.sessionID); found {
profile, supported := tg.ResolveLayerProfile(layer)
profile, supported := tlprofile.ResolveProfile(layer)
if !supported {
return nil
}
@ -373,7 +373,7 @@ func (s *Server) seedInitialLayerProfile(
// row again. Unsupported metadata remains unknown and must not fall through
// to a weaker mirror.
if c.authKeyExpiresAt == 0 && fetchedLayer != 0 {
profile, supported := tg.ResolveLayerProfile(fetchedLayer)
profile, supported := tlprofile.ResolveProfile(fetchedLayer)
if !supported {
return nil
}
@ -406,7 +406,7 @@ func (s *Server) seedInitialLayerProfile(
// Fall through to a raw auth-key shadow when the resolver has no
// canonical permanent-key default (for example an unbound temp key).
} else {
profile, supported := tg.ResolveLayerProfile(layer)
profile, supported := tlprofile.ResolveProfile(layer)
if !supported {
return nil
}
@ -414,7 +414,7 @@ func (s *Server) seedInitialLayerProfile(
}
}
if fetchedLayer != 0 {
profile, supported := tg.ResolveLayerProfile(fetchedLayer)
profile, supported := tlprofile.ResolveProfile(fetchedLayer)
if !supported {
return nil
}
@ -446,7 +446,7 @@ func (s *Server) refreshActivatedInheritedLayerProfile(ctx context.Context, c *C
if fetchedLayer == 0 {
return nil
}
profile, ok := tg.ResolveLayerProfile(fetchedLayer)
profile, ok := tlprofile.ResolveProfile(fetchedLayer)
if !ok {
return c.clearInheritedLayerProfile()
}
@ -472,7 +472,7 @@ func (s *Server) refreshActivatedInheritedLayerProfile(ctx context.Context, c *C
zap.String("auth_key_id", c.authKeyHex), zap.Error(err))
}
} else if found {
profile, supported := tg.ResolveLayerProfile(layer)
profile, supported := tlprofile.ResolveProfile(layer)
if !supported {
return c.clearInheritedLayerProfile()
}
@ -483,7 +483,7 @@ func (s *Server) refreshActivatedInheritedLayerProfile(ctx context.Context, c *C
if fetchedLayer == 0 {
return nil
}
profile, ok := tg.ResolveLayerProfile(fetchedLayer)
profile, ok := tlprofile.ResolveProfile(fetchedLayer)
if !ok {
return c.clearInheritedLayerProfile()
}

View file

@ -9,6 +9,7 @@ import (
"github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
)
// TestPushSkipsConnReboundToOtherUser 锁定跨账号投递窗口的修复pushToUserWithSender 在锁外
@ -28,7 +29,7 @@ func TestPushSkipsConnReboundToOtherUser(t *testing.T) {
c.userID.Store(userA)
c.userIDResolved.Store(true)
c.receivesUpdates.Store(true)
if err := c.FreezeLayerProfile(tg.LayerProfileCanonical); err != nil {
if err := c.FreezeLayerProfile(tlprofile.ProfileCanonical); err != nil {
t.Fatal(err)
}
sm.Register(c)

View file

@ -6,7 +6,7 @@ import (
"github.com/iamxvbaba/td/bin"
"github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
)
const (
@ -20,8 +20,8 @@ var errDestroyAuthKeyMustBeExclusive = errors.New("wrapped destroy_auth_key must
// wrappedDestroyAuthKeyTerminal accepts only evidence emitted by the generated
// exact wrapper parser after it has legally reached the innermost non-API
// terminal. It never re-parses wrapper bytes at runtime.
func wrappedDestroyAuthKeyTerminal(err error) (*tg.LayerRPCUnknownTerminalError, bool) {
var terminal *tg.LayerRPCUnknownTerminalError
func wrappedDestroyAuthKeyTerminal(err error) (*tlprofile.UnknownTerminalError, bool) {
var terminal *tlprofile.UnknownTerminalError
if !errors.As(err, &terminal) || terminal == nil || terminal.WireID != destroyAuthKeyRequestTypeID {
return nil, false
}
@ -35,7 +35,7 @@ func wrappedDestroyAuthKeyTerminal(err error) (*tg.LayerRPCUnknownTerminalError,
// invokeWithLayer(initConnection(destroy_auth_key)); an already initialized
// connection sends the bare service message and is classified before Layer RPC
// admission.
func validWrappedDestroyAuthKeyChain(terminal *tg.LayerRPCUnknownTerminalError) bool {
func validWrappedDestroyAuthKeyChain(terminal *tlprofile.UnknownTerminalError) bool {
if terminal == nil || terminal.WrapperCount() != 2 {
return false
}
@ -44,8 +44,8 @@ func validWrappedDestroyAuthKeyChain(terminal *tg.LayerRPCUnknownTerminalError)
return outerOK && innerOK &&
outer.Profile() == terminal.Profile &&
inner.Profile() == terminal.Profile &&
outer.Semantic() == tg.LayerSemanticMethodInvokeWithLayer &&
inner.Semantic() == tg.LayerSemanticMethodInitConnection
outer.Semantic() == tlprofile.SemanticMethodInvokeWithLayer &&
inner.Semantic() == tlprofile.SemanticMethodInitConnection
}
type destroyAuthKeyRequest struct{}

View file

@ -20,10 +20,10 @@ import (
"github.com/iamxvbaba/td/mt"
"github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/proto/codec"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tgerr"
"github.com/iamxvbaba/td/transport"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/observability/dbtrace"
"telesrv/internal/postresponse"
"telesrv/internal/store"
@ -701,8 +701,8 @@ func (s *Server) handleRPC(ctx context.Context, c *Conn, msgID int64, method str
// obey the production exact-codec invariant. Admit a defensive copy using
// the generated current profile before the legacy router consumes b.
admissionBody := &bin.Buffer{Buf: append([]byte(nil), b.Buf...)}
admitted, err := tg.NewServerDispatcher(nil).AdmitDefaultLayerWithLimits(
tg.LayerProfileCanonical,
admitted, err := tlprofile.NewDispatcher().AdmitDefault(
tlprofile.ProfileCanonical,
admissionBody,
inboundLayerDecodeLimits,
)

View file

@ -16,6 +16,7 @@ import (
"github.com/iamxvbaba/td/exchange"
"github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"github.com/iamxvbaba/td/transport"
)
@ -23,7 +24,7 @@ import (
// by old connection-state tests. Production exact-path tests must instead call
// FreezeLayerProfile/SeedLayerProfile with protocol evidence.
func legacyCanonicalTestConn(t testing.TB, c *Conn) *Conn {
return legacyLayerWireTestConn(t, c, int(tg.LayerProfileCanonical))
return legacyLayerWireTestConn(t, c, int(tlprofile.ProfileCanonical))
}
// legacyLayerWireTestConn preserves only the old tests' profile setup. It does
@ -34,7 +35,7 @@ func legacyLayerWireTestConn(t testing.TB, c *Conn, layer int) *Conn {
if c == nil {
t.Fatal("nil legacy exact-layer test Conn")
}
profile, ok := tg.ResolveLayerProfile(layer)
profile, ok := tlprofile.ResolveProfile(layer)
if !ok {
t.Fatalf("unsupported generated test Layer %d", layer)
}
@ -62,7 +63,6 @@ func exactTestUpdatesEncoded(t testing.TB, c *Conn, body []byte) *encodedOutboun
typeID: tg.UpdatesTooLongTypeID,
layer: &outboundLayerBinding{
profile: state.Profile,
typ: tg.LayerClassUpdatesType().Ref(),
epoch: state.Epoch,
},
}
@ -86,8 +86,7 @@ func (r *opaqueExactTestRPCResult) Encode(b *bin.Buffer) error { return r.result
func (r *opaqueExactTestRPCResult) exactLayerRPCResultBinding() outboundLayerBinding {
return outboundLayerBinding{
profile: tg.LayerProfileCanonical,
typ: tg.LayerClassUpdatesType().Ref(),
profile: tlprofile.ProfileCanonical,
kind: outboundLayerBindingRequest,
}
}
@ -180,7 +179,7 @@ func dialTransportOnly(t *testing.T, addr string) transport.Conn {
// profile that a production invokeWithLayer admission would have proven. It is
// intentionally explicit: handshake/new_session_created alone never implies a
// TL Layer, and production push code must keep failing closed in that state.
func freezeActiveTestSessionProfile(t *testing.T, sessions *SessionManager, authKeyID [8]byte, sessionID int64, profile tg.LayerProfile) {
func freezeActiveTestSessionProfile(t *testing.T, sessions *SessionManager, authKeyID [8]byte, sessionID int64, profile tlprofile.Profile) {
t.Helper()
if sessions == nil {
t.Fatal("freeze test session profile on nil SessionManager")

View file

@ -9,10 +9,11 @@ import (
"github.com/iamxvbaba/td/mt"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tgerr"
"github.com/iamxvbaba/td/tlprofile"
"go.uber.org/zap"
)
var inboundLayerDecodeLimits = tg.LayerDecodeLimits{
var inboundLayerDecodeLimits = tlprofile.Limits{
MaxWireBytes: maxInflightRPCBytes,
// contacts.editCloseFriends and contacts.setBlocked deliberately allow
// 5,000 entries. Keep the coarse generated allocation ceiling above every
@ -48,7 +49,7 @@ type layerRPCDependencySet struct {
}
type layerRPCProfileEvidence struct {
profile tg.LayerProfile
profile tlprofile.Profile
admissionSeq uint64
present bool
fresh bool
@ -67,7 +68,7 @@ type layerRPCAdmissionCursor struct {
evidenceMsgID int64
}
func (c *layerRPCAdmissionCursor) observe(profile tg.LayerProfile, msgID int64) error {
func (c *layerRPCAdmissionCursor) observe(profile tlprofile.Profile, msgID int64) error {
return c.observeRaw(int(profile), msgID)
}
@ -90,7 +91,7 @@ func (c *layerRPCAdmissionCursor) observeRaw(layer int, msgID int64) error {
}
}
c.state = LayerProfileSnapshot{}
if profile, supported := tg.ResolveLayerProfile(layer); supported {
if profile, supported := tlprofile.ResolveProfile(layer); supported {
c.state = LayerProfileSnapshot{Profile: profile, Origin: LayerProfileExplicit}
}
c.rawLayer = layer
@ -136,7 +137,7 @@ func (s *Server) initialLayerRPCAdmissionCursor(ctx context.Context, c *Conn) (l
if registryMsgID == 0 {
if cursor.evidenceMsgID == 0 {
cursor.state = LayerProfileSnapshot{}
if profile, supported := tg.ResolveLayerProfile(layer); supported {
if profile, supported := tlprofile.ResolveProfile(layer); supported {
cursor.state = LayerProfileSnapshot{Profile: profile, Origin: LayerProfileExplicit}
}
cursor.rawLayer = layer
@ -466,7 +467,7 @@ func (s *Server) prepareInboundLayerRPCBatch(ctx context.Context, c *Conn, plan
}
plan.rejectNewRPCOwners(indices)
for _, index := range candidateItems {
plan.items[index].admitted = tg.LayerRequest{}
plan.items[index].admitted = tlprofile.Admission{}
}
if err := reservation.retain(nil, nil); err != nil {
return err
@ -534,7 +535,7 @@ func (s *Server) prepareInboundLayerRPCBatch(ctx context.Context, c *Conn, plan
}
plan.rewrapAliases = keptAliases
for _, index := range candidateItems {
plan.items[index].admitted = tg.LayerRequest{}
plan.items[index].admitted = tlprofile.Admission{}
}
if err := reservation.retain(nil, nil); err != nil {
return err
@ -581,7 +582,7 @@ func (s *Server) prepareInboundLayerRPCBatch(ctx context.Context, c *Conn, plan
}
if len(specs) == 0 {
for _, index := range candidateItems {
plan.items[index].admitted = tg.LayerRequest{}
plan.items[index].admitted = tlprofile.Admission{}
}
if err := reservation.retain(nil, nil); err != nil {
return err
@ -604,7 +605,7 @@ func (s *Server) prepareInboundLayerRPCBatch(ctx context.Context, c *Conn, plan
// Tasks now own the admitted request leases. Drop the plan's value copies
// before non-fresh reservations become reusable.
for _, index := range candidateItems {
plan.items[index].admitted = tg.LayerRequest{}
plan.items[index].admitted = tlprofile.Admission{}
}
if err := reservation.retain(reservationIndices, specs); err != nil {
return err
@ -628,7 +629,7 @@ func (s *Server) acquireAdmittedLayerRPC(
return rpcResultAcquire{}, ErrRPCResultFlightInvalid
}
acquire := func() (rpcResultAcquire, error) {
profile := tg.LayerProfile(0)
profile := tlprofile.Profile(0)
if effective, known := item.admitted.EffectiveProfile(); known {
profile = effective
}
@ -671,7 +672,7 @@ func (s *Server) acquireAdmittedLayerRPC(
return acquire()
}
func (s *Server) prepareAdmittedLayerRPCReplay(ctx context.Context, c *Conn, msgID int64, admissionSeq uint64, profileEvidenceFresh bool, request tg.LayerRequest) (func() error, error) {
func (s *Server) prepareAdmittedLayerRPCReplay(ctx context.Context, c *Conn, msgID int64, admissionSeq uint64, profileEvidenceFresh bool, request tlprofile.Admission) (func() error, error) {
preparer, ok := s.layerRPC.(LayerRPCReplayPreparer)
if !ok || c == nil {
return nil, nil
@ -703,26 +704,26 @@ func (s *Server) withLayerRPCProfileEvidenceFresh(ctx context.Context, fresh boo
// admitInboundLayerRPC is the force-style compatibility entry point used by
// focused tests and old embedders. Production must call admitInboundLayerRPCAt
// with the real inner MTProto client msg_id.
func (s *Server) admitInboundLayerRPC(c *Conn, body []byte) (tg.LayerRequest, string, error) {
func (s *Server) admitInboundLayerRPC(c *Conn, body []byte) (tlprofile.Admission, string, error) {
return s.admitInboundLayerRPCAt(c, 0, body)
}
func (s *Server) admitInboundLayerRPCAt(c *Conn, msgID int64, body []byte) (tg.LayerRequest, string, error) {
func (s *Server) admitInboundLayerRPCAt(c *Conn, msgID int64, body []byte) (tlprofile.Admission, string, error) {
if s == nil || s.layerRPC == nil || c == nil || len(body) < bin.Word {
return tg.LayerRequest{}, "unknown", fmt.Errorf("invalid exact RPC admission input")
return tlprofile.Admission{}, "unknown", fmt.Errorf("invalid exact RPC admission input")
}
request, method, err := s.decodeInboundLayerRPC(c.LayerProfileState(), body)
if err != nil {
return tg.LayerRequest{}, method, err
return tlprofile.Admission{}, method, err
}
if profile, hasEvidence := request.ProfileEvidence(); hasEvidence {
if _, err := s.commitLayerProfileEvidence(context.Background(), c, profile, msgID); err != nil {
if !isLayerEvidenceDurabilityUnavailable(err) {
return tg.LayerRequest{}, method, err
return tlprofile.Admission{}, method, err
}
if msgID > 0 {
if _, localErr := c.freezeLayerProfileAt(profile, msgID); localErr != nil {
return tg.LayerRequest{}, method, localErr
return tlprofile.Admission{}, method, localErr
}
}
}
@ -737,8 +738,8 @@ func (s *Server) admitInboundLayerRPCAt(c *Conn, msgID int64, body []byte) (tg.L
// constructed, so the bounded fallback walks only transparent wrapper prefixes
// whose query offset is fixed and allocation-free.
func layerRPCAdmissionHasExplicitSelector(body []byte, admissionErr error) bool {
var codecErr *tg.LayerCodecError
if errors.As(admissionErr, &codecErr) && codecErr.Semantic == tg.LayerSemanticMethodInvokeWithLayer {
var codecErr *tlprofile.LayerCodecError
if errors.As(admissionErr, &codecErr) && codecErr.Semantic == tlprofile.SemanticMethodInvokeWithLayer {
return true
}
@ -784,13 +785,13 @@ func layerRPCAdmissionHasExplicitSelector(body []byte, admissionErr error) bool
// it with a wire-ordered provisional profile cursor, then publishes explicit
// evidence only after the full request identity has acquired an owner (or a
// genuine new-msg_id rewrap alias).
func (s *Server) decodeInboundLayerRPC(state LayerProfileSnapshot, body []byte) (tg.LayerRequest, string, error) {
func (s *Server) decodeInboundLayerRPC(state LayerProfileSnapshot, body []byte) (tlprofile.Admission, string, error) {
if s == nil || s.layerRPC == nil || len(body) < bin.Word {
return tg.LayerRequest{}, "unknown", fmt.Errorf("invalid exact RPC admission input")
return tlprofile.Admission{}, "unknown", fmt.Errorf("invalid exact RPC admission input")
}
b := &bin.Buffer{Buf: body}
var (
request tg.LayerRequest
request tlprofile.Admission
err error
)
if state.Origin != LayerProfileUnknown {
@ -806,19 +807,19 @@ func (s *Server) decodeInboundLayerRPC(state LayerProfileSnapshot, body []byte)
}
method := "unknown"
if err == nil {
_, method, _ = tg.LayerSemanticName(request.Call().Method())
_, method, _ = tlprofile.SemanticName(request.Call().Method())
if b.Len() != 0 {
return tg.LayerRequest{}, method, fmt.Errorf("exact RPC admission left %d bytes", b.Len())
return tlprofile.Admission{}, method, fmt.Errorf("exact RPC admission left %d bytes", b.Len())
}
if effective, known := request.EffectiveProfile(); known && effective != request.Call().Profile() {
return tg.LayerRequest{}, method, fmt.Errorf("%w: effective profile %d differs from call profile %d", ErrLayerProfileConflict, effective, request.Call().Profile())
return tlprofile.Admission{}, method, fmt.Errorf("%w: effective profile %d differs from call profile %d", ErrLayerProfileConflict, effective, request.Call().Profile())
}
// A generated invariant terminal may use canonical decoding internally
// before the client declares a layer. Only explicit invokeWithLayer (or the
// strict compatibility fallback above) publishes new profile evidence.
if profile, hasEvidence := request.ProfileEvidence(); hasEvidence {
if profile != request.Call().Profile() {
return tg.LayerRequest{}, method, fmt.Errorf("%w: generated profile evidence %d differs from call profile %d", ErrLayerProfileConflict, profile, request.Call().Profile())
return tlprofile.Admission{}, method, fmt.Errorf("%w: generated profile evidence %d differs from call profile %d", ErrLayerProfileConflict, profile, request.Call().Profile())
}
}
return request, method, nil
@ -834,9 +835,9 @@ func (s *Server) decodeInboundLayerRPC(state LayerProfileSnapshot, body []byte)
if id, peekErr := (&bin.Buffer{Buf: body}).PeekID(); peekErr == nil {
method = s.typeName(id)
}
if codecErr := new(tg.LayerCodecError); errors.As(err, &codecErr) {
if codecErr := new(tlprofile.LayerCodecError); errors.As(err, &codecErr) {
if codecErr.Semantic != 0 {
if _, semanticMethod, ok := tg.LayerSemanticName(codecErr.Semantic); ok && semanticMethod != "" {
if _, semanticMethod, ok := tlprofile.SemanticName(codecErr.Semantic); ok && semanticMethod != "" {
method = semanticMethod
}
} else if codecErr.WireID != 0 {
@ -846,7 +847,7 @@ func (s *Server) decodeInboundLayerRPC(state LayerProfileSnapshot, body []byte)
method = s.typeName(codecErr.WireID)
}
}
if errors.Is(err, tg.ErrLayerUnknownRPCMethod) && s.log != nil {
if errors.Is(err, tlprofile.ErrUnknownRPCMethod) && s.log != nil {
if terminal, recognized := wrappedDestroyAuthKeyTerminal(err); recognized {
method = "destroy_auth_key"
s.log.Debug("Generated wrapper admission exposed MTProto service terminal",
@ -860,7 +861,7 @@ func (s *Server) decodeInboundLayerRPC(state LayerProfileSnapshot, body []byte)
zap.String("method", method), zap.Error(err))
}
}
return tg.LayerRequest{}, method, err
return tlprofile.Admission{}, method, err
}
// commitLayerProfileEvidence publishes one generated invokeWithLayer proof.
@ -868,7 +869,7 @@ func (s *Server) decodeInboundLayerRPC(state LayerProfileSnapshot, body []byte)
// point; the Conn cursor then prevents a concurrent older admission from
// overwriting its local wire epoch. Older cached duplicates remain decodable
// and request-bound, but cannot mutate session/profile state.
func (s *Server) commitLayerProfileEvidence(ctx context.Context, c *Conn, profile tg.LayerProfile, msgID int64) (bool, error) {
func (s *Server) commitLayerProfileEvidence(ctx context.Context, c *Conn, profile tlprofile.Profile, msgID int64) (bool, error) {
if s == nil || c == nil {
return false, fmt.Errorf("invalid layer profile evidence target")
}
@ -890,7 +891,7 @@ func (s *Server) commitLayerProfileEvidence(ctx context.Context, c *Conn, profil
} else if _, err := c.freezeRawLayerProfileAt(layer, authoritativeMsgID); err != nil {
return false, err
}
authoritative, supported := tg.ResolveLayerProfile(layer)
authoritative, supported := tlprofile.ResolveProfile(layer)
return supported && authoritative == profile && authoritativeMsgID == msgID && publishShared, nil
}
if registry, ok := s.layerRPC.(LayerRPCOrderedSessionProfileRegistry); ok {
@ -909,7 +910,7 @@ func (s *Server) commitLayerProfileEvidence(ctx context.Context, c *Conn, profil
if !found || authoritativeMsgID <= 0 {
return false, fmt.Errorf("%w: ordered exact session evidence disappeared after commit", ErrLayerProfileConflict)
}
authoritative, supported := tg.ResolveLayerProfile(layer)
authoritative, supported := tlprofile.ResolveProfile(layer)
if s.conns != nil {
if _, err := s.conns.ApplyOrderedRawLayerForSession(c, c.authKeyID, c.sessionID, layer, authoritativeMsgID); err != nil {
return false, err
@ -980,34 +981,34 @@ func layerRPCAdmissionError(err error) *mt.RPCError {
if errors.Is(err, errDefaultLayerAdmission) {
return &mt.RPCError{ErrorCode: 400, ErrorMessage: "CONNECTION_LAYER_INVALID"}
}
if errors.Is(err, tg.ErrLayerProfileRequired) {
if errors.Is(err, tlprofile.ErrProfileRequired) {
return &mt.RPCError{ErrorCode: 400, ErrorMessage: "CONNECTION_NOT_INITED"}
}
var rpcErr *tgerr.Error
if errors.As(err, &rpcErr) {
return &mt.RPCError{ErrorCode: rpcErr.Code, ErrorMessage: rpcErr.Message}
}
if errors.Is(err, tg.ErrLayerUnknownRPCMethod) {
if errors.Is(err, tlprofile.ErrUnknownRPCMethod) {
return &mt.RPCError{ErrorCode: 501, ErrorMessage: "NOT_IMPLEMENTED"}
}
return &mt.RPCError{ErrorCode: 400, ErrorMessage: "INPUT_REQUEST_INVALID"}
}
func (s *Server) layerRPCDependencies(c *Conn, msgID int64, request tg.LayerRequest) layerRPCDependencySet {
func (s *Server) layerRPCDependencies(c *Conn, msgID int64, request tlprofile.Admission) layerRPCDependencySet {
result := layerRPCDependencySet{}
seen := make(map[int64]struct{})
for index := 0; index < request.WrapperCount(); index++ {
wrapper, _ := request.Wrapper(index)
var ids []int64
switch wrapper.Semantic() {
case tg.LayerSemanticMethodInvokeAfterMsg:
case tlprofile.SemanticMethodInvokeAfterMsg:
id, err := layerRPCWrapperRequired[int64](wrapper, "msg_id")
if err != nil {
result.failed = true
continue
}
ids = []int64{id}
case tg.LayerSemanticMethodInvokeAfterMsgs:
case tlprofile.SemanticMethodInvokeAfterMsgs:
var err error
ids, err = layerRPCWrapperRequired[[]int64](wrapper, "msg_ids")
if err != nil || len(ids) > maxLayerRPCDependencyIDs {
@ -1045,16 +1046,16 @@ func (s *Server) layerRPCDependencies(c *Conn, msgID int64, request tg.LayerRequ
return result
}
func admittedRPCRewrapInit(request tg.LayerRequest) (rpcRewrapInit, bool) {
func admittedRPCRewrapInit(request tlprofile.Admission) (rpcRewrapInit, bool) {
if request.WrapperCount() != 2 {
return rpcRewrapInit{}, false
}
layerWrapper, ok := request.Wrapper(0)
if !ok || layerWrapper.Semantic() != tg.LayerSemanticMethodInvokeWithLayer {
if !ok || layerWrapper.Semantic() != tlprofile.SemanticMethodInvokeWithLayer {
return rpcRewrapInit{}, false
}
initWrapper, ok := request.Wrapper(1)
if !ok || initWrapper.Semantic() != tg.LayerSemanticMethodInitConnection {
if !ok || initWrapper.Semantic() != tlprofile.SemanticMethodInitConnection {
return rpcRewrapInit{}, false
}
layer, err := layerRPCWrapperRequired[int](layerWrapper, "layer")
@ -1095,7 +1096,7 @@ func admittedRPCRewrapInit(request tg.LayerRequest) (rpcRewrapInit, bool) {
}, true
}
func layerRPCWrapperRequired[T any](wrapper tg.LayerRPCWrapper, name string) (T, error) {
func layerRPCWrapperRequired[T any](wrapper tlprofile.Wrapper, name string) (T, error) {
var zero T
value, present, ok, err := wrapper.Value(name)
if err != nil || !ok || !present {

View file

@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"strings"
"sync"
"sync/atomic"
"testing"
@ -15,6 +16,7 @@ import (
"github.com/iamxvbaba/td/tg"
"go.uber.org/zap/zaptest"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/rpc"
"telesrv/internal/store"
"telesrv/internal/store/memory"
@ -29,17 +31,17 @@ func exactLayerRPCBody(t *testing.T, request bin.Encoder) []byte {
return body.Copy()
}
func exactOutboundLayerRPCBody(t *testing.T, profile tg.LayerProfile, request bin.Object) []byte {
func exactOutboundLayerRPCBody(t *testing.T, profile tlprofile.Profile, request bin.Object) []byte {
t.Helper()
outbound, err := tg.PrepareLayerOutboundCall(profile, request)
if err != nil {
var body bin.Buffer
if err := tlprofile.EncodeObject(profile, request, &body); err != nil {
t.Fatal(err)
}
return exactLayerRPCBody(t, outbound)
return body.Copy()
}
type admissionOnlyLayerRPC struct {
dispatcher *tg.ServerDispatcher
dispatcher *tlprofile.Dispatcher
mu sync.Mutex
published []publishedLayerEvidence
}
@ -128,8 +130,8 @@ func (s unavailableEdgeSessionLayerStore) DeleteExpiredSessionLayers(context.Con
type dispatchProfileCaptureRouter struct {
*rpc.Router
mu sync.Mutex
requestProfile tg.LayerProfile
resultProfile tg.LayerProfile
requestProfile tlprofile.Profile
resultProfile tlprofile.Profile
}
func (h *dispatchProfileCaptureRouter) DispatchAdmitted(
@ -138,8 +140,8 @@ func (h *dispatchProfileCaptureRouter) DispatchAdmitted(
sessionID int64,
msgID int64,
admissionSeq uint64,
request tg.LayerRequest,
) (tg.LayerRPCResult, string, error) {
request tlprofile.Admission,
) (tlprofile.Result, string, error) {
result, method, err := h.Router.DispatchAdmitted(ctx, authKeyID, sessionID, msgID, admissionSeq, request)
h.mu.Lock()
h.requestProfile = request.Call().Profile()
@ -150,7 +152,7 @@ func (h *dispatchProfileCaptureRouter) DispatchAdmitted(
return result, method, err
}
func (h *dispatchProfileCaptureRouter) profiles() (tg.LayerProfile, tg.LayerProfile) {
func (h *dispatchProfileCaptureRouter) profiles() (tlprofile.Profile, tlprofile.Profile) {
h.mu.Lock()
defer h.mu.Unlock()
return h.requestProfile, h.resultProfile
@ -189,7 +191,7 @@ func (h *capacityAdmissionOnlyLayerRPC) FreezeNegotiatedSessionLayerAt([8]byte,
type replayProfileCaptureLayerRPC struct {
*admissionOnlyLayerRPC
mu sync.Mutex
profiles []tg.LayerProfile
profiles []tlprofile.Profile
known []bool
}
@ -199,7 +201,7 @@ func (h *replayProfileCaptureLayerRPC) PrepareAdmittedReplay(
_ int64,
_ int64,
_ uint64,
request tg.LayerRequest,
request tlprofile.Admission,
) (func() error, error) {
profile, known := request.EffectiveProfile()
h.mu.Lock()
@ -209,14 +211,14 @@ func (h *replayProfileCaptureLayerRPC) PrepareAdmittedReplay(
return nil, nil
}
func (h *replayProfileCaptureLayerRPC) capturedProfiles() ([]tg.LayerProfile, []bool) {
func (h *replayProfileCaptureLayerRPC) capturedProfiles() ([]tlprofile.Profile, []bool) {
h.mu.Lock()
defer h.mu.Unlock()
return append([]tg.LayerProfile(nil), h.profiles...), append([]bool(nil), h.known...)
return append([]tlprofile.Profile(nil), h.profiles...), append([]bool(nil), h.known...)
}
func newAdmissionOnlyLayerRPC() *admissionOnlyLayerRPC {
return &admissionOnlyLayerRPC{dispatcher: tg.NewServerDispatcher(nil)}
return &admissionOnlyLayerRPC{dispatcher: tlprofile.NewDispatcher()}
}
func newOrderedAdmissionOnlyLayerRPC() *orderedAdmissionOnlyLayerRPC {
@ -259,19 +261,19 @@ func (h *orderedAdmissionOnlyLayerRPC) FreezeNegotiatedSessionLayerAt(authKeyID
return true, nil
}
func (h *admissionOnlyLayerRPC) AdmitLayer(profile tg.LayerProfile, b *bin.Buffer, limits tg.LayerDecodeLimits) (tg.LayerRequest, error) {
return h.dispatcher.AdmitLayerWithLimits(profile, b, limits)
func (h *admissionOnlyLayerRPC) AdmitLayer(profile tlprofile.Profile, b *bin.Buffer, limits tlprofile.Limits) (tlprofile.Admission, error) {
return h.dispatcher.Admit(profile, b, limits)
}
func (h *admissionOnlyLayerRPC) AdmitDefaultLayer(profile tg.LayerProfile, b *bin.Buffer, limits tg.LayerDecodeLimits) (tg.LayerRequest, error) {
return h.dispatcher.AdmitDefaultLayerWithLimits(profile, b, limits)
func (h *admissionOnlyLayerRPC) AdmitDefaultLayer(profile tlprofile.Profile, b *bin.Buffer, limits tlprofile.Limits) (tlprofile.Admission, error) {
return h.dispatcher.AdmitDefault(profile, b, limits)
}
func (h *admissionOnlyLayerRPC) AdmitUnprofiled(b *bin.Buffer, limits tg.LayerDecodeLimits) (tg.LayerRequest, error) {
return h.dispatcher.AdmitUnprofiledWithLimits(b, limits)
func (h *admissionOnlyLayerRPC) AdmitUnprofiled(b *bin.Buffer, limits tlprofile.Limits) (tlprofile.Admission, error) {
return h.dispatcher.AdmitUnprofiled(b, limits)
}
func (*admissionOnlyLayerRPC) DispatchAdmitted(context.Context, [8]byte, int64, int64, uint64, tg.LayerRequest) (tg.LayerRPCResult, string, error) {
func (*admissionOnlyLayerRPC) DispatchAdmitted(context.Context, [8]byte, int64, int64, uint64, tlprofile.Admission) (tlprofile.Result, string, error) {
return nil, "", fmt.Errorf("admission-only handler")
}
@ -302,7 +304,7 @@ func (h *admissionOnlyLayerRPC) publications() []publishedLayerEvidence {
func TestNestedExplicitLayerAdmissionErrorsAreNotDefaultFailures(t *testing.T) {
handler := newAdmissionOnlyLayerRPC()
s := New(Options{DC: 2, LayerRPC: handler})
state := LayerProfileSnapshot{Profile: tg.LayerProfile227, Origin: LayerProfileInherited}
state := LayerProfileSnapshot{Profile: tlprofile.Profile227, Origin: LayerProfileInherited}
unsupported := exactLayerRPCBody(t, &tg.InvokeAfterMsgRequest{
MsgID: 1,
@ -334,12 +336,11 @@ func TestNestedExplicitLayerAdmissionErrorsAreNotDefaultFailures(t *testing.T) {
malformedSelectedQuery.PutID(tg.MessagesGetHistoryRequestTypeID)
for _, test := range []struct {
name string
body []byte
wantSemantic bool
name string
body []byte
}{
{name: "unsupported", body: unsupported, wantSemantic: true},
{name: "conflict", body: conflict, wantSemantic: true},
{name: "unsupported", body: unsupported},
{name: "conflict", body: conflict},
{name: "truncated_selector", body: truncatedSelector.Copy()},
{name: "malformed_selected_query", body: malformedSelectedQuery.Copy()},
} {
@ -351,10 +352,14 @@ func TestNestedExplicitLayerAdmissionErrorsAreNotDefaultFailures(t *testing.T) {
if errors.Is(err, errDefaultLayerAdmission) {
t.Fatalf("explicit admission was misclassified as stale default: %v", err)
}
if test.wantSemantic {
var codecErr *tg.LayerCodecError
if !errors.As(err, &codecErr) || codecErr.Semantic != tg.LayerSemanticMethodInvokeWithLayer {
t.Fatalf("explicit error semantic = %#v, err=%v", codecErr, err)
switch test.name {
case "unsupported":
if !strings.Contains(err.Error(), "unsupported exact profile 229") {
t.Fatalf("unsupported selector error = %v", err)
}
case "conflict":
if !errors.Is(err, tlprofile.ErrProfileConflict) {
t.Fatalf("conflicting selector error = %v", err)
}
}
})
@ -376,7 +381,7 @@ func TestBatchProvisionalCursorKeepsRegistryWatermarkAcrossOldReplay(t *testing.
t.Fatal(err)
}
c := &Conn{authKeyID: authKeyID, sessionID: sessionID, metrics: NopMetrics{}}
if err := c.SeedInheritedLayerProfile(tg.LayerProfile225); err != nil {
if err := c.SeedInheritedLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
c.startInboundRPCScheduler(s.rpcScheduler, 1, 8, time.Second)
@ -384,7 +389,7 @@ func TestBatchProvisionalCursorKeepsRegistryWatermarkAcrossOldReplay(t *testing.
oldBody := exactLayerRPCBody(t, &tg.InvokeWithLayerRequest{Layer: 225, Query: &tg.HelpGetConfigRequest{}})
oldAdmitted, _, err := s.decodeInboundLayerRPC(
LayerProfileSnapshot{Profile: tg.LayerProfile225, Origin: LayerProfileExplicit}, oldBody,
LayerProfileSnapshot{Profile: tlprofile.Profile225, Origin: LayerProfileExplicit}, oldBody,
)
if err != nil {
t.Fatal(err)
@ -398,7 +403,7 @@ func TestBatchProvisionalCursorKeepsRegistryWatermarkAcrossOldReplay(t *testing.
oldClaim.owner.CompleteExecution(true)
s.rpcResults.Put(authKeyID, sessionID, 100, &encodedOutboundMessage{body: []byte{1}, reqMsgID: 100})
nakedBody := exactOutboundLayerRPCBody(t, tg.LayerProfile227, &tg.MessagesGetHistoryRequest{
nakedBody := exactOutboundLayerRPCBody(t, tlprofile.Profile227, &tg.MessagesGetHistoryRequest{
Peer: &tg.InputPeerSelf{}, Limit: 1,
})
plan := &inboundPlan{items: []inboundItem{
@ -412,7 +417,7 @@ func TestBatchProvisionalCursorKeepsRegistryWatermarkAcrossOldReplay(t *testing.
if plan.items[0].kind != inboundItemReplayRPC {
t.Fatalf("old explicit item kind=%d, want completed replay", plan.items[0].kind)
}
if profile, ok := s.rpcResults.ExactAdmissionProfile(authKeyID, sessionID, 108); !ok || profile != tg.LayerProfile227 {
if profile, ok := s.rpcResults.ExactAdmissionProfile(authKeyID, sessionID, 108); !ok || profile != tlprofile.Profile227 {
t.Fatalf("following naked admission profile = (%d,%v), want registry Layer 227", profile, ok)
}
}
@ -423,7 +428,7 @@ func TestBatchProvisionalCursorUsesPendingNewerExplicitEvidence(t *testing.T) {
authKeyID := [8]byte{0x31, 0x02}
const sessionID = int64(3102)
c := &Conn{authKeyID: authKeyID, sessionID: sessionID, metrics: NopMetrics{}}
if err := c.seedOrderedLayerProfile(tg.LayerProfile225, 100); err != nil {
if err := c.seedOrderedLayerProfile(tlprofile.Profile225, 100); err != nil {
t.Fatal(err)
}
c.startInboundRPCScheduler(s.rpcScheduler, 1, 8, time.Second)
@ -431,7 +436,7 @@ func TestBatchProvisionalCursorUsesPendingNewerExplicitEvidence(t *testing.T) {
explicitBody := exactLayerRPCBody(t, &tg.InvokeWithLayerRequest{Layer: 227, Query: &tg.HelpGetConfigRequest{}})
explicit, _, err := s.decodeInboundLayerRPC(
LayerProfileSnapshot{Profile: tg.LayerProfile227, Origin: LayerProfileExplicit}, explicitBody,
LayerProfileSnapshot{Profile: tlprofile.Profile227, Origin: LayerProfileExplicit}, explicitBody,
)
if err != nil {
t.Fatal(err)
@ -444,7 +449,7 @@ func TestBatchProvisionalCursorUsesPendingNewerExplicitEvidence(t *testing.T) {
}
defer pending.owner.Abort()
nakedBody := exactOutboundLayerRPCBody(t, tg.LayerProfile227, &tg.MessagesGetHistoryRequest{
nakedBody := exactOutboundLayerRPCBody(t, tlprofile.Profile227, &tg.MessagesGetHistoryRequest{
Peer: &tg.InputPeerSelf{}, Limit: 1,
})
plan := &inboundPlan{items: []inboundItem{
@ -458,10 +463,10 @@ func TestBatchProvisionalCursorUsesPendingNewerExplicitEvidence(t *testing.T) {
if plan.items[0].kind != inboundItemRewrappedRPC {
t.Fatalf("pending explicit item kind=%d, want pending replay", plan.items[0].kind)
}
if profile, ok := s.rpcResults.ExactAdmissionProfile(authKeyID, sessionID, 108); !ok || profile != tg.LayerProfile227 {
if profile, ok := s.rpcResults.ExactAdmissionProfile(authKeyID, sessionID, 108); !ok || profile != tlprofile.Profile227 {
t.Fatalf("following naked admission profile = (%d,%v), want pending Layer 227", profile, ok)
}
if state, msgID := c.layerProfileEvidenceState(); state.Profile != tg.LayerProfile227 || state.Origin != LayerProfileExplicit || msgID != 104 {
if state, msgID := c.layerProfileEvidenceState(); state.Profile != tlprofile.Profile227 || state.Origin != LayerProfileExplicit || msgID != 104 {
t.Fatalf("pending full-identity evidence was not committed = %#v msgID:%d", state, msgID)
}
if got := handler.publications(); len(got) != 0 {
@ -500,7 +505,7 @@ func TestFutureExactLayerWatermarkAllowsOnlyNewerSupportedSelfHeal(t *testing.T)
t.Fatal(err)
}
c := &Conn{authKeyID: authKeyID, sessionID: sessionID, metrics: NopMetrics{}}
if err := c.SeedInheritedLayerProfile(tg.LayerProfile225); err != nil {
if err := c.SeedInheritedLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
c.startInboundRPCScheduler(s.rpcScheduler, 1, 8, time.Second)
@ -513,7 +518,7 @@ func TestFutureExactLayerWatermarkAllowsOnlyNewerSupportedSelfHeal(t *testing.T)
},
{
kind: inboundItemRPC, msgID: 104,
body: exactOutboundLayerRPCBody(t, tg.LayerProfile227, &tg.MessagesGetHistoryRequest{
body: exactOutboundLayerRPCBody(t, tlprofile.Profile227, &tg.MessagesGetHistoryRequest{
Peer: &tg.InputPeerSelf{}, Limit: 1,
}),
},
@ -549,7 +554,7 @@ func TestFutureExactLayerWatermarkAllowsOnlyNewerSupportedSelfHeal(t *testing.T)
if err := s.prepareInboundLayerRPCBatch(context.Background(), c, newPlan); err != nil {
t.Fatal(err)
}
if state, rawLayer, msgID := c.layerProfileRawEvidenceState(); state.Profile != tg.LayerProfile227 || state.Origin != LayerProfileExplicit || rawLayer != 227 || msgID != 108 {
if state, rawLayer, msgID := c.layerProfileRawEvidenceState(); state.Profile != tlprofile.Profile227 || state.Origin != LayerProfileExplicit || rawLayer != 227 || msgID != 108 {
t.Fatalf("newer supported self-heal = %#v raw:%d msgID:%d", state, rawLayer, msgID)
}
if got := handler.publications(); len(got) != 1 || got[0].layer != 227 || got[0].msgID != 108 {
@ -602,7 +607,7 @@ func TestDurabilityOutageKeepsExplicitLayerConnectionLocal(t *testing.T) {
if !plan.items[0].profileEvidenceFresh() {
t.Fatal("durability fallback incorrectly disabled current-connection wrapper effects")
}
if state, msgID := c.layerProfileEvidenceState(); state.Profile != tg.LayerProfile227 || state.Origin != LayerProfileExplicit || msgID != 100 {
if state, msgID := c.layerProfileEvidenceState(); state.Profile != tlprofile.Profile227 || state.Origin != LayerProfileExplicit || msgID != 100 {
t.Fatalf("connection-local evidence = %#v msgID:%d", state, msgID)
}
if _, _, found := handler.NegotiatedSessionLayerEvidence(c.authKeyID, c.sessionID); found {
@ -679,7 +684,7 @@ func TestDurabilityOutageInitializesOnlyCurrentConnection(t *testing.T) {
if !c.rpcRewrapInitialized.Load() {
t.Fatal("current connection did not retain successful init wrapper state")
}
if state, evidenceMsgID := c.layerProfileEvidenceState(); state.Profile != tg.LayerProfile227 || state.Origin != LayerProfileExplicit || evidenceMsgID != msgID {
if state, evidenceMsgID := c.layerProfileEvidenceState(); state.Profile != tlprofile.Profile227 || state.Origin != LayerProfileExplicit || evidenceMsgID != msgID {
t.Fatalf("current connection profile = %#v msg:%d", state, evidenceMsgID)
}
if _, _, found := router.NegotiatedSessionLayerEvidence(c.authKeyID, c.sessionID); found {
@ -710,7 +715,7 @@ func TestDurabilityOutageInitializesOnlyCurrentConnection(t *testing.T) {
t.Fatal(err)
}
encoded, err := fanout.prepareForConn(ctx, c)
if err != nil || encoded.layer == nil || encoded.layer.profile != tg.LayerProfile227 {
if err != nil || encoded.layer == nil || encoded.layer.profile != tlprofile.Profile227 {
t.Fatalf("outage-local push profile = encoded:%#v err:%v", encoded, err)
}
@ -723,7 +728,7 @@ func TestDurabilityOutageInitializesOnlyCurrentConnection(t *testing.T) {
if state := replacement.LayerProfileState(); state.Origin != LayerProfileUnknown {
t.Fatalf("new session inherited outage-local profile: %#v", state)
}
naked := exactOutboundLayerRPCBody(t, tg.LayerProfile227, &tg.MessagesGetHistoryRequest{
naked := exactOutboundLayerRPCBody(t, tlprofile.Profile227, &tg.MessagesGetHistoryRequest{
Peer: &tg.InputPeerSelf{}, Limit: 1,
})
if _, _, err := s.admitInboundLayerRPCAt(replacement, msgID+4, naked); err == nil {
@ -782,7 +787,7 @@ func TestInvariantReplayNeverCachesInternalCanonicalProfile(t *testing.T) {
}
profiled := newConn()
defer profiled.Close()
if err := profiled.seedOrderedLayerProfile(tg.LayerProfile225, 104); err != nil {
if err := profiled.seedOrderedLayerProfile(tlprofile.Profile225, 104); err != nil {
t.Fatal(err)
}
completed := &inboundPlan{items: []inboundItem{{kind: inboundItemRPC, msgID: 100, body: body}}}
@ -793,7 +798,7 @@ func TestInvariantReplayNeverCachesInternalCanonicalProfile(t *testing.T) {
if completed.items[0].kind != inboundItemReplayRPC {
t.Fatalf("profiled invariant completed replay kind=%d", completed.items[0].kind)
}
if state, msgID := profiled.layerProfileEvidenceState(); state.Profile != tg.LayerProfile225 || state.Origin != LayerProfileExplicit || msgID != 104 {
if state, msgID := profiled.layerProfileEvidenceState(); state.Profile != tlprofile.Profile225 || state.Origin != LayerProfileExplicit || msgID != 104 {
t.Fatalf("invariant replay polluted explicit profile = %#v msgID:%d", state, msgID)
}
}
@ -804,21 +809,21 @@ func TestSameMsgIDNakedReplayUsesWinnerAdmissionProfile(t *testing.T) {
s.rpcResults = newRPCResultCacheWithFlightLimit(time.Now, 8)
authKeyID := [8]byte{0x22, 0x99}
const sessionID = int64(2299)
body := exactOutboundLayerRPCBody(t, tg.LayerProfile225, &tg.MessagesGetHistoryRequest{
body := exactOutboundLayerRPCBody(t, tlprofile.Profile225, &tg.MessagesGetHistoryRequest{
Peer: &tg.InputPeerSelf{}, Limit: 1,
})
item220 := inboundItem{msgID: 100, body: body}
var err error
item220.admitted, item220.method, err = s.decodeInboundLayerRPC(
LayerProfileSnapshot{Profile: tg.LayerProfile225, Origin: LayerProfileInherited}, body,
LayerProfileSnapshot{Profile: tlprofile.Profile225, Origin: LayerProfileInherited}, body,
)
if err != nil {
t.Fatal(err)
}
item227 := inboundItem{msgID: 100, body: body}
item227.admitted, item227.method, err = s.decodeInboundLayerRPC(
LayerProfileSnapshot{Profile: tg.LayerProfile227, Origin: LayerProfileInherited}, body,
LayerProfileSnapshot{Profile: tlprofile.Profile227, Origin: LayerProfileInherited}, body,
)
if err != nil {
t.Fatal(err)
@ -836,13 +841,13 @@ func TestSameMsgIDNakedReplayUsesWinnerAdmissionProfile(t *testing.T) {
if err != nil || loser.state != rpcResultAcquirePending || loser.admissionSeq != winner.admissionSeq {
t.Fatalf("loser join = state:%d seq:%d err:%v, winner seq:%d", loser.state, loser.admissionSeq, err, winner.admissionSeq)
}
if got := item227.admitted.Call().Profile(); got != tg.LayerProfile225 {
if got := item227.admitted.Call().Profile(); got != tlprofile.Profile225 {
t.Fatalf("loser re-admitted profile = %d, want winner 225", got)
}
changed := inboundItem{msgID: 100, body: exactLayerRPCBody(t, &tg.HelpGetNearestDCRequest{})}
changed.admitted, changed.method, err = s.decodeInboundLayerRPC(
LayerProfileSnapshot{Profile: tg.LayerProfile225, Origin: LayerProfileInherited}, changed.body,
LayerProfileSnapshot{Profile: tlprofile.Profile225, Origin: LayerProfileInherited}, changed.body,
)
if err != nil {
t.Fatal(err)
@ -854,7 +859,7 @@ func TestSameMsgIDNakedReplayUsesWinnerAdmissionProfile(t *testing.T) {
}
func TestInheritedLayerServesRepeatedNakedRPCsWithoutSelectorRefresh(t *testing.T) {
for _, profile := range []tg.LayerProfile{tg.LayerProfile225, tg.LayerProfile227} {
for _, profile := range []tlprofile.Profile{tlprofile.Profile225, tlprofile.Profile227} {
t.Run(fmt.Sprintf("layer_%d", profile), func(t *testing.T) {
s := New(Options{DC: 2, LayerRPC: newAdmissionOnlyLayerRPC()})
c := &Conn{authKeyID: [8]byte{0x71, byte(profile)}, sessionID: int64(profile), metrics: NopMetrics{}}
@ -985,7 +990,7 @@ func TestOldCompletedLayerRequestCannotRollBackCorrectedSession(t *testing.T) {
if err := s.prepareInboundLayerRPCBatch(context.Background(), c, correctPlan); err != nil {
t.Fatal(err)
}
if state, msgID := c.layerProfileEvidenceState(); state.Profile != tg.LayerProfile227 || msgID != 104 {
if state, msgID := c.layerProfileEvidenceState(); state.Profile != tlprofile.Profile227 || msgID != 104 {
t.Fatalf("corrected Conn = %#v msgID:%d", state, msgID)
}
@ -1002,7 +1007,7 @@ func TestOldCompletedLayerRequestCannotRollBackCorrectedSession(t *testing.T) {
if replay.items[0].kind != inboundItemReplayRPC {
t.Fatalf("old request kind=%d, want completed replay", replay.items[0].kind)
}
if state, msgID := replacement.layerProfileEvidenceState(); state.Profile != tg.LayerProfile227 || msgID != 104 {
if state, msgID := replacement.layerProfileEvidenceState(); state.Profile != tlprofile.Profile227 || msgID != 104 {
t.Fatalf("old replay rolled replacement back = %#v msgID:%d", state, msgID)
}
if layer, msgID, ok := handler.NegotiatedSessionLayerEvidence(authKeyID, sessionID); !ok || layer != 227 || msgID != 104 {
@ -1069,11 +1074,11 @@ func TestLogicalSessionLayerWatermarkSurvivesResultExpiryAndOldContainer(t *test
if err := s.seedInitialLayerProfile(context.Background(), replacement, 0, LayerProfileSnapshot{}); err != nil {
t.Fatal(err)
}
if state, msgID := replacement.layerProfileEvidenceState(); state.Profile != tg.LayerProfile227 || state.Origin != LayerProfileInherited || msgID != 0 {
if state, msgID := replacement.layerProfileEvidenceState(); state.Profile != tlprofile.Profile227 || state.Origin != LayerProfileInherited || msgID != 0 {
t.Fatalf("replacement seed = %#v msgID:%d, want inherited Layer 227", state, msgID)
}
freshMsgID := proto.NewMessageIDGen(now.Now).New(proto.MessageFromClient)
nakedBody := exactOutboundLayerRPCBody(t, tg.LayerProfile227, &tg.MessagesGetHistoryRequest{
nakedBody := exactOutboundLayerRPCBody(t, tlprofile.Profile227, &tg.MessagesGetHistoryRequest{
Peer: &tg.InputPeerSelf{}, Limit: 1,
})
replayPlan := &inboundPlan{items: []inboundItem{
@ -1090,10 +1095,10 @@ func TestLogicalSessionLayerWatermarkSurvivesResultExpiryAndOldContainer(t *test
if err := s.prepareInboundLayerRPCBatch(context.Background(), replacement, replayPlan); err != nil {
t.Fatal(err)
}
if profile, ok := s.rpcResults.ExactAdmissionProfile(authKeyID, sessionID, freshMsgID); !ok || profile != tg.LayerProfile227 {
if profile, ok := s.rpcResults.ExactAdmissionProfile(authKeyID, sessionID, freshMsgID); !ok || profile != tlprofile.Profile227 {
t.Fatalf("naked request after expired old replay = (%d,%v), want Layer 227", profile, ok)
}
if state, msgID := replacement.layerProfileEvidenceState(); state.Profile != tg.LayerProfile227 || state.Origin != LayerProfileInherited || msgID != 0 {
if state, msgID := replacement.layerProfileEvidenceState(); state.Profile != tlprofile.Profile227 || state.Origin != LayerProfileInherited || msgID != 0 {
t.Fatalf("old request-bound flight rolled replacement back = %#v msgID:%d", state, msgID)
}
if _, _, ok := router.NegotiatedSessionLayerEvidence(authKeyID, sessionID); ok {
@ -1156,14 +1161,14 @@ func TestDurableLayerEvidenceRestoresAcrossEdgeRouterRestart(t *testing.T) {
t.Fatal(err)
}
state, rawLayer, evidenceMsgID := replacement.layerProfileRawEvidenceState()
if state.Profile != tg.LayerProfile225 || state.Origin != LayerProfileExplicit || rawLayer != 225 || evidenceMsgID != selectorMsgID {
if state.Profile != tlprofile.Profile225 || state.Origin != LayerProfileExplicit || rawLayer != 225 || evidenceMsgID != selectorMsgID {
t.Fatalf("restart seed = state:%#v raw:%d msg:%d", state, rawLayer, evidenceMsgID)
}
nakedMsgID := msgIDs.New(proto.MessageFromClient)
nakedPlan := &inboundPlan{items: []inboundItem{{
kind: inboundItemRPC, msgID: nakedMsgID,
body: exactOutboundLayerRPCBody(t, tg.LayerProfile225, &tg.MessagesGetHistoryRequest{
body: exactOutboundLayerRPCBody(t, tlprofile.Profile225, &tg.MessagesGetHistoryRequest{
Peer: &tg.InputPeerSelf{}, Limit: 1,
}),
layerProfileEvidenceFreshness: inboundLayerProfileEvidenceFresh,
@ -1173,7 +1178,7 @@ func TestDurableLayerEvidenceRestoresAcrossEdgeRouterRestart(t *testing.T) {
t.Fatal(err)
}
profile, profiled := restartedEdge.rpcResults.ExactAdmissionProfile(authKeyID, sessionID, nakedMsgID)
if len(nakedPlan.rpcTasks) != 1 || !profiled || profile != tg.LayerProfile225 {
if len(nakedPlan.rpcTasks) != 1 || !profiled || profile != tlprofile.Profile225 {
t.Fatalf("restart naked admission = tasks:%d item:%+v", len(nakedPlan.rpcTasks), nakedPlan.items[0])
}
}
@ -1207,7 +1212,7 @@ func TestPhysicalConnectionReadsDurableLayerOnceBeforeAdmissionHotPath(t *testin
t.Fatalf("connection seed GetSessionLayer calls=%d, want 1", got)
}
body := exactOutboundLayerRPCBody(t, tg.LayerProfile225, &tg.HelpGetConfigRequest{})
body := exactOutboundLayerRPCBody(t, tlprofile.Profile225, &tg.HelpGetConfigRequest{})
for i := 0; i < 64; i++ {
plan := &inboundPlan{items: []inboundItem{{
kind: inboundItemRPC, msgID: msgIDs.New(proto.MessageFromClient), body: body,
@ -1237,12 +1242,12 @@ func TestDurableExactSeedOutageKeepsFetchedAuthKeyDefaultServing(t *testing.T) {
t.Fatal(err)
}
initial := c.LayerProfileState()
if initial.Profile != tg.LayerProfile225 || initial.Origin != LayerProfileInherited {
if initial.Profile != tlprofile.Profile225 || initial.Origin != LayerProfileInherited {
t.Fatalf("outage seed discarded fetched auth-key default: %#v", initial)
}
msgIDs := proto.NewMessageIDGen(time.Now)
body := exactOutboundLayerRPCBody(t, tg.LayerProfile225, &tg.MessagesGetHistoryRequest{
body := exactOutboundLayerRPCBody(t, tlprofile.Profile225, &tg.MessagesGetHistoryRequest{
Peer: &tg.InputPeerSelf{}, Limit: 1,
})
for i := 0; i < 64; i++ {
@ -1280,12 +1285,12 @@ func TestBoundTempSeedOutageKeepsRawFetchedLayerServingCurrentConn(t *testing.T)
t.Fatal(err)
}
initial := c.LayerProfileState()
if initial.Profile != tg.LayerProfile225 || initial.Origin != LayerProfileInherited {
if initial.Profile != tlprofile.Profile225 || initial.Origin != LayerProfileInherited {
t.Fatalf("bound-temp outage discarded same-frame raw default: %#v", initial)
}
msgIDs := proto.NewMessageIDGen(time.Now)
body := exactOutboundLayerRPCBody(t, tg.LayerProfile225, &tg.MessagesGetHistoryRequest{
body := exactOutboundLayerRPCBody(t, tlprofile.Profile225, &tg.MessagesGetHistoryRequest{
Peer: &tg.InputPeerSelf{}, Limit: 1,
})
for i := 0; i < 64; i++ {
@ -1347,7 +1352,7 @@ func TestLiveConnectionKeepsFrozenLayerUntilItsOwnExplicitCorrection(t *testing.
t.Fatal(err)
}
oldPlan.close()
if state, raw, msgID := connA.layerProfileRawEvidenceState(); state.Profile != tg.LayerProfile225 || raw != 225 || msgID != oldMsgID {
if state, raw, msgID := connA.layerProfileRawEvidenceState(); state.Profile != tlprofile.Profile225 || raw != 225 || msgID != oldMsgID {
t.Fatalf("A initial profile = state:%#v raw:%d msg:%d", state, raw, msgID)
}
@ -1366,18 +1371,18 @@ func TestLiveConnectionKeepsFrozenLayerUntilItsOwnExplicitCorrection(t *testing.
oldNakedMsgID := msgIDs.New(proto.MessageFromClient)
oldNakedPlan := &inboundPlan{items: []inboundItem{{
kind: inboundItemRPC, msgID: oldNakedMsgID,
body: exactOutboundLayerRPCBody(t, tg.LayerProfile225, &tg.HelpGetConfigRequest{}),
body: exactOutboundLayerRPCBody(t, tlprofile.Profile225, &tg.HelpGetConfigRequest{}),
layerProfileEvidenceFreshness: inboundLayerProfileEvidenceFresh,
}}}
if err := edgeA.prepareInboundLayerRPCBatch(ctx, connA, oldNakedPlan); err != nil {
oldNakedPlan.close()
t.Fatal(err)
}
if state, raw, msgID := connA.layerProfileRawEvidenceState(); state.Profile != tg.LayerProfile225 || raw != 225 || msgID != oldMsgID {
if state, raw, msgID := connA.layerProfileRawEvidenceState(); state.Profile != tlprofile.Profile225 || raw != 225 || msgID != oldMsgID {
oldNakedPlan.close()
t.Fatalf("remote durable advance rewrote live A = state:%#v raw:%d msg:%d", state, raw, msgID)
}
if profile, ok := edgeA.rpcResults.ExactAdmissionProfile(authKeyID, sessionID, oldNakedMsgID); !ok || profile != tg.LayerProfile225 {
if profile, ok := edgeA.rpcResults.ExactAdmissionProfile(authKeyID, sessionID, oldNakedMsgID); !ok || profile != tlprofile.Profile225 {
oldNakedPlan.close()
t.Fatalf("old naked admission profile = (%d,%v), want 225", profile, ok)
}
@ -1388,7 +1393,7 @@ func TestLiveConnectionKeepsFrozenLayerUntilItsOwnExplicitCorrection(t *testing.
mismatchMsgID := msgIDs.New(proto.MessageFromClient)
mismatchPlan := &inboundPlan{items: []inboundItem{{
kind: inboundItemRPC, msgID: mismatchMsgID,
body: exactOutboundLayerRPCBody(t, tg.LayerProfile227, &tg.ChannelsJoinChannelRequest{
body: exactOutboundLayerRPCBody(t, tlprofile.Profile227, &tg.ChannelsJoinChannelRequest{
Channel: &tg.InputChannelEmpty{},
}),
layerProfileEvidenceFreshness: inboundLayerProfileEvidenceFresh,
@ -1402,7 +1407,7 @@ func TestLiveConnectionKeepsFrozenLayerUntilItsOwnExplicitCorrection(t *testing.
t.Fatalf("new naked grammar kind=%d, want admission error", mismatchPlan.items[0].kind)
}
mismatchPlan.close()
if state, raw, msgID := connA.layerProfileRawEvidenceState(); state.Profile != tg.LayerProfile225 || raw != 225 || msgID != oldMsgID {
if state, raw, msgID := connA.layerProfileRawEvidenceState(); state.Profile != tlprofile.Profile225 || raw != 225 || msgID != oldMsgID {
t.Fatalf("failed naked correction mutated A = state:%#v raw:%d msg:%d", state, raw, msgID)
}
@ -1418,10 +1423,10 @@ func TestLiveConnectionKeepsFrozenLayerUntilItsOwnExplicitCorrection(t *testing.
if err := edgeA.prepareInboundLayerRPCBatch(ctx, connA, correctionPlan); err != nil {
t.Fatal(err)
}
if state, raw, msgID := connA.layerProfileRawEvidenceState(); state.Profile != tg.LayerProfile227 || raw != 227 || msgID != correctionMsgID {
if state, raw, msgID := connA.layerProfileRawEvidenceState(); state.Profile != tlprofile.Profile227 || raw != 227 || msgID != correctionMsgID {
t.Fatalf("A explicit correction = state:%#v raw:%d msg:%d", state, raw, msgID)
}
if profile, ok := edgeA.rpcResults.ExactAdmissionProfile(authKeyID, sessionID, correctionMsgID); !ok || profile != tg.LayerProfile227 {
if profile, ok := edgeA.rpcResults.ExactAdmissionProfile(authKeyID, sessionID, correctionMsgID); !ok || profile != tlprofile.Profile227 {
t.Fatalf("corrected admission profile = (%d,%v), want 227", profile, ok)
}
if len(correctionPlan.rpcTasks) != 1 {
@ -1431,7 +1436,7 @@ func TestLiveConnectionKeepsFrozenLayerUntilItsOwnExplicitCorrection(t *testing.
t.Fatal(err)
}
requestProfile, resultProfile := handlerA.profiles()
if requestProfile != tg.LayerProfile227 || resultProfile != tg.LayerProfile227 {
if requestProfile != tlprofile.Profile227 || resultProfile != tlprofile.Profile227 {
t.Fatalf("dispatch/result profiles = %d/%d, want 227/227", requestProfile, resultProfile)
}
}
@ -1488,7 +1493,7 @@ func TestExactSessionProfileSurvivesUnregisterAndSeedsNakedReplay(t *testing.T)
if !ok || layer != 225 {
t.Fatalf("reconnect seed = (%d,%v), want (225,true)", layer, ok)
}
profile, ok := tg.ResolveLayerProfile(layer)
profile, ok := tlprofile.ResolveProfile(layer)
if !ok {
t.Fatalf("resolve retained profile %d", layer)
}
@ -1500,7 +1505,7 @@ func TestExactSessionProfileSurvivesUnregisterAndSeedsNakedReplay(t *testing.T)
if err != nil {
t.Fatalf("same-session naked replay admission: %v", err)
}
if method != "help.getConfig" || admitted.Call().Profile() != tg.LayerProfile225 {
if method != "help.getConfig" || admitted.Call().Profile() != tlprofile.Profile225 {
t.Fatalf("naked replay = method:%q profile:%d", method, admitted.Call().Profile())
}
}
@ -1523,7 +1528,7 @@ func TestSameAuthKeyNewSessionRequiresOwnLayerEvidence(t *testing.T) {
if err != nil {
t.Fatalf("Bob session layer 227: %v", err)
}
if bobRequest.Call().Profile() != tg.LayerProfile227 {
if bobRequest.Call().Profile() != tlprofile.Profile227 {
t.Fatalf("Bob profile = %d, want 227", bobRequest.Call().Profile())
}
@ -1532,7 +1537,7 @@ func TestSameAuthKeyNewSessionRequiresOwnLayerEvidence(t *testing.T) {
Peer: &tg.InputPeerSelf{},
Limit: 1,
}
naked228 := exactOutboundLayerRPCBody(t, tg.LayerProfile228, profileDependent)
naked228 := exactOutboundLayerRPCBody(t, tlprofile.Profile228, profileDependent)
if _, _, err := s.admitInboundLayerRPC(aliceConn, naked228); err == nil {
t.Fatal("new session inherited another session's Layer for naked application RPC")
}
@ -1551,7 +1556,7 @@ func TestSameAuthKeyNewSessionRequiresOwnLayerEvidence(t *testing.T) {
if err != nil {
t.Fatalf("Alice session own layer 228 evidence: %v", err)
}
if aliceRequest.Call().Profile() != tg.LayerProfile228 {
if aliceRequest.Call().Profile() != tlprofile.Profile228 {
t.Fatalf("Alice profile = %d, want 228", aliceRequest.Call().Profile())
}
if layer, ok := router.NegotiatedSessionLayer(authKeyID, bobSession); !ok || layer != 227 {
@ -1573,7 +1578,7 @@ func TestExactSessionRegistryAllowsOrderedExplicitCorrection(t *testing.T) {
// A later well-formed invokeWithLayer is authoritative correction, including
// when same-session recovery initially restored an older explicit profile.
c := &Conn{authKeyID: authKeyID, sessionID: sessionID, metrics: NopMetrics{}}
if err := c.SeedLayerProfile(tg.LayerProfile225); err != nil {
if err := c.SeedLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
wrapped := exactLayerRPCBody(t, &tg.InvokeWithLayerRequest{
@ -1584,10 +1589,10 @@ func TestExactSessionRegistryAllowsOrderedExplicitCorrection(t *testing.T) {
if err != nil {
t.Fatalf("profile correction: %v", err)
}
if request.Call().Profile() != tg.LayerProfile227 {
if request.Call().Profile() != tlprofile.Profile227 {
t.Fatalf("corrected request profile = %d", request.Call().Profile())
}
if got := c.LayerProfileState(); got.Profile != tg.LayerProfile227 || got.Origin != LayerProfileExplicit || got.Epoch < 2 {
if got := c.LayerProfileState(); got.Profile != tlprofile.Profile227 || got.Origin != LayerProfileExplicit || got.Epoch < 2 {
t.Fatalf("corrected Conn profile = %#v", got)
}
if layer, ok := router.NegotiatedSessionLayer(authKeyID, sessionID); !ok || layer != 227 {
@ -1599,17 +1604,17 @@ func TestRestoredExplicitProfileNakedFailureRequestsLayerCorrection(t *testing.T
router := rpc.New(rpc.Config{DC: 2}, rpc.Deps{}, zaptest.NewLogger(t), clock.System)
s := New(Options{DC: 2, LayerRPC: router})
c := &Conn{authKeyID: [8]byte{2, 2, 0, 2}, sessionID: 220227, metrics: NopMetrics{}}
if err := c.SeedLayerProfile(tg.LayerProfile225); err != nil {
if err := c.SeedLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
request := &tg.ChannelsJoinChannelRequest{Channel: &tg.InputChannelEmpty{}}
naked227 := exactOutboundLayerRPCBody(t, tg.LayerProfile227, request)
naked227 := exactOutboundLayerRPCBody(t, tlprofile.Profile227, request)
if _, _, err := s.admitInboundLayerRPC(c, naked227); err == nil {
t.Fatal("stale explicit profile admitted newer naked constructor")
} else if rpcErr := layerRPCAdmissionError(err); rpcErr.ErrorCode != 400 || rpcErr.ErrorMessage != "CONNECTION_LAYER_INVALID" {
t.Fatalf("stale explicit profile error = (%d,%q): %v", rpcErr.ErrorCode, rpcErr.ErrorMessage, err)
}
if got := c.LayerProfileState(); got.Profile != tg.LayerProfile225 || got.Origin != LayerProfileExplicit {
if got := c.LayerProfileState(); got.Profile != tlprofile.Profile225 || got.Origin != LayerProfileExplicit {
t.Fatalf("failed naked admission changed profile = %#v", got)
}
@ -1618,10 +1623,10 @@ func TestRestoredExplicitProfileNakedFailureRequestsLayerCorrection(t *testing.T
if err != nil {
t.Fatalf("explicit correction retry: %v", err)
}
if admitted.Call().Profile() != tg.LayerProfile227 {
if admitted.Call().Profile() != tlprofile.Profile227 {
t.Fatalf("corrected call profile = %d", admitted.Call().Profile())
}
if got := c.LayerProfileState(); got.Profile != tg.LayerProfile227 || got.Origin != LayerProfileExplicit || got.Epoch < 2 {
if got := c.LayerProfileState(); got.Profile != tlprofile.Profile227 || got.Origin != LayerProfileExplicit || got.Epoch < 2 {
t.Fatalf("corrected profile = %#v", got)
}
}
@ -1675,9 +1680,9 @@ func TestUnprofiledInvariantBindKeepsProfileUnknownAndReturnsExactBool(t *testin
if err := envelope.Decode(&bin.Buffer{Buf: encoded.body}); err != nil {
t.Fatal(err)
}
for _, profile := range []tg.LayerProfile{tg.LayerProfile225, tg.LayerProfile227} {
for _, profile := range []tlprofile.Profile{tlprofile.Profile225, tlprofile.Profile227} {
inner := bin.Buffer{Buf: append([]byte(nil), envelope.Result...)}
decoded, err := tg.DecodeLayer(profile, tg.LayerClassBoolType(), &inner)
decoded, err := tlprofile.DecodeObject(profile, &inner, tlprofile.Limits{})
if err != nil {
t.Fatalf("decode invariant Bool at layer %d: %v", profile, err)
}
@ -1688,13 +1693,13 @@ func TestUnprofiledInvariantBindKeepsProfileUnknownAndReturnsExactBool(t *testin
// The same immutable bytes remain legal if profile evidence arrives before
// the queued bind result is physically written.
if err := c.FreezeLayerProfile(tg.LayerProfile225); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
if err := validateOutboundLayerBinding(c, encoded); err != nil {
t.Fatalf("validate invariant result after layer 225 freeze: %v", err)
}
profiledBody := exactOutboundLayerRPCBody(t, tg.LayerProfile225, bind)
profiledBody := exactOutboundLayerRPCBody(t, tlprofile.Profile225, bind)
profiled, _, err := s.admitInboundLayerRPC(c, profiledBody)
if err != nil {
t.Fatal(err)
@ -1714,13 +1719,13 @@ func TestLayerRPCBatchCapacityKeepsExistingPendingReplay(t *testing.T) {
metrics: NopMetrics{},
}
c.startInboundRPCScheduler(s.rpcScheduler, 1, 8, time.Second)
if err := c.FreezeLayerProfile(tg.LayerProfile225); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
firstBody := exactLayerRPCBody(t, &tg.HelpGetConfigRequest{})
identityBuffer := &bin.Buffer{Buf: append([]byte(nil), firstBody...)}
admitted, err := router.AdmitLayer(tg.LayerProfile225, identityBuffer, tg.LayerDecodeLimits{})
admitted, err := router.AdmitLayer(tlprofile.Profile225, identityBuffer, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}
@ -1768,7 +1773,7 @@ func TestLayerRPCBatchCapacityAbortsRejectedRewrapOwner(t *testing.T) {
sessionID = int64(779)
msgID = int64(900)
)
identity := rpcFlightExactIdentity(t, tg.LayerProfile225, &tg.HelpGetConfigRequest{})
identity := rpcFlightExactIdentity(t, tlprofile.Profile225, &tg.HelpGetConfigRequest{})
claim, err := cache.AcquireIdentified(authKeyID, sessionID, msgID, identity)
if err != nil || claim.state != rpcResultAcquireOwner || claim.owner == nil {
t.Fatalf("rewrap owner = state:%d err:%v", claim.state, err)
@ -1819,7 +1824,7 @@ func TestLayerRPCDependencyGateUsesBusinessOutcome(t *testing.T) {
MsgID: test.dependency,
Query: &tg.HelpGetConfigRequest{},
})
admitted, err := router.AdmitLayer(tg.LayerProfile225, &bin.Buffer{Buf: body}, tg.LayerDecodeLimits{})
admitted, err := router.AdmitLayer(tlprofile.Profile225, &bin.Buffer{Buf: body}, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}
@ -1847,7 +1852,7 @@ func TestLayerRPCDependencyGateUsesBusinessOutcome(t *testing.T) {
MsgID: 300,
Query: &tg.HelpGetConfigRequest{},
})
missing, err := router.AdmitLayer(tg.LayerProfile225, &bin.Buffer{Buf: missingBody}, tg.LayerDecodeLimits{})
missing, err := router.AdmitLayer(tlprofile.Profile225, &bin.Buffer{Buf: missingBody}, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}
@ -1874,12 +1879,12 @@ func TestLayerRPCTimeoutMessageDistinguishesDependencyWait(t *testing.T) {
}
func TestLayerRPCAdmissionErrorUsesTypedUnknownClassification(t *testing.T) {
err := &tg.LayerCodecError{
err := &tlprofile.LayerCodecError{
Operation: "admit RPC request",
Profile: tg.LayerProfile225,
Profile: tlprofile.Profile225,
WireID: 0x01020304,
Reason: "wording may change",
Cause: tg.ErrLayerUnknownRPCMethod,
Cause: tlprofile.ErrUnknownRPCMethod,
}
rpcErr := layerRPCAdmissionError(err)
if rpcErr.ErrorCode != 501 || rpcErr.ErrorMessage != "NOT_IMPLEMENTED" {
@ -1888,11 +1893,11 @@ func TestLayerRPCAdmissionErrorUsesTypedUnknownClassification(t *testing.T) {
}
func TestLayerRPCAdmissionErrorDistinguishesUnknownAndInheritedProfiles(t *testing.T) {
profileRequired := &tg.LayerCodecError{Operation: "admit", Cause: tg.ErrLayerProfileRequired}
profileRequired := &tlprofile.LayerCodecError{Operation: "admit", Cause: tlprofile.ErrProfileRequired}
if rpcErr := layerRPCAdmissionError(profileRequired); rpcErr.ErrorCode != 400 || rpcErr.ErrorMessage != "CONNECTION_NOT_INITED" {
t.Fatalf("unknown profile admission = (%d,%q)", rpcErr.ErrorCode, rpcErr.ErrorMessage)
}
inherited := fmt.Errorf("%w: %w", errDefaultLayerAdmission, tg.ErrLayerUnknownRPCMethod)
inherited := fmt.Errorf("%w: %w", errDefaultLayerAdmission, tlprofile.ErrUnknownRPCMethod)
if rpcErr := layerRPCAdmissionError(inherited); rpcErr.ErrorCode != 400 || rpcErr.ErrorMessage != "CONNECTION_LAYER_INVALID" {
t.Fatalf("inherited profile admission = (%d,%q)", rpcErr.ErrorCode, rpcErr.ErrorMessage)
}

View file

@ -13,7 +13,7 @@ import (
"github.com/iamxvbaba/td/bin"
"github.com/iamxvbaba/td/mt"
"github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
)
type inboundItemKind uint8
@ -58,7 +58,7 @@ type inboundItem struct {
content bool
body []byte
payload any
admitted tg.LayerRequest
admitted tlprofile.Admission
method string
replayAfterSuccessfulDelivery func() error
layerProfileEvidenceFreshness inboundLayerProfileEvidenceFreshness
@ -108,7 +108,7 @@ func (p *inboundPlan) close() {
// advertise the same bytes to another connection while this plan still kept
// the old graph reachable until its caller returned.
for i := range p.items {
p.items[i].admitted = tg.LayerRequest{}
p.items[i].admitted = tlprofile.Admission{}
}
for i := range p.rpcTasks {
p.rpcTasks[i] = inboundRPC{}

View file

@ -14,6 +14,7 @@ import (
"github.com/iamxvbaba/td/tg"
"go.uber.org/zap/zaptest"
"github.com/iamxvbaba/td/tlprofile"
appfiles "telesrv/internal/app/files"
"telesrv/internal/rpc"
)
@ -102,17 +103,17 @@ func (h *failingReplayLayerRPC) PrepareAdmittedReplay(
int64,
int64,
uint64,
tg.LayerRequest,
tlprofile.Admission,
) (func() error, error) {
return nil, h.err
}
func (h *countingLayerRPCAdmission) AdmitLayer(profile tg.LayerProfile, b *bin.Buffer, limits tg.LayerDecodeLimits) (tg.LayerRequest, error) {
func (h *countingLayerRPCAdmission) AdmitLayer(profile tlprofile.Profile, b *bin.Buffer, limits tlprofile.Limits) (tlprofile.Admission, error) {
h.decodeCalls.Add(1)
return h.LayerRPCHandler.AdmitLayer(profile, b, limits)
}
func (h *countingLayerRPCAdmission) AdmitUnprofiled(b *bin.Buffer, limits tg.LayerDecodeLimits) (tg.LayerRequest, error) {
func (h *countingLayerRPCAdmission) AdmitUnprofiled(b *bin.Buffer, limits tlprofile.Limits) (tlprofile.Admission, error) {
h.decodeCalls.Add(1)
return h.LayerRPCHandler.AdmitUnprofiled(b, limits)
}
@ -166,13 +167,13 @@ func TestLayerRPCAdmissionTransfersOriginalReservationToFreshOwner(t *testing.T)
s := New(Options{DC: 2, LayerRPC: router})
c := &Conn{authKeyID: [8]byte{8, 3}, sessionID: 83, metrics: NopMetrics{}}
c.startInboundRPCScheduler(s.rpcScheduler, 1, 4, time.Second)
if err := c.FreezeLayerProfile(tg.LayerProfile225); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
bad := make([]byte, bin.Word)
bad[0], bad[1], bad[2], bad[3] = 0x04, 0x03, 0x02, 0x01
fresh := exactOutboundLayerRPCBody(t, tg.LayerProfile225, &tg.HelpGetConfigRequest{})
fresh := exactOutboundLayerRPCBody(t, tlprofile.Profile225, &tg.HelpGetConfigRequest{})
plan := &inboundPlan{items: []inboundItem{
{kind: inboundItemRPC, msgID: 100, body: bad},
{kind: inboundItemRPC, msgID: 104, body: fresh},
@ -211,13 +212,13 @@ func TestLayerRPCAdmissionPendingReplayReleasesProvisionalEntry(t *testing.T) {
s := New(Options{DC: 2, LayerRPC: router})
c := &Conn{authKeyID: [8]byte{8, 4}, sessionID: 84, metrics: NopMetrics{}}
c.startInboundRPCScheduler(s.rpcScheduler, 1, 4, time.Second)
if err := c.FreezeLayerProfile(tg.LayerProfile225); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
pendingBody := exactOutboundLayerRPCBody(t, tg.LayerProfile225, &tg.HelpGetConfigRequest{})
pendingBody := exactOutboundLayerRPCBody(t, tlprofile.Profile225, &tg.HelpGetConfigRequest{})
identityBuffer := &bin.Buffer{Buf: append([]byte(nil), pendingBody...)}
pendingRequest, err := router.AdmitLayer(tg.LayerProfile225, identityBuffer, tg.LayerDecodeLimits{})
pendingRequest, err := router.AdmitLayer(tlprofile.Profile225, identityBuffer, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}
@ -226,7 +227,7 @@ func TestLayerRPCAdmissionPendingReplayReleasesProvisionalEntry(t *testing.T) {
t.Fatalf("pending owner = %v, %v", pending.owner, err)
}
freshBody := exactOutboundLayerRPCBody(t, tg.LayerProfile225, &tg.HelpGetNearestDCRequest{})
freshBody := exactOutboundLayerRPCBody(t, tlprofile.Profile225, &tg.HelpGetNearestDCRequest{})
plan := &inboundPlan{items: []inboundItem{
{kind: inboundItemRPC, msgID: 100, body: pendingBody},
{kind: inboundItemRPC, msgID: 104, body: freshBody},
@ -253,12 +254,12 @@ func TestLayerRPCAdmissionCompletedReplayReleasesWholeProvisionalBatch(t *testin
s := New(Options{DC: 2, LayerRPC: router})
c := &Conn{authKeyID: [8]byte{8, 8}, sessionID: 88, metrics: NopMetrics{}}
c.startInboundRPCScheduler(s.rpcScheduler, 1, 2, time.Second)
if err := c.FreezeLayerProfile(tg.LayerProfile225); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
body := exactOutboundLayerRPCBody(t, tg.LayerProfile225, &tg.HelpGetConfigRequest{})
body := exactOutboundLayerRPCBody(t, tlprofile.Profile225, &tg.HelpGetConfigRequest{})
identityBuffer := &bin.Buffer{Buf: append([]byte(nil), body...)}
request, err := router.AdmitLayer(tg.LayerProfile225, identityBuffer, tg.LayerDecodeLimits{})
request, err := router.AdmitLayer(tlprofile.Profile225, identityBuffer, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}
@ -299,12 +300,12 @@ func TestLayerRPCAdmissionReplayPreparationErrorIsNotSilentlyDelivered(t *testin
}})
c := &Conn{authKeyID: [8]byte{8, 9}, sessionID: 89, metrics: NopMetrics{}}
c.startInboundRPCScheduler(s.rpcScheduler, 1, 2, time.Second)
if err := c.FreezeLayerProfile(tg.LayerProfile225); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
body := exactOutboundLayerRPCBody(t, tg.LayerProfile225, &tg.HelpGetConfigRequest{})
body := exactOutboundLayerRPCBody(t, tlprofile.Profile225, &tg.HelpGetConfigRequest{})
identityBuffer := &bin.Buffer{Buf: append([]byte(nil), body...)}
request, err := router.AdmitLayer(tg.LayerProfile225, identityBuffer, tg.LayerDecodeLimits{})
request, err := router.AdmitLayer(tlprofile.Profile225, identityBuffer, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}
@ -343,12 +344,12 @@ func TestLayerRPCAdmissionTransferredBatchClosesWithoutLeak(t *testing.T) {
s := New(Options{DC: 2, LayerRPC: router})
c := &Conn{authKeyID: [8]byte{8, 5}, sessionID: 85, metrics: NopMetrics{}}
c.startInboundRPCScheduler(s.rpcScheduler, 1, 2, time.Second)
if err := c.FreezeLayerProfile(tg.LayerProfile225); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
plan := &inboundPlan{items: []inboundItem{{
kind: inboundItemRPC, msgID: 100,
body: exactOutboundLayerRPCBody(t, tg.LayerProfile225, &tg.HelpGetConfigRequest{}),
body: exactOutboundLayerRPCBody(t, tlprofile.Profile225, &tg.HelpGetConfigRequest{}),
}}}
defer plan.close()
if err := s.prepareInboundLayerRPCBatch(context.Background(), c, plan); err != nil {
@ -378,10 +379,10 @@ func TestLayerRPCAdmissionTransferredBatchCommitsConservativeCharge(t *testing.T
s := New(Options{DC: 2, LayerRPC: router})
c := &Conn{authKeyID: [8]byte{8, 6}, sessionID: 86, metrics: NopMetrics{}}
c.startInboundRPCScheduler(s.rpcScheduler, 1, 2, time.Second)
if err := c.FreezeLayerProfile(tg.LayerProfile225); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
body := exactOutboundLayerRPCBody(t, tg.LayerProfile225, &tg.HelpGetConfigRequest{})
body := exactOutboundLayerRPCBody(t, tlprofile.Profile225, &tg.HelpGetConfigRequest{})
plan := &inboundPlan{items: []inboundItem{{kind: inboundItemRPC, msgID: 100, body: body}}}
defer plan.close()
if err := s.prepareInboundLayerRPCBatch(context.Background(), c, plan); err != nil {
@ -418,10 +419,10 @@ func TestLayerRPCAdmissionLocalDuplicateConsumesNoProvisionalEntry(t *testing.T)
s := New(Options{DC: 2, LayerRPC: router})
c := &Conn{authKeyID: [8]byte{8, 7}, sessionID: 87, metrics: NopMetrics{}}
c.startInboundRPCScheduler(s.rpcScheduler, 1, 2, time.Second)
if err := c.FreezeLayerProfile(tg.LayerProfile225); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
body := exactOutboundLayerRPCBody(t, tg.LayerProfile225, &tg.HelpGetConfigRequest{})
body := exactOutboundLayerRPCBody(t, tlprofile.Profile225, &tg.HelpGetConfigRequest{})
plan := &inboundPlan{items: []inboundItem{
{kind: inboundItemDuplicate, msgID: 96, body: body},
{kind: inboundItemRPC, msgID: 100, body: body},

View file

@ -13,13 +13,13 @@ import (
"github.com/iamxvbaba/td/bin"
"github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"go.uber.org/zap/zaptest"
)
type countingLayerRPCResult struct {
inner tg.LayerRPCResult
encodeCalls atomic.Int32
prepareCalls atomic.Int32
inner tlprofile.Result
encodeCalls atomic.Int32
}
const (
@ -27,15 +27,15 @@ const (
testChannelWireID228 uint32 = 0xd49f34c6
)
func testChannelWireID(profile tg.LayerProfile) uint32 {
if profile == tg.LayerProfile228 {
func testChannelWireID(profile tlprofile.Profile) uint32 {
if profile == tlprofile.Profile228 {
return testChannelWireID228
}
return testChannelWireID227
}
func testOtherChannelWireID(profile tg.LayerProfile) uint32 {
if profile == tg.LayerProfile228 {
func testOtherChannelWireID(profile tlprofile.Profile) uint32 {
if profile == tlprofile.Profile228 {
return testChannelWireID227
}
return testChannelWireID228
@ -55,28 +55,21 @@ func (r *countingLayerRPCResult) Encode(b *bin.Buffer) error {
return r.inner.Encode(b)
}
func (r *countingLayerRPCResult) Prepared() tg.LayerPreparedCall { return r.inner.Prepared() }
func (r *countingLayerRPCResult) Prepared() tlprofile.PreparedCall { return r.inner.Prepared() }
func (r *countingLayerRPCResult) WireInvariant() bool { return r.inner.WireInvariant() }
func (r *countingLayerRPCResult) Freeze() (tg.LayerFrozenResult, error) {
return r.inner.Freeze()
}
func (r *countingLayerRPCResult) Prepare() (tg.LayerPreparedResult, error) {
r.prepareCalls.Add(1)
return r.inner.Prepare()
}
func (r *countingLayerRPCResult) CanonicalValue() any { return r.inner.CanonicalValue() }
func TestExactLayerRPCResultEncodesDifferenceWithAdmittedCodec(t *testing.T) {
for _, profile := range []tg.LayerProfile{tg.LayerProfile225, tg.LayerProfile227, tg.LayerProfile228} {
for _, profile := range []tlprofile.Profile{tlprofile.Profile225, tlprofile.Profile227, tlprofile.Profile228} {
t.Run(fmt.Sprintf("layer_%d", profile), func(t *testing.T) {
testExactLayerRPCResultEncodesDifferenceWithAdmittedCodec(t, profile)
})
}
}
func testExactLayerRPCResultEncodesDifferenceWithAdmittedCodec(t *testing.T, profile tg.LayerProfile) {
func testExactLayerRPCResultEncodesDifferenceWithAdmittedCodec(t *testing.T, profile tlprofile.Profile) {
t.Helper()
diff := &tg.UpdatesDifference{
NewMessages: []tg.MessageClass{
@ -95,23 +88,21 @@ func testExactLayerRPCResultEncodesDifferenceWithAdmittedCodec(t *testing.T, pro
State: tg.UpdatesState{Pts: 2, Date: 1},
}
dispatcher := tg.NewServerDispatcher(nil)
dispatcher.OnUpdatesGetDifference(func(context.Context, *tg.UpdatesGetDifferenceRequest) (tg.UpdatesDifferenceClass, error) {
dispatcher := tlprofile.NewDispatcher()
if err := dispatcher.Register(tlprofile.SemanticMethodUpdatesGetDifference, func(context.Context, bin.Object) (any, error) {
return diff, nil
})
outbound, err := tg.PrepareLayerOutboundCall(profile, &tg.UpdatesGetDifferenceRequest{Pts: 1, Date: 1})
if err != nil {
}); err != nil {
t.Fatal(err)
}
var requestBody bin.Buffer
if err := outbound.Encode(&requestBody); err != nil {
if err := tlprofile.EncodeObject(profile, &tg.UpdatesGetDifferenceRequest{Pts: 1, Date: 1}, &requestBody); err != nil {
t.Fatal(err)
}
admitted, err := dispatcher.AdmitLayer(profile, &requestBody)
admitted, err := dispatcher.Admit(profile, &requestBody, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}
serverResult, err := dispatcher.DispatchAdmitted(context.Background(), admitted)
serverResult, err := dispatcher.Dispatch(context.Background(), admitted)
if err != nil {
t.Fatal(err)
}
@ -124,9 +115,9 @@ func testExactLayerRPCResultEncodesDifferenceWithAdmittedCodec(t *testing.T, pro
}
// Simulate an invokeWithLayer correction admitted while this handler was
// still running. The result must retain the request's admitted profile.
corrected := tg.LayerProfile227
if profile == tg.LayerProfile227 {
corrected = tg.LayerProfile225
corrected := tlprofile.Profile227
if profile == tlprofile.Profile227 {
corrected = tlprofile.Profile225
}
if err := c.FreezeLayerProfile(corrected); err != nil {
t.Fatal(err)
@ -136,14 +127,11 @@ func testExactLayerRPCResultEncodesDifferenceWithAdmittedCodec(t *testing.T, pro
if err != nil {
t.Fatalf("encode rpc_result: %v", err)
}
if got := counted.prepareCalls.Load(); got != 0 {
t.Fatalf("generated Prepare calls = %d, want 0; inbound workers must not snapshot result bytes", got)
}
if got := counted.encodeCalls.Load(); got != 1 {
t.Fatalf("generated Encode calls = %d, want exactly 1 under outbound admission", got)
}
if encoded.layer == nil || encoded.layer.profile != profile || encoded.layer.typ != admitted.Call().WireResultType() {
t.Fatalf("result binding = %#v, want profile %d and admitted result TypeRef", encoded.layer, profile)
if encoded.layer == nil || encoded.layer.profile != profile {
t.Fatalf("result binding = %#v, want profile %d", encoded.layer, profile)
}
if encoded.layer.kind != outboundLayerBindingRequest {
t.Fatalf("exact RPC result binding kind = %d, want request-bound", encoded.layer.kind)
@ -171,7 +159,7 @@ func testExactLayerRPCResultEncodesDifferenceWithAdmittedCodec(t *testing.T, pro
t.Fatalf("profile %d offline difference leaked channel constructor %#08x", profile, otherChannelID)
}
inner := bin.Buffer{Buf: rpcEnvelope.Result}
decoded, err := tg.DecodeLayer(profile, tg.LayerClassUpdatesDifferenceType(), &inner)
decoded, err := tlprofile.DecodeObject(profile, &inner, tlprofile.Limits{})
if err != nil {
t.Fatalf("decode exact difference: %v", err)
}
@ -199,27 +187,25 @@ func testExactLayerRPCResultEncodesDifferenceWithAdmittedCodec(t *testing.T, pro
}
func TestExactLayerRPCResultUsesHistoricalMethodResultType(t *testing.T) {
const profile = tg.LayerProfile225
dispatcher := tg.NewServerDispatcher(nil)
dispatcher.OnChannelsJoinChannel(func(context.Context, tg.InputChannelClass) (tg.MessagesChatInviteJoinResultClass, error) {
const profile = tlprofile.Profile225
dispatcher := tlprofile.NewDispatcher()
if err := dispatcher.Register(tlprofile.SemanticMethodChannelsJoinChannel, func(context.Context, bin.Object) (any, error) {
return &tg.MessagesChatInviteJoinResultOk{Updates: &tg.UpdatesTooLong{}}, nil
})
outbound, err := tg.PrepareLayerOutboundCall(profile, &tg.ChannelsJoinChannelRequest{Channel: &tg.InputChannelEmpty{}})
if err != nil {
}); err != nil {
t.Fatal(err)
}
var requestBody bin.Buffer
if err := outbound.Encode(&requestBody); err != nil {
if err := tlprofile.EncodeObject(profile, &tg.ChannelsJoinChannelRequest{Channel: &tg.InputChannelEmpty{}}, &requestBody); err != nil {
t.Fatal(err)
}
admitted, err := dispatcher.AdmitLayer(profile, &requestBody)
admitted, err := dispatcher.Admit(profile, &requestBody, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}
if admitted.Call().WireID() == tg.ChannelsJoinChannelRequestTypeID {
t.Fatal("historical request unexpectedly retained canonical method id")
}
serverResult, err := dispatcher.DispatchAdmitted(context.Background(), admitted)
serverResult, err := dispatcher.Dispatch(context.Background(), admitted)
if err != nil {
t.Fatal(err)
}
@ -237,7 +223,7 @@ func TestExactLayerRPCResultUsesHistoricalMethodResultType(t *testing.T) {
t.Fatal(err)
}
inner := bin.Buffer{Buf: rpcEnvelope.Result}
updates, err := tg.DecodeLayer(profile, tg.LayerClassUpdatesType(), &inner)
updates, err := tlprofile.DecodeObject(profile, &inner, tlprofile.Limits{})
if err != nil {
t.Fatalf("decode historical channels.joinChannel result: %v", err)
}
@ -251,7 +237,7 @@ func TestExactLayerRPCResultUsesHistoricalMethodResultType(t *testing.T) {
func TestProductionUnboundApplicationResultFailsClosedForLayer227(t *testing.T) {
c := &Conn{metrics: NopMetrics{}}
if err := c.FreezeLayerProfile(tg.LayerProfile227); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile227); err != nil {
t.Fatal(err)
}
encoded, err := (&Server{log: zaptest.NewLogger(t)}).encodeRPCResult(c, 12345, testLayerChannel())
@ -265,7 +251,7 @@ func TestProductionUnboundApplicationResultFailsClosedForLayer227(t *testing.T)
func TestProductionUnboundApplicationPushFailsClosedForLayer227(t *testing.T) {
c := &Conn{metrics: NopMetrics{}}
if err := c.FreezeLayerProfile(tg.LayerProfile227); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile227); err != nil {
t.Fatal(err)
}
frame, err := c.buildFrame(context.Background(), proto.MessageFromServer, testLayerChannelUpdatesValue(321), nil)

View file

@ -7,7 +7,7 @@ import (
"sync"
"testing"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
)
type countingInheritedLayerResolver struct {
@ -40,66 +40,66 @@ func TestConnLayerProfileUnknownFreezeAndIdempotence(t *testing.T) {
t.Fatalf("initial LayerProfile = (%d, %v), want (0, false)", profile, ok)
}
if err := c.FreezeLayerProfile(tg.LayerProfile225); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile225); err != nil {
t.Fatalf("freeze layer 225: %v", err)
}
if err := c.FreezeLayerProfile(tg.LayerProfile225); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile225); err != nil {
t.Fatalf("repeat freeze layer 225: %v", err)
}
if profile, ok := c.LayerProfile(); !ok || profile != tg.LayerProfile225 {
if profile, ok := c.LayerProfile(); !ok || profile != tlprofile.Profile225 {
t.Fatalf("LayerProfile = (%d, %v), want (225, true)", profile, ok)
}
}
func TestConnLayerProfileInheritedCanBeCorrectedExplicitly(t *testing.T) {
c := &Conn{}
if err := c.SeedInheritedLayerProfile(tg.LayerProfile225); err != nil {
if err := c.SeedInheritedLayerProfile(tlprofile.Profile225); err != nil {
t.Fatalf("seed inherited layer 225: %v", err)
}
initial := c.LayerProfileState()
if initial.Profile != tg.LayerProfile225 || initial.Origin != LayerProfileInherited || initial.Epoch != 1 {
if initial.Profile != tlprofile.Profile225 || initial.Origin != LayerProfileInherited || initial.Epoch != 1 {
t.Fatalf("initial inherited state = %#v", initial)
}
if err := c.SeedInheritedLayerProfile(tg.LayerProfile226); err != nil {
if err := c.SeedInheritedLayerProfile(tlprofile.Profile226); err != nil {
t.Fatalf("repeat inherited seed: %v", err)
}
if got := c.LayerProfileState(); got != initial {
t.Fatalf("second inherited seed replaced selected default: got %#v want %#v", got, initial)
}
if err := c.FreezeLayerProfile(tg.LayerProfile225); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile225); err != nil {
t.Fatalf("promote inherited evidence: %v", err)
}
promoted := c.LayerProfileState()
if promoted.Profile != tg.LayerProfile225 || promoted.Origin != LayerProfileExplicit || promoted.Epoch != initial.Epoch+1 {
if promoted.Profile != tlprofile.Profile225 || promoted.Origin != LayerProfileExplicit || promoted.Epoch != initial.Epoch+1 {
t.Fatalf("promoted explicit state = %#v", promoted)
}
if err := c.FreezeLayerProfile(tg.LayerProfile227); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile227); err != nil {
t.Fatalf("correct explicit layer: %v", err)
}
corrected := c.LayerProfileState()
if corrected.Profile != tg.LayerProfile227 || corrected.Origin != LayerProfileExplicit || corrected.Epoch != promoted.Epoch+1 {
if corrected.Profile != tlprofile.Profile227 || corrected.Origin != LayerProfileExplicit || corrected.Epoch != promoted.Epoch+1 {
t.Fatalf("corrected explicit state = %#v", corrected)
}
}
func TestConnSeedLayerProfile(t *testing.T) {
c := &Conn{}
if err := c.SeedLayerProfile(tg.LayerProfile226); err != nil {
if err := c.SeedLayerProfile(tlprofile.Profile226); err != nil {
t.Fatalf("seed layer 226: %v", err)
}
if err := c.SeedLayerProfile(tg.LayerProfile226); err != nil {
if err := c.SeedLayerProfile(tlprofile.Profile226); err != nil {
t.Fatalf("repeat seed layer 226: %v", err)
}
if err := c.FreezeLayerProfile(tg.LayerProfile226); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile226); err != nil {
t.Fatalf("freeze seeded layer 226: %v", err)
}
if profile, ok := c.LayerProfile(); !ok || profile != tg.LayerProfile226 {
if profile, ok := c.LayerProfile(); !ok || profile != tlprofile.Profile226 {
t.Fatalf("LayerProfile = (%d, %v), want (226, true)", profile, ok)
}
}
func TestConnLayerProfileRejectsUnsupported(t *testing.T) {
for _, profile := range []tg.LayerProfile{0, 219, 229} {
for _, profile := range []tlprofile.Profile{0, 219, 229} {
t.Run(fmt.Sprintf("layer_%d", profile), func(t *testing.T) {
c := &Conn{}
if err := c.FreezeLayerProfile(profile); !errors.Is(err, ErrLayerProfileUnsupported) {
@ -120,16 +120,16 @@ func TestConnLayerProfileConcurrentCorrectionsRemainAtomic(t *testing.T) {
c := &Conn{}
start := make(chan struct{})
errs := make([]error, goroutines)
profiles := make([]tg.LayerProfile, goroutines)
profiles := make([]tlprofile.Profile, goroutines)
var wg sync.WaitGroup
wg.Add(goroutines)
for i := range goroutines {
profile := tg.LayerProfile225
profile := tlprofile.Profile225
if i%2 != 0 {
profile = tg.LayerProfile227
profile = tlprofile.Profile227
}
profiles[i] = profile
go func(index int, requested tg.LayerProfile) {
go func(index int, requested tlprofile.Profile) {
defer wg.Done()
<-start
errs[index] = c.FreezeLayerProfile(requested)
@ -139,7 +139,7 @@ func TestConnLayerProfileConcurrentCorrectionsRemainAtomic(t *testing.T) {
wg.Wait()
state := c.LayerProfileState()
if state.Origin != LayerProfileExplicit || (state.Profile != tg.LayerProfile225 && state.Profile != tg.LayerProfile227) {
if state.Origin != LayerProfileExplicit || (state.Profile != tlprofile.Profile225 && state.Profile != tlprofile.Profile227) {
t.Fatalf("concurrent final state = %#v, want supported explicit contender", state)
}
if state.Epoch == 0 || state.Epoch > goroutines {
@ -154,23 +154,23 @@ func TestConnLayerProfileConcurrentCorrectionsRemainAtomic(t *testing.T) {
func TestConnLayerProfileEvidenceUsesClientMessageOrder(t *testing.T) {
c := &Conn{}
if err := c.seedOrderedLayerProfile(tg.LayerProfile225, 100); err != nil {
if err := c.seedOrderedLayerProfile(tlprofile.Profile225, 100); err != nil {
t.Fatal(err)
}
if applied, err := c.FreezeLayerProfileAt(tg.LayerProfile227, 104); err != nil || !applied {
if applied, err := c.FreezeLayerProfileAt(tlprofile.Profile227, 104); err != nil || !applied {
t.Fatalf("newer correction applied=%v err=%v", applied, err)
}
corrected := c.LayerProfileState()
if applied, err := c.FreezeLayerProfileAt(tg.LayerProfile225, 100); err != nil || applied {
if applied, err := c.FreezeLayerProfileAt(tlprofile.Profile225, 100); err != nil || applied {
t.Fatalf("old duplicate applied=%v err=%v", applied, err)
}
if got := c.LayerProfileState(); got != corrected {
t.Fatalf("old duplicate changed profile: got %#v want %#v", got, corrected)
}
if applied, err := c.FreezeLayerProfileAt(tg.LayerProfile225, 104); !errors.Is(err, ErrLayerProfileConflict) || applied {
if applied, err := c.FreezeLayerProfileAt(tlprofile.Profile225, 104); !errors.Is(err, ErrLayerProfileConflict) || applied {
t.Fatalf("same-msg conflicting evidence applied=%v err=%v", applied, err)
}
if applied, err := c.FreezeLayerProfileAt(tg.LayerProfile227, 108); err != nil || !applied {
if applied, err := c.FreezeLayerProfileAt(tlprofile.Profile227, 108); err != nil || !applied {
t.Fatalf("same-layer newer evidence applied=%v err=%v", applied, err)
}
state, msgID := c.layerProfileEvidenceState()
@ -185,10 +185,10 @@ func TestSessionManagerSeedsOnlyUnknownRawAuthKeyConnections(t *testing.T) {
unknown := &Conn{authKeyID: authKeyID, sessionID: 1}
explicit := &Conn{authKeyID: authKeyID, sessionID: 2}
inherited := &Conn{authKeyID: authKeyID, sessionID: 3}
if err := explicit.FreezeLayerProfile(tg.LayerProfile225); err != nil {
if err := explicit.FreezeLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
if err := inherited.SeedInheritedLayerProfile(tg.LayerProfile226); err != nil {
if err := inherited.SeedInheritedLayerProfile(tlprofile.Profile226); err != nil {
t.Fatal(err)
}
for _, c := range []*Conn{unknown, explicit, inherited} {
@ -200,13 +200,13 @@ func TestSessionManagerSeedsOnlyUnknownRawAuthKeyConnections(t *testing.T) {
if seeded := m.SeedInheritedLayerForRawAuthKey(authKeyID, 227); seeded != 1 {
t.Fatalf("seeded connections = %d, want 1", seeded)
}
if got := unknown.LayerProfileState(); got.Profile != tg.LayerProfile227 || got.Origin != LayerProfileInherited {
if got := unknown.LayerProfileState(); got.Profile != tlprofile.Profile227 || got.Origin != LayerProfileInherited {
t.Fatalf("unknown connection seed = %#v", got)
}
if got := explicit.LayerProfileState(); got.Profile != tg.LayerProfile225 || got.Origin != LayerProfileExplicit {
if got := explicit.LayerProfileState(); got.Profile != tlprofile.Profile225 || got.Origin != LayerProfileExplicit {
t.Fatalf("explicit connection was overwritten = %#v", got)
}
if got := inherited.LayerProfileState(); got.Profile != tg.LayerProfile226 || got.Origin != LayerProfileInherited {
if got := inherited.LayerProfileState(); got.Profile != tlprofile.Profile226 || got.Origin != LayerProfileInherited {
t.Fatalf("existing inherited connection was overwritten = %#v", got)
}
if seeded := m.SeedInheritedLayerForRawAuthKey(authKeyID, 229); seeded != 0 {
@ -220,10 +220,10 @@ func TestSessionManagerRefreshesInheritedRawKeyShadowAtBind(t *testing.T) {
unknown := &Conn{authKeyID: authKeyID, sessionID: 1}
inherited := &Conn{authKeyID: authKeyID, sessionID: 2}
explicit := &Conn{authKeyID: authKeyID, sessionID: 3}
if err := inherited.SeedInheritedLayerProfile(tg.LayerProfile225); err != nil {
if err := inherited.SeedInheritedLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
if err := explicit.FreezeLayerProfile(tg.LayerProfile225); err != nil {
if err := explicit.FreezeLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
for _, c := range []*Conn{unknown, inherited, explicit} {
@ -236,11 +236,11 @@ func TestSessionManagerRefreshesInheritedRawKeyShadowAtBind(t *testing.T) {
t.Fatalf("refreshed connections = %d, want 2", refreshed)
}
for name, c := range map[string]*Conn{"unknown": unknown, "inherited": inherited} {
if got := c.LayerProfileState(); got.Profile != tg.LayerProfile227 || got.Origin != LayerProfileInherited {
if got := c.LayerProfileState(); got.Profile != tlprofile.Profile227 || got.Origin != LayerProfileInherited {
t.Fatalf("%s refresh = %#v", name, got)
}
}
if got := explicit.LayerProfileState(); got.Profile != tg.LayerProfile225 || got.Origin != LayerProfileExplicit {
if got := explicit.LayerProfileState(); got.Profile != tlprofile.Profile225 || got.Origin != LayerProfileExplicit {
t.Fatalf("explicit evidence overwritten = %#v", got)
}
}
@ -251,10 +251,10 @@ func TestSessionManagerClearsOnlyInheritedRawKeyShadowAtBind(t *testing.T) {
inherited := &Conn{authKeyID: authKeyID, sessionID: 1}
explicit := &Conn{authKeyID: authKeyID, sessionID: 2}
unknown := &Conn{authKeyID: authKeyID, sessionID: 3}
if err := inherited.SeedInheritedLayerProfile(tg.LayerProfile225); err != nil {
if err := inherited.SeedInheritedLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
if err := explicit.seedOrderedLayerProfile(tg.LayerProfile227, 104); err != nil {
if err := explicit.seedOrderedLayerProfile(tlprofile.Profile227, 104); err != nil {
t.Fatal(err)
}
for _, c := range []*Conn{inherited, explicit, unknown} {
@ -269,7 +269,7 @@ func TestSessionManagerClearsOnlyInheritedRawKeyShadowAtBind(t *testing.T) {
if got := inherited.LayerProfileState(); got.Origin != LayerProfileUnknown || got.Profile != 0 {
t.Fatalf("inherited shadow after clear = %#v, want unknown", got)
}
if got := explicit.LayerProfileState(); got.Profile != tg.LayerProfile227 || got.Origin != LayerProfileExplicit {
if got := explicit.LayerProfileState(); got.Profile != tlprofile.Profile227 || got.Origin != LayerProfileExplicit {
t.Fatalf("explicit evidence was cleared = %#v", got)
}
if state, msgID := explicit.layerProfileEvidenceState(); state.Origin != LayerProfileExplicit || msgID != 104 {
@ -292,10 +292,10 @@ func TestSessionManagerSeedsUnknownSessionsAcrossBusinessAuthKey(t *testing.T) {
second := &Conn{authKeyID: rawTwo, sessionID: 2}
explicit := &Conn{authKeyID: rawTwo, sessionID: 3}
inherited := &Conn{authKeyID: rawOne, sessionID: 4}
if err := explicit.FreezeLayerProfile(tg.LayerProfile225); err != nil {
if err := explicit.FreezeLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
if err := inherited.SeedInheritedLayerProfile(tg.LayerProfile225); err != nil {
if err := inherited.SeedInheritedLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
for _, c := range []*Conn{first, second, explicit, inherited} {
@ -308,14 +308,14 @@ func TestSessionManagerSeedsUnknownSessionsAcrossBusinessAuthKey(t *testing.T) {
t.Fatalf("business auth-key seeded=%d, want 2", seeded)
}
for name, c := range map[string]*Conn{"first": first, "second": second} {
if got := c.LayerProfileState(); got.Profile != tg.LayerProfile227 || got.Origin != LayerProfileInherited {
if got := c.LayerProfileState(); got.Profile != tlprofile.Profile227 || got.Origin != LayerProfileInherited {
t.Fatalf("%s business default = %#v", name, got)
}
}
if got := explicit.LayerProfileState(); got.Profile != tg.LayerProfile225 || got.Origin != LayerProfileExplicit {
if got := explicit.LayerProfileState(); got.Profile != tlprofile.Profile225 || got.Origin != LayerProfileExplicit {
t.Fatalf("business seed overwrote explicit = %#v", got)
}
if got := inherited.LayerProfileState(); got.Profile != tg.LayerProfile225 || got.Origin != LayerProfileInherited {
if got := inherited.LayerProfileState(); got.Profile != tlprofile.Profile225 || got.Origin != LayerProfileInherited {
t.Fatalf("business seed overwrote inherited = %#v", got)
}
}
@ -325,7 +325,7 @@ func TestSessionManagerExplicitLayerEvidenceUsesLiveExactSession(t *testing.T) {
authKeyID := [8]byte{2, 2, 9}
const sessionID = int64(229)
c := &Conn{authKeyID: authKeyID, sessionID: sessionID}
if err := c.seedOrderedLayerProfile(tg.LayerProfile226, 1234); err != nil {
if err := c.seedOrderedLayerProfile(tlprofile.Profile226, 1234); err != nil {
t.Fatal(err)
}
if err := m.Register(c); err != nil {
@ -336,7 +336,7 @@ func TestSessionManagerExplicitLayerEvidenceUsesLiveExactSession(t *testing.T) {
}
inherited := &Conn{authKeyID: authKeyID, sessionID: sessionID + 1}
if err := inherited.SeedInheritedLayerProfile(tg.LayerProfile227); err != nil {
if err := inherited.SeedInheritedLayerProfile(tlprofile.Profile227); err != nil {
t.Fatal(err)
}
if err := m.Register(inherited); err != nil {
@ -353,10 +353,10 @@ func TestSessionManagerExplicitLayerEvidenceChoosesNewestActiveOrClaim(t *testin
const sessionID = int64(230)
active := &Conn{authKeyID: authKeyID, sessionID: sessionID}
claim := &Conn{authKeyID: authKeyID, sessionID: sessionID}
if err := active.seedOrderedLayerProfile(tg.LayerProfile225, 100); err != nil {
if err := active.seedOrderedLayerProfile(tlprofile.Profile225, 100); err != nil {
t.Fatal(err)
}
if err := claim.seedOrderedLayerProfile(tg.LayerProfile227, 104); err != nil {
if err := claim.seedOrderedLayerProfile(tlprofile.Profile227, 104); err != nil {
t.Fatal(err)
}
key := sessionKey{authKeyID: authKeyID, sessionID: sessionID}
@ -384,15 +384,15 @@ func TestOrderedSessionLayerBroadcastConvergesAcrossPhysicalGenerations(t *testi
m.bySession[sessionKey{authKeyID: authKeyID, sessionID: sessionID}] = current
m.mu.Unlock()
if applied, err := m.ApplyOrderedLayerProfileForSession(oldPhysical, authKeyID, sessionID, tg.LayerProfile227, 300); err != nil || applied != 2 {
if applied, err := m.ApplyOrderedLayerProfileForSession(oldPhysical, authKeyID, sessionID, tlprofile.Profile227, 300); err != nil || applied != 2 {
t.Fatalf("newer broadcast applied=%d err=%v", applied, err)
}
if applied, err := m.ApplyOrderedLayerProfileForSession(oldPhysical, authKeyID, sessionID, tg.LayerProfile225, 200); err != nil || applied != 0 {
if applied, err := m.ApplyOrderedLayerProfileForSession(oldPhysical, authKeyID, sessionID, tlprofile.Profile225, 200); err != nil || applied != 0 {
t.Fatalf("delayed older broadcast applied=%d err=%v", applied, err)
}
for name, c := range map[string]*Conn{"old": oldPhysical, "current": current} {
state, msgID := c.layerProfileEvidenceState()
if state.Profile != tg.LayerProfile227 || state.Origin != LayerProfileExplicit || msgID != 300 {
if state.Profile != tlprofile.Profile227 || state.Origin != LayerProfileExplicit || msgID != 300 {
t.Fatalf("%s physical state = %#v msgID:%d", name, state, msgID)
}
}
@ -409,7 +409,7 @@ func TestInitialProfileSeedAvoidsPermanentKeyResolverAndPrefersPermForTemp(t *te
if resolver.calls != 0 {
t.Fatalf("permanent resolver calls = %d, want 0", resolver.calls)
}
if got := c.LayerProfileState(); got.Profile != tg.LayerProfile227 || got.Origin != LayerProfileInherited {
if got := c.LayerProfileState(); got.Profile != tlprofile.Profile227 || got.Origin != LayerProfileInherited {
t.Fatalf("permanent seed = %#v", got)
}
})
@ -424,7 +424,7 @@ func TestInitialProfileSeedAvoidsPermanentKeyResolverAndPrefersPermForTemp(t *te
if resolver.calls != 1 {
t.Fatalf("temporary resolver calls = %d, want 1", resolver.calls)
}
if got := c.LayerProfileState(); got.Profile != tg.LayerProfile227 || got.Origin != LayerProfileInherited {
if got := c.LayerProfileState(); got.Profile != tlprofile.Profile227 || got.Origin != LayerProfileInherited {
t.Fatalf("temporary canonical seed = %#v", got)
}
})
@ -465,7 +465,7 @@ func TestInitialProfileSeedRestoresOrderedExactSessionEvidence(t *testing.T) {
t.Fatal(err)
}
state, msgID := c.layerProfileEvidenceState()
if state.Profile != tg.LayerProfile226 || state.Origin != LayerProfileExplicit || msgID != resolver.msgID {
if state.Profile != tlprofile.Profile226 || state.Origin != LayerProfileExplicit || msgID != resolver.msgID {
t.Fatalf("ordered exact seed = state:%#v msgID:%d", state, msgID)
}
}
@ -487,10 +487,10 @@ func TestInheritedLayerResolverAvailabilityUsesOnlySupportedRawTempShadow(t *tes
for _, tt := range []struct {
name string
fetchedLayer int
wantProfile tg.LayerProfile
wantProfile tlprofile.Profile
wantOrigin LayerProfileOrigin
}{
{name: "supported raw shadow", fetchedLayer: 225, wantProfile: tg.LayerProfile225, wantOrigin: LayerProfileInherited},
{name: "supported raw shadow", fetchedLayer: 225, wantProfile: tlprofile.Profile225, wantOrigin: LayerProfileInherited},
{name: "future raw shadow stays unknown", fetchedLayer: 229, wantOrigin: LayerProfileUnknown},
} {
t.Run(tt.name, func(t *testing.T) {
@ -529,7 +529,7 @@ func TestActivationClaimRecheckClosesTempBindLayerRace(t *testing.T) {
if err := s.refreshActivatedInheritedLayerProfile(context.Background(), c, 225); err != nil {
t.Fatal(err)
}
if got := c.LayerProfileState(); got.Profile != tg.LayerProfile227 || got.Origin != LayerProfileInherited {
if got := c.LayerProfileState(); got.Profile != tlprofile.Profile227 || got.Origin != LayerProfileInherited {
t.Fatalf("post-claim permanent recheck = %#v", got)
}
})
@ -537,7 +537,7 @@ func TestActivationClaimRecheckClosesTempBindLayerRace(t *testing.T) {
t.Run("claim wins before bind refresh", func(t *testing.T) {
m := NewSessionManager(nil)
c := &Conn{authKeyID: authKeyID, sessionID: 402, authKeyExpiresAt: 1_900_000_000}
if err := c.SeedInheritedLayerProfile(tg.LayerProfile225); err != nil {
if err := c.SeedInheritedLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
if err := m.BeginActivation(c); err != nil {
@ -547,7 +547,7 @@ func TestActivationClaimRecheckClosesTempBindLayerRace(t *testing.T) {
if refreshed := m.RefreshInheritedLayerForRawAuthKey(authKeyID, 227); refreshed != 1 {
t.Fatalf("bind refresh count = %d, want 1", refreshed)
}
if got := c.LayerProfileState(); got.Profile != tg.LayerProfile227 || got.Origin != LayerProfileInherited {
if got := c.LayerProfileState(); got.Profile != tlprofile.Profile227 || got.Origin != LayerProfileInherited {
t.Fatalf("claim-visible bind refresh = %#v", got)
}
})
@ -556,7 +556,7 @@ func TestActivationClaimRecheckClosesTempBindLayerRace(t *testing.T) {
resolver := &countingInheritedLayerResolver{layer: 229, found: true}
s := &Server{layerRPC: resolver}
c := &Conn{authKeyID: authKeyID, sessionID: 403, authKeyExpiresAt: 1_900_000_000}
if err := c.SeedInheritedLayerProfile(tg.LayerProfile225); err != nil {
if err := c.SeedInheritedLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
if err := s.refreshActivatedInheritedLayerProfile(context.Background(), c, 225); err != nil {
@ -574,7 +574,7 @@ func TestActivationClaimRecheckClosesTempBindLayerRace(t *testing.T) {
if err := s.refreshActivatedInheritedLayerProfile(context.Background(), c, 225); err != nil {
t.Fatal(err)
}
if got := c.LayerProfileState(); got.Profile != tg.LayerProfile225 || got.Origin != LayerProfileInherited {
if got := c.LayerProfileState(); got.Profile != tlprofile.Profile225 || got.Origin != LayerProfileInherited {
t.Fatalf("availability recheck lost raw shadow = %#v", got)
}
})
@ -583,7 +583,7 @@ func TestActivationClaimRecheckClosesTempBindLayerRace(t *testing.T) {
resolver := &countingInheritedLayerResolver{err: errors.New("invalid binding identity")}
s := &Server{layerRPC: resolver}
c := &Conn{authKeyID: authKeyID, sessionID: 405, authKeyExpiresAt: 1_900_000_000}
if err := c.SeedInheritedLayerProfile(tg.LayerProfile225); err != nil {
if err := c.SeedInheritedLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
if err := s.refreshActivatedInheritedLayerProfile(context.Background(), c, 225); err != nil {

View file

@ -7,10 +7,10 @@ import (
"github.com/iamxvbaba/td/bin"
"github.com/iamxvbaba/td/mt"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tgerr"
"go.uber.org/zap"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/observability/dbtrace"
"telesrv/internal/postresponse"
)
@ -20,8 +20,8 @@ import (
// the inbound worker. The only Encode call happens later under outbound encode
// and retained-byte admission.
type layerRPCResultEncoder struct {
call tg.LayerCall
result tg.LayerRPCResult
call tlprofile.Call
result tlprofile.Result
}
func (e *layerRPCResultEncoder) Encode(b *bin.Buffer) error {
@ -40,7 +40,6 @@ func (e *layerRPCResultEncoder) exactLayerRPCResultBinding() outboundLayerBindin
}
return outboundLayerBinding{
profile: e.call.Profile(),
typ: e.call.WireResultType(),
wireInvariant: e.call.WireInvariant(),
kind: outboundLayerBindingRequest,
}
@ -55,7 +54,7 @@ type exactLayerRPCResultEncoder interface {
// hook onto the generated result codec. The hook may still exercise the old
// scheduling API, but it no longer has a canonical-bytes escape hatch.
type legacyTestRPCResultEncoder struct {
call tg.LayerCall
call tlprofile.Call
result bin.Encoder
}
@ -63,7 +62,7 @@ func (e *legacyTestRPCResultEncoder) Encode(b *bin.Buffer) error {
if e == nil || e.result == nil {
return errors.New("nil legacy test RPC result")
}
return e.call.EncodeResult(e.result, b)
return e.result.Encode(b)
}
func (e *legacyTestRPCResultEncoder) exactLayerRPCResultBinding() outboundLayerBinding {
@ -72,7 +71,6 @@ func (e *legacyTestRPCResultEncoder) exactLayerRPCResultBinding() outboundLayerB
}
return outboundLayerBinding{
profile: e.call.Profile(),
typ: e.call.WireResultType(),
wireInvariant: e.call.WireInvariant(),
kind: outboundLayerBindingRequest,
}
@ -85,7 +83,7 @@ var errLayerRPCResultIdentityMismatch = errors.New("layer RPC result does not ma
// result capability created from this exact admission; accepting a result from
// another request would pair the wrong result TypeRef/profile with this
// flight/cache identity even when both methods happen to share a Go type.
func bindAdmittedLayerRPCResult(request tg.LayerRequest, result tg.LayerRPCResult) (*layerRPCResultEncoder, error) {
func bindAdmittedLayerRPCResult(request tlprofile.Admission, result tlprofile.Result) (*layerRPCResultEncoder, error) {
if result == nil {
return nil, nil
}
@ -101,7 +99,7 @@ func (s *Server) newInboundLayerRPCTask(
admissionSeq uint64,
method string,
profileEvidenceFresh bool,
request tg.LayerRequest,
request tlprofile.Admission,
dependencies layerRPCDependencySet,
owner *rpcResultOwnerLease,
) inboundRPC {
@ -202,7 +200,7 @@ func (s *Server) handleAdmittedLayerRPC(
msgID int64,
admissionSeq uint64,
method string,
request tg.LayerRequest,
request tlprofile.Admission,
owner *rpcResultOwnerLease,
) error {
if s.layerRPC == nil {

View file

@ -11,24 +11,25 @@ import (
"github.com/iamxvbaba/td/mt"
"github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
)
// preparedOnlyLayerRPCResult is intentionally incapable of encoding. The
// binding guard must reject it solely from immutable admission identity before
// any result method other than Prepared can be observed.
type preparedOnlyLayerRPCResult struct {
tg.LayerRPCResult
prepared tg.LayerPreparedCall
tlprofile.Result
prepared tlprofile.PreparedCall
}
func (r *preparedOnlyLayerRPCResult) Prepared() tg.LayerPreparedCall { return r.prepared }
func (r *preparedOnlyLayerRPCResult) Prepared() tlprofile.PreparedCall { return r.prepared }
func TestBindAdmittedLayerRPCResultRequiresExactRequestIdentity(t *testing.T) {
dispatcher := tg.NewServerDispatcher(nil)
admit := func(request bin.Encoder) tg.LayerRequest {
dispatcher := tlprofile.NewDispatcher()
admit := func(request bin.Encoder) tlprofile.Admission {
t.Helper()
body := &bin.Buffer{Buf: exactLayerRPCBody(t, request)}
admitted, err := dispatcher.AdmitLayer(tg.LayerProfile227, body)
admitted, err := dispatcher.Admit(tlprofile.Profile227, body, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}
@ -53,7 +54,7 @@ func TestBindAdmittedLayerRPCResultRequiresExactRequestIdentity(t *testing.T) {
type mismatchedProjectionLayerRPC struct {
*admissionOnlyLayerRPC
result tg.LayerRPCResult
result tlprofile.Result
calls atomic.Int32
}
@ -63,18 +64,18 @@ func (h *mismatchedProjectionLayerRPC) DispatchAdmitted(
int64,
int64,
uint64,
tg.LayerRequest,
) (tg.LayerRPCResult, string, error) {
tlprofile.Admission,
) (tlprofile.Result, string, error) {
h.calls.Add(1)
return h.result, "help.getConfig", nil
}
func TestProjectionFailureCachesInternalWithoutRepeatingBusiness(t *testing.T) {
dispatcher := tg.NewServerDispatcher(nil)
admit := func(request bin.Encoder) tg.LayerRequest {
dispatcher := tlprofile.NewDispatcher()
admit := func(request bin.Encoder) tlprofile.Admission {
t.Helper()
body := &bin.Buffer{Buf: exactLayerRPCBody(t, request)}
admitted, err := dispatcher.AdmitLayer(tg.LayerProfile227, body)
admitted, err := dispatcher.Admit(tlprofile.Profile227, body, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}
@ -93,7 +94,7 @@ func TestProjectionFailureCachesInternalWithoutRepeatingBusiness(t *testing.T) {
const reqMsgID = int64(410100)
claim, err := s.rpcResults.AcquireLayerIdentified(
c.authKeyID, c.sessionID, reqMsgID,
tg.LayerProfile227, request.Prepared().Identity(),
tlprofile.Profile227, request.Prepared().Identity(),
)
if err != nil || claim.owner == nil {
t.Fatalf("owner acquisition err=%v", err)
@ -110,7 +111,7 @@ func TestProjectionFailureCachesInternalWithoutRepeatingBusiness(t *testing.T) {
for {
completed, err = s.rpcResults.AcquireLayerIdentified(
c.authKeyID, c.sessionID, reqMsgID,
tg.LayerProfile227, request.Prepared().Identity(),
tlprofile.Profile227, request.Prepared().Identity(),
)
if err == nil && completed.state == rpcResultAcquireCompleted {
break
@ -142,7 +143,7 @@ func TestProjectionFailureCachesInternalWithoutRepeatingBusiness(t *testing.T) {
// success.
replay, err := s.rpcResults.AcquireLayerIdentified(
c.authKeyID, c.sessionID, reqMsgID,
tg.LayerProfile227, request.Prepared().Identity(),
tlprofile.Profile227, request.Prepared().Identity(),
)
if err != nil || replay.state != rpcResultAcquireCompleted || replay.encoded != completed.encoded {
t.Fatalf("projection replay = state:%d err:%v", replay.state, err)

View file

@ -8,6 +8,7 @@ import (
"github.com/iamxvbaba/td/bin"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
)
var (
@ -36,8 +37,7 @@ const (
)
type outboundLayerBinding struct {
profile tg.LayerProfile
typ *tg.LayerTypeRef
profile tlprofile.Profile
wireInvariant bool
kind outboundLayerBindingKind
// epoch is required for proactive updates. Zero is accepted only for older
@ -52,26 +52,26 @@ type preparedLayerUpdates struct {
}
// layerUpdatesFanout is one immutable canonical Updates snapshot plus a
// request-scoped cache of exact prepared bytes. FreezeLayer and
// PrepareFrozenLayer are the same generated TypeRef codec used by RPC results
// request-scoped cache of exact prepared bytes. FreezeObject and
// FrozenObject.Prepare use the same sparse TypeRef execution plans as RPC results
// and differences; this type adds only fan-out singleflight and ownership.
type layerUpdatesFanout struct {
frozen tg.LayerFrozen[tg.UpdatesClass]
frozen *tlprofile.FrozenObject
size int
mu sync.Mutex
prepared map[tg.LayerProfile]*preparedLayerUpdates
prepared map[tlprofile.Profile]*preparedLayerUpdates
}
func newLayerUpdatesFanout(value tg.UpdatesClass) (*layerUpdatesFanout, error) {
frozen, err := tg.FreezeLayer(tg.LayerClassUpdatesType(), value)
frozen, err := tlprofile.FreezeObject(value)
if err != nil {
return nil, fmt.Errorf("freeze exact layer updates: %w", err)
}
return &layerUpdatesFanout{
frozen: frozen,
size: frozen.CanonicalSize(),
prepared: make(map[tg.LayerProfile]*preparedLayerUpdates),
prepared: make(map[tlprofile.Profile]*preparedLayerUpdates),
}, nil
}
@ -107,7 +107,7 @@ func (u *layerUpdatesFanout) prepareForConn(ctx context.Context, c *Conn) (*enco
return &encoded, nil
}
func (u *layerUpdatesFanout) prepare(ctx context.Context, profile tg.LayerProfile) (*encodedOutboundMessage, error) {
func (u *layerUpdatesFanout) prepare(ctx context.Context, profile tlprofile.Profile) (*encodedOutboundMessage, error) {
if ctx == nil {
ctx = context.Background()
}
@ -149,7 +149,7 @@ func (u *layerUpdatesFanout) prepare(ctx context.Context, profile tg.LayerProfil
return entry.encoded, entry.err
}
func (u *layerUpdatesFanout) discardPrepared(profile tg.LayerProfile, encoded *encodedOutboundMessage) {
func (u *layerUpdatesFanout) discardPrepared(profile tlprofile.Profile, encoded *encodedOutboundMessage) {
if u == nil || encoded == nil {
return
}
@ -172,18 +172,13 @@ func (u *layerUpdatesFanout) discardPrepared(profile tg.LayerProfile, encoded *e
func prepareFrozenLayerUpdatesContext(
ctx context.Context,
profile tg.LayerProfile,
frozen tg.LayerFrozen[tg.UpdatesClass],
profile tlprofile.Profile,
frozen *tlprofile.FrozenObject,
) (*encodedOutboundMessage, error) {
var encoded *encodedOutboundMessage
err := withOutboundEncodeSlot(ctx, nil, func() error {
prepared, err := tg.PrepareFrozenLayer(profile, frozen)
if err != nil {
return err
}
var body bin.Buffer
typ := tg.LayerClassUpdatesType()
if err := prepared.Encode(profile, typ, &body); err != nil {
if err := frozen.Encode(profile, &body); err != nil {
return err
}
id, err := body.PeekID()
@ -192,7 +187,7 @@ func prepareFrozenLayerUpdatesContext(
}
encoded = &encodedOutboundMessage{
body: body.Copy(), typeID: id,
layer: &outboundLayerBinding{profile: profile, typ: prepared.TypeRef()},
layer: &outboundLayerBinding{profile: profile},
}
return nil
})
@ -206,9 +201,6 @@ func validateOutboundLayerBinding(c *Conn, encoded *encodedOutboundMessage) erro
if encoded == nil || encoded.layer == nil {
return nil
}
if encoded.layer.typ == nil {
return errors.New("outbound exact layer TypeRef is nil")
}
if encoded.layer.wireInvariant || encoded.layer.kind == outboundLayerBindingRequest {
return nil
}

View file

@ -11,6 +11,7 @@ import (
"github.com/iamxvbaba/td/bin"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
)
type epochBlockingTransport struct {
@ -65,7 +66,7 @@ func testLayerChannelUpdatesValue(expires int) tg.UpdatesClass {
}
}
func testConnWithLayerProfile(t *testing.T, profile tg.LayerProfile) *Conn {
func testConnWithLayerProfile(t *testing.T, profile tlprofile.Profile) *Conn {
t.Helper()
c := &Conn{}
if err := c.FreezeLayerProfile(profile); err != nil {
@ -79,7 +80,7 @@ func TestLayerUpdatesFanoutPreparesExactMixedProfiles(t *testing.T) {
if err != nil {
t.Fatalf("freeze updates: %v", err)
}
for _, profile := range []tg.LayerProfile{tg.LayerProfile225, tg.LayerProfile227, tg.LayerProfile228} {
for _, profile := range []tlprofile.Profile{tlprofile.Profile225, tlprofile.Profile227, tlprofile.Profile228} {
t.Run(fmt.Sprintf("layer_%d", profile), func(t *testing.T) {
c := testConnWithLayerProfile(t, profile)
encoded, err := fanout.prepareForConn(context.Background(), c)
@ -90,7 +91,7 @@ func TestLayerUpdatesFanoutPreparesExactMixedProfiles(t *testing.T) {
t.Fatalf("binding = %#v, want profile %d", encoded.layer, profile)
}
input := bin.Buffer{Buf: encoded.body}
decoded, err := tg.DecodeLayer(profile, tg.LayerClassUpdatesType(), &input)
decoded, err := tlprofile.DecodeObject(profile, &input, tlprofile.Limits{})
if err != nil {
t.Fatalf("decode profile %d: %v", profile, err)
}
@ -121,7 +122,7 @@ func TestLayerUpdatesFanoutFreezesDefensivelyAndSharesPreparedProfile(t *testing
}
value.(*tg.UpdateShort).Update.(*tg.UpdateUserStatus).Status.(*tg.UserStatusOnline).Expires = 999
c := testConnWithLayerProfile(t, tg.LayerProfile225)
c := testConnWithLayerProfile(t, tlprofile.Profile225)
const workers = 16
prepared := make([]*encodedOutboundMessage, workers)
prepareErrs := make([]error, workers)
@ -149,7 +150,7 @@ func TestLayerUpdatesFanoutFreezesDefensivelyAndSharesPreparedProfile(t *testing
}
input := bin.Buffer{Buf: prepared[0].body}
decoded, err := tg.DecodeLayer(tg.LayerProfile225, tg.LayerClassUpdatesType(), &input)
decoded, err := tlprofile.DecodeObject(tlprofile.Profile225, &input, tlprofile.Limits{})
if err != nil {
t.Fatalf("decode frozen value: %v", err)
}
@ -165,7 +166,7 @@ func TestLayerUpdatesEpochBecomesStaleWithoutRetiringProfile(t *testing.T) {
t.Fatal(err)
}
c := &Conn{}
if err := c.SeedInheritedLayerProfile(tg.LayerProfile225); err != nil {
if err := c.SeedInheritedLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
encoded, err := fanout.prepareForConn(context.Background(), c)
@ -173,14 +174,14 @@ func TestLayerUpdatesEpochBecomesStaleWithoutRetiringProfile(t *testing.T) {
t.Fatal(err)
}
oldEpoch := encoded.layer.epoch
if err := c.FreezeLayerProfile(tg.LayerProfile227); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile227); err != nil {
t.Fatal(err)
}
if err := validateOutboundLayerBinding(c, encoded); !errors.Is(err, ErrOutboundLayerProfileStale) {
t.Fatalf("old push validation = %v, want ErrOutboundLayerProfileStale", err)
}
state := c.LayerProfileState()
if state.Profile != tg.LayerProfile227 || state.Origin != LayerProfileExplicit || state.Epoch <= oldEpoch {
if state.Profile != tlprofile.Profile227 || state.Origin != LayerProfileExplicit || state.Epoch <= oldEpoch {
t.Fatalf("corrected profile state = %#v, old epoch %d", state, oldEpoch)
}
}
@ -190,13 +191,13 @@ func TestRequestBoundLayerResultSurvivesConnectionCorrection(t *testing.T) {
if err != nil {
t.Fatal(err)
}
c := testConnWithLayerProfile(t, tg.LayerProfile225)
encoded, err := fanout.prepare(context.Background(), tg.LayerProfile225)
c := testConnWithLayerProfile(t, tlprofile.Profile225)
encoded, err := fanout.prepare(context.Background(), tlprofile.Profile225)
if err != nil {
t.Fatal(err)
}
encoded.layer.kind = outboundLayerBindingRequest
if err := c.FreezeLayerProfile(tg.LayerProfile227); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile227); err != nil {
t.Fatal(err)
}
if err := validateOutboundLayerBinding(c, encoded); err != nil {
@ -207,7 +208,7 @@ func TestRequestBoundLayerResultSurvivesConnectionCorrection(t *testing.T) {
func TestProfileCorrectionLinearizesAfterStartedPushWrite(t *testing.T) {
transport := newEpochBlockingTransport()
c := newOutboundTestConn(t, transport, nil)
if err := c.FreezeLayerProfile(tg.LayerProfile225); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
fanout, err := newLayerUpdatesFanout(testLayerUpdatesValue(123))
@ -228,7 +229,7 @@ func TestProfileCorrectionLinearizesAfterStartedPushWrite(t *testing.T) {
}
corrected := make(chan error, 1)
go func() { corrected <- c.FreezeLayerProfile(tg.LayerProfile227) }()
go func() { corrected <- c.FreezeLayerProfile(tlprofile.Profile227) }()
select {
case err := <-corrected:
t.Fatalf("profile correction crossed an old-epoch physical write: %v", err)
@ -252,7 +253,7 @@ func TestProfileCorrectionLinearizesAfterStartedPushWrite(t *testing.T) {
func TestStaleLayerPushIsRemovedFromResendTracking(t *testing.T) {
c := &Conn{metrics: NopMetrics{}}
if err := c.FreezeLayerProfile(tg.LayerProfile225); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
fanout, err := newLayerUpdatesFanout(testLayerUpdatesValue(123))
@ -263,7 +264,7 @@ func TestStaleLayerPushIsRemovedFromResendTracking(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if err := c.FreezeLayerProfile(tg.LayerProfile227); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile227); err != nil {
t.Fatal(err)
}
frame := &outboundFrame{msgID: 100, body: encoded.body, layer: encoded.layer}
@ -285,14 +286,14 @@ func TestOutboundLayerBindingRejectsUnknownAndMismatchedConnections(t *testing.T
if err != nil {
t.Fatalf("freeze updates: %v", err)
}
encoded, err := fanout.prepare(context.Background(), tg.LayerProfile225)
encoded, err := fanout.prepare(context.Background(), tlprofile.Profile225)
if err != nil {
t.Fatalf("prepare profile 225: %v", err)
}
if _, err := (&Conn{}).buildFrame(context.Background(), 0, nil, encoded); !errors.Is(err, ErrOutboundLayerProfileUnknown) {
t.Fatalf("unknown profile error = %v", err)
}
wrong := testConnWithLayerProfile(t, tg.LayerProfile227)
wrong := testConnWithLayerProfile(t, tlprofile.Profile227)
if _, err := wrong.buildFrame(context.Background(), 0, nil, encoded); !errors.Is(err, ErrOutboundLayerProfileMismatch) {
t.Fatalf("profile mismatch error = %v", err)
}
@ -307,13 +308,13 @@ func TestPendingPushReservationAccountsPreparedProfilesOnce(t *testing.T) {
reservation.bytes.Store(100)
reservation.refs.Store(1)
if !reservation.reservePrepared(tg.LayerProfile225, 80) {
if !reservation.reservePrepared(tlprofile.Profile225, 80) {
t.Fatal("reserve first profile")
}
if !reservation.reservePrepared(tg.LayerProfile225, 80) {
if !reservation.reservePrepared(tlprofile.Profile225, 80) {
t.Fatal("reuse first profile reservation")
}
if !reservation.reservePrepared(tg.LayerProfile227, 120) {
if !reservation.reservePrepared(tlprofile.Profile227, 120) {
t.Fatal("reserve second profile")
}
if got := budget.snapshot(); got != 300 {

View file

@ -16,6 +16,7 @@ import (
"github.com/iamxvbaba/td/mt"
"github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"github.com/iamxvbaba/td/transport"
)
@ -545,7 +546,7 @@ func TestOutboundActorSerializesConcurrentSends(t *testing.T) {
clientMsgID := proto.NewMessageIDGen(time.Now)
sendEncrypted(t, conn, cipher, auth, clientMsgID.New(proto.MessageFromClient), &mt.PingRequest{PingID: 1})
collectReplies(t, conn, cipher, auth.AuthKey, mt.MsgsAckTypeID)
freezeActiveTestSessionProfile(t, srv.Conns(), auth.AuthKey.ID, auth.SessionID, tg.LayerProfileCanonical)
freezeActiveTestSessionProfile(t, srv.Conns(), auth.AuthKey.ID, auth.SessionID, tlprofile.ProfileCanonical)
srv.Conns().SetReceivesUpdates(auth.SessionID, true)
const sends = 64
@ -1035,7 +1036,7 @@ func TestOutboundResendAndAckState(t *testing.T) {
clientMsgID := proto.NewMessageIDGen(time.Now)
sendEncrypted(t, conn, cipher, auth, clientMsgID.New(proto.MessageFromClient), &mt.PingRequest{PingID: 1})
collectReplies(t, conn, cipher, auth.AuthKey, mt.MsgsAckTypeID)
freezeActiveTestSessionProfile(t, srv.Conns(), auth.AuthKey.ID, auth.SessionID, tg.LayerProfileCanonical)
freezeActiveTestSessionProfile(t, srv.Conns(), auth.AuthKey.ID, auth.SessionID, tlprofile.ProfileCanonical)
srv.Conns().SetReceivesUpdates(auth.SessionID, true)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)

View file

@ -8,6 +8,7 @@ import (
"github.com/iamxvbaba/td/mt"
"github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
)
// TestSetReceivesUpdatesFlushesPendingBeforeActivation 验证置位时先排空暂存推送
@ -23,7 +24,7 @@ func TestSetReceivesUpdatesFlushesPendingBeforeActivation(t *testing.T) {
collectReplies(t, conn, cipher, auth.AuthKey, mt.MsgsAckTypeID)
raw := auth.AuthKey.ID
freezeActiveTestSessionProfile(t, srv.Conns(), raw, auth.SessionID, tg.LayerProfileCanonical)
freezeActiveTestSessionProfile(t, srv.Conns(), raw, auth.SessionID, tlprofile.ProfileCanonical)
ctx := context.Background()
// 完全就绪还要求 membership 路由建立ReceivesUpdatesForAuthKey 的另一半条件)。

View file

@ -11,6 +11,7 @@ import (
"github.com/iamxvbaba/td/bin"
"github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
)
var ErrSessionAmbiguous = errors.New("session id is shared by multiple auth keys")
@ -154,7 +155,7 @@ func (m *SessionManager) SetReceivesUpdates(sessionID int64, receives bool) {
}
}
func (m *SessionManager) SetLayerProfile(sessionID int64, profile tg.LayerProfile) bool {
func (m *SessionManager) SetLayerProfile(sessionID int64, profile tlprofile.Profile) bool {
m.mu.RLock()
c, _, ok, ambiguous := m.uniqueSessionForTestLocked(sessionID)
m.mu.RUnlock()

View file

@ -6,7 +6,7 @@ import (
"sync"
"sync/atomic"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
)
const rpcResultFlightDefaultMaxPending = 8192
@ -26,7 +26,7 @@ var (
// re-decode the same naked body under that grammar even if the winner aborts
// immediately after the mismatch is returned.
type rpcResultIdentityMismatchError struct {
profile tg.LayerProfile
profile tlprofile.Profile
hasProfile bool
}
@ -40,11 +40,11 @@ func identityMismatch(identity rpcResultRequestIdentity) error {
}
type rpcResultRequestIdentity struct {
exact tg.LayerPreparedCallIdentity
// profile is retained separately because LayerPreparedCallIdentity is opaque.
exact tlprofile.PreparedIdentity
// profile is retained separately because PreparedIdentity is opaque.
// It lets a same-msg_id replay be re-admitted with the original request
// grammar after the session default has moved to another Layer.
profile tg.LayerProfile
profile tlprofile.Profile
valid bool
}
@ -474,7 +474,7 @@ func (c *rpcResultCache) Acquire(authKeyID [8]byte, sessionID, reqMsgID int64) (
func (c *rpcResultCache) AcquireIdentified(
authKeyID [8]byte,
sessionID, reqMsgID int64,
identity tg.LayerPreparedCallIdentity,
identity tlprofile.PreparedIdentity,
) (rpcResultAcquire, error) {
return c.acquire(authKeyID, sessionID, reqMsgID, rpcResultRequestIdentity{exact: identity, valid: true})
}
@ -485,8 +485,8 @@ func (c *rpcResultCache) AcquireIdentified(
func (c *rpcResultCache) AcquireLayerIdentified(
authKeyID [8]byte,
sessionID, reqMsgID int64,
profile tg.LayerProfile,
identity tg.LayerPreparedCallIdentity,
profile tlprofile.Profile,
identity tlprofile.PreparedIdentity,
) (rpcResultAcquire, error) {
return c.acquire(authKeyID, sessionID, reqMsgID, rpcResultRequestIdentity{
exact: identity, profile: profile, valid: true,
@ -497,7 +497,7 @@ func (c *rpcResultCache) AcquireLayerIdentified(
// owner/result. It does not create or join a flight. Callers still perform
// AcquireLayerIdentified after decode, which atomically rejects a same-msg_id
// body change by comparing the full prepared identity.
func (c *rpcResultCache) ExactAdmissionProfile(authKeyID [8]byte, sessionID, reqMsgID int64) (tg.LayerProfile, bool) {
func (c *rpcResultCache) ExactAdmissionProfile(authKeyID [8]byte, sessionID, reqMsgID int64) (tlprofile.Profile, bool) {
if c == nil || reqMsgID == 0 {
return 0, false
}

View file

@ -9,19 +9,20 @@ import (
"github.com/iamxvbaba/td/bin"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
)
func rpcFlightTestAuthID(seed byte) [8]byte {
return [8]byte{seed, seed + 1, seed + 2, seed + 3}
}
func rpcFlightExactIdentity(t *testing.T, profile tg.LayerProfile, request bin.Encoder) tg.LayerPreparedCallIdentity {
func rpcFlightExactIdentity(t *testing.T, profile tlprofile.Profile, request bin.Object) tlprofile.PreparedIdentity {
t.Helper()
var body bin.Buffer
if err := request.Encode(&body); err != nil {
if err := tlprofile.EncodeObject(profile, request, &body); err != nil {
t.Fatalf("encode exact request: %v", err)
}
admitted, err := tg.NewServerDispatcher(nil).AdmitLayer(profile, &body)
admitted, err := tlprofile.NewDispatcher().Admit(profile, &body, tlprofile.Limits{})
if err != nil {
t.Fatalf("admit exact request: %v", err)
}
@ -223,8 +224,8 @@ func TestRPCResultFlightRepeatedReplayJoinsStayBoundedAndPutCleansExecution(t *t
func TestRPCResultFlightExactIdentityGuardsPendingAndCompletedReuse(t *testing.T) {
cache := newRPCResultCacheWithFlightLimit(time.Now, 2)
authKeyID := rpcFlightTestAuthID(90)
firstIdentity := rpcFlightExactIdentity(t, tg.LayerProfile225, &tg.HelpGetConfigRequest{})
otherIdentity := rpcFlightExactIdentity(t, tg.LayerProfile225, &tg.HelpGetNearestDCRequest{})
firstIdentity := rpcFlightExactIdentity(t, tlprofile.Profile225, &tg.HelpGetConfigRequest{})
otherIdentity := rpcFlightExactIdentity(t, tlprofile.Profile225, &tg.HelpGetNearestDCRequest{})
owner, err := cache.AcquireIdentified(authKeyID, 90, 900, firstIdentity)
if err != nil || owner.state != rpcResultAcquireOwner || owner.owner == nil {
@ -255,23 +256,23 @@ func TestRPCResultFlightExactIdentityGuardsPendingAndCompletedReuse(t *testing.T
func TestRPCResultFlightAdmissionSequenceAllocatedOnceAndReplayed(t *testing.T) {
cache := newRPCResultCacheWithFlightLimit(time.Now, 4)
authKeyID := rpcFlightTestAuthID(89)
identity := rpcFlightExactIdentity(t, tg.LayerProfile225, &tg.HelpGetConfigRequest{})
owner, err := cache.AcquireLayerIdentified(authKeyID, 89, 890, tg.LayerProfile225, identity)
identity := rpcFlightExactIdentity(t, tlprofile.Profile225, &tg.HelpGetConfigRequest{})
owner, err := cache.AcquireLayerIdentified(authKeyID, 89, 890, tlprofile.Profile225, identity)
if err != nil || owner.state != rpcResultAcquireOwner || owner.owner == nil || owner.admissionSeq == 0 {
t.Fatalf("owner = state:%d seq:%d err:%v", owner.state, owner.admissionSeq, err)
}
pending, err := cache.AcquireLayerIdentified(authKeyID, 89, 890, tg.LayerProfile225, identity)
pending, err := cache.AcquireLayerIdentified(authKeyID, 89, 890, tlprofile.Profile225, identity)
if err != nil || pending.state != rpcResultAcquirePending || pending.admissionSeq != owner.admissionSeq {
t.Fatalf("pending = state:%d seq:%d err:%v, want seq:%d", pending.state, pending.admissionSeq, err, owner.admissionSeq)
}
owner.owner.CompleteExecution(true)
encoded := &encodedOutboundMessage{body: []byte{1}, reqMsgID: 890}
cache.Put(authKeyID, 89, 890, encoded)
completed, err := cache.AcquireLayerIdentified(authKeyID, 89, 890, tg.LayerProfile225, identity)
completed, err := cache.AcquireLayerIdentified(authKeyID, 89, 890, tlprofile.Profile225, identity)
if err != nil || completed.state != rpcResultAcquireCompleted || completed.admissionSeq != owner.admissionSeq {
t.Fatalf("completed = state:%d seq:%d err:%v, want seq:%d", completed.state, completed.admissionSeq, err, owner.admissionSeq)
}
second, err := cache.AcquireLayerIdentified(authKeyID, 89, 894, tg.LayerProfile225, identity)
second, err := cache.AcquireLayerIdentified(authKeyID, 89, 894, tlprofile.Profile225, identity)
if err != nil || second.admissionSeq <= owner.admissionSeq {
t.Fatalf("second owner seq=%d err=%v, want > %d", second.admissionSeq, err, owner.admissionSeq)
}
@ -286,12 +287,12 @@ func TestRPCResultFlightAdmissionSequenceAllocatedOnceAndReplayed(t *testing.T)
func TestRPCAdmissionSafeFloorTracksOwnersUntilPutOrAbort(t *testing.T) {
cache := newRPCResultCacheWithFlightLimit(time.Now, 4)
authKeyID := rpcFlightTestAuthID(86)
identity := rpcFlightExactIdentity(t, tg.LayerProfile225, &tg.HelpGetConfigRequest{})
first, err := cache.AcquireLayerIdentified(authKeyID, 86, 860, tg.LayerProfile225, identity)
identity := rpcFlightExactIdentity(t, tlprofile.Profile225, &tg.HelpGetConfigRequest{})
first, err := cache.AcquireLayerIdentified(authKeyID, 86, 860, tlprofile.Profile225, identity)
if err != nil || first.owner == nil {
t.Fatalf("first owner err=%v", err)
}
second, err := cache.AcquireLayerIdentified(authKeyID, 86, 864, tg.LayerProfile225, identity)
second, err := cache.AcquireLayerIdentified(authKeyID, 86, 864, tlprofile.Profile225, identity)
if err != nil || second.owner == nil {
t.Fatalf("second owner err=%v", err)
}
@ -315,12 +316,12 @@ func TestRPCAdmissionSequenceExhaustionCannotWrap(t *testing.T) {
cache := newRPCResultCacheWithFlightLimit(time.Now, 2)
cache.nextAdmissionSeq.Store(^uint64(0) - 1)
authKeyID := rpcFlightTestAuthID(85)
identity := rpcFlightExactIdentity(t, tg.LayerProfile225, &tg.HelpGetConfigRequest{})
last, err := cache.AcquireLayerIdentified(authKeyID, 85, 850, tg.LayerProfile225, identity)
identity := rpcFlightExactIdentity(t, tlprofile.Profile225, &tg.HelpGetConfigRequest{})
last, err := cache.AcquireLayerIdentified(authKeyID, 85, 850, tlprofile.Profile225, identity)
if err != nil || last.admissionSeq != ^uint64(0) || last.owner == nil {
t.Fatalf("last sequence=%d owner:%v err=%v", last.admissionSeq, last.owner != nil, err)
}
if _, err := cache.AcquireLayerIdentified(authKeyID, 85, 854, tg.LayerProfile225, identity); !errors.Is(err, ErrRPCAdmissionSeqExhausted) {
if _, err := cache.AcquireLayerIdentified(authKeyID, 85, 854, tlprofile.Profile225, identity); !errors.Is(err, ErrRPCAdmissionSeqExhausted) {
t.Fatalf("post-max allocation err=%v, want %v", err, ErrRPCAdmissionSeqExhausted)
}
if got := cache.nextAdmissionSeq.Load(); got != ^uint64(0) {
@ -333,15 +334,15 @@ func TestRPCIdentityMismatchCarriesWinnerProfileAcrossAbort(t *testing.T) {
cache := newRPCResultCacheWithFlightLimit(time.Now, 2)
authKeyID := rpcFlightTestAuthID(88)
request := &tg.MessagesGetHistoryRequest{Peer: &tg.InputPeerSelf{}, Limit: 1}
winnerIdentity := rpcFlightExactIdentity(t, tg.LayerProfile225, request)
loserIdentity := rpcFlightExactIdentity(t, tg.LayerProfile227, request)
winner, err := cache.AcquireLayerIdentified(authKeyID, 88, 880, tg.LayerProfile225, winnerIdentity)
winnerIdentity := rpcFlightExactIdentity(t, tlprofile.Profile225, request)
loserIdentity := rpcFlightExactIdentity(t, tlprofile.Profile227, request)
winner, err := cache.AcquireLayerIdentified(authKeyID, 88, 880, tlprofile.Profile225, winnerIdentity)
if err != nil || winner.owner == nil {
t.Fatalf("winner owner err=%v", err)
}
_, err = cache.AcquireLayerIdentified(authKeyID, 88, 880, tg.LayerProfile227, loserIdentity)
_, err = cache.AcquireLayerIdentified(authKeyID, 88, 880, tlprofile.Profile227, loserIdentity)
var mismatch *rpcResultIdentityMismatchError
if !errors.As(err, &mismatch) || !mismatch.hasProfile || mismatch.profile != tg.LayerProfile225 {
if !errors.As(err, &mismatch) || !mismatch.hasProfile || mismatch.profile != tlprofile.Profile225 {
t.Fatalf("mismatch = %#v err=%v", mismatch, err)
}
if !winner.owner.Abort() {
@ -358,17 +359,17 @@ func TestRPCAdmissionProfileHintSurvivesCompletedEvictionWindow(t *testing.T) {
now := time.Unix(1_900_000_000, 0)
cache := newRPCResultCacheWithFlightLimit(func() time.Time { return now }, 2)
authKeyID := rpcFlightTestAuthID(87)
identity := rpcFlightExactIdentity(t, tg.LayerProfile225, &tg.MessagesGetHistoryRequest{
identity := rpcFlightExactIdentity(t, tlprofile.Profile225, &tg.MessagesGetHistoryRequest{
Peer: &tg.InputPeerSelf{}, Limit: 1,
})
claim, err := cache.AcquireLayerIdentified(authKeyID, 87, 870, tg.LayerProfile225, identity)
claim, err := cache.AcquireLayerIdentified(authKeyID, 87, 870, tlprofile.Profile225, identity)
if err != nil || claim.owner == nil {
t.Fatalf("owner err=%v", err)
}
claim.owner.CompleteExecution(true)
cache.Put(authKeyID, 87, 870, &encodedOutboundMessage{body: []byte{1}, reqMsgID: 870})
profile, ok := cache.ExactAdmissionProfile(authKeyID, 87, 870)
if !ok || profile != tg.LayerProfile225 {
if !ok || profile != tlprofile.Profile225 {
t.Fatalf("profile hint = (%d,%v)", profile, ok)
}
// Admission already copied the hint into its local decoder cursor. Expiry
@ -385,7 +386,7 @@ func TestRPCAdmissionProfileHintSurvivesCompletedEvictionWindow(t *testing.T) {
func TestRPCInvariantIdentityDoesNotExposeCanonicalProfileHint(t *testing.T) {
cache := newRPCResultCacheWithFlightLimit(time.Now, 2)
authKeyID := rpcFlightTestAuthID(84)
identity := rpcFlightExactIdentity(t, tg.LayerProfile227, &tg.AuthBindTempAuthKeyRequest{
identity := rpcFlightExactIdentity(t, tlprofile.Profile227, &tg.AuthBindTempAuthKeyRequest{
PermAuthKeyID: 1, Nonce: 2, ExpiresAt: 3, EncryptedMessage: []byte("bind"),
})
claim, err := cache.AcquireLayerIdentified(authKeyID, 84, 840, 0, identity)

View file

@ -15,6 +15,7 @@ import (
"github.com/iamxvbaba/td/crypto"
"github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
)
type opaqueRPCResult struct{ body []byte }
@ -491,8 +492,7 @@ func encodedRPCResultForPriorityTest(reqMsgID int64, payloadBytes int) *encodedO
return &encodedOutboundMessage{
typeID: proto.ResultTypeID, reqMsgID: reqMsgID, body: b.Raw(),
layer: &outboundLayerBinding{
profile: tg.LayerProfileCanonical,
typ: tg.LayerClassBoolType().Ref(),
profile: tlprofile.ProfileCanonical,
kind: outboundLayerBindingRequest,
},
}

View file

@ -15,6 +15,7 @@ import (
"github.com/iamxvbaba/td/bin"
"github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
)
// rpcRewrapRegistry links only an explicit official-client transition:
@ -40,8 +41,8 @@ type rpcRewrapSessionKey struct {
type rpcRewrapKey struct {
rpcRewrapSessionKey
fingerprint [sha256.Size]byte
semantic tg.LayerSemanticRequestIdentity
call tg.LayerCallIdentity
semantic tlprofile.SemanticIdentity
call tlprofile.CallIdentity
exact bool
}
@ -110,8 +111,8 @@ func (r *rpcRewrapRegistry) register(c *Conn, body []byte, reqMsgID int64, metho
func (r *rpcRewrapRegistry) registerSemantic(
c *Conn,
identity tg.LayerSemanticRequestIdentity,
call tg.LayerCallIdentity,
identity tlprofile.SemanticIdentity,
call tlprofile.CallIdentity,
reqMsgID int64,
method string,
owner *rpcResultOwnerLease,
@ -183,8 +184,8 @@ func (r *rpcRewrapRegistry) claim(c *Conn, inner []byte) *rpcRewrapCandidate {
func (r *rpcRewrapRegistry) claimSemantic(
c *Conn,
identity tg.LayerSemanticRequestIdentity,
call tg.LayerCallIdentity,
identity tlprofile.SemanticIdentity,
call tlprofile.CallIdentity,
) *rpcRewrapCandidate {
if r == nil || c == nil || identity.Method() == 0 || identity.CanonicalSize() <= 0 {
return nil

View file

@ -17,11 +17,12 @@ import (
"github.com/iamxvbaba/td/tgerr"
"github.com/iamxvbaba/td/transport"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/rpc"
)
// TestRPCGetConfig 验证 M3握手后 client 加密 help.getConfig
// server 经 tg.ServerDispatcher 路由并回 rpc_result含本地 DC外加 new_session_created + ack。
// server 经 tlprofile.Dispatcher 路由并回 rpc_result含本地 DC外加 new_session_created + ack。
func TestRPCGetConfig(t *testing.T) {
const (
dc = 2
@ -80,7 +81,7 @@ func TestLayerRPCGetConfigUsesExactAdmittedProfile(t *testing.T) {
clientMsgID := proto.NewMessageIDGen(time.Now)
reqMsgID := clientMsgID.New(proto.MessageFromClient)
request := &tg.InvokeWithLayerRequest{
Layer: int(tg.LayerProfile225),
Layer: int(tlprofile.Profile225),
Query: &tg.InitConnectionRequest{
APIID: 123,
DeviceModel: "Desktop",
@ -104,10 +105,14 @@ func TestLayerRPCGetConfigUsesExactAdmittedProfile(t *testing.T) {
t.Fatalf("rpc_result req_msg_id = %d, want %d", result.RequestMessageID, reqMsgID)
}
exact := &bin.Buffer{Buf: result.Result}
config, err := tg.DecodeLayer(tg.LayerProfile225, tg.LayerConstructorConfigType(), exact)
configObject, err := tlprofile.DecodeObject(tlprofile.Profile225, exact, tlprofile.Limits{})
if err != nil {
t.Fatalf("decode layer 225 config: %v", err)
}
config, ok := configObject.(*tg.Config)
if !ok {
t.Fatalf("layer 225 config = %T, want *tg.Config", configObject)
}
if exact.Len() != 0 || config.ThisDC != dc {
t.Fatalf("layer 225 config = dc:%d remaining:%d", config.ThisDC, exact.Len())
}

View file

@ -25,6 +25,7 @@ import (
"github.com/iamxvbaba/td/tmap"
"github.com/iamxvbaba/td/transport"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/store"
"telesrv/internal/store/memory"
)
@ -57,25 +58,25 @@ type legacyRPCHandlerWithMethod interface {
// validate wrapper dependencies and establish exact request identity before
// flight/cache/scheduler ownership is acquired.
type LayerRPCHandler interface {
AdmitLayer(profile tg.LayerProfile, b *bin.Buffer, limits tg.LayerDecodeLimits) (tg.LayerRequest, error)
AdmitUnprofiled(b *bin.Buffer, limits tg.LayerDecodeLimits) (tg.LayerRequest, error)
AdmitLayer(profile tlprofile.Profile, b *bin.Buffer, limits tlprofile.Limits) (tlprofile.Admission, error)
AdmitUnprofiled(b *bin.Buffer, limits tlprofile.Limits) (tlprofile.Admission, error)
DispatchAdmitted(
ctx context.Context,
authKeyID [8]byte,
sessionID int64,
msgID int64,
admissionSeq uint64,
request tg.LayerRequest,
) (tg.LayerRPCResult, string, error)
request tlprofile.Admission,
) (tlprofile.Result, string, error)
}
// LayerRPCDefaultProfileAdmitter decodes with a recoverable inherited/default
// profile. Production handlers should implement it with the same generated
// ServerDispatcher and adapter registry used by AdmitLayer. The split keeps old
// profile. Production handlers should implement it with the same sparse
// tlprofile dispatcher and semantic adapter registry used by AdmitLayer. The split keeps old
// test doubles source-compatible while allowing invokeWithLayer to correct even
// a previously explicit Conn profile.
type LayerRPCDefaultProfileAdmitter interface {
AdmitDefaultLayer(profile tg.LayerProfile, b *bin.Buffer, limits tg.LayerDecodeLimits) (tg.LayerRequest, error)
AdmitDefaultLayer(profile tlprofile.Profile, b *bin.Buffer, limits tlprofile.Limits) (tlprofile.Admission, error)
}
// LayerRPCSessionProfileResolver may restore an exact profile only when it was
@ -168,7 +169,7 @@ type LayerRPCReplayPreparer interface {
sessionID int64,
msgID int64,
admissionSeq uint64,
request tg.LayerRequest,
request tlprofile.Admission,
) (afterSuccessfulDelivery func() error, err error)
}

View file

@ -14,6 +14,7 @@ import (
"github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
)
// ErrSessionNotFound 表示目标 session 当前无活跃连接。
@ -82,7 +83,7 @@ type pendingPushReservation struct {
refs atomic.Int32
mu sync.Mutex
profiles map[tg.LayerProfile]struct{}
profiles map[tlprofile.Profile]struct{}
}
func (r *pendingPushReservation) retain() {
@ -110,7 +111,7 @@ func (r *pendingPushReservation) release() {
// reservePrepared accounts the profile-specific immutable body retained by the
// semantic pending fanout. Multiple queued sessions sharing this reservation
// and profile share both the bytes and this one budget charge.
func (r *pendingPushReservation) reservePrepared(profile tg.LayerProfile, bytes int) bool {
func (r *pendingPushReservation) reservePrepared(profile tlprofile.Profile, bytes int) bool {
if r == nil || bytes < 0 {
return false
}
@ -123,7 +124,7 @@ func (r *pendingPushReservation) reservePrepared(profile tg.LayerProfile, bytes
return false
}
if r.profiles == nil {
r.profiles = make(map[tg.LayerProfile]struct{})
r.profiles = make(map[tlprofile.Profile]struct{})
}
r.profiles[profile] = struct{}{}
r.bytes.Add(int64(bytes))
@ -274,7 +275,7 @@ func (m *SessionManager) SeedInheritedLayerForBusinessAuthKey(businessAuthKeyID
if m == nil || businessAuthKeyID == ([8]byte{}) {
return 0
}
profile, ok := tg.ResolveLayerProfile(layer)
profile, ok := tlprofile.ResolveProfile(layer)
if !ok {
return 0
}
@ -313,7 +314,7 @@ func (m *SessionManager) applyInheritedLayerForRawAuthKey(rawAuthKeyID [8]byte,
if m == nil {
return 0
}
profile, ok := tg.ResolveLayerProfile(layer)
profile, ok := tlprofile.ResolveProfile(layer)
if !ok {
return 0
}
@ -364,7 +365,7 @@ func (m *SessionManager) ApplyOrderedLayerProfileForSession(
primary *Conn,
rawAuthKeyID [8]byte,
sessionID int64,
profile tg.LayerProfile,
profile tlprofile.Profile,
msgID int64,
) (int, error) {
if err := validateLayerProfile(profile); err != nil {
@ -452,7 +453,7 @@ func (m *SessionManager) ExplicitLayerEvidenceForAuthKey(rawAuthKeyID [8]byte, s
if c.isRetired() || state.Origin != LayerProfileExplicit {
continue
}
profile, supported := tg.ResolveLayerProfile(int(state.Profile))
profile, supported := tlprofile.ResolveProfile(int(state.Profile))
if !supported || profile != state.Profile {
continue
}
@ -477,7 +478,7 @@ func (m *SessionManager) SetClientLayerForAuthKey(rawAuthKeyID [8]byte, sessionI
if m == nil {
return
}
profile, ok := tg.ResolveLayerProfile(layer)
profile, ok := tlprofile.ResolveProfile(layer)
if !ok {
return
}

View file

@ -15,6 +15,7 @@ import (
"github.com/iamxvbaba/td/mt"
"github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
)
type closeCountingTransport struct {
@ -226,7 +227,7 @@ func TestSessionManagerBestEffortFanoutPreparesOncePerProfile(t *testing.T) {
c.userID.Store(userID)
c.userIDResolved.Store(true)
c.receivesUpdates.Store(true)
if err := c.FreezeLayerProfile(tg.LayerProfile225); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile225); err != nil {
t.Fatalf("freeze profile: %v", err)
}
sm.Register(c)
@ -263,7 +264,7 @@ func TestSessionManagerMixedLayerFanoutUsesProfileBoundBodies(t *testing.T) {
sm := NewSessionManager(zaptest.NewLogger(t))
const userID = int64(103)
authKeyID := [8]byte{0x22, 0x70, 0x22, 0x80}
profiles := []tg.LayerProfile{tg.LayerProfile225, tg.LayerProfile227, tg.LayerProfile228}
profiles := []tlprofile.Profile{tlprofile.Profile225, tlprofile.Profile227, tlprofile.Profile228}
conns := make([]*Conn, 0, len(profiles))
for _, profile := range profiles {
c := &Conn{
@ -277,8 +278,8 @@ func TestSessionManagerMixedLayerFanoutUsesProfileBoundBodies(t *testing.T) {
c.userID.Store(userID)
c.userIDResolved.Store(true)
c.receivesUpdates.Store(true)
if profile == tg.LayerProfile228 {
if err := c.SeedInheritedLayerProfile(tg.LayerProfile227); err != nil {
if profile == tlprofile.Profile228 {
if err := c.SeedInheritedLayerProfile(tlprofile.Profile227); err != nil {
t.Fatalf("seed Alice inherited profile: %v", err)
}
}
@ -319,7 +320,7 @@ func TestSessionManagerMixedLayerFanoutUsesProfileBoundBodies(t *testing.T) {
t.Fatalf("profile %d push leaked channel constructor %#08x", profiles[i], otherChannelID)
}
input := bin.Buffer{Buf: op.encoded.body}
decoded, decodeErr := tg.DecodeLayer(profiles[i], tg.LayerClassUpdatesType(), &input)
decoded, decodeErr := tlprofile.DecodeObject(profiles[i], &input, tlprofile.Limits{})
if decodeErr != nil || input.Len() != 0 {
t.Fatalf("decode profile %d: remaining=%d err=%v", profiles[i], input.Len(), decodeErr)
}
@ -397,7 +398,7 @@ func TestSessionManagerBestEffortFanoutUsesOneBudgetAndDropsOnlySlowConsumers(t
c.userID.Store(userID)
c.userIDResolved.Store(true)
c.receivesUpdates.Store(true)
if err := c.FreezeLayerProfile(tg.LayerProfile227); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile227); err != nil {
t.Fatalf("freeze profile: %v", err)
}
sm.Register(c)
@ -415,7 +416,7 @@ func TestSessionManagerBestEffortFanoutUsesOneBudgetAndDropsOnlySlowConsumers(t
healthy.userID.Store(userID)
healthy.userIDResolved.Store(true)
healthy.receivesUpdates.Store(true)
if err := healthy.FreezeLayerProfile(tg.LayerProfile227); err != nil {
if err := healthy.FreezeLayerProfile(tlprofile.Profile227); err != nil {
t.Fatalf("freeze healthy profile: %v", err)
}
sm.Register(healthy)
@ -747,7 +748,7 @@ func TestPushToUserAuthKeyUsesOneDeadlineAndDropsOnlySlowPFSConnections(t *testi
outboundStop: make(chan struct{}),
}
c.receivesUpdates.Store(true)
if err := c.FreezeLayerProfile(tg.LayerProfile227); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile227); err != nil {
t.Fatalf("freeze profile: %v", err)
}
if queueFull {
@ -912,7 +913,7 @@ func TestPushToSessionForAuthKeyImmediateBypassesReadinessQueue(t *testing.T) {
outboundControl: make(chan outboundOp, 1),
outboundStop: make(chan struct{}),
}
if err := c.FreezeLayerProfile(tg.LayerProfile227); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile227); err != nil {
t.Fatalf("freeze profile: %v", err)
}
sm.Register(c)
@ -991,7 +992,7 @@ func TestSessionManagerWithholdsUpdatesReadinessUntilExactProfile(t *testing.T)
default:
}
if err := c.FreezeLayerProfile(tg.LayerProfile225); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err)
}
c.membershipsSynced.Store(true)
@ -1002,7 +1003,7 @@ func TestSessionManagerWithholdsUpdatesReadinessUntilExactProfile(t *testing.T)
case <-time.After(time.Second):
t.Fatal("profiled readiness did not flush pending update")
}
if op.encoded == nil || op.encoded.layer == nil || op.encoded.layer.profile != tg.LayerProfile225 {
if op.encoded == nil || op.encoded.layer == nil || op.encoded.layer.profile != tlprofile.Profile225 {
t.Fatalf("flushed update layer binding = %#v", op.encoded)
}
op.releaseReservation(c.outboundTrackedBudget)
@ -1062,7 +1063,7 @@ func TestPendingFlushGlobalBodyPressureDoesNotTerminateHealthyConnection(t *test
const userID = int64(606)
c.userID.Store(userID)
c.userIDResolved.Store(true)
if err := c.FreezeLayerProfile(tg.LayerProfileCanonical); err != nil {
if err := c.FreezeLayerProfile(tlprofile.ProfileCanonical); err != nil {
t.Fatal(err)
}
sm.Register(c)
@ -1160,8 +1161,8 @@ func TestSessionManagerPush(t *testing.T) {
if got := srv.Conns().Online(); got != 2 {
t.Fatalf("online = %d, want 2", got)
}
if !srv.Conns().SetLayerProfile(auth1.SessionID, tg.LayerProfile227) ||
!srv.Conns().SetLayerProfile(auth2.SessionID, tg.LayerProfile227) {
if !srv.Conns().SetLayerProfile(auth1.SessionID, tlprofile.Profile227) ||
!srv.Conns().SetLayerProfile(auth2.SessionID, tlprofile.Profile227) {
t.Fatal("seed exact test profiles")
}

View file

@ -5,7 +5,7 @@ import (
"testing"
"time"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"go.uber.org/zap/zaptest"
)
@ -67,7 +67,7 @@ func TestSetSessionChannelMembershipsDetectsConcurrentIncrementalUpdates(t *test
sm := NewSessionManager(zaptest.NewLogger(t))
raw := [8]byte{1, 2, 3}
c := &Conn{sessionID: 42, authKeyID: raw}
if err := c.FreezeLayerProfile(tg.LayerProfile227); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile227); err != nil {
t.Fatal(err)
}
sm.Register(c)

View file

@ -3,7 +3,7 @@ package mtprotoedge
import (
"testing"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"go.uber.org/zap/zaptest"
)
@ -16,7 +16,7 @@ func TestReceivesUpdatesForAuthKeyRequiresMembershipSync(t *testing.T) {
sm := NewSessionManager(zaptest.NewLogger(t))
raw := [8]byte{1, 2, 3}
c := &Conn{sessionID: 42, authKeyID: raw}
if err := c.FreezeLayerProfile(tg.LayerProfile227); err != nil {
if err := c.FreezeLayerProfile(tlprofile.Profile227); err != nil {
t.Fatal(err)
}
sm.Register(c)

View file

@ -7,99 +7,241 @@ import (
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
ioscompat "telesrv/internal/compat/ios"
"telesrv/internal/compat/tdesktop"
"telesrv/internal/domain"
)
// registerAccount 注册 account.* RPC handler。
func (r *Router) registerAccount(d *tg.ServerDispatcher) {
d.OnAccountRegisterDevice(func(ctx context.Context, req *tg.AccountRegisterDeviceRequest) (bool, error) {
func (r *Router) registerAccount(d *tlprofile.Dispatcher) {
registerRPC[*tg.AccountRegisterDeviceRequest](d, tlprofile.SemanticMethodAccountRegisterDevice, func(ctx context.Context, req *tg.AccountRegisterDeviceRequest) (any, error) {
return true, nil
})
d.OnAccountUnregisterDevice(func(ctx context.Context, req *tg.AccountUnregisterDeviceRequest) (bool, error) {
registerRPC[*tg.AccountUnregisterDeviceRequest](d, tlprofile.SemanticMethodAccountUnregisterDevice, func(ctx context.Context, req *tg.AccountUnregisterDeviceRequest) (any, error) {
return true, nil
})
d.OnAccountUpdateDeviceLocked(func(ctx context.Context, period int) (bool, error) {
registerRPC[*tg.AccountUpdateDeviceLockedRequest](d, tlprofile.SemanticMethodAccountUpdateDeviceLocked, func(ctx context.Context, layerRequest *tg.AccountUpdateDeviceLockedRequest) (any, error) {
period := layerRequest.
Period
_ = period
if _, _, err := r.currentUserID(ctx); err != nil {
return false, internalErr()
}
return ioscompat.DeviceLockedUpdated(), nil
})
d.OnAccountSendChangePhoneCode(r.onAccountSendChangePhoneCode)
d.OnAccountChangePhone(r.onAccountChangePhone)
d.OnAccountCheckUsername(r.onAccountCheckUsername)
d.OnAccountUpdateProfile(r.onAccountUpdateProfile)
d.OnAccountUpdateUsername(r.onAccountUpdateUsername)
d.OnAccountUpdateBirthday(r.onAccountUpdateBirthday)
d.OnAccountUpdatePersonalChannel(r.onAccountUpdatePersonalChannel)
d.OnAccountGetPassword(r.onAccountGetPassword)
d.OnAccountGetNotifySettings(r.onAccountGetNotifySettings)
d.OnAccountUpdateNotifySettings(r.onAccountUpdateNotifySettings)
d.OnAccountResetNotifySettings(r.onAccountResetNotifySettings)
d.OnAccountGetPrivacy(r.onAccountGetPrivacy)
d.OnAccountSetPrivacy(r.onAccountSetPrivacy)
d.OnAccountGetAuthorizations(r.onAccountGetAuthorizations)
d.OnAccountResetAuthorization(r.onAccountResetAuthorization)
d.OnAccountGetPasswordSettings(r.onAccountGetPasswordSettings)
d.OnAccountUpdatePasswordSettings(r.onAccountUpdatePasswordSettings)
d.OnAccountConfirmPasswordEmail(r.onAccountConfirmPasswordEmail)
d.OnAccountResendPasswordEmail(r.onAccountResendPasswordEmail)
d.OnAccountCancelPasswordEmail(r.onAccountCancelPasswordEmail)
d.OnAccountSendVerifyEmailCode(r.onAccountSendVerifyEmailCode)
d.OnAccountVerifyEmail(r.onAccountVerifyEmail)
d.OnAccountGetDefaultEmojiStatuses(r.onAccountGetDefaultEmojiStatuses)
d.OnAccountGetCollectibleEmojiStatuses(func(ctx context.Context, hash int64) (tg.AccountEmojiStatusesClass, error) {
registerRPC[*tg.AccountSendChangePhoneCodeRequest](d, tlprofile.SemanticMethodAccountSendChangePhoneCode, func(ctx context.Context, layerRequest *tg.AccountSendChangePhoneCodeRequest) (any, error) {
return r.onAccountSendChangePhoneCode(ctx, layerRequest)
})
registerRPC[*tg.AccountChangePhoneRequest](d, tlprofile.SemanticMethodAccountChangePhone, func(ctx context.Context, layerRequest *tg.AccountChangePhoneRequest) (any, error) {
return r.onAccountChangePhone(ctx, layerRequest)
})
registerRPC[*tg.AccountCheckUsernameRequest](d, tlprofile.SemanticMethodAccountCheckUsername, func(ctx context.Context, layerRequest *tg.AccountCheckUsernameRequest) (any, error) {
return r.onAccountCheckUsername(ctx, layerRequest.
Username)
})
registerRPC[*tg.AccountUpdateProfileRequest](d, tlprofile.SemanticMethodAccountUpdateProfile, func(ctx context.Context, layerRequest *tg.AccountUpdateProfileRequest) (any, error) {
return r.onAccountUpdateProfile(ctx, layerRequest)
})
registerRPC[*tg.AccountUpdateUsernameRequest](d, tlprofile.SemanticMethodAccountUpdateUsername, func(ctx context.Context, layerRequest *tg.AccountUpdateUsernameRequest) (any, error) {
return r.onAccountUpdateUsername(ctx, layerRequest.
Username)
})
registerRPC[*tg.AccountUpdateBirthdayRequest](d, tlprofile.SemanticMethodAccountUpdateBirthday, func(ctx context.Context, layerRequest *tg.AccountUpdateBirthdayRequest) (any, error) {
return r.onAccountUpdateBirthday(ctx, layerRequest)
})
registerRPC[*tg.AccountUpdatePersonalChannelRequest](d, tlprofile.SemanticMethodAccountUpdatePersonalChannel, func(ctx context.Context, layerRequest *tg.AccountUpdatePersonalChannelRequest) (any, error) {
return r.onAccountUpdatePersonalChannel(ctx, layerRequest.
Channel)
})
registerRPC[*tg.AccountGetPasswordRequest](d, tlprofile.SemanticMethodAccountGetPassword, func(ctx context.Context, layerRequest *tg.AccountGetPasswordRequest) (any, error) {
return r.onAccountGetPassword(ctx)
})
registerRPC[*tg.AccountGetNotifySettingsRequest](d, tlprofile.SemanticMethodAccountGetNotifySettings, func(ctx context.Context, layerRequest *tg.AccountGetNotifySettingsRequest) (any, error) {
return r.onAccountGetNotifySettings(ctx, layerRequest.
Peer)
})
registerRPC[*tg.AccountUpdateNotifySettingsRequest](d, tlprofile.SemanticMethodAccountUpdateNotifySettings, func(ctx context.Context, layerRequest *tg.AccountUpdateNotifySettingsRequest) (any, error) {
return r.onAccountUpdateNotifySettings(ctx, layerRequest)
})
registerRPC[*tg.AccountResetNotifySettingsRequest](d, tlprofile.SemanticMethodAccountResetNotifySettings, func(ctx context.Context, layerRequest *tg.AccountResetNotifySettingsRequest) (any, error) {
return r.onAccountResetNotifySettings(ctx)
})
registerRPC[*tg.AccountGetPrivacyRequest](d, tlprofile.SemanticMethodAccountGetPrivacy, func(ctx context.Context, layerRequest *tg.AccountGetPrivacyRequest) (any, error) {
return r.onAccountGetPrivacy(ctx, layerRequest.
Key)
})
registerRPC[*tg.AccountSetPrivacyRequest](d, tlprofile.SemanticMethodAccountSetPrivacy, func(ctx context.Context, layerRequest *tg.AccountSetPrivacyRequest) (any, error) {
return r.onAccountSetPrivacy(ctx, layerRequest)
})
registerRPC[*tg.AccountGetAuthorizationsRequest](d, tlprofile.SemanticMethodAccountGetAuthorizations, func(ctx context.Context, layerRequest *tg.AccountGetAuthorizationsRequest) (any, error) {
return r.onAccountGetAuthorizations(ctx)
})
registerRPC[*tg.AccountResetAuthorizationRequest](d, tlprofile.SemanticMethodAccountResetAuthorization, func(ctx context.Context, layerRequest *tg.AccountResetAuthorizationRequest) (any, error) {
return r.onAccountResetAuthorization(ctx, layerRequest.
Hash)
})
registerRPC[*tg.AccountGetPasswordSettingsRequest](d, tlprofile.SemanticMethodAccountGetPasswordSettings, func(ctx context.Context, layerRequest *tg.AccountGetPasswordSettingsRequest) (any, error) {
return r.onAccountGetPasswordSettings(ctx, layerRequest.
Password)
})
registerRPC[*tg.AccountUpdatePasswordSettingsRequest](d, tlprofile.SemanticMethodAccountUpdatePasswordSettings, func(ctx context.Context, layerRequest *tg.AccountUpdatePasswordSettingsRequest) (any, error) {
return r.onAccountUpdatePasswordSettings(ctx, layerRequest)
})
registerRPC[*tg.AccountConfirmPasswordEmailRequest](d, tlprofile.SemanticMethodAccountConfirmPasswordEmail, func(ctx context.Context, layerRequest *tg.AccountConfirmPasswordEmailRequest) (any, error) {
return r.onAccountConfirmPasswordEmail(ctx, layerRequest.
Code)
})
registerRPC[*tg.AccountResendPasswordEmailRequest](d, tlprofile.SemanticMethodAccountResendPasswordEmail, func(ctx context.Context, layerRequest *tg.AccountResendPasswordEmailRequest) (any, error) {
return r.onAccountResendPasswordEmail(ctx)
})
registerRPC[*tg.AccountCancelPasswordEmailRequest](d, tlprofile.SemanticMethodAccountCancelPasswordEmail, func(ctx context.Context, layerRequest *tg.AccountCancelPasswordEmailRequest) (any, error) {
return r.onAccountCancelPasswordEmail(ctx)
})
registerRPC[*tg.AccountSendVerifyEmailCodeRequest](d, tlprofile.SemanticMethodAccountSendVerifyEmailCode, func(ctx context.Context, layerRequest *tg.AccountSendVerifyEmailCodeRequest) (any, error) {
return r.onAccountSendVerifyEmailCode(ctx, layerRequest)
})
registerRPC[*tg.AccountVerifyEmailRequest](d, tlprofile.SemanticMethodAccountVerifyEmail, func(ctx context.Context, layerRequest *tg.AccountVerifyEmailRequest) (any, error) {
return r.onAccountVerifyEmail(ctx, layerRequest)
})
registerRPC[*tg.AccountGetDefaultEmojiStatusesRequest](d, tlprofile.SemanticMethodAccountGetDefaultEmojiStatuses, func(ctx context.Context, layerRequest *tg.AccountGetDefaultEmojiStatusesRequest) (any, error) {
return r.onAccountGetDefaultEmojiStatuses(ctx, layerRequest.
Hash)
})
registerRPC[*tg.AccountGetCollectibleEmojiStatusesRequest](d, tlprofile.SemanticMethodAccountGetCollectibleEmojiStatuses, func(ctx context.Context, layerRequest *tg.AccountGetCollectibleEmojiStatusesRequest) (any, error) {
hash := layerRequest.
Hash
_ = hash
return tdesktop.CollectibleEmojiStatuses(), nil
})
d.OnAccountGetDefaultGroupPhotoEmojis(func(ctx context.Context, hash int64) (tg.EmojiListClass, error) {
registerRPC[*tg.AccountGetDefaultGroupPhotoEmojisRequest](d, tlprofile.SemanticMethodAccountGetDefaultGroupPhotoEmojis, func(ctx context.Context, layerRequest *tg.AccountGetDefaultGroupPhotoEmojisRequest) (any, error) {
hash := layerRequest.
Hash
_ = hash
return tdesktop.DefaultGroupPhotoEmojis(), nil
})
d.OnAccountGetConnectedBots(r.onAccountGetConnectedBots)
d.OnAccountUpdateBusinessWorkHours(r.onAccountUpdateBusinessWorkHours)
d.OnAccountUpdateBusinessLocation(r.onAccountUpdateBusinessLocation)
d.OnAccountUpdateBusinessIntro(r.onAccountUpdateBusinessIntro)
d.OnAccountUpdateBusinessGreetingMessage(r.onAccountUpdateBusinessGreetingMessage)
d.OnAccountUpdateBusinessAwayMessage(r.onAccountUpdateBusinessAwayMessage)
d.OnAccountGetBusinessChatLinks(r.onAccountGetBusinessChatLinks)
d.OnAccountCreateBusinessChatLink(r.onAccountCreateBusinessChatLink)
d.OnAccountEditBusinessChatLink(r.onAccountEditBusinessChatLink)
d.OnAccountDeleteBusinessChatLink(r.onAccountDeleteBusinessChatLink)
d.OnAccountResolveBusinessChatLink(r.onAccountResolveBusinessChatLink)
d.OnAccountUpdateConnectedBot(r.onAccountUpdateConnectedBot)
d.OnAccountGetBotBusinessConnection(func(ctx context.Context, connectionID string) (tg.UpdatesClass, error) {
registerRPC[*tg.AccountGetConnectedBotsRequest](d, tlprofile.SemanticMethodAccountGetConnectedBots, func(ctx context.Context, layerRequest *tg.AccountGetConnectedBotsRequest) (any, error) {
return r.onAccountGetConnectedBots(ctx)
})
registerRPC[*tg.AccountUpdateBusinessWorkHoursRequest](d, tlprofile.SemanticMethodAccountUpdateBusinessWorkHours, func(ctx context.Context, layerRequest *tg.AccountUpdateBusinessWorkHoursRequest) (any, error) {
return r.onAccountUpdateBusinessWorkHours(ctx, layerRequest)
})
registerRPC[*tg.AccountUpdateBusinessLocationRequest](d, tlprofile.SemanticMethodAccountUpdateBusinessLocation, func(ctx context.Context, layerRequest *tg.AccountUpdateBusinessLocationRequest) (any, error) {
return r.onAccountUpdateBusinessLocation(ctx, layerRequest)
})
registerRPC[*tg.AccountUpdateBusinessIntroRequest](d, tlprofile.SemanticMethodAccountUpdateBusinessIntro, func(ctx context.Context, layerRequest *tg.AccountUpdateBusinessIntroRequest) (any, error) {
return r.onAccountUpdateBusinessIntro(ctx, layerRequest)
})
registerRPC[*tg.AccountUpdateBusinessGreetingMessageRequest](d, tlprofile.SemanticMethodAccountUpdateBusinessGreetingMessage, func(ctx context.Context, layerRequest *tg.AccountUpdateBusinessGreetingMessageRequest) (any, error) {
return r.onAccountUpdateBusinessGreetingMessage(ctx, layerRequest)
})
registerRPC[*tg.AccountUpdateBusinessAwayMessageRequest](d, tlprofile.SemanticMethodAccountUpdateBusinessAwayMessage, func(ctx context.Context, layerRequest *tg.AccountUpdateBusinessAwayMessageRequest) (any, error) {
return r.onAccountUpdateBusinessAwayMessage(ctx, layerRequest)
})
registerRPC[*tg.AccountGetBusinessChatLinksRequest](d, tlprofile.SemanticMethodAccountGetBusinessChatLinks, func(ctx context.Context, layerRequest *tg.AccountGetBusinessChatLinksRequest) (any, error) {
return r.onAccountGetBusinessChatLinks(ctx)
})
registerRPC[*tg.AccountCreateBusinessChatLinkRequest](d, tlprofile.SemanticMethodAccountCreateBusinessChatLink, func(ctx context.Context, layerRequest *tg.AccountCreateBusinessChatLinkRequest) (any, error) {
return r.onAccountCreateBusinessChatLink(ctx, layerRequest.
Link)
})
registerRPC[*tg.AccountEditBusinessChatLinkRequest](d, tlprofile.SemanticMethodAccountEditBusinessChatLink, func(ctx context.Context, layerRequest *tg.AccountEditBusinessChatLinkRequest) (any, error) {
return r.onAccountEditBusinessChatLink(ctx, layerRequest)
})
registerRPC[*tg.AccountDeleteBusinessChatLinkRequest](d, tlprofile.SemanticMethodAccountDeleteBusinessChatLink, func(ctx context.Context, layerRequest *tg.AccountDeleteBusinessChatLinkRequest) (any, error) {
return r.onAccountDeleteBusinessChatLink(ctx, layerRequest.
Slug)
})
registerRPC[*tg.AccountResolveBusinessChatLinkRequest](d, tlprofile.SemanticMethodAccountResolveBusinessChatLink, func(ctx context.Context, layerRequest *tg.AccountResolveBusinessChatLinkRequest) (any, error) {
return r.onAccountResolveBusinessChatLink(ctx, layerRequest.
Slug)
})
registerRPC[*tg.AccountUpdateConnectedBotRequest](d, tlprofile.SemanticMethodAccountUpdateConnectedBot, func(ctx context.Context, layerRequest *tg.AccountUpdateConnectedBotRequest) (any, error) {
return r.onAccountUpdateConnectedBot(ctx, layerRequest)
})
registerRPC[*tg.AccountGetBotBusinessConnectionRequest](d, tlprofile.SemanticMethodAccountGetBotBusinessConnection, func(ctx context.Context, layerRequest *tg.AccountGetBotBusinessConnectionRequest) (any, error) {
connectionID := layerRequest.
ConnectionID
_ = connectionID
if _, _, err := r.currentUserID(ctx); err != nil {
return nil, internalErr()
}
return nil, tgerr400("BOT_BUSINESS_MISSING")
})
d.OnAccountToggleConnectedBotPaused(r.onAccountToggleConnectedBotPaused)
d.OnAccountDisablePeerConnectedBot(r.onAccountDisablePeerConnectedBot)
d.OnAccountGetReactionsNotifySettings(r.onAccountGetReactionsNotifySettings)
d.OnAccountSetReactionsNotifySettings(r.onAccountSetReactionsNotifySettings)
d.OnAccountGetContactSignUpNotification(r.onAccountGetContactSignUpNotification)
d.OnAccountSetContactSignUpNotification(r.onAccountSetContactSignUpNotification)
d.OnAccountGetThemes(r.onAccountGetThemes)
d.OnAccountGetChatThemes(func(ctx context.Context, hash int64) (tg.AccountThemesClass, error) {
registerRPC[*tg.AccountToggleConnectedBotPausedRequest](d, tlprofile.SemanticMethodAccountToggleConnectedBotPaused, func(ctx context.Context, layerRequest *tg.AccountToggleConnectedBotPausedRequest) (any, error) {
return r.onAccountToggleConnectedBotPaused(ctx, layerRequest)
})
registerRPC[*tg.AccountDisablePeerConnectedBotRequest](d, tlprofile.SemanticMethodAccountDisablePeerConnectedBot, func(ctx context.Context, layerRequest *tg.AccountDisablePeerConnectedBotRequest) (any, error) {
return r.onAccountDisablePeerConnectedBot(ctx, layerRequest.
Peer)
})
registerRPC[*tg.AccountGetReactionsNotifySettingsRequest](d, tlprofile.SemanticMethodAccountGetReactionsNotifySettings, func(ctx context.Context, layerRequest *tg.AccountGetReactionsNotifySettingsRequest) (any, error) {
return r.onAccountGetReactionsNotifySettings(ctx)
})
registerRPC[*tg.AccountSetReactionsNotifySettingsRequest](d, tlprofile.SemanticMethodAccountSetReactionsNotifySettings, func(ctx context.Context, layerRequest *tg.AccountSetReactionsNotifySettingsRequest) (any, error) {
return r.onAccountSetReactionsNotifySettings(ctx, layerRequest.
Settings)
})
registerRPC[*tg.AccountGetContactSignUpNotificationRequest](d, tlprofile.SemanticMethodAccountGetContactSignUpNotification, func(ctx context.Context, layerRequest *tg.AccountGetContactSignUpNotificationRequest) (any, error) {
return r.onAccountGetContactSignUpNotification(ctx)
})
registerRPC[*tg.AccountSetContactSignUpNotificationRequest](d, tlprofile.SemanticMethodAccountSetContactSignUpNotification, func(ctx context.Context, layerRequest *tg.AccountSetContactSignUpNotificationRequest) (any, error) {
return r.onAccountSetContactSignUpNotification(ctx, layerRequest.
Silent)
})
registerRPC[*tg.AccountGetThemesRequest](d, tlprofile.SemanticMethodAccountGetThemes, func(ctx context.Context, layerRequest *tg.AccountGetThemesRequest) (any, error) {
return r.onAccountGetThemes(ctx, layerRequest)
})
registerRPC[*tg.AccountGetChatThemesRequest](d, tlprofile.SemanticMethodAccountGetChatThemes, func(ctx context.Context, layerRequest *tg.AccountGetChatThemesRequest) (any, error) {
hash := layerRequest.
Hash
_ = hash
if _, _, err := r.currentUserID(ctx); err != nil {
return nil, internalErr()
}
return tdesktop.ChatThemes(hash), nil
})
registerRPC[
// 自定义云主题(Create a New Theme 全链路):upload→create→update→save→install→get。
d.OnAccountUploadTheme(r.onAccountUploadTheme)
d.OnAccountCreateTheme(r.onAccountCreateTheme)
d.OnAccountUpdateTheme(r.onAccountUpdateTheme)
d.OnAccountSaveTheme(r.onAccountSaveTheme)
d.OnAccountInstallTheme(r.onAccountInstallTheme)
d.OnAccountGetTheme(r.onAccountGetTheme)
d.OnAccountGetWallPapers(func(ctx context.Context, hash int64) (tg.AccountWallPapersClass, error) {
*tg.AccountUploadThemeRequest](d, tlprofile.SemanticMethodAccountUploadTheme, func(ctx context.Context, layerRequest *tg.AccountUploadThemeRequest) (any, error) {
return r.onAccountUploadTheme(ctx, layerRequest)
})
registerRPC[*tg.AccountCreateThemeRequest](d, tlprofile.SemanticMethodAccountCreateTheme, func(ctx context.Context, layerRequest *tg.AccountCreateThemeRequest) (any, error) {
return r.onAccountCreateTheme(ctx, layerRequest)
})
registerRPC[*tg.AccountUpdateThemeRequest](d, tlprofile.SemanticMethodAccountUpdateTheme, func(ctx context.Context, layerRequest *tg.AccountUpdateThemeRequest) (any, error) {
return r.onAccountUpdateTheme(ctx, layerRequest)
})
registerRPC[*tg.AccountSaveThemeRequest](d, tlprofile.SemanticMethodAccountSaveTheme, func(ctx context.Context, layerRequest *tg.AccountSaveThemeRequest) (any, error) {
return r.onAccountSaveTheme(ctx, layerRequest)
})
registerRPC[*tg.AccountInstallThemeRequest](d, tlprofile.SemanticMethodAccountInstallTheme, func(ctx context.Context, layerRequest *tg.AccountInstallThemeRequest) (any, error) {
return r.onAccountInstallTheme(ctx, layerRequest)
})
registerRPC[*tg.AccountGetThemeRequest](d, tlprofile.SemanticMethodAccountGetTheme, func(ctx context.Context, layerRequest *tg.AccountGetThemeRequest) (any, error) {
return r.onAccountGetTheme(ctx, layerRequest)
})
registerRPC[*tg.AccountGetWallPapersRequest](d, tlprofile.SemanticMethodAccountGetWallPapers, func(ctx context.Context, layerRequest *tg.AccountGetWallPapersRequest) (any, error) {
hash := layerRequest.
Hash
_ = hash
if _, _, err := r.currentUserID(ctx); err != nil {
return nil, internalErr()
}
return tdesktop.WallPapers(hash), nil
})
d.OnAccountGetWallPaper(func(ctx context.Context, wallpaper tg.InputWallPaperClass) (tg.WallPaperClass, error) {
registerRPC[*tg.AccountGetWallPaperRequest](d, tlprofile.SemanticMethodAccountGetWallPaper, func(ctx context.Context, layerRequest *tg.AccountGetWallPaperRequest) (any, error) {
wallpaper := layerRequest.
Wallpaper
_ = wallpaper
if _, _, err := r.currentUserID(ctx); err != nil {
return nil, internalErr()
}
@ -109,7 +251,11 @@ func (r *Router) registerAccount(d *tg.ServerDispatcher) {
}
return found, nil
})
d.OnAccountGetMultiWallPapers(func(ctx context.Context, wallpapers []tg.InputWallPaperClass) ([]tg.WallPaperClass, error) {
registerRPC[*tg.AccountGetMultiWallPapersRequest](d, tlprofile.SemanticMethodAccountGetMultiWallPapers, func(ctx context.Context, layerRequest *tg.AccountGetMultiWallPapersRequest) (any, error) {
wallpapers := layerRequest.
Wallpapers
_ = wallpapers
if _, _, err := r.currentUserID(ctx); err != nil {
return nil, internalErr()
}
@ -122,7 +268,7 @@ func (r *Router) registerAccount(d *tg.ServerDispatcher) {
}
return found, nil
})
d.OnAccountSaveWallPaper(func(ctx context.Context, req *tg.AccountSaveWallPaperRequest) (bool, error) {
registerRPC[*tg.AccountSaveWallPaperRequest](d, tlprofile.SemanticMethodAccountSaveWallPaper, func(ctx context.Context, req *tg.AccountSaveWallPaperRequest) (any, error) {
if _, _, err := r.currentUserID(ctx); err != nil {
return false, internalErr()
}
@ -134,7 +280,7 @@ func (r *Router) registerAccount(d *tg.ServerDispatcher) {
}
return true, nil
})
d.OnAccountInstallWallPaper(func(ctx context.Context, req *tg.AccountInstallWallPaperRequest) (bool, error) {
registerRPC[*tg.AccountInstallWallPaperRequest](d, tlprofile.SemanticMethodAccountInstallWallPaper, func(ctx context.Context, req *tg.AccountInstallWallPaperRequest) (any, error) {
if _, _, err := r.currentUserID(ctx); err != nil {
return false, internalErr()
}
@ -146,13 +292,13 @@ func (r *Router) registerAccount(d *tg.ServerDispatcher) {
}
return true, nil
})
d.OnAccountResetWallPapers(func(ctx context.Context) (bool, error) {
registerRPC[*tg.AccountResetWallPapersRequest](d, tlprofile.SemanticMethodAccountResetWallPapers, func(ctx context.Context, layerRequest *tg.AccountResetWallPapersRequest) (any, error) {
if _, _, err := r.currentUserID(ctx); err != nil {
return false, internalErr()
}
return true, nil
})
d.OnAccountGetUniqueGiftChatThemes(func(ctx context.Context, req *tg.AccountGetUniqueGiftChatThemesRequest) (tg.AccountChatThemesClass, error) {
registerRPC[*tg.AccountGetUniqueGiftChatThemesRequest](d, tlprofile.SemanticMethodAccountGetUniqueGiftChatThemes, func(ctx context.Context, req *tg.AccountGetUniqueGiftChatThemesRequest) (any, error) {
if _, _, err := r.currentUserID(ctx); err != nil {
return nil, internalErr()
}
@ -161,71 +307,151 @@ func (r *Router) registerAccount(d *tg.ServerDispatcher) {
}
return tdesktop.UniqueGiftChatThemes(req.Hash), nil
})
d.OnAccountGetRecentEmojiStatuses(func(ctx context.Context, hash int64) (tg.AccountEmojiStatusesClass, error) {
registerRPC[*tg.AccountGetRecentEmojiStatusesRequest](d, tlprofile.SemanticMethodAccountGetRecentEmojiStatuses, func(ctx context.Context, layerRequest *tg.AccountGetRecentEmojiStatusesRequest) (any, error) {
hash := layerRequest.
Hash
_ = hash
return &tg.AccountEmojiStatuses{Hash: 0, Statuses: []tg.EmojiStatusClass{}}, nil
})
d.OnAccountClearRecentEmojiStatuses(func(ctx context.Context) (bool, error) {
registerRPC[*tg.AccountClearRecentEmojiStatusesRequest](d, tlprofile.SemanticMethodAccountClearRecentEmojiStatuses, func(ctx context.Context, layerRequest *tg.AccountClearRecentEmojiStatusesRequest) (any, error) {
return true, nil
})
d.OnAccountUpdateEmojiStatus(r.onAccountUpdateEmojiStatus)
d.OnAccountUpdateColor(r.onAccountUpdateColor)
d.OnAccountGetDefaultProfilePhotoEmojis(r.onAccountGetDefaultProfilePhotoEmojis)
d.OnAccountGetDefaultBackgroundEmojis(r.onAccountGetDefaultBackgroundEmojis)
d.OnAccountGetChannelDefaultEmojiStatuses(func(ctx context.Context, hash int64) (tg.AccountEmojiStatusesClass, error) {
registerRPC[*tg.AccountUpdateEmojiStatusRequest](d, tlprofile.SemanticMethodAccountUpdateEmojiStatus, func(ctx context.Context, layerRequest *tg.AccountUpdateEmojiStatusRequest) (any, error) {
return r.onAccountUpdateEmojiStatus(ctx, layerRequest.
EmojiStatus)
})
registerRPC[*tg.AccountUpdateColorRequest](d, tlprofile.SemanticMethodAccountUpdateColor, func(ctx context.Context, layerRequest *tg.AccountUpdateColorRequest) (any, error) {
return r.onAccountUpdateColor(ctx, layerRequest)
})
registerRPC[*tg.AccountGetDefaultProfilePhotoEmojisRequest](d, tlprofile.SemanticMethodAccountGetDefaultProfilePhotoEmojis, func(ctx context.Context, layerRequest *tg.AccountGetDefaultProfilePhotoEmojisRequest) (any, error) {
return r.onAccountGetDefaultProfilePhotoEmojis(ctx, layerRequest.
Hash)
})
registerRPC[*tg.AccountGetDefaultBackgroundEmojisRequest](d, tlprofile.SemanticMethodAccountGetDefaultBackgroundEmojis, func(ctx context.Context, layerRequest *tg.AccountGetDefaultBackgroundEmojisRequest) (any, error) {
return r.onAccountGetDefaultBackgroundEmojis(ctx, layerRequest.
Hash)
})
registerRPC[*tg.AccountGetChannelDefaultEmojiStatusesRequest](d, tlprofile.SemanticMethodAccountGetChannelDefaultEmojiStatuses, func(ctx context.Context, layerRequest *tg.AccountGetChannelDefaultEmojiStatusesRequest) (any, error) {
hash := layerRequest.
Hash
_ = hash
return &tg.AccountEmojiStatuses{Hash: 0, Statuses: []tg.EmojiStatusClass{}}, nil
})
d.OnAccountGetChannelRestrictedStatusEmojis(func(ctx context.Context, hash int64) (tg.EmojiListClass, error) {
registerRPC[*tg.AccountGetChannelRestrictedStatusEmojisRequest](d, tlprofile.SemanticMethodAccountGetChannelRestrictedStatusEmojis, func(ctx context.Context, layerRequest *tg.AccountGetChannelRestrictedStatusEmojisRequest) (any, error) {
hash := layerRequest.
Hash
_ = hash
return tdesktop.DefaultGroupPhotoEmojis(), nil
})
d.OnAccountSetContentSettings(r.onAccountSetContentSettings)
d.OnAccountGetContentSettings(r.onAccountGetContentSettings)
d.OnAccountGetGlobalPrivacySettings(r.onAccountGetGlobalPrivacySettings)
d.OnAccountSetGlobalPrivacySettings(r.onAccountSetGlobalPrivacySettings)
d.OnAccountGetPasskeys(r.onAccountGetPasskeys)
d.OnAccountInitPasskeyRegistration(r.onAccountInitPasskeyRegistration)
d.OnAccountRegisterPasskey(r.onAccountRegisterPasskey)
d.OnAccountDeletePasskey(r.onAccountDeletePasskey)
d.OnAccountGetWebAuthorizations(func(ctx context.Context) (*tg.AccountWebAuthorizations, error) {
registerRPC[*tg.AccountSetContentSettingsRequest](d, tlprofile.SemanticMethodAccountSetContentSettings, func(ctx context.Context, layerRequest *tg.AccountSetContentSettingsRequest) (any, error) {
return r.onAccountSetContentSettings(ctx, layerRequest)
})
registerRPC[*tg.AccountGetContentSettingsRequest](d, tlprofile.SemanticMethodAccountGetContentSettings, func(ctx context.Context, layerRequest *tg.AccountGetContentSettingsRequest) (any, error) {
return r.onAccountGetContentSettings(ctx)
})
registerRPC[*tg.AccountGetGlobalPrivacySettingsRequest](d, tlprofile.SemanticMethodAccountGetGlobalPrivacySettings, func(ctx context.Context, layerRequest *tg.AccountGetGlobalPrivacySettingsRequest) (any, error) {
return r.onAccountGetGlobalPrivacySettings(ctx)
})
registerRPC[*tg.AccountSetGlobalPrivacySettingsRequest](d, tlprofile.SemanticMethodAccountSetGlobalPrivacySettings, func(ctx context.Context, layerRequest *tg.AccountSetGlobalPrivacySettingsRequest) (any, error) {
return r.onAccountSetGlobalPrivacySettings(ctx, layerRequest.
Settings)
})
registerRPC[*tg.AccountGetPasskeysRequest](d, tlprofile.SemanticMethodAccountGetPasskeys, func(ctx context.Context, layerRequest *tg.AccountGetPasskeysRequest) (any, error) {
return r.onAccountGetPasskeys(ctx)
})
registerRPC[*tg.AccountInitPasskeyRegistrationRequest](d, tlprofile.SemanticMethodAccountInitPasskeyRegistration, func(ctx context.Context, layerRequest *tg.AccountInitPasskeyRegistrationRequest) (any, error) {
return r.onAccountInitPasskeyRegistration(ctx)
})
registerRPC[*tg.AccountRegisterPasskeyRequest](d, tlprofile.SemanticMethodAccountRegisterPasskey, func(ctx context.Context, layerRequest *tg.AccountRegisterPasskeyRequest) (any, error) {
return r.onAccountRegisterPasskey(ctx, layerRequest.
Credential)
})
registerRPC[*tg.AccountDeletePasskeyRequest](d, tlprofile.SemanticMethodAccountDeletePasskey, func(ctx context.Context, layerRequest *tg.AccountDeletePasskeyRequest) (any, error) {
return r.onAccountDeletePasskey(ctx, layerRequest.
ID)
})
registerRPC[*tg.AccountGetWebAuthorizationsRequest](d, tlprofile.SemanticMethodAccountGetWebAuthorizations, func(ctx context.Context, layerRequest *tg.AccountGetWebAuthorizationsRequest) (any, error) {
return tdesktop.WebAuthorizations(), nil
})
d.OnAccountResetWebAuthorization(func(ctx context.Context, hash int64) (bool, error) {
registerRPC[*tg.AccountResetWebAuthorizationRequest](d, tlprofile.SemanticMethodAccountResetWebAuthorization, func(ctx context.Context, layerRequest *tg.AccountResetWebAuthorizationRequest) (any, error) {
hash := layerRequest.
Hash
_ = hash
return true, nil
})
d.OnAccountResetWebAuthorizations(func(ctx context.Context) (bool, error) {
registerRPC[*tg.AccountResetWebAuthorizationsRequest](d, tlprofile.SemanticMethodAccountResetWebAuthorizations, func(ctx context.Context, layerRequest *tg.AccountResetWebAuthorizationsRequest) (
// account.getWebBrowserSettingstelesrv 不接入网页浏览器web bot集成返回空设置
// 无内置浏览器例外、不强制外部浏览器。Android 启动时会拉取,缺它会反复 500
// NOT_IMPLEMENTED。空结构 Hash=0客户端按默认内置浏览器、无例外渲染。
any, error) {
return true, nil
})
// account.getWebBrowserSettingstelesrv 不接入网页浏览器web bot集成返回空设置
// 无内置浏览器例外、不强制外部浏览器。Android 启动时会拉取,缺它会反复 500
// NOT_IMPLEMENTED。空结构 Hash=0客户端按默认内置浏览器、无例外渲染。
d.OnAccountGetWebBrowserSettings(func(ctx context.Context, hash int64) (tg.AccountWebBrowserSettingsClass, error) {
registerRPC[*tg.AccountGetWebBrowserSettingsRequest](d, tlprofile.SemanticMethodAccountGetWebBrowserSettings, func(ctx context.Context, layerRequest *tg.AccountGetWebBrowserSettingsRequest) (any, error) {
hash := layerRequest.
Hash
_ = hash
return &tg.AccountWebBrowserSettings{}, nil
})
d.OnAccountGetNotifyExceptions(r.onAccountGetNotifyExceptions)
d.OnAccountGetAutoDownloadSettings(func(ctx context.Context) (*tg.AccountAutoDownloadSettings, error) {
registerRPC[*tg.AccountGetNotifyExceptionsRequest](d, tlprofile.SemanticMethodAccountGetNotifyExceptions, func(ctx context.Context, layerRequest *tg.AccountGetNotifyExceptionsRequest) (any, error) {
return r.onAccountGetNotifyExceptions(ctx, layerRequest)
})
registerRPC[*tg.AccountGetAutoDownloadSettingsRequest](d, tlprofile.SemanticMethodAccountGetAutoDownloadSettings, func(ctx context.Context, layerRequest *tg.AccountGetAutoDownloadSettingsRequest) (any, error) {
return tdesktop.AutoDownloadSettings(), nil
})
d.OnAccountSaveAutoDownloadSettings(func(ctx context.Context, req *tg.AccountSaveAutoDownloadSettingsRequest) (bool, error) {
registerRPC[*tg.AccountSaveAutoDownloadSettingsRequest](d, tlprofile.SemanticMethodAccountSaveAutoDownloadSettings, func(ctx context.Context, req *tg.AccountSaveAutoDownloadSettingsRequest) (any, error) {
return true, nil
})
d.OnAccountSaveMusic(r.onAccountSaveMusic)
d.OnAccountGetSavedMusicIDs(r.onAccountGetSavedMusicIDs)
d.OnAccountGetSavedRingtones(func(ctx context.Context, hash int64) (tg.AccountSavedRingtonesClass, error) {
registerRPC[*tg.AccountSaveMusicRequest](d, tlprofile.SemanticMethodAccountSaveMusic, func(ctx context.Context, layerRequest *tg.AccountSaveMusicRequest) (any, error) {
return r.onAccountSaveMusic(ctx, layerRequest)
})
registerRPC[*tg.AccountGetSavedMusicIDsRequest](d, tlprofile.SemanticMethodAccountGetSavedMusicIDs, func(ctx context.Context, layerRequest *tg.AccountGetSavedMusicIDsRequest) (any, error) {
return r.onAccountGetSavedMusicIDs(ctx, layerRequest.
Hash)
})
registerRPC[*tg.AccountGetSavedRingtonesRequest](d, tlprofile.SemanticMethodAccountGetSavedRingtones, func(ctx context.Context, layerRequest *tg.AccountGetSavedRingtonesRequest) (any, error) {
hash := layerRequest.
Hash
_ = hash
if _, _, err := r.currentUserID(ctx); err != nil {
return nil, internalErr()
}
return &tg.AccountSavedRingtones{Hash: 0, Ringtones: []tg.DocumentClass{}}, nil
})
d.OnAccountGetAccountTTL(r.onAccountGetAccountTTL)
d.OnAccountSetAccountTTL(r.onAccountSetAccountTTL)
d.OnAccountSetAuthorizationTTL(func(ctx context.Context, authorizationttldays int) (bool, error) {
registerRPC[*tg.AccountGetAccountTTLRequest](d, tlprofile.SemanticMethodAccountGetAccountTTL, func(ctx context.Context, layerRequest *tg.AccountGetAccountTTLRequest) (any, error) {
return r.onAccountGetAccountTTL(ctx)
})
registerRPC[*tg.AccountSetAccountTTLRequest](d, tlprofile.SemanticMethodAccountSetAccountTTL, func(ctx context.Context, layerRequest *tg.AccountSetAccountTTLRequest) (any, error) {
return r.onAccountSetAccountTTL(ctx, layerRequest.
TTL)
})
registerRPC[*tg.AccountSetAuthorizationTTLRequest](d, tlprofile.SemanticMethodAccountSetAuthorizationTTL, func(ctx context.Context, layerRequest *tg.AccountSetAuthorizationTTLRequest) (any, error) {
authorizationttldays := layerRequest.
AuthorizationTTLDays
_ = authorizationttldays
return true, nil
})
d.OnAccountChangeAuthorizationSettings(func(ctx context.Context, req *tg.AccountChangeAuthorizationSettingsRequest) (bool, error) {
registerRPC[*tg.AccountChangeAuthorizationSettingsRequest](d, tlprofile.SemanticMethodAccountChangeAuthorizationSettings, func(ctx context.Context, req *tg.AccountChangeAuthorizationSettingsRequest) (any, error) {
return true, nil
})
d.OnAccountResetPassword(r.onAccountResetPassword)
d.OnAccountDeclinePasswordReset(r.onAccountDeclinePasswordReset)
d.OnAccountUpdateStatus(r.onAccountUpdateStatus)
registerRPC[*tg.AccountResetPasswordRequest](d, tlprofile.SemanticMethodAccountResetPassword, func(ctx context.Context, layerRequest *tg.AccountResetPasswordRequest) (any, error) {
return r.onAccountResetPassword(ctx)
})
registerRPC[*tg.AccountDeclinePasswordResetRequest](d, tlprofile.SemanticMethodAccountDeclinePasswordReset, func(ctx context.Context, layerRequest *tg.AccountDeclinePasswordResetRequest) (any, error) {
return r.onAccountDeclinePasswordReset(ctx)
})
registerRPC[*tg.AccountUpdateStatusRequest](d, tlprofile.SemanticMethodAccountUpdateStatus, func(ctx context.Context, layerRequest *tg.AccountUpdateStatusRequest) (any, error) {
return r.onAccountUpdateStatus(ctx, layerRequest.
Offline)
})
}
func (r *Router) onAccountGetPassword(ctx context.Context) (*tg.AccountPassword, error) {

View file

@ -8,17 +8,35 @@ import (
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tgerr"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/domain"
)
func (r *Router) registerAiCompose(d *tg.ServerDispatcher) {
d.OnAicomposeGetTones(r.onAicomposeGetTones)
d.OnAicomposeCreateTone(r.onAicomposeCreateTone)
d.OnAicomposeUpdateTone(r.onAicomposeUpdateTone)
d.OnAicomposeSaveTone(r.onAicomposeSaveTone)
d.OnAicomposeDeleteTone(r.onAicomposeDeleteTone)
d.OnAicomposeGetTone(r.onAicomposeGetTone)
d.OnAicomposeGetToneExample(r.onAicomposeGetToneExample)
func (r *Router) registerAiCompose(d *tlprofile.Dispatcher) {
registerRPC[*tg.AicomposeGetTonesRequest](d, tlprofile.SemanticMethodAicomposeGetTones, func(ctx context.Context, layerRequest *tg.AicomposeGetTonesRequest) (any, error) {
return r.onAicomposeGetTones(ctx, layerRequest.
Hash)
})
registerRPC[*tg.AicomposeCreateToneRequest](d, tlprofile.SemanticMethodAicomposeCreateTone, func(ctx context.Context, layerRequest *tg.AicomposeCreateToneRequest) (any, error) {
return r.onAicomposeCreateTone(ctx, layerRequest)
})
registerRPC[*tg.AicomposeUpdateToneRequest](d, tlprofile.SemanticMethodAicomposeUpdateTone, func(ctx context.Context, layerRequest *tg.AicomposeUpdateToneRequest) (any, error) {
return r.onAicomposeUpdateTone(ctx, layerRequest)
})
registerRPC[*tg.AicomposeSaveToneRequest](d, tlprofile.SemanticMethodAicomposeSaveTone, func(ctx context.Context, layerRequest *tg.AicomposeSaveToneRequest) (any, error) {
return r.onAicomposeSaveTone(ctx, layerRequest)
})
registerRPC[*tg.AicomposeDeleteToneRequest](d, tlprofile.SemanticMethodAicomposeDeleteTone, func(ctx context.Context, layerRequest *tg.AicomposeDeleteToneRequest) (any, error) {
return r.onAicomposeDeleteTone(ctx, layerRequest.
Tone)
})
registerRPC[*tg.AicomposeGetToneRequest](d, tlprofile.SemanticMethodAicomposeGetTone, func(ctx context.Context, layerRequest *tg.AicomposeGetToneRequest) (any, error) {
return r.onAicomposeGetTone(ctx, layerRequest.
Tone)
})
registerRPC[*tg.AicomposeGetToneExampleRequest](d, tlprofile.SemanticMethodAicomposeGetToneExample, func(ctx context.Context, layerRequest *tg.AicomposeGetToneExampleRequest) (any, error) {
return r.onAicomposeGetToneExample(ctx, layerRequest)
})
}
func (r *Router) onAicomposeGetTones(ctx context.Context, hash int64) (tg.AicomposeTonesClass, error) {

View file

@ -105,15 +105,11 @@ func TestLegacyAndroidAuthSignUpAllowedBeforeAuthorization(t *testing.T) {
if err != nil {
t.Fatalf("dispatch legacy auth.signUp: %v", err)
}
// Routed through the generated static client overlay + the normal gotd
// dispatcher, which boxes a class result (auth.Authorization) as *...Box.
box, ok := enc.(*tg.AuthAuthorizationBox)
// Routed through the generated static client overlay and sparse semantic
// dispatcher, which exposes the canonical concrete result at this test seam.
authorization, ok := enc.(*tg.AuthAuthorization)
if !ok {
t.Fatalf("response = %T, want *tg.AuthAuthorizationBox", enc)
}
authorization, ok := box.Authorization.(*tg.AuthAuthorization)
if !ok {
t.Fatalf("authorization = %T, want auth.authorization", box.Authorization)
t.Fatalf("response = %T, want *tg.AuthAuthorization", enc)
}
user, ok := authorization.User.(*tg.User)
if !ok || user.ID != auth.signUpUser.ID {

View file

@ -10,13 +10,14 @@ import (
"github.com/iamxvbaba/td/tg"
"go.uber.org/zap/zaptest"
"github.com/iamxvbaba/td/tlprofile"
compatandroid "telesrv/internal/compat/android"
)
type androidPrivateLayerFixture struct {
name string
privateID uint32
semantic tg.LayerSemanticID
semantic tlprofile.SemanticID
method string
wire func(*testing.T) []byte
}
@ -46,14 +47,14 @@ func TestAndroidPrivateLayerRPCsAdaptAcrossCanonicalBoundary(t *testing.T) {
}
private := bin.Buffer{Buf: append([]byte(nil), raw...)}
canonical, handled, err := compatandroid.UpgradePrivateLayerRPC(tg.LayerProfileCanonical, &private, tg.LayerDecodeLimits{})
canonical, handled, err := compatandroid.UpgradePrivateLayerRPC(tlprofile.ProfileCanonical, &private, tlprofile.Limits{})
if err != nil || !handled || canonical == nil {
t.Fatalf("canonical upgrade = value:%v handled:%v err:%v", canonical != nil, handled, err)
}
if private.Len() != 0 {
t.Fatalf("canonical upgrade left %d private bytes", private.Len())
}
canonicalID, ok := tg.LayerWireID(tg.LayerProfileCanonical, fixture.semantic)
canonicalID, ok := tlprofile.WireID(tlprofile.ProfileCanonical, fixture.semantic)
if !ok {
t.Fatalf("canonical profile has no wire id for %s", fixture.method)
}
@ -61,11 +62,11 @@ func TestAndroidPrivateLayerRPCsAdaptAcrossCanonicalBoundary(t *testing.T) {
t.Fatalf("canonical upgrade id = %#x err=%v, want %#x", got, peekErr, canonicalID)
}
for _, profile := range []tg.LayerProfile{tg.LayerProfile227, tg.LayerProfile228} {
for _, profile := range []tlprofile.Profile{tlprofile.Profile227, tlprofile.Profile228} {
profile := profile
t.Run(fmt.Sprintf("layer_%d", profile), func(t *testing.T) {
body := bin.Buffer{Buf: append([]byte(nil), raw...)}
admitted, err := r.AdmitLayer(profile, &body, tg.LayerDecodeLimits{})
admitted, err := r.AdmitLayer(profile, &body, tlprofile.Limits{})
if err != nil {
t.Fatalf("production exact admission: %v", err)
}
@ -77,11 +78,11 @@ func TestAndroidPrivateLayerRPCsAdaptAcrossCanonicalBoundary(t *testing.T) {
if call.Profile() != profile || call.Method() != fixture.semantic {
t.Fatalf("admitted call = profile:%d semantic:%#x, want profile:%d semantic:%#x", call.Profile(), call.Method(), profile, fixture.semantic)
}
category, method, ok := tg.LayerSemanticName(call.Method())
category, method, ok := tlprofile.SemanticName(call.Method())
if !ok || category != "function" || method != fixture.method {
t.Fatalf("admitted semantic name = (%q,%q,%v), want (function,%q,true)", category, method, ok, fixture.method)
}
wantWireID, ok := tg.LayerWireID(profile, fixture.semantic)
wantWireID, ok := tlprofile.WireID(profile, fixture.semantic)
if !ok || call.WireID() != wantWireID {
t.Fatalf("admitted exact id = %#x, want %#x (ok=%v)", call.WireID(), wantWireID, ok)
}
@ -95,12 +96,12 @@ func androidPrivateLayerFixtures() []androidPrivateLayerFixture {
return []androidPrivateLayerFixture{
{
name: "messages.forwardMessages_alias", privateID: 0x41d41ade,
semantic: tg.LayerSemanticMethodMessagesForwardMessages, method: "messages.forwardMessages",
semantic: tlprofile.SemanticMethodMessagesForwardMessages, method: "messages.forwardMessages",
wire: func(*testing.T) []byte { return androidPrivateForwardMessagesWire() },
},
{
name: "channels.inviteToChannel_alias", privateID: 0x199f3a6c,
semantic: tg.LayerSemanticMethodChannelsInviteToChannel, method: "channels.inviteToChannel",
semantic: tlprofile.SemanticMethodChannelsInviteToChannel, method: "channels.inviteToChannel",
wire: func(t *testing.T) []byte {
return androidPrivateAliasWire(t, 0x199f3a6c, &tg.ChannelsInviteToChannelRequest{
Channel: &tg.InputChannel{ChannelID: 41, AccessHash: 42},
@ -110,14 +111,14 @@ func androidPrivateLayerFixtures() []androidPrivateLayerFixture {
},
{
name: "updates.getDifference_alias", privateID: 0x25939651,
semantic: tg.LayerSemanticMethodUpdatesGetDifference, method: "updates.getDifference",
semantic: tlprofile.SemanticMethodUpdatesGetDifference, method: "updates.getDifference",
wire: func(t *testing.T) []byte {
return androidPrivateAliasWire(t, 0x25939651, &tg.UpdatesGetDifferenceRequest{Pts: 100, Date: 200, Qts: 3})
},
},
{
name: "messages.createChat_alias", privateID: 0x0034a818,
semantic: tg.LayerSemanticMethodMessagesCreateChat, method: "messages.createChat",
semantic: tlprofile.SemanticMethodMessagesCreateChat, method: "messages.createChat",
wire: func(t *testing.T) []byte {
return androidPrivateAliasWire(t, 0x0034a818, &tg.MessagesCreateChatRequest{
Users: []tg.InputUserClass{&tg.InputUser{UserID: 51, AccessHash: 52}},
@ -127,7 +128,7 @@ func androidPrivateLayerFixtures() []androidPrivateLayerFixture {
},
{
name: "messages.uploadMedia_transform", privateID: 0x519bc2b1,
semantic: tg.LayerSemanticMethodMessagesUploadMedia, method: "messages.uploadMedia",
semantic: tlprofile.SemanticMethodMessagesUploadMedia, method: "messages.uploadMedia",
wire: func(t *testing.T) []byte {
return androidPrivateRawWire(t, 0x519bc2b1, func(body *bin.Buffer) error {
if err := (&tg.InputPeerSelf{}).Encode(body); err != nil {
@ -139,7 +140,7 @@ func androidPrivateLayerFixtures() []androidPrivateLayerFixture {
},
{
name: "auth.signUp_transform", privateID: 0x80eee427,
semantic: tg.LayerSemanticMethodAuthSignUp, method: "auth.signUp",
semantic: tlprofile.SemanticMethodAuthSignUp, method: "auth.signUp",
wire: func(t *testing.T) []byte {
return androidPrivateRawWire(t, 0x80eee427, func(body *bin.Buffer) error {
body.PutString("+15550000228")
@ -152,7 +153,7 @@ func androidPrivateLayerFixtures() []androidPrivateLayerFixture {
},
{
name: "messages.getMessages_transform", privateID: 0x4222fa74,
semantic: tg.LayerSemanticMethodMessagesGetMessages, method: "messages.getMessages",
semantic: tlprofile.SemanticMethodMessagesGetMessages, method: "messages.getMessages",
wire: func(t *testing.T) []byte {
return androidPrivateRawWire(t, 0x4222fa74, func(body *bin.Buffer) error {
body.PutVectorHeader(2)
@ -164,7 +165,7 @@ func androidPrivateLayerFixtures() []androidPrivateLayerFixture {
},
{
name: "channels.getMessages_transform", privateID: 0x93d7b347,
semantic: tg.LayerSemanticMethodChannelsGetMessages, method: "channels.getMessages",
semantic: tlprofile.SemanticMethodChannelsGetMessages, method: "channels.getMessages",
wire: func(t *testing.T) []byte {
return androidPrivateRawWire(t, 0x93d7b347, func(body *bin.Buffer) error {
if err := (&tg.InputChannel{ChannelID: 81, AccessHash: 82}).Encode(body); err != nil {
@ -179,7 +180,7 @@ func androidPrivateLayerFixtures() []androidPrivateLayerFixture {
},
{
name: "bots.exportBotToken_transform", privateID: 0x0063b089,
semantic: tg.LayerSemanticMethodBotsExportBotToken, method: "bots.exportBotToken",
semantic: tlprofile.SemanticMethodBotsExportBotToken, method: "bots.exportBotToken",
wire: func(t *testing.T) []byte {
return androidPrivateRawWire(t, 0x0063b089, func(body *bin.Buffer) error {
body.PutLong(91)
@ -190,7 +191,7 @@ func androidPrivateLayerFixtures() []androidPrivateLayerFixture {
},
{
name: "account.registerDevice_transform", privateID: 0x637ea878,
semantic: tg.LayerSemanticMethodAccountRegisterDevice, method: "account.registerDevice",
semantic: tlprofile.SemanticMethodAccountRegisterDevice, method: "account.registerDevice",
wire: func(t *testing.T) []byte {
return androidPrivateRawWire(t, 0x637ea878, func(body *bin.Buffer) error {
body.PutInt(2)
@ -201,7 +202,7 @@ func androidPrivateLayerFixtures() []androidPrivateLayerFixture {
},
{
name: "contacts.search_transform", privateID: 0x11f812d8,
semantic: tg.LayerSemanticMethodContactsSearch, method: "contacts.search",
semantic: tlprofile.SemanticMethodContactsSearch, method: "contacts.search",
wire: func(t *testing.T) []byte {
return androidPrivateRawWire(t, 0x11f812d8, func(body *bin.Buffer) error {
body.PutString("private-query")
@ -212,7 +213,7 @@ func androidPrivateLayerFixtures() []androidPrivateLayerFixture {
},
{
name: "langpack.getLangPack_transform", privateID: 0x9ab5c58e,
semantic: tg.LayerSemanticMethodLangpackGetLangPack, method: "langpack.getLangPack",
semantic: tlprofile.SemanticMethodLangpackGetLangPack, method: "langpack.getLangPack",
wire: func(t *testing.T) []byte {
return androidPrivateRawWire(t, 0x9ab5c58e, func(body *bin.Buffer) error {
body.PutString("en")
@ -222,7 +223,7 @@ func androidPrivateLayerFixtures() []androidPrivateLayerFixture {
},
{
name: "langpack.getStrings_transform", privateID: 0x2e1ee318,
semantic: tg.LayerSemanticMethodLangpackGetStrings, method: "langpack.getStrings",
semantic: tlprofile.SemanticMethodLangpackGetStrings, method: "langpack.getStrings",
wire: func(t *testing.T) []byte {
return androidPrivateRawWire(t, 0x2e1ee318, func(body *bin.Buffer) error {
body.PutString("en")
@ -235,14 +236,14 @@ func androidPrivateLayerFixtures() []androidPrivateLayerFixture {
},
{
name: "langpack.getLanguages_transform", privateID: 0x800fd57d,
semantic: tg.LayerSemanticMethodLangpackGetLanguages, method: "langpack.getLanguages",
semantic: tlprofile.SemanticMethodLangpackGetLanguages, method: "langpack.getLanguages",
wire: func(t *testing.T) []byte {
return androidPrivateRawWire(t, 0x800fd57d, func(*bin.Buffer) error { return nil })
},
},
{
name: "messages.editChatCreator_transform", privateID: 0x8f38cd1f,
semantic: tg.LayerSemanticMethodMessagesEditChatCreator, method: "messages.editChatCreator",
semantic: tlprofile.SemanticMethodMessagesEditChatCreator, method: "messages.editChatCreator",
wire: func(t *testing.T) []byte {
return androidPrivateRawWire(t, 0x8f38cd1f, func(body *bin.Buffer) error {
if err := (&tg.InputChannel{ChannelID: 101, AccessHash: 102}).Encode(body); err != nil {

View file

@ -13,6 +13,7 @@ import (
"github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/app/auth"
"telesrv/internal/domain"
)
@ -21,38 +22,90 @@ import (
const devCodeLength = 5
// registerAuth 注册 auth.* RPC handler。
func (r *Router) registerAuth(d *tg.ServerDispatcher) {
d.OnAuthBindTempAuthKey(r.onAuthBindTempAuthKey)
d.OnAuthExportLoginToken(r.onAuthExportLoginToken)
d.OnAuthImportLoginToken(r.onAuthImportLoginToken)
d.OnAuthAcceptLoginToken(r.onAuthAcceptLoginToken)
d.OnAuthExportAuthorization(func(ctx context.Context, dcid int) (*tg.AuthExportedAuthorization, error) {
func (r *Router) registerAuth(d *tlprofile.Dispatcher) {
registerRPC[*tg.AuthBindTempAuthKeyRequest](d, tlprofile.SemanticMethodAuthBindTempAuthKey, func(ctx context.Context, layerRequest *tg.AuthBindTempAuthKeyRequest) (any, error) {
return r.onAuthBindTempAuthKey(ctx, layerRequest)
})
registerRPC[*tg.AuthExportLoginTokenRequest](d, tlprofile.SemanticMethodAuthExportLoginToken, func(ctx context.Context, layerRequest *tg.AuthExportLoginTokenRequest) (any, error) {
return r.onAuthExportLoginToken(ctx, layerRequest)
})
registerRPC[*tg.AuthImportLoginTokenRequest](d, tlprofile.SemanticMethodAuthImportLoginToken, func(ctx context.Context, layerRequest *tg.AuthImportLoginTokenRequest) (any, error) {
return r.onAuthImportLoginToken(ctx, layerRequest.
Token)
})
registerRPC[*tg.AuthAcceptLoginTokenRequest](d, tlprofile.SemanticMethodAuthAcceptLoginToken, func(ctx context.Context, layerRequest *tg.AuthAcceptLoginTokenRequest) (any, error) {
return r.onAuthAcceptLoginToken(ctx, layerRequest.
Token)
})
registerRPC[*tg.AuthExportAuthorizationRequest](d, tlprofile.SemanticMethodAuthExportAuthorization, func(ctx context.Context, layerRequest *tg.AuthExportAuthorizationRequest) (any, error) {
dcid := layerRequest.
DCID
_ = dcid
return nil, dcIDInvalidErr()
})
d.OnAuthImportAuthorization(func(ctx context.Context, req *tg.AuthImportAuthorizationRequest) (tg.AuthAuthorizationClass, error) {
registerRPC[*tg.AuthImportAuthorizationRequest](d, tlprofile.SemanticMethodAuthImportAuthorization, func(ctx context.Context, req *tg.AuthImportAuthorizationRequest) (any, error) {
return nil, dcIDInvalidErr()
})
d.OnAuthDropTempAuthKeys(func(ctx context.Context, exceptauthkeys []int64) (bool, error) {
registerRPC[*tg.AuthDropTempAuthKeysRequest](d, tlprofile.SemanticMethodAuthDropTempAuthKeys, func(ctx context.Context, layerRequest *tg.AuthDropTempAuthKeysRequest) (any, error) {
exceptauthkeys := layerRequest.
ExceptAuthKeys
_ = exceptauthkeys
return true, nil
})
d.OnAuthInitPasskeyLogin(r.onAuthInitPasskeyLogin)
d.OnAuthFinishPasskeyLogin(r.onAuthFinishPasskeyLogin)
d.OnAuthSendCode(r.onAuthSendCode)
d.OnAuthResendCode(r.onAuthResendCode)
d.OnAuthCancelCode(r.onAuthCancelCode)
d.OnAuthSignIn(r.onAuthSignIn)
d.OnAuthSignUp(r.onAuthSignUp)
d.OnAuthImportBotAuthorization(r.onAuthImportBotAuthorization)
d.OnAuthLogOut(r.onAuthLogOut)
d.OnAuthResetAuthorizations(r.onAuthResetAuthorizations)
d.OnAuthCheckPassword(r.onAuthCheckPassword)
d.OnAuthRequestPasswordRecovery(r.onAuthRequestPasswordRecovery)
d.OnAuthRecoverPassword(r.onAuthRecoverPassword)
d.OnAuthCheckRecoveryPassword(r.onAuthCheckRecoveryPassword)
d.OnAuthResetLoginEmail(r.onAuthResetLoginEmail)
registerRPC[*tg.AuthInitPasskeyLoginRequest](d, tlprofile.SemanticMethodAuthInitPasskeyLogin, func(ctx context.Context, layerRequest *tg.AuthInitPasskeyLoginRequest) (any, error) {
return r.onAuthInitPasskeyLogin(ctx, layerRequest)
})
registerRPC[*tg.AuthFinishPasskeyLoginRequest](d, tlprofile.SemanticMethodAuthFinishPasskeyLogin, func(ctx context.Context, layerRequest *tg.AuthFinishPasskeyLoginRequest) (any, error) {
return r.onAuthFinishPasskeyLogin(ctx, layerRequest)
})
registerRPC[*tg.AuthSendCodeRequest](d, tlprofile.SemanticMethodAuthSendCode, func(ctx context.Context, layerRequest *tg.AuthSendCodeRequest) (any, error) {
return r.onAuthSendCode(ctx, layerRequest)
})
registerRPC[*tg.AuthResendCodeRequest](d, tlprofile.SemanticMethodAuthResendCode, func(ctx context.Context, layerRequest *tg.AuthResendCodeRequest) (any, error) {
return r.onAuthResendCode(ctx, layerRequest)
})
registerRPC[*tg.AuthCancelCodeRequest](d, tlprofile.SemanticMethodAuthCancelCode, func(ctx context.Context, layerRequest *tg.AuthCancelCodeRequest) (any, error) {
return r.onAuthCancelCode(ctx, layerRequest)
})
registerRPC[*tg.AuthSignInRequest](d, tlprofile.SemanticMethodAuthSignIn, func(ctx context.Context, layerRequest *tg.AuthSignInRequest) (any, error) {
return r.onAuthSignIn(ctx, layerRequest)
})
registerRPC[*tg.AuthSignUpRequest](d, tlprofile.SemanticMethodAuthSignUp, func(ctx context.Context, layerRequest *tg.AuthSignUpRequest) (any, error) {
return r.onAuthSignUp(ctx, layerRequest)
})
registerRPC[*tg.AuthImportBotAuthorizationRequest](d, tlprofile.SemanticMethodAuthImportBotAuthorization, func(ctx context.Context, layerRequest *tg.AuthImportBotAuthorizationRequest) (any, error) {
return r.onAuthImportBotAuthorization(ctx, layerRequest)
})
registerRPC[*tg.AuthLogOutRequest](d, tlprofile.SemanticMethodAuthLogOut, func(ctx context.Context, layerRequest *tg.AuthLogOutRequest) (any, error) {
return r.onAuthLogOut(ctx)
})
registerRPC[*tg.AuthResetAuthorizationsRequest](d, tlprofile.SemanticMethodAuthResetAuthorizations, func(ctx context.Context, layerRequest *tg.AuthResetAuthorizationsRequest) (any, error) {
return r.onAuthResetAuthorizations(ctx)
})
registerRPC[*tg.AuthCheckPasswordRequest](d, tlprofile.SemanticMethodAuthCheckPassword, func(ctx context.Context, layerRequest *tg.AuthCheckPasswordRequest) (any, error) {
return r.onAuthCheckPassword(ctx, layerRequest.
Password)
})
registerRPC[*tg.AuthRequestPasswordRecoveryRequest](d, tlprofile.SemanticMethodAuthRequestPasswordRecovery, func(ctx context.Context, layerRequest *tg.AuthRequestPasswordRecoveryRequest) (any, error) {
return r.onAuthRequestPasswordRecovery(ctx)
})
registerRPC[*tg.AuthRecoverPasswordRequest](d, tlprofile.SemanticMethodAuthRecoverPassword, func(ctx context.Context, layerRequest *tg.AuthRecoverPasswordRequest) (any, error) {
return r.onAuthRecoverPassword(ctx, layerRequest)
})
registerRPC[*tg.AuthCheckRecoveryPasswordRequest](d, tlprofile.SemanticMethodAuthCheckRecoveryPassword, func(ctx context.Context, layerRequest *tg.AuthCheckRecoveryPasswordRequest) (
// onAuthBindTempAuthKey 记录 TDesktop 的 PFS temp→perm auth key 绑定。
any, error) {
return r.onAuthCheckRecoveryPassword(ctx, layerRequest.
Code)
})
registerRPC[*tg.AuthResetLoginEmailRequest](d, tlprofile.SemanticMethodAuthResetLoginEmail, func(ctx context.Context, layerRequest *tg.AuthResetLoginEmailRequest) (any, error) {
return r.onAuthResetLoginEmail(ctx, layerRequest)
})
}
// onAuthBindTempAuthKey 记录 TDesktop 的 PFS temp→perm auth key 绑定。
func (r *Router) onAuthBindTempAuthKey(ctx context.Context, req *tg.AuthBindTempAuthKeyRequest) (bool, error) {
if !layerRPCProfileEvidenceFresh(ctx) {
// The inner request is outside MTProto's mutable msg_id window. It may be

View file

@ -6,6 +6,7 @@ import (
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/domain"
)
@ -22,58 +23,165 @@ import (
// bump bot_info_version命令变更后 bots_hooks.PushBotCommandsChanged 给在线相关用户
// 推 updateBotCommands扇出封顶 100无 pts离线/超界用户靠 version bump 在下次
// getFullUser 重拉兜底。
func (r *Router) registerBots(d *tg.ServerDispatcher) {
d.OnBotsSendCustomRequest(r.onBotsSendCustomRequest)
d.OnBotsAnswerWebhookJSONQuery(r.onBotsAnswerWebhookJSONQuery)
d.OnBotsSetBotBroadcastDefaultAdminRights(r.onBotsSetBotBroadcastDefaultAdminRights)
d.OnBotsSetBotGroupDefaultAdminRights(r.onBotsSetBotGroupDefaultAdminRights)
d.OnBotsSetBotCommands(r.onBotsSetBotCommands)
d.OnBotsResetBotCommands(r.onBotsResetBotCommands)
d.OnBotsGetBotCommands(r.onBotsGetBotCommands)
d.OnBotsSetBotInfo(r.onBotsSetBotInfo)
d.OnBotsGetBotInfo(r.onBotsGetBotInfo)
d.OnBotsSetBotMenuButton(r.onBotsSetBotMenuButton)
d.OnBotsGetBotMenuButton(r.onBotsGetBotMenuButton)
d.OnBotsReorderUsernames(r.onBotsReorderUsernames)
d.OnBotsToggleUsername(r.onBotsToggleUsername)
d.OnBotsCanSendMessage(r.onBotsCanSendMessage)
d.OnBotsAllowSendMessage(r.onBotsAllowSendMessage)
d.OnBotsInvokeWebViewCustomMethod(r.onBotsInvokeWebViewCustomMethod)
d.OnBotsGetPopularAppBots(r.onBotsGetPopularAppBots)
d.OnBotsAddPreviewMedia(r.onBotsAddPreviewMedia)
d.OnBotsEditPreviewMedia(r.onBotsEditPreviewMedia)
d.OnBotsDeletePreviewMedia(r.onBotsDeletePreviewMedia)
d.OnBotsReorderPreviewMedias(r.onBotsReorderPreviewMedias)
d.OnBotsGetPreviewInfo(r.onBotsGetPreviewInfo)
d.OnBotsGetPreviewMedias(r.onBotsGetPreviewMedias)
d.OnBotsUpdateUserEmojiStatus(r.onBotsUpdateUserEmojiStatus)
d.OnBotsToggleUserEmojiStatusPermission(r.onBotsToggleUserEmojiStatusPermission)
d.OnBotsCheckDownloadFileParams(r.onBotsCheckDownloadFileParams)
d.OnBotsGetAdminedBots(r.onBotsGetAdminedBots)
d.OnBotsUpdateStarRefProgram(r.onBotsUpdateStarRefProgram)
d.OnBotsSetCustomVerification(r.onBotsSetCustomVerification)
d.OnBotsGetBotRecommendations(r.onBotsGetBotRecommendations)
d.OnBotsCheckUsername(r.onBotsCheckUsername)
d.OnBotsCreateBot(r.onBotsCreateBot)
d.OnBotsExportBotToken(r.onBotsExportBotToken)
d.OnBotsRequestWebViewButton(r.onBotsRequestWebViewButton)
d.OnBotsGetRequestedWebViewButton(r.onBotsGetRequestedWebViewButton)
d.OnBotsGetAccessSettings(r.onBotsGetAccessSettings)
d.OnBotsEditAccessSettings(r.onBotsEditAccessSettings)
// P3startBot 深链 + inline callback 闭环。
d.OnMessagesStartBot(r.onMessagesStartBot)
d.OnMessagesGetBotCallbackAnswer(r.onMessagesGetBotCallbackAnswer)
d.OnMessagesSetBotCallbackAnswer(r.onMessagesSetBotCallbackAnswer)
d.OnMessagesGetInlineBotResults(r.onMessagesGetInlineBotResults)
d.OnMessagesSetInlineBotResults(r.onMessagesSetInlineBotResults)
d.OnMessagesSendInlineBotResult(r.onMessagesSendInlineBotResult)
d.OnMessagesSavePreparedInlineMessage(r.onMessagesSavePreparedInlineMessage)
d.OnMessagesEditInlineBotMessage(r.onMessagesEditInlineBotMessage)
d.OnMessagesSetBotShippingResults(r.onMessagesSetBotShippingResults)
d.OnMessagesSetBotPrecheckoutResults(r.onMessagesSetBotPrecheckoutResults)
func (r *Router) registerBots(d *tlprofile.Dispatcher) {
registerRPC[*tg.BotsSendCustomRequestRequest](d, tlprofile.SemanticMethodBotsSendCustomRequest, func(ctx context.Context, layerRequest *tg.BotsSendCustomRequestRequest) (any, error) {
return r.onBotsSendCustomRequest(ctx, layerRequest)
})
registerRPC[*tg.BotsAnswerWebhookJSONQueryRequest](d, tlprofile.SemanticMethodBotsAnswerWebhookJSONQuery, func(ctx context.Context, layerRequest *tg.BotsAnswerWebhookJSONQueryRequest) (any, error) {
return r.onBotsAnswerWebhookJSONQuery(ctx, layerRequest)
})
registerRPC[*tg.BotsSetBotBroadcastDefaultAdminRightsRequest](d, tlprofile.SemanticMethodBotsSetBotBroadcastDefaultAdminRights, func(ctx context.Context, layerRequest *tg.BotsSetBotBroadcastDefaultAdminRightsRequest) (any, error) {
return r.onBotsSetBotBroadcastDefaultAdminRights(ctx, layerRequest.
AdminRights)
})
registerRPC[*tg.BotsSetBotGroupDefaultAdminRightsRequest](d, tlprofile.SemanticMethodBotsSetBotGroupDefaultAdminRights, func(ctx context.Context, layerRequest *tg.BotsSetBotGroupDefaultAdminRightsRequest) (any, error) {
return r.onBotsSetBotGroupDefaultAdminRights(ctx, layerRequest.
AdminRights)
})
registerRPC[*tg.BotsSetBotCommandsRequest](d, tlprofile.SemanticMethodBotsSetBotCommands, func(ctx context.Context, layerRequest *tg.BotsSetBotCommandsRequest) (any, error) {
return r.onBotsSetBotCommands(ctx, layerRequest)
})
registerRPC[*tg.BotsResetBotCommandsRequest](d, tlprofile.SemanticMethodBotsResetBotCommands, func(ctx context.Context, layerRequest *tg.BotsResetBotCommandsRequest) (any, error) {
return r.onBotsResetBotCommands(ctx, layerRequest)
})
registerRPC[*tg.BotsGetBotCommandsRequest](d, tlprofile.SemanticMethodBotsGetBotCommands, func(ctx context.Context, layerRequest *tg.BotsGetBotCommandsRequest) (any, error) {
return r.onBotsGetBotCommands(ctx, layerRequest)
})
registerRPC[*tg.BotsSetBotInfoRequest](d, tlprofile.SemanticMethodBotsSetBotInfo, func(ctx context.Context, layerRequest *tg.BotsSetBotInfoRequest) (any, error) {
return r.onBotsSetBotInfo(ctx, layerRequest)
})
registerRPC[*tg.BotsGetBotInfoRequest](d, tlprofile.SemanticMethodBotsGetBotInfo, func(ctx context.Context, layerRequest *tg.BotsGetBotInfoRequest) (any, error) {
return r.onBotsGetBotInfo(ctx, layerRequest)
})
registerRPC[*tg.BotsSetBotMenuButtonRequest](d, tlprofile.SemanticMethodBotsSetBotMenuButton, func(ctx context.Context, layerRequest *tg.BotsSetBotMenuButtonRequest) (any, error) {
return r.onBotsSetBotMenuButton(ctx, layerRequest)
})
registerRPC[*tg.BotsGetBotMenuButtonRequest](d, tlprofile.SemanticMethodBotsGetBotMenuButton, func(ctx context.Context, layerRequest *tg.BotsGetBotMenuButtonRequest) (any, error) {
return r.onBotsGetBotMenuButton(ctx, layerRequest.
UserID)
})
registerRPC[*tg.BotsReorderUsernamesRequest](d, tlprofile.SemanticMethodBotsReorderUsernames, func(ctx context.Context, layerRequest *tg.BotsReorderUsernamesRequest) (any, error) {
return r.onBotsReorderUsernames(ctx, layerRequest)
})
registerRPC[*tg.BotsToggleUsernameRequest](d, tlprofile.SemanticMethodBotsToggleUsername, func(ctx context.Context, layerRequest *tg.BotsToggleUsernameRequest) (any, error) {
return r.onBotsToggleUsername(ctx, layerRequest)
})
registerRPC[*tg.BotsCanSendMessageRequest](d, tlprofile.SemanticMethodBotsCanSendMessage, func(ctx context.Context, layerRequest *tg.BotsCanSendMessageRequest) (any, error) {
return r.onBotsCanSendMessage(ctx, layerRequest.
Bot)
})
registerRPC[*tg.BotsAllowSendMessageRequest](d, tlprofile.SemanticMethodBotsAllowSendMessage, func(ctx context.Context, layerRequest *tg.BotsAllowSendMessageRequest) (any, error) {
return r.onBotsAllowSendMessage(ctx, layerRequest.
Bot)
})
registerRPC[*tg.BotsInvokeWebViewCustomMethodRequest](d, tlprofile.SemanticMethodBotsInvokeWebViewCustomMethod, func(ctx context.Context, layerRequest *tg.BotsInvokeWebViewCustomMethodRequest) (any, error) {
return r.onBotsInvokeWebViewCustomMethod(ctx, layerRequest)
})
registerRPC[*tg.BotsGetPopularAppBotsRequest](d, tlprofile.SemanticMethodBotsGetPopularAppBots, func(ctx context.Context, layerRequest *tg.BotsGetPopularAppBotsRequest) (any, error) {
return r.onBotsGetPopularAppBots(ctx, layerRequest)
})
registerRPC[*tg.BotsAddPreviewMediaRequest](d, tlprofile.SemanticMethodBotsAddPreviewMedia, func(ctx context.Context, layerRequest *tg.BotsAddPreviewMediaRequest) (any, error) {
return r.onBotsAddPreviewMedia(ctx, layerRequest)
})
registerRPC[*tg.BotsEditPreviewMediaRequest](d, tlprofile.SemanticMethodBotsEditPreviewMedia, func(ctx context.Context, layerRequest *tg.BotsEditPreviewMediaRequest) (any, error) {
return r.onBotsEditPreviewMedia(ctx, layerRequest)
})
registerRPC[*tg.BotsDeletePreviewMediaRequest](d, tlprofile.SemanticMethodBotsDeletePreviewMedia, func(ctx context.Context, layerRequest *tg.BotsDeletePreviewMediaRequest) (any, error) {
return r.onBotsDeletePreviewMedia(ctx, layerRequest)
})
registerRPC[*tg.BotsReorderPreviewMediasRequest](d, tlprofile.SemanticMethodBotsReorderPreviewMedias, func(ctx context.Context, layerRequest *tg.BotsReorderPreviewMediasRequest) (any, error) {
return r.onBotsReorderPreviewMedias(ctx, layerRequest)
})
registerRPC[*tg.BotsGetPreviewInfoRequest](d, tlprofile.SemanticMethodBotsGetPreviewInfo, func(ctx context.Context, layerRequest *tg.BotsGetPreviewInfoRequest) (any, error) {
return r.onBotsGetPreviewInfo(ctx, layerRequest)
})
registerRPC[*tg.BotsGetPreviewMediasRequest](d, tlprofile.SemanticMethodBotsGetPreviewMedias, func(ctx context.Context, layerRequest *tg.BotsGetPreviewMediasRequest) (any, error) {
return r.onBotsGetPreviewMedias(ctx, layerRequest.
Bot)
})
registerRPC[*tg.BotsUpdateUserEmojiStatusRequest](d, tlprofile.SemanticMethodBotsUpdateUserEmojiStatus, func(ctx context.Context, layerRequest *tg.BotsUpdateUserEmojiStatusRequest) (any, error) {
return r.onBotsUpdateUserEmojiStatus(ctx, layerRequest)
})
registerRPC[*tg.BotsToggleUserEmojiStatusPermissionRequest](d, tlprofile.SemanticMethodBotsToggleUserEmojiStatusPermission, func(ctx context.Context, layerRequest *tg.BotsToggleUserEmojiStatusPermissionRequest) (any, error) {
return r.onBotsToggleUserEmojiStatusPermission(ctx, layerRequest)
})
registerRPC[*tg.BotsCheckDownloadFileParamsRequest](d, tlprofile.SemanticMethodBotsCheckDownloadFileParams, func(ctx context.Context, layerRequest *tg.BotsCheckDownloadFileParamsRequest) (any, error) {
return r.onBotsCheckDownloadFileParams(ctx, layerRequest)
})
registerRPC[*tg.BotsGetAdminedBotsRequest](d, tlprofile.SemanticMethodBotsGetAdminedBots, func(ctx context.Context, layerRequest *tg.BotsGetAdminedBotsRequest) (any, error) {
return r.onBotsGetAdminedBots(ctx)
})
registerRPC[*tg.BotsUpdateStarRefProgramRequest](d, tlprofile.SemanticMethodBotsUpdateStarRefProgram, func(ctx context.Context, layerRequest *tg.BotsUpdateStarRefProgramRequest) (any, error) {
return r.onBotsUpdateStarRefProgram(ctx, layerRequest)
})
registerRPC[*tg.BotsSetCustomVerificationRequest](d, tlprofile.SemanticMethodBotsSetCustomVerification, func(ctx context.Context, layerRequest *tg.BotsSetCustomVerificationRequest) (any, error) {
return r.onBotsSetCustomVerification(ctx, layerRequest)
})
registerRPC[*tg.BotsGetBotRecommendationsRequest](d, tlprofile.SemanticMethodBotsGetBotRecommendations, func(ctx context.Context, layerRequest *tg.BotsGetBotRecommendationsRequest) (any, error) {
return r.onBotsGetBotRecommendations(ctx, layerRequest.
Bot)
})
registerRPC[*tg.BotsCheckUsernameRequest](d, tlprofile.SemanticMethodBotsCheckUsername, func(ctx context.Context, layerRequest *tg.BotsCheckUsernameRequest) (any, error) {
return r.onBotsCheckUsername(ctx, layerRequest.
Username)
})
registerRPC[*tg.BotsCreateBotRequest](d, tlprofile.SemanticMethodBotsCreateBot, func(ctx context.Context, layerRequest *tg.BotsCreateBotRequest) (any, error) {
return r.onBotsCreateBot(ctx, layerRequest)
})
registerRPC[*tg.BotsExportBotTokenRequest](d, tlprofile.SemanticMethodBotsExportBotToken, func(ctx context.Context, layerRequest *tg.BotsExportBotTokenRequest) (any, error) {
return r.onBotsExportBotToken(ctx, layerRequest)
})
registerRPC[*tg.BotsRequestWebViewButtonRequest](d, tlprofile.SemanticMethodBotsRequestWebViewButton, func(ctx context.Context, layerRequest *tg.BotsRequestWebViewButtonRequest) (any, error) {
return r.onBotsRequestWebViewButton(ctx, layerRequest)
})
registerRPC[*tg.BotsGetRequestedWebViewButtonRequest](d, tlprofile.SemanticMethodBotsGetRequestedWebViewButton, func(ctx context.Context, layerRequest *tg.BotsGetRequestedWebViewButtonRequest) (any, error) {
return r.onBotsGetRequestedWebViewButton(ctx, layerRequest)
})
registerRPC[*tg.BotsGetAccessSettingsRequest](d, tlprofile.SemanticMethodBotsGetAccessSettings, func(ctx context.Context, layerRequest *tg.BotsGetAccessSettingsRequest) (
// P3startBot 深链 + inline callback 闭环。
any, error) {
return r.onBotsGetAccessSettings(ctx, layerRequest.
Bot)
})
registerRPC[*tg.BotsEditAccessSettingsRequest](d, tlprofile.SemanticMethodBotsEditAccessSettings, func(ctx context.Context, layerRequest *tg.BotsEditAccessSettingsRequest) (any, error) {
return r.onBotsEditAccessSettings(ctx, layerRequest)
})
registerRPC[*tg.MessagesStartBotRequest](d, tlprofile.SemanticMethodMessagesStartBot, func(ctx context.Context, layerRequest *tg.MessagesStartBotRequest) (any, error) {
return r.onMessagesStartBot(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetBotCallbackAnswerRequest](d, tlprofile.SemanticMethodMessagesGetBotCallbackAnswer, func(ctx context.Context, layerRequest *tg.MessagesGetBotCallbackAnswerRequest) (any, error) {
return r.onMessagesGetBotCallbackAnswer(ctx, layerRequest)
})
registerRPC[*tg.MessagesSetBotCallbackAnswerRequest](d, tlprofile.SemanticMethodMessagesSetBotCallbackAnswer, func(ctx context.Context, layerRequest *tg.MessagesSetBotCallbackAnswerRequest) (any, error) {
return r.onMessagesSetBotCallbackAnswer(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetInlineBotResultsRequest](d, tlprofile.SemanticMethodMessagesGetInlineBotResults, func(ctx context.Context, layerRequest *tg.MessagesGetInlineBotResultsRequest) (any, error) {
return r.onMessagesGetInlineBotResults(ctx, layerRequest)
})
registerRPC[*tg.MessagesSetInlineBotResultsRequest](d, tlprofile.SemanticMethodMessagesSetInlineBotResults, func(ctx context.Context, layerRequest *tg.MessagesSetInlineBotResultsRequest) (any, error) {
return r.onMessagesSetInlineBotResults(ctx, layerRequest)
})
registerRPC[*tg.MessagesSendInlineBotResultRequest](d, tlprofile.SemanticMethodMessagesSendInlineBotResult, func(ctx context.Context, layerRequest *tg.MessagesSendInlineBotResultRequest) (any, error) {
return r.onMessagesSendInlineBotResult(ctx, layerRequest)
})
registerRPC[*tg.MessagesSavePreparedInlineMessageRequest](d, tlprofile.SemanticMethodMessagesSavePreparedInlineMessage, func(ctx context.Context, layerRequest *tg.MessagesSavePreparedInlineMessageRequest) (any, error) {
return r.onMessagesSavePreparedInlineMessage(ctx, layerRequest)
})
registerRPC[*tg.MessagesEditInlineBotMessageRequest](d, tlprofile.SemanticMethodMessagesEditInlineBotMessage, func(ctx context.Context, layerRequest *tg.MessagesEditInlineBotMessageRequest) (any, error) {
return r.onMessagesEditInlineBotMessage(ctx, layerRequest)
})
registerRPC[*tg.MessagesSetBotShippingResultsRequest](d, tlprofile.SemanticMethodMessagesSetBotShippingResults, func(ctx context.Context, layerRequest *tg.MessagesSetBotShippingResultsRequest) (any, error) {
return r.onMessagesSetBotShippingResults(ctx, layerRequest)
})
registerRPC[*tg.MessagesSetBotPrecheckoutResultsRequest](d, tlprofile.SemanticMethodMessagesSetBotPrecheckoutResults, func(ctx context.Context,
// callerBotID 校验调用者本身是 bot 账号,返回其 user_idbot-only RPC 用)。
layerRequest *tg.MessagesSetBotPrecheckoutResultsRequest) (any, error) {
return r.onMessagesSetBotPrecheckoutResults(ctx, layerRequest)
})
}
// callerBotID 校验调用者本身是 bot 账号,返回其 user_idbot-only RPC 用)。
func (r *Router) callerBotID(ctx context.Context) (int64, error) {
userID, _, err := r.currentUserID(ctx)
if err != nil {

View file

@ -64,12 +64,8 @@ func TestChannelsDeleteChannelCascadesMonoforumForbiddenRPC(t *testing.T) {
if err != nil {
t.Fatalf("dispatch get dialogs: %v", err)
}
box, ok := enc.(*tg.MessagesDialogsBox)
if !ok {
t.Fatalf("dialogs response = %T, want box", enc)
}
ids := map[int64]struct{}{}
switch d := box.Dialogs.(type) {
switch d := enc.(type) {
case *tg.MessagesDialogs:
for _, ch := range d.Chats {
if c, ok := ch.(*tg.Channel); ok {
@ -83,7 +79,7 @@ func TestChannelsDeleteChannelCascadesMonoforumForbiddenRPC(t *testing.T) {
}
}
default:
t.Fatalf("dialogs = %T, want messages.dialogs(Slice)", box.Dialogs)
t.Fatalf("dialogs = %T, want messages.dialogs(Slice)", enc)
}
return ids
}

View file

@ -347,13 +347,12 @@ func TestChannelDialogCarriesChannelPts(t *testing.T) {
}
got := dispatch(&tg.MessagesGetDialogsRequest{OffsetPeer: &tg.InputPeerEmpty{}, Limit: 20})
box, ok := got.(*tg.MessagesDialogsBox)
dialogs, ok := got.(*tg.MessagesDialogs)
if !ok {
t.Fatalf("dialogs response = %T, want box", got)
t.Fatalf("dialogs response = %T, want *tg.MessagesDialogs", got)
}
dialogs, ok := box.Dialogs.(*tg.MessagesDialogs)
if !ok || len(dialogs.Dialogs) != 1 {
t.Fatalf("dialogs = %T %+v, want one channel dialog", box.Dialogs, box.Dialogs)
if len(dialogs.Dialogs) != 1 {
t.Fatalf("dialogs = %+v, want one channel dialog", dialogs)
}
dialog, ok := dialogs.Dialogs[0].(*tg.Dialog)
if !ok {

View file

@ -58,13 +58,9 @@ func TestChannelsDeleteChannelReturnsForbiddenChatAndHidesDialogRPC(t *testing.T
if err != nil {
t.Fatalf("dispatch get dialogs: %v", err)
}
box, ok := enc.(*tg.MessagesDialogsBox)
dialogs, ok := enc.(*tg.MessagesDialogs)
if !ok {
t.Fatalf("dialogs response = %T, want box", enc)
}
dialogs, ok := box.Dialogs.(*tg.MessagesDialogs)
if !ok {
t.Fatalf("dialogs = %T %+v, want messages.dialogs", box.Dialogs, box.Dialogs)
t.Fatalf("dialogs response = %T, want *tg.MessagesDialogs", enc)
}
return dialogs
}
@ -380,7 +376,7 @@ func TestChannelsDeleteHistoryForEveryoneDrainsBatchesAndKeepsDialogVisible(t *t
if err != nil {
t.Fatalf("dispatch get dialogs: %v", err)
}
dialogs, ok := enc.(*tg.MessagesDialogsBox).Dialogs.(*tg.MessagesDialogs)
dialogs, ok := enc.(*tg.MessagesDialogs)
if !ok {
t.Fatalf("dialogs response = %T, want messages.dialogs", enc)
}

View file

@ -103,9 +103,6 @@ func TestChannelMultiPinAndroidOpenAndJump(t *testing.T) {
Filter: &tg.InputMessagesFilterPinned{},
Limit: 40,
})
if box, ok := searchEnc.(*tg.MessagesMessagesBox); ok {
searchEnc = box.Messages
}
channelMessages, ok := searchEnc.(*tg.MessagesChannelMessages)
if !ok {
t.Fatalf("pinned search response = %T, want messages.channelMessages", searchEnc)

View file

@ -102,13 +102,9 @@ func TestPublicChannelPreviewRPCsAllowNonMember(t *testing.T) {
if err != nil {
t.Fatalf("dispatch getHistory public preview: %v", err)
}
box, ok := enc.(*tg.MessagesMessagesBox)
history, ok := enc.(*tg.MessagesChannelMessages)
if !ok {
t.Fatalf("getHistory response = %T, want boxed messages", enc)
}
history, ok := box.Messages.(*tg.MessagesChannelMessages)
if !ok {
t.Fatalf("boxed getHistory = %T, want channel messages", box.Messages)
t.Fatalf("getHistory response = %T, want *tg.MessagesChannelMessages", enc)
}
foundPost := false
for _, item := range history.Messages {

View file

@ -1,101 +1,301 @@
package rpc
import (
"context"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
)
// registerChannels 注册超级群/频道相关 RPC。messages.createChat 在这里注册,
// 因为 telesrv 将普通群创建直接实现为 megagroup。
func (r *Router) registerChannels(d *tg.ServerDispatcher) {
d.OnMessagesCreateChat(r.onMessagesCreateChat)
d.OnMessagesMigrateChat(r.onMessagesMigrateChat)
d.OnMessagesGetChats(r.onMessagesGetChats)
d.OnMessagesGetFullChat(r.onMessagesGetFullChat)
d.OnMessagesAddChatUser(r.onMessagesAddChatUser)
d.OnMessagesDeleteChatUser(r.onMessagesDeleteChatUser)
d.OnMessagesEditChatTitle(r.onMessagesEditChatTitle)
d.OnMessagesEditChatPhoto(r.onMessagesEditChatPhoto)
d.OnMessagesEditChatAdmin(r.onMessagesEditChatAdmin)
d.OnMessagesEditChatAbout(r.onMessagesEditChatAbout)
d.OnMessagesEditChatDefaultBannedRights(r.onMessagesEditChatDefaultBannedRights)
d.OnMessagesEditChatCreator(r.onMessagesEditChatCreator)
d.OnMessagesGetFutureChatCreatorAfterLeave(r.onMessagesGetFutureChatCreatorAfterLeave)
d.OnMessagesEditChatParticipantRank(r.onMessagesEditChatParticipantRank)
d.OnMessagesSetChatTheme(r.onMessagesSetChatTheme)
d.OnMessagesSetChatWallPaper(r.onMessagesSetChatWallPaper)
d.OnMessagesToggleNoForwards(r.onMessagesToggleNoForwards)
d.OnMessagesSetChatAvailableReactions(r.onMessagesSetChatAvailableReactions)
d.OnChannelsCreateChannel(r.onChannelsCreateChannel)
d.OnChannelsGetChannels(r.onChannelsGetChannels)
d.OnChannelsGetFullChannel(r.onChannelsGetFullChannel)
d.OnChannelsGetParticipants(r.onChannelsGetParticipants)
d.OnChannelsGetParticipant(r.onChannelsGetParticipant)
d.OnChannelsGetSendAs(r.onChannelsGetSendAs)
d.OnChannelsCheckUsername(r.onChannelsCheckUsername)
d.OnChannelsUpdateUsername(r.onChannelsUpdateUsername)
d.OnChannelsGetAdminedPublicChannels(r.onChannelsGetAdminedPublicChannels)
d.OnChannelsExportMessageLink(r.onChannelsExportMessageLink)
d.OnChannelsToggleSignatures(r.onChannelsToggleSignatures)
d.OnChannelsTogglePreHistoryHidden(r.onChannelsTogglePreHistoryHidden)
d.OnChannelsToggleSlowMode(r.onChannelsToggleSlowMode)
d.OnChannelsSetStickers(r.onChannelsSetStickers)
d.OnChannelsSetEmojiStickers(r.onChannelsSetEmojiStickers)
d.OnChannelsReorderUsernames(r.onChannelsReorderUsernames)
d.OnChannelsToggleUsername(r.onChannelsToggleUsername)
d.OnChannelsDeactivateAllUsernames(r.onChannelsDeactivateAllUsernames)
d.OnChannelsUpdateColor(r.onChannelsUpdateColor)
d.OnChannelsUpdateEmojiStatus(r.onChannelsUpdateEmojiStatus)
d.OnChannelsReadMessageContents(r.onChannelsReadMessageContents)
d.OnChannelsReportSpam(r.onChannelsReportSpam)
d.OnChannelsGetLeftChannels(r.onChannelsGetLeftChannels)
d.OnChannelsGetInactiveChannels(r.onChannelsGetInactiveChannels)
d.OnChannelsGetGroupsForDiscussion(r.onChannelsGetGroupsForDiscussion)
d.OnChannelsSetDiscussionGroup(r.onChannelsSetDiscussionGroup)
d.OnChannelsEditLocation(r.onChannelsEditLocation)
d.OnChannelsConvertToGigagroup(r.onChannelsConvertToGigagroup)
d.OnChannelsDeleteParticipantHistory(r.onChannelsDeleteParticipantHistory)
d.OnChannelsToggleJoinToSend(r.onChannelsToggleJoinToSend)
d.OnChannelsToggleJoinRequest(r.onChannelsToggleJoinRequest)
d.OnChannelsToggleForum(r.onChannelsToggleForum)
d.OnChannelsToggleAntiSpam(r.onChannelsToggleAntiSpam)
d.OnChannelsReportAntiSpamFalsePositive(r.onChannelsReportAntiSpamFalsePositive)
d.OnChannelsToggleParticipantsHidden(r.onChannelsToggleParticipantsHidden)
d.OnChannelsToggleViewForumAsMessages(r.onChannelsToggleViewForumAsMessages)
d.OnChannelsGetChannelRecommendations(r.onChannelsGetChannelRecommendations)
d.OnChannelsSetBoostsToUnblockRestrictions(r.onChannelsSetBoostsToUnblockRestrictions)
d.OnChannelsRestrictSponsoredMessages(r.onChannelsRestrictSponsoredMessages)
d.OnChannelsSearchPosts(r.onChannelsSearchPosts)
d.OnChannelsUpdatePaidMessagesPrice(r.onChannelsUpdatePaidMessagesPrice)
d.OnChannelsToggleAutotranslation(r.onChannelsToggleAutotranslation)
d.OnChannelsGetMessageAuthor(r.onChannelsGetMessageAuthor)
d.OnChannelsCheckSearchPostsFlood(r.onChannelsCheckSearchPostsFlood)
d.OnChannelsSetMainProfileTab(r.onChannelsSetMainProfileTab)
d.OnChannelsInviteToChannel(r.onChannelsInviteToChannel)
d.OnChannelsJoinChannel(r.onChannelsJoinChannel)
d.OnChannelsLeaveChannel(r.onChannelsLeaveChannel)
d.OnChannelsEditAdmin(r.onChannelsEditAdmin)
d.OnChannelsEditBanned(r.onChannelsEditBanned)
d.OnChannelsEditTitle(r.onChannelsEditTitle)
d.OnChannelsEditPhoto(r.onChannelsEditPhoto)
d.OnChannelsDeleteChannel(r.onChannelsDeleteChannel)
d.OnChannelsGetAdminLog(r.onChannelsGetAdminLog)
d.OnChannelsReadHistory(r.onChannelsReadHistory)
d.OnChannelsGetMessages(r.onChannelsGetMessages)
d.OnChannelsDeleteMessages(r.onChannelsDeleteMessages)
d.OnChannelsDeleteHistory(r.onChannelsDeleteHistory)
d.OnMessagesUpdatePinnedMessage(r.onMessagesUpdatePinnedMessage)
d.OnMessagesUnpinAllMessages(r.onMessagesUnpinAllMessages)
d.OnMessagesExportChatInvite(r.onMessagesExportChatInvite)
d.OnMessagesCheckChatInvite(r.onMessagesCheckChatInvite)
d.OnMessagesImportChatInvite(r.onMessagesImportChatInvite)
d.OnMessagesGetExportedChatInvites(r.onMessagesGetExportedChatInvites)
d.OnMessagesGetExportedChatInvite(r.onMessagesGetExportedChatInvite)
d.OnMessagesEditExportedChatInvite(r.onMessagesEditExportedChatInvite)
d.OnMessagesDeleteRevokedExportedChatInvites(r.onMessagesDeleteRevokedExportedChatInvites)
d.OnMessagesDeleteExportedChatInvite(r.onMessagesDeleteExportedChatInvite)
d.OnMessagesGetAdminsWithInvites(r.onMessagesGetAdminsWithInvites)
d.OnMessagesGetChatInviteImporters(r.onMessagesGetChatInviteImporters)
d.OnMessagesHideChatJoinRequest(r.onMessagesHideChatJoinRequest)
d.OnMessagesHideAllChatJoinRequests(r.onMessagesHideAllChatJoinRequests)
d.OnUpdatesGetChannelDifference(r.onUpdatesGetChannelDifference)
func (r *Router) registerChannels(d *tlprofile.Dispatcher) {
registerRPC[*tg.MessagesCreateChatRequest](d, tlprofile.SemanticMethodMessagesCreateChat, func(ctx context.Context, layerRequest *tg.MessagesCreateChatRequest) (any, error) {
return r.onMessagesCreateChat(ctx, layerRequest)
})
registerRPC[*tg.MessagesMigrateChatRequest](d, tlprofile.SemanticMethodMessagesMigrateChat, func(ctx context.Context, layerRequest *tg.MessagesMigrateChatRequest) (any, error) {
return r.onMessagesMigrateChat(ctx, layerRequest.
ChatID)
})
registerRPC[*tg.MessagesGetChatsRequest](d, tlprofile.SemanticMethodMessagesGetChats, func(ctx context.Context, layerRequest *tg.MessagesGetChatsRequest) (any, error) {
return r.onMessagesGetChats(ctx, layerRequest.
ID)
})
registerRPC[*tg.MessagesGetFullChatRequest](d, tlprofile.SemanticMethodMessagesGetFullChat, func(ctx context.Context, layerRequest *tg.MessagesGetFullChatRequest) (any, error) {
return r.onMessagesGetFullChat(ctx, layerRequest.
ChatID)
})
registerRPC[*tg.MessagesAddChatUserRequest](d, tlprofile.SemanticMethodMessagesAddChatUser, func(ctx context.Context, layerRequest *tg.MessagesAddChatUserRequest) (any, error) {
return r.onMessagesAddChatUser(ctx, layerRequest)
})
registerRPC[*tg.MessagesDeleteChatUserRequest](d, tlprofile.SemanticMethodMessagesDeleteChatUser, func(ctx context.Context, layerRequest *tg.MessagesDeleteChatUserRequest) (any, error) {
return r.onMessagesDeleteChatUser(ctx, layerRequest)
})
registerRPC[*tg.MessagesEditChatTitleRequest](d, tlprofile.SemanticMethodMessagesEditChatTitle, func(ctx context.Context, layerRequest *tg.MessagesEditChatTitleRequest) (any, error) {
return r.onMessagesEditChatTitle(ctx, layerRequest)
})
registerRPC[*tg.MessagesEditChatPhotoRequest](d, tlprofile.SemanticMethodMessagesEditChatPhoto, func(ctx context.Context, layerRequest *tg.MessagesEditChatPhotoRequest) (any, error) {
return r.onMessagesEditChatPhoto(ctx, layerRequest)
})
registerRPC[*tg.MessagesEditChatAdminRequest](d, tlprofile.SemanticMethodMessagesEditChatAdmin, func(ctx context.Context, layerRequest *tg.MessagesEditChatAdminRequest) (any, error) {
return r.onMessagesEditChatAdmin(ctx, layerRequest)
})
registerRPC[*tg.MessagesEditChatAboutRequest](d, tlprofile.SemanticMethodMessagesEditChatAbout, func(ctx context.Context, layerRequest *tg.MessagesEditChatAboutRequest) (any, error) {
return r.onMessagesEditChatAbout(ctx, layerRequest)
})
registerRPC[*tg.MessagesEditChatDefaultBannedRightsRequest](d, tlprofile.SemanticMethodMessagesEditChatDefaultBannedRights, func(ctx context.Context, layerRequest *tg.MessagesEditChatDefaultBannedRightsRequest) (any, error) {
return r.onMessagesEditChatDefaultBannedRights(ctx, layerRequest)
})
registerRPC[*tg.MessagesEditChatCreatorRequest](d, tlprofile.SemanticMethodMessagesEditChatCreator, func(ctx context.Context, layerRequest *tg.MessagesEditChatCreatorRequest) (any, error) {
return r.onMessagesEditChatCreator(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetFutureChatCreatorAfterLeaveRequest](d, tlprofile.SemanticMethodMessagesGetFutureChatCreatorAfterLeave, func(ctx context.Context, layerRequest *tg.MessagesGetFutureChatCreatorAfterLeaveRequest) (any, error) {
return r.onMessagesGetFutureChatCreatorAfterLeave(ctx, layerRequest.
Peer)
})
registerRPC[*tg.MessagesEditChatParticipantRankRequest](d, tlprofile.SemanticMethodMessagesEditChatParticipantRank, func(ctx context.Context, layerRequest *tg.MessagesEditChatParticipantRankRequest) (any, error) {
return r.onMessagesEditChatParticipantRank(ctx, layerRequest)
})
registerRPC[*tg.MessagesSetChatThemeRequest](d, tlprofile.SemanticMethodMessagesSetChatTheme, func(ctx context.Context, layerRequest *tg.MessagesSetChatThemeRequest) (any, error) {
return r.onMessagesSetChatTheme(ctx, layerRequest)
})
registerRPC[*tg.MessagesSetChatWallPaperRequest](d, tlprofile.SemanticMethodMessagesSetChatWallPaper, func(ctx context.Context, layerRequest *tg.MessagesSetChatWallPaperRequest) (any, error) {
return r.onMessagesSetChatWallPaper(ctx, layerRequest)
})
registerRPC[*tg.MessagesToggleNoForwardsRequest](d, tlprofile.SemanticMethodMessagesToggleNoForwards, func(ctx context.Context, layerRequest *tg.MessagesToggleNoForwardsRequest) (any, error) {
return r.onMessagesToggleNoForwards(ctx, layerRequest)
})
registerRPC[*tg.MessagesSetChatAvailableReactionsRequest](d, tlprofile.SemanticMethodMessagesSetChatAvailableReactions, func(ctx context.Context, layerRequest *tg.MessagesSetChatAvailableReactionsRequest) (any, error) {
return r.onMessagesSetChatAvailableReactions(ctx, layerRequest)
})
registerRPC[*tg.ChannelsCreateChannelRequest](d, tlprofile.SemanticMethodChannelsCreateChannel, func(ctx context.Context, layerRequest *tg.ChannelsCreateChannelRequest) (any, error) {
return r.onChannelsCreateChannel(ctx, layerRequest)
})
registerRPC[*tg.ChannelsGetChannelsRequest](d, tlprofile.SemanticMethodChannelsGetChannels, func(ctx context.Context, layerRequest *tg.ChannelsGetChannelsRequest) (any, error) {
return r.onChannelsGetChannels(ctx, layerRequest.
ID)
})
registerRPC[*tg.ChannelsGetFullChannelRequest](d, tlprofile.SemanticMethodChannelsGetFullChannel, func(ctx context.Context, layerRequest *tg.ChannelsGetFullChannelRequest) (any, error) {
return r.onChannelsGetFullChannel(ctx, layerRequest.
Channel)
})
registerRPC[*tg.ChannelsGetParticipantsRequest](d, tlprofile.SemanticMethodChannelsGetParticipants, func(ctx context.Context, layerRequest *tg.ChannelsGetParticipantsRequest) (any, error) {
return r.onChannelsGetParticipants(ctx, layerRequest)
})
registerRPC[*tg.ChannelsGetParticipantRequest](d, tlprofile.SemanticMethodChannelsGetParticipant, func(ctx context.Context, layerRequest *tg.ChannelsGetParticipantRequest) (any, error) {
return r.onChannelsGetParticipant(ctx, layerRequest)
})
registerRPC[*tg.ChannelsGetSendAsRequest](d, tlprofile.SemanticMethodChannelsGetSendAs, func(ctx context.Context, layerRequest *tg.ChannelsGetSendAsRequest) (any, error) {
return r.onChannelsGetSendAs(ctx, layerRequest)
})
registerRPC[*tg.ChannelsCheckUsernameRequest](d, tlprofile.SemanticMethodChannelsCheckUsername, func(ctx context.Context, layerRequest *tg.ChannelsCheckUsernameRequest) (any, error) {
return r.onChannelsCheckUsername(ctx, layerRequest)
})
registerRPC[*tg.ChannelsUpdateUsernameRequest](d, tlprofile.SemanticMethodChannelsUpdateUsername, func(ctx context.Context, layerRequest *tg.ChannelsUpdateUsernameRequest) (any, error) {
return r.onChannelsUpdateUsername(ctx, layerRequest)
})
registerRPC[*tg.ChannelsGetAdminedPublicChannelsRequest](d, tlprofile.SemanticMethodChannelsGetAdminedPublicChannels, func(ctx context.Context, layerRequest *tg.ChannelsGetAdminedPublicChannelsRequest) (any, error) {
return r.onChannelsGetAdminedPublicChannels(ctx, layerRequest)
})
registerRPC[*tg.ChannelsExportMessageLinkRequest](d, tlprofile.SemanticMethodChannelsExportMessageLink, func(ctx context.Context, layerRequest *tg.ChannelsExportMessageLinkRequest) (any, error) {
return r.onChannelsExportMessageLink(ctx, layerRequest)
})
registerRPC[*tg.ChannelsToggleSignaturesRequest](d, tlprofile.SemanticMethodChannelsToggleSignatures, func(ctx context.Context, layerRequest *tg.ChannelsToggleSignaturesRequest) (any, error) {
return r.onChannelsToggleSignatures(ctx, layerRequest)
})
registerRPC[*tg.ChannelsTogglePreHistoryHiddenRequest](d, tlprofile.SemanticMethodChannelsTogglePreHistoryHidden, func(ctx context.Context, layerRequest *tg.ChannelsTogglePreHistoryHiddenRequest) (any, error) {
return r.onChannelsTogglePreHistoryHidden(ctx, layerRequest)
})
registerRPC[*tg.ChannelsToggleSlowModeRequest](d, tlprofile.SemanticMethodChannelsToggleSlowMode, func(ctx context.Context, layerRequest *tg.ChannelsToggleSlowModeRequest) (any, error) {
return r.onChannelsToggleSlowMode(ctx, layerRequest)
})
registerRPC[*tg.ChannelsSetStickersRequest](d, tlprofile.SemanticMethodChannelsSetStickers, func(ctx context.Context, layerRequest *tg.ChannelsSetStickersRequest) (any, error) {
return r.onChannelsSetStickers(ctx, layerRequest)
})
registerRPC[*tg.ChannelsSetEmojiStickersRequest](d, tlprofile.SemanticMethodChannelsSetEmojiStickers, func(ctx context.Context, layerRequest *tg.ChannelsSetEmojiStickersRequest) (any, error) {
return r.onChannelsSetEmojiStickers(ctx, layerRequest)
})
registerRPC[*tg.ChannelsReorderUsernamesRequest](d, tlprofile.SemanticMethodChannelsReorderUsernames, func(ctx context.Context, layerRequest *tg.ChannelsReorderUsernamesRequest) (any, error) {
return r.onChannelsReorderUsernames(ctx, layerRequest)
})
registerRPC[*tg.ChannelsToggleUsernameRequest](d, tlprofile.SemanticMethodChannelsToggleUsername, func(ctx context.Context, layerRequest *tg.ChannelsToggleUsernameRequest) (any, error) {
return r.onChannelsToggleUsername(ctx, layerRequest)
})
registerRPC[*tg.ChannelsDeactivateAllUsernamesRequest](d, tlprofile.SemanticMethodChannelsDeactivateAllUsernames, func(ctx context.Context, layerRequest *tg.ChannelsDeactivateAllUsernamesRequest) (any, error) {
return r.onChannelsDeactivateAllUsernames(ctx, layerRequest.
Channel)
})
registerRPC[*tg.ChannelsUpdateColorRequest](d, tlprofile.SemanticMethodChannelsUpdateColor, func(ctx context.Context, layerRequest *tg.ChannelsUpdateColorRequest) (any, error) {
return r.onChannelsUpdateColor(ctx, layerRequest)
})
registerRPC[*tg.ChannelsUpdateEmojiStatusRequest](d, tlprofile.SemanticMethodChannelsUpdateEmojiStatus, func(ctx context.Context, layerRequest *tg.ChannelsUpdateEmojiStatusRequest) (any, error) {
return r.onChannelsUpdateEmojiStatus(ctx, layerRequest)
})
registerRPC[*tg.ChannelsReadMessageContentsRequest](d, tlprofile.SemanticMethodChannelsReadMessageContents, func(ctx context.Context, layerRequest *tg.ChannelsReadMessageContentsRequest) (any, error) {
return r.onChannelsReadMessageContents(ctx, layerRequest)
})
registerRPC[*tg.ChannelsReportSpamRequest](d, tlprofile.SemanticMethodChannelsReportSpam, func(ctx context.Context, layerRequest *tg.ChannelsReportSpamRequest) (any, error) {
return r.onChannelsReportSpam(ctx, layerRequest)
})
registerRPC[*tg.ChannelsGetLeftChannelsRequest](d, tlprofile.SemanticMethodChannelsGetLeftChannels, func(ctx context.Context, layerRequest *tg.ChannelsGetLeftChannelsRequest) (any, error) {
return r.onChannelsGetLeftChannels(ctx, layerRequest.
Offset)
})
registerRPC[*tg.ChannelsGetInactiveChannelsRequest](d, tlprofile.SemanticMethodChannelsGetInactiveChannels, func(ctx context.Context, layerRequest *tg.ChannelsGetInactiveChannelsRequest) (any, error) {
return r.onChannelsGetInactiveChannels(ctx)
})
registerRPC[*tg.ChannelsGetGroupsForDiscussionRequest](d, tlprofile.SemanticMethodChannelsGetGroupsForDiscussion, func(ctx context.Context, layerRequest *tg.ChannelsGetGroupsForDiscussionRequest) (any, error) {
return r.onChannelsGetGroupsForDiscussion(ctx)
})
registerRPC[*tg.ChannelsSetDiscussionGroupRequest](d, tlprofile.SemanticMethodChannelsSetDiscussionGroup, func(ctx context.Context, layerRequest *tg.ChannelsSetDiscussionGroupRequest) (any, error) {
return r.onChannelsSetDiscussionGroup(ctx, layerRequest)
})
registerRPC[*tg.ChannelsEditLocationRequest](d, tlprofile.SemanticMethodChannelsEditLocation, func(ctx context.Context, layerRequest *tg.ChannelsEditLocationRequest) (any, error) {
return r.onChannelsEditLocation(ctx, layerRequest)
})
registerRPC[*tg.ChannelsConvertToGigagroupRequest](d, tlprofile.SemanticMethodChannelsConvertToGigagroup, func(ctx context.Context, layerRequest *tg.ChannelsConvertToGigagroupRequest) (any, error) {
return r.onChannelsConvertToGigagroup(ctx, layerRequest.
Channel)
})
registerRPC[*tg.ChannelsDeleteParticipantHistoryRequest](d, tlprofile.SemanticMethodChannelsDeleteParticipantHistory, func(ctx context.Context, layerRequest *tg.ChannelsDeleteParticipantHistoryRequest) (any, error) {
return r.onChannelsDeleteParticipantHistory(ctx, layerRequest)
})
registerRPC[*tg.ChannelsToggleJoinToSendRequest](d, tlprofile.SemanticMethodChannelsToggleJoinToSend, func(ctx context.Context, layerRequest *tg.ChannelsToggleJoinToSendRequest) (any, error) {
return r.onChannelsToggleJoinToSend(ctx, layerRequest)
})
registerRPC[*tg.ChannelsToggleJoinRequestRequest](d, tlprofile.SemanticMethodChannelsToggleJoinRequest, func(ctx context.Context, layerRequest *tg.ChannelsToggleJoinRequestRequest) (any, error) {
return r.onChannelsToggleJoinRequest(ctx, layerRequest)
})
registerRPC[*tg.ChannelsToggleForumRequest](d, tlprofile.SemanticMethodChannelsToggleForum, func(ctx context.Context, layerRequest *tg.ChannelsToggleForumRequest) (any, error) {
return r.onChannelsToggleForum(ctx, layerRequest)
})
registerRPC[*tg.ChannelsToggleAntiSpamRequest](d, tlprofile.SemanticMethodChannelsToggleAntiSpam, func(ctx context.Context, layerRequest *tg.ChannelsToggleAntiSpamRequest) (any, error) {
return r.onChannelsToggleAntiSpam(ctx, layerRequest)
})
registerRPC[*tg.ChannelsReportAntiSpamFalsePositiveRequest](d, tlprofile.SemanticMethodChannelsReportAntiSpamFalsePositive, func(ctx context.Context, layerRequest *tg.ChannelsReportAntiSpamFalsePositiveRequest) (any, error) {
return r.onChannelsReportAntiSpamFalsePositive(ctx, layerRequest)
})
registerRPC[*tg.ChannelsToggleParticipantsHiddenRequest](d, tlprofile.SemanticMethodChannelsToggleParticipantsHidden, func(ctx context.Context, layerRequest *tg.ChannelsToggleParticipantsHiddenRequest) (any, error) {
return r.onChannelsToggleParticipantsHidden(ctx, layerRequest)
})
registerRPC[*tg.ChannelsToggleViewForumAsMessagesRequest](d, tlprofile.SemanticMethodChannelsToggleViewForumAsMessages, func(ctx context.Context, layerRequest *tg.ChannelsToggleViewForumAsMessagesRequest) (any, error) {
return r.onChannelsToggleViewForumAsMessages(ctx, layerRequest)
})
registerRPC[*tg.ChannelsGetChannelRecommendationsRequest](d, tlprofile.SemanticMethodChannelsGetChannelRecommendations, func(ctx context.Context, layerRequest *tg.ChannelsGetChannelRecommendationsRequest) (any, error) {
return r.onChannelsGetChannelRecommendations(ctx, layerRequest)
})
registerRPC[*tg.ChannelsSetBoostsToUnblockRestrictionsRequest](d, tlprofile.SemanticMethodChannelsSetBoostsToUnblockRestrictions, func(ctx context.Context, layerRequest *tg.ChannelsSetBoostsToUnblockRestrictionsRequest) (any, error) {
return r.onChannelsSetBoostsToUnblockRestrictions(ctx, layerRequest)
})
registerRPC[*tg.ChannelsRestrictSponsoredMessagesRequest](d, tlprofile.SemanticMethodChannelsRestrictSponsoredMessages, func(ctx context.Context, layerRequest *tg.ChannelsRestrictSponsoredMessagesRequest) (any, error) {
return r.onChannelsRestrictSponsoredMessages(ctx, layerRequest)
})
registerRPC[*tg.ChannelsSearchPostsRequest](d, tlprofile.SemanticMethodChannelsSearchPosts, func(ctx context.Context, layerRequest *tg.ChannelsSearchPostsRequest) (any, error) {
return r.onChannelsSearchPosts(ctx, layerRequest)
})
registerRPC[*tg.ChannelsUpdatePaidMessagesPriceRequest](d, tlprofile.SemanticMethodChannelsUpdatePaidMessagesPrice, func(ctx context.Context, layerRequest *tg.ChannelsUpdatePaidMessagesPriceRequest) (any, error) {
return r.onChannelsUpdatePaidMessagesPrice(ctx, layerRequest)
})
registerRPC[*tg.ChannelsToggleAutotranslationRequest](d, tlprofile.SemanticMethodChannelsToggleAutotranslation, func(ctx context.Context, layerRequest *tg.ChannelsToggleAutotranslationRequest) (any, error) {
return r.onChannelsToggleAutotranslation(ctx, layerRequest)
})
registerRPC[*tg.ChannelsGetMessageAuthorRequest](d, tlprofile.SemanticMethodChannelsGetMessageAuthor, func(ctx context.Context, layerRequest *tg.ChannelsGetMessageAuthorRequest) (any, error) {
return r.onChannelsGetMessageAuthor(ctx, layerRequest)
})
registerRPC[*tg.ChannelsCheckSearchPostsFloodRequest](d, tlprofile.SemanticMethodChannelsCheckSearchPostsFlood, func(ctx context.Context, layerRequest *tg.ChannelsCheckSearchPostsFloodRequest) (any, error) {
return r.onChannelsCheckSearchPostsFlood(ctx, layerRequest)
})
registerRPC[*tg.ChannelsSetMainProfileTabRequest](d, tlprofile.SemanticMethodChannelsSetMainProfileTab, func(ctx context.Context, layerRequest *tg.ChannelsSetMainProfileTabRequest) (any, error) {
return r.onChannelsSetMainProfileTab(ctx, layerRequest)
})
registerRPC[*tg.ChannelsInviteToChannelRequest](d, tlprofile.SemanticMethodChannelsInviteToChannel, func(ctx context.Context, layerRequest *tg.ChannelsInviteToChannelRequest) (any, error) {
return r.onChannelsInviteToChannel(ctx, layerRequest)
})
registerRPC[*tg.ChannelsJoinChannelRequest](d, tlprofile.SemanticMethodChannelsJoinChannel, func(ctx context.Context, layerRequest *tg.ChannelsJoinChannelRequest) (any, error) {
return r.onChannelsJoinChannel(ctx, layerRequest.
Channel)
})
registerRPC[*tg.ChannelsLeaveChannelRequest](d, tlprofile.SemanticMethodChannelsLeaveChannel, func(ctx context.Context, layerRequest *tg.ChannelsLeaveChannelRequest) (any, error) {
return r.onChannelsLeaveChannel(ctx, layerRequest.
Channel)
})
registerRPC[*tg.ChannelsEditAdminRequest](d, tlprofile.SemanticMethodChannelsEditAdmin, func(ctx context.Context, layerRequest *tg.ChannelsEditAdminRequest) (any, error) {
return r.onChannelsEditAdmin(ctx, layerRequest)
})
registerRPC[*tg.ChannelsEditBannedRequest](d, tlprofile.SemanticMethodChannelsEditBanned, func(ctx context.Context, layerRequest *tg.ChannelsEditBannedRequest) (any, error) {
return r.onChannelsEditBanned(ctx, layerRequest)
})
registerRPC[*tg.ChannelsEditTitleRequest](d, tlprofile.SemanticMethodChannelsEditTitle, func(ctx context.Context, layerRequest *tg.ChannelsEditTitleRequest) (any, error) {
return r.onChannelsEditTitle(ctx, layerRequest)
})
registerRPC[*tg.ChannelsEditPhotoRequest](d, tlprofile.SemanticMethodChannelsEditPhoto, func(ctx context.Context, layerRequest *tg.ChannelsEditPhotoRequest) (any, error) {
return r.onChannelsEditPhoto(ctx, layerRequest)
})
registerRPC[*tg.ChannelsDeleteChannelRequest](d, tlprofile.SemanticMethodChannelsDeleteChannel, func(ctx context.Context, layerRequest *tg.ChannelsDeleteChannelRequest) (any, error) {
return r.onChannelsDeleteChannel(ctx, layerRequest.
Channel)
})
registerRPC[*tg.ChannelsGetAdminLogRequest](d, tlprofile.SemanticMethodChannelsGetAdminLog, func(ctx context.Context, layerRequest *tg.ChannelsGetAdminLogRequest) (any, error) {
return r.onChannelsGetAdminLog(ctx, layerRequest)
})
registerRPC[*tg.ChannelsReadHistoryRequest](d, tlprofile.SemanticMethodChannelsReadHistory, func(ctx context.Context, layerRequest *tg.ChannelsReadHistoryRequest) (any, error) {
return r.onChannelsReadHistory(ctx, layerRequest)
})
registerRPC[*tg.ChannelsGetMessagesRequest](d, tlprofile.SemanticMethodChannelsGetMessages, func(ctx context.Context, layerRequest *tg.ChannelsGetMessagesRequest) (any, error) {
return r.onChannelsGetMessages(ctx, layerRequest)
})
registerRPC[*tg.ChannelsDeleteMessagesRequest](d, tlprofile.SemanticMethodChannelsDeleteMessages, func(ctx context.Context, layerRequest *tg.ChannelsDeleteMessagesRequest) (any, error) {
return r.onChannelsDeleteMessages(ctx, layerRequest)
})
registerRPC[*tg.ChannelsDeleteHistoryRequest](d, tlprofile.SemanticMethodChannelsDeleteHistory, func(ctx context.Context, layerRequest *tg.ChannelsDeleteHistoryRequest) (any, error) {
return r.onChannelsDeleteHistory(ctx, layerRequest)
})
registerRPC[*tg.MessagesUpdatePinnedMessageRequest](d, tlprofile.SemanticMethodMessagesUpdatePinnedMessage, func(ctx context.Context, layerRequest *tg.MessagesUpdatePinnedMessageRequest) (any, error) {
return r.onMessagesUpdatePinnedMessage(ctx, layerRequest)
})
registerRPC[*tg.MessagesUnpinAllMessagesRequest](d, tlprofile.SemanticMethodMessagesUnpinAllMessages, func(ctx context.Context, layerRequest *tg.MessagesUnpinAllMessagesRequest) (any, error) {
return r.onMessagesUnpinAllMessages(ctx, layerRequest)
})
registerRPC[*tg.MessagesExportChatInviteRequest](d, tlprofile.SemanticMethodMessagesExportChatInvite, func(ctx context.Context, layerRequest *tg.MessagesExportChatInviteRequest) (any, error) {
return r.onMessagesExportChatInvite(ctx, layerRequest)
})
registerRPC[*tg.MessagesCheckChatInviteRequest](d, tlprofile.SemanticMethodMessagesCheckChatInvite, func(ctx context.Context, layerRequest *tg.MessagesCheckChatInviteRequest) (any, error) {
return r.onMessagesCheckChatInvite(ctx, layerRequest.
Hash)
})
registerRPC[*tg.MessagesImportChatInviteRequest](d, tlprofile.SemanticMethodMessagesImportChatInvite, func(ctx context.Context, layerRequest *tg.MessagesImportChatInviteRequest) (any, error) {
return r.onMessagesImportChatInvite(ctx, layerRequest.
Hash)
})
registerRPC[*tg.MessagesGetExportedChatInvitesRequest](d, tlprofile.SemanticMethodMessagesGetExportedChatInvites, func(ctx context.Context, layerRequest *tg.MessagesGetExportedChatInvitesRequest) (any, error) {
return r.onMessagesGetExportedChatInvites(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetExportedChatInviteRequest](d, tlprofile.SemanticMethodMessagesGetExportedChatInvite, func(ctx context.Context, layerRequest *tg.MessagesGetExportedChatInviteRequest) (any, error) {
return r.onMessagesGetExportedChatInvite(ctx, layerRequest)
})
registerRPC[*tg.MessagesEditExportedChatInviteRequest](d, tlprofile.SemanticMethodMessagesEditExportedChatInvite, func(ctx context.Context, layerRequest *tg.MessagesEditExportedChatInviteRequest) (any, error) {
return r.onMessagesEditExportedChatInvite(ctx, layerRequest)
})
registerRPC[*tg.MessagesDeleteRevokedExportedChatInvitesRequest](d, tlprofile.SemanticMethodMessagesDeleteRevokedExportedChatInvites, func(ctx context.Context, layerRequest *tg.MessagesDeleteRevokedExportedChatInvitesRequest) (any, error) {
return r.onMessagesDeleteRevokedExportedChatInvites(ctx, layerRequest)
})
registerRPC[*tg.MessagesDeleteExportedChatInviteRequest](d, tlprofile.SemanticMethodMessagesDeleteExportedChatInvite, func(ctx context.Context, layerRequest *tg.MessagesDeleteExportedChatInviteRequest) (any, error) {
return r.onMessagesDeleteExportedChatInvite(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetAdminsWithInvitesRequest](d, tlprofile.SemanticMethodMessagesGetAdminsWithInvites, func(ctx context.Context, layerRequest *tg.MessagesGetAdminsWithInvitesRequest) (any, error) {
return r.onMessagesGetAdminsWithInvites(ctx, layerRequest.
Peer)
})
registerRPC[*tg.MessagesGetChatInviteImportersRequest](d, tlprofile.SemanticMethodMessagesGetChatInviteImporters, func(ctx context.Context, layerRequest *tg.MessagesGetChatInviteImportersRequest) (any, error) {
return r.onMessagesGetChatInviteImporters(ctx, layerRequest)
})
registerRPC[*tg.MessagesHideChatJoinRequestRequest](d, tlprofile.SemanticMethodMessagesHideChatJoinRequest, func(ctx context.Context, layerRequest *tg.MessagesHideChatJoinRequestRequest) (any, error) {
return r.onMessagesHideChatJoinRequest(ctx, layerRequest)
})
registerRPC[*tg.MessagesHideAllChatJoinRequestsRequest](d, tlprofile.SemanticMethodMessagesHideAllChatJoinRequests, func(ctx context.Context, layerRequest *tg.MessagesHideAllChatJoinRequestsRequest) (any, error) {
return r.onMessagesHideAllChatJoinRequests(ctx, layerRequest)
})
registerRPC[*tg.UpdatesGetChannelDifferenceRequest](d, tlprofile.SemanticMethodUpdatesGetChannelDifference, func(ctx context.Context, layerRequest *tg.UpdatesGetChannelDifferenceRequest) (any, error) {
return r.onUpdatesGetChannelDifference(ctx, layerRequest)
})
}

View file

@ -6,21 +6,49 @@ import (
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/domain"
)
func (r *Router) registerChatlists(d *tg.ServerDispatcher) {
d.OnChatlistsExportChatlistInvite(r.onChatlistsExportChatlistInvite)
d.OnChatlistsDeleteExportedInvite(r.onChatlistsDeleteExportedInvite)
d.OnChatlistsEditExportedInvite(r.onChatlistsEditExportedInvite)
d.OnChatlistsGetExportedInvites(r.onChatlistsGetExportedInvites)
d.OnChatlistsCheckChatlistInvite(r.onChatlistsCheckChatlistInvite)
d.OnChatlistsJoinChatlistInvite(r.onChatlistsJoinChatlistInvite)
d.OnChatlistsGetChatlistUpdates(r.onChatlistsGetChatlistUpdates)
d.OnChatlistsJoinChatlistUpdates(r.onChatlistsJoinChatlistUpdates)
d.OnChatlistsHideChatlistUpdates(r.onChatlistsHideChatlistUpdates)
d.OnChatlistsGetLeaveChatlistSuggestions(r.onChatlistsGetLeaveChatlistSuggestions)
d.OnChatlistsLeaveChatlist(r.onChatlistsLeaveChatlist)
func (r *Router) registerChatlists(d *tlprofile.Dispatcher) {
registerRPC[*tg.ChatlistsExportChatlistInviteRequest](d, tlprofile.SemanticMethodChatlistsExportChatlistInvite, func(ctx context.Context, layerRequest *tg.ChatlistsExportChatlistInviteRequest) (any, error) {
return r.onChatlistsExportChatlistInvite(ctx, layerRequest)
})
registerRPC[*tg.ChatlistsDeleteExportedInviteRequest](d, tlprofile.SemanticMethodChatlistsDeleteExportedInvite, func(ctx context.Context, layerRequest *tg.ChatlistsDeleteExportedInviteRequest) (any, error) {
return r.onChatlistsDeleteExportedInvite(ctx, layerRequest)
})
registerRPC[*tg.ChatlistsEditExportedInviteRequest](d, tlprofile.SemanticMethodChatlistsEditExportedInvite, func(ctx context.Context, layerRequest *tg.ChatlistsEditExportedInviteRequest) (any, error) {
return r.onChatlistsEditExportedInvite(ctx, layerRequest)
})
registerRPC[*tg.ChatlistsGetExportedInvitesRequest](d, tlprofile.SemanticMethodChatlistsGetExportedInvites, func(ctx context.Context, layerRequest *tg.ChatlistsGetExportedInvitesRequest) (any, error) {
return r.onChatlistsGetExportedInvites(ctx, layerRequest.
Chatlist)
})
registerRPC[*tg.ChatlistsCheckChatlistInviteRequest](d, tlprofile.SemanticMethodChatlistsCheckChatlistInvite, func(ctx context.Context, layerRequest *tg.ChatlistsCheckChatlistInviteRequest) (any, error) {
return r.onChatlistsCheckChatlistInvite(ctx, layerRequest.
Slug)
})
registerRPC[*tg.ChatlistsJoinChatlistInviteRequest](d, tlprofile.SemanticMethodChatlistsJoinChatlistInvite, func(ctx context.Context, layerRequest *tg.ChatlistsJoinChatlistInviteRequest) (any, error) {
return r.onChatlistsJoinChatlistInvite(ctx, layerRequest)
})
registerRPC[*tg.ChatlistsGetChatlistUpdatesRequest](d, tlprofile.SemanticMethodChatlistsGetChatlistUpdates, func(ctx context.Context, layerRequest *tg.ChatlistsGetChatlistUpdatesRequest) (any, error) {
return r.onChatlistsGetChatlistUpdates(ctx, layerRequest.
Chatlist)
})
registerRPC[*tg.ChatlistsJoinChatlistUpdatesRequest](d, tlprofile.SemanticMethodChatlistsJoinChatlistUpdates, func(ctx context.Context, layerRequest *tg.ChatlistsJoinChatlistUpdatesRequest) (any, error) {
return r.onChatlistsJoinChatlistUpdates(ctx, layerRequest)
})
registerRPC[*tg.ChatlistsHideChatlistUpdatesRequest](d, tlprofile.SemanticMethodChatlistsHideChatlistUpdates, func(ctx context.Context, layerRequest *tg.ChatlistsHideChatlistUpdatesRequest) (any, error) {
return r.onChatlistsHideChatlistUpdates(ctx, layerRequest.
Chatlist)
})
registerRPC[*tg.ChatlistsGetLeaveChatlistSuggestionsRequest](d, tlprofile.SemanticMethodChatlistsGetLeaveChatlistSuggestions, func(ctx context.Context, layerRequest *tg.ChatlistsGetLeaveChatlistSuggestionsRequest) (any, error) {
return r.onChatlistsGetLeaveChatlistSuggestions(ctx, layerRequest.
Chatlist)
})
registerRPC[*tg.ChatlistsLeaveChatlistRequest](d, tlprofile.SemanticMethodChatlistsLeaveChatlist, func(ctx context.Context, layerRequest *tg.ChatlistsLeaveChatlistRequest) (any, error) {
return r.onChatlistsLeaveChatlist(ctx, layerRequest)
})
}
func (r *Router) onChatlistsExportChatlistInvite(ctx context.Context, req *tg.ChatlistsExportChatlistInviteRequest) (*tg.ChatlistsExportedChatlistInvite, error) {

View file

@ -10,6 +10,7 @@ import (
"github.com/iamxvbaba/td/tg"
"go.uber.org/zap"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/app/contacts"
"telesrv/internal/compat/tdesktop"
"telesrv/internal/domain"
@ -28,38 +29,82 @@ const (
)
// registerContacts 注册 contacts.* RPC handler。
func (r *Router) registerContacts(d *tg.ServerDispatcher) {
d.OnContactsGetContacts(r.onContactsGetContacts)
d.OnContactsGetContactIDs(r.onContactsGetContactIDs)
d.OnContactsGetStatuses(r.onContactsGetStatuses)
d.OnContactsImportContacts(r.onContactsImportContacts)
d.OnContactsAddContact(r.onContactsAddContact)
d.OnContactsAcceptContact(r.onContactsAcceptContact)
d.OnContactsDeleteContacts(r.onContactsDeleteContacts)
d.OnContactsEditCloseFriends(r.onContactsEditCloseFriends)
d.OnContactsBlock(r.onContactsBlock)
d.OnContactsUnblock(r.onContactsUnblock)
d.OnContactsSetBlocked(r.onContactsSetBlocked)
d.OnContactsUpdateContactNote(r.onContactsUpdateContactNote)
d.OnContactsSearch(r.onContactsSearch)
d.OnContactsResolveUsername(r.onContactsResolveUsername)
d.OnContactsResolvePhone(r.onContactsResolvePhone)
d.OnContactsGetTopPeers(func(ctx context.Context, req *tg.ContactsGetTopPeersRequest) (tg.ContactsTopPeersClass, error) {
func (r *Router) registerContacts(d *tlprofile.Dispatcher) {
registerRPC[*tg.ContactsGetContactsRequest](d, tlprofile.SemanticMethodContactsGetContacts, func(ctx context.Context, layerRequest *tg.ContactsGetContactsRequest) (any, error) {
return r.onContactsGetContacts(ctx, layerRequest.
Hash)
})
registerRPC[*tg.ContactsGetContactIDsRequest](d, tlprofile.SemanticMethodContactsGetContactIDs, func(ctx context.Context, layerRequest *tg.ContactsGetContactIDsRequest) (any, error) {
return r.onContactsGetContactIDs(ctx, layerRequest.
Hash)
})
registerRPC[*tg.ContactsGetStatusesRequest](d, tlprofile.SemanticMethodContactsGetStatuses, func(ctx context.Context, layerRequest *tg.ContactsGetStatusesRequest) (any, error) {
return r.onContactsGetStatuses(ctx)
})
registerRPC[*tg.ContactsImportContactsRequest](d, tlprofile.SemanticMethodContactsImportContacts, func(ctx context.Context, layerRequest *tg.ContactsImportContactsRequest) (any, error) {
return r.onContactsImportContacts(ctx, layerRequest.
Contacts)
})
registerRPC[*tg.ContactsAddContactRequest](d, tlprofile.SemanticMethodContactsAddContact, func(ctx context.Context, layerRequest *tg.ContactsAddContactRequest) (any, error) {
return r.onContactsAddContact(ctx, layerRequest)
})
registerRPC[*tg.ContactsAcceptContactRequest](d, tlprofile.SemanticMethodContactsAcceptContact, func(ctx context.Context, layerRequest *tg.ContactsAcceptContactRequest) (any, error) {
return r.onContactsAcceptContact(ctx, layerRequest.
ID)
})
registerRPC[*tg.ContactsDeleteContactsRequest](d, tlprofile.SemanticMethodContactsDeleteContacts, func(ctx context.Context, layerRequest *tg.ContactsDeleteContactsRequest) (any, error) {
return r.onContactsDeleteContacts(ctx, layerRequest.
ID)
})
registerRPC[*tg.ContactsEditCloseFriendsRequest](d, tlprofile.SemanticMethodContactsEditCloseFriends, func(ctx context.Context, layerRequest *tg.ContactsEditCloseFriendsRequest) (any, error) {
return r.onContactsEditCloseFriends(ctx, layerRequest.
ID)
})
registerRPC[*tg.ContactsBlockRequest](d, tlprofile.SemanticMethodContactsBlock, func(ctx context.Context, layerRequest *tg.ContactsBlockRequest) (any, error) {
return r.onContactsBlock(ctx, layerRequest)
})
registerRPC[*tg.ContactsUnblockRequest](d, tlprofile.SemanticMethodContactsUnblock, func(ctx context.Context, layerRequest *tg.ContactsUnblockRequest) (any, error) {
return r.onContactsUnblock(ctx, layerRequest)
})
registerRPC[*tg.ContactsSetBlockedRequest](d, tlprofile.SemanticMethodContactsSetBlocked, func(ctx context.Context, layerRequest *tg.ContactsSetBlockedRequest) (any, error) {
return r.onContactsSetBlocked(ctx, layerRequest)
})
registerRPC[*tg.ContactsUpdateContactNoteRequest](d, tlprofile.SemanticMethodContactsUpdateContactNote, func(ctx context.Context, layerRequest *tg.ContactsUpdateContactNoteRequest) (any, error) {
return r.onContactsUpdateContactNote(ctx, layerRequest)
})
registerRPC[*tg.ContactsSearchRequest](d, tlprofile.SemanticMethodContactsSearch, func(ctx context.Context, layerRequest *tg.ContactsSearchRequest) (any, error) {
return r.onContactsSearch(ctx, layerRequest)
})
registerRPC[*tg.ContactsResolveUsernameRequest](d, tlprofile.SemanticMethodContactsResolveUsername, func(ctx context.Context, layerRequest *tg.ContactsResolveUsernameRequest) (any, error) {
return r.onContactsResolveUsername(ctx, layerRequest)
})
registerRPC[*tg.ContactsResolvePhoneRequest](d, tlprofile.SemanticMethodContactsResolvePhone, func(ctx context.Context, layerRequest *tg.ContactsResolvePhoneRequest) (any, error) {
return r.onContactsResolvePhone(ctx, layerRequest.
Phone)
})
registerRPC[*tg.ContactsGetTopPeersRequest](d, tlprofile.SemanticMethodContactsGetTopPeers, func(ctx context.Context, req *tg.ContactsGetTopPeersRequest) (any, error) {
return tdesktop.TopPeers(), nil
})
d.OnContactsGetBlocked(r.onContactsGetBlocked)
d.OnContactsGetBirthdays(func(ctx context.Context) (*tg.ContactsContactBirthdays, error) {
registerRPC[*tg.ContactsGetBlockedRequest](d, tlprofile.SemanticMethodContactsGetBlocked, func(ctx context.Context, layerRequest *tg.ContactsGetBlockedRequest) (any, error) {
return r.onContactsGetBlocked(ctx, layerRequest)
})
registerRPC[*tg.ContactsGetBirthdaysRequest](d, tlprofile.SemanticMethodContactsGetBirthdays, func(ctx context.Context, layerRequest *tg.ContactsGetBirthdaysRequest) (any, error) {
if _, _, err := r.currentUserID(ctx); err != nil {
return nil, internalErr()
}
return &tg.ContactsContactBirthdays{Contacts: []tg.ContactBirthday{}, Users: []tg.UserClass{}}, nil
})
d.OnContactsGetSponsoredPeers(func(ctx context.Context, q string) (tg.ContactsSponsoredPeersClass, error) {
registerRPC[*tg.ContactsGetSponsoredPeersRequest](d, tlprofile.SemanticMethodContactsGetSponsoredPeers, func(ctx context.Context, layerRequest *tg.ContactsGetSponsoredPeersRequest) (any, error) {
q := layerRequest.
Q
_ = q
if utf8.RuneCountInString(q) > maxContactSearchQLen {
return nil, limitInvalidErr()
}
return &tg.ContactsSponsoredPeersEmpty{}, nil
})
}
func (r *Router) onContactsEditCloseFriends(ctx context.Context, id []int64) (bool, error) {

View file

@ -83,12 +83,8 @@ func TestContactsEditCloseFriendsProjectsUserFlag(t *testing.T) {
if err != nil {
t.Fatalf("dispatch edit close friends: %v", err)
}
box, ok := enc.(*tg.BoolBox)
if !ok {
t.Fatalf("edit close friends result = %T, want BoolBox", enc)
}
if _, ok := box.Bool.(*tg.BoolTrue); !ok {
t.Fatalf("edit close friends bool = %T, want BoolTrue", box.Bool)
if value, ok := dispatchCanonicalValue(enc).(bool); !ok || !value {
t.Fatalf("edit close friends result = %#v (%T), want true", dispatchCanonicalValue(enc), enc)
}
var get bin.Buffer
@ -99,12 +95,11 @@ func TestContactsEditCloseFriendsProjectsUserFlag(t *testing.T) {
if err != nil {
t.Fatalf("dispatch get contacts: %v", err)
}
contactsBox, ok := got.(*tg.ContactsContactsBox)
list, ok := got.(*tg.ContactsContacts)
if !ok {
t.Fatalf("contacts result = %T %+v, want ContactsContactsBox", got, got)
t.Fatalf("contacts result = %T %+v, want *tg.ContactsContacts", got, got)
}
list, ok := contactsBox.Contacts.(*tg.ContactsContacts)
if !ok || len(list.Users) != 1 {
if len(list.Users) != 1 {
t.Fatalf("contacts result = %T %+v, want one contact user", got, got)
}
user, ok := list.Users[0].(*tg.User)

View file

@ -65,13 +65,9 @@ func TestMessagesGetDialogsReturnsNotModifiedFromFullListHash(t *testing.T) {
if err != nil {
t.Fatalf("dispatch: %v", err)
}
box, ok := enc.(*tg.MessagesDialogsBox)
got, ok := enc.(*tg.MessagesDialogsNotModified)
if !ok {
t.Fatalf("response = %T, want *tg.MessagesDialogsBox", enc)
}
got, ok := box.Dialogs.(*tg.MessagesDialogsNotModified)
if !ok {
t.Fatalf("boxed response = %T, want *tg.MessagesDialogsNotModified", box.Dialogs)
t.Fatalf("response = %T, want *tg.MessagesDialogsNotModified", enc)
}
if got.Count != 3 || dialogs.filter.Hash != 77 {
t.Fatalf("not modified = %+v filter %+v, want count/hash from service", got, dialogs.filter)
@ -100,13 +96,9 @@ func TestMessagesGetDialogsHashHitSkipsFullListLoad(t *testing.T) {
if err != nil {
t.Fatalf("dispatch: %v", err)
}
box, ok := enc.(*tg.MessagesDialogsBox)
got, ok := enc.(*tg.MessagesDialogsNotModified)
if !ok {
t.Fatalf("response = %T, want *tg.MessagesDialogsBox", enc)
}
got, ok := box.Dialogs.(*tg.MessagesDialogsNotModified)
if !ok {
t.Fatalf("boxed response = %T, want *tg.MessagesDialogsNotModified", box.Dialogs)
t.Fatalf("response = %T, want *tg.MessagesDialogsNotModified", enc)
}
if got.Count != 5 {
t.Fatalf("not modified count = %d, want 5", got.Count)
@ -733,13 +725,12 @@ func TestMessagesGetDialogsIncludesCloudDraft(t *testing.T) {
if err != nil {
t.Fatalf("get dialogs: %v", err)
}
box, ok := got.(*tg.MessagesDialogsBox)
dialogs, ok := got.(*tg.MessagesDialogs)
if !ok {
t.Fatalf("response = %T, want boxed messages.dialogs", got)
t.Fatalf("response = %T, want *tg.MessagesDialogs", got)
}
dialogs, ok := box.Dialogs.(*tg.MessagesDialogs)
if !ok || len(dialogs.Dialogs) != 1 {
t.Fatalf("dialogs = %T %+v, want one messages.dialogs", box.Dialogs, box.Dialogs)
if len(dialogs.Dialogs) != 1 {
t.Fatalf("dialogs = %+v, want one messages.dialogs", dialogs)
}
dialog, ok := dialogs.Dialogs[0].(*tg.Dialog)
if !ok {
@ -1092,13 +1083,9 @@ func TestMessagesGetDialogsTDesktopInitialPageMergesPinnedHeader(t *testing.T) {
if err != nil {
t.Fatalf("dispatch get dialogs: %v", err)
}
box, ok := enc.(*tg.MessagesDialogsBox)
out, ok := enc.(*tg.MessagesDialogs)
if !ok {
t.Fatalf("response = %T, want *tg.MessagesDialogsBox", enc)
}
out, ok := box.Dialogs.(*tg.MessagesDialogs)
if !ok {
t.Fatalf("dialogs response = %T, want *tg.MessagesDialogs", box.Dialogs)
t.Fatalf("response = %T, want *tg.MessagesDialogs", enc)
}
if dialogs.getDialogsCalls != 2 || len(dialogs.filters) != 2 {
t.Fatalf("GetDialogs calls = %d filters %+v, want normal + pinned", dialogs.getDialogsCalls, dialogs.filters)
@ -1186,8 +1173,7 @@ func TestMessagesGetDialogsNonTDesktopKeepsExcludePinnedResponse(t *testing.T) {
if err != nil {
t.Fatalf("dispatch get dialogs: %v", err)
}
box := enc.(*tg.MessagesDialogsBox)
out := box.Dialogs.(*tg.MessagesDialogs)
out := enc.(*tg.MessagesDialogs)
if dialogs.getDialogsCalls != 1 {
t.Fatalf("GetDialogs calls = %d, want no pinned compatibility load", dialogs.getDialogsCalls)
}

View file

@ -1,4 +1,4 @@
// Package rpc 是按 TypeID 路由的 RPC 层:封装 tg.ServerDispatcher或等价薄封装
// Package rpc 是按 semantic method 路由的 RPC 层:封装 tlprofile.Dispatcher
// 在 handler 边界把 iamxvbaba/td/tg 类型转换为内部 domain command/query统一 tgerr.Error 到
// rpc_error 的映射,注入 auth_key_id/session_id/user_id/layer/设备/语言 等上下文,
// 并对未知 RPC 进入 compatibility trace不静默吞掉记入 docs/compatibility-matrix.md

View file

@ -5,8 +5,7 @@ import (
"errors"
"fmt"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/store"
)
@ -142,7 +141,7 @@ func (r *Router) cacheResolvedDurableSessionLayer(
return nil
}
key := clientInfoSessionKey{rawAuthKeyID: rawAuthKeyID, sessionID: sessionID}
profile, supported := tg.ResolveLayerProfile(value.Layer)
profile, supported := tlprofile.ResolveProfile(value.Layer)
if !supported || int(profile) != value.Layer {
// A newer binary may have persisted a future profile. Never leave an old
// typed codec shadow beside that raw authoritative watermark. Observation

View file

@ -1,30 +1,64 @@
package rpc
import "github.com/iamxvbaba/td/tg"
import (
"context"
// registerEncrypted 注册私聊端对端加密Secret Chat域 RPC。
//
// 归属约定messages.getDhConfig 属通话域DH 参数下发),由 registerPhone 注册、
// 密聊复用,**本处绝不重复注册 OnMessagesGetDhConfig**gotd ServerDispatcher 同一
// RPC 重复 On* 是静默 last-wins会覆盖 phone 域真实现)。
//
// P0 落地握手三件套sendEncrypted / sendEncryptedFile / sendEncryptedService /
// readEncryptedHistory / setEncryptedTyping / receivedQueue / reportEncryptedSpam /
// uploadEncryptedFile 暂未注册,落 fallback → NOT_IMPLEMENTED + compatibility trace
// 属 P1/P2qts 引擎 + 消息投递)。设计 docs/secret-chat-module.md。
func (r *Router) registerEncrypted(d *tg.ServerDispatcher) {
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
// registerEncrypted 注册私聊端对端加密Secret Chat域 RPC。
//
// 归属约定messages.getDhConfig 属通话域DH 参数下发),由 registerPhone 注册、
// 密聊复用,**本处绝不重复注册 messages.getDhConfig**tlprofile.Dispatcher 同一
// RPC 重复 On* 是静默 last-wins会覆盖 phone 域真实现)。
//
// P0 落地握手三件套sendEncrypted / sendEncryptedFile / sendEncryptedService /
// readEncryptedHistory / setEncryptedTyping / receivedQueue / reportEncryptedSpam /
// uploadEncryptedFile 暂未注册,落 fallback → NOT_IMPLEMENTED + compatibility trace
// 属 P1/P2qts 引擎 + 消息投递)。设计 docs/secret-chat-module.md。
)
func (r *Router) registerEncrypted(d *tlprofile.Dispatcher) {
registerRPC[
// P0握手三件套。
d.OnMessagesRequestEncryption(r.onMessagesRequestEncryption)
d.OnMessagesAcceptEncryption(r.onMessagesAcceptEncryption)
d.OnMessagesDiscardEncryption(r.onMessagesDiscardEncryption)
// P1qts 消息收发 + 已读/typing + 队列确认。
d.OnMessagesSendEncrypted(r.onMessagesSendEncrypted)
d.OnMessagesSendEncryptedService(r.onMessagesSendEncryptedService)
d.OnMessagesReadEncryptedHistory(r.onMessagesReadEncryptedHistory)
d.OnMessagesSetEncryptedTyping(r.onMessagesSetEncryptedTyping)
d.OnMessagesReceivedQueue(r.onMessagesReceivedQueue)
d.OnMessagesReportEncryptedSpam(r.onMessagesReportEncryptedSpam)
// P2密聊文件。
d.OnMessagesSendEncryptedFile(r.onMessagesSendEncryptedFile)
d.OnMessagesUploadEncryptedFile(r.onMessagesUploadEncryptedFile)
*tg.MessagesRequestEncryptionRequest](d, tlprofile.SemanticMethodMessagesRequestEncryption, func(ctx context.Context, layerRequest *tg.MessagesRequestEncryptionRequest) (any, error) {
return r.onMessagesRequestEncryption(ctx, layerRequest)
})
registerRPC[*tg.MessagesAcceptEncryptionRequest](d, tlprofile.SemanticMethodMessagesAcceptEncryption, func(ctx context.Context, layerRequest *tg.MessagesAcceptEncryptionRequest) (
// P1qts 消息收发 + 已读/typing + 队列确认。
any, error) {
return r.onMessagesAcceptEncryption(ctx, layerRequest)
})
registerRPC[*tg.MessagesDiscardEncryptionRequest](d, tlprofile.SemanticMethodMessagesDiscardEncryption, func(ctx context.Context, layerRequest *tg.MessagesDiscardEncryptionRequest) (any, error) {
return r.onMessagesDiscardEncryption(ctx, layerRequest)
})
registerRPC[*tg.MessagesSendEncryptedRequest](d, tlprofile.SemanticMethodMessagesSendEncrypted, func(ctx context.Context, layerRequest *tg.MessagesSendEncryptedRequest) (any, error) {
return r.onMessagesSendEncrypted(ctx, layerRequest)
})
registerRPC[*tg.MessagesSendEncryptedServiceRequest](d, tlprofile.SemanticMethodMessagesSendEncryptedService, func(ctx context.Context, layerRequest *tg.MessagesSendEncryptedServiceRequest) (any, error) {
return r.onMessagesSendEncryptedService(ctx, layerRequest)
})
registerRPC[*tg.MessagesReadEncryptedHistoryRequest](d, tlprofile.SemanticMethodMessagesReadEncryptedHistory, func(ctx context.Context, layerRequest *tg.MessagesReadEncryptedHistoryRequest) (any, error) {
return r.onMessagesReadEncryptedHistory(ctx, layerRequest)
})
registerRPC[*tg.MessagesSetEncryptedTypingRequest](d, tlprofile.SemanticMethodMessagesSetEncryptedTyping, func(ctx context.Context, layerRequest *tg.MessagesSetEncryptedTypingRequest) (any, error) {
return r.onMessagesSetEncryptedTyping(ctx, layerRequest)
})
registerRPC[*tg.MessagesReceivedQueueRequest](d, tlprofile.SemanticMethodMessagesReceivedQueue, func(ctx context.Context, layerRequest *tg.MessagesReceivedQueueRequest) (any,
// P2密聊文件。
error) {
return r.onMessagesReceivedQueue(ctx, layerRequest.
MaxQts)
})
registerRPC[*tg.MessagesReportEncryptedSpamRequest](d, tlprofile.SemanticMethodMessagesReportEncryptedSpam, func(ctx context.Context, layerRequest *tg.MessagesReportEncryptedSpamRequest) (any, error) {
return r.onMessagesReportEncryptedSpam(ctx, layerRequest.
Peer)
})
registerRPC[*tg.MessagesSendEncryptedFileRequest](d, tlprofile.SemanticMethodMessagesSendEncryptedFile, func(ctx context.Context, layerRequest *tg.MessagesSendEncryptedFileRequest) (any, error) {
return r.onMessagesSendEncryptedFile(ctx, layerRequest)
})
registerRPC[*tg.MessagesUploadEncryptedFileRequest](d, tlprofile.SemanticMethodMessagesUploadEncryptedFile, func(ctx context.Context, layerRequest *tg.MessagesUploadEncryptedFileRequest) (any, error) {
return r.onMessagesUploadEncryptedFile(ctx, layerRequest)
})
}

View file

@ -5,11 +5,16 @@ import (
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/domain"
)
func (r *Router) registerFolders(d *tg.ServerDispatcher) {
d.OnFoldersEditPeerFolders(r.onFoldersEditPeerFolders)
func (r *Router) registerFolders(d *tlprofile.Dispatcher) {
registerRPC[*tg.FoldersEditPeerFoldersRequest](d, tlprofile.SemanticMethodFoldersEditPeerFolders, func(ctx context.Context, layerRequest *tg.FoldersEditPeerFoldersRequest) (any, error) {
return r.onFoldersEditPeerFolders(ctx, layerRequest.
FolderPeers)
})
}
func (r *Router) onFoldersEditPeerFolders(ctx context.Context, folderPeers []tg.InputFolderPeer) (tg.UpdatesClass, error) {

View file

@ -13,6 +13,7 @@ import (
"github.com/iamxvbaba/td/tgerr"
"go.uber.org/zap/zaptest"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/domain"
)
@ -130,11 +131,11 @@ func TestFrozenMethodGateIsUserScopedAcrossSessionsAndUnfreezesImmediately(t *te
func TestFrozenMethodGateReturns420BeforeLayerHandler(t *testing.T) {
const userID = int64(1001)
for _, profile := range []tg.LayerProfile{
tg.LayerProfile225,
tg.LayerProfile226,
tg.LayerProfile227,
tg.LayerProfile228,
for _, profile := range []tlprofile.Profile{
tlprofile.Profile225,
tlprofile.Profile226,
tlprofile.Profile227,
tlprofile.Profile228,
} {
t.Run(fmt.Sprintf("layer_%d", profile), func(t *testing.T) {
provider := &frozenGateFreezeProvider{freeze: frozenGateActiveState(userID), found: true}
@ -149,7 +150,7 @@ func TestFrozenMethodGateReturns420BeforeLayerHandler(t *testing.T) {
Message: "must not reach handler",
RandomID: 1,
})
admitted, err := router.AdmitLayer(profile, &body, tg.LayerDecodeLimits{})
admitted, err := router.AdmitLayer(profile, &body, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}

View file

@ -6,27 +6,38 @@ import (
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
androidcompat "telesrv/internal/compat/android"
ioscompat "telesrv/internal/compat/ios"
"telesrv/internal/compat/tdesktop"
)
// registerHelp 注册 help.* RPC handlerDC 配置、最近 DC
func (r *Router) registerHelp(d *tg.ServerDispatcher) {
d.OnHelpGetConfig(r.onHelpGetConfig)
d.OnHelpGetNearestDC(func(ctx context.Context) (*tg.NearestDC, error) {
func (r *Router) registerHelp(d *tlprofile.Dispatcher) {
registerRPC[*tg.HelpGetConfigRequest](d, tlprofile.SemanticMethodHelpGetConfig, func(ctx context.Context, layerRequest *tg.HelpGetConfigRequest) (any, error) {
return r.onHelpGetConfig(ctx)
})
registerRPC[*tg.HelpGetNearestDCRequest](d, tlprofile.SemanticMethodHelpGetNearestDC, func(ctx context.Context, layerRequest *tg.HelpGetNearestDCRequest) (any, error) {
return tdesktop.NearestDC(r.cfg.DC), nil
})
d.OnHelpGetInviteText(func(ctx context.Context) (*tg.HelpInviteText, error) {
registerRPC[*tg.HelpGetInviteTextRequest](d, tlprofile.SemanticMethodHelpGetInviteText, func(ctx context.Context, layerRequest *tg.HelpGetInviteTextRequest) (any, error) {
return &tg.HelpInviteText{Message: "Join me on Telegram."}, nil
})
d.OnHelpGetAppUpdate(func(ctx context.Context, source string) (tg.HelpAppUpdateClass, error) {
registerRPC[*tg.HelpGetAppUpdateRequest](d, tlprofile.SemanticMethodHelpGetAppUpdate, func(ctx context.Context, layerRequest *tg.HelpGetAppUpdateRequest) (any, error) {
source := layerRequest.
Source
_ = source
if _, _, err := r.currentUserID(ctx); err != nil {
return nil, internalErr()
}
return ioscompat.NoAppUpdate(), nil
})
d.OnHelpGetAppConfig(func(ctx context.Context, hash int) (tg.HelpAppConfigClass, error) {
registerRPC[*tg.HelpGetAppConfigRequest](d, tlprofile.SemanticMethodHelpGetAppConfig, func(ctx context.Context, layerRequest *tg.HelpGetAppConfigRequest) (any, error) {
hash := layerRequest.
Hash
_ = hash
if r.deps.Help == nil {
return tdesktop.AppConfig(hash), nil
}
@ -40,7 +51,7 @@ func (r *Router) registerHelp(d *tg.ServerDispatcher) {
}
return &tg.HelpAppConfig{Hash: cfg.Hash, Config: tgJSONValue(cfg.JSON)}, nil
})
d.OnHelpGetCountriesList(func(ctx context.Context, req *tg.HelpGetCountriesListRequest) (tg.HelpCountriesListClass, error) {
registerRPC[*tg.HelpGetCountriesListRequest](d, tlprofile.SemanticMethodHelpGetCountriesList, func(ctx context.Context, req *tg.HelpGetCountriesListRequest) (any, error) {
if r.deps.Help == nil {
return tdesktop.CountriesList(req.Hash), nil
}
@ -53,31 +64,53 @@ func (r *Router) registerHelp(d *tg.ServerDispatcher) {
}
return tgCountriesList(list), nil
})
d.OnHelpGetTimezonesList(func(ctx context.Context, hash int) (tg.HelpTimezonesListClass, error) {
registerRPC[*tg.HelpGetTimezonesListRequest](d, tlprofile.SemanticMethodHelpGetTimezonesList, func(ctx context.Context, layerRequest *tg.HelpGetTimezonesListRequest) (any, error) {
hash := layerRequest.
Hash
_ = hash
return tdesktop.TimezonesList(hash), nil
})
d.OnHelpGetPeerColors(func(ctx context.Context, hash int) (tg.HelpPeerColorsClass, error) {
registerRPC[*tg.HelpGetPeerColorsRequest](d, tlprofile.SemanticMethodHelpGetPeerColors, func(ctx context.Context, layerRequest *tg.HelpGetPeerColorsRequest) (any, error) {
hash := layerRequest.
Hash
_ = hash
return tdesktop.PeerColors(hash), nil
})
d.OnHelpGetPeerProfileColors(func(ctx context.Context, hash int) (tg.HelpPeerColorsClass, error) {
registerRPC[*tg.HelpGetPeerProfileColorsRequest](d, tlprofile.SemanticMethodHelpGetPeerProfileColors, func(ctx context.Context, layerRequest *tg.HelpGetPeerProfileColorsRequest) (any, error) {
hash := layerRequest.
Hash
_ = hash
return tdesktop.PeerProfileColors(hash), nil
})
d.OnHelpGetPromoData(func(ctx context.Context) (tg.HelpPromoDataClass, error) {
registerRPC[*tg.HelpGetPromoDataRequest](d, tlprofile.SemanticMethodHelpGetPromoData, func(ctx context.Context, layerRequest *tg.HelpGetPromoDataRequest) (any, error) {
return tdesktop.PromoData(r.clock.Now()), nil
})
d.OnHelpGetTermsOfServiceUpdate(func(ctx context.Context) (tg.HelpTermsOfServiceUpdateClass, error) {
registerRPC[*tg.HelpGetTermsOfServiceUpdateRequest](d, tlprofile.SemanticMethodHelpGetTermsOfServiceUpdate, func(ctx context.Context, layerRequest *tg.HelpGetTermsOfServiceUpdateRequest) (any, error) {
return tdesktop.TermsOfServiceUpdate(r.clock.Now()), nil
})
registerRPC[
// 客户端遇到无法识别的 tg:// 深链时会查询 help.getDeepLinkInfo。telesrv 不维护
// “需更新 App”的特殊深链提示库对所有 path 返回 deepLinkInfoEmpty——这是规范的
// “无特殊信息”应答DrKLO 仅在收到非空 deepLinkInfo 时才弹“请更新 App”弹窗
// LaunchActivity.java:5175收到 Empty 则静默放行按普通链接处理。此前未注册
// handler 会落 fallback 返回 500 NOT_IMPLEMENTED污染日志且非正确协议行为
d.OnHelpGetDeepLinkInfo(func(ctx context.Context, path string) (tg.HelpDeepLinkInfoClass, error) {
*tg.HelpGetDeepLinkInfoRequest](d, tlprofile.SemanticMethodHelpGetDeepLinkInfo, func(ctx context.Context, layerRequest *tg.HelpGetDeepLinkInfoRequest) (any, error) {
path := layerRequest.
Path
_ = path
return &tg.HelpDeepLinkInfoEmpty{}, nil
})
d.OnHelpDismissSuggestion(r.onHelpDismissSuggestion)
d.OnHelpGetPremiumPromo(r.onHelpGetPremiumPromo)
registerRPC[*tg.HelpDismissSuggestionRequest](d, tlprofile.SemanticMethodHelpDismissSuggestion, func(ctx context.Context, layerRequest *tg.HelpDismissSuggestionRequest) (any, error) {
return r.onHelpDismissSuggestion(ctx, layerRequest)
})
registerRPC[*tg.HelpGetPremiumPromoRequest](d, tlprofile.SemanticMethodHelpGetPremiumPromo, func(ctx context.Context, layerRequest *tg.HelpGetPremiumPromoRequest) (any, error) {
return r.onHelpGetPremiumPromo(ctx)
})
}
func (r *Router) onHelpGetConfig(ctx context.Context) (*tg.Config, error) {

View file

@ -26,12 +26,8 @@ func TestHelpDismissSuggestionAndroidChangePhone(t *testing.T) {
if err != nil {
t.Fatalf("dispatch: %v", err)
}
box, ok := enc.(*tg.BoolBox)
if !ok {
t.Fatalf("response = %T, want *tg.BoolBox", enc)
}
if _, ok := box.Bool.(*tg.BoolTrue); !ok {
t.Fatalf("bool response = %T, want BoolTrue", box.Bool)
if value, ok := dispatchCanonicalValue(enc).(bool); !ok || !value {
t.Fatalf("response = %#v (%T), want true", dispatchCanonicalValue(enc), enc)
}
}

View file

@ -5,6 +5,7 @@ import (
"strings"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
)
// registerLangpack 注册 langpack.* RPC handler。
@ -12,19 +13,24 @@ import (
// 老客户端DrKLO发的是不带 lang_pack 参数的旧构造器,已由 gotdgen client overlay 入站升级为 canonical
// 形态并把 lang_pack 置空;故这里 lang_pack 为空时回退到按 client 信息派生langPackFromClient
// 与历史 handleLegacyLangpack* 的行为一致。
func (r *Router) registerLangpack(d *tg.ServerDispatcher) {
d.OnLangpackGetLanguages(func(ctx context.Context, langPack string) ([]tg.LangPackLanguage, error) {
func (r *Router) registerLangpack(d *tlprofile.Dispatcher) {
registerRPC[*tg.LangpackGetLanguagesRequest](d, tlprofile.SemanticMethodLangpackGetLanguages, func(ctx context.Context, layerRequest *tg.LangpackGetLanguagesRequest) (any, error) {
langPack := layerRequest.
LangPack
_ = langPack
return r.langpackLanguages(ctx, langPack), nil
})
d.OnLangpackGetLanguage(func(ctx context.Context, req *tg.LangpackGetLanguageRequest) (*tg.LangPackLanguage, error) {
registerRPC[*tg.LangpackGetLanguageRequest](d, tlprofile.SemanticMethodLangpackGetLanguage, func(ctx context.Context, req *tg.LangpackGetLanguageRequest) (any, error) {
if req == nil {
return nil, inputConstructorInvalidErr()
}
lang := r.langpackLanguage(ctx, req.LangPack, req.LangCode)
return &lang, nil
})
d.OnLangpackGetLangPack(func(ctx context.Context, req *tg.LangpackGetLangPackRequest) (*tg.LangPackDifference, error) {
registerRPC[*tg.LangpackGetLangPackRequest](d, tlprofile.SemanticMethodLangpackGetLangPack, func(ctx context.Context, req *tg.LangpackGetLangPackRequest) (any, error) {
langPack := langPackOrClient(ctx, req.LangPack)
_ = langPack
if r.deps.LangPack == nil {
return &tg.LangPackDifference{LangCode: req.LangCode}, nil
}
@ -34,7 +40,7 @@ func (r *Router) registerLangpack(d *tg.ServerDispatcher) {
}
return tgLangPackDifference(pack), nil
})
d.OnLangpackGetDifference(func(ctx context.Context, req *tg.LangpackGetDifferenceRequest) (*tg.LangPackDifference, error) {
registerRPC[*tg.LangpackGetDifferenceRequest](d, tlprofile.SemanticMethodLangpackGetDifference, func(ctx context.Context, req *tg.LangpackGetDifferenceRequest) (any, error) {
if r.deps.LangPack == nil {
return &tg.LangPackDifference{LangCode: req.LangCode, FromVersion: req.FromVersion}, nil
}
@ -44,9 +50,9 @@ func (r *Router) registerLangpack(d *tg.ServerDispatcher) {
}
return tgLangPackDifference(pack), nil
})
d.OnLangpackGetStrings(func(ctx context.Context, req *tg.LangpackGetStringsRequest) ([]tg.LangPackStringClass, error) {
registerRPC[*tg.LangpackGetStringsRequest](d, tlprofile.SemanticMethodLangpackGetStrings, func(ctx context.Context, req *tg.LangpackGetStringsRequest) (any, error) {
if r.deps.LangPack == nil {
return nil, nil
return []tg.LangPackStringClass{}, nil
}
pack, err := r.deps.LangPack.GetStrings(ctx, langPackOrClient(ctx, req.LangPack), req.LangCode, req.Keys)
if err != nil {
@ -54,6 +60,7 @@ func (r *Router) registerLangpack(d *tg.ServerDispatcher) {
}
return tgLangPackStrings(pack.Strings), nil
})
}
// langPackOrClient 返回请求里的 lang_pack为空老客户端经生成 overlay 升级而来)时按 client 派生。

View file

@ -7,9 +7,9 @@ import (
"time"
"github.com/iamxvbaba/td/bin"
"github.com/iamxvbaba/td/tg"
"go.uber.org/zap"
"github.com/iamxvbaba/td/tlprofile"
compatandroid "telesrv/internal/compat/android"
"telesrv/internal/observability/dbtrace"
)
@ -56,28 +56,28 @@ const layerRPCReplayRestoreTimeout = 5 * time.Second
// AdmitLayer performs generated, bounded, exact-profile admission without
// touching auth/session stores. The MTProto edge must call it before acquiring
// an RPC flight/cache slot or scheduling business work.
func (r *Router) AdmitLayer(profile tg.LayerProfile, b *bin.Buffer, limits tg.LayerDecodeLimits) (tg.LayerRequest, error) {
func (r *Router) AdmitLayer(profile tlprofile.Profile, b *bin.Buffer, limits tlprofile.Limits) (tlprofile.Admission, error) {
if r == nil || r.dispatcher == nil {
return tg.LayerRequest{}, internalErr()
return tlprofile.Admission{}, internalErr()
}
if b == nil {
return tg.LayerRequest{}, inputRequestInvalidErr()
return tlprofile.Admission{}, inputRequestInvalidErr()
}
return r.dispatcher.AdmitLayerWithLimits(profile, b, limits)
return r.dispatcher.Admit(profile, b, limits)
}
// AdmitDefaultLayer admits a request using an inherited auth-key profile as
// its effective codec while still allowing an explicit invokeWithLayer in the
// same wrapper chain to correct that default. Generated admission preserves
// the distinction through EffectiveProfile and ProfileEvidence.
func (r *Router) AdmitDefaultLayer(profile tg.LayerProfile, b *bin.Buffer, limits tg.LayerDecodeLimits) (tg.LayerRequest, error) {
func (r *Router) AdmitDefaultLayer(profile tlprofile.Profile, b *bin.Buffer, limits tlprofile.Limits) (tlprofile.Admission, error) {
if r == nil || r.dispatcher == nil {
return tg.LayerRequest{}, internalErr()
return tlprofile.Admission{}, internalErr()
}
if b == nil {
return tg.LayerRequest{}, inputRequestInvalidErr()
return tlprofile.Admission{}, inputRequestInvalidErr()
}
return r.dispatcher.AdmitDefaultLayerWithLimits(profile, b, limits)
return r.dispatcher.AdmitDefault(profile, b, limits)
}
// registerAndroidLayerRPCAdapter installs the only client-private schema seam.
@ -85,20 +85,20 @@ func (r *Router) AdmitDefaultLayer(profile tg.LayerProfile, b *bin.Buffer, limit
// after recursively peeling every official wrapper. AdaptCanonical runs this
// dispatcher's semantic field policies before the first generated typed
// materialization, then core revalidates exact wire with the adapter disabled.
func (r *Router) registerAndroidLayerRPCAdapter(d *tg.ServerDispatcher) {
func (r *Router) registerAndroidLayerRPCAdapter(d *tlprofile.Dispatcher) {
if d == nil {
panic("rpc: register Android layer RPC adapter on nil dispatcher")
}
d.OnLayerRPCUnknownMethod(func(view tg.LayerRPCUnknownMethodView) (tg.LayerOutboundCall, bool, error) {
d.OnUnknownMethod(func(view tlprofile.UnknownMethodView) (tlprofile.OutboundCall, bool, error) {
outbound, handled, err := compatandroid.AdaptPrivateLayerRPC(view)
if !handled {
return tg.LayerOutboundCall{}, false, nil
return tlprofile.OutboundCall{}, false, nil
}
if err != nil {
return tg.LayerOutboundCall{}, true, err
return tlprofile.OutboundCall{}, true, err
}
if r.log != nil {
_, method, _ := tg.LayerSemanticName(outbound.Method())
_, method, _ := tlprofile.SemanticName(outbound.Method())
r.log.Info("Android private RPC admitted through generated exact adapter",
zap.Int("profile", int(view.Profile())),
zap.String("method", method),
@ -119,12 +119,12 @@ func (r *Router) PrepareAdmittedReplay(
sessionID int64,
msgID int64,
admissionSeq uint64,
request tg.LayerRequest,
request tlprofile.Admission,
) (func() error, error) {
if r == nil || r.dispatcher == nil || request.Prepared().WireSize() <= 0 {
return nil, inputRequestInvalidErr()
}
_, method, ok := tg.LayerSemanticName(request.Call().Method())
_, method, ok := tlprofile.SemanticName(request.Call().Method())
if !ok || method == "" {
return nil, inputRequestInvalidErr()
}
@ -190,11 +190,11 @@ func (r *Router) PrepareAdmittedReplay(
// either obtains authoritative profile evidence from invokeWithLayer or admits
// a closed terminal whose complete request and result wire graphs were proven
// invariant across every generated profile. The latter never freezes a layer.
func (r *Router) AdmitUnprofiled(b *bin.Buffer, limits tg.LayerDecodeLimits) (tg.LayerRequest, error) {
func (r *Router) AdmitUnprofiled(b *bin.Buffer, limits tlprofile.Limits) (tlprofile.Admission, error) {
if r == nil || r.dispatcher == nil {
return tg.LayerRequest{}, internalErr()
return tlprofile.Admission{}, internalErr()
}
return r.dispatcher.AdmitUnprofiledWithLimits(b, limits)
return r.dispatcher.AdmitUnprofiled(b, limits)
}
// DispatchAdmitted executes one generated admission lease. invokeAfterMsg(s)
@ -206,14 +206,14 @@ func (r *Router) DispatchAdmitted(
sessionID int64,
msgID int64,
admissionSeq uint64,
request tg.LayerRequest,
) (tg.LayerRPCResult, string, error) {
request tlprofile.Admission,
) (tlprofile.Result, string, error) {
if r == nil || r.dispatcher == nil {
return nil, "", internalErr()
}
prepared := request.Prepared()
call := request.Call()
category, method, ok := tg.LayerSemanticName(call.Method())
category, method, ok := tlprofile.SemanticName(call.Method())
if !ok || category != "function" || method == "" || prepared.WireSize() <= 0 {
return nil, "", inputRequestInvalidErr()
}
@ -228,7 +228,7 @@ func (r *Router) DispatchAdmitted(
if err != nil {
return nil, method, err
}
if !r.dispatcher.HasLayerRPCHandler(call.Method()) {
if !r.dispatcher.Has(call.Method()) {
fields := append([]zap.Field{
zap.String("method", method),
zap.String("type_id", fmt.Sprintf("%#x", call.WireID())),
@ -239,7 +239,7 @@ func (r *Router) DispatchAdmitted(
}
return nil, method, notImplementedErr()
}
canonicalID, hasCanonicalID := tg.LayerWireID(tg.LayerProfileCanonical, call.Method())
canonicalID, hasCanonicalID := tlprofile.WireID(tlprofile.ProfileCanonical, call.Method())
if !hasCanonicalID {
return nil, method, inputRequestInvalidErr()
}
@ -262,7 +262,7 @@ func (r *Router) DispatchAdmitted(
}
dbBefore := dbtrace.SnapshotFromContext(ctx)
start := time.Now()
result, err := r.dispatcher.DispatchAdmitted(ctx, request)
result, err := r.dispatcher.Dispatch(ctx, request)
dur := time.Since(start)
dbDelta := dbtrace.SnapshotFromContext(ctx).Sub(dbBefore)
fields := append([]zap.Field{
@ -294,12 +294,12 @@ func (r *Router) DispatchAdmitted(
}
type layerRPCWrapperEffect struct {
semantic tg.LayerSemanticID
semantic tlprofile.SemanticID
layer int
info ClientInfo
}
func (r *Router) snapshotLayerRPCWrapperEffects(ctx context.Context, request tg.LayerRequest) ([]layerRPCWrapperEffect, error) {
func (r *Router) snapshotLayerRPCWrapperEffects(ctx context.Context, request tlprofile.Admission) ([]layerRPCWrapperEffect, error) {
if err := r.validateLayerRPCWrappers(ctx, request); err != nil {
return nil, err
}
@ -323,19 +323,19 @@ func (r *Router) snapshotLayerRPCWrapperEffects(ctx context.Context, request tg.
wrapper, _ := request.Wrapper(index)
effect := layerRPCWrapperEffect{semantic: wrapper.Semantic()}
switch wrapper.Semantic() {
case tg.LayerSemanticMethodInvokeWithLayer:
case tlprofile.SemanticMethodInvokeWithLayer:
layer, err := layerWrapperRequired[int](wrapper, "layer")
if err != nil || layer != int(profile) {
return nil, inputRequestInvalidErr()
}
effect.layer = layer
case tg.LayerSemanticMethodInitConnection:
case tlprofile.SemanticMethodInitConnection:
info, err := clientInfoFromLayerWrapper(wrapper)
if err != nil {
return nil, err
}
effect.info = info
case tg.LayerSemanticMethodInvokeAfterMsg, tg.LayerSemanticMethodInvokeAfterMsgs:
case tlprofile.SemanticMethodInvokeAfterMsg, tlprofile.SemanticMethodInvokeAfterMsgs:
// Dependency completion is an MTProto message-lifecycle fact. The edge
// validates it before scheduling this one-shot admission lease.
}
@ -346,9 +346,9 @@ func (r *Router) snapshotLayerRPCWrapperEffects(ctx context.Context, request tg.
func (r *Router) applyLayerRPCWrapperEffects(
ctx context.Context,
profile tg.LayerProfile,
profile tlprofile.Profile,
profileKnown bool,
identity tg.LayerPreparedCallIdentity,
identity tlprofile.PreparedIdentity,
effects []layerRPCWrapperEffect,
msgID int64,
admissionSeq uint64,
@ -379,15 +379,15 @@ func (r *Router) applyLayerRPCWrapperEffects(
}
for _, effect := range effects {
switch effect.semantic {
case tg.LayerSemanticMethodInvokeWithLayer:
case tlprofile.SemanticMethodInvokeWithLayer:
ctx = WithLayer(ctx, effect.layer)
// Shared Layer publication is an admission-time protocol effect. It has
// already been linearized by admissionSeq before the scheduler/rewrap
// split; handler execution and physical replay are intentionally unable
// to move that auth-key-wide default.
case tg.LayerSemanticMethodInvokeWithoutUpdates:
case tlprofile.SemanticMethodInvokeWithoutUpdates:
ctx = withInvokeWithoutUpdates(ctx)
case tg.LayerSemanticMethodInitConnection:
case tlprofile.SemanticMethodInitConnection:
if mode == layerRPCWrapperApplyReplayRestore {
if _, exists := ClientInfoFrom(ctx); exists {
// prepareRPCDispatchContext restored newer session/auth metadata.
@ -416,7 +416,7 @@ func (r *Router) applyLayerRPCWrapperEffects(
return context.WithValue(ctx, layerWrappersAppliedKey{}, identity)
}
func (r *Router) applyLayerRPCWrappers(ctx context.Context, msgID int64, admissionSeq uint64, request tg.LayerRequest) (context.Context, error) {
func (r *Router) applyLayerRPCWrappers(ctx context.Context, msgID int64, admissionSeq uint64, request tlprofile.Admission) (context.Context, error) {
effects, err := r.snapshotLayerRPCWrapperEffects(ctx, request)
if err != nil {
return nil, err
@ -428,25 +428,25 @@ func (r *Router) applyLayerRPCWrappers(ctx context.Context, msgID int64, admissi
func hasMutableLayerRPCWrapperEffect(effects []layerRPCWrapperEffect) bool {
for _, effect := range effects {
switch effect.semantic {
case tg.LayerSemanticMethodInvokeWithLayer, tg.LayerSemanticMethodInitConnection:
case tlprofile.SemanticMethodInvokeWithLayer, tlprofile.SemanticMethodInitConnection:
return true
}
}
return false
}
func (r *Router) validateLayerRPCWrappers(ctx context.Context, request tg.LayerRequest) error {
func (r *Router) validateLayerRPCWrappers(ctx context.Context, request tlprofile.Admission) error {
for index := 0; index < request.WrapperCount(); index++ {
wrapper, _ := request.Wrapper(index)
switch wrapper.Semantic() {
case tg.LayerSemanticMethodInvokeWithLayer,
tg.LayerSemanticMethodInvokeWithoutUpdates,
tg.LayerSemanticMethodInitConnection,
tg.LayerSemanticMethodInvokeAfterMsg,
tg.LayerSemanticMethodInvokeAfterMsgs:
case tlprofile.SemanticMethodInvokeWithLayer,
tlprofile.SemanticMethodInvokeWithoutUpdates,
tlprofile.SemanticMethodInitConnection,
tlprofile.SemanticMethodInvokeAfterMsg,
tlprofile.SemanticMethodInvokeAfterMsgs:
continue
default:
_, name, _ := tg.LayerSemanticName(wrapper.Semantic())
_, name, _ := tlprofile.SemanticName(wrapper.Semantic())
fields := append([]zap.Field{
zap.String("wrapper", name),
zap.String("type_id", fmt.Sprintf("%#x", wrapper.WireID())),
@ -461,15 +461,15 @@ func (r *Router) validateLayerRPCWrappers(ctx context.Context, request tg.LayerR
return nil
}
func (r *Router) consumeLayerRPCWrappers(ctx context.Context, request tg.LayerRequest, next tg.LayerRPCNext) error {
applied, ok := ctx.Value(layerWrappersAppliedKey{}).(tg.LayerPreparedCallIdentity)
func (r *Router) consumeLayerRPCWrappers(ctx context.Context, request tlprofile.Admission, next tlprofile.Next) error {
applied, ok := ctx.Value(layerWrappersAppliedKey{}).(tlprofile.PreparedIdentity)
if !ok || applied != request.Prepared().Identity() {
return fmt.Errorf("rpc: exact wrapper context was not applied to admitted request")
}
return next(ctx)
}
func layerWrapperRequired[T any](wrapper tg.LayerRPCWrapper, name string) (T, error) {
func layerWrapperRequired[T any](wrapper tlprofile.Wrapper, name string) (T, error) {
var zero T
value, present, ok, err := wrapper.Value(name)
if err != nil || !ok || !present {
@ -482,7 +482,7 @@ func layerWrapperRequired[T any](wrapper tg.LayerRPCWrapper, name string) (T, er
return typed, nil
}
func clientInfoFromLayerWrapper(wrapper tg.LayerRPCWrapper) (ClientInfo, error) {
func clientInfoFromLayerWrapper(wrapper tlprofile.Wrapper) (ClientInfo, error) {
apiID, err := layerWrapperRequired[int](wrapper, "api_id")
if err != nil {
return ClientInfo{}, err

View file

@ -14,6 +14,7 @@ import (
"github.com/iamxvbaba/td/tgerr"
"go.uber.org/zap/zaptest"
"github.com/iamxvbaba/td/tlprofile"
appfiles "telesrv/internal/app/files"
"telesrv/internal/domain"
"telesrv/internal/postresponse"
@ -33,7 +34,7 @@ func TestLayerAdmissionAndroidPrivateOverlayUsesExactProfile(t *testing.T) {
body.PutVectorHeader(0)
body.PutID(0x7f3b18ea) // inputPeerEmpty
admitted, err := r.AdmitLayer(tg.LayerProfile227, &body, tg.LayerDecodeLimits{})
admitted, err := r.AdmitLayer(tlprofile.Profile227, &body, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}
@ -41,10 +42,10 @@ func TestLayerAdmissionAndroidPrivateOverlayUsesExactProfile(t *testing.T) {
t.Fatalf("private request left %d bytes", body.Len())
}
call := admitted.Call()
if call.Profile() != tg.LayerProfile227 || call.Method() != tg.LayerSemanticMethodMessagesForwardMessages {
if call.Profile() != tlprofile.Profile227 || call.Method() != tlprofile.SemanticMethodMessagesForwardMessages {
t.Fatalf("private admission = profile:%d method:%#x", call.Profile(), call.Method())
}
if want, ok := tg.LayerWireID(tg.LayerProfile227, call.Method()); !ok || call.WireID() != want {
if want, ok := tlprofile.WireID(tlprofile.Profile227, call.Method()); !ok || call.WireID() != want {
t.Fatalf("private admission wire id = %#x, want %#x (ok=%v)", call.WireID(), want, ok)
}
}
@ -52,12 +53,12 @@ func TestLayerAdmissionAndroidPrivateOverlayUsesExactProfile(t *testing.T) {
func TestLayerAdmissionOfficialProfileOwnsOverlappingAndroidID(t *testing.T) {
r := New(Config{DC: 2, IP: "127.0.0.1", Port: 2398}, Deps{}, zaptest.NewLogger(t), clock.System)
request := &tg.ContactsSearchRequest{Q: "exact", Limit: 20}
body := encodeExactLayerRPC(t, tg.LayerProfile225, request)
admitted, err := r.AdmitLayer(tg.LayerProfile225, &body, tg.LayerDecodeLimits{})
body := encodeExactLayerRPC(t, tlprofile.Profile225, request)
admitted, err := r.AdmitLayer(tlprofile.Profile225, &body, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}
if body.Len() != 0 || admitted.Call().Method() != tg.LayerSemanticMethodContactsSearch {
if body.Len() != 0 || admitted.Call().Method() != tlprofile.SemanticMethodContactsSearch {
t.Fatalf("official overlap = remaining:%d method:%#x", body.Len(), admitted.Call().Method())
}
}
@ -66,7 +67,7 @@ func TestLayerAdmissionAndroidOverlayFailureDoesNotConsumeInput(t *testing.T) {
r := New(Config{DC: 2, IP: "127.0.0.1", Port: 2398}, Deps{}, zaptest.NewLogger(t), clock.System)
bounded := bin.Buffer{Buf: androidPrivateForwardMessagesWire()}
boundedOriginal := bounded.Copy()
if _, err := r.AdmitLayer(tg.LayerProfile227, &bounded, tg.LayerDecodeLimits{MaxWireBytes: len(boundedOriginal) - 1}); err == nil {
if _, err := r.AdmitLayer(tlprofile.Profile227, &bounded, tlprofile.Limits{MaxWireBytes: len(boundedOriginal) - 1}); err == nil {
t.Fatal("oversized Android-private request was admitted")
}
if !bytes.Equal(bounded.Raw(), boundedOriginal) {
@ -76,7 +77,7 @@ func TestLayerAdmissionAndroidOverlayFailureDoesNotConsumeInput(t *testing.T) {
malformed := bin.Buffer{}
malformed.PutID(0x41d41ade)
original := malformed.Copy()
if _, err := r.AdmitLayer(tg.LayerProfile227, &malformed, tg.LayerDecodeLimits{}); err == nil {
if _, err := r.AdmitLayer(tlprofile.Profile227, &malformed, tlprofile.Limits{}); err == nil {
t.Fatal("malformed Android-private request was admitted")
}
if string(malformed.Raw()) != string(original) {
@ -86,7 +87,7 @@ func TestLayerAdmissionAndroidOverlayFailureDoesNotConsumeInput(t *testing.T) {
unknown := bin.Buffer{}
unknown.PutID(0xdeadbeef)
original = unknown.Copy()
if _, err := r.AdmitLayer(tg.LayerProfile227, &unknown, tg.LayerDecodeLimits{}); !errors.Is(err, tg.ErrLayerUnknownRPCMethod) {
if _, err := r.AdmitLayer(tlprofile.Profile227, &unknown, tlprofile.Limits{}); !errors.Is(err, tlprofile.ErrUnknownRPCMethod) {
t.Fatalf("official unknown error = %v", err)
}
if string(unknown.Raw()) != string(original) {
@ -154,12 +155,12 @@ func TestLayerAdmissionAndroidPrivateInnermostAcrossWrappers(t *testing.T) {
replaceTerminalRPC(t, &initWire, private)
var unprofiledInit bin.Buffer
unprofiledInit.PutID(tg.InvokeWithLayerRequestTypeID)
unprofiledInit.PutInt(int(tg.LayerProfile227))
unprofiledInit.PutInt(int(tlprofile.Profile227))
unprofiledInit.Put(initWire.Raw())
var unprofiledBare bin.Buffer
unprofiledBare.PutID(tg.InvokeWithLayerRequestTypeID)
unprofiledBare.PutInt(int(tg.LayerProfile227))
unprofiledBare.PutInt(int(tlprofile.Profile227))
unprofiledBare.Put(private)
tests := []struct {
@ -179,18 +180,18 @@ func TestLayerAdmissionAndroidPrivateInnermostAcrossWrappers(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
body := bin.Buffer{Buf: append([]byte(nil), tc.body...)}
var (
admitted tg.LayerRequest
admitted tlprofile.Admission
err error
)
if tc.unprofiled {
admitted, err = r.AdmitUnprofiled(&body, tg.LayerDecodeLimits{})
admitted, err = r.AdmitUnprofiled(&body, tlprofile.Limits{})
} else {
admitted, err = r.AdmitLayer(tg.LayerProfile227, &body, tg.LayerDecodeLimits{})
admitted, err = r.AdmitLayer(tlprofile.Profile227, &body, tlprofile.Limits{})
}
if err != nil {
t.Fatal(err)
}
if body.Len() != 0 || admitted.WrapperCount() != tc.wrappers || admitted.Call().Method() != tg.LayerSemanticMethodMessagesForwardMessages {
if body.Len() != 0 || admitted.WrapperCount() != tc.wrappers || admitted.Call().Method() != tlprofile.SemanticMethodMessagesForwardMessages {
t.Fatalf("admission = remaining:%d wrappers:%d method:%#x", body.Len(), admitted.WrapperCount(), admitted.Call().Method())
}
})
@ -210,7 +211,7 @@ func TestLayerAdmissionAndroidPrivateFieldPolicyBeforeTypedMaterialization(t *te
// DrKLO's private createChat constructor is body-identical to canonical.
binary.LittleEndian.PutUint32(body.Buf[:4], 0x0034a818)
original := body.Copy()
if _, err := r.AdmitLayer(tg.LayerProfile227, &body, tg.LayerDecodeLimits{MaxVectorElements: 8 << 10}); !tgerr.Is(err, "LIMIT_INVALID") {
if _, err := r.AdmitLayer(tlprofile.Profile227, &body, tlprofile.Limits{MaxVectorElements: 8 << 10}); !tgerr.Is(err, "LIMIT_INVALID") {
t.Fatalf("private createChat admission err = %v, want LIMIT_INVALID", err)
}
if !bytes.Equal(body.Raw(), original) {
@ -219,7 +220,7 @@ func TestLayerAdmissionAndroidPrivateFieldPolicyBeforeTypedMaterialization(t *te
}
func TestLayerDispatchExactProfilesShareOneHandler(t *testing.T) {
for _, profile := range []tg.LayerProfile{tg.LayerProfile225, tg.LayerProfile227} {
for _, profile := range []tlprofile.Profile{tlprofile.Profile225, tlprofile.Profile227} {
t.Run(fmt.Sprintf("layer_%d", profile), func(t *testing.T) {
r := New(Config{DC: 2, IP: "127.0.0.1", Port: 2398}, Deps{}, zaptest.NewLogger(t), clock.System)
request := &tg.InvokeWithLayerRequest{
@ -239,7 +240,7 @@ func TestLayerDispatchExactProfilesShareOneHandler(t *testing.T) {
if err := request.Encode(&body); err != nil {
t.Fatal(err)
}
admitted, err := r.AdmitUnprofiled(&body, tg.LayerDecodeLimits{})
admitted, err := r.AdmitUnprofiled(&body, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}
@ -257,23 +258,19 @@ func TestLayerDispatchExactProfilesShareOneHandler(t *testing.T) {
if err := result.Encode(&exact); err != nil {
t.Fatal(err)
}
decoded, err := tg.DecodeLayer(profile, tg.LayerConstructorConfigType(), &exact)
decodedObject, err := tlprofile.DecodeObject(profile, &exact, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}
decoded, ok := decodedObject.(*tg.Config)
if !ok {
t.Fatalf("decoded config type = %T", decodedObject)
}
if exact.Len() != 0 || decoded.ThisDC != 2 {
t.Fatalf("decoded config = dc:%d remaining:%d", decoded.ThisDC, exact.Len())
}
frozen, err := result.Freeze()
if err != nil {
t.Fatal(err)
}
prepared, err := admitted.Call().PrepareFrozenResult(frozen)
if err != nil {
t.Fatal(err)
}
var replay bin.Buffer
if err := prepared.Encode(admitted.Call(), &replay); err != nil {
if err := result.Encode(&replay); err != nil {
t.Fatal(err)
}
if string(replay.Raw()) != string(tgBufferBytes(t, result)) {
@ -308,7 +305,7 @@ func TestLayerDispatchUnprofiledInvariantDoesNotPublishRepresentativeLayer(t *te
if err := request.Encode(&body); err != nil {
t.Fatal(err)
}
admitted, err := r.AdmitUnprofiled(&body, tg.LayerDecodeLimits{})
admitted, err := r.AdmitUnprofiled(&body, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}
@ -339,11 +336,11 @@ func TestLayerDispatchUnprofiledInvariantDoesNotPublishRepresentativeLayer(t *te
if err := (&tg.InvokeWithLayerRequest{Layer: 227, Query: request}).Encode(&profiledBody); err != nil {
t.Fatal(err)
}
profiled, err := r.AdmitUnprofiled(&profiledBody, tg.LayerDecodeLimits{})
profiled, err := r.AdmitUnprofiled(&profiledBody, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}
if profile, ok := profiled.ProfileEvidence(); !ok || profile != tg.LayerProfile227 {
if profile, ok := profiled.ProfileEvidence(); !ok || profile != tlprofile.Profile227 {
t.Fatalf("profiled bind evidence = (%d,%v), want layer 227", profile, ok)
}
freezeAndPublishLayer(t, r, rawAuthKeyID, sessionID, 100, 1, 227)
@ -376,14 +373,14 @@ func TestLayerDispatchInheritedDefaultIsEffectiveWithoutBecomingExplicitEvidence
if err := request.Encode(&body); err != nil {
t.Fatal(err)
}
admitted, err := r.AdmitDefaultLayer(tg.LayerProfile225, &body, tg.LayerDecodeLimits{})
admitted, err := r.AdmitDefaultLayer(tlprofile.Profile225, &body, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}
if profile, ok := admitted.EffectiveProfile(); !ok || profile != tg.LayerProfile225 {
if profile, ok := admitted.EffectiveProfile(); !ok || profile != tlprofile.Profile225 {
t.Fatalf("effective profile = (%d,%v), want (225,true)", profile, ok)
}
if profile, ok := admitted.ProfileEvidence(); ok || profile != tg.LayerProfile(0) {
if profile, ok := admitted.ProfileEvidence(); ok || profile != tlprofile.Profile(0) {
t.Fatalf("explicit evidence = (%d,%v), want (0,false)", profile, ok)
}
result, _, err := r.DispatchAdmitted(WithLayer(context.Background(), 227), rawAuthKeyID, sessionID, 0, 0, admitted)
@ -419,8 +416,8 @@ func TestLayerDispatchProfiledBareIgnoresStaleMetadataLayer(t *testing.T) {
ExpiresAt: 3,
EncryptedMessage: []byte("bind"),
}
body := encodeExactLayerRPC(t, tg.LayerProfile225, request)
admitted, err := r.AdmitLayer(tg.LayerProfile225, &body, tg.LayerDecodeLimits{})
body := encodeExactLayerRPC(t, tlprofile.Profile225, request)
admitted, err := r.AdmitLayer(tlprofile.Profile225, &body, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}
@ -461,7 +458,7 @@ func TestLayerDispatchUnprofiledInvariantWithholdsUpdatesReadinessUntilEvidence(
if err := request.Encode(&body); err != nil {
t.Fatal(err)
}
admitted, err := r.AdmitUnprofiled(&body, tg.LayerDecodeLimits{})
admitted, err := r.AdmitUnprofiled(&body, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}
@ -497,7 +494,7 @@ func TestLayerDispatchUnprofiledInvariantWithholdsUpdatesReadinessUntilEvidence(
if err := (&tg.InvokeWithLayerRequest{Layer: 227, Query: request}).Encode(&wrapped); err != nil {
t.Fatal(err)
}
profiled, err := r.AdmitUnprofiled(&wrapped, tg.LayerDecodeLimits{})
profiled, err := r.AdmitUnprofiled(&wrapped, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}
@ -522,14 +519,14 @@ func TestLayerAdmissionPreflightRunsBeforeLargeVectorDecode(t *testing.T) {
users[index] = &tg.InputUserEmpty{}
}
request := &tg.InvokeWithLayerRequest{
Layer: int(tg.LayerProfile225),
Layer: int(tlprofile.Profile225),
Query: &tg.UsersGetUsersRequest{ID: users},
}
var body bin.Buffer
if err := request.Encode(&body); err != nil {
t.Fatal(err)
}
if _, err := r.AdmitUnprofiled(&body, tg.LayerDecodeLimits{}); !tgerr.Is(err, "INPUT_REQUEST_TOO_LONG") {
if _, err := r.AdmitUnprofiled(&body, tlprofile.Limits{}); !tgerr.Is(err, "INPUT_REQUEST_TOO_LONG") {
t.Fatalf("admission err = %v, want INPUT_REQUEST_TOO_LONG", err)
}
}
@ -537,62 +534,62 @@ func TestLayerAdmissionPreflightRunsBeforeLargeVectorDecode(t *testing.T) {
func TestLayerAdmissionFieldPoliciesCoverEveryRoutableProfile(t *testing.T) {
type vectorCase struct {
name string
method tg.LayerSemanticID
method tlprofile.SemanticID
max int
errorCode string
request func(int) bin.Object
}
cases := []vectorCase{
{"users.getUsers", tg.LayerSemanticMethodUsersGetUsers, 100, "INPUT_REQUEST_TOO_LONG", func(n int) bin.Object {
{"users.getUsers", tlprofile.SemanticMethodUsersGetUsers, 100, "INPUT_REQUEST_TOO_LONG", func(n int) bin.Object {
return &tg.UsersGetUsersRequest{ID: repeatLayerPreflightValue[tg.InputUserClass](n, &tg.InputUserEmpty{})}
}},
{"users.getRequirementsToContact", tg.LayerSemanticMethodUsersGetRequirementsToContact, maxRequirementsToContactUsers, "LIMIT_INVALID", func(n int) bin.Object {
{"users.getRequirementsToContact", tlprofile.SemanticMethodUsersGetRequirementsToContact, maxRequirementsToContactUsers, "LIMIT_INVALID", func(n int) bin.Object {
return &tg.UsersGetRequirementsToContactRequest{ID: repeatLayerPreflightValue[tg.InputUserClass](n, &tg.InputUserEmpty{})}
}},
{"contacts.importContacts", tg.LayerSemanticMethodContactsImportContacts, maxContactImportBatch, "LIMIT_INVALID", func(n int) bin.Object {
{"contacts.importContacts", tlprofile.SemanticMethodContactsImportContacts, maxContactImportBatch, "LIMIT_INVALID", func(n int) bin.Object {
return &tg.ContactsImportContactsRequest{Contacts: make([]tg.InputPhoneContact, n)}
}},
{"contacts.deleteContacts", tg.LayerSemanticMethodContactsDeleteContacts, maxContactDeleteBatch, "LIMIT_INVALID", func(n int) bin.Object {
{"contacts.deleteContacts", tlprofile.SemanticMethodContactsDeleteContacts, maxContactDeleteBatch, "LIMIT_INVALID", func(n int) bin.Object {
return &tg.ContactsDeleteContactsRequest{ID: repeatLayerPreflightValue[tg.InputUserClass](n, &tg.InputUserEmpty{})}
}},
{"contacts.editCloseFriends", tg.LayerSemanticMethodContactsEditCloseFriends, maxCloseFriendsCount, "LIMIT_INVALID", func(n int) bin.Object {
{"contacts.editCloseFriends", tlprofile.SemanticMethodContactsEditCloseFriends, maxCloseFriendsCount, "LIMIT_INVALID", func(n int) bin.Object {
return &tg.ContactsEditCloseFriendsRequest{ID: make([]int64, n)}
}},
{"contacts.setBlocked", tg.LayerSemanticMethodContactsSetBlocked, maxContactSetBlocked, "LIMIT_INVALID", func(n int) bin.Object {
{"contacts.setBlocked", tlprofile.SemanticMethodContactsSetBlocked, maxContactSetBlocked, "LIMIT_INVALID", func(n int) bin.Object {
return &tg.ContactsSetBlockedRequest{ID: repeatLayerPreflightValue[tg.InputPeerClass](n, &tg.InputPeerEmpty{})}
}},
{"messages.getMessages", tg.LayerSemanticMethodMessagesGetMessages, maxGetMessagesIDs, "LIMIT_INVALID", func(n int) bin.Object {
{"messages.getMessages", tlprofile.SemanticMethodMessagesGetMessages, maxGetMessagesIDs, "LIMIT_INVALID", func(n int) bin.Object {
return &tg.MessagesGetMessagesRequest{ID: repeatLayerPreflightValue[tg.InputMessageClass](n, &tg.InputMessageID{})}
}},
{"messages.getChats", tg.LayerSemanticMethodMessagesGetChats, maxGetMessagesIDs, "LIMIT_INVALID", func(n int) bin.Object {
{"messages.getChats", tlprofile.SemanticMethodMessagesGetChats, maxGetMessagesIDs, "LIMIT_INVALID", func(n int) bin.Object {
return &tg.MessagesGetChatsRequest{ID: make([]int64, n)}
}},
{"messages.getPeerDialogs", tg.LayerSemanticMethodMessagesGetPeerDialogs, maxDialogInputPeers, "LIMIT_INVALID", func(n int) bin.Object {
{"messages.getPeerDialogs", tlprofile.SemanticMethodMessagesGetPeerDialogs, maxDialogInputPeers, "LIMIT_INVALID", func(n int) bin.Object {
return &tg.MessagesGetPeerDialogsRequest{Peers: repeatLayerPreflightValue[tg.InputDialogPeerClass](n, &tg.InputDialogPeer{Peer: &tg.InputPeerEmpty{}})}
}},
{"messages.readMessageContents", tg.LayerSemanticMethodMessagesReadMessageContents, maxGetMessagesIDs, "LIMIT_INVALID", func(n int) bin.Object {
{"messages.readMessageContents", tlprofile.SemanticMethodMessagesReadMessageContents, maxGetMessagesIDs, "LIMIT_INVALID", func(n int) bin.Object {
return &tg.MessagesReadMessageContentsRequest{ID: make([]int, n)}
}},
{"messages.getCustomEmojiDocuments", tg.LayerSemanticMethodMessagesGetCustomEmojiDocuments, maxEmojiDocuments, "LIMIT_INVALID", func(n int) bin.Object {
{"messages.getCustomEmojiDocuments", tlprofile.SemanticMethodMessagesGetCustomEmojiDocuments, maxEmojiDocuments, "LIMIT_INVALID", func(n int) bin.Object {
return &tg.MessagesGetCustomEmojiDocumentsRequest{DocumentID: make([]int64, n)}
}},
{"messages.deleteMessages", tg.LayerSemanticMethodMessagesDeleteMessages, domain.MaxDeleteMessageIDs, "LIMIT_INVALID", func(n int) bin.Object {
{"messages.deleteMessages", tlprofile.SemanticMethodMessagesDeleteMessages, domain.MaxDeleteMessageIDs, "LIMIT_INVALID", func(n int) bin.Object {
return &tg.MessagesDeleteMessagesRequest{ID: make([]int, n)}
}},
{"messages.createChat", tg.LayerSemanticMethodMessagesCreateChat, 200, "LIMIT_INVALID", func(n int) bin.Object {
{"messages.createChat", tlprofile.SemanticMethodMessagesCreateChat, 200, "LIMIT_INVALID", func(n int) bin.Object {
return &tg.MessagesCreateChatRequest{Users: repeatLayerPreflightValue[tg.InputUserClass](n, &tg.InputUserEmpty{}), Title: "layer-policy"}
}},
{"channels.getChannels", tg.LayerSemanticMethodChannelsGetChannels, maxGetMessagesIDs, "LIMIT_INVALID", func(n int) bin.Object {
{"channels.getChannels", tlprofile.SemanticMethodChannelsGetChannels, maxGetMessagesIDs, "LIMIT_INVALID", func(n int) bin.Object {
return &tg.ChannelsGetChannelsRequest{ID: repeatLayerPreflightValue[tg.InputChannelClass](n, &tg.InputChannelEmpty{})}
}},
}
r := New(Config{DC: 2, IP: "127.0.0.1", Port: 2398}, Deps{}, zaptest.NewLogger(t), clock.System)
limits := tg.LayerDecodeLimits{MaxVectorElements: 8 << 10}
for profile := tg.LayerProfile225; profile <= tg.LayerProfile227; profile++ {
limits := tlprofile.Limits{MaxVectorElements: 8 << 10}
for profile := tlprofile.Profile225; profile <= tlprofile.Profile227; profile++ {
for _, tc := range cases {
tc := tc
if _, available := tg.LayerWireID(profile, tc.method); !available {
if _, available := tlprofile.WireID(profile, tc.method); !available {
continue
}
t.Run(fmt.Sprintf("layer_%d/%s/at_cap", profile, tc.name), func(t *testing.T) {
@ -620,15 +617,15 @@ func TestLayerAdmissionFieldPoliciesCoverEveryRoutableProfile(t *testing.T) {
func TestLayerAdmissionUploadFieldsCoverEveryProfile(t *testing.T) {
r := New(Config{DC: 2, IP: "127.0.0.1", Port: 2398}, Deps{}, zaptest.NewLogger(t), clock.System)
for profile := tg.LayerProfile225; profile <= tg.LayerProfile227; profile++ {
for profile := tlprofile.Profile225; profile <= tlprofile.Profile227; profile++ {
t.Run(fmt.Sprintf("layer_%d/saveFilePart", profile), func(t *testing.T) {
atCap := encodeExactLayerRPC(t, profile, &tg.UploadSaveFilePartRequest{Bytes: make([]byte, appfiles.MaxUploadPartBytes)})
if _, err := r.AdmitLayer(profile, &atCap, tg.LayerDecodeLimits{}); err != nil {
if _, err := r.AdmitLayer(profile, &atCap, tlprofile.Limits{}); err != nil {
t.Fatal(err)
}
over := encodeExactLayerRPC(t, profile, &tg.UploadSaveFilePartRequest{Bytes: make([]byte, appfiles.MaxUploadPartBytes+1)})
original := over.Copy()
if _, err := r.AdmitLayer(profile, &over, tg.LayerDecodeLimits{}); !tgerr.Is(err, "FILE_PART_TOO_BIG") {
if _, err := r.AdmitLayer(profile, &over, tlprofile.Limits{}); !tgerr.Is(err, "FILE_PART_TOO_BIG") {
t.Fatalf("oversized part err = %v", err)
}
if string(over.Raw()) != string(original) {
@ -637,13 +634,13 @@ func TestLayerAdmissionUploadFieldsCoverEveryProfile(t *testing.T) {
})
t.Run(fmt.Sprintf("layer_%d/saveBigFilePart", profile), func(t *testing.T) {
atCap := encodeExactLayerRPC(t, profile, &tg.UploadSaveBigFilePartRequest{FileTotalParts: appfiles.MaxUploadParts, Bytes: make([]byte, appfiles.MaxUploadPartBytes)})
if _, err := r.AdmitLayer(profile, &atCap, tg.LayerDecodeLimits{}); err != nil {
if _, err := r.AdmitLayer(profile, &atCap, tlprofile.Limits{}); err != nil {
t.Fatal(err)
}
for _, totalParts := range []int{0, -1, appfiles.MaxUploadParts + 1} {
body := encodeExactLayerRPC(t, profile, &tg.UploadSaveBigFilePartRequest{FileTotalParts: totalParts})
original := body.Copy()
if _, err := r.AdmitLayer(profile, &body, tg.LayerDecodeLimits{}); !tgerr.Is(err, "FILE_PART_INVALID") {
if _, err := r.AdmitLayer(profile, &body, tlprofile.Limits{}); !tgerr.Is(err, "FILE_PART_INVALID") {
t.Fatalf("total parts %d err = %v", totalParts, err)
}
if string(body.Raw()) != string(original) {
@ -651,7 +648,7 @@ func TestLayerAdmissionUploadFieldsCoverEveryProfile(t *testing.T) {
}
}
over := encodeExactLayerRPC(t, profile, &tg.UploadSaveBigFilePartRequest{FileTotalParts: 1, Bytes: make([]byte, appfiles.MaxUploadPartBytes+1)})
if _, err := r.AdmitLayer(profile, &over, tg.LayerDecodeLimits{}); !tgerr.Is(err, "FILE_PART_TOO_BIG") {
if _, err := r.AdmitLayer(profile, &over, tlprofile.Limits{}); !tgerr.Is(err, "FILE_PART_TOO_BIG") {
t.Fatalf("oversized big part err = %v", err)
}
})
@ -666,14 +663,10 @@ func repeatLayerPreflightValue[T any](n int, value T) []T {
return result
}
func encodeExactLayerRPC(t *testing.T, profile tg.LayerProfile, request bin.Object) bin.Buffer {
func encodeExactLayerRPC(t *testing.T, profile tlprofile.Profile, request bin.Object) bin.Buffer {
t.Helper()
outbound, err := tg.PrepareLayerOutboundCall(profile, request)
if err != nil {
t.Fatal(err)
}
var body bin.Buffer
if err := outbound.Encode(&body); err != nil {
if err := tlprofile.EncodeObject(profile, request, &body); err != nil {
t.Fatal(err)
}
return body
@ -682,7 +675,7 @@ func encodeExactLayerRPC(t *testing.T, profile tg.LayerProfile, request bin.Obje
func TestLayerDispatchRejectsUnsupportedWrapperBeforeHandler(t *testing.T) {
r := New(Config{DC: 2, IP: "127.0.0.1", Port: 2398}, Deps{}, zaptest.NewLogger(t), clock.System)
request := &tg.InvokeWithLayerRequest{
Layer: int(tg.LayerProfile227),
Layer: int(tlprofile.Profile227),
Query: &tg.InvokeWithTakeoutRequest{
TakeoutID: 1,
Query: &tg.HelpGetConfigRequest{},
@ -692,7 +685,7 @@ func TestLayerDispatchRejectsUnsupportedWrapperBeforeHandler(t *testing.T) {
if err := request.Encode(&body); err != nil {
t.Fatal(err)
}
admitted, err := r.AdmitUnprofiled(&body, tg.LayerDecodeLimits{})
admitted, err := r.AdmitUnprofiled(&body, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}
@ -715,7 +708,7 @@ func TestPrepareAdmittedReplayRestoresReadinessOnlyAfterDelivery(t *testing.T) {
clock.System,
)
request := &tg.InvokeWithLayerRequest{
Layer: int(tg.LayerProfile225),
Layer: int(tlprofile.Profile225),
Query: &tg.InitConnectionRequest{
APIID: 123, DeviceModel: "Desktop", SystemVersion: "Windows", AppVersion: "test",
SystemLangCode: "en", LangPack: "tdesktop", LangCode: "en",
@ -726,7 +719,7 @@ func TestPrepareAdmittedReplayRestoresReadinessOnlyAfterDelivery(t *testing.T) {
if err := request.Encode(&body); err != nil {
t.Fatal(err)
}
admitted, err := r.AdmitUnprofiled(&body, tg.LayerDecodeLimits{})
admitted, err := r.AdmitUnprofiled(&body, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}
@ -774,7 +767,7 @@ func TestRequestBoundOnlyLayerRPCDoesNotPublishMetadataOrReadiness(t *testing.T)
clock.System,
)
request := &tg.InvokeWithLayerRequest{
Layer: int(tg.LayerProfile225),
Layer: int(tlprofile.Profile225),
Query: &tg.InitConnectionRequest{
APIID: 123, DeviceModel: "Stale Desktop", SystemVersion: "Windows", AppVersion: "stale",
SystemLangCode: "en", LangPack: "tdesktop", LangCode: "en",
@ -785,7 +778,7 @@ func TestRequestBoundOnlyLayerRPCDoesNotPublishMetadataOrReadiness(t *testing.T)
if err := request.Encode(&body); err != nil {
t.Fatal(err)
}
admitted, err := r.AdmitUnprofiled(&body, tg.LayerDecodeLimits{})
admitted, err := r.AdmitUnprofiled(&body, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}
@ -830,7 +823,7 @@ func TestPrepareAdmittedReplayDoesNotRollBackNewerExplicitLayerOrClientInfo(t *t
clock.System,
)
oldRequest := &tg.InvokeWithLayerRequest{
Layer: int(tg.LayerProfile225),
Layer: int(tlprofile.Profile225),
Query: &tg.InitConnectionRequest{
APIID: 123, DeviceModel: "Old Desktop", SystemVersion: "Windows 10", AppVersion: "old",
SystemLangCode: "en", LangPack: "tdesktop", LangCode: "en",
@ -841,7 +834,7 @@ func TestPrepareAdmittedReplayDoesNotRollBackNewerExplicitLayerOrClientInfo(t *t
if err := oldRequest.Encode(&body); err != nil {
t.Fatal(err)
}
admitted, err := r.AdmitUnprofiled(&body, tg.LayerDecodeLimits{})
admitted, err := r.AdmitUnprofiled(&body, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}
@ -886,7 +879,7 @@ func TestDelayedExplicitDispatchCannotRollBackAdmissionTimeLayerOrInitMetadata(t
zaptest.NewLogger(t),
clock.System,
)
admit := func(layer int, device, version string) tg.LayerRequest {
admit := func(layer int, device, version string) tlprofile.Admission {
t.Helper()
request := &tg.InvokeWithLayerRequest{
Layer: layer,
@ -900,7 +893,7 @@ func TestDelayedExplicitDispatchCannotRollBackAdmissionTimeLayerOrInitMetadata(t
if err := request.Encode(&body); err != nil {
t.Fatal(err)
}
admitted, err := r.AdmitUnprofiled(&body, tg.LayerDecodeLimits{})
admitted, err := r.AdmitUnprofiled(&body, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}
@ -940,7 +933,7 @@ func TestSameLayerNakedInitConnectionUsesMessageIDWatermark(t *testing.T) {
zaptest.NewLogger(t),
clock.System,
)
admit := func(device, version string) tg.LayerRequest {
admit := func(device, version string) tlprofile.Admission {
t.Helper()
request := &tg.InitConnectionRequest{
APIID: 123, DeviceModel: device, SystemVersion: "Windows", AppVersion: version,
@ -951,7 +944,7 @@ func TestSameLayerNakedInitConnectionUsesMessageIDWatermark(t *testing.T) {
if err := request.Encode(&body); err != nil {
t.Fatal(err)
}
admitted, err := r.AdmitDefaultLayer(tg.LayerProfile227, &body, tg.LayerDecodeLimits{})
admitted, err := r.AdmitDefaultLayer(tlprofile.Profile227, &body, tlprofile.Limits{})
if err != nil {
t.Fatal(err)
}

View file

@ -7,9 +7,9 @@ import (
"sort"
"time"
"github.com/iamxvbaba/td/tg"
"go.uber.org/zap"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/domain"
"telesrv/internal/store"
)
@ -609,6 +609,6 @@ func isSupportedLayer(layer int) bool {
if layer <= 0 {
return false
}
profile, ok := tg.ResolveLayerProfile(layer)
profile, ok := tlprofile.ResolveProfile(layer)
return ok && int(profile) == layer
}

View file

@ -107,13 +107,12 @@ func TestMessagesCreateChatCreatesMegagroupAndDialogsRPC(t *testing.T) {
if err != nil {
t.Fatalf("dispatch get dialogs: %v", err)
}
box, ok := enc.(*tg.MessagesDialogsBox)
dialogs, ok := enc.(*tg.MessagesDialogs)
if !ok {
t.Fatalf("dialogs response = %T, want box", enc)
t.Fatalf("dialogs response = %T, want *tg.MessagesDialogs", enc)
}
dialogs, ok := box.Dialogs.(*tg.MessagesDialogs)
if !ok || len(dialogs.Dialogs) != 1 || len(dialogs.Chats) != 1 || len(dialogs.Messages) != 1 {
t.Fatalf("dialogs = %T %+v, want channel dialog/chat/message", box.Dialogs, box.Dialogs)
if len(dialogs.Dialogs) != 1 || len(dialogs.Chats) != 1 || len(dialogs.Messages) != 1 {
t.Fatalf("dialogs = %+v, want channel dialog/chat/message", dialogs)
}
dialog := dialogs.Dialogs[0].(*tg.Dialog)
if peer, ok := dialog.Peer.(*tg.PeerChannel); !ok || peer.ChannelID != channel.ID {
@ -227,12 +226,11 @@ func TestMessagesCreateChatTDesktopReturnsLegacyChatAndAcceptsInputPeerChatRPC(t
if err != nil {
t.Fatalf("legacy history: %v", err)
}
legacyBox, ok := legacyHistory.(*tg.MessagesMessagesBox)
legacyMessages, ok := legacyHistory.(*tg.MessagesMessages)
if !ok {
t.Fatalf("legacy history = %T %+v, want messages box", legacyHistory, legacyHistory)
t.Fatalf("legacy history = %T %+v, want *tg.MessagesMessages", legacyHistory, legacyHistory)
}
legacyMessages, ok := legacyBox.Messages.(*tg.MessagesMessages)
if !ok || len(legacyMessages.Messages) != 0 {
if len(legacyMessages.Messages) != 0 {
t.Fatalf("legacy history = %T %+v, want empty messages.messages", legacyHistory, legacyHistory)
}

View file

@ -73,13 +73,12 @@ func TestMessagesEditMessageReturnsUpdateAndRecordsOwnerContext(t *testing.T) {
if err != nil {
t.Fatalf("dispatch: %v", err)
}
box, ok := enc.(*tg.UpdatesBox)
got, ok := enc.(*tg.Updates)
if !ok {
t.Fatalf("response = %T, want *tg.UpdatesBox", enc)
t.Fatalf("response = %T, want *tg.Updates", enc)
}
got, ok := box.Updates.(*tg.Updates)
if !ok || len(got.Updates) != 1 {
t.Fatalf("boxed updates = %T %+v, want one update", box.Updates, box.Updates)
if len(got.Updates) != 1 {
t.Fatalf("updates = %+v, want one update", got)
}
edit, ok := got.Updates[0].(*tg.UpdateEditMessage)
if !ok || edit.Pts != 7 || edit.PtsCount != 1 {

View file

@ -60,11 +60,10 @@ func TestMessagesForwardMessagesRecordsRequestAndReturnsUpdates(t *testing.T) {
if messages.sendReq.ReplyTo == nil || messages.sendReq.ReplyTo.MessageID != 9 || messages.sendReq.ReplyTo.Peer.ID != toID || messages.sendReq.ReplyTo.QuoteText != "target" {
t.Fatalf("forward reply = %+v, want target peer reply metadata", messages.sendReq.ReplyTo)
}
box, ok := enc.(*tg.UpdatesBox)
got, ok := enc.(*tg.Updates)
if !ok {
t.Fatalf("response = %T, want *tg.UpdatesBox", enc)
t.Fatalf("response = %T, want *tg.Updates", enc)
}
got := box.Updates.(*tg.Updates)
if len(got.Updates) != 4 {
t.Fatalf("updates = %+v, want two message ids and two new messages", got.Updates)
}

View file

@ -130,9 +130,6 @@ func TestMessagesSearchChannelPeerReturnsSingleCopyMessages(t *testing.T) {
if err != nil {
t.Fatalf("dispatch shared media count search: %v", err)
}
if box, ok := enc.(*tg.MessagesMessagesBox); ok {
enc = box.Messages
}
channelMessages, ok := enc.(*tg.MessagesChannelMessages)
if !ok {
t.Fatalf("shared media count search result = %T, want messages.channelMessages", enc)
@ -210,9 +207,6 @@ func TestMessagesSearchChatPhotosDoesNotReturnOrdinaryChannelHistory(t *testing.
if err != nil {
t.Fatalf("dispatch chat photos search: %v", err)
}
if box, ok := enc.(*tg.MessagesMessagesBox); ok {
enc = box.Messages
}
got, ok := enc.(*tg.MessagesChannelMessages)
if !ok {
t.Fatalf("chat photos search result = %T, want messages.channelMessages", enc)
@ -315,9 +309,6 @@ func TestMessagesGetSearchCountersUsesMediaCategoryCounts(t *testing.T) {
if err != nil {
t.Fatalf("dispatch shared media count search: %v", err)
}
if box, ok := enc.(*tg.MessagesMessagesBox); ok {
enc = box.Messages
}
channelMessages, ok := enc.(*tg.MessagesChannelMessages)
if !ok {
t.Fatalf("shared media count search result = %T, want messages.channelMessages", enc)
@ -498,13 +489,9 @@ func TestMessagesGetHistoryReturnsStoredMessages(t *testing.T) {
if err != nil {
t.Fatalf("dispatch: %v", err)
}
box, ok := enc.(*tg.MessagesMessagesBox)
got, ok := enc.(*tg.MessagesMessages)
if !ok {
t.Fatalf("response = %T, want *tg.MessagesMessagesBox", enc)
}
got, ok := box.Messages.(*tg.MessagesMessages)
if !ok {
t.Fatalf("boxed response = %T, want *tg.MessagesMessages", box.Messages)
t.Fatalf("response = %T, want *tg.MessagesMessages", enc)
}
if len(got.Messages) != 1 || len(got.Users) != 1 {
t.Fatalf("history = %+v, want one message and one user", got)

View file

@ -73,13 +73,9 @@ func TestMonoforumSavedDialogsAndHistory(t *testing.T) {
if err != nil {
t.Fatalf("dispatch getHistory(monoforum): %v", err)
}
mainBox, ok := mainEnc.(*tg.MessagesMessagesBox)
mainHistory, ok := mainEnc.(*tg.MessagesChannelMessages)
if !ok {
t.Fatalf("getHistory(monoforum) = %T, want MessagesMessagesBox", mainEnc)
}
mainHistory, ok := mainBox.Messages.(*tg.MessagesChannelMessages)
if !ok {
t.Fatalf("getHistory(monoforum) boxed = %T, want MessagesChannelMessages", mainBox.Messages)
t.Fatalf("getHistory(monoforum) = %T, want *tg.MessagesChannelMessages", mainEnc)
}
if len(mainHistory.Messages) != 1 {
t.Fatalf("main monoforum history = %d msgs, want only the creation service", len(mainHistory.Messages))

View file

@ -126,8 +126,8 @@ func TestMessagesReceivedMessagesIsRegistered(t *testing.T) {
if method != "messages.receivedMessages" {
t.Fatalf("method = %q, want messages.receivedMessages", method)
}
vector, ok := result.(*tg.ReceivedNotifyMessageVector)
if !ok || vector == nil || vector.Elems == nil || len(vector.Elems) != 0 {
vector, ok := dispatchCanonicalValue(result).([]tg.ReceivedNotifyMessage)
if !ok || vector == nil || len(vector) != 0 {
t.Fatalf("result = %#v (%T), want non-nil empty ReceivedNotifyMessageVector", result, result)
}
}

View file

@ -3,142 +3,394 @@ package rpc
import (
"context"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/compat/tdesktop"
"telesrv/internal/domain"
"unicode/utf8"
)
// registerMessages 注册 messages.* RPC handler。
func (r *Router) registerMessages(d *tg.ServerDispatcher) {
d.OnMessagesReceivedMessages(r.onMessagesReceivedMessages)
d.OnMessagesSetTyping(r.onMessagesSetTyping)
d.OnMessagesSaveDraft(r.onMessagesSaveDraft)
d.OnMessagesSaveDefaultSendAs(r.onMessagesSaveDefaultSendAs)
d.OnMessagesGetAllDrafts(r.onMessagesGetAllDrafts)
d.OnMessagesClearAllDrafts(r.onMessagesClearAllDrafts)
d.OnMessagesGetAllStickers(r.onMessagesGetAllStickers)
d.OnMessagesGetEmojiStickers(r.onMessagesGetEmojiStickers)
d.OnMessagesGetMaskStickers(r.onMessagesGetMaskStickers)
d.OnMessagesGetFeaturedStickers(r.onMessagesGetFeaturedStickers)
d.OnMessagesGetFeaturedEmojiStickers(r.onMessagesGetFeaturedEmojiStickers)
d.OnMessagesGetOldFeaturedStickers(r.onMessagesGetOldFeaturedStickers)
d.OnMessagesGetRecentStickers(r.onMessagesGetRecentStickers)
d.OnMessagesGetFavedStickers(r.onMessagesGetFavedStickers)
d.OnMessagesGetSavedGifs(r.onMessagesGetSavedGifs)
d.OnMessagesFaveSticker(r.onMessagesFaveSticker)
d.OnMessagesSaveRecentSticker(r.onMessagesSaveRecentSticker)
d.OnMessagesSaveGif(r.onMessagesSaveGif)
d.OnMessagesClearRecentStickers(r.onMessagesClearRecentStickers)
d.OnMessagesSendMessage(r.onMessagesSendMessage)
d.OnMessagesForwardMessages(r.onMessagesForwardMessages)
d.OnMessagesGetDialogFilters(r.onMessagesGetDialogFilters)
d.OnMessagesGetSuggestedDialogFilters(func(ctx context.Context) ([]tg.DialogFilterSuggested, error) {
func (r *Router) registerMessages(d *tlprofile.Dispatcher) {
registerRPC[*tg.MessagesReceivedMessagesRequest](d, tlprofile.SemanticMethodMessagesReceivedMessages, func(ctx context.Context, layerRequest *tg.MessagesReceivedMessagesRequest) (any, error) {
return r.onMessagesReceivedMessages(ctx, layerRequest.
MaxID)
})
registerRPC[*tg.MessagesSetTypingRequest](d, tlprofile.SemanticMethodMessagesSetTyping, func(ctx context.Context, layerRequest *tg.MessagesSetTypingRequest) (any, error) {
return r.onMessagesSetTyping(ctx, layerRequest)
})
registerRPC[*tg.MessagesSaveDraftRequest](d, tlprofile.SemanticMethodMessagesSaveDraft, func(ctx context.Context, layerRequest *tg.MessagesSaveDraftRequest) (any, error) {
return r.onMessagesSaveDraft(ctx, layerRequest)
})
registerRPC[*tg.MessagesSaveDefaultSendAsRequest](d, tlprofile.SemanticMethodMessagesSaveDefaultSendAs, func(ctx context.Context, layerRequest *tg.MessagesSaveDefaultSendAsRequest) (any, error) {
return r.onMessagesSaveDefaultSendAs(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetAllDraftsRequest](d, tlprofile.SemanticMethodMessagesGetAllDrafts, func(ctx context.Context, layerRequest *tg.MessagesGetAllDraftsRequest) (any, error) {
return r.onMessagesGetAllDrafts(ctx)
})
registerRPC[*tg.MessagesClearAllDraftsRequest](d, tlprofile.SemanticMethodMessagesClearAllDrafts, func(ctx context.Context, layerRequest *tg.MessagesClearAllDraftsRequest) (any, error) {
return r.onMessagesClearAllDrafts(ctx)
})
registerRPC[*tg.MessagesGetAllStickersRequest](d, tlprofile.SemanticMethodMessagesGetAllStickers, func(ctx context.Context, layerRequest *tg.MessagesGetAllStickersRequest) (any, error) {
return r.onMessagesGetAllStickers(ctx, layerRequest.
Hash)
})
registerRPC[*tg.MessagesGetEmojiStickersRequest](d, tlprofile.SemanticMethodMessagesGetEmojiStickers, func(ctx context.Context, layerRequest *tg.MessagesGetEmojiStickersRequest) (any, error) {
return r.onMessagesGetEmojiStickers(ctx, layerRequest.
Hash)
})
registerRPC[*tg.MessagesGetMaskStickersRequest](d, tlprofile.SemanticMethodMessagesGetMaskStickers, func(ctx context.Context, layerRequest *tg.MessagesGetMaskStickersRequest) (any, error) {
return r.onMessagesGetMaskStickers(ctx, layerRequest.
Hash)
})
registerRPC[*tg.MessagesGetFeaturedStickersRequest](d, tlprofile.SemanticMethodMessagesGetFeaturedStickers, func(ctx context.Context, layerRequest *tg.MessagesGetFeaturedStickersRequest) (any, error) {
return r.onMessagesGetFeaturedStickers(ctx, layerRequest.
Hash)
})
registerRPC[*tg.MessagesGetFeaturedEmojiStickersRequest](d, tlprofile.SemanticMethodMessagesGetFeaturedEmojiStickers, func(ctx context.Context, layerRequest *tg.MessagesGetFeaturedEmojiStickersRequest) (any, error) {
return r.onMessagesGetFeaturedEmojiStickers(ctx, layerRequest.
Hash)
})
registerRPC[*tg.MessagesGetOldFeaturedStickersRequest](d, tlprofile.SemanticMethodMessagesGetOldFeaturedStickers, func(ctx context.Context, layerRequest *tg.MessagesGetOldFeaturedStickersRequest) (any, error) {
return r.onMessagesGetOldFeaturedStickers(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetRecentStickersRequest](d, tlprofile.SemanticMethodMessagesGetRecentStickers, func(ctx context.Context, layerRequest *tg.MessagesGetRecentStickersRequest) (any, error) {
return r.onMessagesGetRecentStickers(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetFavedStickersRequest](d, tlprofile.SemanticMethodMessagesGetFavedStickers, func(ctx context.Context, layerRequest *tg.MessagesGetFavedStickersRequest) (any, error) {
return r.onMessagesGetFavedStickers(ctx, layerRequest.
Hash)
})
registerRPC[*tg.MessagesGetSavedGifsRequest](d, tlprofile.SemanticMethodMessagesGetSavedGifs, func(ctx context.Context, layerRequest *tg.MessagesGetSavedGifsRequest) (any, error) {
return r.onMessagesGetSavedGifs(ctx, layerRequest.
Hash)
})
registerRPC[*tg.MessagesFaveStickerRequest](d, tlprofile.SemanticMethodMessagesFaveSticker, func(ctx context.Context, layerRequest *tg.MessagesFaveStickerRequest) (any, error) {
return r.onMessagesFaveSticker(ctx, layerRequest)
})
registerRPC[*tg.MessagesSaveRecentStickerRequest](d, tlprofile.SemanticMethodMessagesSaveRecentSticker, func(ctx context.Context, layerRequest *tg.MessagesSaveRecentStickerRequest) (any, error) {
return r.onMessagesSaveRecentSticker(ctx, layerRequest)
})
registerRPC[*tg.MessagesSaveGifRequest](d, tlprofile.SemanticMethodMessagesSaveGif, func(ctx context.Context, layerRequest *tg.MessagesSaveGifRequest) (any, error) {
return r.onMessagesSaveGif(ctx, layerRequest)
})
registerRPC[*tg.MessagesClearRecentStickersRequest](d, tlprofile.SemanticMethodMessagesClearRecentStickers, func(ctx context.Context, layerRequest *tg.MessagesClearRecentStickersRequest) (any, error) {
return r.onMessagesClearRecentStickers(ctx, layerRequest)
})
registerRPC[*tg.MessagesSendMessageRequest](d, tlprofile.SemanticMethodMessagesSendMessage, func(ctx context.Context, layerRequest *tg.MessagesSendMessageRequest) (any, error) {
return r.onMessagesSendMessage(ctx, layerRequest)
})
registerRPC[*tg.MessagesForwardMessagesRequest](d, tlprofile.SemanticMethodMessagesForwardMessages, func(ctx context.Context, layerRequest *tg.MessagesForwardMessagesRequest) (any, error) {
return r.onMessagesForwardMessages(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetDialogFiltersRequest](d, tlprofile.SemanticMethodMessagesGetDialogFilters, func(ctx context.Context, layerRequest *tg.MessagesGetDialogFiltersRequest) (any, error) {
return r.onMessagesGetDialogFilters(ctx)
})
registerRPC[*tg.MessagesGetSuggestedDialogFiltersRequest](d, tlprofile.SemanticMethodMessagesGetSuggestedDialogFilters, func(ctx context.Context, layerRequest *tg.MessagesGetSuggestedDialogFiltersRequest) (any, error) {
return tdesktop.SuggestedDialogFilters(), nil
})
d.OnMessagesUpdateDialogFilter(r.onMessagesUpdateDialogFilter)
d.OnMessagesUpdateDialogFiltersOrder(r.onMessagesUpdateDialogFiltersOrder)
d.OnMessagesToggleDialogFilterTags(r.onMessagesToggleDialogFilterTags)
d.OnMessagesGetSavedDialogs(r.onMessagesGetSavedDialogs)
d.OnMessagesGetPinnedSavedDialogs(func(ctx context.Context) (tg.MessagesSavedDialogsClass, error) {
registerRPC[*tg.MessagesUpdateDialogFilterRequest](d, tlprofile.SemanticMethodMessagesUpdateDialogFilter, func(ctx context.Context, layerRequest *tg.MessagesUpdateDialogFilterRequest) (any, error) {
return r.onMessagesUpdateDialogFilter(ctx, layerRequest)
})
registerRPC[*tg.MessagesUpdateDialogFiltersOrderRequest](d, tlprofile.SemanticMethodMessagesUpdateDialogFiltersOrder, func(ctx context.Context, layerRequest *tg.MessagesUpdateDialogFiltersOrderRequest) (any, error) {
return r.onMessagesUpdateDialogFiltersOrder(ctx, layerRequest.
Order)
})
registerRPC[*tg.MessagesToggleDialogFilterTagsRequest](d, tlprofile.SemanticMethodMessagesToggleDialogFilterTags, func(ctx context.Context, layerRequest *tg.MessagesToggleDialogFilterTagsRequest) (any, error) {
return r.onMessagesToggleDialogFilterTags(ctx, layerRequest.
Enabled)
})
registerRPC[*tg.MessagesGetSavedDialogsRequest](d, tlprofile.SemanticMethodMessagesGetSavedDialogs, func(ctx context.Context, layerRequest *tg.MessagesGetSavedDialogsRequest) (any, error) {
return r.onMessagesGetSavedDialogs(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetPinnedSavedDialogsRequest](d, tlprofile.SemanticMethodMessagesGetPinnedSavedDialogs, func(ctx context.Context, layerRequest *tg.MessagesGetPinnedSavedDialogsRequest) (any, error) {
return r.onMessagesGetPinnedSavedDialogs(ctx)
})
d.OnMessagesToggleSavedDialogPin(r.onMessagesToggleSavedDialogPin)
d.OnMessagesReorderPinnedSavedDialogs(r.onMessagesReorderPinnedSavedDialogs)
d.OnMessagesGetSavedDialogsByID(r.onMessagesGetSavedDialogsByID)
d.OnMessagesGetSavedHistory(r.onMessagesGetSavedHistory)
d.OnMessagesReadSavedHistory(r.onMessagesReadSavedHistory)
d.OnMessagesDeleteSavedHistory(r.onMessagesDeleteSavedHistory)
d.OnMessagesGetCommonChats(r.onMessagesGetCommonChats)
d.OnMessagesGetDefaultHistoryTTL(r.onMessagesGetDefaultHistoryTTL)
d.OnMessagesSetHistoryTTL(r.onMessagesSetHistoryTTL)
d.OnMessagesSetDefaultHistoryTTL(r.onMessagesSetDefaultHistoryTTL)
d.OnMessagesGetSponsoredMessages(r.onMessagesGetSponsoredMessages)
d.OnMessagesGetWebPagePreview(r.onMessagesGetWebPagePreview)
d.OnMessagesRequestWebView(r.onMessagesRequestWebView)
d.OnMessagesProlongWebView(r.onMessagesProlongWebView)
d.OnMessagesSendWebViewResultMessage(r.onMessagesSendWebViewResultMessage)
d.OnMessagesRequestSimpleWebView(r.onMessagesRequestSimpleWebView)
d.OnMessagesGetBotApp(r.onMessagesGetBotApp)
d.OnMessagesRequestAppWebView(r.onMessagesRequestAppWebView)
d.OnMessagesRequestMainWebView(r.onMessagesRequestMainWebView)
d.OnMessagesSendWebViewData(r.onMessagesSendWebViewData)
d.OnMessagesSendBotRequestedPeer(r.onMessagesSendBotRequestedPeer)
d.OnMessagesGetPreparedInlineMessage(r.onMessagesGetPreparedInlineMessage)
d.OnMessagesGetEmojiGameInfo(r.onMessagesGetEmojiGameInfo)
d.OnMessagesGetGameHighScores(r.onMessagesGetGameHighScores)
d.OnMessagesGetInlineGameHighScores(r.onMessagesGetInlineGameHighScores)
d.OnMessagesSetGameScore(r.onMessagesSetGameScore)
d.OnMessagesSetInlineGameScore(r.onMessagesSetInlineGameScore)
d.OnMessagesUploadMedia(r.onMessagesUploadMedia)
d.OnMessagesSendMedia(r.onMessagesSendMedia)
d.OnMessagesSendMultiMedia(r.onMessagesSendMultiMedia)
d.OnMessagesReportSpam(r.onMessagesReportSpam)
d.OnMessagesReport(r.onMessagesReport)
d.OnMessagesReportReaction(r.onMessagesReportReaction)
d.OnMessagesReportMessagesDelivery(r.onMessagesReportMessagesDelivery)
d.OnMessagesReportReadMetrics(r.onMessagesReportReadMetrics)
d.OnMessagesReportMusicListen(r.onMessagesReportMusicListen)
d.OnMessagesReportSponsoredMessage(r.onMessagesReportSponsoredMessage)
d.OnMessagesReadMessageContents(r.onMessagesReadMessageContents)
d.OnMessagesTranslateText(r.onMessagesTranslateText)
d.OnMessagesTogglePeerTranslations(r.onMessagesTogglePeerTranslations)
d.OnMessagesGetMessagesViews(r.onMessagesGetMessagesViews)
d.OnMessagesGetUnreadMentions(r.onMessagesGetUnreadMentions)
d.OnMessagesReadMentions(r.onMessagesReadMentions)
d.OnMessagesGetSearchCounters(r.onMessagesGetSearchCounters)
d.OnMessagesGetReplies(r.onMessagesGetReplies)
d.OnMessagesGetDiscussionMessage(r.onMessagesGetDiscussionMessage)
d.OnMessagesReadDiscussion(r.onMessagesReadDiscussion)
d.OnMessagesGetForumTopics(r.onMessagesGetForumTopics)
d.OnMessagesGetForumTopicsByID(r.onMessagesGetForumTopicsByID)
d.OnMessagesGetOnlines(r.onMessagesGetOnlines)
d.OnMessagesGetAvailableReactions(r.onMessagesGetAvailableReactions)
d.OnMessagesGetAvailableEffects(r.onMessagesGetAvailableEffects)
d.OnMessagesGetStickers(r.onMessagesGetStickers)
d.OnMessagesInstallStickerSet(r.onMessagesInstallStickerSet)
d.OnMessagesUninstallStickerSet(r.onMessagesUninstallStickerSet)
d.OnMessagesReorderStickerSets(r.onMessagesReorderStickerSets)
d.OnMessagesToggleStickerSets(r.onMessagesToggleStickerSets)
d.OnMessagesGetMyStickers(r.onMessagesGetMyStickers)
d.OnMessagesGetArchivedStickers(func(ctx context.Context, req *tg.MessagesGetArchivedStickersRequest) (*tg.MessagesArchivedStickers, error) {
registerRPC[*tg.MessagesToggleSavedDialogPinRequest](d, tlprofile.SemanticMethodMessagesToggleSavedDialogPin, func(ctx context.Context, layerRequest *tg.MessagesToggleSavedDialogPinRequest) (any, error) {
return r.onMessagesToggleSavedDialogPin(ctx, layerRequest)
})
registerRPC[*tg.MessagesReorderPinnedSavedDialogsRequest](d, tlprofile.SemanticMethodMessagesReorderPinnedSavedDialogs, func(ctx context.Context, layerRequest *tg.MessagesReorderPinnedSavedDialogsRequest) (any, error) {
return r.onMessagesReorderPinnedSavedDialogs(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetSavedDialogsByIDRequest](d, tlprofile.SemanticMethodMessagesGetSavedDialogsByID, func(ctx context.Context, layerRequest *tg.MessagesGetSavedDialogsByIDRequest) (any, error) {
return r.onMessagesGetSavedDialogsByID(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetSavedHistoryRequest](d, tlprofile.SemanticMethodMessagesGetSavedHistory, func(ctx context.Context, layerRequest *tg.MessagesGetSavedHistoryRequest) (any, error) {
return r.onMessagesGetSavedHistory(ctx, layerRequest)
})
registerRPC[*tg.MessagesReadSavedHistoryRequest](d, tlprofile.SemanticMethodMessagesReadSavedHistory, func(ctx context.Context, layerRequest *tg.MessagesReadSavedHistoryRequest) (any, error) {
return r.onMessagesReadSavedHistory(ctx, layerRequest)
})
registerRPC[*tg.MessagesDeleteSavedHistoryRequest](d, tlprofile.SemanticMethodMessagesDeleteSavedHistory, func(ctx context.Context, layerRequest *tg.MessagesDeleteSavedHistoryRequest) (any, error) {
return r.onMessagesDeleteSavedHistory(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetCommonChatsRequest](d, tlprofile.SemanticMethodMessagesGetCommonChats, func(ctx context.Context, layerRequest *tg.MessagesGetCommonChatsRequest) (any, error) {
return r.onMessagesGetCommonChats(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetDefaultHistoryTTLRequest](d, tlprofile.SemanticMethodMessagesGetDefaultHistoryTTL, func(ctx context.Context, layerRequest *tg.MessagesGetDefaultHistoryTTLRequest) (any, error) {
return r.onMessagesGetDefaultHistoryTTL(ctx)
})
registerRPC[*tg.MessagesSetHistoryTTLRequest](d, tlprofile.SemanticMethodMessagesSetHistoryTTL, func(ctx context.Context, layerRequest *tg.MessagesSetHistoryTTLRequest) (any, error) {
return r.onMessagesSetHistoryTTL(ctx, layerRequest)
})
registerRPC[*tg.MessagesSetDefaultHistoryTTLRequest](d, tlprofile.SemanticMethodMessagesSetDefaultHistoryTTL, func(ctx context.Context, layerRequest *tg.MessagesSetDefaultHistoryTTLRequest) (any, error) {
return r.onMessagesSetDefaultHistoryTTL(ctx, layerRequest.
Period)
})
registerRPC[*tg.MessagesGetSponsoredMessagesRequest](d, tlprofile.SemanticMethodMessagesGetSponsoredMessages, func(ctx context.Context, layerRequest *tg.MessagesGetSponsoredMessagesRequest) (any, error) {
return r.onMessagesGetSponsoredMessages(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetWebPagePreviewRequest](d, tlprofile.SemanticMethodMessagesGetWebPagePreview, func(ctx context.Context, layerRequest *tg.MessagesGetWebPagePreviewRequest) (any, error) {
return r.onMessagesGetWebPagePreview(ctx, layerRequest)
})
registerRPC[*tg.MessagesRequestWebViewRequest](d, tlprofile.SemanticMethodMessagesRequestWebView, func(ctx context.Context, layerRequest *tg.MessagesRequestWebViewRequest) (any, error) {
return r.onMessagesRequestWebView(ctx, layerRequest)
})
registerRPC[*tg.MessagesProlongWebViewRequest](d, tlprofile.SemanticMethodMessagesProlongWebView, func(ctx context.Context, layerRequest *tg.MessagesProlongWebViewRequest) (any, error) {
return r.onMessagesProlongWebView(ctx, layerRequest)
})
registerRPC[*tg.MessagesSendWebViewResultMessageRequest](d, tlprofile.SemanticMethodMessagesSendWebViewResultMessage, func(ctx context.Context, layerRequest *tg.MessagesSendWebViewResultMessageRequest) (any, error) {
return r.onMessagesSendWebViewResultMessage(ctx, layerRequest)
})
registerRPC[*tg.MessagesRequestSimpleWebViewRequest](d, tlprofile.SemanticMethodMessagesRequestSimpleWebView, func(ctx context.Context, layerRequest *tg.MessagesRequestSimpleWebViewRequest) (any, error) {
return r.onMessagesRequestSimpleWebView(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetBotAppRequest](d, tlprofile.SemanticMethodMessagesGetBotApp, func(ctx context.Context, layerRequest *tg.MessagesGetBotAppRequest) (any, error) {
return r.onMessagesGetBotApp(ctx, layerRequest)
})
registerRPC[*tg.MessagesRequestAppWebViewRequest](d, tlprofile.SemanticMethodMessagesRequestAppWebView, func(ctx context.Context, layerRequest *tg.MessagesRequestAppWebViewRequest) (any, error) {
return r.onMessagesRequestAppWebView(ctx, layerRequest)
})
registerRPC[*tg.MessagesRequestMainWebViewRequest](d, tlprofile.SemanticMethodMessagesRequestMainWebView, func(ctx context.Context, layerRequest *tg.MessagesRequestMainWebViewRequest) (any, error) {
return r.onMessagesRequestMainWebView(ctx, layerRequest)
})
registerRPC[*tg.MessagesSendWebViewDataRequest](d, tlprofile.SemanticMethodMessagesSendWebViewData, func(ctx context.Context, layerRequest *tg.MessagesSendWebViewDataRequest) (any, error) {
return r.onMessagesSendWebViewData(ctx, layerRequest)
})
registerRPC[*tg.MessagesSendBotRequestedPeerRequest](d, tlprofile.SemanticMethodMessagesSendBotRequestedPeer, func(ctx context.Context, layerRequest *tg.MessagesSendBotRequestedPeerRequest) (any, error) {
return r.onMessagesSendBotRequestedPeer(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetPreparedInlineMessageRequest](d, tlprofile.SemanticMethodMessagesGetPreparedInlineMessage, func(ctx context.Context, layerRequest *tg.MessagesGetPreparedInlineMessageRequest) (any, error) {
return r.onMessagesGetPreparedInlineMessage(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetEmojiGameInfoRequest](d, tlprofile.SemanticMethodMessagesGetEmojiGameInfo, func(ctx context.Context, layerRequest *tg.MessagesGetEmojiGameInfoRequest) (any, error) {
return r.onMessagesGetEmojiGameInfo(ctx)
})
registerRPC[*tg.MessagesGetGameHighScoresRequest](d, tlprofile.SemanticMethodMessagesGetGameHighScores, func(ctx context.Context, layerRequest *tg.MessagesGetGameHighScoresRequest) (any, error) {
return r.onMessagesGetGameHighScores(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetInlineGameHighScoresRequest](d, tlprofile.SemanticMethodMessagesGetInlineGameHighScores, func(ctx context.Context, layerRequest *tg.MessagesGetInlineGameHighScoresRequest) (any, error) {
return r.onMessagesGetInlineGameHighScores(ctx, layerRequest)
})
registerRPC[*tg.MessagesSetGameScoreRequest](d, tlprofile.SemanticMethodMessagesSetGameScore, func(ctx context.Context, layerRequest *tg.MessagesSetGameScoreRequest) (any, error) {
return r.onMessagesSetGameScore(ctx, layerRequest)
})
registerRPC[*tg.MessagesSetInlineGameScoreRequest](d, tlprofile.SemanticMethodMessagesSetInlineGameScore, func(ctx context.Context, layerRequest *tg.MessagesSetInlineGameScoreRequest) (any, error) {
return r.onMessagesSetInlineGameScore(ctx, layerRequest)
})
registerRPC[*tg.MessagesUploadMediaRequest](d, tlprofile.SemanticMethodMessagesUploadMedia, func(ctx context.Context, layerRequest *tg.MessagesUploadMediaRequest) (any, error) {
return r.onMessagesUploadMedia(ctx, layerRequest)
})
registerRPC[*tg.MessagesSendMediaRequest](d, tlprofile.SemanticMethodMessagesSendMedia, func(ctx context.Context, layerRequest *tg.MessagesSendMediaRequest) (any, error) {
return r.onMessagesSendMedia(ctx, layerRequest)
})
registerRPC[*tg.MessagesSendMultiMediaRequest](d, tlprofile.SemanticMethodMessagesSendMultiMedia, func(ctx context.Context, layerRequest *tg.MessagesSendMultiMediaRequest) (any, error) {
return r.onMessagesSendMultiMedia(ctx, layerRequest)
})
registerRPC[*tg.MessagesReportSpamRequest](d, tlprofile.SemanticMethodMessagesReportSpam, func(ctx context.Context, layerRequest *tg.MessagesReportSpamRequest) (any, error) {
return r.onMessagesReportSpam(ctx, layerRequest.
Peer)
})
registerRPC[*tg.MessagesReportRequest](d, tlprofile.SemanticMethodMessagesReport, func(ctx context.Context, layerRequest *tg.MessagesReportRequest) (any, error) {
return r.onMessagesReport(ctx, layerRequest)
})
registerRPC[*tg.MessagesReportReactionRequest](d, tlprofile.SemanticMethodMessagesReportReaction, func(ctx context.Context, layerRequest *tg.MessagesReportReactionRequest) (any, error) {
return r.onMessagesReportReaction(ctx, layerRequest)
})
registerRPC[*tg.MessagesReportMessagesDeliveryRequest](d, tlprofile.SemanticMethodMessagesReportMessagesDelivery, func(ctx context.Context, layerRequest *tg.MessagesReportMessagesDeliveryRequest) (any, error) {
return r.onMessagesReportMessagesDelivery(ctx, layerRequest)
})
registerRPC[*tg.MessagesReportReadMetricsRequest](d, tlprofile.SemanticMethodMessagesReportReadMetrics, func(ctx context.Context, layerRequest *tg.MessagesReportReadMetricsRequest) (any, error) {
return r.onMessagesReportReadMetrics(ctx, layerRequest)
})
registerRPC[*tg.MessagesReportMusicListenRequest](d, tlprofile.SemanticMethodMessagesReportMusicListen, func(ctx context.Context, layerRequest *tg.MessagesReportMusicListenRequest) (any, error) {
return r.onMessagesReportMusicListen(ctx, layerRequest)
})
registerRPC[*tg.MessagesReportSponsoredMessageRequest](d, tlprofile.SemanticMethodMessagesReportSponsoredMessage, func(ctx context.Context, layerRequest *tg.MessagesReportSponsoredMessageRequest) (any, error) {
return r.onMessagesReportSponsoredMessage(ctx, layerRequest)
})
registerRPC[*tg.MessagesReadMessageContentsRequest](d, tlprofile.SemanticMethodMessagesReadMessageContents, func(ctx context.Context, layerRequest *tg.MessagesReadMessageContentsRequest) (any, error) {
return r.onMessagesReadMessageContents(ctx, layerRequest.
ID)
})
registerRPC[*tg.MessagesTranslateTextRequest](d, tlprofile.SemanticMethodMessagesTranslateText, func(ctx context.Context, layerRequest *tg.MessagesTranslateTextRequest) (any, error) {
return r.onMessagesTranslateText(ctx, layerRequest)
})
registerRPC[*tg.MessagesTogglePeerTranslationsRequest](d, tlprofile.SemanticMethodMessagesTogglePeerTranslations, func(ctx context.Context, layerRequest *tg.MessagesTogglePeerTranslationsRequest) (any, error) {
return r.onMessagesTogglePeerTranslations(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetMessagesViewsRequest](d, tlprofile.SemanticMethodMessagesGetMessagesViews, func(ctx context.Context, layerRequest *tg.MessagesGetMessagesViewsRequest) (any, error) {
return r.onMessagesGetMessagesViews(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetUnreadMentionsRequest](d, tlprofile.SemanticMethodMessagesGetUnreadMentions, func(ctx context.Context, layerRequest *tg.MessagesGetUnreadMentionsRequest) (any, error) {
return r.onMessagesGetUnreadMentions(ctx, layerRequest)
})
registerRPC[*tg.MessagesReadMentionsRequest](d, tlprofile.SemanticMethodMessagesReadMentions, func(ctx context.Context, layerRequest *tg.MessagesReadMentionsRequest) (any, error) {
return r.onMessagesReadMentions(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetSearchCountersRequest](d, tlprofile.SemanticMethodMessagesGetSearchCounters, func(ctx context.Context, layerRequest *tg.MessagesGetSearchCountersRequest) (any, error) {
return r.onMessagesGetSearchCounters(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetRepliesRequest](d, tlprofile.SemanticMethodMessagesGetReplies, func(ctx context.Context, layerRequest *tg.MessagesGetRepliesRequest) (any, error) {
return r.onMessagesGetReplies(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetDiscussionMessageRequest](d, tlprofile.SemanticMethodMessagesGetDiscussionMessage, func(ctx context.Context, layerRequest *tg.MessagesGetDiscussionMessageRequest) (any, error) {
return r.onMessagesGetDiscussionMessage(ctx, layerRequest)
})
registerRPC[*tg.MessagesReadDiscussionRequest](d, tlprofile.SemanticMethodMessagesReadDiscussion, func(ctx context.Context, layerRequest *tg.MessagesReadDiscussionRequest) (any, error) {
return r.onMessagesReadDiscussion(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetForumTopicsRequest](d, tlprofile.SemanticMethodMessagesGetForumTopics, func(ctx context.Context, layerRequest *tg.MessagesGetForumTopicsRequest) (any, error) {
return r.onMessagesGetForumTopics(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetForumTopicsByIDRequest](d, tlprofile.SemanticMethodMessagesGetForumTopicsByID, func(ctx context.Context, layerRequest *tg.MessagesGetForumTopicsByIDRequest) (any, error) {
return r.onMessagesGetForumTopicsByID(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetOnlinesRequest](d, tlprofile.SemanticMethodMessagesGetOnlines, func(ctx context.Context, layerRequest *tg.MessagesGetOnlinesRequest) (any, error) {
return r.onMessagesGetOnlines(ctx, layerRequest.
Peer)
})
registerRPC[*tg.MessagesGetAvailableReactionsRequest](d, tlprofile.SemanticMethodMessagesGetAvailableReactions, func(ctx context.Context, layerRequest *tg.MessagesGetAvailableReactionsRequest) (any, error) {
return r.onMessagesGetAvailableReactions(ctx, layerRequest.
Hash)
})
registerRPC[*tg.MessagesGetAvailableEffectsRequest](d, tlprofile.SemanticMethodMessagesGetAvailableEffects, func(ctx context.Context, layerRequest *tg.MessagesGetAvailableEffectsRequest) (any, error) {
return r.onMessagesGetAvailableEffects(ctx, layerRequest.
Hash)
})
registerRPC[*tg.MessagesGetStickersRequest](d, tlprofile.SemanticMethodMessagesGetStickers, func(ctx context.Context, layerRequest *tg.MessagesGetStickersRequest) (any, error) {
return r.onMessagesGetStickers(ctx, layerRequest)
})
registerRPC[*tg.MessagesInstallStickerSetRequest](d, tlprofile.SemanticMethodMessagesInstallStickerSet, func(ctx context.Context, layerRequest *tg.MessagesInstallStickerSetRequest) (any, error) {
return r.onMessagesInstallStickerSet(ctx, layerRequest)
})
registerRPC[*tg.MessagesUninstallStickerSetRequest](d, tlprofile.SemanticMethodMessagesUninstallStickerSet, func(ctx context.Context, layerRequest *tg.MessagesUninstallStickerSetRequest) (any, error) {
return r.onMessagesUninstallStickerSet(ctx, layerRequest.
Stickerset)
})
registerRPC[*tg.MessagesReorderStickerSetsRequest](d, tlprofile.SemanticMethodMessagesReorderStickerSets, func(ctx context.Context, layerRequest *tg.MessagesReorderStickerSetsRequest) (any, error) {
return r.onMessagesReorderStickerSets(ctx, layerRequest)
})
registerRPC[*tg.MessagesToggleStickerSetsRequest](d, tlprofile.SemanticMethodMessagesToggleStickerSets, func(ctx context.Context, layerRequest *tg.MessagesToggleStickerSetsRequest) (any, error) {
return r.onMessagesToggleStickerSets(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetMyStickersRequest](d, tlprofile.SemanticMethodMessagesGetMyStickers, func(ctx context.Context, layerRequest *tg.MessagesGetMyStickersRequest) (any, error) {
return r.onMessagesGetMyStickers(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetArchivedStickersRequest](d, tlprofile.SemanticMethodMessagesGetArchivedStickers, func(ctx context.Context, req *tg.MessagesGetArchivedStickersRequest) (any, error) {
return &tg.MessagesArchivedStickers{
Count: 0,
Sets: []tg.StickerSetCoveredClass{},
}, nil
})
d.OnMessagesGetStickerSet(r.onMessagesGetStickerSet)
d.OnMessagesGetEmojiGroups(func(ctx context.Context, hash int) (tg.MessagesEmojiGroupsClass, error) {
registerRPC[*tg.MessagesGetStickerSetRequest](d, tlprofile.SemanticMethodMessagesGetStickerSet, func(ctx context.Context, layerRequest *tg.MessagesGetStickerSetRequest) (any, error) {
return r.onMessagesGetStickerSet(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetEmojiGroupsRequest](d, tlprofile.SemanticMethodMessagesGetEmojiGroups, func(ctx context.Context, layerRequest *tg.MessagesGetEmojiGroupsRequest) (any, error) {
hash := layerRequest.
Hash
_ = hash
return tdesktop.EmojiGroups(hash), nil
})
d.OnMessagesGetEmojiStatusGroups(func(ctx context.Context, hash int) (tg.MessagesEmojiGroupsClass, error) {
registerRPC[*tg.MessagesGetEmojiStatusGroupsRequest](d, tlprofile.SemanticMethodMessagesGetEmojiStatusGroups, func(ctx context.Context, layerRequest *tg.MessagesGetEmojiStatusGroupsRequest) (any, error) {
hash := layerRequest.
Hash
_ = hash
return tdesktop.EmojiStatusGroups(), nil
})
d.OnMessagesGetEmojiStickerGroups(r.onMessagesGetEmojiStickerGroups)
d.OnMessagesGetEmojiProfilePhotoGroups(func(ctx context.Context, hash int) (tg.MessagesEmojiGroupsClass, error) {
registerRPC[*tg.MessagesGetEmojiStickerGroupsRequest](d, tlprofile.SemanticMethodMessagesGetEmojiStickerGroups, func(ctx context.Context, layerRequest *tg.MessagesGetEmojiStickerGroupsRequest) (any, error) {
return r.onMessagesGetEmojiStickerGroups(ctx, layerRequest.
Hash)
})
registerRPC[*tg.MessagesGetEmojiProfilePhotoGroupsRequest](d, tlprofile.SemanticMethodMessagesGetEmojiProfilePhotoGroups, func(ctx context.Context, layerRequest *tg.MessagesGetEmojiProfilePhotoGroupsRequest) (any, error) {
hash := layerRequest.
Hash
_ = hash
return tdesktop.EmojiProfilePhotoGroups(), nil
})
d.OnMessagesGetEmojiKeywords(r.onMessagesGetEmojiKeywords)
d.OnMessagesGetEmojiKeywordsDifference(r.onMessagesGetEmojiKeywordsDifference)
d.OnMessagesGetEmojiKeywordsLanguages(func(ctx context.Context, langcodes []string) ([]tg.EmojiLanguage, error) {
registerRPC[*tg.MessagesGetEmojiKeywordsRequest](d, tlprofile.SemanticMethodMessagesGetEmojiKeywords, func(ctx context.Context, layerRequest *tg.MessagesGetEmojiKeywordsRequest) (any, error) {
return r.onMessagesGetEmojiKeywords(ctx, layerRequest.
LangCode)
})
registerRPC[*tg.MessagesGetEmojiKeywordsDifferenceRequest](d, tlprofile.SemanticMethodMessagesGetEmojiKeywordsDifference, func(ctx context.Context, layerRequest *tg.MessagesGetEmojiKeywordsDifferenceRequest) (any, error) {
return r.onMessagesGetEmojiKeywordsDifference(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetEmojiKeywordsLanguagesRequest](d, tlprofile.SemanticMethodMessagesGetEmojiKeywordsLanguages, func(ctx context.Context, layerRequest *tg.MessagesGetEmojiKeywordsLanguagesRequest) (any, error) {
langcodes := layerRequest.
LangCodes
_ = langcodes
return []tg.EmojiLanguage{}, nil
})
d.OnMessagesGetCustomEmojiDocuments(r.onMessagesGetCustomEmojiDocuments)
d.OnMessagesGetAttachedStickers(r.onMessagesGetAttachedStickers)
d.OnMessagesSearchStickerSets(r.onMessagesSearchStickerSets)
d.OnMessagesSearchStickers(r.onMessagesSearchStickers)
d.OnMessagesGetAttachMenuBots(r.onMessagesGetAttachMenuBots)
d.OnMessagesGetAttachMenuBot(r.onMessagesGetAttachMenuBot)
d.OnMessagesToggleBotInAttachMenu(r.onMessagesToggleBotInAttachMenu)
d.OnMessagesGetQuickReplies(r.onMessagesGetQuickReplies)
d.OnMessagesCheckQuickReplyShortcut(r.onMessagesCheckQuickReplyShortcut)
d.OnMessagesReorderQuickReplies(r.onMessagesReorderQuickReplies)
d.OnMessagesEditQuickReplyShortcut(r.onMessagesEditQuickReplyShortcut)
d.OnMessagesDeleteQuickReplyShortcut(r.onMessagesDeleteQuickReplyShortcut)
d.OnMessagesGetQuickReplyMessages(r.onMessagesGetQuickReplyMessages)
d.OnMessagesSendQuickReplyMessages(r.onMessagesSendQuickReplyMessages)
d.OnMessagesDeleteQuickReplyMessages(r.onMessagesDeleteQuickReplyMessages)
d.OnMessagesGetWebPage(r.onMessagesGetWebPage)
d.OnMessagesGetDialogs(func(ctx context.Context, req *tg.MessagesGetDialogsRequest) (tg.MessagesDialogsClass, error) {
registerRPC[*tg.MessagesGetCustomEmojiDocumentsRequest](d, tlprofile.SemanticMethodMessagesGetCustomEmojiDocuments, func(ctx context.Context, layerRequest *tg.MessagesGetCustomEmojiDocumentsRequest) (any, error) {
return r.onMessagesGetCustomEmojiDocuments(ctx, layerRequest.
DocumentID)
})
registerRPC[*tg.MessagesGetAttachedStickersRequest](d, tlprofile.SemanticMethodMessagesGetAttachedStickers, func(ctx context.Context, layerRequest *tg.MessagesGetAttachedStickersRequest) (any, error) {
return r.onMessagesGetAttachedStickers(ctx, layerRequest.
Media)
})
registerRPC[*tg.MessagesSearchStickerSetsRequest](d, tlprofile.SemanticMethodMessagesSearchStickerSets, func(ctx context.Context, layerRequest *tg.MessagesSearchStickerSetsRequest) (any, error) {
return r.onMessagesSearchStickerSets(ctx, layerRequest)
})
registerRPC[*tg.MessagesSearchStickersRequest](d, tlprofile.SemanticMethodMessagesSearchStickers, func(ctx context.Context, layerRequest *tg.MessagesSearchStickersRequest) (any, error) {
return r.onMessagesSearchStickers(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetAttachMenuBotsRequest](d, tlprofile.SemanticMethodMessagesGetAttachMenuBots, func(ctx context.Context, layerRequest *tg.MessagesGetAttachMenuBotsRequest) (any, error) {
return r.onMessagesGetAttachMenuBots(ctx, layerRequest.
Hash)
})
registerRPC[*tg.MessagesGetAttachMenuBotRequest](d, tlprofile.SemanticMethodMessagesGetAttachMenuBot, func(ctx context.Context, layerRequest *tg.MessagesGetAttachMenuBotRequest) (any, error) {
return r.onMessagesGetAttachMenuBot(ctx, layerRequest.
Bot)
})
registerRPC[*tg.MessagesToggleBotInAttachMenuRequest](d, tlprofile.SemanticMethodMessagesToggleBotInAttachMenu, func(ctx context.Context, layerRequest *tg.MessagesToggleBotInAttachMenuRequest) (any, error) {
return r.onMessagesToggleBotInAttachMenu(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetQuickRepliesRequest](d, tlprofile.SemanticMethodMessagesGetQuickReplies, func(ctx context.Context, layerRequest *tg.MessagesGetQuickRepliesRequest) (any, error) {
return r.onMessagesGetQuickReplies(ctx, layerRequest.
Hash)
})
registerRPC[*tg.MessagesCheckQuickReplyShortcutRequest](d, tlprofile.SemanticMethodMessagesCheckQuickReplyShortcut, func(ctx context.Context, layerRequest *tg.MessagesCheckQuickReplyShortcutRequest) (any, error) {
return r.onMessagesCheckQuickReplyShortcut(ctx, layerRequest.
Shortcut)
})
registerRPC[*tg.MessagesReorderQuickRepliesRequest](d, tlprofile.SemanticMethodMessagesReorderQuickReplies, func(ctx context.Context, layerRequest *tg.MessagesReorderQuickRepliesRequest) (any, error) {
return r.onMessagesReorderQuickReplies(ctx, layerRequest.
Order)
})
registerRPC[*tg.MessagesEditQuickReplyShortcutRequest](d, tlprofile.SemanticMethodMessagesEditQuickReplyShortcut, func(ctx context.Context, layerRequest *tg.MessagesEditQuickReplyShortcutRequest) (any, error) {
return r.onMessagesEditQuickReplyShortcut(ctx, layerRequest)
})
registerRPC[*tg.MessagesDeleteQuickReplyShortcutRequest](d, tlprofile.SemanticMethodMessagesDeleteQuickReplyShortcut, func(ctx context.Context, layerRequest *tg.MessagesDeleteQuickReplyShortcutRequest) (any, error) {
return r.onMessagesDeleteQuickReplyShortcut(ctx, layerRequest.
ShortcutID)
})
registerRPC[*tg.MessagesGetQuickReplyMessagesRequest](d, tlprofile.SemanticMethodMessagesGetQuickReplyMessages, func(ctx context.Context, layerRequest *tg.MessagesGetQuickReplyMessagesRequest) (any, error) {
return r.onMessagesGetQuickReplyMessages(ctx, layerRequest)
})
registerRPC[*tg.MessagesSendQuickReplyMessagesRequest](d, tlprofile.SemanticMethodMessagesSendQuickReplyMessages, func(ctx context.Context, layerRequest *tg.MessagesSendQuickReplyMessagesRequest) (any, error) {
return r.onMessagesSendQuickReplyMessages(ctx, layerRequest)
})
registerRPC[*tg.MessagesDeleteQuickReplyMessagesRequest](d, tlprofile.SemanticMethodMessagesDeleteQuickReplyMessages, func(ctx context.Context, layerRequest *tg.MessagesDeleteQuickReplyMessagesRequest) (any, error) {
return r.onMessagesDeleteQuickReplyMessages(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetWebPageRequest](d, tlprofile.SemanticMethodMessagesGetWebPage, func(ctx context.Context, layerRequest *tg.MessagesGetWebPageRequest) (any, error) {
return r.onMessagesGetWebPage(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetDialogsRequest](d, tlprofile.SemanticMethodMessagesGetDialogs, func(ctx context.Context, req *tg.MessagesGetDialogsRequest) (any, error) {
if r.deps.Dialogs == nil {
return &tg.MessagesDialogs{}, nil
}
@ -175,7 +427,11 @@ func (r *Router) registerMessages(d *tg.ServerDispatcher) {
}
return r.tgMessagesDialogs(ctx, userID, r.withDialogListPresence(ctx, userID, list)), nil
})
d.OnMessagesGetPinnedDialogs(func(ctx context.Context, folderID int) (*tg.MessagesPeerDialogs, error) {
registerRPC[*tg.MessagesGetPinnedDialogsRequest](d, tlprofile.SemanticMethodMessagesGetPinnedDialogs, func(ctx context.Context, layerRequest *tg.MessagesGetPinnedDialogsRequest) (any, error) {
folderID := layerRequest.
FolderID
_ = folderID
id, _ := AuthKeyIDFrom(ctx)
userID, _, err := r.currentUserID(ctx)
if err != nil {
@ -195,7 +451,11 @@ func (r *Router) registerMessages(d *tg.ServerDispatcher) {
}
return r.tgPeerDialogs(ctx, userID, r.withDialogListPresence(ctx, userID, list), st), nil
})
d.OnMessagesGetPeerDialogs(func(ctx context.Context, peers []tg.InputDialogPeerClass) (*tg.MessagesPeerDialogs, error) {
registerRPC[*tg.MessagesGetPeerDialogsRequest](d, tlprofile.SemanticMethodMessagesGetPeerDialogs, func(ctx context.Context, layerRequest *tg.MessagesGetPeerDialogsRequest) (any, error) {
peers := layerRequest.
Peers
_ = peers
id, _ := AuthKeyIDFrom(ctx)
userID, _, err := r.currentUserID(ctx)
if err != nil {
@ -240,21 +500,52 @@ func (r *Router) registerMessages(d *tg.ServerDispatcher) {
r.trackChannelInterest(ctx, userID, channelIDsFromDialogs(list)...)
return r.tgPeerDialogs(ctx, userID, r.withDialogListPresence(ctx, userID, list), st), nil
})
d.OnMessagesGetPeerSettings(r.onMessagesGetPeerSettings)
d.OnMessagesToggleDialogPin(r.onMessagesToggleDialogPin)
d.OnMessagesReorderPinnedDialogs(r.onMessagesReorderPinnedDialogs)
d.OnMessagesMarkDialogUnread(r.onMessagesMarkDialogUnread)
d.OnMessagesGetDialogUnreadMarks(r.onMessagesGetDialogUnreadMarks)
d.OnMessagesHidePeerSettingsBar(r.onMessagesHidePeerSettingsBar)
d.OnMessagesGetMessageEditData(r.onMessagesGetMessageEditData)
d.OnMessagesEditMessage(r.onMessagesEditMessage)
d.OnMessagesGetOutboxReadDate(r.onMessagesGetOutboxReadDate)
d.OnMessagesGetMessageReadParticipants(r.onMessagesGetMessageReadParticipants)
d.OnMessagesDeleteMessages(r.onMessagesDeleteMessages)
d.OnMessagesDeleteHistory(r.onMessagesDeleteHistory)
d.OnMessagesGetMessages(r.onMessagesGetMessages)
d.OnMessagesGetRichMessage(r.onMessagesGetRichMessage)
d.OnMessagesGetHistory(func(ctx context.Context, req *tg.MessagesGetHistoryRequest) (tg.MessagesMessagesClass, error) {
registerRPC[*tg.MessagesGetPeerSettingsRequest](d, tlprofile.SemanticMethodMessagesGetPeerSettings, func(ctx context.Context, layerRequest *tg.MessagesGetPeerSettingsRequest) (any, error) {
return r.onMessagesGetPeerSettings(ctx, layerRequest.
Peer)
})
registerRPC[*tg.MessagesToggleDialogPinRequest](d, tlprofile.SemanticMethodMessagesToggleDialogPin, func(ctx context.Context, layerRequest *tg.MessagesToggleDialogPinRequest) (any, error) {
return r.onMessagesToggleDialogPin(ctx, layerRequest)
})
registerRPC[*tg.MessagesReorderPinnedDialogsRequest](d, tlprofile.SemanticMethodMessagesReorderPinnedDialogs, func(ctx context.Context, layerRequest *tg.MessagesReorderPinnedDialogsRequest) (any, error) {
return r.onMessagesReorderPinnedDialogs(ctx, layerRequest)
})
registerRPC[*tg.MessagesMarkDialogUnreadRequest](d, tlprofile.SemanticMethodMessagesMarkDialogUnread, func(ctx context.Context, layerRequest *tg.MessagesMarkDialogUnreadRequest) (any, error) {
return r.onMessagesMarkDialogUnread(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetDialogUnreadMarksRequest](d, tlprofile.SemanticMethodMessagesGetDialogUnreadMarks, func(ctx context.Context, layerRequest *tg.MessagesGetDialogUnreadMarksRequest) (any, error) {
return r.onMessagesGetDialogUnreadMarks(ctx, layerRequest)
})
registerRPC[*tg.MessagesHidePeerSettingsBarRequest](d, tlprofile.SemanticMethodMessagesHidePeerSettingsBar, func(ctx context.Context, layerRequest *tg.MessagesHidePeerSettingsBarRequest) (any, error) {
return r.onMessagesHidePeerSettingsBar(ctx, layerRequest.
Peer)
})
registerRPC[*tg.MessagesGetMessageEditDataRequest](d, tlprofile.SemanticMethodMessagesGetMessageEditData, func(ctx context.Context, layerRequest *tg.MessagesGetMessageEditDataRequest) (any, error) {
return r.onMessagesGetMessageEditData(ctx, layerRequest)
})
registerRPC[*tg.MessagesEditMessageRequest](d, tlprofile.SemanticMethodMessagesEditMessage, func(ctx context.Context, layerRequest *tg.MessagesEditMessageRequest) (any, error) {
return r.onMessagesEditMessage(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetOutboxReadDateRequest](d, tlprofile.SemanticMethodMessagesGetOutboxReadDate, func(ctx context.Context, layerRequest *tg.MessagesGetOutboxReadDateRequest) (any, error) {
return r.onMessagesGetOutboxReadDate(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetMessageReadParticipantsRequest](d, tlprofile.SemanticMethodMessagesGetMessageReadParticipants, func(ctx context.Context, layerRequest *tg.MessagesGetMessageReadParticipantsRequest) (any, error) {
return r.onMessagesGetMessageReadParticipants(ctx, layerRequest)
})
registerRPC[*tg.MessagesDeleteMessagesRequest](d, tlprofile.SemanticMethodMessagesDeleteMessages, func(ctx context.Context, layerRequest *tg.MessagesDeleteMessagesRequest) (any, error) {
return r.onMessagesDeleteMessages(ctx, layerRequest)
})
registerRPC[*tg.MessagesDeleteHistoryRequest](d, tlprofile.SemanticMethodMessagesDeleteHistory, func(ctx context.Context, layerRequest *tg.MessagesDeleteHistoryRequest) (any, error) {
return r.onMessagesDeleteHistory(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetMessagesRequest](d, tlprofile.SemanticMethodMessagesGetMessages, func(ctx context.Context, layerRequest *tg.MessagesGetMessagesRequest) (any, error) {
return r.onMessagesGetMessages(ctx, layerRequest.
ID)
})
registerRPC[*tg.MessagesGetRichMessageRequest](d, tlprofile.SemanticMethodMessagesGetRichMessage, func(ctx context.Context, layerRequest *tg.MessagesGetRichMessageRequest) (any, error) {
return r.onMessagesGetRichMessage(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetHistoryRequest](d, tlprofile.SemanticMethodMessagesGetHistory, func(ctx context.Context, req *tg.MessagesGetHistoryRequest) (any, error) {
userID, _, err := r.currentUserID(ctx)
if err != nil {
return nil, internalErr()
@ -309,8 +600,10 @@ func (r *Router) registerMessages(d *tg.ServerDispatcher) {
}
return r.tgMessagesMessages(ctx, userID, r.enrichMessageList(ctx, userID, list)), nil
})
d.OnMessagesGetRecentLocations(r.onMessagesGetRecentLocations)
d.OnMessagesReadHistory(func(ctx context.Context, req *tg.MessagesReadHistoryRequest) (*tg.MessagesAffectedMessages, error) {
registerRPC[*tg.MessagesGetRecentLocationsRequest](d, tlprofile.SemanticMethodMessagesGetRecentLocations, func(ctx context.Context, layerRequest *tg.MessagesGetRecentLocationsRequest) (any, error) {
return r.onMessagesGetRecentLocations(ctx, layerRequest)
})
registerRPC[*tg.MessagesReadHistoryRequest](d, tlprofile.SemanticMethodMessagesReadHistory, func(ctx context.Context, req *tg.MessagesReadHistoryRequest) (any, error) {
id, _ := AuthKeyIDFrom(ctx)
userID, _, err := r.currentUserID(ctx)
if err != nil {
@ -365,7 +658,7 @@ func (r *Router) registerMessages(d *tg.ServerDispatcher) {
}
return r.affectedMessages(ctx, id, userID)
})
d.OnMessagesSearch(func(ctx context.Context, req *tg.MessagesSearchRequest) (tg.MessagesMessagesClass, error) {
registerRPC[*tg.MessagesSearchRequest](d, tlprofile.SemanticMethodMessagesSearch, func(ctx context.Context, req *tg.MessagesSearchRequest) (any, error) {
if utf8.RuneCountInString(req.Q) > maxMessageSearchQLength {
return nil, limitInvalidErr()
}
@ -536,51 +829,132 @@ func (r *Router) registerMessages(d *tg.ServerDispatcher) {
}
return r.tgMessagesMessages(ctx, userID, r.enrichMessageList(ctx, userID, list)), nil
})
d.OnMessagesSearchGlobal(r.onMessagesSearchGlobal)
d.OnMessagesGetSearchResultsCalendar(r.onMessagesGetSearchResultsCalendar)
d.OnMessagesGetSearchResultsPositions(r.onMessagesGetSearchResultsPositions)
d.OnMessagesSendReaction(r.onMessagesSendReaction)
d.OnMessagesComposeMessageWithAI(r.onMessagesComposeMessageWithAI)
// 语音转文字无识别后端注册为显式失败TRANSCRIPTION_FAILEDpremium
// 客户端点击转录按钮得到优雅失败提示,而不是 NOT_IMPLEMENTED trace。
d.OnMessagesTranscribeAudio(func(ctx context.Context, req *tg.MessagesTranscribeAudioRequest) (*tg.MessagesTranscribedAudio, error) {
registerRPC[*tg.MessagesSearchGlobalRequest](d, tlprofile.SemanticMethodMessagesSearchGlobal, func(ctx context.Context, layerRequest *tg.MessagesSearchGlobalRequest) (any, error) {
return r.onMessagesSearchGlobal(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetSearchResultsCalendarRequest](d, tlprofile.SemanticMethodMessagesGetSearchResultsCalendar, func(ctx context.Context, layerRequest *tg.MessagesGetSearchResultsCalendarRequest) (any, error) {
return r.onMessagesGetSearchResultsCalendar(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetSearchResultsPositionsRequest](d, tlprofile.SemanticMethodMessagesGetSearchResultsPositions, func(ctx context.Context, layerRequest *tg.MessagesGetSearchResultsPositionsRequest) (any, error) {
return r.onMessagesGetSearchResultsPositions(ctx, layerRequest)
})
registerRPC[*tg.MessagesSendReactionRequest](d, tlprofile.SemanticMethodMessagesSendReaction, func(ctx context.Context, layerRequest *tg.MessagesSendReactionRequest) (any, error) {
// 语音转文字无识别后端注册为显式失败TRANSCRIPTION_FAILEDpremium
// 客户端点击转录按钮得到优雅失败提示,而不是 NOT_IMPLEMENTED trace。
return r.onMessagesSendReaction(ctx, layerRequest)
})
registerRPC[*tg.MessagesComposeMessageWithAIRequest](d, tlprofile.SemanticMethodMessagesComposeMessageWithAI, func(ctx context.Context, layerRequest *tg.MessagesComposeMessageWithAIRequest) (any, error) {
return r.onMessagesComposeMessageWithAI(ctx, layerRequest)
})
registerRPC[*tg.MessagesTranscribeAudioRequest](d, tlprofile.SemanticMethodMessagesTranscribeAudio, func(ctx context.Context, req *tg.MessagesTranscribeAudioRequest) (any, error) {
if _, _, err := r.currentUserID(ctx); err != nil {
return nil, internalErr()
}
return nil, tgerr400("TRANSCRIPTION_FAILED")
})
d.OnMessagesGetMessagesReactions(r.onMessagesGetMessagesReactions)
d.OnMessagesGetMessageReactionsList(r.onMessagesGetMessageReactionsList)
d.OnMessagesSetDefaultReaction(r.onMessagesSetDefaultReaction)
d.OnMessagesGetPaidReactionPrivacy(r.onMessagesGetPaidReactionPrivacy)
d.OnMessagesTogglePaidReactionPrivacy(r.onMessagesTogglePaidReactionPrivacy)
d.OnMessagesSendPaidReaction(r.onMessagesSendPaidReaction)
d.OnMessagesDeleteParticipantReactions(r.onMessagesDeleteParticipantReactions)
d.OnMessagesDeleteParticipantReaction(r.onMessagesDeleteParticipantReaction)
d.OnMessagesGetUnreadReactions(r.onMessagesGetUnreadReactions)
d.OnMessagesReadReactions(r.onMessagesReadReactions)
d.OnMessagesGetTopReactions(r.onMessagesGetTopReactions)
d.OnMessagesGetRecentReactions(r.onMessagesGetRecentReactions)
d.OnMessagesClearRecentReactions(r.onMessagesClearRecentReactions)
d.OnMessagesGetSavedReactionTags(r.onMessagesGetSavedReactionTags)
d.OnMessagesUpdateSavedReactionTag(r.onMessagesUpdateSavedReactionTag)
d.OnMessagesGetDefaultTagReactions(r.onMessagesGetDefaultTagReactions)
d.OnMessagesSendVote(r.onMessagesSendVote)
d.OnMessagesGetPollResults(r.onMessagesGetPollResults)
d.OnMessagesGetPollVotes(r.onMessagesGetPollVotes)
d.OnMessagesAddPollAnswer(r.onMessagesAddPollAnswer)
d.OnMessagesDeletePollAnswer(r.onMessagesDeletePollAnswer)
d.OnMessagesGetUnreadPollVotes(r.onMessagesGetUnreadPollVotes)
d.OnMessagesReadPollVotes(r.onMessagesReadPollVotes)
d.OnMessagesAppendTodoList(r.onMessagesAppendTodoList)
d.OnMessagesToggleTodoCompleted(r.onMessagesToggleTodoCompleted)
d.OnMessagesGetScheduledHistory(r.onMessagesGetScheduledHistory)
d.OnMessagesGetScheduledMessages(r.onMessagesGetScheduledMessages)
d.OnMessagesSendScheduledMessages(r.onMessagesSendScheduledMessages)
d.OnMessagesDeleteScheduledMessages(r.onMessagesDeleteScheduledMessages)
d.OnMessagesCreateForumTopic(r.onMessagesCreateForumTopic)
d.OnMessagesEditForumTopic(r.onMessagesEditForumTopic)
d.OnMessagesUpdatePinnedForumTopic(r.onMessagesUpdatePinnedForumTopic)
d.OnMessagesReorderPinnedForumTopics(r.onMessagesReorderPinnedForumTopics)
d.OnMessagesDeleteTopicHistory(r.onMessagesDeleteTopicHistory)
registerRPC[*tg.MessagesGetMessagesReactionsRequest](d, tlprofile.SemanticMethodMessagesGetMessagesReactions, func(ctx context.Context, layerRequest *tg.MessagesGetMessagesReactionsRequest) (any, error) {
return r.onMessagesGetMessagesReactions(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetMessageReactionsListRequest](d, tlprofile.SemanticMethodMessagesGetMessageReactionsList, func(ctx context.Context, layerRequest *tg.MessagesGetMessageReactionsListRequest) (any, error) {
return r.onMessagesGetMessageReactionsList(ctx, layerRequest)
})
registerRPC[*tg.MessagesSetDefaultReactionRequest](d, tlprofile.SemanticMethodMessagesSetDefaultReaction, func(ctx context.Context, layerRequest *tg.MessagesSetDefaultReactionRequest) (any, error) {
return r.onMessagesSetDefaultReaction(ctx, layerRequest.
Reaction)
})
registerRPC[*tg.MessagesGetPaidReactionPrivacyRequest](d, tlprofile.SemanticMethodMessagesGetPaidReactionPrivacy, func(ctx context.Context, layerRequest *tg.MessagesGetPaidReactionPrivacyRequest) (any, error) {
return r.onMessagesGetPaidReactionPrivacy(ctx)
})
registerRPC[*tg.MessagesTogglePaidReactionPrivacyRequest](d, tlprofile.SemanticMethodMessagesTogglePaidReactionPrivacy, func(ctx context.Context, layerRequest *tg.MessagesTogglePaidReactionPrivacyRequest) (any, error) {
return r.onMessagesTogglePaidReactionPrivacy(ctx, layerRequest)
})
registerRPC[*tg.MessagesSendPaidReactionRequest](d, tlprofile.SemanticMethodMessagesSendPaidReaction, func(ctx context.Context, layerRequest *tg.MessagesSendPaidReactionRequest) (any, error) {
return r.onMessagesSendPaidReaction(ctx, layerRequest)
})
registerRPC[*tg.MessagesDeleteParticipantReactionsRequest](d, tlprofile.SemanticMethodMessagesDeleteParticipantReactions, func(ctx context.Context, layerRequest *tg.MessagesDeleteParticipantReactionsRequest) (any, error) {
return r.onMessagesDeleteParticipantReactions(ctx, layerRequest)
})
registerRPC[*tg.MessagesDeleteParticipantReactionRequest](d, tlprofile.SemanticMethodMessagesDeleteParticipantReaction, func(ctx context.Context, layerRequest *tg.MessagesDeleteParticipantReactionRequest) (any, error) {
return r.onMessagesDeleteParticipantReaction(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetUnreadReactionsRequest](d, tlprofile.SemanticMethodMessagesGetUnreadReactions, func(ctx context.Context, layerRequest *tg.MessagesGetUnreadReactionsRequest) (any, error) {
return r.onMessagesGetUnreadReactions(ctx, layerRequest)
})
registerRPC[*tg.MessagesReadReactionsRequest](d, tlprofile.SemanticMethodMessagesReadReactions, func(ctx context.Context, layerRequest *tg.MessagesReadReactionsRequest) (any, error) {
return r.onMessagesReadReactions(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetTopReactionsRequest](d, tlprofile.SemanticMethodMessagesGetTopReactions, func(ctx context.Context, layerRequest *tg.MessagesGetTopReactionsRequest) (any, error) {
return r.onMessagesGetTopReactions(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetRecentReactionsRequest](d, tlprofile.SemanticMethodMessagesGetRecentReactions, func(ctx context.Context, layerRequest *tg.MessagesGetRecentReactionsRequest) (any, error) {
return r.onMessagesGetRecentReactions(ctx, layerRequest)
})
registerRPC[*tg.MessagesClearRecentReactionsRequest](d, tlprofile.SemanticMethodMessagesClearRecentReactions, func(ctx context.Context, layerRequest *tg.MessagesClearRecentReactionsRequest) (any, error) {
return r.onMessagesClearRecentReactions(ctx)
})
registerRPC[*tg.MessagesGetSavedReactionTagsRequest](d, tlprofile.SemanticMethodMessagesGetSavedReactionTags, func(ctx context.Context, layerRequest *tg.MessagesGetSavedReactionTagsRequest) (any, error) {
return r.onMessagesGetSavedReactionTags(ctx, layerRequest)
})
registerRPC[*tg.MessagesUpdateSavedReactionTagRequest](d, tlprofile.SemanticMethodMessagesUpdateSavedReactionTag, func(ctx context.Context, layerRequest *tg.MessagesUpdateSavedReactionTagRequest) (any, error) {
return r.onMessagesUpdateSavedReactionTag(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetDefaultTagReactionsRequest](d, tlprofile.SemanticMethodMessagesGetDefaultTagReactions, func(ctx context.Context, layerRequest *tg.MessagesGetDefaultTagReactionsRequest) (any, error) {
return r.onMessagesGetDefaultTagReactions(ctx, layerRequest.
Hash)
})
registerRPC[*tg.MessagesSendVoteRequest](d, tlprofile.SemanticMethodMessagesSendVote, func(ctx context.Context, layerRequest *tg.MessagesSendVoteRequest) (any, error) {
return r.onMessagesSendVote(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetPollResultsRequest](d, tlprofile.SemanticMethodMessagesGetPollResults, func(ctx context.Context, layerRequest *tg.MessagesGetPollResultsRequest) (any, error) {
return r.onMessagesGetPollResults(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetPollVotesRequest](d, tlprofile.SemanticMethodMessagesGetPollVotes, func(ctx context.Context, layerRequest *tg.MessagesGetPollVotesRequest) (any, error) {
return r.onMessagesGetPollVotes(ctx, layerRequest)
})
registerRPC[*tg.MessagesAddPollAnswerRequest](d, tlprofile.SemanticMethodMessagesAddPollAnswer, func(ctx context.Context, layerRequest *tg.MessagesAddPollAnswerRequest) (any, error) {
return r.onMessagesAddPollAnswer(ctx, layerRequest)
})
registerRPC[*tg.MessagesDeletePollAnswerRequest](d, tlprofile.SemanticMethodMessagesDeletePollAnswer, func(ctx context.Context, layerRequest *tg.MessagesDeletePollAnswerRequest) (any, error) {
return r.onMessagesDeletePollAnswer(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetUnreadPollVotesRequest](d, tlprofile.SemanticMethodMessagesGetUnreadPollVotes, func(ctx context.Context, layerRequest *tg.MessagesGetUnreadPollVotesRequest) (any, error) {
return r.onMessagesGetUnreadPollVotes(ctx, layerRequest)
})
registerRPC[*tg.MessagesReadPollVotesRequest](d, tlprofile.SemanticMethodMessagesReadPollVotes, func(ctx context.Context, layerRequest *tg.MessagesReadPollVotesRequest) (any, error) {
return r.onMessagesReadPollVotes(ctx, layerRequest)
})
registerRPC[*tg.MessagesAppendTodoListRequest](d, tlprofile.SemanticMethodMessagesAppendTodoList, func(ctx context.Context, layerRequest *tg.MessagesAppendTodoListRequest) (any, error) {
return r.onMessagesAppendTodoList(ctx, layerRequest)
})
registerRPC[*tg.MessagesToggleTodoCompletedRequest](d, tlprofile.SemanticMethodMessagesToggleTodoCompleted, func(ctx context.Context, layerRequest *tg.MessagesToggleTodoCompletedRequest) (any, error) {
return r.onMessagesToggleTodoCompleted(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetScheduledHistoryRequest](d, tlprofile.SemanticMethodMessagesGetScheduledHistory, func(ctx context.Context, layerRequest *tg.MessagesGetScheduledHistoryRequest) (any, error) {
return r.onMessagesGetScheduledHistory(ctx, layerRequest)
})
registerRPC[*tg.MessagesGetScheduledMessagesRequest](d, tlprofile.SemanticMethodMessagesGetScheduledMessages, func(ctx context.Context, layerRequest *tg.MessagesGetScheduledMessagesRequest) (any, error) {
return r.onMessagesGetScheduledMessages(ctx, layerRequest)
})
registerRPC[*tg.MessagesSendScheduledMessagesRequest](d, tlprofile.SemanticMethodMessagesSendScheduledMessages, func(ctx context.Context, layerRequest *tg.MessagesSendScheduledMessagesRequest) (any, error) {
return r.onMessagesSendScheduledMessages(ctx, layerRequest)
})
registerRPC[*tg.MessagesDeleteScheduledMessagesRequest](d, tlprofile.SemanticMethodMessagesDeleteScheduledMessages, func(ctx context.Context, layerRequest *tg.MessagesDeleteScheduledMessagesRequest) (any, error) {
return r.onMessagesDeleteScheduledMessages(ctx, layerRequest)
})
registerRPC[*tg.MessagesCreateForumTopicRequest](d, tlprofile.SemanticMethodMessagesCreateForumTopic, func(ctx context.Context, layerRequest *tg.MessagesCreateForumTopicRequest) (any, error) {
return r.onMessagesCreateForumTopic(ctx, layerRequest)
})
registerRPC[*tg.MessagesEditForumTopicRequest](d, tlprofile.SemanticMethodMessagesEditForumTopic, func(ctx context.Context, layerRequest *tg.MessagesEditForumTopicRequest) (any, error) {
return r.onMessagesEditForumTopic(ctx, layerRequest)
})
registerRPC[*tg.MessagesUpdatePinnedForumTopicRequest](d, tlprofile.SemanticMethodMessagesUpdatePinnedForumTopic, func(ctx context.Context, layerRequest *tg.MessagesUpdatePinnedForumTopicRequest) (any, error) {
return r.onMessagesUpdatePinnedForumTopic(ctx, layerRequest)
})
registerRPC[*tg.MessagesReorderPinnedForumTopicsRequest](d, tlprofile.SemanticMethodMessagesReorderPinnedForumTopics, func(ctx context.Context, layerRequest *tg.MessagesReorderPinnedForumTopicsRequest) (any, error) {
return r.onMessagesReorderPinnedForumTopics(ctx, layerRequest)
})
registerRPC[*tg.MessagesDeleteTopicHistoryRequest](d, tlprofile.SemanticMethodMessagesDeleteTopicHistory, func(ctx context.Context, layerRequest *tg.MessagesDeleteTopicHistoryRequest) (any, error) {
return r.onMessagesDeleteTopicHistory(ctx, layerRequest)
})
}

View file

@ -600,17 +600,9 @@ func TestModernForwardMessagesConstructorSavesToSelf(t *testing.T) {
if err != nil {
t.Fatalf("dispatch messages.forwardMessages#41d41ade: %v", err)
}
// Now routed through the generated static client overlay and
// the normal gotd dispatcher, which boxes a class result as *tg.UpdatesBox
// (wire-identical to the raw UpdatesClass the dedicated handler used to return).
switch v := enc.(type) {
case tg.UpdatesClass:
case *tg.UpdatesBox:
if v.Updates == nil {
t.Fatalf("forward result box has nil Updates")
}
default:
t.Fatalf("forward result = %T, want UpdatesClass or *tg.UpdatesBox", enc)
// The sparse dispatcher exposes the canonical concrete class result.
if _, ok := enc.(tg.UpdatesClass); !ok {
t.Fatalf("forward result = %T, want tg.UpdatesClass", enc)
}
res, err := r.onMessagesGetSavedDialogs(WithUserID(ctx, alice.ID), &tg.MessagesGetSavedDialogsRequest{Limit: 20})

View file

@ -41,13 +41,9 @@ func TestMessagesSendMessageReturnsUpdateAndRecordsOwnerContext(t *testing.T) {
if err != nil {
t.Fatalf("dispatch: %v", err)
}
box, ok := enc.(*tg.UpdatesBox)
got, ok := enc.(*tg.Updates)
if !ok {
t.Fatalf("response = %T, want *tg.UpdatesBox", enc)
}
got, ok := box.Updates.(*tg.Updates)
if !ok {
t.Fatalf("boxed response = %T, want *tg.Updates", box.Updates)
t.Fatalf("response = %T, want *tg.Updates", enc)
}
if messages.sendUserID != sender.ID || messages.sendReq.SenderUserID != sender.ID || messages.sendReq.RecipientUserID != recipient.ID || messages.sendReq.OriginSessionID != 77 {
t.Fatalf("send context = user %d req %+v, want sender/recipient/session", messages.sendUserID, messages.sendReq)
@ -176,11 +172,10 @@ func TestMessagesSendMessageSupportsReplyAndFlags(t *testing.T) {
if !messages.sendReq.Silent || !messages.sendReq.NoForwards {
t.Fatalf("send flags silent=%v noforwards=%v, want true/true", messages.sendReq.Silent, messages.sendReq.NoForwards)
}
box, ok := enc.(*tg.UpdatesBox)
got, ok := enc.(*tg.Updates)
if !ok {
t.Fatalf("response = %T, want *tg.UpdatesBox", enc)
t.Fatalf("response = %T, want *tg.Updates", enc)
}
got := box.Updates.(*tg.Updates)
newMsg := got.Updates[1].(*tg.UpdateNewMessage)
msg := newMsg.Message.(*tg.Message)
if !msg.Silent || !msg.Noforwards {

View file

@ -44,15 +44,10 @@ func TestLegacyAndroidMessagesUploadMediaDispatch(t *testing.T) {
if err != nil {
t.Fatalf("dispatch legacy uploadMedia: %v", err)
}
// Routed through the generated static client overlay + the normal
// gotd dispatcher, which boxes a class result (MessageMedia) as *...Box.
box, ok := enc.(*tg.MessageMediaBox)
// Routed through the generated static client overlay and sparse dispatcher.
media, ok := enc.(*tg.MessageMediaPhoto)
if !ok {
t.Fatalf("response = %T, want *tg.MessageMediaBox", enc)
}
media, ok := box.MessageMedia.(*tg.MessageMediaPhoto)
if !ok {
t.Fatalf("media = %T, want messageMediaPhoto", box.MessageMedia)
t.Fatalf("response = %T, want *tg.MessageMediaPhoto", enc)
}
photo, ok := media.Photo.(*tg.Photo)
if !ok || photo.ID != 777 {

View file

@ -23,13 +23,9 @@ func TestAccountGetChatThemesReturnsStaticThemes(t *testing.T) {
if err != nil {
t.Fatalf("dispatch: %v", err)
}
box, ok := got.(*tg.AccountThemesBox)
themes, ok := got.(*tg.AccountThemes)
if !ok {
t.Fatalf("response type = %T, want *tg.AccountThemesBox", got)
}
themes, ok := box.Themes.(*tg.AccountThemes)
if !ok {
t.Fatalf("boxed response type = %T, want *tg.AccountThemes", box.Themes)
t.Fatalf("response type = %T, want *tg.AccountThemes", got)
}
if themes.Hash == 0 || len(themes.Themes) == 0 {
t.Fatalf("themes = %+v, want non-empty stable list", themes)
@ -48,20 +44,16 @@ func TestAccountGetUniqueGiftChatThemesReturnsEmptyStub(t *testing.T) {
if err != nil {
t.Fatalf("dispatch: %v", err)
}
box, ok := got.(*tg.AccountChatThemesBox)
themes, ok := got.(*tg.AccountChatThemes)
if !ok {
t.Fatalf("response type = %T, want *tg.AccountChatThemesBox", got)
}
themes, ok := box.ChatThemes.(*tg.AccountChatThemes)
if !ok {
t.Fatalf("boxed response type = %T, want *tg.AccountChatThemes", box.ChatThemes)
t.Fatalf("response type = %T, want *tg.AccountChatThemes", got)
}
if themes.Hash == 0 || len(themes.Themes) != 0 || len(themes.Chats) != 0 || len(themes.Users) != 0 {
t.Fatalf("unique gift themes = %+v, want stable empty catalog", themes)
}
}
func TestAccountGetWallPapersReturnsDefaultCatalog(t *testing.T) {
func TestAccountGetWallPapersReturnsOrangeCatalog(t *testing.T) {
r := New(Config{DC: 2, IP: "127.0.0.1", Port: 2398}, Deps{}, zaptest.NewLogger(t), clock.System)
req := &tg.AccountGetWallPapersRequest{}
var in bin.Buffer
@ -73,13 +65,9 @@ func TestAccountGetWallPapersReturnsDefaultCatalog(t *testing.T) {
if err != nil {
t.Fatalf("dispatch: %v", err)
}
box, ok := got.(*tg.AccountWallPapersBox)
wallpapers, ok := got.(*tg.AccountWallPapers)
if !ok {
t.Fatalf("response type = %T, want *tg.AccountWallPapersBox", got)
}
wallpapers, ok := box.WallPapers.(*tg.AccountWallPapers)
if !ok {
t.Fatalf("boxed response type = %T, want *tg.AccountWallPapers", box.WallPapers)
t.Fatalf("response type = %T, want *tg.AccountWallPapers", got)
}
if wallpapers.Hash == 0 || len(wallpapers.Wallpapers) == 0 {
t.Fatalf("wallpapers = %+v, want stable default catalog", wallpapers)
@ -98,7 +86,7 @@ func TestAccountWallpaperSeedLookupAndAckRPCs(t *testing.T) {
if err != nil {
t.Fatalf("dispatch list: %v", err)
}
list := listGot.(*tg.AccountWallPapersBox).WallPapers.(*tg.AccountWallPapers)
list := listGot.(*tg.AccountWallPapers)
first := list.Wallpapers[0].(*tg.WallPaper)
input := &tg.InputWallPaper{ID: first.ID, AccessHash: first.AccessHash}
@ -110,13 +98,9 @@ func TestAccountWallpaperSeedLookupAndAckRPCs(t *testing.T) {
if err != nil {
t.Fatalf("dispatch getWallPaper: %v", err)
}
oneBox, ok := oneGot.(*tg.WallPaperBox)
oneWallpaper, ok := oneGot.(*tg.WallPaper)
if !ok {
t.Fatalf("getWallPaper = %T, want *tg.WallPaperBox", oneGot)
}
oneWallpaper, ok := oneBox.WallPaper.(*tg.WallPaper)
if !ok {
t.Fatalf("getWallPaper boxed = %T, want *tg.WallPaper", oneBox.WallPaper)
t.Fatalf("getWallPaper = %T, want *tg.WallPaper", oneGot)
}
if oneWallpaper.ID != first.ID {
t.Fatalf("getWallPaper id = %d, want %d", oneWallpaper.ID, first.ID)
@ -131,13 +115,12 @@ func TestAccountWallpaperSeedLookupAndAckRPCs(t *testing.T) {
if err != nil {
t.Fatalf("dispatch getWallPaper nofile: %v", err)
}
nofileBox, ok := nofileGot.(*tg.WallPaperBox)
nofileWallpaper, ok := nofileGot.(*tg.WallPaperNoFile)
if !ok {
t.Fatalf("getWallPaper nofile = %T, want *tg.WallPaperBox", nofileGot)
t.Fatalf("getWallPaper nofile = %T, want *tg.WallPaperNoFile", nofileGot)
}
nofileWallpaper, ok := nofileBox.WallPaper.(*tg.WallPaperNoFile)
if !ok || nofileWallpaper.ID != nofileInput.ID {
t.Fatalf("getWallPaper nofile boxed = %T %#v, want no-file id", nofileBox.WallPaper, nofileBox.WallPaper)
if nofileWallpaper.ID != nofileInput.ID {
t.Fatalf("getWallPaper nofile = %#v, want no-file id", nofileWallpaper)
}
var multiReq bin.Buffer
@ -152,7 +135,7 @@ func TestAccountWallpaperSeedLookupAndAckRPCs(t *testing.T) {
if err != nil {
t.Fatalf("dispatch getMultiWallPapers: %v", err)
}
if vector, ok := multiGot.(*tg.WallPaperClassVector); !ok || len(vector.Elems) != 3 {
if vector, ok := dispatchCanonicalValue(multiGot).([]tg.WallPaperClass); !ok || len(vector) != 3 {
t.Fatalf("getMultiWallPapers = %T %#v, want 3 wallpapers", multiGot, multiGot)
}
@ -171,12 +154,8 @@ func TestAccountWallpaperSeedLookupAndAckRPCs(t *testing.T) {
if err != nil {
t.Fatalf("dispatch %s: %v", name, err)
}
box, ok := got.(*tg.BoolBox)
if !ok {
t.Fatalf("%s = %T, want *tg.BoolBox", name, got)
}
if _, ok := box.Bool.(*tg.BoolTrue); !ok {
t.Fatalf("%s boxed = %T, want *tg.BoolTrue", name, box.Bool)
if value, ok := dispatchCanonicalValue(got).(bool); !ok || !value {
t.Fatalf("%s = %#v (%T), want true", name, dispatchCanonicalValue(got), got)
}
}
@ -201,13 +180,9 @@ func TestPaymentsGetStarGiftCollectionsNoServiceFallbackAndValidatesPeer(t *test
if err != nil {
t.Fatalf("dispatch: %v", err)
}
box, ok := got.(*tg.PaymentsStarGiftCollectionsBox)
collections, ok := got.(*tg.PaymentsStarGiftCollections)
if !ok {
t.Fatalf("response type = %T, want *tg.PaymentsStarGiftCollectionsBox", got)
}
collections, ok := box.StarGiftCollections.(*tg.PaymentsStarGiftCollections)
if !ok {
t.Fatalf("boxed response type = %T, want *tg.PaymentsStarGiftCollections", box.StarGiftCollections)
t.Fatalf("response type = %T, want *tg.PaymentsStarGiftCollections", got)
}
if len(collections.Collections) != 0 {
t.Fatalf("collections = %+v, want empty list", collections.Collections)

View file

@ -7,45 +7,97 @@ import (
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/compat/tdesktop"
"telesrv/internal/domain"
)
// registerPayments 注册 payments.* RPCStars 本地账本(余额/流水真实化)+ 其余
// gift/auction/revenue 第一阶段兼容桩。
func (r *Router) registerPayments(d *tg.ServerDispatcher) {
d.OnPaymentsGetStarsTopupOptions(func(ctx context.Context) ([]tg.StarsTopupOption, error) {
func (r *Router) registerPayments(d *tlprofile.Dispatcher) {
registerRPC[*tg.PaymentsGetStarsTopupOptionsRequest](d, tlprofile.SemanticMethodPaymentsGetStarsTopupOptions, func(ctx context.Context, layerRequest *tg.PaymentsGetStarsTopupOptionsRequest) (any,
// premium 订阅赠送 telesrv 不实现无支付流返回空选项。关键作用TDesktop 送礼框
// ShowStarGiftBox 的 ready() 门控要求 getPremiumGiftCodeOptions 成功返回(on_next)才置
// premiumGiftsReady=true否则整框不弹出——此前返 NOT_IMPLEMENTED 导致点生日礼物无反应。
// 空列表即解门星礼物正常发送premium 区段另由 userFull.disallow_premium_gifts=true 隐藏。
error) {
return devStarsTopupOptions(), nil
})
// premium 订阅赠送 telesrv 不实现无支付流返回空选项。关键作用TDesktop 送礼框
// ShowStarGiftBox 的 ready() 门控要求 getPremiumGiftCodeOptions 成功返回(on_next)才置
// premiumGiftsReady=true否则整框不弹出——此前返 NOT_IMPLEMENTED 导致点生日礼物无反应。
// 空列表即解门星礼物正常发送premium 区段另由 userFull.disallow_premium_gifts=true 隐藏。
d.OnPaymentsGetPremiumGiftCodeOptions(func(ctx context.Context, req *tg.PaymentsGetPremiumGiftCodeOptionsRequest) ([]tg.PremiumGiftCodeOption, error) {
registerRPC[*tg.PaymentsGetPremiumGiftCodeOptionsRequest](d, tlprofile.SemanticMethodPaymentsGetPremiumGiftCodeOptions, func(ctx context.Context, req *tg.PaymentsGetPremiumGiftCodeOptionsRequest) (any, error) {
return []tg.PremiumGiftCodeOption{}, nil
})
d.OnPaymentsGetStarsStatus(r.onPaymentsGetStarsStatus)
d.OnPaymentsGetStarsTransactions(r.onPaymentsGetStarsTransactions)
d.OnPaymentsGetStarGiftActiveAuctions(func(ctx context.Context, hash int64) (tg.PaymentsStarGiftActiveAuctionsClass, error) {
registerRPC[*tg.PaymentsGetStarsStatusRequest](d, tlprofile.SemanticMethodPaymentsGetStarsStatus, func(ctx context.Context, layerRequest *tg.PaymentsGetStarsStatusRequest) (any, error) {
return r.onPaymentsGetStarsStatus(ctx, layerRequest)
})
registerRPC[*tg.PaymentsGetStarsTransactionsRequest](d, tlprofile.SemanticMethodPaymentsGetStarsTransactions, func(ctx context.Context, layerRequest *tg.PaymentsGetStarsTransactionsRequest) (any, error) {
return r.onPaymentsGetStarsTransactions(ctx, layerRequest)
})
registerRPC[*tg.PaymentsGetStarGiftActiveAuctionsRequest](d, tlprofile.SemanticMethodPaymentsGetStarGiftActiveAuctions, func(ctx context.Context, layerRequest *tg.PaymentsGetStarGiftActiveAuctionsRequest) (any, error) {
hash := layerRequest.
Hash
_ = hash
return tdesktop.StarGiftActiveAuctions(), nil
})
d.OnPaymentsGetStarGifts(r.onPaymentsGetStarGifts)
d.OnPaymentsGetStarGiftUpgradePreview(r.onPaymentsGetStarGiftUpgradePreview)
d.OnPaymentsGetUniqueStarGift(r.onPaymentsGetUniqueStarGift)
d.OnPaymentsGetPaymentForm(r.onPaymentsGetPaymentForm)
d.OnPaymentsSendStarsForm(r.onPaymentsSendStarsForm)
d.OnPaymentsGetSavedStarGifts(r.onPaymentsGetSavedStarGifts)
d.OnPaymentsGetSavedStarGift(r.onPaymentsGetSavedStarGift)
d.OnPaymentsSaveStarGift(r.onPaymentsSaveStarGift)
d.OnPaymentsConvertStarGift(r.onPaymentsConvertStarGift)
d.OnPaymentsUpgradeStarGift(r.onPaymentsUpgradeStarGift)
d.OnPaymentsGetStarGiftCollections(r.onPaymentsGetStarGiftCollections)
d.OnPaymentsCreateStarGiftCollection(r.onPaymentsCreateStarGiftCollection)
d.OnPaymentsUpdateStarGiftCollection(r.onPaymentsUpdateStarGiftCollection)
d.OnPaymentsDeleteStarGiftCollection(r.onPaymentsDeleteStarGiftCollection)
d.OnPaymentsReorderStarGiftCollections(r.onPaymentsReorderStarGiftCollections)
d.OnPaymentsToggleStarGiftsPinnedToTop(r.onPaymentsToggleStarGiftsPinnedToTop)
d.OnPaymentsGetStarsRevenueAdsAccountURL(func(ctx context.Context, peer tg.InputPeerClass) (*tg.PaymentsStarsRevenueAdsAccountURL, error) {
registerRPC[*tg.PaymentsGetStarGiftsRequest](d, tlprofile.SemanticMethodPaymentsGetStarGifts, func(ctx context.Context, layerRequest *tg.PaymentsGetStarGiftsRequest) (any, error) {
return r.onPaymentsGetStarGifts(ctx, layerRequest.
Hash)
})
registerRPC[*tg.PaymentsGetStarGiftUpgradePreviewRequest](d, tlprofile.SemanticMethodPaymentsGetStarGiftUpgradePreview, func(ctx context.Context, layerRequest *tg.PaymentsGetStarGiftUpgradePreviewRequest) (any, error) {
return r.onPaymentsGetStarGiftUpgradePreview(ctx, layerRequest.
GiftID)
})
registerRPC[*tg.PaymentsGetUniqueStarGiftRequest](d, tlprofile.SemanticMethodPaymentsGetUniqueStarGift, func(ctx context.Context, layerRequest *tg.PaymentsGetUniqueStarGiftRequest) (any, error) {
return r.onPaymentsGetUniqueStarGift(ctx, layerRequest.
Slug)
})
registerRPC[*tg.PaymentsGetPaymentFormRequest](d, tlprofile.SemanticMethodPaymentsGetPaymentForm, func(ctx context.Context, layerRequest *tg.PaymentsGetPaymentFormRequest) (any, error) {
return r.onPaymentsGetPaymentForm(ctx, layerRequest)
})
registerRPC[*tg.PaymentsSendStarsFormRequest](d, tlprofile.SemanticMethodPaymentsSendStarsForm, func(ctx context.Context, layerRequest *tg.PaymentsSendStarsFormRequest) (any, error) {
return r.onPaymentsSendStarsForm(ctx, layerRequest)
})
registerRPC[*tg.PaymentsGetSavedStarGiftsRequest](d, tlprofile.SemanticMethodPaymentsGetSavedStarGifts, func(ctx context.Context, layerRequest *tg.PaymentsGetSavedStarGiftsRequest) (any, error) {
return r.onPaymentsGetSavedStarGifts(ctx, layerRequest)
})
registerRPC[*tg.PaymentsGetSavedStarGiftRequest](d, tlprofile.SemanticMethodPaymentsGetSavedStarGift, func(ctx context.Context, layerRequest *tg.PaymentsGetSavedStarGiftRequest) (any, error) {
return r.onPaymentsGetSavedStarGift(ctx, layerRequest.
Stargift)
})
registerRPC[*tg.PaymentsSaveStarGiftRequest](d, tlprofile.SemanticMethodPaymentsSaveStarGift, func(ctx context.Context, layerRequest *tg.PaymentsSaveStarGiftRequest) (any, error) {
return r.onPaymentsSaveStarGift(ctx, layerRequest)
})
registerRPC[*tg.PaymentsConvertStarGiftRequest](d, tlprofile.SemanticMethodPaymentsConvertStarGift, func(ctx context.Context, layerRequest *tg.PaymentsConvertStarGiftRequest) (any, error) {
return r.onPaymentsConvertStarGift(ctx, layerRequest.
Stargift)
})
registerRPC[*tg.PaymentsUpgradeStarGiftRequest](d, tlprofile.SemanticMethodPaymentsUpgradeStarGift, func(ctx context.Context, layerRequest *tg.PaymentsUpgradeStarGiftRequest) (any, error) {
return r.onPaymentsUpgradeStarGift(ctx, layerRequest)
})
registerRPC[*tg.PaymentsGetStarGiftCollectionsRequest](d, tlprofile.SemanticMethodPaymentsGetStarGiftCollections, func(ctx context.Context, layerRequest *tg.PaymentsGetStarGiftCollectionsRequest) (any, error) {
return r.onPaymentsGetStarGiftCollections(ctx, layerRequest)
})
registerRPC[*tg.PaymentsCreateStarGiftCollectionRequest](d, tlprofile.SemanticMethodPaymentsCreateStarGiftCollection, func(ctx context.Context, layerRequest *tg.PaymentsCreateStarGiftCollectionRequest) (any, error) {
return r.onPaymentsCreateStarGiftCollection(ctx, layerRequest)
})
registerRPC[*tg.PaymentsUpdateStarGiftCollectionRequest](d, tlprofile.SemanticMethodPaymentsUpdateStarGiftCollection, func(ctx context.Context, layerRequest *tg.PaymentsUpdateStarGiftCollectionRequest) (any, error) {
return r.onPaymentsUpdateStarGiftCollection(ctx, layerRequest)
})
registerRPC[*tg.PaymentsDeleteStarGiftCollectionRequest](d, tlprofile.SemanticMethodPaymentsDeleteStarGiftCollection, func(ctx context.Context, layerRequest *tg.PaymentsDeleteStarGiftCollectionRequest) (any, error) {
return r.onPaymentsDeleteStarGiftCollection(ctx, layerRequest)
})
registerRPC[*tg.PaymentsReorderStarGiftCollectionsRequest](d, tlprofile.SemanticMethodPaymentsReorderStarGiftCollections, func(ctx context.Context, layerRequest *tg.PaymentsReorderStarGiftCollectionsRequest) (any, error) {
return r.onPaymentsReorderStarGiftCollections(ctx, layerRequest)
})
registerRPC[*tg.PaymentsToggleStarGiftsPinnedToTopRequest](d, tlprofile.SemanticMethodPaymentsToggleStarGiftsPinnedToTop, func(ctx context.Context, layerRequest *tg.PaymentsToggleStarGiftsPinnedToTopRequest) (any, error) {
return r.onPaymentsToggleStarGiftsPinnedToTop(ctx, layerRequest)
})
registerRPC[*tg.PaymentsGetStarsRevenueAdsAccountURLRequest](d, tlprofile.SemanticMethodPaymentsGetStarsRevenueAdsAccountURL, func(ctx context.Context, layerRequest *tg.PaymentsGetStarsRevenueAdsAccountURLRequest) (any, error) {
peer := layerRequest.
Peer
_ = peer
userID, _, err := r.currentUserID(ctx)
if err != nil {
return nil, internalErr()
@ -55,7 +107,7 @@ func (r *Router) registerPayments(d *tg.ServerDispatcher) {
}
return &tg.PaymentsStarsRevenueAdsAccountURL{URL: "https://ads.telegram.org/"}, nil
})
d.OnPaymentsGetStarsRevenueStats(func(ctx context.Context, req *tg.PaymentsGetStarsRevenueStatsRequest) (*tg.PaymentsStarsRevenueStats, error) {
registerRPC[*tg.PaymentsGetStarsRevenueStatsRequest](d, tlprofile.SemanticMethodPaymentsGetStarsRevenueStats, func(ctx context.Context, req *tg.PaymentsGetStarsRevenueStatsRequest) (any, error) {
userID, _, err := r.currentUserID(ctx)
if err != nil {
return nil, internalErr()
@ -68,6 +120,7 @@ func (r *Router) registerPayments(d *tg.ServerDispatcher) {
}
return tdesktop.StarsRevenueStats(req.GetTon()), nil
})
}
// onPaymentsGetStarsStatus 返回当前账号的 Stars 余额(首读时惰性授予起始余额)。

View file

@ -10,6 +10,7 @@ import (
"github.com/iamxvbaba/td/tgerr"
"go.uber.org/zap/zaptest"
"github.com/iamxvbaba/td/tlprofile"
appchannels "telesrv/internal/app/channels"
appmessages "telesrv/internal/app/messages"
appstargifts "telesrv/internal/app/stargifts"
@ -107,16 +108,20 @@ func TestSavedStarGiftProjectionCombinesHistoricalCatalogWithCurrentCollectibleA
if upgradeStars, ok := gift.GetUpgradeStars(); !ok || upgradeStars != 75 {
t.Fatalf("upgrade_stars = %d ok=%v, want current price 75", upgradeStars, ok)
}
for _, profile := range []tg.LayerProfile{tg.LayerProfile227, tg.LayerProfile228} {
for _, profile := range []tlprofile.Profile{tlprofile.Profile227, tlprofile.Profile228} {
wire := &tg.PaymentsSavedStarGifts{Count: 1, Gifts: projected, Chats: []tg.ChatClass{}, Users: []tg.UserClass{}}
encoded := &bin.Buffer{}
if err := tg.EncodeLayer(profile, tg.LayerConstructorPaymentsSavedStarGiftsType(), wire, encoded); err != nil {
if err := tlprofile.EncodeObject(profile, wire, encoded); err != nil {
t.Fatalf("encode Layer %d saved gift: %v", profile, err)
}
decoded, err := tg.DecodeLayer(profile, tg.LayerConstructorPaymentsSavedStarGiftsType(), &bin.Buffer{Buf: encoded.Buf})
decodedObject, err := tlprofile.DecodeObject(profile, &bin.Buffer{Buf: encoded.Buf}, tlprofile.Limits{})
if err != nil {
t.Fatalf("decode Layer %d saved gift: %v", profile, err)
}
decoded, ok := decodedObject.(*tg.PaymentsSavedStarGifts)
if !ok {
t.Fatalf("decode Layer %d saved gift type = %T", profile, decodedObject)
}
inner, ok := decoded.Gifts[0].Gift.(*tg.StarGift)
if !ok || !decoded.Gifts[0].CanUpgrade || inner.UpgradeStars != 75 {
t.Fatalf("Layer %d projection lost upgrade flags: %#v", profile, decoded.Gifts[0])
@ -219,28 +224,36 @@ func TestStarGiftCollectiblePreviewUpgradeFormUniqueAndServiceProjection(t *test
} else if user, ok := peer.(*tg.PeerUser); !ok || user.UserID != owner.ID {
t.Fatalf("unique service action peer = %#v", peer)
}
for _, profile := range []tg.LayerProfile{tg.LayerProfile227, tg.LayerProfile228} {
for _, profile := range []tlprofile.Profile{tlprofile.Profile227, tlprofile.Profile228} {
responseWire := &bin.Buffer{}
if err := tg.EncodeLayer(profile, tg.LayerConstructorPaymentsUniqueStarGiftType(), uniqueResponse, responseWire); err != nil {
if err := tlprofile.EncodeObject(profile, uniqueResponse, responseWire); err != nil {
t.Fatalf("encode Layer %d unique response: %v", profile, err)
}
decodedResponse, err := tg.DecodeLayer(profile, tg.LayerConstructorPaymentsUniqueStarGiftType(), &bin.Buffer{Buf: responseWire.Buf})
decodedResponseObject, err := tlprofile.DecodeObject(profile, &bin.Buffer{Buf: responseWire.Buf}, tlprofile.Limits{})
if err != nil {
t.Fatalf("decode Layer %d unique response: %v", profile, err)
}
decodedResponse, ok := decodedResponseObject.(*tg.PaymentsUniqueStarGift)
if !ok {
t.Fatalf("decode Layer %d unique response type = %T", profile, decodedResponseObject)
}
decodedGift, ok := decodedResponse.Gift.(*tg.StarGiftUnique)
if !ok || decodedGift.Slug != unique.Slug || len(decodedGift.Attributes) != 4 {
t.Fatalf("Layer %d unique response lost fields: %#v", profile, decodedResponse.Gift)
}
actionWire := &bin.Buffer{}
if err := tg.EncodeLayer(profile, tg.LayerConstructorMessageActionStarGiftUniqueType(), action, actionWire); err != nil {
if err := tlprofile.EncodeObject(profile, action, actionWire); err != nil {
t.Fatalf("encode Layer %d unique action: %v", profile, err)
}
decodedAction, err := tg.DecodeLayer(profile, tg.LayerConstructorMessageActionStarGiftUniqueType(), &bin.Buffer{Buf: actionWire.Buf})
decodedActionObject, err := tlprofile.DecodeObject(profile, &bin.Buffer{Buf: actionWire.Buf}, tlprofile.Limits{})
if err != nil {
t.Fatalf("decode Layer %d unique action: %v", profile, err)
}
decodedAction, ok := decodedActionObject.(*tg.MessageActionStarGiftUnique)
if !ok {
t.Fatalf("decode Layer %d unique action type = %T", profile, decodedActionObject)
}
if decodedActionGift, ok := decodedAction.Gift.(*tg.StarGiftUnique); !ok || !decodedAction.Upgrade || decodedActionGift.Slug != unique.Slug {
t.Fatalf("Layer %d unique action lost fields: %#v", profile, decodedAction)
}

View file

@ -4,55 +4,130 @@ import (
"context"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
)
// registerPhone 注册通话域 RPC。
//
// 归属约定(跨任务协调,群聊 M0 落地时遵守):本文件是 phone.* 的唯一注册点;
// gotd ServerDispatcher 对同一 RPC 重复 On* 注册是静默 last-wins,群聊 stub
// tlprofile.Dispatcher 对同一 semantic method 重复注册会失败,群聊 stub
// 清单不得覆盖此处已注册的真实现。messages.getDhConfig 属通话域DH 参数下发),
// 注册在这里而非 messages_register.go。
func (r *Router) registerPhone(d *tg.ServerDispatcher) {
d.OnMessagesGetDhConfig(r.onMessagesGetDhConfig)
func (r *Router) registerPhone(d *tlprofile.Dispatcher) {
registerRPC[*tg.MessagesGetDhConfigRequest](d, tlprofile.SemanticMethodMessagesGetDhConfig, func(ctx context.Context, layerRequest *tg.MessagesGetDhConfigRequest) (any, error) {
return r.onMessagesGetDhConfig(ctx, layerRequest)
})
registerRPC[*tg.PhoneRequestCallRequest](d, tlprofile.SemanticMethodPhoneRequestCall, func(ctx context.Context, layerRequest *tg.PhoneRequestCallRequest) (any, error) {
return r.onPhoneRequestCall(ctx, layerRequest)
})
registerRPC[*tg.PhoneReceivedCallRequest](d, tlprofile.SemanticMethodPhoneReceivedCall, func(ctx context.Context, layerRequest *tg.PhoneReceivedCallRequest) (any, error) {
return r.onPhoneReceivedCall(ctx, layerRequest.
Peer)
})
registerRPC[*tg.PhoneAcceptCallRequest](d, tlprofile.SemanticMethodPhoneAcceptCall, func(ctx context.Context, layerRequest *tg.PhoneAcceptCallRequest) (any, error) {
return r.onPhoneAcceptCall(ctx, layerRequest)
})
registerRPC[*tg.PhoneConfirmCallRequest](d, tlprofile.SemanticMethodPhoneConfirmCall, func(ctx context.Context, layerRequest *tg.PhoneConfirmCallRequest) (any, error) {
return r.onPhoneConfirmCall(ctx, layerRequest)
})
registerRPC[*tg.PhoneDiscardCallRequest](d, tlprofile.SemanticMethodPhoneDiscardCall, func(ctx context.Context, layerRequest *tg.PhoneDiscardCallRequest) (any, error) {
return r.onPhoneDiscardCall(ctx, layerRequest)
})
registerRPC[*tg.PhoneSendSignalingDataRequest](d, tlprofile.SemanticMethodPhoneSendSignalingData, func(ctx context.Context, layerRequest *tg.PhoneSendSignalingDataRequest) (any, error) {
return r.onPhoneSendSignalingData(ctx, layerRequest)
})
registerRPC[*tg.PhoneSetCallRatingRequest](d, tlprofile.SemanticMethodPhoneSetCallRating, func(ctx context.Context, layerRequest *tg.PhoneSetCallRatingRequest) (any, error) {
return r.onPhoneSetCallRating(ctx, layerRequest)
})
registerRPC[*tg.PhoneSaveCallDebugRequest](d, tlprofile.SemanticMethodPhoneSaveCallDebug, func(ctx context.Context, layerRequest *tg.PhoneSaveCallDebugRequest) (any, error) {
return r.onPhoneSaveCallDebug(ctx, layerRequest)
})
registerRPC[*tg.PhoneGetCallConfigRequest](d, tlprofile.SemanticMethodPhoneGetCallConfig, func(ctx context.Context, layerRequest *
// tgcalls 对空配置走默认值需要精调audio_max_bitrate 等)时再填键值。
tg.PhoneGetCallConfigRequest) (any, error) {
d.OnPhoneRequestCall(r.onPhoneRequestCall)
d.OnPhoneReceivedCall(r.onPhoneReceivedCall)
d.OnPhoneAcceptCall(r.onPhoneAcceptCall)
d.OnPhoneConfirmCall(r.onPhoneConfirmCall)
d.OnPhoneDiscardCall(r.onPhoneDiscardCall)
d.OnPhoneSendSignalingData(r.onPhoneSendSignalingData)
d.OnPhoneSetCallRating(r.onPhoneSetCallRating)
d.OnPhoneSaveCallDebug(r.onPhoneSaveCallDebug)
d.OnPhoneGetCallConfig(func(ctx context.Context) (*tg.DataJSON, error) {
// tgcalls 对空配置走默认值需要精调audio_max_bitrate 等)时再填键值。
return &tg.DataJSON{Data: "{}"}, nil
})
registerRPC[
// 超级群语音聊天group call
d.OnPhoneCreateGroupCall(r.onPhoneCreateGroupCall)
d.OnPhoneJoinGroupCall(r.onPhoneJoinGroupCall)
d.OnPhoneLeaveGroupCall(r.onPhoneLeaveGroupCall)
d.OnPhoneDiscardGroupCall(r.onPhoneDiscardGroupCall)
d.OnPhoneGetGroupCall(r.onPhoneGetGroupCall)
d.OnPhoneGetGroupParticipants(r.onPhoneGetGroupParticipants)
d.OnPhoneCheckGroupCall(r.onPhoneCheckGroupCall)
d.OnPhoneExportGroupCallInvite(r.onPhoneExportGroupCallInvite)
d.OnPhoneEditGroupCallParticipant(r.onPhoneEditGroupCallParticipant)
d.OnPhoneEditGroupCallTitle(r.onPhoneEditGroupCallTitle)
d.OnPhoneToggleGroupCallSettings(r.onPhoneToggleGroupCallSettings)
d.OnPhoneInviteToGroupCall(r.onPhoneInviteToGroupCall)
// 定时通话scheduled video chat
d.OnPhoneStartScheduledGroupCall(r.onPhoneStartScheduledGroupCall)
d.OnPhoneToggleGroupCallStartSubscription(r.onPhoneToggleGroupCallStartSubscription)
// Ad-hoc E2E conference callP2P 通话升级/拉人路径)。
d.OnPhoneCreateConferenceCall(r.onPhoneCreateConferenceCall)
d.OnPhoneInviteConferenceCallParticipant(r.onPhoneInviteConferenceCallParticipant)
d.OnPhoneDeleteConferenceCallParticipants(r.onPhoneDeleteConferenceCallParticipants)
d.OnPhoneSendConferenceCallBroadcast(r.onPhoneSendConferenceCallBroadcast)
d.OnPhoneDeclineConferenceCallInvite(r.onPhoneDeclineConferenceCallInvite)
d.OnPhoneGetGroupCallChainBlocks(r.onPhoneGetGroupCallChainBlocks)
// 屏幕共享M4同参与者第二媒体连接。
d.OnPhoneJoinGroupCallPresentation(r.onPhoneJoinGroupCallPresentation)
d.OnPhoneLeaveGroupCallPresentation(r.onPhoneLeaveGroupCallPresentation)
*tg.PhoneCreateGroupCallRequest](d, tlprofile.SemanticMethodPhoneCreateGroupCall, func(ctx context.Context, layerRequest *tg.PhoneCreateGroupCallRequest) (any, error) {
return r.onPhoneCreateGroupCall(ctx, layerRequest)
})
registerRPC[*tg.PhoneJoinGroupCallRequest](d, tlprofile.SemanticMethodPhoneJoinGroupCall, func(ctx context.Context, layerRequest *tg.PhoneJoinGroupCallRequest) (any, error) {
return r.onPhoneJoinGroupCall(ctx, layerRequest)
})
registerRPC[*tg.PhoneLeaveGroupCallRequest](d, tlprofile.SemanticMethodPhoneLeaveGroupCall, func(ctx context.Context, layerRequest *tg.PhoneLeaveGroupCallRequest) (any, error) {
return r.onPhoneLeaveGroupCall(ctx, layerRequest)
})
registerRPC[*tg.PhoneDiscardGroupCallRequest](d, tlprofile.SemanticMethodPhoneDiscardGroupCall, func(ctx context.Context, layerRequest *tg.PhoneDiscardGroupCallRequest) (any, error) {
return r.onPhoneDiscardGroupCall(ctx, layerRequest.
Call)
})
registerRPC[*tg.PhoneGetGroupCallRequest](d, tlprofile.SemanticMethodPhoneGetGroupCall, func(ctx context.Context, layerRequest *tg.PhoneGetGroupCallRequest) (any, error) {
return r.onPhoneGetGroupCall(ctx, layerRequest)
})
registerRPC[*tg.PhoneGetGroupParticipantsRequest](d, tlprofile.SemanticMethodPhoneGetGroupParticipants, func(ctx context.Context, layerRequest *tg.PhoneGetGroupParticipantsRequest) (any, error) {
return r.onPhoneGetGroupParticipants(ctx, layerRequest)
})
registerRPC[*tg.PhoneCheckGroupCallRequest](d, tlprofile.SemanticMethodPhoneCheckGroupCall, func(ctx context.Context, layerRequest *tg.PhoneCheckGroupCallRequest) (any, error) {
return r.onPhoneCheckGroupCall(ctx, layerRequest)
})
registerRPC[*tg.PhoneExportGroupCallInviteRequest](d, tlprofile.SemanticMethodPhoneExportGroupCallInvite, func(ctx context.Context, layerRequest *tg.PhoneExportGroupCallInviteRequest) (any, error) {
return r.onPhoneExportGroupCallInvite(ctx, layerRequest)
})
registerRPC[*tg.PhoneEditGroupCallParticipantRequest](d, tlprofile.SemanticMethodPhoneEditGroupCallParticipant, func(ctx context.Context, layerRequest *tg.PhoneEditGroupCallParticipantRequest) (any, error) {
return r.onPhoneEditGroupCallParticipant(ctx, layerRequest)
})
registerRPC[*tg.PhoneEditGroupCallTitleRequest](d, tlprofile.SemanticMethodPhoneEditGroupCallTitle, func(ctx context.Context, layerRequest *tg.PhoneEditGroupCallTitleRequest) (any, error) {
return r.onPhoneEditGroupCallTitle(ctx, layerRequest)
})
registerRPC[*tg.PhoneToggleGroupCallSettingsRequest](d, tlprofile.SemanticMethodPhoneToggleGroupCallSettings, func(ctx context.Context, layerRequest *tg.PhoneToggleGroupCallSettingsRequest) (
// 定时通话scheduled video chat
any, error) {
return r.onPhoneToggleGroupCallSettings(ctx, layerRequest)
})
registerRPC[*tg.PhoneInviteToGroupCallRequest](d, tlprofile.SemanticMethodPhoneInviteToGroupCall, func(ctx context.Context, layerRequest *tg.PhoneInviteToGroupCallRequest) (any, error) {
return r.onPhoneInviteToGroupCall(ctx, layerRequest)
})
registerRPC[*tg.PhoneStartScheduledGroupCallRequest](d, tlprofile.SemanticMethodPhoneStartScheduledGroupCall, func(ctx context.Context, layerRequest *tg.PhoneStartScheduledGroupCallRequest) (any, error) {
return r.onPhoneStartScheduledGroupCall(ctx, layerRequest.
Call)
})
registerRPC[*tg.PhoneToggleGroupCallStartSubscriptionRequest](d, tlprofile.SemanticMethodPhoneToggleGroupCallStartSubscription, func(ctx context.Context,
// Ad-hoc E2E conference callP2P 通话升级/拉人路径)。
layerRequest *tg.PhoneToggleGroupCallStartSubscriptionRequest) (any, error) {
return r.onPhoneToggleGroupCallStartSubscription(ctx, layerRequest)
})
registerRPC[*tg.PhoneCreateConferenceCallRequest](d, tlprofile.SemanticMethodPhoneCreateConferenceCall, func(ctx context.Context, layerRequest *tg.PhoneCreateConferenceCallRequest) (any, error) {
return r.onPhoneCreateConferenceCall(ctx, layerRequest)
})
registerRPC[*tg.PhoneInviteConferenceCallParticipantRequest](d, tlprofile.SemanticMethodPhoneInviteConferenceCallParticipant, func(ctx context.Context, layerRequest *tg.PhoneInviteConferenceCallParticipantRequest) (any, error) {
return r.onPhoneInviteConferenceCallParticipant(ctx, layerRequest)
})
registerRPC[*tg.PhoneDeleteConferenceCallParticipantsRequest](d, tlprofile.SemanticMethodPhoneDeleteConferenceCallParticipants, func(ctx context.Context, layerRequest *tg.PhoneDeleteConferenceCallParticipantsRequest) (any, error) {
return r.onPhoneDeleteConferenceCallParticipants(ctx, layerRequest)
})
registerRPC[*tg.PhoneSendConferenceCallBroadcastRequest](d, tlprofile.SemanticMethodPhoneSendConferenceCallBroadcast, func(ctx context.Context, layerRequest *tg.PhoneSendConferenceCallBroadcastRequest) (any, error) {
return r.onPhoneSendConferenceCallBroadcast(ctx, layerRequest)
})
registerRPC[*tg.PhoneDeclineConferenceCallInviteRequest](d, tlprofile.SemanticMethodPhoneDeclineConferenceCallInvite, func(ctx context.Context, layerRequest *tg.PhoneDeclineConferenceCallInviteRequest) (any, error) {
// 屏幕共享M4同参与者第二媒体连接。
return r.onPhoneDeclineConferenceCallInvite(ctx, layerRequest.
MsgID)
})
registerRPC[*tg.PhoneGetGroupCallChainBlocksRequest](d, tlprofile.SemanticMethodPhoneGetGroupCallChainBlocks, func(ctx context.Context, layerRequest *tg.PhoneGetGroupCallChainBlocksRequest) (any, error) {
return r.onPhoneGetGroupCallChainBlocks(ctx, layerRequest)
})
registerRPC[*tg.PhoneJoinGroupCallPresentationRequest](d, tlprofile.SemanticMethodPhoneJoinGroupCallPresentation, func(ctx context.Context, layerRequest *tg.PhoneJoinGroupCallPresentationRequest) (any, error) {
return r.onPhoneJoinGroupCallPresentation(ctx, layerRequest)
})
registerRPC[*tg.PhoneLeaveGroupCallPresentationRequest](d, tlprofile.SemanticMethodPhoneLeaveGroupCallPresentation, func(ctx context.Context, layerRequest *tg.PhoneLeaveGroupCallPresentationRequest) (any, error) {
return r.onPhoneLeaveGroupCallPresentation(ctx, layerRequest.
Call)
})
r.registerPhoneStubs(d)
}

View file

@ -4,23 +4,38 @@ import (
"context"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
)
// 群通话范围外入口的被动 stub防点崩。未列出的 phone.*conference 族 /
// 通话内消息族 / scheduled / RTMP走 router fallback400/500 NOT_IMPLEMENTED +
// 兼容矩阵日志,客户端不断连。
func (r *Router) registerPhoneStubs(d *tg.ServerDispatcher) {
func (r *Router) registerPhoneStubs(d *tlprofile.Dispatcher) {
registerRPC[
// 入会身份候选:真实实现见 phone_group_call.goself + admin 的频道身份)。
d.OnPhoneGetGroupCallJoinAs(r.onPhoneGetGroupCallJoinAs)
// default join-as 偏好持久化仍是 stubchatFull.groupcall_default_join_as 不回填)。
d.OnPhoneSaveDefaultGroupCallJoinAs(func(ctx context.Context, req *tg.PhoneSaveDefaultGroupCallJoinAsRequest) (bool, error) {
*tg.PhoneGetGroupCallJoinAsRequest](d, tlprofile.SemanticMethodPhoneGetGroupCallJoinAs, func(ctx context.
// default join-as 偏好持久化仍是 stubchatFull.groupcall_default_join_as 不回填)。
Context, layerRequest *tg.PhoneGetGroupCallJoinAsRequest) (any, error) {
return r.onPhoneGetGroupCallJoinAs(ctx, layerRequest.
Peer)
})
registerRPC[*tg.PhoneSaveDefaultGroupCallJoinAsRequest](d, tlprofile.SemanticMethodPhoneSaveDefaultGroupCallJoinAs, func(ctx context.Context, req *tg.PhoneSaveDefaultGroupCallJoinAsRequest) (any, error) {
return true, nil
})
registerRPC[
// 录制范围外:客户端只看 record_start_date恒不下发打发掉即可。
d.OnPhoneToggleGroupCallRecord(func(ctx context.Context, req *tg.PhoneToggleGroupCallRecordRequest) (tg.UpdatesClass, error) {
*tg.PhoneToggleGroupCallRecordRequest](d, tlprofile.SemanticMethodPhoneToggleGroupCallRecord, func(ctx context.Context, req *tg.PhoneToggleGroupCallRecordRequest) (any, error) {
return tgEmptyUpdates(int(r.clock.Now().Unix())), nil
})
registerRPC[
// RTMP 直播Live Stream真实 handler 见 phone_group_call_rtmp.go。
d.OnPhoneGetGroupCallStreamChannels(r.onPhoneGetGroupCallStreamChannels)
d.OnPhoneGetGroupCallStreamRtmpURL(r.onPhoneGetGroupCallStreamRtmpURL)
*tg.PhoneGetGroupCallStreamChannelsRequest](d, tlprofile.SemanticMethodPhoneGetGroupCallStreamChannels, func(ctx context.Context, layerRequest *tg.PhoneGetGroupCallStreamChannelsRequest) (any, error) {
return r.onPhoneGetGroupCallStreamChannels(ctx, layerRequest.
Call)
})
registerRPC[*tg.PhoneGetGroupCallStreamRtmpURLRequest](d, tlprofile.SemanticMethodPhoneGetGroupCallStreamRtmpURL, func(ctx context.Context, layerRequest *tg.PhoneGetGroupCallStreamRtmpURLRequest) (any, error) {
return r.onPhoneGetGroupCallStreamRtmpURL(ctx, layerRequest)
})
}

View file

@ -9,16 +9,29 @@ import (
"github.com/iamxvbaba/td/tg"
"go.uber.org/zap"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/domain"
)
// registerPhotos 注册 photos.* RPC handler头像上传 / 切换 / 查询 / 删除)。
func (r *Router) registerPhotos(d *tg.ServerDispatcher) {
d.OnPhotosUploadProfilePhoto(r.onPhotosUploadProfilePhoto)
d.OnPhotosUpdateProfilePhoto(r.onPhotosUpdateProfilePhoto)
d.OnPhotosUploadContactProfilePhoto(r.onPhotosUploadContactProfilePhoto)
d.OnPhotosGetUserPhotos(r.onPhotosGetUserPhotos)
d.OnPhotosDeletePhotos(r.onPhotosDeletePhotos)
func (r *Router) registerPhotos(d *tlprofile.Dispatcher) {
registerRPC[*tg.PhotosUploadProfilePhotoRequest](d, tlprofile.SemanticMethodPhotosUploadProfilePhoto, func(ctx context.Context, layerRequest *tg.PhotosUploadProfilePhotoRequest) (any, error) {
return r.onPhotosUploadProfilePhoto(ctx, layerRequest)
})
registerRPC[*tg.PhotosUpdateProfilePhotoRequest](d, tlprofile.SemanticMethodPhotosUpdateProfilePhoto, func(ctx context.Context, layerRequest *tg.PhotosUpdateProfilePhotoRequest) (any, error) {
return r.onPhotosUpdateProfilePhoto(ctx, layerRequest)
})
registerRPC[*tg.PhotosUploadContactProfilePhotoRequest](d, tlprofile.SemanticMethodPhotosUploadContactProfilePhoto, func(ctx context.Context, layerRequest *tg.PhotosUploadContactProfilePhotoRequest) (any, error) {
return r.onPhotosUploadContactProfilePhoto(ctx, layerRequest)
})
registerRPC[*tg.PhotosGetUserPhotosRequest](d, tlprofile.SemanticMethodPhotosGetUserPhotos, func(ctx context.Context, layerRequest *tg.PhotosGetUserPhotosRequest) (any, error) {
return r.onPhotosGetUserPhotos(ctx, layerRequest)
})
registerRPC[*tg.PhotosDeletePhotosRequest](d, tlprofile.SemanticMethodPhotosDeletePhotos, func(ctx context.Context, layerRequest *tg.PhotosDeletePhotosRequest) (any, error) {
return r.onPhotosDeletePhotos(ctx, layerRequest.
ID)
})
}
func (r *Router) onPhotosUploadProfilePhoto(ctx context.Context, req *tg.PhotosUploadProfilePhotoRequest) (*tg.PhotosPhoto, error) {

View file

@ -8,15 +8,27 @@ import (
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/domain"
)
func (r *Router) registerPremium(d *tg.ServerDispatcher) {
d.OnPremiumGetBoostsStatus(r.onPremiumGetBoostsStatus)
d.OnPremiumGetBoostsList(r.onPremiumGetBoostsList)
d.OnPremiumGetMyBoosts(r.onPremiumGetMyBoosts)
d.OnPremiumApplyBoost(r.onPremiumApplyBoost)
d.OnPremiumGetUserBoosts(r.onPremiumGetUserBoosts)
func (r *Router) registerPremium(d *tlprofile.Dispatcher) {
registerRPC[*tg.PremiumGetBoostsStatusRequest](d, tlprofile.SemanticMethodPremiumGetBoostsStatus, func(ctx context.Context, layerRequest *tg.PremiumGetBoostsStatusRequest) (any, error) {
return r.onPremiumGetBoostsStatus(ctx, layerRequest.
Peer)
})
registerRPC[*tg.PremiumGetBoostsListRequest](d, tlprofile.SemanticMethodPremiumGetBoostsList, func(ctx context.Context, layerRequest *tg.PremiumGetBoostsListRequest) (any, error) {
return r.onPremiumGetBoostsList(ctx, layerRequest)
})
registerRPC[*tg.PremiumGetMyBoostsRequest](d, tlprofile.SemanticMethodPremiumGetMyBoosts, func(ctx context.Context, layerRequest *tg.PremiumGetMyBoostsRequest) (any, error) {
return r.onPremiumGetMyBoosts(ctx)
})
registerRPC[*tg.PremiumApplyBoostRequest](d, tlprofile.SemanticMethodPremiumApplyBoost, func(ctx context.Context, layerRequest *tg.PremiumApplyBoostRequest) (any, error) {
return r.onPremiumApplyBoost(ctx, layerRequest)
})
registerRPC[*tg.PremiumGetUserBoostsRequest](d, tlprofile.SemanticMethodPremiumGetUserBoosts, func(ctx context.Context, layerRequest *tg.PremiumGetUserBoostsRequest) (any, error) {
return r.onPremiumGetUserBoosts(ctx, layerRequest)
})
}
func (r *Router) onPremiumGetBoostsStatus(ctx context.Context, peer tg.InputPeerClass) (*tg.PremiumBoostsStatus, error) {

View file

@ -7,6 +7,7 @@ import (
"github.com/iamxvbaba/td/bin"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
appfiles "telesrv/internal/app/files"
"telesrv/internal/domain"
)
@ -72,33 +73,33 @@ var requestVectorPolicies = map[uint32]requestVectorPolicy{
// dispatcher construction fail closed until an explicit conversion policy is
// supplied.
var layerRPCVectorPolicies = []struct {
fieldID tg.LayerRPCFieldID
fieldID tlprofile.FieldID
max int
tooLong func() error
}{
{tg.LayerRPCFieldUsersGetUsersID, 100, inputRequestTooLongErr},
{tg.LayerRPCFieldUsersGetRequirementsToContactID, maxRequirementsToContactUsers, limitInvalidErr},
{tg.LayerRPCFieldContactsImportContactsContacts, maxContactImportBatch, limitInvalidErr},
{tg.LayerRPCFieldContactsDeleteContactsID, maxContactDeleteBatch, limitInvalidErr},
{tg.LayerRPCFieldContactsEditCloseFriendsID, maxCloseFriendsCount, limitInvalidErr},
{tg.LayerRPCFieldContactsSetBlockedID, maxContactSetBlocked, limitInvalidErr},
{tg.LayerRPCFieldMessagesGetMessagesID, maxGetMessagesIDs, limitInvalidErr},
{tg.LayerRPCFieldMessagesGetChatsID, maxGetMessagesIDs, limitInvalidErr},
{tg.LayerRPCFieldMessagesGetPeerDialogsPeers, maxDialogInputPeers, limitInvalidErr},
{tg.LayerRPCFieldMessagesReadMessageContentsID, maxGetMessagesIDs, limitInvalidErr},
{tg.LayerRPCFieldMessagesGetCustomEmojiDocumentsDocumentID, maxEmojiDocuments, limitInvalidErr},
{tg.LayerRPCFieldMessagesDeleteMessagesID, domain.MaxDeleteMessageIDs, limitInvalidErr},
{tg.LayerRPCFieldMessagesCreateChatUsers, 200, limitInvalidErr},
{tg.LayerRPCFieldChannelsGetChannelsID, maxGetMessagesIDs, limitInvalidErr},
{tlprofile.FieldUsersGetUsersID, 100, inputRequestTooLongErr},
{tlprofile.FieldUsersGetRequirementsToContactID, maxRequirementsToContactUsers, limitInvalidErr},
{tlprofile.FieldContactsImportContactsContacts, maxContactImportBatch, limitInvalidErr},
{tlprofile.FieldContactsDeleteContactsID, maxContactDeleteBatch, limitInvalidErr},
{tlprofile.FieldContactsEditCloseFriendsID, maxCloseFriendsCount, limitInvalidErr},
{tlprofile.FieldContactsSetBlockedID, maxContactSetBlocked, limitInvalidErr},
{tlprofile.FieldMessagesGetMessagesID, maxGetMessagesIDs, limitInvalidErr},
{tlprofile.FieldMessagesGetChatsID, maxGetMessagesIDs, limitInvalidErr},
{tlprofile.FieldMessagesGetPeerDialogsPeers, maxDialogInputPeers, limitInvalidErr},
{tlprofile.FieldMessagesReadMessageContentsID, maxGetMessagesIDs, limitInvalidErr},
{tlprofile.FieldMessagesGetCustomEmojiDocumentsDocumentID, maxEmojiDocuments, limitInvalidErr},
{tlprofile.FieldMessagesDeleteMessagesID, domain.MaxDeleteMessageIDs, limitInvalidErr},
{tlprofile.FieldMessagesCreateChatUsers, 200, limitInvalidErr},
{tlprofile.FieldChannelsGetChannelsID, maxGetMessagesIDs, limitInvalidErr},
}
func registerLayerRPCAdmissionFieldPreflights(d *tg.ServerDispatcher) error {
func registerLayerRPCAdmissionFieldPreflights(d *tlprofile.Dispatcher) error {
if d == nil {
return fmt.Errorf("nil layer RPC dispatcher")
}
for _, policy := range layerRPCVectorPolicies {
policy := policy
if err := d.OnLayerRPCAdmissionFieldPreflight(policy.fieldID, func(view tg.LayerRPCAdmissionFieldView) error {
if err := d.OnFieldPreflight(policy.fieldID, func(view tlprofile.FieldView) error {
length, ok := view.VectorLength()
if !ok {
return inputRequestInvalidErr()
@ -115,11 +116,11 @@ func registerLayerRPCAdmissionFieldPreflights(d *tg.ServerDispatcher) error {
}
}
for _, fieldID := range []tg.LayerRPCFieldID{
tg.LayerRPCFieldUploadSaveFilePartBytes,
tg.LayerRPCFieldUploadSaveBigFilePartBytes,
for _, fieldID := range []tlprofile.FieldID{
tlprofile.FieldUploadSaveFilePartBytes,
tlprofile.FieldUploadSaveBigFilePartBytes,
} {
if err := d.OnLayerRPCAdmissionFieldPreflight(fieldID, func(view tg.LayerRPCAdmissionFieldView) error {
if err := d.OnFieldPreflight(fieldID, func(view tlprofile.FieldView) error {
length, ok := view.BytesLength()
if !ok {
return inputRequestInvalidErr()
@ -133,9 +134,9 @@ func registerLayerRPCAdmissionFieldPreflights(d *tg.ServerDispatcher) error {
}
}
if err := d.OnLayerRPCAdmissionFieldPreflight(
tg.LayerRPCFieldUploadSaveBigFilePartFileTotalParts,
func(view tg.LayerRPCAdmissionFieldView) error {
if err := d.OnFieldPreflight(
tlprofile.FieldUploadSaveBigFilePartFileTotalParts,
func(view tlprofile.FieldView) error {
totalParts, ok := view.Int32()
if !ok {
return inputRequestInvalidErr()

View file

@ -16,6 +16,7 @@ import (
"github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
compatandroid "telesrv/internal/compat/android"
"telesrv/internal/domain"
"telesrv/internal/observability/dbtrace"
@ -90,7 +91,7 @@ type Config struct {
TempKeyResolveCacheMaxEntries int
}
// Router 把解密后的 RPC 请求按 TypeID 路由到 typed handlertg.ServerDispatcher
// Router 把解密后的 RPC 请求按 semantic method 路由到 typed handlertlprofile.Dispatcher
//
// handler 输入输出均为 iamxvbaba/td/tg 类型,各业务域的 handler
// 与注册见 help.go / auth.go / users.go / updates.go。Router 本身只负责协议外壳:
@ -100,7 +101,7 @@ type Router struct {
log *zap.Logger
clock clock.Clock
deps Deps
dispatcher *tg.ServerDispatcher
dispatcher *tlprofile.Dispatcher
clientInfoMu sync.RWMutex
clientInfo map[clientInfoSessionKey]clientSessionInfo
authInfo map[[8]byte]clientSessionInfo
@ -244,12 +245,12 @@ func New(cfg Config, deps Deps, log *zap.Logger, clk clock.Clock) *Router {
if cfg.DC > 0 {
groupCallStreamDCID = cfg.DC
}
d := tg.NewServerDispatcher(r.fallback)
d := tlprofile.NewDispatcher()
if err := registerLayerRPCAdmissionFieldPreflights(d); err != nil {
panic(fmt.Sprintf("register exact layer RPC admission policy: %v", err))
}
r.registerAndroidLayerRPCAdapter(d)
d.OnLayerRPCWrappers(r.consumeLayerRPCWrappers)
d.OnWrappers(r.consumeLayerRPCWrappers)
r.registerHelp(d)
r.registerAuth(d)
@ -278,6 +279,22 @@ func New(cfg Config, deps Deps, log *zap.Logger, clk clock.Clock) *Router {
return r
}
func registerRPC[T bin.Object](d *tlprofile.Dispatcher, method tlprofile.SemanticID, handler func(context.Context, T) (any, error)) {
if d == nil || handler == nil {
panic("rpc: register nil canonical RPC handler or dispatcher")
}
err := d.Register(method, func(ctx context.Context, object bin.Object) (any, error) {
request, ok := object.(T)
if !ok {
return nil, fmt.Errorf("rpc: semantic %#016x decoded unexpected canonical request %T", uint64(method), object)
}
return handler(ctx, request)
})
if err != nil {
panic(fmt.Sprintf("rpc: register canonical RPC %#016x: %v", uint64(method), err))
}
}
// Dispatch routes one RPC and preserves the historical two-value API used by
// domain/RPC tests. The MTProto edge uses DispatchWithMethod so outbound
// scheduling sees the exact innermost method rather than an invoke wrapper.
@ -622,14 +639,14 @@ func (r *Router) dispatch(ctx context.Context, b *bin.Buffer, depth int, meta *r
// Router.Dispatch is a legacy test seam. Production uses generated exact
// Layer admission, whose unknown-method view invokes the same static DrKLO
// overlay while sharing the outer request budget.
profile := tlprofile.ProfileCanonical
if selected, ok := tlprofile.ResolveProfile(LayerFrom(ctx)); ok {
profile = selected
}
if id != 0 {
profile := tg.LayerProfileCanonical
if selected, ok := tg.ResolveLayerProfile(LayerFrom(ctx)); ok {
profile = selected
}
_, official := tg.LayerSemanticForWireID(profile, id)
_, official := tlprofile.SemanticForWireID(profile, id)
if !official {
if up, ok, err := compatandroid.UpgradePrivateLayerRPC(profile, b, tg.LayerDecodeLimits{}); ok {
if up, ok, err := compatandroid.UpgradePrivateLayerRPC(profile, b, tlprofile.Limits{}); ok {
if err != nil {
return nil, inputRequestInvalidErr()
}
@ -648,15 +665,18 @@ func (r *Router) dispatch(ctx context.Context, b *bin.Buffer, depth int, meta *r
if meta != nil {
meta.method = tlTypeName(id)
}
semantic, knownRequest := tg.LayerSemanticForWireID(tg.LayerProfileCanonical, id)
semantic, knownRequest := tlprofile.SemanticForWireID(tlprofile.ProfileCanonical, id)
if knownRequest {
category, _, named := tg.LayerSemanticName(semantic)
category, _, named := tlprofile.SemanticName(semantic)
knownRequest = named && category == "function"
}
if !knownRequest {
// Unknown methods remain opaque and go to the compatibility trace.
return r.fallback(ctx, b)
}
if !r.dispatcher.Has(semantic) {
return r.fallback(ctx, b)
}
if r.deps.Auth != nil {
if _, ok := UserIDFrom(ctx); !ok && !rpcAllowedWithoutAuthorization(id) {
fields := append([]zap.Field{
@ -680,7 +700,17 @@ func (r *Router) dispatch(ctx context.Context, b *bin.Buffer, depth int, meta *r
r.maybeMarkSessionReceivesUpdates(ctx)
dbBefore := dbtrace.SnapshotFromContext(ctx)
start := time.Now()
enc, err := r.dispatcher.Handle(ctx, b)
admission, err := r.dispatcher.Admit(profile, b, tlprofile.Limits{})
if err != nil {
return nil, err
}
exact, err := r.dispatcher.Dispatch(ctx, admission)
var enc bin.Encoder = exact
if err == nil && exact != nil {
if canonical, ok := exact.CanonicalValue().(bin.Encoder); ok {
enc = canonical
}
}
dur := time.Since(start)
dbDelta := dbtrace.SnapshotFromContext(ctx).Sub(dbBefore)
fields := append([]zap.Field{
@ -1026,7 +1056,7 @@ func (r *Router) FreezeNegotiatedSessionLayerAt(authKeyID [8]byte, sessionID int
if r == nil || authKeyID == ([8]byte{}) || sessionID == 0 {
return false, errors.New("invalid exact session profile identity")
}
profile, ok := tg.ResolveLayerProfile(layer)
profile, ok := tlprofile.ResolveProfile(layer)
if !ok || int(profile) != layer {
return false, fmt.Errorf("unsupported exact session profile %d", layer)
}

View file

@ -61,10 +61,8 @@ func TestDispatchPromotesNegativeSessionCacheFromPositiveAuthCache(t *testing.T)
if err != nil {
t.Fatalf("dispatch: %v", err)
}
if box, ok := enc.(*tg.BoolBox); !ok {
t.Fatalf("dispatch result = %T, want *tg.BoolBox", enc)
} else if _, ok := box.Bool.(*tg.BoolTrue); !ok {
t.Fatalf("dispatch bool = %T, want BoolTrue", box.Bool)
if value, ok := dispatchCanonicalValue(enc).(bool); !ok || !value {
t.Fatalf("dispatch result = %#v (%T), want true", dispatchCanonicalValue(enc), enc)
}
gotSession := sessions.snapshot()
if gotSession.userID != userID || !gotSession.userResolved {
@ -130,10 +128,8 @@ func TestDispatchRevalidatesCachedTempAuthKeyBinding(t *testing.T) {
}
if enc, err := r.Dispatch(context.Background(), tempAuthKeyID, 123, &first); err != nil {
t.Fatalf("first dispatch: %v", err)
} else if box, ok := enc.(*tg.BoolBox); !ok {
t.Fatalf("first dispatch result = %T, want *tg.BoolBox", enc)
} else if _, ok := box.Bool.(*tg.BoolTrue); !ok {
t.Fatalf("first dispatch bool = %T, want BoolTrue", box.Bool)
} else if value, ok := dispatchCanonicalValue(enc).(bool); !ok || !value {
t.Fatalf("first dispatch result = %#v (%T), want true", dispatchCanonicalValue(enc), enc)
}
gotSession := sessions.snapshot()
if gotSession.authKeyID != permAuthKeyID || gotSession.userID != 1000000001 {

View file

@ -8,6 +8,7 @@ import (
"github.com/iamxvbaba/td/clock"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tgerr"
"github.com/iamxvbaba/td/tlprofile"
"go.uber.org/zap"
"go.uber.org/zap/zaptest"
"go.uber.org/zap/zaptest/observer"
@ -1334,7 +1335,7 @@ func TestTDesktopStartupRPCsEncode(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := WithUserID(context.Background(), 1000000001)
result, method := dispatchExactLayerRPCTest(t, r, ctx, tg.LayerProfileCanonical, tt.req)
result, method := dispatchExactLayerRPCTest(t, r, ctx, tlprofile.ProfileCanonical, tt.req)
if method != tt.name {
t.Fatalf("dispatched method = %q, want %q", method, tt.name)
}
@ -1353,12 +1354,12 @@ func dispatchExactLayerRPCTest(
t *testing.T,
r *Router,
ctx context.Context,
profile tg.LayerProfile,
profile tlprofile.Profile,
request bin.Object,
) (tg.LayerRPCResult, string) {
) (tlprofile.Result, string) {
t.Helper()
body := encodeExactLayerRPC(t, profile, request)
admitted, err := r.AdmitLayer(profile, &body, tg.LayerDecodeLimits{})
admitted, err := r.AdmitLayer(profile, &body, tlprofile.Limits{})
if err != nil {
t.Fatalf("admit exact Layer %d request: %v", profile, err)
}
@ -1379,11 +1380,11 @@ func TestMessagesSearchGlobalExactLayerProfiles(t *testing.T) {
r := New(Config{}, Deps{}, zaptest.NewLogger(t), clock.System)
ctx := WithUserID(context.Background(), 1000000001)
for _, tc := range []struct {
profile tg.LayerProfile
profile tlprofile.Profile
wireID uint32
}{
{profile: tg.LayerProfile227, wireID: 0x4bc6589a},
{profile: tg.LayerProfile228, wireID: 0x6126a43c},
{profile: tlprofile.Profile227, wireID: 0x4bc6589a},
{profile: tlprofile.Profile228, wireID: 0x6126a43c},
} {
t.Run(fmt.Sprintf("layer_%d", tc.profile), func(t *testing.T) {
request := &tg.MessagesSearchGlobalRequest{
@ -1396,7 +1397,7 @@ func TestMessagesSearchGlobalExactLayerProfiles(t *testing.T) {
if got := binary.LittleEndian.Uint32(body.Raw()); got != tc.wireID {
t.Fatalf("Layer %d wire id = %#x, want %#x", tc.profile, got, tc.wireID)
}
admitted, err := r.AdmitLayer(tc.profile, &body, tg.LayerDecodeLimits{})
admitted, err := r.AdmitLayer(tc.profile, &body, tlprofile.Limits{})
if err != nil {
t.Fatalf("admit Layer %d searchGlobal: %v", tc.profile, err)
}
@ -1404,7 +1405,7 @@ func TestMessagesSearchGlobalExactLayerProfiles(t *testing.T) {
t.Fatalf("Layer %d admission left %d bytes", tc.profile, body.Len())
}
call := admitted.Call()
if call.Profile() != tc.profile || call.WireID() != tc.wireID || call.Method() != tg.LayerSemanticMethodMessagesSearchGlobal {
if call.Profile() != tc.profile || call.WireID() != tc.wireID || call.Method() != tlprofile.SemanticMethodMessagesSearchGlobal {
t.Fatalf("Layer %d call = profile:%d wire:%#x semantic:%#x", tc.profile, call.Profile(), call.WireID(), call.Method())
}
result, method, err := r.DispatchAdmitted(ctx, [8]byte{}, 0, 0, 0, admitted)
@ -1439,23 +1440,19 @@ func TestMessagesSearchGlobalCommunityProjectionFailsClosedForLayer227(t *testin
request.SetCommunity(&tg.InputChannel{ChannelID: 42, AccessHash: 84})
// The Layer 228 shape is valid and carries the new field.
body228 := encodeExactLayerRPC(t, tg.LayerProfile228, request)
body228 := encodeExactLayerRPC(t, tlprofile.Profile228, request)
if got := binary.LittleEndian.Uint32(body228.Raw()); got != 0x6126a43c {
t.Fatalf("Layer 228 wire id = %#x, want %#x", got, uint32(0x6126a43c))
}
if _, err := New(Config{}, Deps{}, zaptest.NewLogger(t), clock.System).AdmitLayer(tg.LayerProfile228, &body228, tg.LayerDecodeLimits{}); err != nil {
if _, err := New(Config{}, Deps{}, zaptest.NewLogger(t), clock.System).AdmitLayer(tlprofile.Profile228, &body228, tlprofile.Limits{}); err != nil {
t.Fatalf("admit Layer 228 community search: %v", err)
}
if body228.Len() != 0 {
t.Fatalf("Layer 228 community admission left %d bytes", body228.Len())
}
outbound227, err := tg.PrepareLayerOutboundCall(tg.LayerProfile227, request)
if err != nil {
t.Fatalf("prepare Layer 227 searchGlobal projection: %v", err)
}
var body227 bin.Buffer
if err := outbound227.Encode(&body227); err == nil {
if err := tlprofile.EncodeObject(tlprofile.Profile227, request, &body227); err == nil {
t.Fatal("Layer 227 projection accepted a Layer 228-only community scope")
}
if body227.Len() != 0 {

View file

@ -16,6 +16,17 @@ import (
"testing"
)
// dispatchCanonicalValue exposes the handler value behind tlprofile's exact
// result encoder. Object/class results already arrive as concrete tg values;
// primitive and Vector<T> method results remain attached to the exact result
// plan because they are not bin.Object values.
func dispatchCanonicalValue(encoded bin.Encoder) any {
if result, ok := encoded.(interface{ CanonicalValue() any }); ok {
return result.CanonicalValue()
}
return encoded
}
type rpcChannelFixture struct {
t *testing.T
ctx context.Context
@ -111,8 +122,6 @@ func searchMessagesPayload(t *testing.T, enc bin.Encoder) ([]tg.MessageClass, []
return result.Messages, result.Chats, result.Users
case *tg.MessagesChannelMessages:
return result.Messages, result.Chats, result.Users
case *tg.MessagesMessagesBox:
return searchMessagesPayload(t, result.Messages)
default:
t.Fatalf("search result type = %T, want messages/messagesSlice", enc)
return nil, nil, nil

View file

@ -6,6 +6,7 @@ import (
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/domain"
)
@ -15,15 +16,31 @@ const (
maxStatsGraphTokenLength = 128
)
func (r *Router) registerStats(d *tg.ServerDispatcher) {
d.OnStatsGetBroadcastStats(r.onStatsGetBroadcastStats)
d.OnStatsGetMegagroupStats(r.onStatsGetMegagroupStats)
d.OnStatsGetMessageStats(r.onStatsGetMessageStats)
d.OnStatsGetMessagePublicForwards(r.onStatsGetMessagePublicForwards)
d.OnStatsLoadAsyncGraph(r.onStatsLoadAsyncGraph)
d.OnStatsGetStoryStats(r.onStatsGetStoryStats)
d.OnStatsGetStoryPublicForwards(r.onStatsGetStoryPublicForwards)
d.OnStatsGetPollStats(r.onStatsGetPollStats)
func (r *Router) registerStats(d *tlprofile.Dispatcher) {
registerRPC[*tg.StatsGetBroadcastStatsRequest](d, tlprofile.SemanticMethodStatsGetBroadcastStats, func(ctx context.Context, layerRequest *tg.StatsGetBroadcastStatsRequest) (any, error) {
return r.onStatsGetBroadcastStats(ctx, layerRequest)
})
registerRPC[*tg.StatsGetMegagroupStatsRequest](d, tlprofile.SemanticMethodStatsGetMegagroupStats, func(ctx context.Context, layerRequest *tg.StatsGetMegagroupStatsRequest) (any, error) {
return r.onStatsGetMegagroupStats(ctx, layerRequest)
})
registerRPC[*tg.StatsGetMessageStatsRequest](d, tlprofile.SemanticMethodStatsGetMessageStats, func(ctx context.Context, layerRequest *tg.StatsGetMessageStatsRequest) (any, error) {
return r.onStatsGetMessageStats(ctx, layerRequest)
})
registerRPC[*tg.StatsGetMessagePublicForwardsRequest](d, tlprofile.SemanticMethodStatsGetMessagePublicForwards, func(ctx context.Context, layerRequest *tg.StatsGetMessagePublicForwardsRequest) (any, error) {
return r.onStatsGetMessagePublicForwards(ctx, layerRequest)
})
registerRPC[*tg.StatsLoadAsyncGraphRequest](d, tlprofile.SemanticMethodStatsLoadAsyncGraph, func(ctx context.Context, layerRequest *tg.StatsLoadAsyncGraphRequest) (any, error) {
return r.onStatsLoadAsyncGraph(ctx, layerRequest)
})
registerRPC[*tg.StatsGetStoryStatsRequest](d, tlprofile.SemanticMethodStatsGetStoryStats, func(ctx context.Context, layerRequest *tg.StatsGetStoryStatsRequest) (any, error) {
return r.onStatsGetStoryStats(ctx, layerRequest)
})
registerRPC[*tg.StatsGetStoryPublicForwardsRequest](d, tlprofile.SemanticMethodStatsGetStoryPublicForwards, func(ctx context.Context, layerRequest *tg.StatsGetStoryPublicForwardsRequest) (any, error) {
return r.onStatsGetStoryPublicForwards(ctx, layerRequest)
})
registerRPC[*tg.StatsGetPollStatsRequest](d, tlprofile.SemanticMethodStatsGetPollStats, func(ctx context.Context, layerRequest *tg.StatsGetPollStatsRequest) (any, error) {
return r.onStatsGetPollStats(ctx, layerRequest)
})
}
func (r *Router) onStatsGetBroadcastStats(ctx context.Context, req *tg.StatsGetBroadcastStatsRequest) (*tg.StatsBroadcastStats, error) {

View file

@ -139,13 +139,9 @@ func TestCustomStickerPackLinkInstallAndSendSmoke(t *testing.T) {
if err != nil {
t.Fatalf("alice get history: %v", err)
}
box, ok := enc.(*tg.MessagesMessagesBox)
messages, ok := enc.(*tg.MessagesMessages)
if !ok {
t.Fatalf("history response = %T, want *tg.MessagesMessagesBox", enc)
}
messages, ok := box.Messages.(*tg.MessagesMessages)
if !ok {
t.Fatalf("history payload = %T, want *tg.MessagesMessages", box.Messages)
t.Fatalf("history response = %T, want *tg.MessagesMessages", enc)
}
if len(messages.Messages) != 1 {
t.Fatalf("history messages = %d, want 1", len(messages.Messages))

View file

@ -6,18 +6,40 @@ import (
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/domain"
)
func (r *Router) registerStickers(d *tg.ServerDispatcher) {
d.OnStickersCreateStickerSet(r.onStickersCreateStickerSet)
d.OnStickersCheckShortName(r.onStickersCheckShortName)
d.OnStickersSuggestShortName(r.onStickersSuggestShortName)
d.OnStickersAddStickerToSet(r.onStickersAddStickerToSet)
d.OnStickersRemoveStickerFromSet(r.onStickersRemoveStickerFromSet)
d.OnStickersChangeStickerPosition(r.onStickersChangeStickerPosition)
d.OnStickersRenameStickerSet(r.onStickersRenameStickerSet)
d.OnStickersDeleteStickerSet(r.onStickersDeleteStickerSet)
func (r *Router) registerStickers(d *tlprofile.Dispatcher) {
registerRPC[*tg.StickersCreateStickerSetRequest](d, tlprofile.SemanticMethodStickersCreateStickerSet, func(ctx context.Context, layerRequest *tg.StickersCreateStickerSetRequest) (any, error) {
return r.onStickersCreateStickerSet(ctx, layerRequest)
})
registerRPC[*tg.StickersCheckShortNameRequest](d, tlprofile.SemanticMethodStickersCheckShortName, func(ctx context.Context, layerRequest *tg.StickersCheckShortNameRequest) (any, error) {
return r.onStickersCheckShortName(ctx, layerRequest.
ShortName)
})
registerRPC[*tg.StickersSuggestShortNameRequest](d, tlprofile.SemanticMethodStickersSuggestShortName, func(ctx context.Context, layerRequest *tg.StickersSuggestShortNameRequest) (any, error) {
return r.onStickersSuggestShortName(ctx, layerRequest.
Title)
})
registerRPC[*tg.StickersAddStickerToSetRequest](d, tlprofile.SemanticMethodStickersAddStickerToSet, func(ctx context.Context, layerRequest *tg.StickersAddStickerToSetRequest) (any, error) {
return r.onStickersAddStickerToSet(ctx, layerRequest)
})
registerRPC[*tg.StickersRemoveStickerFromSetRequest](d, tlprofile.SemanticMethodStickersRemoveStickerFromSet, func(ctx context.Context, layerRequest *tg.StickersRemoveStickerFromSetRequest) (any, error) {
return r.onStickersRemoveStickerFromSet(ctx, layerRequest.
Sticker)
})
registerRPC[*tg.StickersChangeStickerPositionRequest](d, tlprofile.SemanticMethodStickersChangeStickerPosition, func(ctx context.Context, layerRequest *tg.StickersChangeStickerPositionRequest) (any, error) {
return r.onStickersChangeStickerPosition(ctx, layerRequest)
})
registerRPC[*tg.StickersRenameStickerSetRequest](d, tlprofile.SemanticMethodStickersRenameStickerSet, func(ctx context.Context, layerRequest *tg.StickersRenameStickerSetRequest) (any, error) {
return r.onStickersRenameStickerSet(ctx, layerRequest)
})
registerRPC[*tg.StickersDeleteStickerSetRequest](d, tlprofile.SemanticMethodStickersDeleteStickerSet, func(ctx context.Context, layerRequest *tg.StickersDeleteStickerSetRequest) (any, error) {
return r.onStickersDeleteStickerSet(ctx, layerRequest.
Stickerset)
})
}
func (r *Router) onStickersCreateStickerSet(ctx context.Context, req *tg.StickersCreateStickerSetRequest) (tg.MessagesStickerSetClass, error) {

View file

@ -12,6 +12,7 @@ import (
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/compat/tdesktop"
"telesrv/internal/domain"
)
@ -27,40 +28,110 @@ const (
)
// registerStories 注册 TDesktop/Android 已发现的 stories.* RPC。
func (r *Router) registerStories(d *tg.ServerDispatcher) {
d.OnStoriesGetAllStories(r.onStoriesGetAllStories)
d.OnStoriesGetPeerStories(r.onStoriesGetPeerStories)
d.OnStoriesGetStoriesByID(r.onStoriesGetStoriesByID)
d.OnStoriesGetStoriesArchive(r.onStoriesGetStoriesArchive)
d.OnStoriesGetPinnedStories(r.onStoriesGetPinnedStories)
d.OnStoriesExportStoryLink(r.onStoriesExportStoryLink)
d.OnStoriesReport(r.onStoriesReport)
d.OnStoriesActivateStealthMode(r.onStoriesActivateStealthMode)
d.OnStoriesSearchPosts(r.onStoriesSearchPosts)
d.OnStoriesSendStory(r.onStoriesSendStory)
d.OnStoriesEditStory(r.onStoriesEditStory)
d.OnStoriesDeleteStories(r.onStoriesDeleteStories)
d.OnStoriesTogglePinned(r.onStoriesTogglePinned)
d.OnStoriesTogglePinnedToTop(r.onStoriesTogglePinnedToTop)
d.OnStoriesToggleAllStoriesHidden(r.onStoriesToggleAllStoriesHidden)
d.OnStoriesCreateAlbum(r.onStoriesCreateAlbum)
d.OnStoriesUpdateAlbum(r.onStoriesUpdateAlbum)
d.OnStoriesReorderAlbums(r.onStoriesReorderAlbums)
d.OnStoriesDeleteAlbum(r.onStoriesDeleteAlbum)
d.OnStoriesGetAlbums(r.onStoriesGetAlbums)
d.OnStoriesGetAlbumStories(r.onStoriesGetAlbumStories)
d.OnStoriesGetAllReadPeerStories(r.onStoriesGetAllReadPeerStories)
d.OnStoriesGetPeerMaxIDs(r.onStoriesGetPeerMaxIDs)
d.OnStoriesReadStories(r.onStoriesReadStories)
d.OnStoriesIncrementStoryViews(r.onStoriesIncrementStoryViews)
d.OnStoriesGetStoriesViews(r.onStoriesGetStoriesViews)
d.OnStoriesGetStoryViewsList(r.onStoriesGetStoryViewsList)
d.OnStoriesGetStoryReactionsList(r.onStoriesGetStoryReactionsList)
d.OnStoriesTogglePeerStoriesHidden(r.onStoriesTogglePeerStoriesHidden)
d.OnStoriesCanSendStory(r.onStoriesCanSendStory)
d.OnStoriesGetChatsToSend(r.onStoriesGetChatsToSend)
d.OnStoriesSendReaction(r.onStoriesSendReaction)
d.OnStoriesStartLive(r.onStoriesStartLive)
func (r *Router) registerStories(d *tlprofile.Dispatcher) {
registerRPC[*tg.StoriesGetAllStoriesRequest](d, tlprofile.SemanticMethodStoriesGetAllStories, func(ctx context.Context, layerRequest *tg.StoriesGetAllStoriesRequest) (any, error) {
return r.onStoriesGetAllStories(ctx, layerRequest)
})
registerRPC[*tg.StoriesGetPeerStoriesRequest](d, tlprofile.SemanticMethodStoriesGetPeerStories, func(ctx context.Context, layerRequest *tg.StoriesGetPeerStoriesRequest) (any, error) {
return r.onStoriesGetPeerStories(ctx, layerRequest.
Peer)
})
registerRPC[*tg.StoriesGetStoriesByIDRequest](d, tlprofile.SemanticMethodStoriesGetStoriesByID, func(ctx context.Context, layerRequest *tg.StoriesGetStoriesByIDRequest) (any, error) {
return r.onStoriesGetStoriesByID(ctx, layerRequest)
})
registerRPC[*tg.StoriesGetStoriesArchiveRequest](d, tlprofile.SemanticMethodStoriesGetStoriesArchive, func(ctx context.Context, layerRequest *tg.StoriesGetStoriesArchiveRequest) (any, error) {
return r.onStoriesGetStoriesArchive(ctx, layerRequest)
})
registerRPC[*tg.StoriesGetPinnedStoriesRequest](d, tlprofile.SemanticMethodStoriesGetPinnedStories, func(ctx context.Context, layerRequest *tg.StoriesGetPinnedStoriesRequest) (any, error) {
return r.onStoriesGetPinnedStories(ctx, layerRequest)
})
registerRPC[*tg.StoriesExportStoryLinkRequest](d, tlprofile.SemanticMethodStoriesExportStoryLink, func(ctx context.Context, layerRequest *tg.StoriesExportStoryLinkRequest) (any, error) {
return r.onStoriesExportStoryLink(ctx, layerRequest)
})
registerRPC[*tg.StoriesReportRequest](d, tlprofile.SemanticMethodStoriesReport, func(ctx context.Context, layerRequest *tg.StoriesReportRequest) (any, error) {
return r.onStoriesReport(ctx, layerRequest)
})
registerRPC[*tg.StoriesActivateStealthModeRequest](d, tlprofile.SemanticMethodStoriesActivateStealthMode, func(ctx context.Context, layerRequest *tg.StoriesActivateStealthModeRequest) (any, error) {
return r.onStoriesActivateStealthMode(ctx, layerRequest)
})
registerRPC[*tg.StoriesSearchPostsRequest](d, tlprofile.SemanticMethodStoriesSearchPosts, func(ctx context.Context, layerRequest *tg.StoriesSearchPostsRequest) (any, error) {
return r.onStoriesSearchPosts(ctx, layerRequest)
})
registerRPC[*tg.StoriesSendStoryRequest](d, tlprofile.SemanticMethodStoriesSendStory, func(ctx context.Context, layerRequest *tg.StoriesSendStoryRequest) (any, error) {
return r.onStoriesSendStory(ctx, layerRequest)
})
registerRPC[*tg.StoriesEditStoryRequest](d, tlprofile.SemanticMethodStoriesEditStory, func(ctx context.Context, layerRequest *tg.StoriesEditStoryRequest) (any, error) {
return r.onStoriesEditStory(ctx, layerRequest)
})
registerRPC[*tg.StoriesDeleteStoriesRequest](d, tlprofile.SemanticMethodStoriesDeleteStories, func(ctx context.Context, layerRequest *tg.StoriesDeleteStoriesRequest) (any, error) {
return r.onStoriesDeleteStories(ctx, layerRequest)
})
registerRPC[*tg.StoriesTogglePinnedRequest](d, tlprofile.SemanticMethodStoriesTogglePinned, func(ctx context.Context, layerRequest *tg.StoriesTogglePinnedRequest) (any, error) {
return r.onStoriesTogglePinned(ctx, layerRequest)
})
registerRPC[*tg.StoriesTogglePinnedToTopRequest](d, tlprofile.SemanticMethodStoriesTogglePinnedToTop, func(ctx context.Context, layerRequest *tg.StoriesTogglePinnedToTopRequest) (any, error) {
return r.onStoriesTogglePinnedToTop(ctx, layerRequest)
})
registerRPC[*tg.StoriesToggleAllStoriesHiddenRequest](d, tlprofile.SemanticMethodStoriesToggleAllStoriesHidden, func(ctx context.Context, layerRequest *tg.StoriesToggleAllStoriesHiddenRequest) (any, error) {
return r.onStoriesToggleAllStoriesHidden(ctx, layerRequest.
Hidden)
})
registerRPC[*tg.StoriesCreateAlbumRequest](d, tlprofile.SemanticMethodStoriesCreateAlbum, func(ctx context.Context, layerRequest *tg.StoriesCreateAlbumRequest) (any, error) {
return r.onStoriesCreateAlbum(ctx, layerRequest)
})
registerRPC[*tg.StoriesUpdateAlbumRequest](d, tlprofile.SemanticMethodStoriesUpdateAlbum, func(ctx context.Context, layerRequest *tg.StoriesUpdateAlbumRequest) (any, error) {
return r.onStoriesUpdateAlbum(ctx, layerRequest)
})
registerRPC[*tg.StoriesReorderAlbumsRequest](d, tlprofile.SemanticMethodStoriesReorderAlbums, func(ctx context.Context, layerRequest *tg.StoriesReorderAlbumsRequest) (any, error) {
return r.onStoriesReorderAlbums(ctx, layerRequest)
})
registerRPC[*tg.StoriesDeleteAlbumRequest](d, tlprofile.SemanticMethodStoriesDeleteAlbum, func(ctx context.Context, layerRequest *tg.StoriesDeleteAlbumRequest) (any, error) {
return r.onStoriesDeleteAlbum(ctx, layerRequest)
})
registerRPC[*tg.StoriesGetAlbumsRequest](d, tlprofile.SemanticMethodStoriesGetAlbums, func(ctx context.Context, layerRequest *tg.StoriesGetAlbumsRequest) (any, error) {
return r.onStoriesGetAlbums(ctx, layerRequest)
})
registerRPC[*tg.StoriesGetAlbumStoriesRequest](d, tlprofile.SemanticMethodStoriesGetAlbumStories, func(ctx context.Context, layerRequest *tg.StoriesGetAlbumStoriesRequest) (any, error) {
return r.onStoriesGetAlbumStories(ctx, layerRequest)
})
registerRPC[*tg.StoriesGetAllReadPeerStoriesRequest](d, tlprofile.SemanticMethodStoriesGetAllReadPeerStories, func(ctx context.Context, layerRequest *tg.StoriesGetAllReadPeerStoriesRequest) (any, error) {
return r.onStoriesGetAllReadPeerStories(ctx)
})
registerRPC[*tg.StoriesGetPeerMaxIDsRequest](d, tlprofile.SemanticMethodStoriesGetPeerMaxIDs, func(ctx context.Context, layerRequest *tg.StoriesGetPeerMaxIDsRequest) (any, error) {
return r.onStoriesGetPeerMaxIDs(ctx, layerRequest.
ID)
})
registerRPC[*tg.StoriesReadStoriesRequest](d, tlprofile.SemanticMethodStoriesReadStories, func(ctx context.Context, layerRequest *tg.StoriesReadStoriesRequest) (any, error) {
return r.onStoriesReadStories(ctx, layerRequest)
})
registerRPC[*tg.StoriesIncrementStoryViewsRequest](d, tlprofile.SemanticMethodStoriesIncrementStoryViews, func(ctx context.Context, layerRequest *tg.StoriesIncrementStoryViewsRequest) (any, error) {
return r.onStoriesIncrementStoryViews(ctx, layerRequest)
})
registerRPC[*tg.StoriesGetStoriesViewsRequest](d, tlprofile.SemanticMethodStoriesGetStoriesViews, func(ctx context.Context, layerRequest *tg.StoriesGetStoriesViewsRequest) (any, error) {
return r.onStoriesGetStoriesViews(ctx, layerRequest)
})
registerRPC[*tg.StoriesGetStoryViewsListRequest](d, tlprofile.SemanticMethodStoriesGetStoryViewsList, func(ctx context.Context, layerRequest *tg.StoriesGetStoryViewsListRequest) (any, error) {
return r.onStoriesGetStoryViewsList(ctx, layerRequest)
})
registerRPC[*tg.StoriesGetStoryReactionsListRequest](d, tlprofile.SemanticMethodStoriesGetStoryReactionsList, func(ctx context.Context, layerRequest *tg.StoriesGetStoryReactionsListRequest) (any, error) {
return r.onStoriesGetStoryReactionsList(ctx, layerRequest)
})
registerRPC[*tg.StoriesTogglePeerStoriesHiddenRequest](d, tlprofile.SemanticMethodStoriesTogglePeerStoriesHidden, func(ctx context.Context, layerRequest *tg.StoriesTogglePeerStoriesHiddenRequest) (any, error) {
return r.onStoriesTogglePeerStoriesHidden(ctx, layerRequest)
})
registerRPC[*tg.StoriesCanSendStoryRequest](d, tlprofile.SemanticMethodStoriesCanSendStory, func(ctx context.Context, layerRequest *tg.StoriesCanSendStoryRequest) (any, error) {
return r.onStoriesCanSendStory(ctx, layerRequest.
Peer)
})
registerRPC[*tg.StoriesGetChatsToSendRequest](d, tlprofile.SemanticMethodStoriesGetChatsToSend, func(ctx context.Context, layerRequest *tg.StoriesGetChatsToSendRequest) (any, error) {
return r.onStoriesGetChatsToSend(ctx)
})
registerRPC[*tg.StoriesSendReactionRequest](d, tlprofile.SemanticMethodStoriesSendReaction, func(ctx context.Context, layerRequest *tg.StoriesSendReactionRequest) (any, error) {
return r.onStoriesSendReaction(ctx, layerRequest)
})
registerRPC[*tg.StoriesStartLiveRequest](d, tlprofile.SemanticMethodStoriesStartLive, func(ctx context.Context, layerRequest *tg.StoriesStartLiveRequest) (any, error) {
return r.onStoriesStartLive(ctx, layerRequest)
})
}
func (r *Router) onStoriesGetAllStories(ctx context.Context, req *tg.StoriesGetAllStoriesRequest) (tg.StoriesAllStoriesClass, error) {

View file

@ -1534,9 +1534,6 @@ func dispatchMessagesPayload(t *testing.T, r *Router, ctx context.Context, req b
if err != nil {
t.Fatalf("dispatch: %v", err)
}
if box, ok := out.(*tg.MessagesMessagesBox); ok {
return box.Messages
}
return out
}

View file

@ -5,21 +5,28 @@ import (
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/domain"
)
// registerUpdates 注册 updates.* RPC handler。
func (r *Router) registerUpdates(d *tg.ServerDispatcher) {
d.OnUpdatesGetState(r.onUpdatesGetState)
d.OnUpdatesGetDifference(r.onUpdatesGetDifference)
func (r *Router) registerUpdates(d *tlprofile.Dispatcher) {
registerRPC[*tg.UpdatesGetStateRequest](d, tlprofile.SemanticMethodUpdatesGetState, func(ctx context.Context, layerRequest *tg.UpdatesGetStateRequest) (
// onUpdatesGetState 处理 updates.getState。TDesktop 与 DrKLO 的启动路径把它当作
// 「从当前快照开始同步」的显式 baseline返回账号当前连续水位并且只在 rpc_result
// 物理交付后原子推进该设备 confirmed + observed。对尚未审计 baseline 语义的客户端
// 仍返回同一 current state但交付后只推进 confirmed这保留 observed/durable
// difference tail避免把 TDesktop/DrKLO 的兼容例外扩散成所有客户端都能跨过未实际
// 确认事件的 retention 后门。
any, error) {
return r.onUpdatesGetState(ctx)
})
registerRPC[*tg.UpdatesGetDifferenceRequest](d, tlprofile.SemanticMethodUpdatesGetDifference, func(ctx context.Context, layerRequest *tg.UpdatesGetDifferenceRequest) (any, error) {
return r.onUpdatesGetDifference(ctx, layerRequest)
})
}
// onUpdatesGetState 处理 updates.getState。TDesktop 与 DrKLO 的启动路径把它当作
// 「从当前快照开始同步」的显式 baseline返回账号当前连续水位并且只在 rpc_result
// 物理交付后原子推进该设备 confirmed + observed。对尚未审计 baseline 语义的客户端
// 仍返回同一 current state但交付后只推进 confirmed这保留 observed/durable
// difference tail避免把 TDesktop/DrKLO 的兼容例外扩散成所有客户端都能跨过未实际
// 确认事件的 retention 后门。
func (r *Router) onUpdatesGetState(ctx context.Context) (*tg.UpdatesState, error) {
userID, _, err := r.currentUserID(ctx)
if err != nil {

View file

@ -9,6 +9,7 @@ import (
"github.com/iamxvbaba/td/tg"
"go.uber.org/zap"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/domain"
)
@ -17,11 +18,19 @@ import (
const maxUploadGetFileChunkLimit = 1 << 20
// registerUpload 注册 upload.* RPC handler分片上传 + 文件下载 + 地图 webfile
func (r *Router) registerUpload(d *tg.ServerDispatcher) {
d.OnUploadSaveFilePart(r.onUploadSaveFilePart)
d.OnUploadSaveBigFilePart(r.onUploadSaveBigFilePart)
d.OnUploadGetFile(r.onUploadGetFile)
d.OnUploadGetFileHashes(r.onUploadGetFileHashes)
func (r *Router) registerUpload(d *tlprofile.Dispatcher) {
registerRPC[*tg.UploadSaveFilePartRequest](d, tlprofile.SemanticMethodUploadSaveFilePart, func(ctx context.Context, layerRequest *tg.UploadSaveFilePartRequest) (any, error) {
return r.onUploadSaveFilePart(ctx, layerRequest)
})
registerRPC[*tg.UploadSaveBigFilePartRequest](d, tlprofile.SemanticMethodUploadSaveBigFilePart, func(ctx context.Context, layerRequest *tg.UploadSaveBigFilePartRequest) (any, error) {
return r.onUploadSaveBigFilePart(ctx, layerRequest)
})
registerRPC[*tg.UploadGetFileRequest](d, tlprofile.SemanticMethodUploadGetFile, func(ctx context.Context, layerRequest *tg.UploadGetFileRequest) (any, error) {
return r.onUploadGetFile(ctx, layerRequest)
})
registerRPC[*tg.UploadGetFileHashesRequest](d, tlprofile.SemanticMethodUploadGetFileHashes, func(ctx context.Context, layerRequest *tg.UploadGetFileHashesRequest) (any, error) {
return r.onUploadGetFileHashes(ctx, layerRequest)
})
r.registerUploadWebFile(d)
}

View file

@ -13,6 +13,7 @@ import (
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/domain"
)
@ -39,8 +40,10 @@ type inlineWebFileFetcher func(ctx context.Context, document domain.BotInlineWeb
var fetchInlineWebFile inlineWebFileFetcher = defaultFetchInlineWebFile
func (r *Router) registerUploadWebFile(d *tg.ServerDispatcher) {
d.OnUploadGetWebFile(r.onUploadGetWebFile)
func (r *Router) registerUploadWebFile(d *tlprofile.Dispatcher) {
registerRPC[*tg.UploadGetWebFileRequest](d, tlprofile.SemanticMethodUploadGetWebFile, func(ctx context.Context, layerRequest *tg.UploadGetWebFileRequest) (any, error) {
return r.onUploadGetWebFile(ctx, layerRequest)
})
}
func (r *Router) onUploadGetWebFile(ctx context.Context, req *tg.UploadGetWebFileRequest) (*tg.UploadWebFile, error) {

View file

@ -6,6 +6,7 @@ import (
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/app/users"
"telesrv/internal/compat/tdesktop"
"telesrv/internal/domain"
@ -15,15 +16,30 @@ const maxSavedMusicLimit = 100
const maxRequirementsToContactUsers = 100
// registerUsers 注册 users.* RPC handler。
func (r *Router) registerUsers(d *tg.ServerDispatcher) {
d.OnUsersGetUsers(r.onUsersGetUsers)
d.OnUsersGetFullUser(r.onUsersGetFullUser)
d.OnUsersGetRequirementsToContact(r.onUsersGetRequirementsToContact)
d.OnUsersGetSavedMusic(r.onUsersGetSavedMusic)
d.OnUsersGetSavedMusicByID(r.onUsersGetSavedMusicByID)
func (r *Router) registerUsers(d *tlprofile.Dispatcher) {
registerRPC[*tg.UsersGetUsersRequest](d, tlprofile.SemanticMethodUsersGetUsers, func(ctx context.Context, layerRequest *tg.UsersGetUsersRequest) (any, error) {
return r.onUsersGetUsers(ctx, layerRequest.
ID)
})
registerRPC[*tg.UsersGetFullUserRequest](d, tlprofile.SemanticMethodUsersGetFullUser, func(ctx context.Context, layerRequest *tg.UsersGetFullUserRequest) (any, error) {
return r.onUsersGetFullUser(ctx, layerRequest.
ID)
})
registerRPC[*tg.UsersGetRequirementsToContactRequest](d, tlprofile.SemanticMethodUsersGetRequirementsToContact, func(ctx context.Context, layerRequest *tg.UsersGetRequirementsToContactRequest) (any, error) {
return r.onUsersGetRequirementsToContact(ctx, layerRequest.
ID)
})
registerRPC[*tg.UsersGetSavedMusicRequest](d, tlprofile.SemanticMethodUsersGetSavedMusic, func(ctx context.Context, layerRequest *tg.UsersGetSavedMusicRequest) (
// onUsersGetUsers 处理 users.getUsers支持 self 和已知 user peer含 777000 官方账号)。
any, error) {
return r.onUsersGetSavedMusic(ctx, layerRequest)
})
registerRPC[*tg.UsersGetSavedMusicByIDRequest](d, tlprofile.SemanticMethodUsersGetSavedMusicByID, func(ctx context.Context, layerRequest *tg.UsersGetSavedMusicByIDRequest) (any, error) {
return r.onUsersGetSavedMusicByID(ctx, layerRequest)
})
}
// onUsersGetUsers 处理 users.getUsers支持 self 和已知 user peer含 777000 官方账号)。
func (r *Router) onUsersGetUsers(ctx context.Context, ids []tg.InputUserClass) ([]tg.UserClass, error) {
currentUserID, authorized, err := r.currentUserID(ctx)
if err != nil {