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 The protocol stack is built on the published
[`github.com/iamxvbaba/td`](https://github.com/iamxvbaba/td) module [`github.com/iamxvbaba/td`](https://github.com/iamxvbaba/td) module
(`v1.0.0`), using a canonical Layer 228 schema with exact Layer 225-228 (`v1.1.0`), using a canonical Layer 228 schema with sparse `tlprofile`
compatibility profiles. exact Layer 225-228 compatibility profiles.
If you are looking for a **Telegram server**, **MTProto server**, If you are looking for a **Telegram server**, **MTProto server**,
**Telegram backend**, **Telegram clone server**, or **self-hosted **Telegram backend**, **Telegram clone server**, or **self-hosted
@ -45,7 +45,7 @@ codebase.
| Status | Feature | What works today | | 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. | | ✅ | 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. | | ✅ | 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. | | ✅ | 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 [`github.com/iamxvbaba/td`](https://github.com/iamxvbaba/td) module
(`v1.0.0`),使用 canonical Layer 228 schema,并提供 exact Layer 225-228 (`v1.1.0`),使用 canonical Layer 228 schema,并通过 sparse `tlprofile`
compatibility profiles。 提供 exact Layer 225-228 compatibility profiles。
如果你正在搜索 **Telegram server 实现**、**MTProto server 实现**、 如果你正在搜索 **Telegram server 实现**、**MTProto server 实现**、
**Telegram 后端**、**Telegram clone server**、**自建 Telegram-like 聊天服务器**, **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 相关路径。 | | ✅ | 登录与账号 | 开发验证码登录、sign-in、sign-up、log-out、授权设备、账号设置、SRP/password 状态、email/passkey 相关路径。 |
| ✅ | 用户与联系人 | 用户资料、username、头像、联系人导入/搜索、block/privacy 状态、presence、last seen。 | | ✅ | 用户与联系人 | 用户资料、username、头像、联系人导入/搜索、block/privacy 状态、presence、last seen。 |
| ✅ | 会话与同步 | dialog list、置顶、手动未读、folders/filters、草稿、read boundary、durable updates、在线 fan-out、离线 difference 恢复。 | | ✅ | 会话与同步 | 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/golang-migrate/migrate/v4 v4.19.1
github.com/gotd/ige v0.2.2 github.com/gotd/ige v0.2.2
github.com/gotd/log/logzap v0.1.1 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/pgerrcode v0.0.0-20220416144525-469b46aa5efa
github.com/jackc/pgx/v5 v5.9.2 github.com/jackc/pgx/v5 v5.9.2
github.com/pion/datachannel v1.6.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/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 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= 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.1.0 h1:6Ddxi8sOuxOioGs3vyDGWlC6q53j4AQ2hFJ4AirJvFk=
github.com/iamxvbaba/td v1.0.0/go.mod h1:oG/fu7sqGC7NznoBD8f3fmTy9NFR42+DMNtdCPStX04= 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 h1:s+4MhCQ6YrzisK6hFJUX53drDT4UsSW3DEhKn0ifuHw=
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=

View file

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

View file

@ -5,7 +5,7 @@ import (
"testing" "testing"
"github.com/iamxvbaba/td/bin" "github.com/iamxvbaba/td/bin"
"github.com/iamxvbaba/td/tg" "github.com/iamxvbaba/td/tlprofile"
) )
func TestUpgradePrivateLayerRPCOnlyAcceptsAuditedAndroidConstructors(t *testing.T) { func TestUpgradePrivateLayerRPCOnlyAcceptsAuditedAndroidConstructors(t *testing.T) {
@ -20,7 +20,7 @@ func TestUpgradePrivateLayerRPCOnlyAcceptsAuditedAndroidConstructors(t *testing.
private.PutID(0x7f3b18ea) // inputPeerEmpty private.PutID(0x7f3b18ea) // inputPeerEmpty
in := &bin.Buffer{Buf: private.Copy()} 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 { if err != nil || !ok {
t.Fatalf("upgrade private method = ok:%v err:%v", ok, err) t.Fatalf("upgrade private method = ok:%v err:%v", ok, err)
} }
@ -33,13 +33,13 @@ func TestUpgradePrivateLayerRPCOnlyAcceptsAuditedAndroidConstructors(t *testing.
official := bin.Buffer{} official := bin.Buffer{}
official.PutID(0xb921bd04) // arbitrary non-private/official constructor 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) t.Fatalf("non-private method = value:%v handled:%v err:%v", value, handled, err)
} }
} }
func TestGeneratedPrivateLayerRPCOverlayHasAllAuditedMethods(t *testing.T) { 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) 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) { func TestUpgradePrivateLayerRPCRejectsMalformedBody(t *testing.T) {
malformed := bin.Buffer{} malformed := bin.Buffer{}
malformed.PutID(0x41d41ade) 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) { if !ok || !errors.Is(err, ErrPrivateLayerRPCInvalid) {
t.Fatalf("malformed private method = ok:%v err:%v", ok, err) 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/bin"
"github.com/iamxvbaba/td/crypto" "github.com/iamxvbaba/td/crypto"
"github.com/iamxvbaba/td/proto" "github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/tg" "github.com/iamxvbaba/td/tlprofile"
"github.com/iamxvbaba/td/transport" "github.com/iamxvbaba/td/transport"
) )
@ -279,7 +279,7 @@ func (c *Conn) isPhysicalTransportCurrentOpen() bool {
// LayerProfile returns the exact TL profile currently selected for this // LayerProfile returns the exact TL profile currently selected for this
// connection. ok is false until admission or an inherited auth-key default // connection. ok is false until admission or an inherited auth-key default
// supplies a supported generated profile. // 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() state := c.LayerProfileState()
return state.Profile, state.Origin != LayerProfileUnknown 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 // admission. Repeating the same value is idempotent. A later well-formed
// invokeWithLayer may replace either an inherited default or older explicit // invokeWithLayer may replace either an inherited default or older explicit
// evidence; already-admitted requests retain their own immutable profile. // 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) _, err := c.setLayerProfile(profile, LayerProfileExplicit, true)
return err 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 // 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 // cursor at an unchanged Layer does not rotate the outbound epoch because the
// wire profile itself did not change. // 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) return c.freezeLayerProfileAt(profile, msgID)
} }
// SeedLayerProfile restores explicit evidence previously proven for this exact // SeedLayerProfile restores explicit evidence previously proven for this exact
// logical session. It is kept as the compatible same-session restore API; // logical session. It is kept as the compatible same-session restore API;
// auth-key-wide metadata must use SeedInheritedLayerProfile instead. // 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) _, err := c.setLayerProfile(profile, LayerProfileExplicit, true)
return err return err
} }
@ -313,7 +313,7 @@ func (c *Conn) SeedLayerProfile(profile tg.LayerProfile) error {
// SeedInheritedLayerProfile installs an auth-key-wide default only while the // SeedInheritedLayerProfile installs an auth-key-wide default only while the
// connection is still unknown. It never overwrites explicit evidence or an // connection is still unknown. It never overwrites explicit evidence or an
// already selected inherited default; client protocol evidence owns correction. // 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) _, err := c.setLayerProfile(profile, LayerProfileInherited, false)
return err return err
} }

View file

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

View file

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

View file

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

View file

@ -20,10 +20,10 @@ import (
"github.com/iamxvbaba/td/mt" "github.com/iamxvbaba/td/mt"
"github.com/iamxvbaba/td/proto" "github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/proto/codec" "github.com/iamxvbaba/td/proto/codec"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tgerr" "github.com/iamxvbaba/td/tgerr"
"github.com/iamxvbaba/td/transport" "github.com/iamxvbaba/td/transport"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/observability/dbtrace" "telesrv/internal/observability/dbtrace"
"telesrv/internal/postresponse" "telesrv/internal/postresponse"
"telesrv/internal/store" "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 // obey the production exact-codec invariant. Admit a defensive copy using
// the generated current profile before the legacy router consumes b. // the generated current profile before the legacy router consumes b.
admissionBody := &bin.Buffer{Buf: append([]byte(nil), b.Buf...)} admissionBody := &bin.Buffer{Buf: append([]byte(nil), b.Buf...)}
admitted, err := tg.NewServerDispatcher(nil).AdmitDefaultLayerWithLimits( admitted, err := tlprofile.NewDispatcher().AdmitDefault(
tg.LayerProfileCanonical, tlprofile.ProfileCanonical,
admissionBody, admissionBody,
inboundLayerDecodeLimits, inboundLayerDecodeLimits,
) )

View file

@ -16,6 +16,7 @@ import (
"github.com/iamxvbaba/td/exchange" "github.com/iamxvbaba/td/exchange"
"github.com/iamxvbaba/td/proto" "github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/tg" "github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"github.com/iamxvbaba/td/transport" "github.com/iamxvbaba/td/transport"
) )
@ -23,7 +24,7 @@ import (
// by old connection-state tests. Production exact-path tests must instead call // by old connection-state tests. Production exact-path tests must instead call
// FreezeLayerProfile/SeedLayerProfile with protocol evidence. // FreezeLayerProfile/SeedLayerProfile with protocol evidence.
func legacyCanonicalTestConn(t testing.TB, c *Conn) *Conn { 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 // 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 { if c == nil {
t.Fatal("nil legacy exact-layer test Conn") t.Fatal("nil legacy exact-layer test Conn")
} }
profile, ok := tg.ResolveLayerProfile(layer) profile, ok := tlprofile.ResolveProfile(layer)
if !ok { if !ok {
t.Fatalf("unsupported generated test Layer %d", layer) 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, typeID: tg.UpdatesTooLongTypeID,
layer: &outboundLayerBinding{ layer: &outboundLayerBinding{
profile: state.Profile, profile: state.Profile,
typ: tg.LayerClassUpdatesType().Ref(),
epoch: state.Epoch, epoch: state.Epoch,
}, },
} }
@ -86,8 +86,7 @@ func (r *opaqueExactTestRPCResult) Encode(b *bin.Buffer) error { return r.result
func (r *opaqueExactTestRPCResult) exactLayerRPCResultBinding() outboundLayerBinding { func (r *opaqueExactTestRPCResult) exactLayerRPCResultBinding() outboundLayerBinding {
return outboundLayerBinding{ return outboundLayerBinding{
profile: tg.LayerProfileCanonical, profile: tlprofile.ProfileCanonical,
typ: tg.LayerClassUpdatesType().Ref(),
kind: outboundLayerBindingRequest, 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 // profile that a production invokeWithLayer admission would have proven. It is
// intentionally explicit: handshake/new_session_created alone never implies a // intentionally explicit: handshake/new_session_created alone never implies a
// TL Layer, and production push code must keep failing closed in that state. // 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() t.Helper()
if sessions == nil { if sessions == nil {
t.Fatal("freeze test session profile on nil SessionManager") 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/mt"
"github.com/iamxvbaba/td/tg" "github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tgerr" "github.com/iamxvbaba/td/tgerr"
"github.com/iamxvbaba/td/tlprofile"
"go.uber.org/zap" "go.uber.org/zap"
) )
var inboundLayerDecodeLimits = tg.LayerDecodeLimits{ var inboundLayerDecodeLimits = tlprofile.Limits{
MaxWireBytes: maxInflightRPCBytes, MaxWireBytes: maxInflightRPCBytes,
// contacts.editCloseFriends and contacts.setBlocked deliberately allow // contacts.editCloseFriends and contacts.setBlocked deliberately allow
// 5,000 entries. Keep the coarse generated allocation ceiling above every // 5,000 entries. Keep the coarse generated allocation ceiling above every
@ -48,7 +49,7 @@ type layerRPCDependencySet struct {
} }
type layerRPCProfileEvidence struct { type layerRPCProfileEvidence struct {
profile tg.LayerProfile profile tlprofile.Profile
admissionSeq uint64 admissionSeq uint64
present bool present bool
fresh bool fresh bool
@ -67,7 +68,7 @@ type layerRPCAdmissionCursor struct {
evidenceMsgID int64 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) return c.observeRaw(int(profile), msgID)
} }
@ -90,7 +91,7 @@ func (c *layerRPCAdmissionCursor) observeRaw(layer int, msgID int64) error {
} }
} }
c.state = LayerProfileSnapshot{} 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.state = LayerProfileSnapshot{Profile: profile, Origin: LayerProfileExplicit}
} }
c.rawLayer = layer c.rawLayer = layer
@ -136,7 +137,7 @@ func (s *Server) initialLayerRPCAdmissionCursor(ctx context.Context, c *Conn) (l
if registryMsgID == 0 { if registryMsgID == 0 {
if cursor.evidenceMsgID == 0 { if cursor.evidenceMsgID == 0 {
cursor.state = LayerProfileSnapshot{} 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.state = LayerProfileSnapshot{Profile: profile, Origin: LayerProfileExplicit}
} }
cursor.rawLayer = layer cursor.rawLayer = layer
@ -466,7 +467,7 @@ func (s *Server) prepareInboundLayerRPCBatch(ctx context.Context, c *Conn, plan
} }
plan.rejectNewRPCOwners(indices) plan.rejectNewRPCOwners(indices)
for _, index := range candidateItems { for _, index := range candidateItems {
plan.items[index].admitted = tg.LayerRequest{} plan.items[index].admitted = tlprofile.Admission{}
} }
if err := reservation.retain(nil, nil); err != nil { if err := reservation.retain(nil, nil); err != nil {
return err return err
@ -534,7 +535,7 @@ func (s *Server) prepareInboundLayerRPCBatch(ctx context.Context, c *Conn, plan
} }
plan.rewrapAliases = keptAliases plan.rewrapAliases = keptAliases
for _, index := range candidateItems { for _, index := range candidateItems {
plan.items[index].admitted = tg.LayerRequest{} plan.items[index].admitted = tlprofile.Admission{}
} }
if err := reservation.retain(nil, nil); err != nil { if err := reservation.retain(nil, nil); err != nil {
return err return err
@ -581,7 +582,7 @@ func (s *Server) prepareInboundLayerRPCBatch(ctx context.Context, c *Conn, plan
} }
if len(specs) == 0 { if len(specs) == 0 {
for _, index := range candidateItems { for _, index := range candidateItems {
plan.items[index].admitted = tg.LayerRequest{} plan.items[index].admitted = tlprofile.Admission{}
} }
if err := reservation.retain(nil, nil); err != nil { if err := reservation.retain(nil, nil); err != nil {
return err 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 // Tasks now own the admitted request leases. Drop the plan's value copies
// before non-fresh reservations become reusable. // before non-fresh reservations become reusable.
for _, index := range candidateItems { for _, index := range candidateItems {
plan.items[index].admitted = tg.LayerRequest{} plan.items[index].admitted = tlprofile.Admission{}
} }
if err := reservation.retain(reservationIndices, specs); err != nil { if err := reservation.retain(reservationIndices, specs); err != nil {
return err return err
@ -628,7 +629,7 @@ func (s *Server) acquireAdmittedLayerRPC(
return rpcResultAcquire{}, ErrRPCResultFlightInvalid return rpcResultAcquire{}, ErrRPCResultFlightInvalid
} }
acquire := func() (rpcResultAcquire, error) { acquire := func() (rpcResultAcquire, error) {
profile := tg.LayerProfile(0) profile := tlprofile.Profile(0)
if effective, known := item.admitted.EffectiveProfile(); known { if effective, known := item.admitted.EffectiveProfile(); known {
profile = effective profile = effective
} }
@ -671,7 +672,7 @@ func (s *Server) acquireAdmittedLayerRPC(
return acquire() 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) preparer, ok := s.layerRPC.(LayerRPCReplayPreparer)
if !ok || c == nil { if !ok || c == nil {
return nil, 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 // admitInboundLayerRPC is the force-style compatibility entry point used by
// focused tests and old embedders. Production must call admitInboundLayerRPCAt // focused tests and old embedders. Production must call admitInboundLayerRPCAt
// with the real inner MTProto client msg_id. // 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) 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 { 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) request, method, err := s.decodeInboundLayerRPC(c.LayerProfileState(), body)
if err != nil { if err != nil {
return tg.LayerRequest{}, method, err return tlprofile.Admission{}, method, err
} }
if profile, hasEvidence := request.ProfileEvidence(); hasEvidence { if profile, hasEvidence := request.ProfileEvidence(); hasEvidence {
if _, err := s.commitLayerProfileEvidence(context.Background(), c, profile, msgID); err != nil { if _, err := s.commitLayerProfileEvidence(context.Background(), c, profile, msgID); err != nil {
if !isLayerEvidenceDurabilityUnavailable(err) { if !isLayerEvidenceDurabilityUnavailable(err) {
return tg.LayerRequest{}, method, err return tlprofile.Admission{}, method, err
} }
if msgID > 0 { if msgID > 0 {
if _, localErr := c.freezeLayerProfileAt(profile, msgID); localErr != nil { 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 // constructed, so the bounded fallback walks only transparent wrapper prefixes
// whose query offset is fixed and allocation-free. // whose query offset is fixed and allocation-free.
func layerRPCAdmissionHasExplicitSelector(body []byte, admissionErr error) bool { func layerRPCAdmissionHasExplicitSelector(body []byte, admissionErr error) bool {
var codecErr *tg.LayerCodecError var codecErr *tlprofile.LayerCodecError
if errors.As(admissionErr, &codecErr) && codecErr.Semantic == tg.LayerSemanticMethodInvokeWithLayer { if errors.As(admissionErr, &codecErr) && codecErr.Semantic == tlprofile.SemanticMethodInvokeWithLayer {
return true return true
} }
@ -784,13 +785,13 @@ func layerRPCAdmissionHasExplicitSelector(body []byte, admissionErr error) bool
// it with a wire-ordered provisional profile cursor, then publishes explicit // it with a wire-ordered provisional profile cursor, then publishes explicit
// evidence only after the full request identity has acquired an owner (or a // evidence only after the full request identity has acquired an owner (or a
// genuine new-msg_id rewrap alias). // 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 { 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} b := &bin.Buffer{Buf: body}
var ( var (
request tg.LayerRequest request tlprofile.Admission
err error err error
) )
if state.Origin != LayerProfileUnknown { if state.Origin != LayerProfileUnknown {
@ -806,19 +807,19 @@ func (s *Server) decodeInboundLayerRPC(state LayerProfileSnapshot, body []byte)
} }
method := "unknown" method := "unknown"
if err == nil { if err == nil {
_, method, _ = tg.LayerSemanticName(request.Call().Method()) _, method, _ = tlprofile.SemanticName(request.Call().Method())
if b.Len() != 0 { 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() { 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 // A generated invariant terminal may use canonical decoding internally
// before the client declares a layer. Only explicit invokeWithLayer (or the // before the client declares a layer. Only explicit invokeWithLayer (or the
// strict compatibility fallback above) publishes new profile evidence. // strict compatibility fallback above) publishes new profile evidence.
if profile, hasEvidence := request.ProfileEvidence(); hasEvidence { if profile, hasEvidence := request.ProfileEvidence(); hasEvidence {
if profile != request.Call().Profile() { 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 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 { if id, peekErr := (&bin.Buffer{Buf: body}).PeekID(); peekErr == nil {
method = s.typeName(id) 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 codecErr.Semantic != 0 {
if _, semanticMethod, ok := tg.LayerSemanticName(codecErr.Semantic); ok && semanticMethod != "" { if _, semanticMethod, ok := tlprofile.SemanticName(codecErr.Semantic); ok && semanticMethod != "" {
method = semanticMethod method = semanticMethod
} }
} else if codecErr.WireID != 0 { } else if codecErr.WireID != 0 {
@ -846,7 +847,7 @@ func (s *Server) decodeInboundLayerRPC(state LayerProfileSnapshot, body []byte)
method = s.typeName(codecErr.WireID) 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 { if terminal, recognized := wrappedDestroyAuthKeyTerminal(err); recognized {
method = "destroy_auth_key" method = "destroy_auth_key"
s.log.Debug("Generated wrapper admission exposed MTProto service terminal", 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)) zap.String("method", method), zap.Error(err))
} }
} }
return tg.LayerRequest{}, method, err return tlprofile.Admission{}, method, err
} }
// commitLayerProfileEvidence publishes one generated invokeWithLayer proof. // 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 // point; the Conn cursor then prevents a concurrent older admission from
// overwriting its local wire epoch. Older cached duplicates remain decodable // overwriting its local wire epoch. Older cached duplicates remain decodable
// and request-bound, but cannot mutate session/profile state. // 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 { if s == nil || c == nil {
return false, fmt.Errorf("invalid layer profile evidence target") 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 { } else if _, err := c.freezeRawLayerProfileAt(layer, authoritativeMsgID); err != nil {
return false, err return false, err
} }
authoritative, supported := tg.ResolveLayerProfile(layer) authoritative, supported := tlprofile.ResolveProfile(layer)
return supported && authoritative == profile && authoritativeMsgID == msgID && publishShared, nil return supported && authoritative == profile && authoritativeMsgID == msgID && publishShared, nil
} }
if registry, ok := s.layerRPC.(LayerRPCOrderedSessionProfileRegistry); ok { 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 { if !found || authoritativeMsgID <= 0 {
return false, fmt.Errorf("%w: ordered exact session evidence disappeared after commit", ErrLayerProfileConflict) 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 s.conns != nil {
if _, err := s.conns.ApplyOrderedRawLayerForSession(c, c.authKeyID, c.sessionID, layer, authoritativeMsgID); err != nil { if _, err := s.conns.ApplyOrderedRawLayerForSession(c, c.authKeyID, c.sessionID, layer, authoritativeMsgID); err != nil {
return false, err return false, err
@ -980,34 +981,34 @@ func layerRPCAdmissionError(err error) *mt.RPCError {
if errors.Is(err, errDefaultLayerAdmission) { if errors.Is(err, errDefaultLayerAdmission) {
return &mt.RPCError{ErrorCode: 400, ErrorMessage: "CONNECTION_LAYER_INVALID"} 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"} return &mt.RPCError{ErrorCode: 400, ErrorMessage: "CONNECTION_NOT_INITED"}
} }
var rpcErr *tgerr.Error var rpcErr *tgerr.Error
if errors.As(err, &rpcErr) { if errors.As(err, &rpcErr) {
return &mt.RPCError{ErrorCode: rpcErr.Code, ErrorMessage: rpcErr.Message} 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: 501, ErrorMessage: "NOT_IMPLEMENTED"}
} }
return &mt.RPCError{ErrorCode: 400, ErrorMessage: "INPUT_REQUEST_INVALID"} 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{} result := layerRPCDependencySet{}
seen := make(map[int64]struct{}) seen := make(map[int64]struct{})
for index := 0; index < request.WrapperCount(); index++ { for index := 0; index < request.WrapperCount(); index++ {
wrapper, _ := request.Wrapper(index) wrapper, _ := request.Wrapper(index)
var ids []int64 var ids []int64
switch wrapper.Semantic() { switch wrapper.Semantic() {
case tg.LayerSemanticMethodInvokeAfterMsg: case tlprofile.SemanticMethodInvokeAfterMsg:
id, err := layerRPCWrapperRequired[int64](wrapper, "msg_id") id, err := layerRPCWrapperRequired[int64](wrapper, "msg_id")
if err != nil { if err != nil {
result.failed = true result.failed = true
continue continue
} }
ids = []int64{id} ids = []int64{id}
case tg.LayerSemanticMethodInvokeAfterMsgs: case tlprofile.SemanticMethodInvokeAfterMsgs:
var err error var err error
ids, err = layerRPCWrapperRequired[[]int64](wrapper, "msg_ids") ids, err = layerRPCWrapperRequired[[]int64](wrapper, "msg_ids")
if err != nil || len(ids) > maxLayerRPCDependencyIDs { if err != nil || len(ids) > maxLayerRPCDependencyIDs {
@ -1045,16 +1046,16 @@ func (s *Server) layerRPCDependencies(c *Conn, msgID int64, request tg.LayerRequ
return result return result
} }
func admittedRPCRewrapInit(request tg.LayerRequest) (rpcRewrapInit, bool) { func admittedRPCRewrapInit(request tlprofile.Admission) (rpcRewrapInit, bool) {
if request.WrapperCount() != 2 { if request.WrapperCount() != 2 {
return rpcRewrapInit{}, false return rpcRewrapInit{}, false
} }
layerWrapper, ok := request.Wrapper(0) layerWrapper, ok := request.Wrapper(0)
if !ok || layerWrapper.Semantic() != tg.LayerSemanticMethodInvokeWithLayer { if !ok || layerWrapper.Semantic() != tlprofile.SemanticMethodInvokeWithLayer {
return rpcRewrapInit{}, false return rpcRewrapInit{}, false
} }
initWrapper, ok := request.Wrapper(1) initWrapper, ok := request.Wrapper(1)
if !ok || initWrapper.Semantic() != tg.LayerSemanticMethodInitConnection { if !ok || initWrapper.Semantic() != tlprofile.SemanticMethodInitConnection {
return rpcRewrapInit{}, false return rpcRewrapInit{}, false
} }
layer, err := layerRPCWrapperRequired[int](layerWrapper, "layer") layer, err := layerRPCWrapperRequired[int](layerWrapper, "layer")
@ -1095,7 +1096,7 @@ func admittedRPCRewrapInit(request tg.LayerRequest) (rpcRewrapInit, bool) {
}, true }, 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 var zero T
value, present, ok, err := wrapper.Value(name) value, present, ok, err := wrapper.Value(name)
if err != nil || !ok || !present { if err != nil || !ok || !present {

View file

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

View file

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

View file

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

View file

@ -7,7 +7,7 @@ import (
"sync" "sync"
"testing" "testing"
"github.com/iamxvbaba/td/tg" "github.com/iamxvbaba/td/tlprofile"
) )
type countingInheritedLayerResolver struct { type countingInheritedLayerResolver struct {
@ -40,66 +40,66 @@ func TestConnLayerProfileUnknownFreezeAndIdempotence(t *testing.T) {
t.Fatalf("initial LayerProfile = (%d, %v), want (0, false)", profile, ok) 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) 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) 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) t.Fatalf("LayerProfile = (%d, %v), want (225, true)", profile, ok)
} }
} }
func TestConnLayerProfileInheritedCanBeCorrectedExplicitly(t *testing.T) { func TestConnLayerProfileInheritedCanBeCorrectedExplicitly(t *testing.T) {
c := &Conn{} 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) t.Fatalf("seed inherited layer 225: %v", err)
} }
initial := c.LayerProfileState() 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) 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) t.Fatalf("repeat inherited seed: %v", err)
} }
if got := c.LayerProfileState(); got != initial { if got := c.LayerProfileState(); got != initial {
t.Fatalf("second inherited seed replaced selected default: got %#v want %#v", 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) t.Fatalf("promote inherited evidence: %v", err)
} }
promoted := c.LayerProfileState() 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) 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) t.Fatalf("correct explicit layer: %v", err)
} }
corrected := c.LayerProfileState() 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) t.Fatalf("corrected explicit state = %#v", corrected)
} }
} }
func TestConnSeedLayerProfile(t *testing.T) { func TestConnSeedLayerProfile(t *testing.T) {
c := &Conn{} 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) 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) 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) 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) t.Fatalf("LayerProfile = (%d, %v), want (226, true)", profile, ok)
} }
} }
func TestConnLayerProfileRejectsUnsupported(t *testing.T) { 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) { t.Run(fmt.Sprintf("layer_%d", profile), func(t *testing.T) {
c := &Conn{} c := &Conn{}
if err := c.FreezeLayerProfile(profile); !errors.Is(err, ErrLayerProfileUnsupported) { if err := c.FreezeLayerProfile(profile); !errors.Is(err, ErrLayerProfileUnsupported) {
@ -120,16 +120,16 @@ func TestConnLayerProfileConcurrentCorrectionsRemainAtomic(t *testing.T) {
c := &Conn{} c := &Conn{}
start := make(chan struct{}) start := make(chan struct{})
errs := make([]error, goroutines) errs := make([]error, goroutines)
profiles := make([]tg.LayerProfile, goroutines) profiles := make([]tlprofile.Profile, goroutines)
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(goroutines) wg.Add(goroutines)
for i := range goroutines { for i := range goroutines {
profile := tg.LayerProfile225 profile := tlprofile.Profile225
if i%2 != 0 { if i%2 != 0 {
profile = tg.LayerProfile227 profile = tlprofile.Profile227
} }
profiles[i] = profile profiles[i] = profile
go func(index int, requested tg.LayerProfile) { go func(index int, requested tlprofile.Profile) {
defer wg.Done() defer wg.Done()
<-start <-start
errs[index] = c.FreezeLayerProfile(requested) errs[index] = c.FreezeLayerProfile(requested)
@ -139,7 +139,7 @@ func TestConnLayerProfileConcurrentCorrectionsRemainAtomic(t *testing.T) {
wg.Wait() wg.Wait()
state := c.LayerProfileState() 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) t.Fatalf("concurrent final state = %#v, want supported explicit contender", state)
} }
if state.Epoch == 0 || state.Epoch > goroutines { if state.Epoch == 0 || state.Epoch > goroutines {
@ -154,23 +154,23 @@ func TestConnLayerProfileConcurrentCorrectionsRemainAtomic(t *testing.T) {
func TestConnLayerProfileEvidenceUsesClientMessageOrder(t *testing.T) { func TestConnLayerProfileEvidenceUsesClientMessageOrder(t *testing.T) {
c := &Conn{} c := &Conn{}
if err := c.seedOrderedLayerProfile(tg.LayerProfile225, 100); err != nil { if err := c.seedOrderedLayerProfile(tlprofile.Profile225, 100); err != nil {
t.Fatal(err) 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) t.Fatalf("newer correction applied=%v err=%v", applied, err)
} }
corrected := c.LayerProfileState() 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) t.Fatalf("old duplicate applied=%v err=%v", applied, err)
} }
if got := c.LayerProfileState(); got != corrected { if got := c.LayerProfileState(); got != corrected {
t.Fatalf("old duplicate changed profile: got %#v want %#v", 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) 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) t.Fatalf("same-layer newer evidence applied=%v err=%v", applied, err)
} }
state, msgID := c.layerProfileEvidenceState() state, msgID := c.layerProfileEvidenceState()
@ -185,10 +185,10 @@ func TestSessionManagerSeedsOnlyUnknownRawAuthKeyConnections(t *testing.T) {
unknown := &Conn{authKeyID: authKeyID, sessionID: 1} unknown := &Conn{authKeyID: authKeyID, sessionID: 1}
explicit := &Conn{authKeyID: authKeyID, sessionID: 2} explicit := &Conn{authKeyID: authKeyID, sessionID: 2}
inherited := &Conn{authKeyID: authKeyID, sessionID: 3} 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) t.Fatal(err)
} }
if err := inherited.SeedInheritedLayerProfile(tg.LayerProfile226); err != nil { if err := inherited.SeedInheritedLayerProfile(tlprofile.Profile226); err != nil {
t.Fatal(err) t.Fatal(err)
} }
for _, c := range []*Conn{unknown, explicit, inherited} { for _, c := range []*Conn{unknown, explicit, inherited} {
@ -200,13 +200,13 @@ func TestSessionManagerSeedsOnlyUnknownRawAuthKeyConnections(t *testing.T) {
if seeded := m.SeedInheritedLayerForRawAuthKey(authKeyID, 227); seeded != 1 { if seeded := m.SeedInheritedLayerForRawAuthKey(authKeyID, 227); seeded != 1 {
t.Fatalf("seeded connections = %d, want 1", seeded) 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) 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) 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) t.Fatalf("existing inherited connection was overwritten = %#v", got)
} }
if seeded := m.SeedInheritedLayerForRawAuthKey(authKeyID, 229); seeded != 0 { if seeded := m.SeedInheritedLayerForRawAuthKey(authKeyID, 229); seeded != 0 {
@ -220,10 +220,10 @@ func TestSessionManagerRefreshesInheritedRawKeyShadowAtBind(t *testing.T) {
unknown := &Conn{authKeyID: authKeyID, sessionID: 1} unknown := &Conn{authKeyID: authKeyID, sessionID: 1}
inherited := &Conn{authKeyID: authKeyID, sessionID: 2} inherited := &Conn{authKeyID: authKeyID, sessionID: 2}
explicit := &Conn{authKeyID: authKeyID, sessionID: 3} 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) t.Fatal(err)
} }
if err := explicit.FreezeLayerProfile(tg.LayerProfile225); err != nil { if err := explicit.FreezeLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err) t.Fatal(err)
} }
for _, c := range []*Conn{unknown, inherited, explicit} { for _, c := range []*Conn{unknown, inherited, explicit} {
@ -236,11 +236,11 @@ func TestSessionManagerRefreshesInheritedRawKeyShadowAtBind(t *testing.T) {
t.Fatalf("refreshed connections = %d, want 2", refreshed) t.Fatalf("refreshed connections = %d, want 2", refreshed)
} }
for name, c := range map[string]*Conn{"unknown": unknown, "inherited": inherited} { 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) 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) t.Fatalf("explicit evidence overwritten = %#v", got)
} }
} }
@ -251,10 +251,10 @@ func TestSessionManagerClearsOnlyInheritedRawKeyShadowAtBind(t *testing.T) {
inherited := &Conn{authKeyID: authKeyID, sessionID: 1} inherited := &Conn{authKeyID: authKeyID, sessionID: 1}
explicit := &Conn{authKeyID: authKeyID, sessionID: 2} explicit := &Conn{authKeyID: authKeyID, sessionID: 2}
unknown := &Conn{authKeyID: authKeyID, sessionID: 3} 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) t.Fatal(err)
} }
if err := explicit.seedOrderedLayerProfile(tg.LayerProfile227, 104); err != nil { if err := explicit.seedOrderedLayerProfile(tlprofile.Profile227, 104); err != nil {
t.Fatal(err) t.Fatal(err)
} }
for _, c := range []*Conn{inherited, explicit, unknown} { 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 { if got := inherited.LayerProfileState(); got.Origin != LayerProfileUnknown || got.Profile != 0 {
t.Fatalf("inherited shadow after clear = %#v, want unknown", got) 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) t.Fatalf("explicit evidence was cleared = %#v", got)
} }
if state, msgID := explicit.layerProfileEvidenceState(); state.Origin != LayerProfileExplicit || msgID != 104 { 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} second := &Conn{authKeyID: rawTwo, sessionID: 2}
explicit := &Conn{authKeyID: rawTwo, sessionID: 3} explicit := &Conn{authKeyID: rawTwo, sessionID: 3}
inherited := &Conn{authKeyID: rawOne, sessionID: 4} 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) t.Fatal(err)
} }
if err := inherited.SeedInheritedLayerProfile(tg.LayerProfile225); err != nil { if err := inherited.SeedInheritedLayerProfile(tlprofile.Profile225); err != nil {
t.Fatal(err) t.Fatal(err)
} }
for _, c := range []*Conn{first, second, explicit, inherited} { 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) t.Fatalf("business auth-key seeded=%d, want 2", seeded)
} }
for name, c := range map[string]*Conn{"first": first, "second": second} { 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) 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) 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) t.Fatalf("business seed overwrote inherited = %#v", got)
} }
} }
@ -325,7 +325,7 @@ func TestSessionManagerExplicitLayerEvidenceUsesLiveExactSession(t *testing.T) {
authKeyID := [8]byte{2, 2, 9} authKeyID := [8]byte{2, 2, 9}
const sessionID = int64(229) const sessionID = int64(229)
c := &Conn{authKeyID: authKeyID, sessionID: sessionID} 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) t.Fatal(err)
} }
if err := m.Register(c); err != nil { if err := m.Register(c); err != nil {
@ -336,7 +336,7 @@ func TestSessionManagerExplicitLayerEvidenceUsesLiveExactSession(t *testing.T) {
} }
inherited := &Conn{authKeyID: authKeyID, sessionID: sessionID + 1} 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) t.Fatal(err)
} }
if err := m.Register(inherited); err != nil { if err := m.Register(inherited); err != nil {
@ -353,10 +353,10 @@ func TestSessionManagerExplicitLayerEvidenceChoosesNewestActiveOrClaim(t *testin
const sessionID = int64(230) const sessionID = int64(230)
active := &Conn{authKeyID: authKeyID, sessionID: sessionID} active := &Conn{authKeyID: authKeyID, sessionID: sessionID}
claim := &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) t.Fatal(err)
} }
if err := claim.seedOrderedLayerProfile(tg.LayerProfile227, 104); err != nil { if err := claim.seedOrderedLayerProfile(tlprofile.Profile227, 104); err != nil {
t.Fatal(err) t.Fatal(err)
} }
key := sessionKey{authKeyID: authKeyID, sessionID: sessionID} key := sessionKey{authKeyID: authKeyID, sessionID: sessionID}
@ -384,15 +384,15 @@ func TestOrderedSessionLayerBroadcastConvergesAcrossPhysicalGenerations(t *testi
m.bySession[sessionKey{authKeyID: authKeyID, sessionID: sessionID}] = current m.bySession[sessionKey{authKeyID: authKeyID, sessionID: sessionID}] = current
m.mu.Unlock() 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) 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) t.Fatalf("delayed older broadcast applied=%d err=%v", applied, err)
} }
for name, c := range map[string]*Conn{"old": oldPhysical, "current": current} { for name, c := range map[string]*Conn{"old": oldPhysical, "current": current} {
state, msgID := c.layerProfileEvidenceState() 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) t.Fatalf("%s physical state = %#v msgID:%d", name, state, msgID)
} }
} }
@ -409,7 +409,7 @@ func TestInitialProfileSeedAvoidsPermanentKeyResolverAndPrefersPermForTemp(t *te
if resolver.calls != 0 { if resolver.calls != 0 {
t.Fatalf("permanent resolver calls = %d, want 0", resolver.calls) 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) t.Fatalf("permanent seed = %#v", got)
} }
}) })
@ -424,7 +424,7 @@ func TestInitialProfileSeedAvoidsPermanentKeyResolverAndPrefersPermForTemp(t *te
if resolver.calls != 1 { if resolver.calls != 1 {
t.Fatalf("temporary resolver calls = %d, want 1", resolver.calls) 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) t.Fatalf("temporary canonical seed = %#v", got)
} }
}) })
@ -465,7 +465,7 @@ func TestInitialProfileSeedRestoresOrderedExactSessionEvidence(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
state, msgID := c.layerProfileEvidenceState() 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) t.Fatalf("ordered exact seed = state:%#v msgID:%d", state, msgID)
} }
} }
@ -487,10 +487,10 @@ func TestInheritedLayerResolverAvailabilityUsesOnlySupportedRawTempShadow(t *tes
for _, tt := range []struct { for _, tt := range []struct {
name string name string
fetchedLayer int fetchedLayer int
wantProfile tg.LayerProfile wantProfile tlprofile.Profile
wantOrigin LayerProfileOrigin 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}, {name: "future raw shadow stays unknown", fetchedLayer: 229, wantOrigin: LayerProfileUnknown},
} { } {
t.Run(tt.name, func(t *testing.T) { 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 { if err := s.refreshActivatedInheritedLayerProfile(context.Background(), c, 225); err != nil {
t.Fatal(err) 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) 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) { t.Run("claim wins before bind refresh", func(t *testing.T) {
m := NewSessionManager(nil) m := NewSessionManager(nil)
c := &Conn{authKeyID: authKeyID, sessionID: 402, authKeyExpiresAt: 1_900_000_000} 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) t.Fatal(err)
} }
if err := m.BeginActivation(c); err != nil { if err := m.BeginActivation(c); err != nil {
@ -547,7 +547,7 @@ func TestActivationClaimRecheckClosesTempBindLayerRace(t *testing.T) {
if refreshed := m.RefreshInheritedLayerForRawAuthKey(authKeyID, 227); refreshed != 1 { if refreshed := m.RefreshInheritedLayerForRawAuthKey(authKeyID, 227); refreshed != 1 {
t.Fatalf("bind refresh count = %d, want 1", refreshed) 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) t.Fatalf("claim-visible bind refresh = %#v", got)
} }
}) })
@ -556,7 +556,7 @@ func TestActivationClaimRecheckClosesTempBindLayerRace(t *testing.T) {
resolver := &countingInheritedLayerResolver{layer: 229, found: true} resolver := &countingInheritedLayerResolver{layer: 229, found: true}
s := &Server{layerRPC: resolver} s := &Server{layerRPC: resolver}
c := &Conn{authKeyID: authKeyID, sessionID: 403, authKeyExpiresAt: 1_900_000_000} 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) t.Fatal(err)
} }
if err := s.refreshActivatedInheritedLayerProfile(context.Background(), c, 225); err != nil { 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 { if err := s.refreshActivatedInheritedLayerProfile(context.Background(), c, 225); err != nil {
t.Fatal(err) 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) 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")} resolver := &countingInheritedLayerResolver{err: errors.New("invalid binding identity")}
s := &Server{layerRPC: resolver} s := &Server{layerRPC: resolver}
c := &Conn{authKeyID: authKeyID, sessionID: 405, authKeyExpiresAt: 1_900_000_000} 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) t.Fatal(err)
} }
if err := s.refreshActivatedInheritedLayerProfile(context.Background(), c, 225); err != nil { 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/bin"
"github.com/iamxvbaba/td/mt" "github.com/iamxvbaba/td/mt"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tgerr" "github.com/iamxvbaba/td/tgerr"
"go.uber.org/zap" "go.uber.org/zap"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/observability/dbtrace" "telesrv/internal/observability/dbtrace"
"telesrv/internal/postresponse" "telesrv/internal/postresponse"
) )
@ -20,8 +20,8 @@ import (
// the inbound worker. The only Encode call happens later under outbound encode // the inbound worker. The only Encode call happens later under outbound encode
// and retained-byte admission. // and retained-byte admission.
type layerRPCResultEncoder struct { type layerRPCResultEncoder struct {
call tg.LayerCall call tlprofile.Call
result tg.LayerRPCResult result tlprofile.Result
} }
func (e *layerRPCResultEncoder) Encode(b *bin.Buffer) error { func (e *layerRPCResultEncoder) Encode(b *bin.Buffer) error {
@ -40,7 +40,6 @@ func (e *layerRPCResultEncoder) exactLayerRPCResultBinding() outboundLayerBindin
} }
return outboundLayerBinding{ return outboundLayerBinding{
profile: e.call.Profile(), profile: e.call.Profile(),
typ: e.call.WireResultType(),
wireInvariant: e.call.WireInvariant(), wireInvariant: e.call.WireInvariant(),
kind: outboundLayerBindingRequest, kind: outboundLayerBindingRequest,
} }
@ -55,7 +54,7 @@ type exactLayerRPCResultEncoder interface {
// hook onto the generated result codec. The hook may still exercise the old // 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. // scheduling API, but it no longer has a canonical-bytes escape hatch.
type legacyTestRPCResultEncoder struct { type legacyTestRPCResultEncoder struct {
call tg.LayerCall call tlprofile.Call
result bin.Encoder result bin.Encoder
} }
@ -63,7 +62,7 @@ func (e *legacyTestRPCResultEncoder) Encode(b *bin.Buffer) error {
if e == nil || e.result == nil { if e == nil || e.result == nil {
return errors.New("nil legacy test RPC result") 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 { func (e *legacyTestRPCResultEncoder) exactLayerRPCResultBinding() outboundLayerBinding {
@ -72,7 +71,6 @@ func (e *legacyTestRPCResultEncoder) exactLayerRPCResultBinding() outboundLayerB
} }
return outboundLayerBinding{ return outboundLayerBinding{
profile: e.call.Profile(), profile: e.call.Profile(),
typ: e.call.WireResultType(),
wireInvariant: e.call.WireInvariant(), wireInvariant: e.call.WireInvariant(),
kind: outboundLayerBindingRequest, 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 // result capability created from this exact admission; accepting a result from
// another request would pair the wrong result TypeRef/profile with this // another request would pair the wrong result TypeRef/profile with this
// flight/cache identity even when both methods happen to share a Go type. // 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 { if result == nil {
return nil, nil return nil, nil
} }
@ -101,7 +99,7 @@ func (s *Server) newInboundLayerRPCTask(
admissionSeq uint64, admissionSeq uint64,
method string, method string,
profileEvidenceFresh bool, profileEvidenceFresh bool,
request tg.LayerRequest, request tlprofile.Admission,
dependencies layerRPCDependencySet, dependencies layerRPCDependencySet,
owner *rpcResultOwnerLease, owner *rpcResultOwnerLease,
) inboundRPC { ) inboundRPC {
@ -202,7 +200,7 @@ func (s *Server) handleAdmittedLayerRPC(
msgID int64, msgID int64,
admissionSeq uint64, admissionSeq uint64,
method string, method string,
request tg.LayerRequest, request tlprofile.Admission,
owner *rpcResultOwnerLease, owner *rpcResultOwnerLease,
) error { ) error {
if s.layerRPC == nil { if s.layerRPC == nil {

View file

@ -11,24 +11,25 @@ import (
"github.com/iamxvbaba/td/mt" "github.com/iamxvbaba/td/mt"
"github.com/iamxvbaba/td/proto" "github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/tg" "github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
) )
// preparedOnlyLayerRPCResult is intentionally incapable of encoding. The // preparedOnlyLayerRPCResult is intentionally incapable of encoding. The
// binding guard must reject it solely from immutable admission identity before // binding guard must reject it solely from immutable admission identity before
// any result method other than Prepared can be observed. // any result method other than Prepared can be observed.
type preparedOnlyLayerRPCResult struct { type preparedOnlyLayerRPCResult struct {
tg.LayerRPCResult tlprofile.Result
prepared tg.LayerPreparedCall 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) { func TestBindAdmittedLayerRPCResultRequiresExactRequestIdentity(t *testing.T) {
dispatcher := tg.NewServerDispatcher(nil) dispatcher := tlprofile.NewDispatcher()
admit := func(request bin.Encoder) tg.LayerRequest { admit := func(request bin.Encoder) tlprofile.Admission {
t.Helper() t.Helper()
body := &bin.Buffer{Buf: exactLayerRPCBody(t, request)} 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -53,7 +54,7 @@ func TestBindAdmittedLayerRPCResultRequiresExactRequestIdentity(t *testing.T) {
type mismatchedProjectionLayerRPC struct { type mismatchedProjectionLayerRPC struct {
*admissionOnlyLayerRPC *admissionOnlyLayerRPC
result tg.LayerRPCResult result tlprofile.Result
calls atomic.Int32 calls atomic.Int32
} }
@ -63,18 +64,18 @@ func (h *mismatchedProjectionLayerRPC) DispatchAdmitted(
int64, int64,
int64, int64,
uint64, uint64,
tg.LayerRequest, tlprofile.Admission,
) (tg.LayerRPCResult, string, error) { ) (tlprofile.Result, string, error) {
h.calls.Add(1) h.calls.Add(1)
return h.result, "help.getConfig", nil return h.result, "help.getConfig", nil
} }
func TestProjectionFailureCachesInternalWithoutRepeatingBusiness(t *testing.T) { func TestProjectionFailureCachesInternalWithoutRepeatingBusiness(t *testing.T) {
dispatcher := tg.NewServerDispatcher(nil) dispatcher := tlprofile.NewDispatcher()
admit := func(request bin.Encoder) tg.LayerRequest { admit := func(request bin.Encoder) tlprofile.Admission {
t.Helper() t.Helper()
body := &bin.Buffer{Buf: exactLayerRPCBody(t, request)} 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -93,7 +94,7 @@ func TestProjectionFailureCachesInternalWithoutRepeatingBusiness(t *testing.T) {
const reqMsgID = int64(410100) const reqMsgID = int64(410100)
claim, err := s.rpcResults.AcquireLayerIdentified( claim, err := s.rpcResults.AcquireLayerIdentified(
c.authKeyID, c.sessionID, reqMsgID, c.authKeyID, c.sessionID, reqMsgID,
tg.LayerProfile227, request.Prepared().Identity(), tlprofile.Profile227, request.Prepared().Identity(),
) )
if err != nil || claim.owner == nil { if err != nil || claim.owner == nil {
t.Fatalf("owner acquisition err=%v", err) t.Fatalf("owner acquisition err=%v", err)
@ -110,7 +111,7 @@ func TestProjectionFailureCachesInternalWithoutRepeatingBusiness(t *testing.T) {
for { for {
completed, err = s.rpcResults.AcquireLayerIdentified( completed, err = s.rpcResults.AcquireLayerIdentified(
c.authKeyID, c.sessionID, reqMsgID, c.authKeyID, c.sessionID, reqMsgID,
tg.LayerProfile227, request.Prepared().Identity(), tlprofile.Profile227, request.Prepared().Identity(),
) )
if err == nil && completed.state == rpcResultAcquireCompleted { if err == nil && completed.state == rpcResultAcquireCompleted {
break break
@ -142,7 +143,7 @@ func TestProjectionFailureCachesInternalWithoutRepeatingBusiness(t *testing.T) {
// success. // success.
replay, err := s.rpcResults.AcquireLayerIdentified( replay, err := s.rpcResults.AcquireLayerIdentified(
c.authKeyID, c.sessionID, reqMsgID, 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 { if err != nil || replay.state != rpcResultAcquireCompleted || replay.encoded != completed.encoded {
t.Fatalf("projection replay = state:%d err:%v", replay.state, err) 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/bin"
"github.com/iamxvbaba/td/tg" "github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
) )
var ( var (
@ -36,8 +37,7 @@ const (
) )
type outboundLayerBinding struct { type outboundLayerBinding struct {
profile tg.LayerProfile profile tlprofile.Profile
typ *tg.LayerTypeRef
wireInvariant bool wireInvariant bool
kind outboundLayerBindingKind kind outboundLayerBindingKind
// epoch is required for proactive updates. Zero is accepted only for older // 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 // layerUpdatesFanout is one immutable canonical Updates snapshot plus a
// request-scoped cache of exact prepared bytes. FreezeLayer and // request-scoped cache of exact prepared bytes. FreezeObject and
// PrepareFrozenLayer are the same generated TypeRef codec used by RPC results // FrozenObject.Prepare use the same sparse TypeRef execution plans as RPC results
// and differences; this type adds only fan-out singleflight and ownership. // and differences; this type adds only fan-out singleflight and ownership.
type layerUpdatesFanout struct { type layerUpdatesFanout struct {
frozen tg.LayerFrozen[tg.UpdatesClass] frozen *tlprofile.FrozenObject
size int size int
mu sync.Mutex mu sync.Mutex
prepared map[tg.LayerProfile]*preparedLayerUpdates prepared map[tlprofile.Profile]*preparedLayerUpdates
} }
func newLayerUpdatesFanout(value tg.UpdatesClass) (*layerUpdatesFanout, error) { func newLayerUpdatesFanout(value tg.UpdatesClass) (*layerUpdatesFanout, error) {
frozen, err := tg.FreezeLayer(tg.LayerClassUpdatesType(), value) frozen, err := tlprofile.FreezeObject(value)
if err != nil { if err != nil {
return nil, fmt.Errorf("freeze exact layer updates: %w", err) return nil, fmt.Errorf("freeze exact layer updates: %w", err)
} }
return &layerUpdatesFanout{ return &layerUpdatesFanout{
frozen: frozen, frozen: frozen,
size: frozen.CanonicalSize(), size: frozen.CanonicalSize(),
prepared: make(map[tg.LayerProfile]*preparedLayerUpdates), prepared: make(map[tlprofile.Profile]*preparedLayerUpdates),
}, nil }, nil
} }
@ -107,7 +107,7 @@ func (u *layerUpdatesFanout) prepareForConn(ctx context.Context, c *Conn) (*enco
return &encoded, nil 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 { if ctx == nil {
ctx = context.Background() ctx = context.Background()
} }
@ -149,7 +149,7 @@ func (u *layerUpdatesFanout) prepare(ctx context.Context, profile tg.LayerProfil
return entry.encoded, entry.err 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 { if u == nil || encoded == nil {
return return
} }
@ -172,18 +172,13 @@ func (u *layerUpdatesFanout) discardPrepared(profile tg.LayerProfile, encoded *e
func prepareFrozenLayerUpdatesContext( func prepareFrozenLayerUpdatesContext(
ctx context.Context, ctx context.Context,
profile tg.LayerProfile, profile tlprofile.Profile,
frozen tg.LayerFrozen[tg.UpdatesClass], frozen *tlprofile.FrozenObject,
) (*encodedOutboundMessage, error) { ) (*encodedOutboundMessage, error) {
var encoded *encodedOutboundMessage var encoded *encodedOutboundMessage
err := withOutboundEncodeSlot(ctx, nil, func() error { err := withOutboundEncodeSlot(ctx, nil, func() error {
prepared, err := tg.PrepareFrozenLayer(profile, frozen)
if err != nil {
return err
}
var body bin.Buffer var body bin.Buffer
typ := tg.LayerClassUpdatesType() if err := frozen.Encode(profile, &body); err != nil {
if err := prepared.Encode(profile, typ, &body); err != nil {
return err return err
} }
id, err := body.PeekID() id, err := body.PeekID()
@ -192,7 +187,7 @@ func prepareFrozenLayerUpdatesContext(
} }
encoded = &encodedOutboundMessage{ encoded = &encodedOutboundMessage{
body: body.Copy(), typeID: id, body: body.Copy(), typeID: id,
layer: &outboundLayerBinding{profile: profile, typ: prepared.TypeRef()}, layer: &outboundLayerBinding{profile: profile},
} }
return nil return nil
}) })
@ -206,9 +201,6 @@ func validateOutboundLayerBinding(c *Conn, encoded *encodedOutboundMessage) erro
if encoded == nil || encoded.layer == nil { if encoded == nil || encoded.layer == nil {
return 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 { if encoded.layer.wireInvariant || encoded.layer.kind == outboundLayerBindingRequest {
return nil return nil
} }

View file

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

View file

@ -16,6 +16,7 @@ import (
"github.com/iamxvbaba/td/mt" "github.com/iamxvbaba/td/mt"
"github.com/iamxvbaba/td/proto" "github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/tg" "github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"github.com/iamxvbaba/td/transport" "github.com/iamxvbaba/td/transport"
) )
@ -545,7 +546,7 @@ func TestOutboundActorSerializesConcurrentSends(t *testing.T) {
clientMsgID := proto.NewMessageIDGen(time.Now) clientMsgID := proto.NewMessageIDGen(time.Now)
sendEncrypted(t, conn, cipher, auth, clientMsgID.New(proto.MessageFromClient), &mt.PingRequest{PingID: 1}) sendEncrypted(t, conn, cipher, auth, clientMsgID.New(proto.MessageFromClient), &mt.PingRequest{PingID: 1})
collectReplies(t, conn, cipher, auth.AuthKey, mt.MsgsAckTypeID) 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) srv.Conns().SetReceivesUpdates(auth.SessionID, true)
const sends = 64 const sends = 64
@ -1035,7 +1036,7 @@ func TestOutboundResendAndAckState(t *testing.T) {
clientMsgID := proto.NewMessageIDGen(time.Now) clientMsgID := proto.NewMessageIDGen(time.Now)
sendEncrypted(t, conn, cipher, auth, clientMsgID.New(proto.MessageFromClient), &mt.PingRequest{PingID: 1}) sendEncrypted(t, conn, cipher, auth, clientMsgID.New(proto.MessageFromClient), &mt.PingRequest{PingID: 1})
collectReplies(t, conn, cipher, auth.AuthKey, mt.MsgsAckTypeID) 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) srv.Conns().SetReceivesUpdates(auth.SessionID, true)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 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/mt"
"github.com/iamxvbaba/td/proto" "github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/tg" "github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
) )
// TestSetReceivesUpdatesFlushesPendingBeforeActivation 验证置位时先排空暂存推送 // TestSetReceivesUpdatesFlushesPendingBeforeActivation 验证置位时先排空暂存推送
@ -23,7 +24,7 @@ func TestSetReceivesUpdatesFlushesPendingBeforeActivation(t *testing.T) {
collectReplies(t, conn, cipher, auth.AuthKey, mt.MsgsAckTypeID) collectReplies(t, conn, cipher, auth.AuthKey, mt.MsgsAckTypeID)
raw := auth.AuthKey.ID 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() ctx := context.Background()
// 完全就绪还要求 membership 路由建立(ReceivesUpdatesForAuthKey 的另一半条件)。 // 完全就绪还要求 membership 路由建立(ReceivesUpdatesForAuthKey 的另一半条件)。

View file

@ -11,6 +11,7 @@ import (
"github.com/iamxvbaba/td/bin" "github.com/iamxvbaba/td/bin"
"github.com/iamxvbaba/td/proto" "github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/tg" "github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
) )
var ErrSessionAmbiguous = errors.New("session id is shared by multiple auth keys") 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() m.mu.RLock()
c, _, ok, ambiguous := m.uniqueSessionForTestLocked(sessionID) c, _, ok, ambiguous := m.uniqueSessionForTestLocked(sessionID)
m.mu.RUnlock() m.mu.RUnlock()

View file

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

View file

@ -9,19 +9,20 @@ import (
"github.com/iamxvbaba/td/bin" "github.com/iamxvbaba/td/bin"
"github.com/iamxvbaba/td/tg" "github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
) )
func rpcFlightTestAuthID(seed byte) [8]byte { func rpcFlightTestAuthID(seed byte) [8]byte {
return [8]byte{seed, seed + 1, seed + 2, seed + 3} 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() t.Helper()
var body bin.Buffer 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) 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 { if err != nil {
t.Fatalf("admit exact request: %v", err) t.Fatalf("admit exact request: %v", err)
} }
@ -223,8 +224,8 @@ func TestRPCResultFlightRepeatedReplayJoinsStayBoundedAndPutCleansExecution(t *t
func TestRPCResultFlightExactIdentityGuardsPendingAndCompletedReuse(t *testing.T) { func TestRPCResultFlightExactIdentityGuardsPendingAndCompletedReuse(t *testing.T) {
cache := newRPCResultCacheWithFlightLimit(time.Now, 2) cache := newRPCResultCacheWithFlightLimit(time.Now, 2)
authKeyID := rpcFlightTestAuthID(90) authKeyID := rpcFlightTestAuthID(90)
firstIdentity := rpcFlightExactIdentity(t, tg.LayerProfile225, &tg.HelpGetConfigRequest{}) firstIdentity := rpcFlightExactIdentity(t, tlprofile.Profile225, &tg.HelpGetConfigRequest{})
otherIdentity := rpcFlightExactIdentity(t, tg.LayerProfile225, &tg.HelpGetNearestDCRequest{}) otherIdentity := rpcFlightExactIdentity(t, tlprofile.Profile225, &tg.HelpGetNearestDCRequest{})
owner, err := cache.AcquireIdentified(authKeyID, 90, 900, firstIdentity) owner, err := cache.AcquireIdentified(authKeyID, 90, 900, firstIdentity)
if err != nil || owner.state != rpcResultAcquireOwner || owner.owner == nil { if err != nil || owner.state != rpcResultAcquireOwner || owner.owner == nil {
@ -255,23 +256,23 @@ func TestRPCResultFlightExactIdentityGuardsPendingAndCompletedReuse(t *testing.T
func TestRPCResultFlightAdmissionSequenceAllocatedOnceAndReplayed(t *testing.T) { func TestRPCResultFlightAdmissionSequenceAllocatedOnceAndReplayed(t *testing.T) {
cache := newRPCResultCacheWithFlightLimit(time.Now, 4) cache := newRPCResultCacheWithFlightLimit(time.Now, 4)
authKeyID := rpcFlightTestAuthID(89) authKeyID := rpcFlightTestAuthID(89)
identity := rpcFlightExactIdentity(t, tg.LayerProfile225, &tg.HelpGetConfigRequest{}) identity := rpcFlightExactIdentity(t, tlprofile.Profile225, &tg.HelpGetConfigRequest{})
owner, err := cache.AcquireLayerIdentified(authKeyID, 89, 890, tg.LayerProfile225, identity) owner, err := cache.AcquireLayerIdentified(authKeyID, 89, 890, tlprofile.Profile225, identity)
if err != nil || owner.state != rpcResultAcquireOwner || owner.owner == nil || owner.admissionSeq == 0 { 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) 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 { 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) t.Fatalf("pending = state:%d seq:%d err:%v, want seq:%d", pending.state, pending.admissionSeq, err, owner.admissionSeq)
} }
owner.owner.CompleteExecution(true) owner.owner.CompleteExecution(true)
encoded := &encodedOutboundMessage{body: []byte{1}, reqMsgID: 890} encoded := &encodedOutboundMessage{body: []byte{1}, reqMsgID: 890}
cache.Put(authKeyID, 89, 890, encoded) 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 { 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) 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 { if err != nil || second.admissionSeq <= owner.admissionSeq {
t.Fatalf("second owner seq=%d err=%v, want > %d", second.admissionSeq, err, 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) { func TestRPCAdmissionSafeFloorTracksOwnersUntilPutOrAbort(t *testing.T) {
cache := newRPCResultCacheWithFlightLimit(time.Now, 4) cache := newRPCResultCacheWithFlightLimit(time.Now, 4)
authKeyID := rpcFlightTestAuthID(86) authKeyID := rpcFlightTestAuthID(86)
identity := rpcFlightExactIdentity(t, tg.LayerProfile225, &tg.HelpGetConfigRequest{}) identity := rpcFlightExactIdentity(t, tlprofile.Profile225, &tg.HelpGetConfigRequest{})
first, err := cache.AcquireLayerIdentified(authKeyID, 86, 860, tg.LayerProfile225, identity) first, err := cache.AcquireLayerIdentified(authKeyID, 86, 860, tlprofile.Profile225, identity)
if err != nil || first.owner == nil { if err != nil || first.owner == nil {
t.Fatalf("first owner err=%v", err) 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 { if err != nil || second.owner == nil {
t.Fatalf("second owner err=%v", err) t.Fatalf("second owner err=%v", err)
} }
@ -315,12 +316,12 @@ func TestRPCAdmissionSequenceExhaustionCannotWrap(t *testing.T) {
cache := newRPCResultCacheWithFlightLimit(time.Now, 2) cache := newRPCResultCacheWithFlightLimit(time.Now, 2)
cache.nextAdmissionSeq.Store(^uint64(0) - 1) cache.nextAdmissionSeq.Store(^uint64(0) - 1)
authKeyID := rpcFlightTestAuthID(85) authKeyID := rpcFlightTestAuthID(85)
identity := rpcFlightExactIdentity(t, tg.LayerProfile225, &tg.HelpGetConfigRequest{}) identity := rpcFlightExactIdentity(t, tlprofile.Profile225, &tg.HelpGetConfigRequest{})
last, err := cache.AcquireLayerIdentified(authKeyID, 85, 850, tg.LayerProfile225, identity) last, err := cache.AcquireLayerIdentified(authKeyID, 85, 850, tlprofile.Profile225, identity)
if err != nil || last.admissionSeq != ^uint64(0) || last.owner == nil { 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) 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) t.Fatalf("post-max allocation err=%v, want %v", err, ErrRPCAdmissionSeqExhausted)
} }
if got := cache.nextAdmissionSeq.Load(); got != ^uint64(0) { if got := cache.nextAdmissionSeq.Load(); got != ^uint64(0) {
@ -333,15 +334,15 @@ func TestRPCIdentityMismatchCarriesWinnerProfileAcrossAbort(t *testing.T) {
cache := newRPCResultCacheWithFlightLimit(time.Now, 2) cache := newRPCResultCacheWithFlightLimit(time.Now, 2)
authKeyID := rpcFlightTestAuthID(88) authKeyID := rpcFlightTestAuthID(88)
request := &tg.MessagesGetHistoryRequest{Peer: &tg.InputPeerSelf{}, Limit: 1} request := &tg.MessagesGetHistoryRequest{Peer: &tg.InputPeerSelf{}, Limit: 1}
winnerIdentity := rpcFlightExactIdentity(t, tg.LayerProfile225, request) winnerIdentity := rpcFlightExactIdentity(t, tlprofile.Profile225, request)
loserIdentity := rpcFlightExactIdentity(t, tg.LayerProfile227, request) loserIdentity := rpcFlightExactIdentity(t, tlprofile.Profile227, request)
winner, err := cache.AcquireLayerIdentified(authKeyID, 88, 880, tg.LayerProfile225, winnerIdentity) winner, err := cache.AcquireLayerIdentified(authKeyID, 88, 880, tlprofile.Profile225, winnerIdentity)
if err != nil || winner.owner == nil { if err != nil || winner.owner == nil {
t.Fatalf("winner owner err=%v", err) 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 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) t.Fatalf("mismatch = %#v err=%v", mismatch, err)
} }
if !winner.owner.Abort() { if !winner.owner.Abort() {
@ -358,17 +359,17 @@ func TestRPCAdmissionProfileHintSurvivesCompletedEvictionWindow(t *testing.T) {
now := time.Unix(1_900_000_000, 0) now := time.Unix(1_900_000_000, 0)
cache := newRPCResultCacheWithFlightLimit(func() time.Time { return now }, 2) cache := newRPCResultCacheWithFlightLimit(func() time.Time { return now }, 2)
authKeyID := rpcFlightTestAuthID(87) authKeyID := rpcFlightTestAuthID(87)
identity := rpcFlightExactIdentity(t, tg.LayerProfile225, &tg.MessagesGetHistoryRequest{ identity := rpcFlightExactIdentity(t, tlprofile.Profile225, &tg.MessagesGetHistoryRequest{
Peer: &tg.InputPeerSelf{}, Limit: 1, 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 { if err != nil || claim.owner == nil {
t.Fatalf("owner err=%v", err) t.Fatalf("owner err=%v", err)
} }
claim.owner.CompleteExecution(true) claim.owner.CompleteExecution(true)
cache.Put(authKeyID, 87, 870, &encodedOutboundMessage{body: []byte{1}, reqMsgID: 870}) cache.Put(authKeyID, 87, 870, &encodedOutboundMessage{body: []byte{1}, reqMsgID: 870})
profile, ok := cache.ExactAdmissionProfile(authKeyID, 87, 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) t.Fatalf("profile hint = (%d,%v)", profile, ok)
} }
// Admission already copied the hint into its local decoder cursor. Expiry // 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) { func TestRPCInvariantIdentityDoesNotExposeCanonicalProfileHint(t *testing.T) {
cache := newRPCResultCacheWithFlightLimit(time.Now, 2) cache := newRPCResultCacheWithFlightLimit(time.Now, 2)
authKeyID := rpcFlightTestAuthID(84) 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"), PermAuthKeyID: 1, Nonce: 2, ExpiresAt: 3, EncryptedMessage: []byte("bind"),
}) })
claim, err := cache.AcquireLayerIdentified(authKeyID, 84, 840, 0, identity) 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/crypto"
"github.com/iamxvbaba/td/proto" "github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/tg" "github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
) )
type opaqueRPCResult struct{ body []byte } type opaqueRPCResult struct{ body []byte }
@ -491,8 +492,7 @@ func encodedRPCResultForPriorityTest(reqMsgID int64, payloadBytes int) *encodedO
return &encodedOutboundMessage{ return &encodedOutboundMessage{
typeID: proto.ResultTypeID, reqMsgID: reqMsgID, body: b.Raw(), typeID: proto.ResultTypeID, reqMsgID: reqMsgID, body: b.Raw(),
layer: &outboundLayerBinding{ layer: &outboundLayerBinding{
profile: tg.LayerProfileCanonical, profile: tlprofile.ProfileCanonical,
typ: tg.LayerClassBoolType().Ref(),
kind: outboundLayerBindingRequest, kind: outboundLayerBindingRequest,
}, },
} }

View file

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

View file

@ -17,11 +17,12 @@ import (
"github.com/iamxvbaba/td/tgerr" "github.com/iamxvbaba/td/tgerr"
"github.com/iamxvbaba/td/transport" "github.com/iamxvbaba/td/transport"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/rpc" "telesrv/internal/rpc"
) )
// TestRPCGetConfig 验证 M3:握手后 client 加密 help.getConfig, // 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) { func TestRPCGetConfig(t *testing.T) {
const ( const (
dc = 2 dc = 2
@ -80,7 +81,7 @@ func TestLayerRPCGetConfigUsesExactAdmittedProfile(t *testing.T) {
clientMsgID := proto.NewMessageIDGen(time.Now) clientMsgID := proto.NewMessageIDGen(time.Now)
reqMsgID := clientMsgID.New(proto.MessageFromClient) reqMsgID := clientMsgID.New(proto.MessageFromClient)
request := &tg.InvokeWithLayerRequest{ request := &tg.InvokeWithLayerRequest{
Layer: int(tg.LayerProfile225), Layer: int(tlprofile.Profile225),
Query: &tg.InitConnectionRequest{ Query: &tg.InitConnectionRequest{
APIID: 123, APIID: 123,
DeviceModel: "Desktop", DeviceModel: "Desktop",
@ -104,10 +105,14 @@ func TestLayerRPCGetConfigUsesExactAdmittedProfile(t *testing.T) {
t.Fatalf("rpc_result req_msg_id = %d, want %d", result.RequestMessageID, reqMsgID) t.Fatalf("rpc_result req_msg_id = %d, want %d", result.RequestMessageID, reqMsgID)
} }
exact := &bin.Buffer{Buf: result.Result} 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 { if err != nil {
t.Fatalf("decode layer 225 config: %v", err) 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 { if exact.Len() != 0 || config.ThisDC != dc {
t.Fatalf("layer 225 config = dc:%d remaining:%d", config.ThisDC, exact.Len()) 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/tmap"
"github.com/iamxvbaba/td/transport" "github.com/iamxvbaba/td/transport"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/store" "telesrv/internal/store"
"telesrv/internal/store/memory" "telesrv/internal/store/memory"
) )
@ -57,25 +58,25 @@ type legacyRPCHandlerWithMethod interface {
// validate wrapper dependencies and establish exact request identity before // validate wrapper dependencies and establish exact request identity before
// flight/cache/scheduler ownership is acquired. // flight/cache/scheduler ownership is acquired.
type LayerRPCHandler interface { type LayerRPCHandler interface {
AdmitLayer(profile tg.LayerProfile, 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 tg.LayerDecodeLimits) (tg.LayerRequest, error) AdmitUnprofiled(b *bin.Buffer, limits tlprofile.Limits) (tlprofile.Admission, error)
DispatchAdmitted( DispatchAdmitted(
ctx context.Context, ctx context.Context,
authKeyID [8]byte, authKeyID [8]byte,
sessionID int64, sessionID int64,
msgID int64, msgID int64,
admissionSeq uint64, admissionSeq uint64,
request tg.LayerRequest, request tlprofile.Admission,
) (tg.LayerRPCResult, string, error) ) (tlprofile.Result, string, error)
} }
// LayerRPCDefaultProfileAdmitter decodes with a recoverable inherited/default // LayerRPCDefaultProfileAdmitter decodes with a recoverable inherited/default
// profile. Production handlers should implement it with the same generated // profile. Production handlers should implement it with the same sparse
// ServerDispatcher and adapter registry used by AdmitLayer. The split keeps old // tlprofile dispatcher and semantic adapter registry used by AdmitLayer. The split keeps old
// test doubles source-compatible while allowing invokeWithLayer to correct even // test doubles source-compatible while allowing invokeWithLayer to correct even
// a previously explicit Conn profile. // a previously explicit Conn profile.
type LayerRPCDefaultProfileAdmitter interface { 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 // LayerRPCSessionProfileResolver may restore an exact profile only when it was
@ -168,7 +169,7 @@ type LayerRPCReplayPreparer interface {
sessionID int64, sessionID int64,
msgID int64, msgID int64,
admissionSeq uint64, admissionSeq uint64,
request tg.LayerRequest, request tlprofile.Admission,
) (afterSuccessfulDelivery func() error, err error) ) (afterSuccessfulDelivery func() error, err error)
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -7,99 +7,241 @@ import (
"github.com/iamxvbaba/td/tg" "github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
ioscompat "telesrv/internal/compat/ios" ioscompat "telesrv/internal/compat/ios"
"telesrv/internal/compat/tdesktop" "telesrv/internal/compat/tdesktop"
"telesrv/internal/domain" "telesrv/internal/domain"
) )
// registerAccount 注册 account.* RPC handler。 // registerAccount 注册 account.* RPC handler。
func (r *Router) registerAccount(d *tg.ServerDispatcher) { func (r *Router) registerAccount(d *tlprofile.Dispatcher) {
d.OnAccountRegisterDevice(func(ctx context.Context, req *tg.AccountRegisterDeviceRequest) (bool, error) { registerRPC[*tg.AccountRegisterDeviceRequest](d, tlprofile.SemanticMethodAccountRegisterDevice, func(ctx context.Context, req *tg.AccountRegisterDeviceRequest) (any, error) {
return true, nil 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 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 { if _, _, err := r.currentUserID(ctx); err != nil {
return false, internalErr() return false, internalErr()
} }
return ioscompat.DeviceLockedUpdated(), nil return ioscompat.DeviceLockedUpdated(), nil
}) })
d.OnAccountSendChangePhoneCode(r.onAccountSendChangePhoneCode) registerRPC[*tg.AccountSendChangePhoneCodeRequest](d, tlprofile.SemanticMethodAccountSendChangePhoneCode, func(ctx context.Context, layerRequest *tg.AccountSendChangePhoneCodeRequest) (any, error) {
d.OnAccountChangePhone(r.onAccountChangePhone) return r.onAccountSendChangePhoneCode(ctx, layerRequest)
d.OnAccountCheckUsername(r.onAccountCheckUsername) })
d.OnAccountUpdateProfile(r.onAccountUpdateProfile) registerRPC[*tg.AccountChangePhoneRequest](d, tlprofile.SemanticMethodAccountChangePhone, func(ctx context.Context, layerRequest *tg.AccountChangePhoneRequest) (any, error) {
d.OnAccountUpdateUsername(r.onAccountUpdateUsername) return r.onAccountChangePhone(ctx, layerRequest)
d.OnAccountUpdateBirthday(r.onAccountUpdateBirthday) })
d.OnAccountUpdatePersonalChannel(r.onAccountUpdatePersonalChannel) registerRPC[*tg.AccountCheckUsernameRequest](d, tlprofile.SemanticMethodAccountCheckUsername, func(ctx context.Context, layerRequest *tg.AccountCheckUsernameRequest) (any, error) {
d.OnAccountGetPassword(r.onAccountGetPassword) return r.onAccountCheckUsername(ctx, layerRequest.
d.OnAccountGetNotifySettings(r.onAccountGetNotifySettings) Username)
d.OnAccountUpdateNotifySettings(r.onAccountUpdateNotifySettings) })
d.OnAccountResetNotifySettings(r.onAccountResetNotifySettings) registerRPC[*tg.AccountUpdateProfileRequest](d, tlprofile.SemanticMethodAccountUpdateProfile, func(ctx context.Context, layerRequest *tg.AccountUpdateProfileRequest) (any, error) {
d.OnAccountGetPrivacy(r.onAccountGetPrivacy) return r.onAccountUpdateProfile(ctx, layerRequest)
d.OnAccountSetPrivacy(r.onAccountSetPrivacy) })
d.OnAccountGetAuthorizations(r.onAccountGetAuthorizations) registerRPC[*tg.AccountUpdateUsernameRequest](d, tlprofile.SemanticMethodAccountUpdateUsername, func(ctx context.Context, layerRequest *tg.AccountUpdateUsernameRequest) (any, error) {
d.OnAccountResetAuthorization(r.onAccountResetAuthorization) return r.onAccountUpdateUsername(ctx, layerRequest.
d.OnAccountGetPasswordSettings(r.onAccountGetPasswordSettings) Username)
d.OnAccountUpdatePasswordSettings(r.onAccountUpdatePasswordSettings) })
d.OnAccountConfirmPasswordEmail(r.onAccountConfirmPasswordEmail) registerRPC[*tg.AccountUpdateBirthdayRequest](d, tlprofile.SemanticMethodAccountUpdateBirthday, func(ctx context.Context, layerRequest *tg.AccountUpdateBirthdayRequest) (any, error) {
d.OnAccountResendPasswordEmail(r.onAccountResendPasswordEmail) return r.onAccountUpdateBirthday(ctx, layerRequest)
d.OnAccountCancelPasswordEmail(r.onAccountCancelPasswordEmail) })
d.OnAccountSendVerifyEmailCode(r.onAccountSendVerifyEmailCode) registerRPC[*tg.AccountUpdatePersonalChannelRequest](d, tlprofile.SemanticMethodAccountUpdatePersonalChannel, func(ctx context.Context, layerRequest *tg.AccountUpdatePersonalChannelRequest) (any, error) {
d.OnAccountVerifyEmail(r.onAccountVerifyEmail) return r.onAccountUpdatePersonalChannel(ctx, layerRequest.
d.OnAccountGetDefaultEmojiStatuses(r.onAccountGetDefaultEmojiStatuses) Channel)
d.OnAccountGetCollectibleEmojiStatuses(func(ctx context.Context, hash int64) (tg.AccountEmojiStatusesClass, error) { })
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 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 return tdesktop.DefaultGroupPhotoEmojis(), nil
}) })
d.OnAccountGetConnectedBots(r.onAccountGetConnectedBots) registerRPC[*tg.AccountGetConnectedBotsRequest](d, tlprofile.SemanticMethodAccountGetConnectedBots, func(ctx context.Context, layerRequest *tg.AccountGetConnectedBotsRequest) (any, error) {
d.OnAccountUpdateBusinessWorkHours(r.onAccountUpdateBusinessWorkHours) return r.onAccountGetConnectedBots(ctx)
d.OnAccountUpdateBusinessLocation(r.onAccountUpdateBusinessLocation) })
d.OnAccountUpdateBusinessIntro(r.onAccountUpdateBusinessIntro) registerRPC[*tg.AccountUpdateBusinessWorkHoursRequest](d, tlprofile.SemanticMethodAccountUpdateBusinessWorkHours, func(ctx context.Context, layerRequest *tg.AccountUpdateBusinessWorkHoursRequest) (any, error) {
d.OnAccountUpdateBusinessGreetingMessage(r.onAccountUpdateBusinessGreetingMessage) return r.onAccountUpdateBusinessWorkHours(ctx, layerRequest)
d.OnAccountUpdateBusinessAwayMessage(r.onAccountUpdateBusinessAwayMessage) })
d.OnAccountGetBusinessChatLinks(r.onAccountGetBusinessChatLinks) registerRPC[*tg.AccountUpdateBusinessLocationRequest](d, tlprofile.SemanticMethodAccountUpdateBusinessLocation, func(ctx context.Context, layerRequest *tg.AccountUpdateBusinessLocationRequest) (any, error) {
d.OnAccountCreateBusinessChatLink(r.onAccountCreateBusinessChatLink) return r.onAccountUpdateBusinessLocation(ctx, layerRequest)
d.OnAccountEditBusinessChatLink(r.onAccountEditBusinessChatLink) })
d.OnAccountDeleteBusinessChatLink(r.onAccountDeleteBusinessChatLink) registerRPC[*tg.AccountUpdateBusinessIntroRequest](d, tlprofile.SemanticMethodAccountUpdateBusinessIntro, func(ctx context.Context, layerRequest *tg.AccountUpdateBusinessIntroRequest) (any, error) {
d.OnAccountResolveBusinessChatLink(r.onAccountResolveBusinessChatLink) return r.onAccountUpdateBusinessIntro(ctx, layerRequest)
d.OnAccountUpdateConnectedBot(r.onAccountUpdateConnectedBot) })
d.OnAccountGetBotBusinessConnection(func(ctx context.Context, connectionID string) (tg.UpdatesClass, error) { 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 { if _, _, err := r.currentUserID(ctx); err != nil {
return nil, internalErr() return nil, internalErr()
} }
return nil, tgerr400("BOT_BUSINESS_MISSING") return nil, tgerr400("BOT_BUSINESS_MISSING")
}) })
d.OnAccountToggleConnectedBotPaused(r.onAccountToggleConnectedBotPaused) registerRPC[*tg.AccountToggleConnectedBotPausedRequest](d, tlprofile.SemanticMethodAccountToggleConnectedBotPaused, func(ctx context.Context, layerRequest *tg.AccountToggleConnectedBotPausedRequest) (any, error) {
d.OnAccountDisablePeerConnectedBot(r.onAccountDisablePeerConnectedBot) return r.onAccountToggleConnectedBotPaused(ctx, layerRequest)
d.OnAccountGetReactionsNotifySettings(r.onAccountGetReactionsNotifySettings) })
d.OnAccountSetReactionsNotifySettings(r.onAccountSetReactionsNotifySettings) registerRPC[*tg.AccountDisablePeerConnectedBotRequest](d, tlprofile.SemanticMethodAccountDisablePeerConnectedBot, func(ctx context.Context, layerRequest *tg.AccountDisablePeerConnectedBotRequest) (any, error) {
d.OnAccountGetContactSignUpNotification(r.onAccountGetContactSignUpNotification) return r.onAccountDisablePeerConnectedBot(ctx, layerRequest.
d.OnAccountSetContactSignUpNotification(r.onAccountSetContactSignUpNotification) Peer)
d.OnAccountGetThemes(r.onAccountGetThemes) })
d.OnAccountGetChatThemes(func(ctx context.Context, hash int64) (tg.AccountThemesClass, error) { 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 { if _, _, err := r.currentUserID(ctx); err != nil {
return nil, internalErr() return nil, internalErr()
} }
return tdesktop.ChatThemes(hash), nil return tdesktop.ChatThemes(hash), nil
}) })
registerRPC[
// 自定义云主题(Create a New Theme 全链路):upload→create→update→save→install→get。 // 自定义云主题(Create a New Theme 全链路):upload→create→update→save→install→get。
d.OnAccountUploadTheme(r.onAccountUploadTheme) *tg.AccountUploadThemeRequest](d, tlprofile.SemanticMethodAccountUploadTheme, func(ctx context.Context, layerRequest *tg.AccountUploadThemeRequest) (any, error) {
d.OnAccountCreateTheme(r.onAccountCreateTheme) return r.onAccountUploadTheme(ctx, layerRequest)
d.OnAccountUpdateTheme(r.onAccountUpdateTheme) })
d.OnAccountSaveTheme(r.onAccountSaveTheme) registerRPC[*tg.AccountCreateThemeRequest](d, tlprofile.SemanticMethodAccountCreateTheme, func(ctx context.Context, layerRequest *tg.AccountCreateThemeRequest) (any, error) {
d.OnAccountInstallTheme(r.onAccountInstallTheme) return r.onAccountCreateTheme(ctx, layerRequest)
d.OnAccountGetTheme(r.onAccountGetTheme) })
d.OnAccountGetWallPapers(func(ctx context.Context, hash int64) (tg.AccountWallPapersClass, error) { 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 { if _, _, err := r.currentUserID(ctx); err != nil {
return nil, internalErr() return nil, internalErr()
} }
return tdesktop.WallPapers(hash), nil 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 { if _, _, err := r.currentUserID(ctx); err != nil {
return nil, internalErr() return nil, internalErr()
} }
@ -109,7 +251,11 @@ func (r *Router) registerAccount(d *tg.ServerDispatcher) {
} }
return found, nil 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 { if _, _, err := r.currentUserID(ctx); err != nil {
return nil, internalErr() return nil, internalErr()
} }
@ -122,7 +268,7 @@ func (r *Router) registerAccount(d *tg.ServerDispatcher) {
} }
return found, nil 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 { if _, _, err := r.currentUserID(ctx); err != nil {
return false, internalErr() return false, internalErr()
} }
@ -134,7 +280,7 @@ func (r *Router) registerAccount(d *tg.ServerDispatcher) {
} }
return true, nil 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 { if _, _, err := r.currentUserID(ctx); err != nil {
return false, internalErr() return false, internalErr()
} }
@ -146,13 +292,13 @@ func (r *Router) registerAccount(d *tg.ServerDispatcher) {
} }
return true, nil 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 { if _, _, err := r.currentUserID(ctx); err != nil {
return false, internalErr() return false, internalErr()
} }
return true, nil 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 { if _, _, err := r.currentUserID(ctx); err != nil {
return nil, internalErr() return nil, internalErr()
} }
@ -161,71 +307,151 @@ func (r *Router) registerAccount(d *tg.ServerDispatcher) {
} }
return tdesktop.UniqueGiftChatThemes(req.Hash), nil 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 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 return true, nil
}) })
d.OnAccountUpdateEmojiStatus(r.onAccountUpdateEmojiStatus) registerRPC[*tg.AccountUpdateEmojiStatusRequest](d, tlprofile.SemanticMethodAccountUpdateEmojiStatus, func(ctx context.Context, layerRequest *tg.AccountUpdateEmojiStatusRequest) (any, error) {
d.OnAccountUpdateColor(r.onAccountUpdateColor) return r.onAccountUpdateEmojiStatus(ctx, layerRequest.
d.OnAccountGetDefaultProfilePhotoEmojis(r.onAccountGetDefaultProfilePhotoEmojis) EmojiStatus)
d.OnAccountGetDefaultBackgroundEmojis(r.onAccountGetDefaultBackgroundEmojis) })
d.OnAccountGetChannelDefaultEmojiStatuses(func(ctx context.Context, hash int64) (tg.AccountEmojiStatusesClass, error) { 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 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 return tdesktop.DefaultGroupPhotoEmojis(), nil
}) })
d.OnAccountSetContentSettings(r.onAccountSetContentSettings) registerRPC[*tg.AccountSetContentSettingsRequest](d, tlprofile.SemanticMethodAccountSetContentSettings, func(ctx context.Context, layerRequest *tg.AccountSetContentSettingsRequest) (any, error) {
d.OnAccountGetContentSettings(r.onAccountGetContentSettings) return r.onAccountSetContentSettings(ctx, layerRequest)
d.OnAccountGetGlobalPrivacySettings(r.onAccountGetGlobalPrivacySettings) })
d.OnAccountSetGlobalPrivacySettings(r.onAccountSetGlobalPrivacySettings) registerRPC[*tg.AccountGetContentSettingsRequest](d, tlprofile.SemanticMethodAccountGetContentSettings, func(ctx context.Context, layerRequest *tg.AccountGetContentSettingsRequest) (any, error) {
d.OnAccountGetPasskeys(r.onAccountGetPasskeys) return r.onAccountGetContentSettings(ctx)
d.OnAccountInitPasskeyRegistration(r.onAccountInitPasskeyRegistration) })
d.OnAccountRegisterPasskey(r.onAccountRegisterPasskey) registerRPC[*tg.AccountGetGlobalPrivacySettingsRequest](d, tlprofile.SemanticMethodAccountGetGlobalPrivacySettings, func(ctx context.Context, layerRequest *tg.AccountGetGlobalPrivacySettingsRequest) (any, error) {
d.OnAccountDeletePasskey(r.onAccountDeletePasskey) return r.onAccountGetGlobalPrivacySettings(ctx)
d.OnAccountGetWebAuthorizations(func(ctx context.Context) (*tg.AccountWebAuthorizations, error) { })
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 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) {
return true, nil hash := layerRequest.
}) Hash
d.OnAccountResetWebAuthorizations(func(ctx context.Context) (bool, error) { _ = hash
return true, nil return true, nil
}) })
registerRPC[*tg.AccountResetWebAuthorizationsRequest](d, tlprofile.SemanticMethodAccountResetWebAuthorizations, func(ctx context.Context, layerRequest *tg.AccountResetWebAuthorizationsRequest) (
// account.getWebBrowserSettings:telesrv 不接入网页浏览器(web bot)集成,返回空设置 // account.getWebBrowserSettings:telesrv 不接入网页浏览器(web bot)集成,返回空设置
// (无内置浏览器例外、不强制外部浏览器)。Android 启动时会拉取,缺它会反复 500 // (无内置浏览器例外、不强制外部浏览器)。Android 启动时会拉取,缺它会反复 500
// NOT_IMPLEMENTED。空结构 Hash=0,客户端按默认(内置浏览器、无例外)渲染。 // NOT_IMPLEMENTED。空结构 Hash=0,客户端按默认(内置浏览器、无例外)渲染。
d.OnAccountGetWebBrowserSettings(func(ctx context.Context, hash int64) (tg.AccountWebBrowserSettingsClass, error) { any, error) {
return &tg.AccountWebBrowserSettings{}, nil
})
d.OnAccountGetNotifyExceptions(r.onAccountGetNotifyExceptions)
d.OnAccountGetAutoDownloadSettings(func(ctx context.Context) (*tg.AccountAutoDownloadSettings, error) {
return tdesktop.AutoDownloadSettings(), nil
})
d.OnAccountSaveAutoDownloadSettings(func(ctx context.Context, req *tg.AccountSaveAutoDownloadSettingsRequest) (bool, error) {
return true, nil return true, nil
}) })
d.OnAccountSaveMusic(r.onAccountSaveMusic) registerRPC[*tg.AccountGetWebBrowserSettingsRequest](d, tlprofile.SemanticMethodAccountGetWebBrowserSettings, func(ctx context.Context, layerRequest *tg.AccountGetWebBrowserSettingsRequest) (any, error) {
d.OnAccountGetSavedMusicIDs(r.onAccountGetSavedMusicIDs) hash := layerRequest.
d.OnAccountGetSavedRingtones(func(ctx context.Context, hash int64) (tg.AccountSavedRingtonesClass, error) { Hash
_ = hash
return &tg.AccountWebBrowserSettings{}, nil
})
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
})
registerRPC[*tg.AccountSaveAutoDownloadSettingsRequest](d, tlprofile.SemanticMethodAccountSaveAutoDownloadSettings, func(ctx context.Context, req *tg.AccountSaveAutoDownloadSettingsRequest) (any, error) {
return true, nil
})
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 { if _, _, err := r.currentUserID(ctx); err != nil {
return nil, internalErr() return nil, internalErr()
} }
return &tg.AccountSavedRingtones{Hash: 0, Ringtones: []tg.DocumentClass{}}, nil return &tg.AccountSavedRingtones{Hash: 0, Ringtones: []tg.DocumentClass{}}, nil
}) })
d.OnAccountGetAccountTTL(r.onAccountGetAccountTTL) registerRPC[*tg.AccountGetAccountTTLRequest](d, tlprofile.SemanticMethodAccountGetAccountTTL, func(ctx context.Context, layerRequest *tg.AccountGetAccountTTLRequest) (any, error) {
d.OnAccountSetAccountTTL(r.onAccountSetAccountTTL) return r.onAccountGetAccountTTL(ctx)
d.OnAccountSetAuthorizationTTL(func(ctx context.Context, authorizationttldays int) (bool, error) { })
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 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 return true, nil
}) })
d.OnAccountResetPassword(r.onAccountResetPassword) registerRPC[*tg.AccountResetPasswordRequest](d, tlprofile.SemanticMethodAccountResetPassword, func(ctx context.Context, layerRequest *tg.AccountResetPasswordRequest) (any, error) {
d.OnAccountDeclinePasswordReset(r.onAccountDeclinePasswordReset) return r.onAccountResetPassword(ctx)
d.OnAccountUpdateStatus(r.onAccountUpdateStatus) })
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) { 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/tg"
"github.com/iamxvbaba/td/tgerr" "github.com/iamxvbaba/td/tgerr"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/domain" "telesrv/internal/domain"
) )
func (r *Router) registerAiCompose(d *tg.ServerDispatcher) { func (r *Router) registerAiCompose(d *tlprofile.Dispatcher) {
d.OnAicomposeGetTones(r.onAicomposeGetTones) registerRPC[*tg.AicomposeGetTonesRequest](d, tlprofile.SemanticMethodAicomposeGetTones, func(ctx context.Context, layerRequest *tg.AicomposeGetTonesRequest) (any, error) {
d.OnAicomposeCreateTone(r.onAicomposeCreateTone) return r.onAicomposeGetTones(ctx, layerRequest.
d.OnAicomposeUpdateTone(r.onAicomposeUpdateTone) Hash)
d.OnAicomposeSaveTone(r.onAicomposeSaveTone) })
d.OnAicomposeDeleteTone(r.onAicomposeDeleteTone) registerRPC[*tg.AicomposeCreateToneRequest](d, tlprofile.SemanticMethodAicomposeCreateTone, func(ctx context.Context, layerRequest *tg.AicomposeCreateToneRequest) (any, error) {
d.OnAicomposeGetTone(r.onAicomposeGetTone) return r.onAicomposeCreateTone(ctx, layerRequest)
d.OnAicomposeGetToneExample(r.onAicomposeGetToneExample) })
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) { 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 { if err != nil {
t.Fatalf("dispatch legacy auth.signUp: %v", err) t.Fatalf("dispatch legacy auth.signUp: %v", err)
} }
// Routed through the generated static client overlay + the normal gotd // Routed through the generated static client overlay and sparse semantic
// dispatcher, which boxes a class result (auth.Authorization) as *...Box. // dispatcher, which exposes the canonical concrete result at this test seam.
box, ok := enc.(*tg.AuthAuthorizationBox) authorization, ok := enc.(*tg.AuthAuthorization)
if !ok { if !ok {
t.Fatalf("response = %T, want *tg.AuthAuthorizationBox", enc) t.Fatalf("response = %T, want *tg.AuthAuthorization", enc)
}
authorization, ok := box.Authorization.(*tg.AuthAuthorization)
if !ok {
t.Fatalf("authorization = %T, want auth.authorization", box.Authorization)
} }
user, ok := authorization.User.(*tg.User) user, ok := authorization.User.(*tg.User)
if !ok || user.ID != auth.signUpUser.ID { if !ok || user.ID != auth.signUpUser.ID {

View file

@ -10,13 +10,14 @@ import (
"github.com/iamxvbaba/td/tg" "github.com/iamxvbaba/td/tg"
"go.uber.org/zap/zaptest" "go.uber.org/zap/zaptest"
"github.com/iamxvbaba/td/tlprofile"
compatandroid "telesrv/internal/compat/android" compatandroid "telesrv/internal/compat/android"
) )
type androidPrivateLayerFixture struct { type androidPrivateLayerFixture struct {
name string name string
privateID uint32 privateID uint32
semantic tg.LayerSemanticID semantic tlprofile.SemanticID
method string method string
wire func(*testing.T) []byte wire func(*testing.T) []byte
} }
@ -46,14 +47,14 @@ func TestAndroidPrivateLayerRPCsAdaptAcrossCanonicalBoundary(t *testing.T) {
} }
private := bin.Buffer{Buf: append([]byte(nil), raw...)} 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 { if err != nil || !handled || canonical == nil {
t.Fatalf("canonical upgrade = value:%v handled:%v err:%v", canonical != nil, handled, err) t.Fatalf("canonical upgrade = value:%v handled:%v err:%v", canonical != nil, handled, err)
} }
if private.Len() != 0 { if private.Len() != 0 {
t.Fatalf("canonical upgrade left %d private bytes", private.Len()) 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 { if !ok {
t.Fatalf("canonical profile has no wire id for %s", fixture.method) 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) 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 profile := profile
t.Run(fmt.Sprintf("layer_%d", profile), func(t *testing.T) { t.Run(fmt.Sprintf("layer_%d", profile), func(t *testing.T) {
body := bin.Buffer{Buf: append([]byte(nil), raw...)} 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 { if err != nil {
t.Fatalf("production exact admission: %v", err) t.Fatalf("production exact admission: %v", err)
} }
@ -77,11 +78,11 @@ func TestAndroidPrivateLayerRPCsAdaptAcrossCanonicalBoundary(t *testing.T) {
if call.Profile() != profile || call.Method() != fixture.semantic { 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) 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 { if !ok || category != "function" || method != fixture.method {
t.Fatalf("admitted semantic name = (%q,%q,%v), want (function,%q,true)", category, method, ok, 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 { if !ok || call.WireID() != wantWireID {
t.Fatalf("admitted exact id = %#x, want %#x (ok=%v)", call.WireID(), wantWireID, ok) t.Fatalf("admitted exact id = %#x, want %#x (ok=%v)", call.WireID(), wantWireID, ok)
} }
@ -95,12 +96,12 @@ func androidPrivateLayerFixtures() []androidPrivateLayerFixture {
return []androidPrivateLayerFixture{ return []androidPrivateLayerFixture{
{ {
name: "messages.forwardMessages_alias", privateID: 0x41d41ade, 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() }, wire: func(*testing.T) []byte { return androidPrivateForwardMessagesWire() },
}, },
{ {
name: "channels.inviteToChannel_alias", privateID: 0x199f3a6c, name: "channels.inviteToChannel_alias", privateID: 0x199f3a6c,
semantic: tg.LayerSemanticMethodChannelsInviteToChannel, method: "channels.inviteToChannel", semantic: tlprofile.SemanticMethodChannelsInviteToChannel, method: "channels.inviteToChannel",
wire: func(t *testing.T) []byte { wire: func(t *testing.T) []byte {
return androidPrivateAliasWire(t, 0x199f3a6c, &tg.ChannelsInviteToChannelRequest{ return androidPrivateAliasWire(t, 0x199f3a6c, &tg.ChannelsInviteToChannelRequest{
Channel: &tg.InputChannel{ChannelID: 41, AccessHash: 42}, Channel: &tg.InputChannel{ChannelID: 41, AccessHash: 42},
@ -110,14 +111,14 @@ func androidPrivateLayerFixtures() []androidPrivateLayerFixture {
}, },
{ {
name: "updates.getDifference_alias", privateID: 0x25939651, name: "updates.getDifference_alias", privateID: 0x25939651,
semantic: tg.LayerSemanticMethodUpdatesGetDifference, method: "updates.getDifference", semantic: tlprofile.SemanticMethodUpdatesGetDifference, method: "updates.getDifference",
wire: func(t *testing.T) []byte { wire: func(t *testing.T) []byte {
return androidPrivateAliasWire(t, 0x25939651, &tg.UpdatesGetDifferenceRequest{Pts: 100, Date: 200, Qts: 3}) return androidPrivateAliasWire(t, 0x25939651, &tg.UpdatesGetDifferenceRequest{Pts: 100, Date: 200, Qts: 3})
}, },
}, },
{ {
name: "messages.createChat_alias", privateID: 0x0034a818, name: "messages.createChat_alias", privateID: 0x0034a818,
semantic: tg.LayerSemanticMethodMessagesCreateChat, method: "messages.createChat", semantic: tlprofile.SemanticMethodMessagesCreateChat, method: "messages.createChat",
wire: func(t *testing.T) []byte { wire: func(t *testing.T) []byte {
return androidPrivateAliasWire(t, 0x0034a818, &tg.MessagesCreateChatRequest{ return androidPrivateAliasWire(t, 0x0034a818, &tg.MessagesCreateChatRequest{
Users: []tg.InputUserClass{&tg.InputUser{UserID: 51, AccessHash: 52}}, Users: []tg.InputUserClass{&tg.InputUser{UserID: 51, AccessHash: 52}},
@ -127,7 +128,7 @@ func androidPrivateLayerFixtures() []androidPrivateLayerFixture {
}, },
{ {
name: "messages.uploadMedia_transform", privateID: 0x519bc2b1, name: "messages.uploadMedia_transform", privateID: 0x519bc2b1,
semantic: tg.LayerSemanticMethodMessagesUploadMedia, method: "messages.uploadMedia", semantic: tlprofile.SemanticMethodMessagesUploadMedia, method: "messages.uploadMedia",
wire: func(t *testing.T) []byte { wire: func(t *testing.T) []byte {
return androidPrivateRawWire(t, 0x519bc2b1, func(body *bin.Buffer) error { return androidPrivateRawWire(t, 0x519bc2b1, func(body *bin.Buffer) error {
if err := (&tg.InputPeerSelf{}).Encode(body); err != nil { if err := (&tg.InputPeerSelf{}).Encode(body); err != nil {
@ -139,7 +140,7 @@ func androidPrivateLayerFixtures() []androidPrivateLayerFixture {
}, },
{ {
name: "auth.signUp_transform", privateID: 0x80eee427, name: "auth.signUp_transform", privateID: 0x80eee427,
semantic: tg.LayerSemanticMethodAuthSignUp, method: "auth.signUp", semantic: tlprofile.SemanticMethodAuthSignUp, method: "auth.signUp",
wire: func(t *testing.T) []byte { wire: func(t *testing.T) []byte {
return androidPrivateRawWire(t, 0x80eee427, func(body *bin.Buffer) error { return androidPrivateRawWire(t, 0x80eee427, func(body *bin.Buffer) error {
body.PutString("+15550000228") body.PutString("+15550000228")
@ -152,7 +153,7 @@ func androidPrivateLayerFixtures() []androidPrivateLayerFixture {
}, },
{ {
name: "messages.getMessages_transform", privateID: 0x4222fa74, name: "messages.getMessages_transform", privateID: 0x4222fa74,
semantic: tg.LayerSemanticMethodMessagesGetMessages, method: "messages.getMessages", semantic: tlprofile.SemanticMethodMessagesGetMessages, method: "messages.getMessages",
wire: func(t *testing.T) []byte { wire: func(t *testing.T) []byte {
return androidPrivateRawWire(t, 0x4222fa74, func(body *bin.Buffer) error { return androidPrivateRawWire(t, 0x4222fa74, func(body *bin.Buffer) error {
body.PutVectorHeader(2) body.PutVectorHeader(2)
@ -164,7 +165,7 @@ func androidPrivateLayerFixtures() []androidPrivateLayerFixture {
}, },
{ {
name: "channels.getMessages_transform", privateID: 0x93d7b347, name: "channels.getMessages_transform", privateID: 0x93d7b347,
semantic: tg.LayerSemanticMethodChannelsGetMessages, method: "channels.getMessages", semantic: tlprofile.SemanticMethodChannelsGetMessages, method: "channels.getMessages",
wire: func(t *testing.T) []byte { wire: func(t *testing.T) []byte {
return androidPrivateRawWire(t, 0x93d7b347, func(body *bin.Buffer) error { return androidPrivateRawWire(t, 0x93d7b347, func(body *bin.Buffer) error {
if err := (&tg.InputChannel{ChannelID: 81, AccessHash: 82}).Encode(body); err != nil { 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, name: "bots.exportBotToken_transform", privateID: 0x0063b089,
semantic: tg.LayerSemanticMethodBotsExportBotToken, method: "bots.exportBotToken", semantic: tlprofile.SemanticMethodBotsExportBotToken, method: "bots.exportBotToken",
wire: func(t *testing.T) []byte { wire: func(t *testing.T) []byte {
return androidPrivateRawWire(t, 0x0063b089, func(body *bin.Buffer) error { return androidPrivateRawWire(t, 0x0063b089, func(body *bin.Buffer) error {
body.PutLong(91) body.PutLong(91)
@ -190,7 +191,7 @@ func androidPrivateLayerFixtures() []androidPrivateLayerFixture {
}, },
{ {
name: "account.registerDevice_transform", privateID: 0x637ea878, name: "account.registerDevice_transform", privateID: 0x637ea878,
semantic: tg.LayerSemanticMethodAccountRegisterDevice, method: "account.registerDevice", semantic: tlprofile.SemanticMethodAccountRegisterDevice, method: "account.registerDevice",
wire: func(t *testing.T) []byte { wire: func(t *testing.T) []byte {
return androidPrivateRawWire(t, 0x637ea878, func(body *bin.Buffer) error { return androidPrivateRawWire(t, 0x637ea878, func(body *bin.Buffer) error {
body.PutInt(2) body.PutInt(2)
@ -201,7 +202,7 @@ func androidPrivateLayerFixtures() []androidPrivateLayerFixture {
}, },
{ {
name: "contacts.search_transform", privateID: 0x11f812d8, name: "contacts.search_transform", privateID: 0x11f812d8,
semantic: tg.LayerSemanticMethodContactsSearch, method: "contacts.search", semantic: tlprofile.SemanticMethodContactsSearch, method: "contacts.search",
wire: func(t *testing.T) []byte { wire: func(t *testing.T) []byte {
return androidPrivateRawWire(t, 0x11f812d8, func(body *bin.Buffer) error { return androidPrivateRawWire(t, 0x11f812d8, func(body *bin.Buffer) error {
body.PutString("private-query") body.PutString("private-query")
@ -212,7 +213,7 @@ func androidPrivateLayerFixtures() []androidPrivateLayerFixture {
}, },
{ {
name: "langpack.getLangPack_transform", privateID: 0x9ab5c58e, name: "langpack.getLangPack_transform", privateID: 0x9ab5c58e,
semantic: tg.LayerSemanticMethodLangpackGetLangPack, method: "langpack.getLangPack", semantic: tlprofile.SemanticMethodLangpackGetLangPack, method: "langpack.getLangPack",
wire: func(t *testing.T) []byte { wire: func(t *testing.T) []byte {
return androidPrivateRawWire(t, 0x9ab5c58e, func(body *bin.Buffer) error { return androidPrivateRawWire(t, 0x9ab5c58e, func(body *bin.Buffer) error {
body.PutString("en") body.PutString("en")
@ -222,7 +223,7 @@ func androidPrivateLayerFixtures() []androidPrivateLayerFixture {
}, },
{ {
name: "langpack.getStrings_transform", privateID: 0x2e1ee318, name: "langpack.getStrings_transform", privateID: 0x2e1ee318,
semantic: tg.LayerSemanticMethodLangpackGetStrings, method: "langpack.getStrings", semantic: tlprofile.SemanticMethodLangpackGetStrings, method: "langpack.getStrings",
wire: func(t *testing.T) []byte { wire: func(t *testing.T) []byte {
return androidPrivateRawWire(t, 0x2e1ee318, func(body *bin.Buffer) error { return androidPrivateRawWire(t, 0x2e1ee318, func(body *bin.Buffer) error {
body.PutString("en") body.PutString("en")
@ -235,14 +236,14 @@ func androidPrivateLayerFixtures() []androidPrivateLayerFixture {
}, },
{ {
name: "langpack.getLanguages_transform", privateID: 0x800fd57d, name: "langpack.getLanguages_transform", privateID: 0x800fd57d,
semantic: tg.LayerSemanticMethodLangpackGetLanguages, method: "langpack.getLanguages", semantic: tlprofile.SemanticMethodLangpackGetLanguages, method: "langpack.getLanguages",
wire: func(t *testing.T) []byte { wire: func(t *testing.T) []byte {
return androidPrivateRawWire(t, 0x800fd57d, func(*bin.Buffer) error { return nil }) return androidPrivateRawWire(t, 0x800fd57d, func(*bin.Buffer) error { return nil })
}, },
}, },
{ {
name: "messages.editChatCreator_transform", privateID: 0x8f38cd1f, name: "messages.editChatCreator_transform", privateID: 0x8f38cd1f,
semantic: tg.LayerSemanticMethodMessagesEditChatCreator, method: "messages.editChatCreator", semantic: tlprofile.SemanticMethodMessagesEditChatCreator, method: "messages.editChatCreator",
wire: func(t *testing.T) []byte { wire: func(t *testing.T) []byte {
return androidPrivateRawWire(t, 0x8f38cd1f, func(body *bin.Buffer) error { return androidPrivateRawWire(t, 0x8f38cd1f, func(body *bin.Buffer) error {
if err := (&tg.InputChannel{ChannelID: 101, AccessHash: 102}).Encode(body); err != nil { 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/proto"
"github.com/iamxvbaba/td/tg" "github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/app/auth" "telesrv/internal/app/auth"
"telesrv/internal/domain" "telesrv/internal/domain"
) )
@ -21,38 +22,90 @@ import (
const devCodeLength = 5 const devCodeLength = 5
// registerAuth 注册 auth.* RPC handler。 // registerAuth 注册 auth.* RPC handler。
func (r *Router) registerAuth(d *tg.ServerDispatcher) { func (r *Router) registerAuth(d *tlprofile.Dispatcher) {
d.OnAuthBindTempAuthKey(r.onAuthBindTempAuthKey) registerRPC[*tg.AuthBindTempAuthKeyRequest](d, tlprofile.SemanticMethodAuthBindTempAuthKey, func(ctx context.Context, layerRequest *tg.AuthBindTempAuthKeyRequest) (any, error) {
d.OnAuthExportLoginToken(r.onAuthExportLoginToken) return r.onAuthBindTempAuthKey(ctx, layerRequest)
d.OnAuthImportLoginToken(r.onAuthImportLoginToken) })
d.OnAuthAcceptLoginToken(r.onAuthAcceptLoginToken) registerRPC[*tg.AuthExportLoginTokenRequest](d, tlprofile.SemanticMethodAuthExportLoginToken, func(ctx context.Context, layerRequest *tg.AuthExportLoginTokenRequest) (any, error) {
d.OnAuthExportAuthorization(func(ctx context.Context, dcid int) (*tg.AuthExportedAuthorization, 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() 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() 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 return true, nil
}) })
d.OnAuthInitPasskeyLogin(r.onAuthInitPasskeyLogin) registerRPC[*tg.AuthInitPasskeyLoginRequest](d, tlprofile.SemanticMethodAuthInitPasskeyLogin, func(ctx context.Context, layerRequest *tg.AuthInitPasskeyLoginRequest) (any, error) {
d.OnAuthFinishPasskeyLogin(r.onAuthFinishPasskeyLogin) return r.onAuthInitPasskeyLogin(ctx, layerRequest)
d.OnAuthSendCode(r.onAuthSendCode) })
d.OnAuthResendCode(r.onAuthResendCode) registerRPC[*tg.AuthFinishPasskeyLoginRequest](d, tlprofile.SemanticMethodAuthFinishPasskeyLogin, func(ctx context.Context, layerRequest *tg.AuthFinishPasskeyLoginRequest) (any, error) {
d.OnAuthCancelCode(r.onAuthCancelCode) return r.onAuthFinishPasskeyLogin(ctx, layerRequest)
d.OnAuthSignIn(r.onAuthSignIn) })
d.OnAuthSignUp(r.onAuthSignUp) registerRPC[*tg.AuthSendCodeRequest](d, tlprofile.SemanticMethodAuthSendCode, func(ctx context.Context, layerRequest *tg.AuthSendCodeRequest) (any, error) {
d.OnAuthImportBotAuthorization(r.onAuthImportBotAuthorization) return r.onAuthSendCode(ctx, layerRequest)
d.OnAuthLogOut(r.onAuthLogOut) })
d.OnAuthResetAuthorizations(r.onAuthResetAuthorizations) registerRPC[*tg.AuthResendCodeRequest](d, tlprofile.SemanticMethodAuthResendCode, func(ctx context.Context, layerRequest *tg.AuthResendCodeRequest) (any, error) {
d.OnAuthCheckPassword(r.onAuthCheckPassword) return r.onAuthResendCode(ctx, layerRequest)
d.OnAuthRequestPasswordRecovery(r.onAuthRequestPasswordRecovery) })
d.OnAuthRecoverPassword(r.onAuthRecoverPassword) registerRPC[*tg.AuthCancelCodeRequest](d, tlprofile.SemanticMethodAuthCancelCode, func(ctx context.Context, layerRequest *tg.AuthCancelCodeRequest) (any, error) {
d.OnAuthCheckRecoveryPassword(r.onAuthCheckRecoveryPassword) return r.onAuthCancelCode(ctx, layerRequest)
d.OnAuthResetLoginEmail(r.onAuthResetLoginEmail) })
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) { func (r *Router) onAuthBindTempAuthKey(ctx context.Context, req *tg.AuthBindTempAuthKeyRequest) (bool, error) {
if !layerRPCProfileEvidenceFresh(ctx) { if !layerRPCProfileEvidenceFresh(ctx) {
// The inner request is outside MTProto's mutable msg_id window. It may be // 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/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/domain" "telesrv/internal/domain"
) )
@ -22,58 +23,165 @@ import (
// bump bot_info_version;命令变更后 bots_hooks.PushBotCommandsChanged 给在线相关用户 // bump bot_info_version;命令变更后 bots_hooks.PushBotCommandsChanged 给在线相关用户
// 推 updateBotCommands(扇出封顶 100,无 pts),离线/超界用户靠 version bump 在下次 // 推 updateBotCommands(扇出封顶 100,无 pts),离线/超界用户靠 version bump 在下次
// getFullUser 重拉兜底。 // getFullUser 重拉兜底。
func (r *Router) registerBots(d *tg.ServerDispatcher) { func (r *Router) registerBots(d *tlprofile.Dispatcher) {
d.OnBotsSendCustomRequest(r.onBotsSendCustomRequest) registerRPC[*tg.BotsSendCustomRequestRequest](d, tlprofile.SemanticMethodBotsSendCustomRequest, func(ctx context.Context, layerRequest *tg.BotsSendCustomRequestRequest) (any, error) {
d.OnBotsAnswerWebhookJSONQuery(r.onBotsAnswerWebhookJSONQuery) return r.onBotsSendCustomRequest(ctx, layerRequest)
d.OnBotsSetBotBroadcastDefaultAdminRights(r.onBotsSetBotBroadcastDefaultAdminRights) })
d.OnBotsSetBotGroupDefaultAdminRights(r.onBotsSetBotGroupDefaultAdminRights) registerRPC[*tg.BotsAnswerWebhookJSONQueryRequest](d, tlprofile.SemanticMethodBotsAnswerWebhookJSONQuery, func(ctx context.Context, layerRequest *tg.BotsAnswerWebhookJSONQueryRequest) (any, error) {
d.OnBotsSetBotCommands(r.onBotsSetBotCommands) return r.onBotsAnswerWebhookJSONQuery(ctx, layerRequest)
d.OnBotsResetBotCommands(r.onBotsResetBotCommands) })
d.OnBotsGetBotCommands(r.onBotsGetBotCommands) registerRPC[*tg.BotsSetBotBroadcastDefaultAdminRightsRequest](d, tlprofile.SemanticMethodBotsSetBotBroadcastDefaultAdminRights, func(ctx context.Context, layerRequest *tg.BotsSetBotBroadcastDefaultAdminRightsRequest) (any, error) {
d.OnBotsSetBotInfo(r.onBotsSetBotInfo) return r.onBotsSetBotBroadcastDefaultAdminRights(ctx, layerRequest.
d.OnBotsGetBotInfo(r.onBotsGetBotInfo) AdminRights)
d.OnBotsSetBotMenuButton(r.onBotsSetBotMenuButton) })
d.OnBotsGetBotMenuButton(r.onBotsGetBotMenuButton) registerRPC[*tg.BotsSetBotGroupDefaultAdminRightsRequest](d, tlprofile.SemanticMethodBotsSetBotGroupDefaultAdminRights, func(ctx context.Context, layerRequest *tg.BotsSetBotGroupDefaultAdminRightsRequest) (any, error) {
d.OnBotsReorderUsernames(r.onBotsReorderUsernames) return r.onBotsSetBotGroupDefaultAdminRights(ctx, layerRequest.
d.OnBotsToggleUsername(r.onBotsToggleUsername) AdminRights)
d.OnBotsCanSendMessage(r.onBotsCanSendMessage) })
d.OnBotsAllowSendMessage(r.onBotsAllowSendMessage) registerRPC[*tg.BotsSetBotCommandsRequest](d, tlprofile.SemanticMethodBotsSetBotCommands, func(ctx context.Context, layerRequest *tg.BotsSetBotCommandsRequest) (any, error) {
d.OnBotsInvokeWebViewCustomMethod(r.onBotsInvokeWebViewCustomMethod) return r.onBotsSetBotCommands(ctx, layerRequest)
d.OnBotsGetPopularAppBots(r.onBotsGetPopularAppBots) })
d.OnBotsAddPreviewMedia(r.onBotsAddPreviewMedia) registerRPC[*tg.BotsResetBotCommandsRequest](d, tlprofile.SemanticMethodBotsResetBotCommands, func(ctx context.Context, layerRequest *tg.BotsResetBotCommandsRequest) (any, error) {
d.OnBotsEditPreviewMedia(r.onBotsEditPreviewMedia) return r.onBotsResetBotCommands(ctx, layerRequest)
d.OnBotsDeletePreviewMedia(r.onBotsDeletePreviewMedia) })
d.OnBotsReorderPreviewMedias(r.onBotsReorderPreviewMedias) registerRPC[*tg.BotsGetBotCommandsRequest](d, tlprofile.SemanticMethodBotsGetBotCommands, func(ctx context.Context, layerRequest *tg.BotsGetBotCommandsRequest) (any, error) {
d.OnBotsGetPreviewInfo(r.onBotsGetPreviewInfo) return r.onBotsGetBotCommands(ctx, layerRequest)
d.OnBotsGetPreviewMedias(r.onBotsGetPreviewMedias) })
d.OnBotsUpdateUserEmojiStatus(r.onBotsUpdateUserEmojiStatus) registerRPC[*tg.BotsSetBotInfoRequest](d, tlprofile.SemanticMethodBotsSetBotInfo, func(ctx context.Context, layerRequest *tg.BotsSetBotInfoRequest) (any, error) {
d.OnBotsToggleUserEmojiStatusPermission(r.onBotsToggleUserEmojiStatusPermission) return r.onBotsSetBotInfo(ctx, layerRequest)
d.OnBotsCheckDownloadFileParams(r.onBotsCheckDownloadFileParams) })
d.OnBotsGetAdminedBots(r.onBotsGetAdminedBots) registerRPC[*tg.BotsGetBotInfoRequest](d, tlprofile.SemanticMethodBotsGetBotInfo, func(ctx context.Context, layerRequest *tg.BotsGetBotInfoRequest) (any, error) {
d.OnBotsUpdateStarRefProgram(r.onBotsUpdateStarRefProgram) return r.onBotsGetBotInfo(ctx, layerRequest)
d.OnBotsSetCustomVerification(r.onBotsSetCustomVerification) })
d.OnBotsGetBotRecommendations(r.onBotsGetBotRecommendations) registerRPC[*tg.BotsSetBotMenuButtonRequest](d, tlprofile.SemanticMethodBotsSetBotMenuButton, func(ctx context.Context, layerRequest *tg.BotsSetBotMenuButtonRequest) (any, error) {
d.OnBotsCheckUsername(r.onBotsCheckUsername) return r.onBotsSetBotMenuButton(ctx, layerRequest)
d.OnBotsCreateBot(r.onBotsCreateBot) })
d.OnBotsExportBotToken(r.onBotsExportBotToken) registerRPC[*tg.BotsGetBotMenuButtonRequest](d, tlprofile.SemanticMethodBotsGetBotMenuButton, func(ctx context.Context, layerRequest *tg.BotsGetBotMenuButtonRequest) (any, error) {
d.OnBotsRequestWebViewButton(r.onBotsRequestWebViewButton) return r.onBotsGetBotMenuButton(ctx, layerRequest.
d.OnBotsGetRequestedWebViewButton(r.onBotsGetRequestedWebViewButton) UserID)
d.OnBotsGetAccessSettings(r.onBotsGetAccessSettings) })
d.OnBotsEditAccessSettings(r.onBotsEditAccessSettings) 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) (
// P3:startBot 深链 + inline callback 闭环。 // P3:startBot 深链 + inline callback 闭环。
d.OnMessagesStartBot(r.onMessagesStartBot) any, error) {
d.OnMessagesGetBotCallbackAnswer(r.onMessagesGetBotCallbackAnswer) return r.onBotsGetAccessSettings(ctx, layerRequest.
d.OnMessagesSetBotCallbackAnswer(r.onMessagesSetBotCallbackAnswer) Bot)
d.OnMessagesGetInlineBotResults(r.onMessagesGetInlineBotResults) })
d.OnMessagesSetInlineBotResults(r.onMessagesSetInlineBotResults) registerRPC[*tg.BotsEditAccessSettingsRequest](d, tlprofile.SemanticMethodBotsEditAccessSettings, func(ctx context.Context, layerRequest *tg.BotsEditAccessSettingsRequest) (any, error) {
d.OnMessagesSendInlineBotResult(r.onMessagesSendInlineBotResult) return r.onBotsEditAccessSettings(ctx, layerRequest)
d.OnMessagesSavePreparedInlineMessage(r.onMessagesSavePreparedInlineMessage) })
d.OnMessagesEditInlineBotMessage(r.onMessagesEditInlineBotMessage) registerRPC[*tg.MessagesStartBotRequest](d, tlprofile.SemanticMethodMessagesStartBot, func(ctx context.Context, layerRequest *tg.MessagesStartBotRequest) (any, error) {
d.OnMessagesSetBotShippingResults(r.onMessagesSetBotShippingResults) return r.onMessagesStartBot(ctx, layerRequest)
d.OnMessagesSetBotPrecheckoutResults(r.onMessagesSetBotPrecheckoutResults) })
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_id(bot-only RPC 用)。
layerRequest *tg.MessagesSetBotPrecheckoutResultsRequest) (any, error) {
return r.onMessagesSetBotPrecheckoutResults(ctx, layerRequest)
})
} }
// callerBotID 校验调用者本身是 bot 账号,返回其 user_id(bot-only RPC 用)。
func (r *Router) callerBotID(ctx context.Context) (int64, error) { func (r *Router) callerBotID(ctx context.Context) (int64, error) {
userID, _, err := r.currentUserID(ctx) userID, _, err := r.currentUserID(ctx)
if err != nil { if err != nil {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,101 +1,301 @@
package rpc package rpc
import ( import (
"context"
"github.com/iamxvbaba/td/tg" "github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
) )
// registerChannels 注册超级群/频道相关 RPC。messages.createChat 在这里注册, // registerChannels 注册超级群/频道相关 RPC。messages.createChat 在这里注册,
// 因为 telesrv 将普通群创建直接实现为 megagroup。 // 因为 telesrv 将普通群创建直接实现为 megagroup。
func (r *Router) registerChannels(d *tg.ServerDispatcher) { func (r *Router) registerChannels(d *tlprofile.Dispatcher) {
d.OnMessagesCreateChat(r.onMessagesCreateChat) registerRPC[*tg.MessagesCreateChatRequest](d, tlprofile.SemanticMethodMessagesCreateChat, func(ctx context.Context, layerRequest *tg.MessagesCreateChatRequest) (any, error) {
d.OnMessagesMigrateChat(r.onMessagesMigrateChat) return r.onMessagesCreateChat(ctx, layerRequest)
d.OnMessagesGetChats(r.onMessagesGetChats) })
d.OnMessagesGetFullChat(r.onMessagesGetFullChat) registerRPC[*tg.MessagesMigrateChatRequest](d, tlprofile.SemanticMethodMessagesMigrateChat, func(ctx context.Context, layerRequest *tg.MessagesMigrateChatRequest) (any, error) {
d.OnMessagesAddChatUser(r.onMessagesAddChatUser) return r.onMessagesMigrateChat(ctx, layerRequest.
d.OnMessagesDeleteChatUser(r.onMessagesDeleteChatUser) ChatID)
d.OnMessagesEditChatTitle(r.onMessagesEditChatTitle) })
d.OnMessagesEditChatPhoto(r.onMessagesEditChatPhoto) registerRPC[*tg.MessagesGetChatsRequest](d, tlprofile.SemanticMethodMessagesGetChats, func(ctx context.Context, layerRequest *tg.MessagesGetChatsRequest) (any, error) {
d.OnMessagesEditChatAdmin(r.onMessagesEditChatAdmin) return r.onMessagesGetChats(ctx, layerRequest.
d.OnMessagesEditChatAbout(r.onMessagesEditChatAbout) ID)
d.OnMessagesEditChatDefaultBannedRights(r.onMessagesEditChatDefaultBannedRights) })
d.OnMessagesEditChatCreator(r.onMessagesEditChatCreator) registerRPC[*tg.MessagesGetFullChatRequest](d, tlprofile.SemanticMethodMessagesGetFullChat, func(ctx context.Context, layerRequest *tg.MessagesGetFullChatRequest) (any, error) {
d.OnMessagesGetFutureChatCreatorAfterLeave(r.onMessagesGetFutureChatCreatorAfterLeave) return r.onMessagesGetFullChat(ctx, layerRequest.
d.OnMessagesEditChatParticipantRank(r.onMessagesEditChatParticipantRank) ChatID)
d.OnMessagesSetChatTheme(r.onMessagesSetChatTheme) })
d.OnMessagesSetChatWallPaper(r.onMessagesSetChatWallPaper) registerRPC[*tg.MessagesAddChatUserRequest](d, tlprofile.SemanticMethodMessagesAddChatUser, func(ctx context.Context, layerRequest *tg.MessagesAddChatUserRequest) (any, error) {
d.OnMessagesToggleNoForwards(r.onMessagesToggleNoForwards) return r.onMessagesAddChatUser(ctx, layerRequest)
d.OnMessagesSetChatAvailableReactions(r.onMessagesSetChatAvailableReactions) })
d.OnChannelsCreateChannel(r.onChannelsCreateChannel) registerRPC[*tg.MessagesDeleteChatUserRequest](d, tlprofile.SemanticMethodMessagesDeleteChatUser, func(ctx context.Context, layerRequest *tg.MessagesDeleteChatUserRequest) (any, error) {
d.OnChannelsGetChannels(r.onChannelsGetChannels) return r.onMessagesDeleteChatUser(ctx, layerRequest)
d.OnChannelsGetFullChannel(r.onChannelsGetFullChannel) })
d.OnChannelsGetParticipants(r.onChannelsGetParticipants) registerRPC[*tg.MessagesEditChatTitleRequest](d, tlprofile.SemanticMethodMessagesEditChatTitle, func(ctx context.Context, layerRequest *tg.MessagesEditChatTitleRequest) (any, error) {
d.OnChannelsGetParticipant(r.onChannelsGetParticipant) return r.onMessagesEditChatTitle(ctx, layerRequest)
d.OnChannelsGetSendAs(r.onChannelsGetSendAs) })
d.OnChannelsCheckUsername(r.onChannelsCheckUsername) registerRPC[*tg.MessagesEditChatPhotoRequest](d, tlprofile.SemanticMethodMessagesEditChatPhoto, func(ctx context.Context, layerRequest *tg.MessagesEditChatPhotoRequest) (any, error) {
d.OnChannelsUpdateUsername(r.onChannelsUpdateUsername) return r.onMessagesEditChatPhoto(ctx, layerRequest)
d.OnChannelsGetAdminedPublicChannels(r.onChannelsGetAdminedPublicChannels) })
d.OnChannelsExportMessageLink(r.onChannelsExportMessageLink) registerRPC[*tg.MessagesEditChatAdminRequest](d, tlprofile.SemanticMethodMessagesEditChatAdmin, func(ctx context.Context, layerRequest *tg.MessagesEditChatAdminRequest) (any, error) {
d.OnChannelsToggleSignatures(r.onChannelsToggleSignatures) return r.onMessagesEditChatAdmin(ctx, layerRequest)
d.OnChannelsTogglePreHistoryHidden(r.onChannelsTogglePreHistoryHidden) })
d.OnChannelsToggleSlowMode(r.onChannelsToggleSlowMode) registerRPC[*tg.MessagesEditChatAboutRequest](d, tlprofile.SemanticMethodMessagesEditChatAbout, func(ctx context.Context, layerRequest *tg.MessagesEditChatAboutRequest) (any, error) {
d.OnChannelsSetStickers(r.onChannelsSetStickers) return r.onMessagesEditChatAbout(ctx, layerRequest)
d.OnChannelsSetEmojiStickers(r.onChannelsSetEmojiStickers) })
d.OnChannelsReorderUsernames(r.onChannelsReorderUsernames) registerRPC[*tg.MessagesEditChatDefaultBannedRightsRequest](d, tlprofile.SemanticMethodMessagesEditChatDefaultBannedRights, func(ctx context.Context, layerRequest *tg.MessagesEditChatDefaultBannedRightsRequest) (any, error) {
d.OnChannelsToggleUsername(r.onChannelsToggleUsername) return r.onMessagesEditChatDefaultBannedRights(ctx, layerRequest)
d.OnChannelsDeactivateAllUsernames(r.onChannelsDeactivateAllUsernames) })
d.OnChannelsUpdateColor(r.onChannelsUpdateColor) registerRPC[*tg.MessagesEditChatCreatorRequest](d, tlprofile.SemanticMethodMessagesEditChatCreator, func(ctx context.Context, layerRequest *tg.MessagesEditChatCreatorRequest) (any, error) {
d.OnChannelsUpdateEmojiStatus(r.onChannelsUpdateEmojiStatus) return r.onMessagesEditChatCreator(ctx, layerRequest)
d.OnChannelsReadMessageContents(r.onChannelsReadMessageContents) })
d.OnChannelsReportSpam(r.onChannelsReportSpam) registerRPC[*tg.MessagesGetFutureChatCreatorAfterLeaveRequest](d, tlprofile.SemanticMethodMessagesGetFutureChatCreatorAfterLeave, func(ctx context.Context, layerRequest *tg.MessagesGetFutureChatCreatorAfterLeaveRequest) (any, error) {
d.OnChannelsGetLeftChannels(r.onChannelsGetLeftChannels) return r.onMessagesGetFutureChatCreatorAfterLeave(ctx, layerRequest.
d.OnChannelsGetInactiveChannels(r.onChannelsGetInactiveChannels) Peer)
d.OnChannelsGetGroupsForDiscussion(r.onChannelsGetGroupsForDiscussion) })
d.OnChannelsSetDiscussionGroup(r.onChannelsSetDiscussionGroup) registerRPC[*tg.MessagesEditChatParticipantRankRequest](d, tlprofile.SemanticMethodMessagesEditChatParticipantRank, func(ctx context.Context, layerRequest *tg.MessagesEditChatParticipantRankRequest) (any, error) {
d.OnChannelsEditLocation(r.onChannelsEditLocation) return r.onMessagesEditChatParticipantRank(ctx, layerRequest)
d.OnChannelsConvertToGigagroup(r.onChannelsConvertToGigagroup) })
d.OnChannelsDeleteParticipantHistory(r.onChannelsDeleteParticipantHistory) registerRPC[*tg.MessagesSetChatThemeRequest](d, tlprofile.SemanticMethodMessagesSetChatTheme, func(ctx context.Context, layerRequest *tg.MessagesSetChatThemeRequest) (any, error) {
d.OnChannelsToggleJoinToSend(r.onChannelsToggleJoinToSend) return r.onMessagesSetChatTheme(ctx, layerRequest)
d.OnChannelsToggleJoinRequest(r.onChannelsToggleJoinRequest) })
d.OnChannelsToggleForum(r.onChannelsToggleForum) registerRPC[*tg.MessagesSetChatWallPaperRequest](d, tlprofile.SemanticMethodMessagesSetChatWallPaper, func(ctx context.Context, layerRequest *tg.MessagesSetChatWallPaperRequest) (any, error) {
d.OnChannelsToggleAntiSpam(r.onChannelsToggleAntiSpam) return r.onMessagesSetChatWallPaper(ctx, layerRequest)
d.OnChannelsReportAntiSpamFalsePositive(r.onChannelsReportAntiSpamFalsePositive) })
d.OnChannelsToggleParticipantsHidden(r.onChannelsToggleParticipantsHidden) registerRPC[*tg.MessagesToggleNoForwardsRequest](d, tlprofile.SemanticMethodMessagesToggleNoForwards, func(ctx context.Context, layerRequest *tg.MessagesToggleNoForwardsRequest) (any, error) {
d.OnChannelsToggleViewForumAsMessages(r.onChannelsToggleViewForumAsMessages) return r.onMessagesToggleNoForwards(ctx, layerRequest)
d.OnChannelsGetChannelRecommendations(r.onChannelsGetChannelRecommendations) })
d.OnChannelsSetBoostsToUnblockRestrictions(r.onChannelsSetBoostsToUnblockRestrictions) registerRPC[*tg.MessagesSetChatAvailableReactionsRequest](d, tlprofile.SemanticMethodMessagesSetChatAvailableReactions, func(ctx context.Context, layerRequest *tg.MessagesSetChatAvailableReactionsRequest) (any, error) {
d.OnChannelsRestrictSponsoredMessages(r.onChannelsRestrictSponsoredMessages) return r.onMessagesSetChatAvailableReactions(ctx, layerRequest)
d.OnChannelsSearchPosts(r.onChannelsSearchPosts) })
d.OnChannelsUpdatePaidMessagesPrice(r.onChannelsUpdatePaidMessagesPrice) registerRPC[*tg.ChannelsCreateChannelRequest](d, tlprofile.SemanticMethodChannelsCreateChannel, func(ctx context.Context, layerRequest *tg.ChannelsCreateChannelRequest) (any, error) {
d.OnChannelsToggleAutotranslation(r.onChannelsToggleAutotranslation) return r.onChannelsCreateChannel(ctx, layerRequest)
d.OnChannelsGetMessageAuthor(r.onChannelsGetMessageAuthor) })
d.OnChannelsCheckSearchPostsFlood(r.onChannelsCheckSearchPostsFlood) registerRPC[*tg.ChannelsGetChannelsRequest](d, tlprofile.SemanticMethodChannelsGetChannels, func(ctx context.Context, layerRequest *tg.ChannelsGetChannelsRequest) (any, error) {
d.OnChannelsSetMainProfileTab(r.onChannelsSetMainProfileTab) return r.onChannelsGetChannels(ctx, layerRequest.
d.OnChannelsInviteToChannel(r.onChannelsInviteToChannel) ID)
d.OnChannelsJoinChannel(r.onChannelsJoinChannel) })
d.OnChannelsLeaveChannel(r.onChannelsLeaveChannel) registerRPC[*tg.ChannelsGetFullChannelRequest](d, tlprofile.SemanticMethodChannelsGetFullChannel, func(ctx context.Context, layerRequest *tg.ChannelsGetFullChannelRequest) (any, error) {
d.OnChannelsEditAdmin(r.onChannelsEditAdmin) return r.onChannelsGetFullChannel(ctx, layerRequest.
d.OnChannelsEditBanned(r.onChannelsEditBanned) Channel)
d.OnChannelsEditTitle(r.onChannelsEditTitle) })
d.OnChannelsEditPhoto(r.onChannelsEditPhoto) registerRPC[*tg.ChannelsGetParticipantsRequest](d, tlprofile.SemanticMethodChannelsGetParticipants, func(ctx context.Context, layerRequest *tg.ChannelsGetParticipantsRequest) (any, error) {
d.OnChannelsDeleteChannel(r.onChannelsDeleteChannel) return r.onChannelsGetParticipants(ctx, layerRequest)
d.OnChannelsGetAdminLog(r.onChannelsGetAdminLog) })
d.OnChannelsReadHistory(r.onChannelsReadHistory) registerRPC[*tg.ChannelsGetParticipantRequest](d, tlprofile.SemanticMethodChannelsGetParticipant, func(ctx context.Context, layerRequest *tg.ChannelsGetParticipantRequest) (any, error) {
d.OnChannelsGetMessages(r.onChannelsGetMessages) return r.onChannelsGetParticipant(ctx, layerRequest)
d.OnChannelsDeleteMessages(r.onChannelsDeleteMessages) })
d.OnChannelsDeleteHistory(r.onChannelsDeleteHistory) registerRPC[*tg.ChannelsGetSendAsRequest](d, tlprofile.SemanticMethodChannelsGetSendAs, func(ctx context.Context, layerRequest *tg.ChannelsGetSendAsRequest) (any, error) {
d.OnMessagesUpdatePinnedMessage(r.onMessagesUpdatePinnedMessage) return r.onChannelsGetSendAs(ctx, layerRequest)
d.OnMessagesUnpinAllMessages(r.onMessagesUnpinAllMessages) })
d.OnMessagesExportChatInvite(r.onMessagesExportChatInvite) registerRPC[*tg.ChannelsCheckUsernameRequest](d, tlprofile.SemanticMethodChannelsCheckUsername, func(ctx context.Context, layerRequest *tg.ChannelsCheckUsernameRequest) (any, error) {
d.OnMessagesCheckChatInvite(r.onMessagesCheckChatInvite) return r.onChannelsCheckUsername(ctx, layerRequest)
d.OnMessagesImportChatInvite(r.onMessagesImportChatInvite) })
d.OnMessagesGetExportedChatInvites(r.onMessagesGetExportedChatInvites) registerRPC[*tg.ChannelsUpdateUsernameRequest](d, tlprofile.SemanticMethodChannelsUpdateUsername, func(ctx context.Context, layerRequest *tg.ChannelsUpdateUsernameRequest) (any, error) {
d.OnMessagesGetExportedChatInvite(r.onMessagesGetExportedChatInvite) return r.onChannelsUpdateUsername(ctx, layerRequest)
d.OnMessagesEditExportedChatInvite(r.onMessagesEditExportedChatInvite) })
d.OnMessagesDeleteRevokedExportedChatInvites(r.onMessagesDeleteRevokedExportedChatInvites) registerRPC[*tg.ChannelsGetAdminedPublicChannelsRequest](d, tlprofile.SemanticMethodChannelsGetAdminedPublicChannels, func(ctx context.Context, layerRequest *tg.ChannelsGetAdminedPublicChannelsRequest) (any, error) {
d.OnMessagesDeleteExportedChatInvite(r.onMessagesDeleteExportedChatInvite) return r.onChannelsGetAdminedPublicChannels(ctx, layerRequest)
d.OnMessagesGetAdminsWithInvites(r.onMessagesGetAdminsWithInvites) })
d.OnMessagesGetChatInviteImporters(r.onMessagesGetChatInviteImporters) registerRPC[*tg.ChannelsExportMessageLinkRequest](d, tlprofile.SemanticMethodChannelsExportMessageLink, func(ctx context.Context, layerRequest *tg.ChannelsExportMessageLinkRequest) (any, error) {
d.OnMessagesHideChatJoinRequest(r.onMessagesHideChatJoinRequest) return r.onChannelsExportMessageLink(ctx, layerRequest)
d.OnMessagesHideAllChatJoinRequests(r.onMessagesHideAllChatJoinRequests) })
d.OnUpdatesGetChannelDifference(r.onUpdatesGetChannelDifference) 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/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/domain" "telesrv/internal/domain"
) )
func (r *Router) registerChatlists(d *tg.ServerDispatcher) { func (r *Router) registerChatlists(d *tlprofile.Dispatcher) {
d.OnChatlistsExportChatlistInvite(r.onChatlistsExportChatlistInvite) registerRPC[*tg.ChatlistsExportChatlistInviteRequest](d, tlprofile.SemanticMethodChatlistsExportChatlistInvite, func(ctx context.Context, layerRequest *tg.ChatlistsExportChatlistInviteRequest) (any, error) {
d.OnChatlistsDeleteExportedInvite(r.onChatlistsDeleteExportedInvite) return r.onChatlistsExportChatlistInvite(ctx, layerRequest)
d.OnChatlistsEditExportedInvite(r.onChatlistsEditExportedInvite) })
d.OnChatlistsGetExportedInvites(r.onChatlistsGetExportedInvites) registerRPC[*tg.ChatlistsDeleteExportedInviteRequest](d, tlprofile.SemanticMethodChatlistsDeleteExportedInvite, func(ctx context.Context, layerRequest *tg.ChatlistsDeleteExportedInviteRequest) (any, error) {
d.OnChatlistsCheckChatlistInvite(r.onChatlistsCheckChatlistInvite) return r.onChatlistsDeleteExportedInvite(ctx, layerRequest)
d.OnChatlistsJoinChatlistInvite(r.onChatlistsJoinChatlistInvite) })
d.OnChatlistsGetChatlistUpdates(r.onChatlistsGetChatlistUpdates) registerRPC[*tg.ChatlistsEditExportedInviteRequest](d, tlprofile.SemanticMethodChatlistsEditExportedInvite, func(ctx context.Context, layerRequest *tg.ChatlistsEditExportedInviteRequest) (any, error) {
d.OnChatlistsJoinChatlistUpdates(r.onChatlistsJoinChatlistUpdates) return r.onChatlistsEditExportedInvite(ctx, layerRequest)
d.OnChatlistsHideChatlistUpdates(r.onChatlistsHideChatlistUpdates) })
d.OnChatlistsGetLeaveChatlistSuggestions(r.onChatlistsGetLeaveChatlistSuggestions) registerRPC[*tg.ChatlistsGetExportedInvitesRequest](d, tlprofile.SemanticMethodChatlistsGetExportedInvites, func(ctx context.Context, layerRequest *tg.ChatlistsGetExportedInvitesRequest) (any, error) {
d.OnChatlistsLeaveChatlist(r.onChatlistsLeaveChatlist) 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) { 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" "github.com/iamxvbaba/td/tg"
"go.uber.org/zap" "go.uber.org/zap"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/app/contacts" "telesrv/internal/app/contacts"
"telesrv/internal/compat/tdesktop" "telesrv/internal/compat/tdesktop"
"telesrv/internal/domain" "telesrv/internal/domain"
@ -28,38 +29,82 @@ const (
) )
// registerContacts 注册 contacts.* RPC handler。 // registerContacts 注册 contacts.* RPC handler。
func (r *Router) registerContacts(d *tg.ServerDispatcher) { func (r *Router) registerContacts(d *tlprofile.Dispatcher) {
d.OnContactsGetContacts(r.onContactsGetContacts) registerRPC[*tg.ContactsGetContactsRequest](d, tlprofile.SemanticMethodContactsGetContacts, func(ctx context.Context, layerRequest *tg.ContactsGetContactsRequest) (any, error) {
d.OnContactsGetContactIDs(r.onContactsGetContactIDs) return r.onContactsGetContacts(ctx, layerRequest.
d.OnContactsGetStatuses(r.onContactsGetStatuses) Hash)
d.OnContactsImportContacts(r.onContactsImportContacts) })
d.OnContactsAddContact(r.onContactsAddContact) registerRPC[*tg.ContactsGetContactIDsRequest](d, tlprofile.SemanticMethodContactsGetContactIDs, func(ctx context.Context, layerRequest *tg.ContactsGetContactIDsRequest) (any, error) {
d.OnContactsAcceptContact(r.onContactsAcceptContact) return r.onContactsGetContactIDs(ctx, layerRequest.
d.OnContactsDeleteContacts(r.onContactsDeleteContacts) Hash)
d.OnContactsEditCloseFriends(r.onContactsEditCloseFriends) })
d.OnContactsBlock(r.onContactsBlock) registerRPC[*tg.ContactsGetStatusesRequest](d, tlprofile.SemanticMethodContactsGetStatuses, func(ctx context.Context, layerRequest *tg.ContactsGetStatusesRequest) (any, error) {
d.OnContactsUnblock(r.onContactsUnblock) return r.onContactsGetStatuses(ctx)
d.OnContactsSetBlocked(r.onContactsSetBlocked) })
d.OnContactsUpdateContactNote(r.onContactsUpdateContactNote) registerRPC[*tg.ContactsImportContactsRequest](d, tlprofile.SemanticMethodContactsImportContacts, func(ctx context.Context, layerRequest *tg.ContactsImportContactsRequest) (any, error) {
d.OnContactsSearch(r.onContactsSearch) return r.onContactsImportContacts(ctx, layerRequest.
d.OnContactsResolveUsername(r.onContactsResolveUsername) Contacts)
d.OnContactsResolvePhone(r.onContactsResolvePhone) })
d.OnContactsGetTopPeers(func(ctx context.Context, req *tg.ContactsGetTopPeersRequest) (tg.ContactsTopPeersClass, error) { 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 return tdesktop.TopPeers(), nil
}) })
d.OnContactsGetBlocked(r.onContactsGetBlocked) registerRPC[*tg.ContactsGetBlockedRequest](d, tlprofile.SemanticMethodContactsGetBlocked, func(ctx context.Context, layerRequest *tg.ContactsGetBlockedRequest) (any, error) {
d.OnContactsGetBirthdays(func(ctx context.Context) (*tg.ContactsContactBirthdays, 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 { if _, _, err := r.currentUserID(ctx); err != nil {
return nil, internalErr() return nil, internalErr()
} }
return &tg.ContactsContactBirthdays{Contacts: []tg.ContactBirthday{}, Users: []tg.UserClass{}}, nil 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 { if utf8.RuneCountInString(q) > maxContactSearchQLen {
return nil, limitInvalidErr() return nil, limitInvalidErr()
} }
return &tg.ContactsSponsoredPeersEmpty{}, nil return &tg.ContactsSponsoredPeersEmpty{}, nil
}) })
} }
func (r *Router) onContactsEditCloseFriends(ctx context.Context, id []int64) (bool, error) { 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 { if err != nil {
t.Fatalf("dispatch edit close friends: %v", err) t.Fatalf("dispatch edit close friends: %v", err)
} }
box, ok := enc.(*tg.BoolBox) if value, ok := dispatchCanonicalValue(enc).(bool); !ok || !value {
if !ok { t.Fatalf("edit close friends result = %#v (%T), want true", dispatchCanonicalValue(enc), enc)
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)
} }
var get bin.Buffer var get bin.Buffer
@ -99,12 +95,11 @@ func TestContactsEditCloseFriendsProjectsUserFlag(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("dispatch get contacts: %v", err) t.Fatalf("dispatch get contacts: %v", err)
} }
contactsBox, ok := got.(*tg.ContactsContactsBox) list, ok := got.(*tg.ContactsContacts)
if !ok { 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 len(list.Users) != 1 {
if !ok || len(list.Users) != 1 {
t.Fatalf("contacts result = %T %+v, want one contact user", got, got) t.Fatalf("contacts result = %T %+v, want one contact user", got, got)
} }
user, ok := list.Users[0].(*tg.User) user, ok := list.Users[0].(*tg.User)

View file

@ -65,13 +65,9 @@ func TestMessagesGetDialogsReturnsNotModifiedFromFullListHash(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("dispatch: %v", err) t.Fatalf("dispatch: %v", err)
} }
box, ok := enc.(*tg.MessagesDialogsBox) got, ok := enc.(*tg.MessagesDialogsNotModified)
if !ok { if !ok {
t.Fatalf("response = %T, want *tg.MessagesDialogsBox", enc) t.Fatalf("response = %T, want *tg.MessagesDialogsNotModified", enc)
}
got, ok := box.Dialogs.(*tg.MessagesDialogsNotModified)
if !ok {
t.Fatalf("boxed response = %T, want *tg.MessagesDialogsNotModified", box.Dialogs)
} }
if got.Count != 3 || dialogs.filter.Hash != 77 { if got.Count != 3 || dialogs.filter.Hash != 77 {
t.Fatalf("not modified = %+v filter %+v, want count/hash from service", got, dialogs.filter) 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 { if err != nil {
t.Fatalf("dispatch: %v", err) t.Fatalf("dispatch: %v", err)
} }
box, ok := enc.(*tg.MessagesDialogsBox) got, ok := enc.(*tg.MessagesDialogsNotModified)
if !ok { if !ok {
t.Fatalf("response = %T, want *tg.MessagesDialogsBox", enc) t.Fatalf("response = %T, want *tg.MessagesDialogsNotModified", enc)
}
got, ok := box.Dialogs.(*tg.MessagesDialogsNotModified)
if !ok {
t.Fatalf("boxed response = %T, want *tg.MessagesDialogsNotModified", box.Dialogs)
} }
if got.Count != 5 { if got.Count != 5 {
t.Fatalf("not modified count = %d, want 5", got.Count) t.Fatalf("not modified count = %d, want 5", got.Count)
@ -733,13 +725,12 @@ func TestMessagesGetDialogsIncludesCloudDraft(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("get dialogs: %v", err) t.Fatalf("get dialogs: %v", err)
} }
box, ok := got.(*tg.MessagesDialogsBox) dialogs, ok := got.(*tg.MessagesDialogs)
if !ok { 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 len(dialogs.Dialogs) != 1 {
if !ok || len(dialogs.Dialogs) != 1 { t.Fatalf("dialogs = %+v, want one messages.dialogs", dialogs)
t.Fatalf("dialogs = %T %+v, want one messages.dialogs", box.Dialogs, box.Dialogs)
} }
dialog, ok := dialogs.Dialogs[0].(*tg.Dialog) dialog, ok := dialogs.Dialogs[0].(*tg.Dialog)
if !ok { if !ok {
@ -1092,13 +1083,9 @@ func TestMessagesGetDialogsTDesktopInitialPageMergesPinnedHeader(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("dispatch get dialogs: %v", err) t.Fatalf("dispatch get dialogs: %v", err)
} }
box, ok := enc.(*tg.MessagesDialogsBox) out, ok := enc.(*tg.MessagesDialogs)
if !ok { if !ok {
t.Fatalf("response = %T, want *tg.MessagesDialogsBox", enc) t.Fatalf("response = %T, want *tg.MessagesDialogs", enc)
}
out, ok := box.Dialogs.(*tg.MessagesDialogs)
if !ok {
t.Fatalf("dialogs response = %T, want *tg.MessagesDialogs", box.Dialogs)
} }
if dialogs.getDialogsCalls != 2 || len(dialogs.filters) != 2 { if dialogs.getDialogsCalls != 2 || len(dialogs.filters) != 2 {
t.Fatalf("GetDialogs calls = %d filters %+v, want normal + pinned", dialogs.getDialogsCalls, dialogs.filters) 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 { if err != nil {
t.Fatalf("dispatch get dialogs: %v", err) t.Fatalf("dispatch get dialogs: %v", err)
} }
box := enc.(*tg.MessagesDialogsBox) out := enc.(*tg.MessagesDialogs)
out := box.Dialogs.(*tg.MessagesDialogs)
if dialogs.getDialogsCalls != 1 { if dialogs.getDialogsCalls != 1 {
t.Fatalf("GetDialogs calls = %d, want no pinned compatibility load", dialogs.getDialogsCalls) 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 到 // 在 handler 边界把 iamxvbaba/td/tg 类型转换为内部 domain command/query,统一 tgerr.Error 到
// rpc_error 的映射,注入 auth_key_id/session_id/user_id/layer/设备/语言 等上下文, // rpc_error 的映射,注入 auth_key_id/session_id/user_id/layer/设备/语言 等上下文,
// 并对未知 RPC 进入 compatibility trace(不静默吞掉,记入 docs/compatibility-matrix.md)。 // 并对未知 RPC 进入 compatibility trace(不静默吞掉,记入 docs/compatibility-matrix.md)。

View file

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

View file

@ -1,30 +1,64 @@
package rpc package rpc
import "github.com/iamxvbaba/td/tg" import (
"context"
// registerEncrypted 注册私聊端对端加密(Secret Chat)域 RPC。 "github.com/iamxvbaba/td/tg"
// "github.com/iamxvbaba/td/tlprofile"
// 归属约定:messages.getDhConfig 属通话域(DH 参数下发),由 registerPhone 注册、 // registerEncrypted 注册私聊端对端加密(Secret Chat)域 RPC。
// 密聊复用,**本处绝不重复注册 OnMessagesGetDhConfig**(gotd ServerDispatcher 同一 //
// RPC 重复 On* 是静默 last-wins,会覆盖 phone 域真实现)。 // 归属约定:messages.getDhConfig 属通话域(DH 参数下发),由 registerPhone 注册、
// // 密聊复用,**本处绝不重复注册 messages.getDhConfig**(tlprofile.Dispatcher 同一
// P0 落地握手三件套;sendEncrypted / sendEncryptedFile / sendEncryptedService / // RPC 重复 On* 是静默 last-wins,会覆盖 phone 域真实现)。
// readEncryptedHistory / setEncryptedTyping / receivedQueue / reportEncryptedSpam / //
// uploadEncryptedFile 暂未注册,落 fallback → NOT_IMPLEMENTED + compatibility trace, // P0 落地握手三件套;sendEncrypted / sendEncryptedFile / sendEncryptedService /
// 属 P1/P2(qts 引擎 + 消息投递)。设计 docs/secret-chat-module.md。 // readEncryptedHistory / setEncryptedTyping / receivedQueue / reportEncryptedSpam /
func (r *Router) registerEncrypted(d *tg.ServerDispatcher) { // uploadEncryptedFile 暂未注册,落 fallback → NOT_IMPLEMENTED + compatibility trace,
// 属 P1/P2(qts 引擎 + 消息投递)。设计 docs/secret-chat-module.md。
)
func (r *Router) registerEncrypted(d *tlprofile.Dispatcher) {
registerRPC[
// P0:握手三件套。 // P0:握手三件套。
d.OnMessagesRequestEncryption(r.onMessagesRequestEncryption) *tg.MessagesRequestEncryptionRequest](d, tlprofile.SemanticMethodMessagesRequestEncryption, func(ctx context.Context, layerRequest *tg.MessagesRequestEncryptionRequest) (any, error) {
d.OnMessagesAcceptEncryption(r.onMessagesAcceptEncryption) return r.onMessagesRequestEncryption(ctx, layerRequest)
d.OnMessagesDiscardEncryption(r.onMessagesDiscardEncryption) })
registerRPC[*tg.MessagesAcceptEncryptionRequest](d, tlprofile.SemanticMethodMessagesAcceptEncryption, func(ctx context.Context, layerRequest *tg.MessagesAcceptEncryptionRequest) (
// P1:qts 消息收发 + 已读/typing + 队列确认。 // P1:qts 消息收发 + 已读/typing + 队列确认。
d.OnMessagesSendEncrypted(r.onMessagesSendEncrypted) any, error) {
d.OnMessagesSendEncryptedService(r.onMessagesSendEncryptedService) return r.onMessagesAcceptEncryption(ctx, layerRequest)
d.OnMessagesReadEncryptedHistory(r.onMessagesReadEncryptedHistory) })
d.OnMessagesSetEncryptedTyping(r.onMessagesSetEncryptedTyping) registerRPC[*tg.MessagesDiscardEncryptionRequest](d, tlprofile.SemanticMethodMessagesDiscardEncryption, func(ctx context.Context, layerRequest *tg.MessagesDiscardEncryptionRequest) (any, error) {
d.OnMessagesReceivedQueue(r.onMessagesReceivedQueue) return r.onMessagesDiscardEncryption(ctx, layerRequest)
d.OnMessagesReportEncryptedSpam(r.onMessagesReportEncryptedSpam) })
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:密聊文件。 // P2:密聊文件。
d.OnMessagesSendEncryptedFile(r.onMessagesSendEncryptedFile) error) {
d.OnMessagesUploadEncryptedFile(r.onMessagesUploadEncryptedFile) 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/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/domain" "telesrv/internal/domain"
) )
func (r *Router) registerFolders(d *tg.ServerDispatcher) { func (r *Router) registerFolders(d *tlprofile.Dispatcher) {
d.OnFoldersEditPeerFolders(r.onFoldersEditPeerFolders) 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) { 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" "github.com/iamxvbaba/td/tgerr"
"go.uber.org/zap/zaptest" "go.uber.org/zap/zaptest"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/domain" "telesrv/internal/domain"
) )
@ -130,11 +131,11 @@ func TestFrozenMethodGateIsUserScopedAcrossSessionsAndUnfreezesImmediately(t *te
func TestFrozenMethodGateReturns420BeforeLayerHandler(t *testing.T) { func TestFrozenMethodGateReturns420BeforeLayerHandler(t *testing.T) {
const userID = int64(1001) const userID = int64(1001)
for _, profile := range []tg.LayerProfile{ for _, profile := range []tlprofile.Profile{
tg.LayerProfile225, tlprofile.Profile225,
tg.LayerProfile226, tlprofile.Profile226,
tg.LayerProfile227, tlprofile.Profile227,
tg.LayerProfile228, tlprofile.Profile228,
} { } {
t.Run(fmt.Sprintf("layer_%d", profile), func(t *testing.T) { t.Run(fmt.Sprintf("layer_%d", profile), func(t *testing.T) {
provider := &frozenGateFreezeProvider{freeze: frozenGateActiveState(userID), found: true} provider := &frozenGateFreezeProvider{freeze: frozenGateActiveState(userID), found: true}
@ -149,7 +150,7 @@ func TestFrozenMethodGateReturns420BeforeLayerHandler(t *testing.T) {
Message: "must not reach handler", Message: "must not reach handler",
RandomID: 1, RandomID: 1,
}) })
admitted, err := router.AdmitLayer(profile, &body, tg.LayerDecodeLimits{}) admitted, err := router.AdmitLayer(profile, &body, tlprofile.Limits{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -6,27 +6,38 @@ import (
"github.com/iamxvbaba/td/tg" "github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
androidcompat "telesrv/internal/compat/android" androidcompat "telesrv/internal/compat/android"
ioscompat "telesrv/internal/compat/ios" ioscompat "telesrv/internal/compat/ios"
"telesrv/internal/compat/tdesktop" "telesrv/internal/compat/tdesktop"
) )
// registerHelp 注册 help.* RPC handler(DC 配置、最近 DC)。 // registerHelp 注册 help.* RPC handler(DC 配置、最近 DC)。
func (r *Router) registerHelp(d *tg.ServerDispatcher) { func (r *Router) registerHelp(d *tlprofile.Dispatcher) {
d.OnHelpGetConfig(r.onHelpGetConfig) registerRPC[*tg.HelpGetConfigRequest](d, tlprofile.SemanticMethodHelpGetConfig, func(ctx context.Context, layerRequest *tg.HelpGetConfigRequest) (any, error) {
d.OnHelpGetNearestDC(func(ctx context.Context) (*tg.NearestDC, 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 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 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 { if _, _, err := r.currentUserID(ctx); err != nil {
return nil, internalErr() return nil, internalErr()
} }
return ioscompat.NoAppUpdate(), nil 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 { if r.deps.Help == nil {
return tdesktop.AppConfig(hash), 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 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 { if r.deps.Help == nil {
return tdesktop.CountriesList(req.Hash), nil return tdesktop.CountriesList(req.Hash), nil
} }
@ -53,31 +64,53 @@ func (r *Router) registerHelp(d *tg.ServerDispatcher) {
} }
return tgCountriesList(list), nil 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 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 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 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 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 return tdesktop.TermsOfServiceUpdate(r.clock.Now()), nil
}) })
registerRPC[
// 客户端遇到无法识别的 tg:// 深链时会查询 help.getDeepLinkInfo。telesrv 不维护 // 客户端遇到无法识别的 tg:// 深链时会查询 help.getDeepLinkInfo。telesrv 不维护
// “需更新 App”的特殊深链提示库,对所有 path 返回 deepLinkInfoEmpty——这是规范的 // “需更新 App”的特殊深链提示库,对所有 path 返回 deepLinkInfoEmpty——这是规范的
// “无特殊信息”应答:DrKLO 仅在收到非空 deepLinkInfo 时才弹“请更新 App”弹窗 // “无特殊信息”应答:DrKLO 仅在收到非空 deepLinkInfo 时才弹“请更新 App”弹窗
// (LaunchActivity.java:5175),收到 Empty 则静默放行按普通链接处理。此前未注册 // (LaunchActivity.java:5175),收到 Empty 则静默放行按普通链接处理。此前未注册
// handler 会落 fallback 返回 500 NOT_IMPLEMENTED(污染日志且非正确协议行为)。 // 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 return &tg.HelpDeepLinkInfoEmpty{}, nil
}) })
d.OnHelpDismissSuggestion(r.onHelpDismissSuggestion) registerRPC[*tg.HelpDismissSuggestionRequest](d, tlprofile.SemanticMethodHelpDismissSuggestion, func(ctx context.Context, layerRequest *tg.HelpDismissSuggestionRequest) (any, error) {
d.OnHelpGetPremiumPromo(r.onHelpGetPremiumPromo) 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) { func (r *Router) onHelpGetConfig(ctx context.Context) (*tg.Config, error) {

View file

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

View file

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

View file

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

View file

@ -14,6 +14,7 @@ import (
"github.com/iamxvbaba/td/tgerr" "github.com/iamxvbaba/td/tgerr"
"go.uber.org/zap/zaptest" "go.uber.org/zap/zaptest"
"github.com/iamxvbaba/td/tlprofile"
appfiles "telesrv/internal/app/files" appfiles "telesrv/internal/app/files"
"telesrv/internal/domain" "telesrv/internal/domain"
"telesrv/internal/postresponse" "telesrv/internal/postresponse"
@ -33,7 +34,7 @@ func TestLayerAdmissionAndroidPrivateOverlayUsesExactProfile(t *testing.T) {
body.PutVectorHeader(0) body.PutVectorHeader(0)
body.PutID(0x7f3b18ea) // inputPeerEmpty 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -41,10 +42,10 @@ func TestLayerAdmissionAndroidPrivateOverlayUsesExactProfile(t *testing.T) {
t.Fatalf("private request left %d bytes", body.Len()) t.Fatalf("private request left %d bytes", body.Len())
} }
call := admitted.Call() 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()) 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) 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) { func TestLayerAdmissionOfficialProfileOwnsOverlappingAndroidID(t *testing.T) {
r := New(Config{DC: 2, IP: "127.0.0.1", Port: 2398}, Deps{}, zaptest.NewLogger(t), clock.System) 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} request := &tg.ContactsSearchRequest{Q: "exact", Limit: 20}
body := encodeExactLayerRPC(t, tg.LayerProfile225, request) body := encodeExactLayerRPC(t, tlprofile.Profile225, request)
admitted, err := r.AdmitLayer(tg.LayerProfile225, &body, tg.LayerDecodeLimits{}) admitted, err := r.AdmitLayer(tlprofile.Profile225, &body, tlprofile.Limits{})
if err != nil { if err != nil {
t.Fatal(err) 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()) 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) r := New(Config{DC: 2, IP: "127.0.0.1", Port: 2398}, Deps{}, zaptest.NewLogger(t), clock.System)
bounded := bin.Buffer{Buf: androidPrivateForwardMessagesWire()} bounded := bin.Buffer{Buf: androidPrivateForwardMessagesWire()}
boundedOriginal := bounded.Copy() 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") t.Fatal("oversized Android-private request was admitted")
} }
if !bytes.Equal(bounded.Raw(), boundedOriginal) { if !bytes.Equal(bounded.Raw(), boundedOriginal) {
@ -76,7 +77,7 @@ func TestLayerAdmissionAndroidOverlayFailureDoesNotConsumeInput(t *testing.T) {
malformed := bin.Buffer{} malformed := bin.Buffer{}
malformed.PutID(0x41d41ade) malformed.PutID(0x41d41ade)
original := malformed.Copy() 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") t.Fatal("malformed Android-private request was admitted")
} }
if string(malformed.Raw()) != string(original) { if string(malformed.Raw()) != string(original) {
@ -86,7 +87,7 @@ func TestLayerAdmissionAndroidOverlayFailureDoesNotConsumeInput(t *testing.T) {
unknown := bin.Buffer{} unknown := bin.Buffer{}
unknown.PutID(0xdeadbeef) unknown.PutID(0xdeadbeef)
original = unknown.Copy() 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) t.Fatalf("official unknown error = %v", err)
} }
if string(unknown.Raw()) != string(original) { if string(unknown.Raw()) != string(original) {
@ -154,12 +155,12 @@ func TestLayerAdmissionAndroidPrivateInnermostAcrossWrappers(t *testing.T) {
replaceTerminalRPC(t, &initWire, private) replaceTerminalRPC(t, &initWire, private)
var unprofiledInit bin.Buffer var unprofiledInit bin.Buffer
unprofiledInit.PutID(tg.InvokeWithLayerRequestTypeID) unprofiledInit.PutID(tg.InvokeWithLayerRequestTypeID)
unprofiledInit.PutInt(int(tg.LayerProfile227)) unprofiledInit.PutInt(int(tlprofile.Profile227))
unprofiledInit.Put(initWire.Raw()) unprofiledInit.Put(initWire.Raw())
var unprofiledBare bin.Buffer var unprofiledBare bin.Buffer
unprofiledBare.PutID(tg.InvokeWithLayerRequestTypeID) unprofiledBare.PutID(tg.InvokeWithLayerRequestTypeID)
unprofiledBare.PutInt(int(tg.LayerProfile227)) unprofiledBare.PutInt(int(tlprofile.Profile227))
unprofiledBare.Put(private) unprofiledBare.Put(private)
tests := []struct { tests := []struct {
@ -179,18 +180,18 @@ func TestLayerAdmissionAndroidPrivateInnermostAcrossWrappers(t *testing.T) {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
body := bin.Buffer{Buf: append([]byte(nil), tc.body...)} body := bin.Buffer{Buf: append([]byte(nil), tc.body...)}
var ( var (
admitted tg.LayerRequest admitted tlprofile.Admission
err error err error
) )
if tc.unprofiled { if tc.unprofiled {
admitted, err = r.AdmitUnprofiled(&body, tg.LayerDecodeLimits{}) admitted, err = r.AdmitUnprofiled(&body, tlprofile.Limits{})
} else { } else {
admitted, err = r.AdmitLayer(tg.LayerProfile227, &body, tg.LayerDecodeLimits{}) admitted, err = r.AdmitLayer(tlprofile.Profile227, &body, tlprofile.Limits{})
} }
if err != nil { if err != nil {
t.Fatal(err) 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()) 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. // DrKLO's private createChat constructor is body-identical to canonical.
binary.LittleEndian.PutUint32(body.Buf[:4], 0x0034a818) binary.LittleEndian.PutUint32(body.Buf[:4], 0x0034a818)
original := body.Copy() 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) t.Fatalf("private createChat admission err = %v, want LIMIT_INVALID", err)
} }
if !bytes.Equal(body.Raw(), original) { if !bytes.Equal(body.Raw(), original) {
@ -219,7 +220,7 @@ func TestLayerAdmissionAndroidPrivateFieldPolicyBeforeTypedMaterialization(t *te
} }
func TestLayerDispatchExactProfilesShareOneHandler(t *testing.T) { 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) { 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) r := New(Config{DC: 2, IP: "127.0.0.1", Port: 2398}, Deps{}, zaptest.NewLogger(t), clock.System)
request := &tg.InvokeWithLayerRequest{ request := &tg.InvokeWithLayerRequest{
@ -239,7 +240,7 @@ func TestLayerDispatchExactProfilesShareOneHandler(t *testing.T) {
if err := request.Encode(&body); err != nil { if err := request.Encode(&body); err != nil {
t.Fatal(err) t.Fatal(err)
} }
admitted, err := r.AdmitUnprofiled(&body, tg.LayerDecodeLimits{}) admitted, err := r.AdmitUnprofiled(&body, tlprofile.Limits{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -257,23 +258,19 @@ func TestLayerDispatchExactProfilesShareOneHandler(t *testing.T) {
if err := result.Encode(&exact); err != nil { if err := result.Encode(&exact); err != nil {
t.Fatal(err) t.Fatal(err)
} }
decoded, err := tg.DecodeLayer(profile, tg.LayerConstructorConfigType(), &exact) decodedObject, err := tlprofile.DecodeObject(profile, &exact, tlprofile.Limits{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
decoded, ok := decodedObject.(*tg.Config)
if !ok {
t.Fatalf("decoded config type = %T", decodedObject)
}
if exact.Len() != 0 || decoded.ThisDC != 2 { if exact.Len() != 0 || decoded.ThisDC != 2 {
t.Fatalf("decoded config = dc:%d remaining:%d", decoded.ThisDC, exact.Len()) 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 var replay bin.Buffer
if err := prepared.Encode(admitted.Call(), &replay); err != nil { if err := result.Encode(&replay); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if string(replay.Raw()) != string(tgBufferBytes(t, result)) { if string(replay.Raw()) != string(tgBufferBytes(t, result)) {
@ -308,7 +305,7 @@ func TestLayerDispatchUnprofiledInvariantDoesNotPublishRepresentativeLayer(t *te
if err := request.Encode(&body); err != nil { if err := request.Encode(&body); err != nil {
t.Fatal(err) t.Fatal(err)
} }
admitted, err := r.AdmitUnprofiled(&body, tg.LayerDecodeLimits{}) admitted, err := r.AdmitUnprofiled(&body, tlprofile.Limits{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -339,11 +336,11 @@ func TestLayerDispatchUnprofiledInvariantDoesNotPublishRepresentativeLayer(t *te
if err := (&tg.InvokeWithLayerRequest{Layer: 227, Query: request}).Encode(&profiledBody); err != nil { if err := (&tg.InvokeWithLayerRequest{Layer: 227, Query: request}).Encode(&profiledBody); err != nil {
t.Fatal(err) t.Fatal(err)
} }
profiled, err := r.AdmitUnprofiled(&profiledBody, tg.LayerDecodeLimits{}) profiled, err := r.AdmitUnprofiled(&profiledBody, tlprofile.Limits{})
if err != nil { if err != nil {
t.Fatal(err) 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) t.Fatalf("profiled bind evidence = (%d,%v), want layer 227", profile, ok)
} }
freezeAndPublishLayer(t, r, rawAuthKeyID, sessionID, 100, 1, 227) freezeAndPublishLayer(t, r, rawAuthKeyID, sessionID, 100, 1, 227)
@ -376,14 +373,14 @@ func TestLayerDispatchInheritedDefaultIsEffectiveWithoutBecomingExplicitEvidence
if err := request.Encode(&body); err != nil { if err := request.Encode(&body); err != nil {
t.Fatal(err) t.Fatal(err)
} }
admitted, err := r.AdmitDefaultLayer(tg.LayerProfile225, &body, tg.LayerDecodeLimits{}) admitted, err := r.AdmitDefaultLayer(tlprofile.Profile225, &body, tlprofile.Limits{})
if err != nil { if err != nil {
t.Fatal(err) 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) 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) t.Fatalf("explicit evidence = (%d,%v), want (0,false)", profile, ok)
} }
result, _, err := r.DispatchAdmitted(WithLayer(context.Background(), 227), rawAuthKeyID, sessionID, 0, 0, admitted) result, _, err := r.DispatchAdmitted(WithLayer(context.Background(), 227), rawAuthKeyID, sessionID, 0, 0, admitted)
@ -419,8 +416,8 @@ func TestLayerDispatchProfiledBareIgnoresStaleMetadataLayer(t *testing.T) {
ExpiresAt: 3, ExpiresAt: 3,
EncryptedMessage: []byte("bind"), EncryptedMessage: []byte("bind"),
} }
body := encodeExactLayerRPC(t, tg.LayerProfile225, request) body := encodeExactLayerRPC(t, tlprofile.Profile225, request)
admitted, err := r.AdmitLayer(tg.LayerProfile225, &body, tg.LayerDecodeLimits{}) admitted, err := r.AdmitLayer(tlprofile.Profile225, &body, tlprofile.Limits{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -461,7 +458,7 @@ func TestLayerDispatchUnprofiledInvariantWithholdsUpdatesReadinessUntilEvidence(
if err := request.Encode(&body); err != nil { if err := request.Encode(&body); err != nil {
t.Fatal(err) t.Fatal(err)
} }
admitted, err := r.AdmitUnprofiled(&body, tg.LayerDecodeLimits{}) admitted, err := r.AdmitUnprofiled(&body, tlprofile.Limits{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -497,7 +494,7 @@ func TestLayerDispatchUnprofiledInvariantWithholdsUpdatesReadinessUntilEvidence(
if err := (&tg.InvokeWithLayerRequest{Layer: 227, Query: request}).Encode(&wrapped); err != nil { if err := (&tg.InvokeWithLayerRequest{Layer: 227, Query: request}).Encode(&wrapped); err != nil {
t.Fatal(err) t.Fatal(err)
} }
profiled, err := r.AdmitUnprofiled(&wrapped, tg.LayerDecodeLimits{}) profiled, err := r.AdmitUnprofiled(&wrapped, tlprofile.Limits{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -522,14 +519,14 @@ func TestLayerAdmissionPreflightRunsBeforeLargeVectorDecode(t *testing.T) {
users[index] = &tg.InputUserEmpty{} users[index] = &tg.InputUserEmpty{}
} }
request := &tg.InvokeWithLayerRequest{ request := &tg.InvokeWithLayerRequest{
Layer: int(tg.LayerProfile225), Layer: int(tlprofile.Profile225),
Query: &tg.UsersGetUsersRequest{ID: users}, Query: &tg.UsersGetUsersRequest{ID: users},
} }
var body bin.Buffer var body bin.Buffer
if err := request.Encode(&body); err != nil { if err := request.Encode(&body); err != nil {
t.Fatal(err) 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) 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) { func TestLayerAdmissionFieldPoliciesCoverEveryRoutableProfile(t *testing.T) {
type vectorCase struct { type vectorCase struct {
name string name string
method tg.LayerSemanticID method tlprofile.SemanticID
max int max int
errorCode string errorCode string
request func(int) bin.Object request func(int) bin.Object
} }
cases := []vectorCase{ 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{})} 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{})} 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)} 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{})} 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)} 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{})} 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{})} 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)} 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{}})} 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)} 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)} 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)} 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"} 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{})} 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) r := New(Config{DC: 2, IP: "127.0.0.1", Port: 2398}, Deps{}, zaptest.NewLogger(t), clock.System)
limits := tg.LayerDecodeLimits{MaxVectorElements: 8 << 10} limits := tlprofile.Limits{MaxVectorElements: 8 << 10}
for profile := tg.LayerProfile225; profile <= tg.LayerProfile227; profile++ { for profile := tlprofile.Profile225; profile <= tlprofile.Profile227; profile++ {
for _, tc := range cases { for _, tc := range cases {
tc := tc tc := tc
if _, available := tg.LayerWireID(profile, tc.method); !available { if _, available := tlprofile.WireID(profile, tc.method); !available {
continue continue
} }
t.Run(fmt.Sprintf("layer_%d/%s/at_cap", profile, tc.name), func(t *testing.T) { 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) { func TestLayerAdmissionUploadFieldsCoverEveryProfile(t *testing.T) {
r := New(Config{DC: 2, IP: "127.0.0.1", Port: 2398}, Deps{}, zaptest.NewLogger(t), clock.System) 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) { t.Run(fmt.Sprintf("layer_%d/saveFilePart", profile), func(t *testing.T) {
atCap := encodeExactLayerRPC(t, profile, &tg.UploadSaveFilePartRequest{Bytes: make([]byte, appfiles.MaxUploadPartBytes)}) 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) t.Fatal(err)
} }
over := encodeExactLayerRPC(t, profile, &tg.UploadSaveFilePartRequest{Bytes: make([]byte, appfiles.MaxUploadPartBytes+1)}) over := encodeExactLayerRPC(t, profile, &tg.UploadSaveFilePartRequest{Bytes: make([]byte, appfiles.MaxUploadPartBytes+1)})
original := over.Copy() 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) t.Fatalf("oversized part err = %v", err)
} }
if string(over.Raw()) != string(original) { 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) { 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)}) 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) t.Fatal(err)
} }
for _, totalParts := range []int{0, -1, appfiles.MaxUploadParts + 1} { for _, totalParts := range []int{0, -1, appfiles.MaxUploadParts + 1} {
body := encodeExactLayerRPC(t, profile, &tg.UploadSaveBigFilePartRequest{FileTotalParts: totalParts}) body := encodeExactLayerRPC(t, profile, &tg.UploadSaveBigFilePartRequest{FileTotalParts: totalParts})
original := body.Copy() 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) t.Fatalf("total parts %d err = %v", totalParts, err)
} }
if string(body.Raw()) != string(original) { 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)}) 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) t.Fatalf("oversized big part err = %v", err)
} }
}) })
@ -666,14 +663,10 @@ func repeatLayerPreflightValue[T any](n int, value T) []T {
return result 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() t.Helper()
outbound, err := tg.PrepareLayerOutboundCall(profile, request)
if err != nil {
t.Fatal(err)
}
var body bin.Buffer var body bin.Buffer
if err := outbound.Encode(&body); err != nil { if err := tlprofile.EncodeObject(profile, request, &body); err != nil {
t.Fatal(err) t.Fatal(err)
} }
return body return body
@ -682,7 +675,7 @@ func encodeExactLayerRPC(t *testing.T, profile tg.LayerProfile, request bin.Obje
func TestLayerDispatchRejectsUnsupportedWrapperBeforeHandler(t *testing.T) { func TestLayerDispatchRejectsUnsupportedWrapperBeforeHandler(t *testing.T) {
r := New(Config{DC: 2, IP: "127.0.0.1", Port: 2398}, Deps{}, zaptest.NewLogger(t), clock.System) r := New(Config{DC: 2, IP: "127.0.0.1", Port: 2398}, Deps{}, zaptest.NewLogger(t), clock.System)
request := &tg.InvokeWithLayerRequest{ request := &tg.InvokeWithLayerRequest{
Layer: int(tg.LayerProfile227), Layer: int(tlprofile.Profile227),
Query: &tg.InvokeWithTakeoutRequest{ Query: &tg.InvokeWithTakeoutRequest{
TakeoutID: 1, TakeoutID: 1,
Query: &tg.HelpGetConfigRequest{}, Query: &tg.HelpGetConfigRequest{},
@ -692,7 +685,7 @@ func TestLayerDispatchRejectsUnsupportedWrapperBeforeHandler(t *testing.T) {
if err := request.Encode(&body); err != nil { if err := request.Encode(&body); err != nil {
t.Fatal(err) t.Fatal(err)
} }
admitted, err := r.AdmitUnprofiled(&body, tg.LayerDecodeLimits{}) admitted, err := r.AdmitUnprofiled(&body, tlprofile.Limits{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -715,7 +708,7 @@ func TestPrepareAdmittedReplayRestoresReadinessOnlyAfterDelivery(t *testing.T) {
clock.System, clock.System,
) )
request := &tg.InvokeWithLayerRequest{ request := &tg.InvokeWithLayerRequest{
Layer: int(tg.LayerProfile225), Layer: int(tlprofile.Profile225),
Query: &tg.InitConnectionRequest{ Query: &tg.InitConnectionRequest{
APIID: 123, DeviceModel: "Desktop", SystemVersion: "Windows", AppVersion: "test", APIID: 123, DeviceModel: "Desktop", SystemVersion: "Windows", AppVersion: "test",
SystemLangCode: "en", LangPack: "tdesktop", LangCode: "en", SystemLangCode: "en", LangPack: "tdesktop", LangCode: "en",
@ -726,7 +719,7 @@ func TestPrepareAdmittedReplayRestoresReadinessOnlyAfterDelivery(t *testing.T) {
if err := request.Encode(&body); err != nil { if err := request.Encode(&body); err != nil {
t.Fatal(err) t.Fatal(err)
} }
admitted, err := r.AdmitUnprofiled(&body, tg.LayerDecodeLimits{}) admitted, err := r.AdmitUnprofiled(&body, tlprofile.Limits{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -774,7 +767,7 @@ func TestRequestBoundOnlyLayerRPCDoesNotPublishMetadataOrReadiness(t *testing.T)
clock.System, clock.System,
) )
request := &tg.InvokeWithLayerRequest{ request := &tg.InvokeWithLayerRequest{
Layer: int(tg.LayerProfile225), Layer: int(tlprofile.Profile225),
Query: &tg.InitConnectionRequest{ Query: &tg.InitConnectionRequest{
APIID: 123, DeviceModel: "Stale Desktop", SystemVersion: "Windows", AppVersion: "stale", APIID: 123, DeviceModel: "Stale Desktop", SystemVersion: "Windows", AppVersion: "stale",
SystemLangCode: "en", LangPack: "tdesktop", LangCode: "en", SystemLangCode: "en", LangPack: "tdesktop", LangCode: "en",
@ -785,7 +778,7 @@ func TestRequestBoundOnlyLayerRPCDoesNotPublishMetadataOrReadiness(t *testing.T)
if err := request.Encode(&body); err != nil { if err := request.Encode(&body); err != nil {
t.Fatal(err) t.Fatal(err)
} }
admitted, err := r.AdmitUnprofiled(&body, tg.LayerDecodeLimits{}) admitted, err := r.AdmitUnprofiled(&body, tlprofile.Limits{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -830,7 +823,7 @@ func TestPrepareAdmittedReplayDoesNotRollBackNewerExplicitLayerOrClientInfo(t *t
clock.System, clock.System,
) )
oldRequest := &tg.InvokeWithLayerRequest{ oldRequest := &tg.InvokeWithLayerRequest{
Layer: int(tg.LayerProfile225), Layer: int(tlprofile.Profile225),
Query: &tg.InitConnectionRequest{ Query: &tg.InitConnectionRequest{
APIID: 123, DeviceModel: "Old Desktop", SystemVersion: "Windows 10", AppVersion: "old", APIID: 123, DeviceModel: "Old Desktop", SystemVersion: "Windows 10", AppVersion: "old",
SystemLangCode: "en", LangPack: "tdesktop", LangCode: "en", SystemLangCode: "en", LangPack: "tdesktop", LangCode: "en",
@ -841,7 +834,7 @@ func TestPrepareAdmittedReplayDoesNotRollBackNewerExplicitLayerOrClientInfo(t *t
if err := oldRequest.Encode(&body); err != nil { if err := oldRequest.Encode(&body); err != nil {
t.Fatal(err) t.Fatal(err)
} }
admitted, err := r.AdmitUnprofiled(&body, tg.LayerDecodeLimits{}) admitted, err := r.AdmitUnprofiled(&body, tlprofile.Limits{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -886,7 +879,7 @@ func TestDelayedExplicitDispatchCannotRollBackAdmissionTimeLayerOrInitMetadata(t
zaptest.NewLogger(t), zaptest.NewLogger(t),
clock.System, clock.System,
) )
admit := func(layer int, device, version string) tg.LayerRequest { admit := func(layer int, device, version string) tlprofile.Admission {
t.Helper() t.Helper()
request := &tg.InvokeWithLayerRequest{ request := &tg.InvokeWithLayerRequest{
Layer: layer, Layer: layer,
@ -900,7 +893,7 @@ func TestDelayedExplicitDispatchCannotRollBackAdmissionTimeLayerOrInitMetadata(t
if err := request.Encode(&body); err != nil { if err := request.Encode(&body); err != nil {
t.Fatal(err) t.Fatal(err)
} }
admitted, err := r.AdmitUnprofiled(&body, tg.LayerDecodeLimits{}) admitted, err := r.AdmitUnprofiled(&body, tlprofile.Limits{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -940,7 +933,7 @@ func TestSameLayerNakedInitConnectionUsesMessageIDWatermark(t *testing.T) {
zaptest.NewLogger(t), zaptest.NewLogger(t),
clock.System, clock.System,
) )
admit := func(device, version string) tg.LayerRequest { admit := func(device, version string) tlprofile.Admission {
t.Helper() t.Helper()
request := &tg.InitConnectionRequest{ request := &tg.InitConnectionRequest{
APIID: 123, DeviceModel: device, SystemVersion: "Windows", AppVersion: version, APIID: 123, DeviceModel: device, SystemVersion: "Windows", AppVersion: version,
@ -951,7 +944,7 @@ func TestSameLayerNakedInitConnectionUsesMessageIDWatermark(t *testing.T) {
if err := request.Encode(&body); err != nil { if err := request.Encode(&body); err != nil {
t.Fatal(err) t.Fatal(err)
} }
admitted, err := r.AdmitDefaultLayer(tg.LayerProfile227, &body, tg.LayerDecodeLimits{}) admitted, err := r.AdmitDefaultLayer(tlprofile.Profile227, &body, tlprofile.Limits{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

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

View file

@ -107,13 +107,12 @@ func TestMessagesCreateChatCreatesMegagroupAndDialogsRPC(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("dispatch get dialogs: %v", err) t.Fatalf("dispatch get dialogs: %v", err)
} }
box, ok := enc.(*tg.MessagesDialogsBox) dialogs, ok := enc.(*tg.MessagesDialogs)
if !ok { 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 len(dialogs.Dialogs) != 1 || len(dialogs.Chats) != 1 || len(dialogs.Messages) != 1 {
if !ok || len(dialogs.Dialogs) != 1 || len(dialogs.Chats) != 1 || len(dialogs.Messages) != 1 { t.Fatalf("dialogs = %+v, want channel dialog/chat/message", dialogs)
t.Fatalf("dialogs = %T %+v, want channel dialog/chat/message", box.Dialogs, box.Dialogs)
} }
dialog := dialogs.Dialogs[0].(*tg.Dialog) dialog := dialogs.Dialogs[0].(*tg.Dialog)
if peer, ok := dialog.Peer.(*tg.PeerChannel); !ok || peer.ChannelID != channel.ID { if peer, ok := dialog.Peer.(*tg.PeerChannel); !ok || peer.ChannelID != channel.ID {
@ -227,12 +226,11 @@ func TestMessagesCreateChatTDesktopReturnsLegacyChatAndAcceptsInputPeerChatRPC(t
if err != nil { if err != nil {
t.Fatalf("legacy history: %v", err) t.Fatalf("legacy history: %v", err)
} }
legacyBox, ok := legacyHistory.(*tg.MessagesMessagesBox) legacyMessages, ok := legacyHistory.(*tg.MessagesMessages)
if !ok { 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 len(legacyMessages.Messages) != 0 {
if !ok || len(legacyMessages.Messages) != 0 {
t.Fatalf("legacy history = %T %+v, want empty messages.messages", legacyHistory, legacyHistory) 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 { if err != nil {
t.Fatalf("dispatch: %v", err) t.Fatalf("dispatch: %v", err)
} }
box, ok := enc.(*tg.UpdatesBox) got, ok := enc.(*tg.Updates)
if !ok { 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 len(got.Updates) != 1 {
if !ok || len(got.Updates) != 1 { t.Fatalf("updates = %+v, want one update", got)
t.Fatalf("boxed updates = %T %+v, want one update", box.Updates, box.Updates)
} }
edit, ok := got.Updates[0].(*tg.UpdateEditMessage) edit, ok := got.Updates[0].(*tg.UpdateEditMessage)
if !ok || edit.Pts != 7 || edit.PtsCount != 1 { 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" { 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) 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 { 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 { if len(got.Updates) != 4 {
t.Fatalf("updates = %+v, want two message ids and two new messages", got.Updates) 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 { if err != nil {
t.Fatalf("dispatch shared media count search: %v", err) t.Fatalf("dispatch shared media count search: %v", err)
} }
if box, ok := enc.(*tg.MessagesMessagesBox); ok {
enc = box.Messages
}
channelMessages, ok := enc.(*tg.MessagesChannelMessages) channelMessages, ok := enc.(*tg.MessagesChannelMessages)
if !ok { if !ok {
t.Fatalf("shared media count search result = %T, want messages.channelMessages", enc) t.Fatalf("shared media count search result = %T, want messages.channelMessages", enc)
@ -210,9 +207,6 @@ func TestMessagesSearchChatPhotosDoesNotReturnOrdinaryChannelHistory(t *testing.
if err != nil { if err != nil {
t.Fatalf("dispatch chat photos search: %v", err) t.Fatalf("dispatch chat photos search: %v", err)
} }
if box, ok := enc.(*tg.MessagesMessagesBox); ok {
enc = box.Messages
}
got, ok := enc.(*tg.MessagesChannelMessages) got, ok := enc.(*tg.MessagesChannelMessages)
if !ok { if !ok {
t.Fatalf("chat photos search result = %T, want messages.channelMessages", enc) t.Fatalf("chat photos search result = %T, want messages.channelMessages", enc)
@ -315,9 +309,6 @@ func TestMessagesGetSearchCountersUsesMediaCategoryCounts(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("dispatch shared media count search: %v", err) t.Fatalf("dispatch shared media count search: %v", err)
} }
if box, ok := enc.(*tg.MessagesMessagesBox); ok {
enc = box.Messages
}
channelMessages, ok := enc.(*tg.MessagesChannelMessages) channelMessages, ok := enc.(*tg.MessagesChannelMessages)
if !ok { if !ok {
t.Fatalf("shared media count search result = %T, want messages.channelMessages", enc) t.Fatalf("shared media count search result = %T, want messages.channelMessages", enc)
@ -498,13 +489,9 @@ func TestMessagesGetHistoryReturnsStoredMessages(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("dispatch: %v", err) t.Fatalf("dispatch: %v", err)
} }
box, ok := enc.(*tg.MessagesMessagesBox) got, ok := enc.(*tg.MessagesMessages)
if !ok { if !ok {
t.Fatalf("response = %T, want *tg.MessagesMessagesBox", enc) t.Fatalf("response = %T, want *tg.MessagesMessages", enc)
}
got, ok := box.Messages.(*tg.MessagesMessages)
if !ok {
t.Fatalf("boxed response = %T, want *tg.MessagesMessages", box.Messages)
} }
if len(got.Messages) != 1 || len(got.Users) != 1 { if len(got.Messages) != 1 || len(got.Users) != 1 {
t.Fatalf("history = %+v, want one message and one user", got) 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 { if err != nil {
t.Fatalf("dispatch getHistory(monoforum): %v", err) t.Fatalf("dispatch getHistory(monoforum): %v", err)
} }
mainBox, ok := mainEnc.(*tg.MessagesMessagesBox) mainHistory, ok := mainEnc.(*tg.MessagesChannelMessages)
if !ok { if !ok {
t.Fatalf("getHistory(monoforum) = %T, want MessagesMessagesBox", mainEnc) t.Fatalf("getHistory(monoforum) = %T, want *tg.MessagesChannelMessages", mainEnc)
}
mainHistory, ok := mainBox.Messages.(*tg.MessagesChannelMessages)
if !ok {
t.Fatalf("getHistory(monoforum) boxed = %T, want MessagesChannelMessages", mainBox.Messages)
} }
if len(mainHistory.Messages) != 1 { if len(mainHistory.Messages) != 1 {
t.Fatalf("main monoforum history = %d msgs, want only the creation service", len(mainHistory.Messages)) 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" { if method != "messages.receivedMessages" {
t.Fatalf("method = %q, want messages.receivedMessages", method) t.Fatalf("method = %q, want messages.receivedMessages", method)
} }
vector, ok := result.(*tg.ReceivedNotifyMessageVector) vector, ok := dispatchCanonicalValue(result).([]tg.ReceivedNotifyMessage)
if !ok || vector == nil || vector.Elems == nil || len(vector.Elems) != 0 { if !ok || vector == nil || len(vector) != 0 {
t.Fatalf("result = %#v (%T), want non-nil empty ReceivedNotifyMessageVector", result, result) t.Fatalf("result = %#v (%T), want non-nil empty ReceivedNotifyMessageVector", result, result)
} }
} }

View file

@ -3,142 +3,394 @@ package rpc
import ( import (
"context" "context"
"github.com/iamxvbaba/td/tg" "github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/compat/tdesktop" "telesrv/internal/compat/tdesktop"
"telesrv/internal/domain" "telesrv/internal/domain"
"unicode/utf8" "unicode/utf8"
) )
// registerMessages 注册 messages.* RPC handler。 // registerMessages 注册 messages.* RPC handler。
func (r *Router) registerMessages(d *tg.ServerDispatcher) { func (r *Router) registerMessages(d *tlprofile.Dispatcher) {
d.OnMessagesReceivedMessages(r.onMessagesReceivedMessages) registerRPC[*tg.MessagesReceivedMessagesRequest](d, tlprofile.SemanticMethodMessagesReceivedMessages, func(ctx context.Context, layerRequest *tg.MessagesReceivedMessagesRequest) (any, error) {
d.OnMessagesSetTyping(r.onMessagesSetTyping) return r.onMessagesReceivedMessages(ctx, layerRequest.
d.OnMessagesSaveDraft(r.onMessagesSaveDraft) MaxID)
d.OnMessagesSaveDefaultSendAs(r.onMessagesSaveDefaultSendAs) })
d.OnMessagesGetAllDrafts(r.onMessagesGetAllDrafts) registerRPC[*tg.MessagesSetTypingRequest](d, tlprofile.SemanticMethodMessagesSetTyping, func(ctx context.Context, layerRequest *tg.MessagesSetTypingRequest) (any, error) {
d.OnMessagesClearAllDrafts(r.onMessagesClearAllDrafts) return r.onMessagesSetTyping(ctx, layerRequest)
d.OnMessagesGetAllStickers(r.onMessagesGetAllStickers) })
d.OnMessagesGetEmojiStickers(r.onMessagesGetEmojiStickers) registerRPC[*tg.MessagesSaveDraftRequest](d, tlprofile.SemanticMethodMessagesSaveDraft, func(ctx context.Context, layerRequest *tg.MessagesSaveDraftRequest) (any, error) {
d.OnMessagesGetMaskStickers(r.onMessagesGetMaskStickers) return r.onMessagesSaveDraft(ctx, layerRequest)
d.OnMessagesGetFeaturedStickers(r.onMessagesGetFeaturedStickers) })
d.OnMessagesGetFeaturedEmojiStickers(r.onMessagesGetFeaturedEmojiStickers) registerRPC[*tg.MessagesSaveDefaultSendAsRequest](d, tlprofile.SemanticMethodMessagesSaveDefaultSendAs, func(ctx context.Context, layerRequest *tg.MessagesSaveDefaultSendAsRequest) (any, error) {
d.OnMessagesGetOldFeaturedStickers(r.onMessagesGetOldFeaturedStickers) return r.onMessagesSaveDefaultSendAs(ctx, layerRequest)
d.OnMessagesGetRecentStickers(r.onMessagesGetRecentStickers) })
d.OnMessagesGetFavedStickers(r.onMessagesGetFavedStickers) registerRPC[*tg.MessagesGetAllDraftsRequest](d, tlprofile.SemanticMethodMessagesGetAllDrafts, func(ctx context.Context, layerRequest *tg.MessagesGetAllDraftsRequest) (any, error) {
d.OnMessagesGetSavedGifs(r.onMessagesGetSavedGifs) return r.onMessagesGetAllDrafts(ctx)
d.OnMessagesFaveSticker(r.onMessagesFaveSticker) })
d.OnMessagesSaveRecentSticker(r.onMessagesSaveRecentSticker) registerRPC[*tg.MessagesClearAllDraftsRequest](d, tlprofile.SemanticMethodMessagesClearAllDrafts, func(ctx context.Context, layerRequest *tg.MessagesClearAllDraftsRequest) (any, error) {
d.OnMessagesSaveGif(r.onMessagesSaveGif) return r.onMessagesClearAllDrafts(ctx)
d.OnMessagesClearRecentStickers(r.onMessagesClearRecentStickers) })
d.OnMessagesSendMessage(r.onMessagesSendMessage) registerRPC[*tg.MessagesGetAllStickersRequest](d, tlprofile.SemanticMethodMessagesGetAllStickers, func(ctx context.Context, layerRequest *tg.MessagesGetAllStickersRequest) (any, error) {
d.OnMessagesForwardMessages(r.onMessagesForwardMessages) return r.onMessagesGetAllStickers(ctx, layerRequest.
d.OnMessagesGetDialogFilters(r.onMessagesGetDialogFilters) Hash)
d.OnMessagesGetSuggestedDialogFilters(func(ctx context.Context) ([]tg.DialogFilterSuggested, error) { })
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 return tdesktop.SuggestedDialogFilters(), nil
}) })
d.OnMessagesUpdateDialogFilter(r.onMessagesUpdateDialogFilter) registerRPC[*tg.MessagesUpdateDialogFilterRequest](d, tlprofile.SemanticMethodMessagesUpdateDialogFilter, func(ctx context.Context, layerRequest *tg.MessagesUpdateDialogFilterRequest) (any, error) {
d.OnMessagesUpdateDialogFiltersOrder(r.onMessagesUpdateDialogFiltersOrder) return r.onMessagesUpdateDialogFilter(ctx, layerRequest)
d.OnMessagesToggleDialogFilterTags(r.onMessagesToggleDialogFilterTags) })
d.OnMessagesGetSavedDialogs(r.onMessagesGetSavedDialogs) registerRPC[*tg.MessagesUpdateDialogFiltersOrderRequest](d, tlprofile.SemanticMethodMessagesUpdateDialogFiltersOrder, func(ctx context.Context, layerRequest *tg.MessagesUpdateDialogFiltersOrderRequest) (any, error) {
d.OnMessagesGetPinnedSavedDialogs(func(ctx context.Context) (tg.MessagesSavedDialogsClass, 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) return r.onMessagesGetPinnedSavedDialogs(ctx)
}) })
d.OnMessagesToggleSavedDialogPin(r.onMessagesToggleSavedDialogPin) registerRPC[*tg.MessagesToggleSavedDialogPinRequest](d, tlprofile.SemanticMethodMessagesToggleSavedDialogPin, func(ctx context.Context, layerRequest *tg.MessagesToggleSavedDialogPinRequest) (any, error) {
d.OnMessagesReorderPinnedSavedDialogs(r.onMessagesReorderPinnedSavedDialogs) return r.onMessagesToggleSavedDialogPin(ctx, layerRequest)
d.OnMessagesGetSavedDialogsByID(r.onMessagesGetSavedDialogsByID) })
d.OnMessagesGetSavedHistory(r.onMessagesGetSavedHistory) registerRPC[*tg.MessagesReorderPinnedSavedDialogsRequest](d, tlprofile.SemanticMethodMessagesReorderPinnedSavedDialogs, func(ctx context.Context, layerRequest *tg.MessagesReorderPinnedSavedDialogsRequest) (any, error) {
d.OnMessagesReadSavedHistory(r.onMessagesReadSavedHistory) return r.onMessagesReorderPinnedSavedDialogs(ctx, layerRequest)
d.OnMessagesDeleteSavedHistory(r.onMessagesDeleteSavedHistory) })
d.OnMessagesGetCommonChats(r.onMessagesGetCommonChats) registerRPC[*tg.MessagesGetSavedDialogsByIDRequest](d, tlprofile.SemanticMethodMessagesGetSavedDialogsByID, func(ctx context.Context, layerRequest *tg.MessagesGetSavedDialogsByIDRequest) (any, error) {
d.OnMessagesGetDefaultHistoryTTL(r.onMessagesGetDefaultHistoryTTL) return r.onMessagesGetSavedDialogsByID(ctx, layerRequest)
d.OnMessagesSetHistoryTTL(r.onMessagesSetHistoryTTL) })
d.OnMessagesSetDefaultHistoryTTL(r.onMessagesSetDefaultHistoryTTL) registerRPC[*tg.MessagesGetSavedHistoryRequest](d, tlprofile.SemanticMethodMessagesGetSavedHistory, func(ctx context.Context, layerRequest *tg.MessagesGetSavedHistoryRequest) (any, error) {
d.OnMessagesGetSponsoredMessages(r.onMessagesGetSponsoredMessages) return r.onMessagesGetSavedHistory(ctx, layerRequest)
d.OnMessagesGetWebPagePreview(r.onMessagesGetWebPagePreview) })
d.OnMessagesRequestWebView(r.onMessagesRequestWebView) registerRPC[*tg.MessagesReadSavedHistoryRequest](d, tlprofile.SemanticMethodMessagesReadSavedHistory, func(ctx context.Context, layerRequest *tg.MessagesReadSavedHistoryRequest) (any, error) {
d.OnMessagesProlongWebView(r.onMessagesProlongWebView) return r.onMessagesReadSavedHistory(ctx, layerRequest)
d.OnMessagesSendWebViewResultMessage(r.onMessagesSendWebViewResultMessage) })
d.OnMessagesRequestSimpleWebView(r.onMessagesRequestSimpleWebView) registerRPC[*tg.MessagesDeleteSavedHistoryRequest](d, tlprofile.SemanticMethodMessagesDeleteSavedHistory, func(ctx context.Context, layerRequest *tg.MessagesDeleteSavedHistoryRequest) (any, error) {
d.OnMessagesGetBotApp(r.onMessagesGetBotApp) return r.onMessagesDeleteSavedHistory(ctx, layerRequest)
d.OnMessagesRequestAppWebView(r.onMessagesRequestAppWebView) })
d.OnMessagesRequestMainWebView(r.onMessagesRequestMainWebView) registerRPC[*tg.MessagesGetCommonChatsRequest](d, tlprofile.SemanticMethodMessagesGetCommonChats, func(ctx context.Context, layerRequest *tg.MessagesGetCommonChatsRequest) (any, error) {
d.OnMessagesSendWebViewData(r.onMessagesSendWebViewData) return r.onMessagesGetCommonChats(ctx, layerRequest)
d.OnMessagesSendBotRequestedPeer(r.onMessagesSendBotRequestedPeer) })
d.OnMessagesGetPreparedInlineMessage(r.onMessagesGetPreparedInlineMessage) registerRPC[*tg.MessagesGetDefaultHistoryTTLRequest](d, tlprofile.SemanticMethodMessagesGetDefaultHistoryTTL, func(ctx context.Context, layerRequest *tg.MessagesGetDefaultHistoryTTLRequest) (any, error) {
d.OnMessagesGetEmojiGameInfo(r.onMessagesGetEmojiGameInfo) return r.onMessagesGetDefaultHistoryTTL(ctx)
d.OnMessagesGetGameHighScores(r.onMessagesGetGameHighScores) })
d.OnMessagesGetInlineGameHighScores(r.onMessagesGetInlineGameHighScores) registerRPC[*tg.MessagesSetHistoryTTLRequest](d, tlprofile.SemanticMethodMessagesSetHistoryTTL, func(ctx context.Context, layerRequest *tg.MessagesSetHistoryTTLRequest) (any, error) {
d.OnMessagesSetGameScore(r.onMessagesSetGameScore) return r.onMessagesSetHistoryTTL(ctx, layerRequest)
d.OnMessagesSetInlineGameScore(r.onMessagesSetInlineGameScore) })
d.OnMessagesUploadMedia(r.onMessagesUploadMedia) registerRPC[*tg.MessagesSetDefaultHistoryTTLRequest](d, tlprofile.SemanticMethodMessagesSetDefaultHistoryTTL, func(ctx context.Context, layerRequest *tg.MessagesSetDefaultHistoryTTLRequest) (any, error) {
d.OnMessagesSendMedia(r.onMessagesSendMedia) return r.onMessagesSetDefaultHistoryTTL(ctx, layerRequest.
d.OnMessagesSendMultiMedia(r.onMessagesSendMultiMedia) Period)
d.OnMessagesReportSpam(r.onMessagesReportSpam) })
d.OnMessagesReport(r.onMessagesReport) registerRPC[*tg.MessagesGetSponsoredMessagesRequest](d, tlprofile.SemanticMethodMessagesGetSponsoredMessages, func(ctx context.Context, layerRequest *tg.MessagesGetSponsoredMessagesRequest) (any, error) {
d.OnMessagesReportReaction(r.onMessagesReportReaction) return r.onMessagesGetSponsoredMessages(ctx, layerRequest)
d.OnMessagesReportMessagesDelivery(r.onMessagesReportMessagesDelivery) })
d.OnMessagesReportReadMetrics(r.onMessagesReportReadMetrics) registerRPC[*tg.MessagesGetWebPagePreviewRequest](d, tlprofile.SemanticMethodMessagesGetWebPagePreview, func(ctx context.Context, layerRequest *tg.MessagesGetWebPagePreviewRequest) (any, error) {
d.OnMessagesReportMusicListen(r.onMessagesReportMusicListen) return r.onMessagesGetWebPagePreview(ctx, layerRequest)
d.OnMessagesReportSponsoredMessage(r.onMessagesReportSponsoredMessage) })
d.OnMessagesReadMessageContents(r.onMessagesReadMessageContents) registerRPC[*tg.MessagesRequestWebViewRequest](d, tlprofile.SemanticMethodMessagesRequestWebView, func(ctx context.Context, layerRequest *tg.MessagesRequestWebViewRequest) (any, error) {
d.OnMessagesTranslateText(r.onMessagesTranslateText) return r.onMessagesRequestWebView(ctx, layerRequest)
d.OnMessagesTogglePeerTranslations(r.onMessagesTogglePeerTranslations) })
d.OnMessagesGetMessagesViews(r.onMessagesGetMessagesViews) registerRPC[*tg.MessagesProlongWebViewRequest](d, tlprofile.SemanticMethodMessagesProlongWebView, func(ctx context.Context, layerRequest *tg.MessagesProlongWebViewRequest) (any, error) {
d.OnMessagesGetUnreadMentions(r.onMessagesGetUnreadMentions) return r.onMessagesProlongWebView(ctx, layerRequest)
d.OnMessagesReadMentions(r.onMessagesReadMentions) })
d.OnMessagesGetSearchCounters(r.onMessagesGetSearchCounters) registerRPC[*tg.MessagesSendWebViewResultMessageRequest](d, tlprofile.SemanticMethodMessagesSendWebViewResultMessage, func(ctx context.Context, layerRequest *tg.MessagesSendWebViewResultMessageRequest) (any, error) {
d.OnMessagesGetReplies(r.onMessagesGetReplies) return r.onMessagesSendWebViewResultMessage(ctx, layerRequest)
d.OnMessagesGetDiscussionMessage(r.onMessagesGetDiscussionMessage) })
d.OnMessagesReadDiscussion(r.onMessagesReadDiscussion) registerRPC[*tg.MessagesRequestSimpleWebViewRequest](d, tlprofile.SemanticMethodMessagesRequestSimpleWebView, func(ctx context.Context, layerRequest *tg.MessagesRequestSimpleWebViewRequest) (any, error) {
d.OnMessagesGetForumTopics(r.onMessagesGetForumTopics) return r.onMessagesRequestSimpleWebView(ctx, layerRequest)
d.OnMessagesGetForumTopicsByID(r.onMessagesGetForumTopicsByID) })
d.OnMessagesGetOnlines(r.onMessagesGetOnlines) registerRPC[*tg.MessagesGetBotAppRequest](d, tlprofile.SemanticMethodMessagesGetBotApp, func(ctx context.Context, layerRequest *tg.MessagesGetBotAppRequest) (any, error) {
d.OnMessagesGetAvailableReactions(r.onMessagesGetAvailableReactions) return r.onMessagesGetBotApp(ctx, layerRequest)
d.OnMessagesGetAvailableEffects(r.onMessagesGetAvailableEffects) })
d.OnMessagesGetStickers(r.onMessagesGetStickers) registerRPC[*tg.MessagesRequestAppWebViewRequest](d, tlprofile.SemanticMethodMessagesRequestAppWebView, func(ctx context.Context, layerRequest *tg.MessagesRequestAppWebViewRequest) (any, error) {
d.OnMessagesInstallStickerSet(r.onMessagesInstallStickerSet) return r.onMessagesRequestAppWebView(ctx, layerRequest)
d.OnMessagesUninstallStickerSet(r.onMessagesUninstallStickerSet) })
d.OnMessagesReorderStickerSets(r.onMessagesReorderStickerSets) registerRPC[*tg.MessagesRequestMainWebViewRequest](d, tlprofile.SemanticMethodMessagesRequestMainWebView, func(ctx context.Context, layerRequest *tg.MessagesRequestMainWebViewRequest) (any, error) {
d.OnMessagesToggleStickerSets(r.onMessagesToggleStickerSets) return r.onMessagesRequestMainWebView(ctx, layerRequest)
d.OnMessagesGetMyStickers(r.onMessagesGetMyStickers) })
d.OnMessagesGetArchivedStickers(func(ctx context.Context, req *tg.MessagesGetArchivedStickersRequest) (*tg.MessagesArchivedStickers, error) { 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{ return &tg.MessagesArchivedStickers{
Count: 0, Count: 0,
Sets: []tg.StickerSetCoveredClass{}, Sets: []tg.StickerSetCoveredClass{},
}, nil }, nil
}) })
d.OnMessagesGetStickerSet(r.onMessagesGetStickerSet) registerRPC[*tg.MessagesGetStickerSetRequest](d, tlprofile.SemanticMethodMessagesGetStickerSet, func(ctx context.Context, layerRequest *tg.MessagesGetStickerSetRequest) (any, error) {
d.OnMessagesGetEmojiGroups(func(ctx context.Context, hash int) (tg.MessagesEmojiGroupsClass, 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 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 return tdesktop.EmojiStatusGroups(), nil
}) })
d.OnMessagesGetEmojiStickerGroups(r.onMessagesGetEmojiStickerGroups) registerRPC[*tg.MessagesGetEmojiStickerGroupsRequest](d, tlprofile.SemanticMethodMessagesGetEmojiStickerGroups, func(ctx context.Context, layerRequest *tg.MessagesGetEmojiStickerGroupsRequest) (any, error) {
d.OnMessagesGetEmojiProfilePhotoGroups(func(ctx context.Context, hash int) (tg.MessagesEmojiGroupsClass, 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 return tdesktop.EmojiProfilePhotoGroups(), nil
}) })
d.OnMessagesGetEmojiKeywords(r.onMessagesGetEmojiKeywords) registerRPC[*tg.MessagesGetEmojiKeywordsRequest](d, tlprofile.SemanticMethodMessagesGetEmojiKeywords, func(ctx context.Context, layerRequest *tg.MessagesGetEmojiKeywordsRequest) (any, error) {
d.OnMessagesGetEmojiKeywordsDifference(r.onMessagesGetEmojiKeywordsDifference) return r.onMessagesGetEmojiKeywords(ctx, layerRequest.
d.OnMessagesGetEmojiKeywordsLanguages(func(ctx context.Context, langcodes []string) ([]tg.EmojiLanguage, error) { 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 return []tg.EmojiLanguage{}, nil
}) })
d.OnMessagesGetCustomEmojiDocuments(r.onMessagesGetCustomEmojiDocuments) registerRPC[*tg.MessagesGetCustomEmojiDocumentsRequest](d, tlprofile.SemanticMethodMessagesGetCustomEmojiDocuments, func(ctx context.Context, layerRequest *tg.MessagesGetCustomEmojiDocumentsRequest) (any, error) {
d.OnMessagesGetAttachedStickers(r.onMessagesGetAttachedStickers) return r.onMessagesGetCustomEmojiDocuments(ctx, layerRequest.
d.OnMessagesSearchStickerSets(r.onMessagesSearchStickerSets) DocumentID)
d.OnMessagesSearchStickers(r.onMessagesSearchStickers) })
d.OnMessagesGetAttachMenuBots(r.onMessagesGetAttachMenuBots) registerRPC[*tg.MessagesGetAttachedStickersRequest](d, tlprofile.SemanticMethodMessagesGetAttachedStickers, func(ctx context.Context, layerRequest *tg.MessagesGetAttachedStickersRequest) (any, error) {
d.OnMessagesGetAttachMenuBot(r.onMessagesGetAttachMenuBot) return r.onMessagesGetAttachedStickers(ctx, layerRequest.
d.OnMessagesToggleBotInAttachMenu(r.onMessagesToggleBotInAttachMenu) Media)
d.OnMessagesGetQuickReplies(r.onMessagesGetQuickReplies) })
d.OnMessagesCheckQuickReplyShortcut(r.onMessagesCheckQuickReplyShortcut) registerRPC[*tg.MessagesSearchStickerSetsRequest](d, tlprofile.SemanticMethodMessagesSearchStickerSets, func(ctx context.Context, layerRequest *tg.MessagesSearchStickerSetsRequest) (any, error) {
d.OnMessagesReorderQuickReplies(r.onMessagesReorderQuickReplies) return r.onMessagesSearchStickerSets(ctx, layerRequest)
d.OnMessagesEditQuickReplyShortcut(r.onMessagesEditQuickReplyShortcut) })
d.OnMessagesDeleteQuickReplyShortcut(r.onMessagesDeleteQuickReplyShortcut) registerRPC[*tg.MessagesSearchStickersRequest](d, tlprofile.SemanticMethodMessagesSearchStickers, func(ctx context.Context, layerRequest *tg.MessagesSearchStickersRequest) (any, error) {
d.OnMessagesGetQuickReplyMessages(r.onMessagesGetQuickReplyMessages) return r.onMessagesSearchStickers(ctx, layerRequest)
d.OnMessagesSendQuickReplyMessages(r.onMessagesSendQuickReplyMessages) })
d.OnMessagesDeleteQuickReplyMessages(r.onMessagesDeleteQuickReplyMessages) registerRPC[*tg.MessagesGetAttachMenuBotsRequest](d, tlprofile.SemanticMethodMessagesGetAttachMenuBots, func(ctx context.Context, layerRequest *tg.MessagesGetAttachMenuBotsRequest) (any, error) {
d.OnMessagesGetWebPage(r.onMessagesGetWebPage) return r.onMessagesGetAttachMenuBots(ctx, layerRequest.
d.OnMessagesGetDialogs(func(ctx context.Context, req *tg.MessagesGetDialogsRequest) (tg.MessagesDialogsClass, error) { 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 { if r.deps.Dialogs == nil {
return &tg.MessagesDialogs{}, 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 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) id, _ := AuthKeyIDFrom(ctx)
userID, _, err := r.currentUserID(ctx) userID, _, err := r.currentUserID(ctx)
if err != nil { 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 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) id, _ := AuthKeyIDFrom(ctx)
userID, _, err := r.currentUserID(ctx) userID, _, err := r.currentUserID(ctx)
if err != nil { if err != nil {
@ -240,21 +500,52 @@ func (r *Router) registerMessages(d *tg.ServerDispatcher) {
r.trackChannelInterest(ctx, userID, channelIDsFromDialogs(list)...) r.trackChannelInterest(ctx, userID, channelIDsFromDialogs(list)...)
return r.tgPeerDialogs(ctx, userID, r.withDialogListPresence(ctx, userID, list), st), nil return r.tgPeerDialogs(ctx, userID, r.withDialogListPresence(ctx, userID, list), st), nil
}) })
d.OnMessagesGetPeerSettings(r.onMessagesGetPeerSettings) registerRPC[*tg.MessagesGetPeerSettingsRequest](d, tlprofile.SemanticMethodMessagesGetPeerSettings, func(ctx context.Context, layerRequest *tg.MessagesGetPeerSettingsRequest) (any, error) {
d.OnMessagesToggleDialogPin(r.onMessagesToggleDialogPin) return r.onMessagesGetPeerSettings(ctx, layerRequest.
d.OnMessagesReorderPinnedDialogs(r.onMessagesReorderPinnedDialogs) Peer)
d.OnMessagesMarkDialogUnread(r.onMessagesMarkDialogUnread) })
d.OnMessagesGetDialogUnreadMarks(r.onMessagesGetDialogUnreadMarks) registerRPC[*tg.MessagesToggleDialogPinRequest](d, tlprofile.SemanticMethodMessagesToggleDialogPin, func(ctx context.Context, layerRequest *tg.MessagesToggleDialogPinRequest) (any, error) {
d.OnMessagesHidePeerSettingsBar(r.onMessagesHidePeerSettingsBar) return r.onMessagesToggleDialogPin(ctx, layerRequest)
d.OnMessagesGetMessageEditData(r.onMessagesGetMessageEditData) })
d.OnMessagesEditMessage(r.onMessagesEditMessage) registerRPC[*tg.MessagesReorderPinnedDialogsRequest](d, tlprofile.SemanticMethodMessagesReorderPinnedDialogs, func(ctx context.Context, layerRequest *tg.MessagesReorderPinnedDialogsRequest) (any, error) {
d.OnMessagesGetOutboxReadDate(r.onMessagesGetOutboxReadDate) return r.onMessagesReorderPinnedDialogs(ctx, layerRequest)
d.OnMessagesGetMessageReadParticipants(r.onMessagesGetMessageReadParticipants) })
d.OnMessagesDeleteMessages(r.onMessagesDeleteMessages) registerRPC[*tg.MessagesMarkDialogUnreadRequest](d, tlprofile.SemanticMethodMessagesMarkDialogUnread, func(ctx context.Context, layerRequest *tg.MessagesMarkDialogUnreadRequest) (any, error) {
d.OnMessagesDeleteHistory(r.onMessagesDeleteHistory) return r.onMessagesMarkDialogUnread(ctx, layerRequest)
d.OnMessagesGetMessages(r.onMessagesGetMessages) })
d.OnMessagesGetRichMessage(r.onMessagesGetRichMessage) registerRPC[*tg.MessagesGetDialogUnreadMarksRequest](d, tlprofile.SemanticMethodMessagesGetDialogUnreadMarks, func(ctx context.Context, layerRequest *tg.MessagesGetDialogUnreadMarksRequest) (any, error) {
d.OnMessagesGetHistory(func(ctx context.Context, req *tg.MessagesGetHistoryRequest) (tg.MessagesMessagesClass, 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) userID, _, err := r.currentUserID(ctx)
if err != nil { if err != nil {
return nil, internalErr() 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 return r.tgMessagesMessages(ctx, userID, r.enrichMessageList(ctx, userID, list)), nil
}) })
d.OnMessagesGetRecentLocations(r.onMessagesGetRecentLocations) registerRPC[*tg.MessagesGetRecentLocationsRequest](d, tlprofile.SemanticMethodMessagesGetRecentLocations, func(ctx context.Context, layerRequest *tg.MessagesGetRecentLocationsRequest) (any, error) {
d.OnMessagesReadHistory(func(ctx context.Context, req *tg.MessagesReadHistoryRequest) (*tg.MessagesAffectedMessages, error) { return r.onMessagesGetRecentLocations(ctx, layerRequest)
})
registerRPC[*tg.MessagesReadHistoryRequest](d, tlprofile.SemanticMethodMessagesReadHistory, func(ctx context.Context, req *tg.MessagesReadHistoryRequest) (any, error) {
id, _ := AuthKeyIDFrom(ctx) id, _ := AuthKeyIDFrom(ctx)
userID, _, err := r.currentUserID(ctx) userID, _, err := r.currentUserID(ctx)
if err != nil { if err != nil {
@ -365,7 +658,7 @@ func (r *Router) registerMessages(d *tg.ServerDispatcher) {
} }
return r.affectedMessages(ctx, id, userID) 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 { if utf8.RuneCountInString(req.Q) > maxMessageSearchQLength {
return nil, limitInvalidErr() 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 return r.tgMessagesMessages(ctx, userID, r.enrichMessageList(ctx, userID, list)), nil
}) })
d.OnMessagesSearchGlobal(r.onMessagesSearchGlobal) registerRPC[*tg.MessagesSearchGlobalRequest](d, tlprofile.SemanticMethodMessagesSearchGlobal, func(ctx context.Context, layerRequest *tg.MessagesSearchGlobalRequest) (any, error) {
d.OnMessagesGetSearchResultsCalendar(r.onMessagesGetSearchResultsCalendar) return r.onMessagesSearchGlobal(ctx, layerRequest)
d.OnMessagesGetSearchResultsPositions(r.onMessagesGetSearchResultsPositions) })
d.OnMessagesSendReaction(r.onMessagesSendReaction) registerRPC[*tg.MessagesGetSearchResultsCalendarRequest](d, tlprofile.SemanticMethodMessagesGetSearchResultsCalendar, func(ctx context.Context, layerRequest *tg.MessagesGetSearchResultsCalendarRequest) (any, error) {
d.OnMessagesComposeMessageWithAI(r.onMessagesComposeMessageWithAI) 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_FAILED),premium // 语音转文字无识别后端:注册为显式失败(TRANSCRIPTION_FAILED),premium
// 客户端点击转录按钮得到优雅失败提示,而不是 NOT_IMPLEMENTED trace。 // 客户端点击转录按钮得到优雅失败提示,而不是 NOT_IMPLEMENTED trace。
d.OnMessagesTranscribeAudio(func(ctx context.Context, req *tg.MessagesTranscribeAudioRequest) (*tg.MessagesTranscribedAudio, error) { 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 { if _, _, err := r.currentUserID(ctx); err != nil {
return nil, internalErr() return nil, internalErr()
} }
return nil, tgerr400("TRANSCRIPTION_FAILED") return nil, tgerr400("TRANSCRIPTION_FAILED")
}) })
d.OnMessagesGetMessagesReactions(r.onMessagesGetMessagesReactions) registerRPC[*tg.MessagesGetMessagesReactionsRequest](d, tlprofile.SemanticMethodMessagesGetMessagesReactions, func(ctx context.Context, layerRequest *tg.MessagesGetMessagesReactionsRequest) (any, error) {
d.OnMessagesGetMessageReactionsList(r.onMessagesGetMessageReactionsList) return r.onMessagesGetMessagesReactions(ctx, layerRequest)
d.OnMessagesSetDefaultReaction(r.onMessagesSetDefaultReaction) })
d.OnMessagesGetPaidReactionPrivacy(r.onMessagesGetPaidReactionPrivacy) registerRPC[*tg.MessagesGetMessageReactionsListRequest](d, tlprofile.SemanticMethodMessagesGetMessageReactionsList, func(ctx context.Context, layerRequest *tg.MessagesGetMessageReactionsListRequest) (any, error) {
d.OnMessagesTogglePaidReactionPrivacy(r.onMessagesTogglePaidReactionPrivacy) return r.onMessagesGetMessageReactionsList(ctx, layerRequest)
d.OnMessagesSendPaidReaction(r.onMessagesSendPaidReaction) })
d.OnMessagesDeleteParticipantReactions(r.onMessagesDeleteParticipantReactions) registerRPC[*tg.MessagesSetDefaultReactionRequest](d, tlprofile.SemanticMethodMessagesSetDefaultReaction, func(ctx context.Context, layerRequest *tg.MessagesSetDefaultReactionRequest) (any, error) {
d.OnMessagesDeleteParticipantReaction(r.onMessagesDeleteParticipantReaction) return r.onMessagesSetDefaultReaction(ctx, layerRequest.
d.OnMessagesGetUnreadReactions(r.onMessagesGetUnreadReactions) Reaction)
d.OnMessagesReadReactions(r.onMessagesReadReactions) })
d.OnMessagesGetTopReactions(r.onMessagesGetTopReactions) registerRPC[*tg.MessagesGetPaidReactionPrivacyRequest](d, tlprofile.SemanticMethodMessagesGetPaidReactionPrivacy, func(ctx context.Context, layerRequest *tg.MessagesGetPaidReactionPrivacyRequest) (any, error) {
d.OnMessagesGetRecentReactions(r.onMessagesGetRecentReactions) return r.onMessagesGetPaidReactionPrivacy(ctx)
d.OnMessagesClearRecentReactions(r.onMessagesClearRecentReactions) })
d.OnMessagesGetSavedReactionTags(r.onMessagesGetSavedReactionTags) registerRPC[*tg.MessagesTogglePaidReactionPrivacyRequest](d, tlprofile.SemanticMethodMessagesTogglePaidReactionPrivacy, func(ctx context.Context, layerRequest *tg.MessagesTogglePaidReactionPrivacyRequest) (any, error) {
d.OnMessagesUpdateSavedReactionTag(r.onMessagesUpdateSavedReactionTag) return r.onMessagesTogglePaidReactionPrivacy(ctx, layerRequest)
d.OnMessagesGetDefaultTagReactions(r.onMessagesGetDefaultTagReactions) })
d.OnMessagesSendVote(r.onMessagesSendVote) registerRPC[*tg.MessagesSendPaidReactionRequest](d, tlprofile.SemanticMethodMessagesSendPaidReaction, func(ctx context.Context, layerRequest *tg.MessagesSendPaidReactionRequest) (any, error) {
d.OnMessagesGetPollResults(r.onMessagesGetPollResults) return r.onMessagesSendPaidReaction(ctx, layerRequest)
d.OnMessagesGetPollVotes(r.onMessagesGetPollVotes) })
d.OnMessagesAddPollAnswer(r.onMessagesAddPollAnswer) registerRPC[*tg.MessagesDeleteParticipantReactionsRequest](d, tlprofile.SemanticMethodMessagesDeleteParticipantReactions, func(ctx context.Context, layerRequest *tg.MessagesDeleteParticipantReactionsRequest) (any, error) {
d.OnMessagesDeletePollAnswer(r.onMessagesDeletePollAnswer) return r.onMessagesDeleteParticipantReactions(ctx, layerRequest)
d.OnMessagesGetUnreadPollVotes(r.onMessagesGetUnreadPollVotes) })
d.OnMessagesReadPollVotes(r.onMessagesReadPollVotes) registerRPC[*tg.MessagesDeleteParticipantReactionRequest](d, tlprofile.SemanticMethodMessagesDeleteParticipantReaction, func(ctx context.Context, layerRequest *tg.MessagesDeleteParticipantReactionRequest) (any, error) {
d.OnMessagesAppendTodoList(r.onMessagesAppendTodoList) return r.onMessagesDeleteParticipantReaction(ctx, layerRequest)
d.OnMessagesToggleTodoCompleted(r.onMessagesToggleTodoCompleted) })
d.OnMessagesGetScheduledHistory(r.onMessagesGetScheduledHistory) registerRPC[*tg.MessagesGetUnreadReactionsRequest](d, tlprofile.SemanticMethodMessagesGetUnreadReactions, func(ctx context.Context, layerRequest *tg.MessagesGetUnreadReactionsRequest) (any, error) {
d.OnMessagesGetScheduledMessages(r.onMessagesGetScheduledMessages) return r.onMessagesGetUnreadReactions(ctx, layerRequest)
d.OnMessagesSendScheduledMessages(r.onMessagesSendScheduledMessages) })
d.OnMessagesDeleteScheduledMessages(r.onMessagesDeleteScheduledMessages) registerRPC[*tg.MessagesReadReactionsRequest](d, tlprofile.SemanticMethodMessagesReadReactions, func(ctx context.Context, layerRequest *tg.MessagesReadReactionsRequest) (any, error) {
d.OnMessagesCreateForumTopic(r.onMessagesCreateForumTopic) return r.onMessagesReadReactions(ctx, layerRequest)
d.OnMessagesEditForumTopic(r.onMessagesEditForumTopic) })
d.OnMessagesUpdatePinnedForumTopic(r.onMessagesUpdatePinnedForumTopic) registerRPC[*tg.MessagesGetTopReactionsRequest](d, tlprofile.SemanticMethodMessagesGetTopReactions, func(ctx context.Context, layerRequest *tg.MessagesGetTopReactionsRequest) (any, error) {
d.OnMessagesReorderPinnedForumTopics(r.onMessagesReorderPinnedForumTopics) return r.onMessagesGetTopReactions(ctx, layerRequest)
d.OnMessagesDeleteTopicHistory(r.onMessagesDeleteTopicHistory) })
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 { if err != nil {
t.Fatalf("dispatch messages.forwardMessages#41d41ade: %v", err) t.Fatalf("dispatch messages.forwardMessages#41d41ade: %v", err)
} }
// Now routed through the generated static client overlay and // The sparse dispatcher exposes the canonical concrete class result.
// the normal gotd dispatcher, which boxes a class result as *tg.UpdatesBox if _, ok := enc.(tg.UpdatesClass); !ok {
// (wire-identical to the raw UpdatesClass the dedicated handler used to return). t.Fatalf("forward result = %T, want tg.UpdatesClass", enc)
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)
} }
res, err := r.onMessagesGetSavedDialogs(WithUserID(ctx, alice.ID), &tg.MessagesGetSavedDialogsRequest{Limit: 20}) 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 { if err != nil {
t.Fatalf("dispatch: %v", err) t.Fatalf("dispatch: %v", err)
} }
box, ok := enc.(*tg.UpdatesBox) got, ok := enc.(*tg.Updates)
if !ok { 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 {
t.Fatalf("boxed response = %T, want *tg.Updates", box.Updates)
} }
if messages.sendUserID != sender.ID || messages.sendReq.SenderUserID != sender.ID || messages.sendReq.RecipientUserID != recipient.ID || messages.sendReq.OriginSessionID != 77 { 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) 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 { if !messages.sendReq.Silent || !messages.sendReq.NoForwards {
t.Fatalf("send flags silent=%v noforwards=%v, want true/true", 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 { 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) newMsg := got.Updates[1].(*tg.UpdateNewMessage)
msg := newMsg.Message.(*tg.Message) msg := newMsg.Message.(*tg.Message)
if !msg.Silent || !msg.Noforwards { if !msg.Silent || !msg.Noforwards {

View file

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

View file

@ -23,13 +23,9 @@ func TestAccountGetChatThemesReturnsStaticThemes(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("dispatch: %v", err) t.Fatalf("dispatch: %v", err)
} }
box, ok := got.(*tg.AccountThemesBox) themes, ok := got.(*tg.AccountThemes)
if !ok { if !ok {
t.Fatalf("response type = %T, want *tg.AccountThemesBox", got) t.Fatalf("response type = %T, want *tg.AccountThemes", got)
}
themes, ok := box.Themes.(*tg.AccountThemes)
if !ok {
t.Fatalf("boxed response type = %T, want *tg.AccountThemes", box.Themes)
} }
if themes.Hash == 0 || len(themes.Themes) == 0 { if themes.Hash == 0 || len(themes.Themes) == 0 {
t.Fatalf("themes = %+v, want non-empty stable list", themes) t.Fatalf("themes = %+v, want non-empty stable list", themes)
@ -48,20 +44,16 @@ func TestAccountGetUniqueGiftChatThemesReturnsEmptyStub(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("dispatch: %v", err) t.Fatalf("dispatch: %v", err)
} }
box, ok := got.(*tg.AccountChatThemesBox) themes, ok := got.(*tg.AccountChatThemes)
if !ok { if !ok {
t.Fatalf("response type = %T, want *tg.AccountChatThemesBox", got) t.Fatalf("response type = %T, want *tg.AccountChatThemes", got)
}
themes, ok := box.ChatThemes.(*tg.AccountChatThemes)
if !ok {
t.Fatalf("boxed response type = %T, want *tg.AccountChatThemes", box.ChatThemes)
} }
if themes.Hash == 0 || len(themes.Themes) != 0 || len(themes.Chats) != 0 || len(themes.Users) != 0 { 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) 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) r := New(Config{DC: 2, IP: "127.0.0.1", Port: 2398}, Deps{}, zaptest.NewLogger(t), clock.System)
req := &tg.AccountGetWallPapersRequest{} req := &tg.AccountGetWallPapersRequest{}
var in bin.Buffer var in bin.Buffer
@ -73,13 +65,9 @@ func TestAccountGetWallPapersReturnsDefaultCatalog(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("dispatch: %v", err) t.Fatalf("dispatch: %v", err)
} }
box, ok := got.(*tg.AccountWallPapersBox) wallpapers, ok := got.(*tg.AccountWallPapers)
if !ok { if !ok {
t.Fatalf("response type = %T, want *tg.AccountWallPapersBox", got) t.Fatalf("response type = %T, want *tg.AccountWallPapers", got)
}
wallpapers, ok := box.WallPapers.(*tg.AccountWallPapers)
if !ok {
t.Fatalf("boxed response type = %T, want *tg.AccountWallPapers", box.WallPapers)
} }
if wallpapers.Hash == 0 || len(wallpapers.Wallpapers) == 0 { if wallpapers.Hash == 0 || len(wallpapers.Wallpapers) == 0 {
t.Fatalf("wallpapers = %+v, want stable default catalog", wallpapers) t.Fatalf("wallpapers = %+v, want stable default catalog", wallpapers)
@ -98,7 +86,7 @@ func TestAccountWallpaperSeedLookupAndAckRPCs(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("dispatch list: %v", err) t.Fatalf("dispatch list: %v", err)
} }
list := listGot.(*tg.AccountWallPapersBox).WallPapers.(*tg.AccountWallPapers) list := listGot.(*tg.AccountWallPapers)
first := list.Wallpapers[0].(*tg.WallPaper) first := list.Wallpapers[0].(*tg.WallPaper)
input := &tg.InputWallPaper{ID: first.ID, AccessHash: first.AccessHash} input := &tg.InputWallPaper{ID: first.ID, AccessHash: first.AccessHash}
@ -110,13 +98,9 @@ func TestAccountWallpaperSeedLookupAndAckRPCs(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("dispatch getWallPaper: %v", err) t.Fatalf("dispatch getWallPaper: %v", err)
} }
oneBox, ok := oneGot.(*tg.WallPaperBox) oneWallpaper, ok := oneGot.(*tg.WallPaper)
if !ok { if !ok {
t.Fatalf("getWallPaper = %T, want *tg.WallPaperBox", oneGot) t.Fatalf("getWallPaper = %T, want *tg.WallPaper", oneGot)
}
oneWallpaper, ok := oneBox.WallPaper.(*tg.WallPaper)
if !ok {
t.Fatalf("getWallPaper boxed = %T, want *tg.WallPaper", oneBox.WallPaper)
} }
if oneWallpaper.ID != first.ID { if oneWallpaper.ID != first.ID {
t.Fatalf("getWallPaper id = %d, want %d", 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 { if err != nil {
t.Fatalf("dispatch getWallPaper nofile: %v", err) t.Fatalf("dispatch getWallPaper nofile: %v", err)
} }
nofileBox, ok := nofileGot.(*tg.WallPaperBox) nofileWallpaper, ok := nofileGot.(*tg.WallPaperNoFile)
if !ok { 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 nofileWallpaper.ID != nofileInput.ID {
if !ok || nofileWallpaper.ID != nofileInput.ID { t.Fatalf("getWallPaper nofile = %#v, want no-file id", nofileWallpaper)
t.Fatalf("getWallPaper nofile boxed = %T %#v, want no-file id", nofileBox.WallPaper, nofileBox.WallPaper)
} }
var multiReq bin.Buffer var multiReq bin.Buffer
@ -152,7 +135,7 @@ func TestAccountWallpaperSeedLookupAndAckRPCs(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("dispatch getMultiWallPapers: %v", err) 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) t.Fatalf("getMultiWallPapers = %T %#v, want 3 wallpapers", multiGot, multiGot)
} }
@ -171,12 +154,8 @@ func TestAccountWallpaperSeedLookupAndAckRPCs(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("dispatch %s: %v", name, err) t.Fatalf("dispatch %s: %v", name, err)
} }
box, ok := got.(*tg.BoolBox) if value, ok := dispatchCanonicalValue(got).(bool); !ok || !value {
if !ok { t.Fatalf("%s = %#v (%T), want true", name, dispatchCanonicalValue(got), got)
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)
} }
} }
@ -201,13 +180,9 @@ func TestPaymentsGetStarGiftCollectionsNoServiceFallbackAndValidatesPeer(t *test
if err != nil { if err != nil {
t.Fatalf("dispatch: %v", err) t.Fatalf("dispatch: %v", err)
} }
box, ok := got.(*tg.PaymentsStarGiftCollectionsBox) collections, ok := got.(*tg.PaymentsStarGiftCollections)
if !ok { if !ok {
t.Fatalf("response type = %T, want *tg.PaymentsStarGiftCollectionsBox", got) t.Fatalf("response type = %T, want *tg.PaymentsStarGiftCollections", got)
}
collections, ok := box.StarGiftCollections.(*tg.PaymentsStarGiftCollections)
if !ok {
t.Fatalf("boxed response type = %T, want *tg.PaymentsStarGiftCollections", box.StarGiftCollections)
} }
if len(collections.Collections) != 0 { if len(collections.Collections) != 0 {
t.Fatalf("collections = %+v, want empty list", collections.Collections) 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/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/compat/tdesktop" "telesrv/internal/compat/tdesktop"
"telesrv/internal/domain" "telesrv/internal/domain"
) )
// registerPayments 注册 payments.* RPC:Stars 本地账本(余额/流水真实化)+ 其余 // registerPayments 注册 payments.* RPC:Stars 本地账本(余额/流水真实化)+ 其余
// gift/auction/revenue 第一阶段兼容桩。 // gift/auction/revenue 第一阶段兼容桩。
func (r *Router) registerPayments(d *tg.ServerDispatcher) { func (r *Router) registerPayments(d *tlprofile.Dispatcher) {
d.OnPaymentsGetStarsTopupOptions(func(ctx context.Context) ([]tg.StarsTopupOption, error) { registerRPC[*tg.PaymentsGetStarsTopupOptionsRequest](d, tlprofile.SemanticMethodPaymentsGetStarsTopupOptions, func(ctx context.Context, layerRequest *tg.PaymentsGetStarsTopupOptionsRequest) (any,
return devStarsTopupOptions(), nil
})
// premium 订阅赠送 telesrv 不实现(无支付流),返回空选项。关键作用:TDesktop 送礼框 // premium 订阅赠送 telesrv 不实现(无支付流),返回空选项。关键作用:TDesktop 送礼框
// ShowStarGiftBox 的 ready() 门控要求 getPremiumGiftCodeOptions 成功返回(on_next)才置 // ShowStarGiftBox 的 ready() 门控要求 getPremiumGiftCodeOptions 成功返回(on_next)才置
// premiumGiftsReady=true,否则整框不弹出——此前返 NOT_IMPLEMENTED 导致点生日礼物无反应。 // premiumGiftsReady=true,否则整框不弹出——此前返 NOT_IMPLEMENTED 导致点生日礼物无反应。
// 空列表即解门,星礼物正常发送;premium 区段另由 userFull.disallow_premium_gifts=true 隐藏。 // 空列表即解门,星礼物正常发送;premium 区段另由 userFull.disallow_premium_gifts=true 隐藏。
d.OnPaymentsGetPremiumGiftCodeOptions(func(ctx context.Context, req *tg.PaymentsGetPremiumGiftCodeOptionsRequest) ([]tg.PremiumGiftCodeOption, error) { error) {
return devStarsTopupOptions(), nil
})
registerRPC[*tg.PaymentsGetPremiumGiftCodeOptionsRequest](d, tlprofile.SemanticMethodPaymentsGetPremiumGiftCodeOptions, func(ctx context.Context, req *tg.PaymentsGetPremiumGiftCodeOptionsRequest) (any, error) {
return []tg.PremiumGiftCodeOption{}, nil return []tg.PremiumGiftCodeOption{}, nil
}) })
d.OnPaymentsGetStarsStatus(r.onPaymentsGetStarsStatus) registerRPC[*tg.PaymentsGetStarsStatusRequest](d, tlprofile.SemanticMethodPaymentsGetStarsStatus, func(ctx context.Context, layerRequest *tg.PaymentsGetStarsStatusRequest) (any, error) {
d.OnPaymentsGetStarsTransactions(r.onPaymentsGetStarsTransactions) return r.onPaymentsGetStarsStatus(ctx, layerRequest)
d.OnPaymentsGetStarGiftActiveAuctions(func(ctx context.Context, hash int64) (tg.PaymentsStarGiftActiveAuctionsClass, error) { })
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 return tdesktop.StarGiftActiveAuctions(), nil
}) })
d.OnPaymentsGetStarGifts(r.onPaymentsGetStarGifts) registerRPC[*tg.PaymentsGetStarGiftsRequest](d, tlprofile.SemanticMethodPaymentsGetStarGifts, func(ctx context.Context, layerRequest *tg.PaymentsGetStarGiftsRequest) (any, error) {
d.OnPaymentsGetStarGiftUpgradePreview(r.onPaymentsGetStarGiftUpgradePreview) return r.onPaymentsGetStarGifts(ctx, layerRequest.
d.OnPaymentsGetUniqueStarGift(r.onPaymentsGetUniqueStarGift) Hash)
d.OnPaymentsGetPaymentForm(r.onPaymentsGetPaymentForm) })
d.OnPaymentsSendStarsForm(r.onPaymentsSendStarsForm) registerRPC[*tg.PaymentsGetStarGiftUpgradePreviewRequest](d, tlprofile.SemanticMethodPaymentsGetStarGiftUpgradePreview, func(ctx context.Context, layerRequest *tg.PaymentsGetStarGiftUpgradePreviewRequest) (any, error) {
d.OnPaymentsGetSavedStarGifts(r.onPaymentsGetSavedStarGifts) return r.onPaymentsGetStarGiftUpgradePreview(ctx, layerRequest.
d.OnPaymentsGetSavedStarGift(r.onPaymentsGetSavedStarGift) GiftID)
d.OnPaymentsSaveStarGift(r.onPaymentsSaveStarGift) })
d.OnPaymentsConvertStarGift(r.onPaymentsConvertStarGift) registerRPC[*tg.PaymentsGetUniqueStarGiftRequest](d, tlprofile.SemanticMethodPaymentsGetUniqueStarGift, func(ctx context.Context, layerRequest *tg.PaymentsGetUniqueStarGiftRequest) (any, error) {
d.OnPaymentsUpgradeStarGift(r.onPaymentsUpgradeStarGift) return r.onPaymentsGetUniqueStarGift(ctx, layerRequest.
d.OnPaymentsGetStarGiftCollections(r.onPaymentsGetStarGiftCollections) Slug)
d.OnPaymentsCreateStarGiftCollection(r.onPaymentsCreateStarGiftCollection) })
d.OnPaymentsUpdateStarGiftCollection(r.onPaymentsUpdateStarGiftCollection) registerRPC[*tg.PaymentsGetPaymentFormRequest](d, tlprofile.SemanticMethodPaymentsGetPaymentForm, func(ctx context.Context, layerRequest *tg.PaymentsGetPaymentFormRequest) (any, error) {
d.OnPaymentsDeleteStarGiftCollection(r.onPaymentsDeleteStarGiftCollection) return r.onPaymentsGetPaymentForm(ctx, layerRequest)
d.OnPaymentsReorderStarGiftCollections(r.onPaymentsReorderStarGiftCollections) })
d.OnPaymentsToggleStarGiftsPinnedToTop(r.onPaymentsToggleStarGiftsPinnedToTop) registerRPC[*tg.PaymentsSendStarsFormRequest](d, tlprofile.SemanticMethodPaymentsSendStarsForm, func(ctx context.Context, layerRequest *tg.PaymentsSendStarsFormRequest) (any, error) {
d.OnPaymentsGetStarsRevenueAdsAccountURL(func(ctx context.Context, peer tg.InputPeerClass) (*tg.PaymentsStarsRevenueAdsAccountURL, 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) userID, _, err := r.currentUserID(ctx)
if err != nil { if err != nil {
return nil, internalErr() return nil, internalErr()
@ -55,7 +107,7 @@ func (r *Router) registerPayments(d *tg.ServerDispatcher) {
} }
return &tg.PaymentsStarsRevenueAdsAccountURL{URL: "https://ads.telegram.org/"}, nil 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) userID, _, err := r.currentUserID(ctx)
if err != nil { if err != nil {
return nil, internalErr() return nil, internalErr()
@ -68,6 +120,7 @@ func (r *Router) registerPayments(d *tg.ServerDispatcher) {
} }
return tdesktop.StarsRevenueStats(req.GetTon()), nil return tdesktop.StarsRevenueStats(req.GetTon()), nil
}) })
} }
// onPaymentsGetStarsStatus 返回当前账号的 Stars 余额(首读时惰性授予起始余额)。 // onPaymentsGetStarsStatus 返回当前账号的 Stars 余额(首读时惰性授予起始余额)。

View file

@ -10,6 +10,7 @@ import (
"github.com/iamxvbaba/td/tgerr" "github.com/iamxvbaba/td/tgerr"
"go.uber.org/zap/zaptest" "go.uber.org/zap/zaptest"
"github.com/iamxvbaba/td/tlprofile"
appchannels "telesrv/internal/app/channels" appchannels "telesrv/internal/app/channels"
appmessages "telesrv/internal/app/messages" appmessages "telesrv/internal/app/messages"
appstargifts "telesrv/internal/app/stargifts" appstargifts "telesrv/internal/app/stargifts"
@ -107,16 +108,20 @@ func TestSavedStarGiftProjectionCombinesHistoricalCatalogWithCurrentCollectibleA
if upgradeStars, ok := gift.GetUpgradeStars(); !ok || upgradeStars != 75 { if upgradeStars, ok := gift.GetUpgradeStars(); !ok || upgradeStars != 75 {
t.Fatalf("upgrade_stars = %d ok=%v, want current price 75", upgradeStars, ok) 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{}} wire := &tg.PaymentsSavedStarGifts{Count: 1, Gifts: projected, Chats: []tg.ChatClass{}, Users: []tg.UserClass{}}
encoded := &bin.Buffer{} 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) 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 { if err != nil {
t.Fatalf("decode Layer %d saved gift: %v", profile, err) 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) inner, ok := decoded.Gifts[0].Gift.(*tg.StarGift)
if !ok || !decoded.Gifts[0].CanUpgrade || inner.UpgradeStars != 75 { if !ok || !decoded.Gifts[0].CanUpgrade || inner.UpgradeStars != 75 {
t.Fatalf("Layer %d projection lost upgrade flags: %#v", profile, decoded.Gifts[0]) 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 { } else if user, ok := peer.(*tg.PeerUser); !ok || user.UserID != owner.ID {
t.Fatalf("unique service action peer = %#v", peer) 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{} 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) 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 { if err != nil {
t.Fatalf("decode Layer %d unique response: %v", profile, err) 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) decodedGift, ok := decodedResponse.Gift.(*tg.StarGiftUnique)
if !ok || decodedGift.Slug != unique.Slug || len(decodedGift.Attributes) != 4 { if !ok || decodedGift.Slug != unique.Slug || len(decodedGift.Attributes) != 4 {
t.Fatalf("Layer %d unique response lost fields: %#v", profile, decodedResponse.Gift) t.Fatalf("Layer %d unique response lost fields: %#v", profile, decodedResponse.Gift)
} }
actionWire := &bin.Buffer{} 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) 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 { if err != nil {
t.Fatalf("decode Layer %d unique action: %v", profile, err) 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 { 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) t.Fatalf("Layer %d unique action lost fields: %#v", profile, decodedAction)
} }

View file

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

View file

@ -4,23 +4,38 @@ import (
"context" "context"
"github.com/iamxvbaba/td/tg" "github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
) )
// 群通话范围外入口的被动 stub(防点崩)。未列出的 phone.*(conference 族 / // 群通话范围外入口的被动 stub(防点崩)。未列出的 phone.*(conference 族 /
// 通话内消息族 / scheduled / RTMP)走 router fallback:400/500 NOT_IMPLEMENTED + // 通话内消息族 / scheduled / RTMP)走 router fallback:400/500 NOT_IMPLEMENTED +
// 兼容矩阵日志,客户端不断连。 // 兼容矩阵日志,客户端不断连。
func (r *Router) registerPhoneStubs(d *tg.ServerDispatcher) { func (r *Router) registerPhoneStubs(d *tlprofile.Dispatcher) {
registerRPC[
// 入会身份候选:真实实现见 phone_group_call.go(self + admin 的频道身份)。 // 入会身份候选:真实实现见 phone_group_call.go(self + admin 的频道身份)。
d.OnPhoneGetGroupCallJoinAs(r.onPhoneGetGroupCallJoinAs) *tg.PhoneGetGroupCallJoinAsRequest](d, tlprofile.SemanticMethodPhoneGetGroupCallJoinAs, func(ctx context.
// default join-as 偏好持久化仍是 stub(chatFull.groupcall_default_join_as 不回填)。 // default join-as 偏好持久化仍是 stub(chatFull.groupcall_default_join_as 不回填)。
d.OnPhoneSaveDefaultGroupCallJoinAs(func(ctx context.Context, req *tg.PhoneSaveDefaultGroupCallJoinAsRequest) (bool, error) { 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 return true, nil
}) })
registerRPC[
// 录制范围外:客户端只看 record_start_date(恒不下发),打发掉即可。 // 录制范围外:客户端只看 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 return tgEmptyUpdates(int(r.clock.Now().Unix())), nil
}) })
registerRPC[
// RTMP 直播(Live Stream):真实 handler 见 phone_group_call_rtmp.go。 // RTMP 直播(Live Stream):真实 handler 见 phone_group_call_rtmp.go。
d.OnPhoneGetGroupCallStreamChannels(r.onPhoneGetGroupCallStreamChannels) *tg.PhoneGetGroupCallStreamChannelsRequest](d, tlprofile.SemanticMethodPhoneGetGroupCallStreamChannels, func(ctx context.Context, layerRequest *tg.PhoneGetGroupCallStreamChannelsRequest) (any, error) {
d.OnPhoneGetGroupCallStreamRtmpURL(r.onPhoneGetGroupCallStreamRtmpURL) 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" "github.com/iamxvbaba/td/tg"
"go.uber.org/zap" "go.uber.org/zap"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/domain" "telesrv/internal/domain"
) )
// registerPhotos 注册 photos.* RPC handler(头像上传 / 切换 / 查询 / 删除)。 // registerPhotos 注册 photos.* RPC handler(头像上传 / 切换 / 查询 / 删除)。
func (r *Router) registerPhotos(d *tg.ServerDispatcher) { func (r *Router) registerPhotos(d *tlprofile.Dispatcher) {
d.OnPhotosUploadProfilePhoto(r.onPhotosUploadProfilePhoto) registerRPC[*tg.PhotosUploadProfilePhotoRequest](d, tlprofile.SemanticMethodPhotosUploadProfilePhoto, func(ctx context.Context, layerRequest *tg.PhotosUploadProfilePhotoRequest) (any, error) {
d.OnPhotosUpdateProfilePhoto(r.onPhotosUpdateProfilePhoto) return r.onPhotosUploadProfilePhoto(ctx, layerRequest)
d.OnPhotosUploadContactProfilePhoto(r.onPhotosUploadContactProfilePhoto) })
d.OnPhotosGetUserPhotos(r.onPhotosGetUserPhotos) registerRPC[*tg.PhotosUpdateProfilePhotoRequest](d, tlprofile.SemanticMethodPhotosUpdateProfilePhoto, func(ctx context.Context, layerRequest *tg.PhotosUpdateProfilePhotoRequest) (any, error) {
d.OnPhotosDeletePhotos(r.onPhotosDeletePhotos) 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) { 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/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/domain" "telesrv/internal/domain"
) )
func (r *Router) registerPremium(d *tg.ServerDispatcher) { func (r *Router) registerPremium(d *tlprofile.Dispatcher) {
d.OnPremiumGetBoostsStatus(r.onPremiumGetBoostsStatus) registerRPC[*tg.PremiumGetBoostsStatusRequest](d, tlprofile.SemanticMethodPremiumGetBoostsStatus, func(ctx context.Context, layerRequest *tg.PremiumGetBoostsStatusRequest) (any, error) {
d.OnPremiumGetBoostsList(r.onPremiumGetBoostsList) return r.onPremiumGetBoostsStatus(ctx, layerRequest.
d.OnPremiumGetMyBoosts(r.onPremiumGetMyBoosts) Peer)
d.OnPremiumApplyBoost(r.onPremiumApplyBoost) })
d.OnPremiumGetUserBoosts(r.onPremiumGetUserBoosts) 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) { 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/bin"
"github.com/iamxvbaba/td/tg" "github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
appfiles "telesrv/internal/app/files" appfiles "telesrv/internal/app/files"
"telesrv/internal/domain" "telesrv/internal/domain"
) )
@ -72,33 +73,33 @@ var requestVectorPolicies = map[uint32]requestVectorPolicy{
// dispatcher construction fail closed until an explicit conversion policy is // dispatcher construction fail closed until an explicit conversion policy is
// supplied. // supplied.
var layerRPCVectorPolicies = []struct { var layerRPCVectorPolicies = []struct {
fieldID tg.LayerRPCFieldID fieldID tlprofile.FieldID
max int max int
tooLong func() error tooLong func() error
}{ }{
{tg.LayerRPCFieldUsersGetUsersID, 100, inputRequestTooLongErr}, {tlprofile.FieldUsersGetUsersID, 100, inputRequestTooLongErr},
{tg.LayerRPCFieldUsersGetRequirementsToContactID, maxRequirementsToContactUsers, limitInvalidErr}, {tlprofile.FieldUsersGetRequirementsToContactID, maxRequirementsToContactUsers, limitInvalidErr},
{tg.LayerRPCFieldContactsImportContactsContacts, maxContactImportBatch, limitInvalidErr}, {tlprofile.FieldContactsImportContactsContacts, maxContactImportBatch, limitInvalidErr},
{tg.LayerRPCFieldContactsDeleteContactsID, maxContactDeleteBatch, limitInvalidErr}, {tlprofile.FieldContactsDeleteContactsID, maxContactDeleteBatch, limitInvalidErr},
{tg.LayerRPCFieldContactsEditCloseFriendsID, maxCloseFriendsCount, limitInvalidErr}, {tlprofile.FieldContactsEditCloseFriendsID, maxCloseFriendsCount, limitInvalidErr},
{tg.LayerRPCFieldContactsSetBlockedID, maxContactSetBlocked, limitInvalidErr}, {tlprofile.FieldContactsSetBlockedID, maxContactSetBlocked, limitInvalidErr},
{tg.LayerRPCFieldMessagesGetMessagesID, maxGetMessagesIDs, limitInvalidErr}, {tlprofile.FieldMessagesGetMessagesID, maxGetMessagesIDs, limitInvalidErr},
{tg.LayerRPCFieldMessagesGetChatsID, maxGetMessagesIDs, limitInvalidErr}, {tlprofile.FieldMessagesGetChatsID, maxGetMessagesIDs, limitInvalidErr},
{tg.LayerRPCFieldMessagesGetPeerDialogsPeers, maxDialogInputPeers, limitInvalidErr}, {tlprofile.FieldMessagesGetPeerDialogsPeers, maxDialogInputPeers, limitInvalidErr},
{tg.LayerRPCFieldMessagesReadMessageContentsID, maxGetMessagesIDs, limitInvalidErr}, {tlprofile.FieldMessagesReadMessageContentsID, maxGetMessagesIDs, limitInvalidErr},
{tg.LayerRPCFieldMessagesGetCustomEmojiDocumentsDocumentID, maxEmojiDocuments, limitInvalidErr}, {tlprofile.FieldMessagesGetCustomEmojiDocumentsDocumentID, maxEmojiDocuments, limitInvalidErr},
{tg.LayerRPCFieldMessagesDeleteMessagesID, domain.MaxDeleteMessageIDs, limitInvalidErr}, {tlprofile.FieldMessagesDeleteMessagesID, domain.MaxDeleteMessageIDs, limitInvalidErr},
{tg.LayerRPCFieldMessagesCreateChatUsers, 200, limitInvalidErr}, {tlprofile.FieldMessagesCreateChatUsers, 200, limitInvalidErr},
{tg.LayerRPCFieldChannelsGetChannelsID, maxGetMessagesIDs, limitInvalidErr}, {tlprofile.FieldChannelsGetChannelsID, maxGetMessagesIDs, limitInvalidErr},
} }
func registerLayerRPCAdmissionFieldPreflights(d *tg.ServerDispatcher) error { func registerLayerRPCAdmissionFieldPreflights(d *tlprofile.Dispatcher) error {
if d == nil { if d == nil {
return fmt.Errorf("nil layer RPC dispatcher") return fmt.Errorf("nil layer RPC dispatcher")
} }
for _, policy := range layerRPCVectorPolicies { for _, policy := range layerRPCVectorPolicies {
policy := policy 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() length, ok := view.VectorLength()
if !ok { if !ok {
return inputRequestInvalidErr() return inputRequestInvalidErr()
@ -115,11 +116,11 @@ func registerLayerRPCAdmissionFieldPreflights(d *tg.ServerDispatcher) error {
} }
} }
for _, fieldID := range []tg.LayerRPCFieldID{ for _, fieldID := range []tlprofile.FieldID{
tg.LayerRPCFieldUploadSaveFilePartBytes, tlprofile.FieldUploadSaveFilePartBytes,
tg.LayerRPCFieldUploadSaveBigFilePartBytes, 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() length, ok := view.BytesLength()
if !ok { if !ok {
return inputRequestInvalidErr() return inputRequestInvalidErr()
@ -133,9 +134,9 @@ func registerLayerRPCAdmissionFieldPreflights(d *tg.ServerDispatcher) error {
} }
} }
if err := d.OnLayerRPCAdmissionFieldPreflight( if err := d.OnFieldPreflight(
tg.LayerRPCFieldUploadSaveBigFilePartFileTotalParts, tlprofile.FieldUploadSaveBigFilePartFileTotalParts,
func(view tg.LayerRPCAdmissionFieldView) error { func(view tlprofile.FieldView) error {
totalParts, ok := view.Int32() totalParts, ok := view.Int32()
if !ok { if !ok {
return inputRequestInvalidErr() return inputRequestInvalidErr()

View file

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

View file

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

View file

@ -16,6 +16,17 @@ import (
"testing" "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 { type rpcChannelFixture struct {
t *testing.T t *testing.T
ctx context.Context ctx context.Context
@ -111,8 +122,6 @@ func searchMessagesPayload(t *testing.T, enc bin.Encoder) ([]tg.MessageClass, []
return result.Messages, result.Chats, result.Users return result.Messages, result.Chats, result.Users
case *tg.MessagesChannelMessages: case *tg.MessagesChannelMessages:
return result.Messages, result.Chats, result.Users return result.Messages, result.Chats, result.Users
case *tg.MessagesMessagesBox:
return searchMessagesPayload(t, result.Messages)
default: default:
t.Fatalf("search result type = %T, want messages/messagesSlice", enc) t.Fatalf("search result type = %T, want messages/messagesSlice", enc)
return nil, nil, nil return nil, nil, nil

View file

@ -6,6 +6,7 @@ import (
"github.com/iamxvbaba/td/tg" "github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/domain" "telesrv/internal/domain"
) )
@ -15,15 +16,31 @@ const (
maxStatsGraphTokenLength = 128 maxStatsGraphTokenLength = 128
) )
func (r *Router) registerStats(d *tg.ServerDispatcher) { func (r *Router) registerStats(d *tlprofile.Dispatcher) {
d.OnStatsGetBroadcastStats(r.onStatsGetBroadcastStats) registerRPC[*tg.StatsGetBroadcastStatsRequest](d, tlprofile.SemanticMethodStatsGetBroadcastStats, func(ctx context.Context, layerRequest *tg.StatsGetBroadcastStatsRequest) (any, error) {
d.OnStatsGetMegagroupStats(r.onStatsGetMegagroupStats) return r.onStatsGetBroadcastStats(ctx, layerRequest)
d.OnStatsGetMessageStats(r.onStatsGetMessageStats) })
d.OnStatsGetMessagePublicForwards(r.onStatsGetMessagePublicForwards) registerRPC[*tg.StatsGetMegagroupStatsRequest](d, tlprofile.SemanticMethodStatsGetMegagroupStats, func(ctx context.Context, layerRequest *tg.StatsGetMegagroupStatsRequest) (any, error) {
d.OnStatsLoadAsyncGraph(r.onStatsLoadAsyncGraph) return r.onStatsGetMegagroupStats(ctx, layerRequest)
d.OnStatsGetStoryStats(r.onStatsGetStoryStats) })
d.OnStatsGetStoryPublicForwards(r.onStatsGetStoryPublicForwards) registerRPC[*tg.StatsGetMessageStatsRequest](d, tlprofile.SemanticMethodStatsGetMessageStats, func(ctx context.Context, layerRequest *tg.StatsGetMessageStatsRequest) (any, error) {
d.OnStatsGetPollStats(r.onStatsGetPollStats) 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) { 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 { if err != nil {
t.Fatalf("alice get history: %v", err) t.Fatalf("alice get history: %v", err)
} }
box, ok := enc.(*tg.MessagesMessagesBox) messages, ok := enc.(*tg.MessagesMessages)
if !ok { if !ok {
t.Fatalf("history response = %T, want *tg.MessagesMessagesBox", enc) t.Fatalf("history response = %T, want *tg.MessagesMessages", enc)
}
messages, ok := box.Messages.(*tg.MessagesMessages)
if !ok {
t.Fatalf("history payload = %T, want *tg.MessagesMessages", box.Messages)
} }
if len(messages.Messages) != 1 { if len(messages.Messages) != 1 {
t.Fatalf("history messages = %d, want 1", len(messages.Messages)) 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/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/domain" "telesrv/internal/domain"
) )
func (r *Router) registerStickers(d *tg.ServerDispatcher) { func (r *Router) registerStickers(d *tlprofile.Dispatcher) {
d.OnStickersCreateStickerSet(r.onStickersCreateStickerSet) registerRPC[*tg.StickersCreateStickerSetRequest](d, tlprofile.SemanticMethodStickersCreateStickerSet, func(ctx context.Context, layerRequest *tg.StickersCreateStickerSetRequest) (any, error) {
d.OnStickersCheckShortName(r.onStickersCheckShortName) return r.onStickersCreateStickerSet(ctx, layerRequest)
d.OnStickersSuggestShortName(r.onStickersSuggestShortName) })
d.OnStickersAddStickerToSet(r.onStickersAddStickerToSet) registerRPC[*tg.StickersCheckShortNameRequest](d, tlprofile.SemanticMethodStickersCheckShortName, func(ctx context.Context, layerRequest *tg.StickersCheckShortNameRequest) (any, error) {
d.OnStickersRemoveStickerFromSet(r.onStickersRemoveStickerFromSet) return r.onStickersCheckShortName(ctx, layerRequest.
d.OnStickersChangeStickerPosition(r.onStickersChangeStickerPosition) ShortName)
d.OnStickersRenameStickerSet(r.onStickersRenameStickerSet) })
d.OnStickersDeleteStickerSet(r.onStickersDeleteStickerSet) 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) { 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/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/compat/tdesktop" "telesrv/internal/compat/tdesktop"
"telesrv/internal/domain" "telesrv/internal/domain"
) )
@ -27,40 +28,110 @@ const (
) )
// registerStories 注册 TDesktop/Android 已发现的 stories.* RPC。 // registerStories 注册 TDesktop/Android 已发现的 stories.* RPC。
func (r *Router) registerStories(d *tg.ServerDispatcher) { func (r *Router) registerStories(d *tlprofile.Dispatcher) {
d.OnStoriesGetAllStories(r.onStoriesGetAllStories) registerRPC[*tg.StoriesGetAllStoriesRequest](d, tlprofile.SemanticMethodStoriesGetAllStories, func(ctx context.Context, layerRequest *tg.StoriesGetAllStoriesRequest) (any, error) {
d.OnStoriesGetPeerStories(r.onStoriesGetPeerStories) return r.onStoriesGetAllStories(ctx, layerRequest)
d.OnStoriesGetStoriesByID(r.onStoriesGetStoriesByID) })
d.OnStoriesGetStoriesArchive(r.onStoriesGetStoriesArchive) registerRPC[*tg.StoriesGetPeerStoriesRequest](d, tlprofile.SemanticMethodStoriesGetPeerStories, func(ctx context.Context, layerRequest *tg.StoriesGetPeerStoriesRequest) (any, error) {
d.OnStoriesGetPinnedStories(r.onStoriesGetPinnedStories) return r.onStoriesGetPeerStories(ctx, layerRequest.
d.OnStoriesExportStoryLink(r.onStoriesExportStoryLink) Peer)
d.OnStoriesReport(r.onStoriesReport) })
d.OnStoriesActivateStealthMode(r.onStoriesActivateStealthMode) registerRPC[*tg.StoriesGetStoriesByIDRequest](d, tlprofile.SemanticMethodStoriesGetStoriesByID, func(ctx context.Context, layerRequest *tg.StoriesGetStoriesByIDRequest) (any, error) {
d.OnStoriesSearchPosts(r.onStoriesSearchPosts) return r.onStoriesGetStoriesByID(ctx, layerRequest)
d.OnStoriesSendStory(r.onStoriesSendStory) })
d.OnStoriesEditStory(r.onStoriesEditStory) registerRPC[*tg.StoriesGetStoriesArchiveRequest](d, tlprofile.SemanticMethodStoriesGetStoriesArchive, func(ctx context.Context, layerRequest *tg.StoriesGetStoriesArchiveRequest) (any, error) {
d.OnStoriesDeleteStories(r.onStoriesDeleteStories) return r.onStoriesGetStoriesArchive(ctx, layerRequest)
d.OnStoriesTogglePinned(r.onStoriesTogglePinned) })
d.OnStoriesTogglePinnedToTop(r.onStoriesTogglePinnedToTop) registerRPC[*tg.StoriesGetPinnedStoriesRequest](d, tlprofile.SemanticMethodStoriesGetPinnedStories, func(ctx context.Context, layerRequest *tg.StoriesGetPinnedStoriesRequest) (any, error) {
d.OnStoriesToggleAllStoriesHidden(r.onStoriesToggleAllStoriesHidden) return r.onStoriesGetPinnedStories(ctx, layerRequest)
d.OnStoriesCreateAlbum(r.onStoriesCreateAlbum) })
d.OnStoriesUpdateAlbum(r.onStoriesUpdateAlbum) registerRPC[*tg.StoriesExportStoryLinkRequest](d, tlprofile.SemanticMethodStoriesExportStoryLink, func(ctx context.Context, layerRequest *tg.StoriesExportStoryLinkRequest) (any, error) {
d.OnStoriesReorderAlbums(r.onStoriesReorderAlbums) return r.onStoriesExportStoryLink(ctx, layerRequest)
d.OnStoriesDeleteAlbum(r.onStoriesDeleteAlbum) })
d.OnStoriesGetAlbums(r.onStoriesGetAlbums) registerRPC[*tg.StoriesReportRequest](d, tlprofile.SemanticMethodStoriesReport, func(ctx context.Context, layerRequest *tg.StoriesReportRequest) (any, error) {
d.OnStoriesGetAlbumStories(r.onStoriesGetAlbumStories) return r.onStoriesReport(ctx, layerRequest)
d.OnStoriesGetAllReadPeerStories(r.onStoriesGetAllReadPeerStories) })
d.OnStoriesGetPeerMaxIDs(r.onStoriesGetPeerMaxIDs) registerRPC[*tg.StoriesActivateStealthModeRequest](d, tlprofile.SemanticMethodStoriesActivateStealthMode, func(ctx context.Context, layerRequest *tg.StoriesActivateStealthModeRequest) (any, error) {
d.OnStoriesReadStories(r.onStoriesReadStories) return r.onStoriesActivateStealthMode(ctx, layerRequest)
d.OnStoriesIncrementStoryViews(r.onStoriesIncrementStoryViews) })
d.OnStoriesGetStoriesViews(r.onStoriesGetStoriesViews) registerRPC[*tg.StoriesSearchPostsRequest](d, tlprofile.SemanticMethodStoriesSearchPosts, func(ctx context.Context, layerRequest *tg.StoriesSearchPostsRequest) (any, error) {
d.OnStoriesGetStoryViewsList(r.onStoriesGetStoryViewsList) return r.onStoriesSearchPosts(ctx, layerRequest)
d.OnStoriesGetStoryReactionsList(r.onStoriesGetStoryReactionsList) })
d.OnStoriesTogglePeerStoriesHidden(r.onStoriesTogglePeerStoriesHidden) registerRPC[*tg.StoriesSendStoryRequest](d, tlprofile.SemanticMethodStoriesSendStory, func(ctx context.Context, layerRequest *tg.StoriesSendStoryRequest) (any, error) {
d.OnStoriesCanSendStory(r.onStoriesCanSendStory) return r.onStoriesSendStory(ctx, layerRequest)
d.OnStoriesGetChatsToSend(r.onStoriesGetChatsToSend) })
d.OnStoriesSendReaction(r.onStoriesSendReaction) registerRPC[*tg.StoriesEditStoryRequest](d, tlprofile.SemanticMethodStoriesEditStory, func(ctx context.Context, layerRequest *tg.StoriesEditStoryRequest) (any, error) {
d.OnStoriesStartLive(r.onStoriesStartLive) 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) { 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 { if err != nil {
t.Fatalf("dispatch: %v", err) t.Fatalf("dispatch: %v", err)
} }
if box, ok := out.(*tg.MessagesMessagesBox); ok {
return box.Messages
}
return out return out
} }

View file

@ -5,21 +5,28 @@ import (
"github.com/iamxvbaba/td/tg" "github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/domain" "telesrv/internal/domain"
) )
// registerUpdates 注册 updates.* RPC handler。 // registerUpdates 注册 updates.* RPC handler。
func (r *Router) registerUpdates(d *tg.ServerDispatcher) { func (r *Router) registerUpdates(d *tlprofile.Dispatcher) {
d.OnUpdatesGetState(r.onUpdatesGetState) registerRPC[*tg.UpdatesGetStateRequest](d, tlprofile.SemanticMethodUpdatesGetState, func(ctx context.Context, layerRequest *tg.UpdatesGetStateRequest) (
d.OnUpdatesGetDifference(r.onUpdatesGetDifference)
// 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) { func (r *Router) onUpdatesGetState(ctx context.Context) (*tg.UpdatesState, error) {
userID, _, err := r.currentUserID(ctx) userID, _, err := r.currentUserID(ctx)
if err != nil { if err != nil {

View file

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

View file

@ -13,6 +13,7 @@ import (
"github.com/iamxvbaba/td/tg" "github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/domain" "telesrv/internal/domain"
) )
@ -39,8 +40,10 @@ type inlineWebFileFetcher func(ctx context.Context, document domain.BotInlineWeb
var fetchInlineWebFile inlineWebFileFetcher = defaultFetchInlineWebFile var fetchInlineWebFile inlineWebFileFetcher = defaultFetchInlineWebFile
func (r *Router) registerUploadWebFile(d *tg.ServerDispatcher) { func (r *Router) registerUploadWebFile(d *tlprofile.Dispatcher) {
d.OnUploadGetWebFile(r.onUploadGetWebFile) 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) { 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/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/app/users" "telesrv/internal/app/users"
"telesrv/internal/compat/tdesktop" "telesrv/internal/compat/tdesktop"
"telesrv/internal/domain" "telesrv/internal/domain"
@ -15,15 +16,30 @@ const maxSavedMusicLimit = 100
const maxRequirementsToContactUsers = 100 const maxRequirementsToContactUsers = 100
// registerUsers 注册 users.* RPC handler。 // registerUsers 注册 users.* RPC handler。
func (r *Router) registerUsers(d *tg.ServerDispatcher) { func (r *Router) registerUsers(d *tlprofile.Dispatcher) {
d.OnUsersGetUsers(r.onUsersGetUsers) registerRPC[*tg.UsersGetUsersRequest](d, tlprofile.SemanticMethodUsersGetUsers, func(ctx context.Context, layerRequest *tg.UsersGetUsersRequest) (any, error) {
d.OnUsersGetFullUser(r.onUsersGetFullUser) return r.onUsersGetUsers(ctx, layerRequest.
d.OnUsersGetRequirementsToContact(r.onUsersGetRequirementsToContact) ID)
d.OnUsersGetSavedMusic(r.onUsersGetSavedMusic) })
d.OnUsersGetSavedMusicByID(r.onUsersGetSavedMusicByID) 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) { func (r *Router) onUsersGetUsers(ctx context.Context, ids []tg.InputUserClass) ([]tg.UserClass, error) {
currentUserID, authorized, err := r.currentUserID(ctx) currentUserID, authorized, err := r.currentUserID(ctx)
if err != nil { if err != nil {