move gotd fork into repo. (#111)

- update to latest telegram layer
- remove some references to fields in tg.Entities that don't exist in
the schema
- originally added here:
https://github.com/beeper/td/commit/820929062a2ba0104397bc01235ab58a9cff780e
  - referenced here
-
https://github.com/mautrix/telegramgo/commit/124f0967ed195b5a380c9bd02e170ada9710dde3
-
https://github.com/mautrix/telegramgo/commit/4205047aab2e0639217148b5d125bfaab668bd8e
This commit is contained in:
Adam Van Ymeren
2025-06-27 20:03:37 -07:00
committed by GitHub
parent 0952df0244
commit 7a04f298d2
19264 changed files with 1539697 additions and 84 deletions
+198
View File
@@ -0,0 +1,198 @@
package tdjson
import (
"encoding/base64"
"encoding/hex"
"github.com/go-faster/errors"
"github.com/go-faster/jx"
"go.mau.fi/mautrix-telegram/pkg/gotd/bin"
)
// Decoder is a simple wrapper around jx.Decoder to conform TL type system.
type Decoder struct {
*jx.Decoder
}
// Obj calls f for every key in object, using byte slice as key.
//
// The key value is valid only until f is not returned.
func (b Decoder) Obj(cb func(d Decoder, key []byte) error) error {
return b.Decoder.ObjBytes(func(d *jx.Decoder, key []byte) error {
return cb(Decoder{Decoder: d}, key)
})
}
// Arr decodes array and invokes callback on each array element.
func (b Decoder) Arr(cb func(d Decoder) error) error {
return b.Decoder.Arr(func(d *jx.Decoder) error {
return cb(Decoder{Decoder: d})
})
}
// ID deserializes given typeID.
func (b Decoder) ID() (string, error) {
return b.Decoder.Str()
}
// FindTypeID tries to find @type field or returns error.
func (b Decoder) FindTypeID() (string, error) {
var (
found bool
typ string
)
if err := b.Decoder.Capture(func(d *jx.Decoder) error {
return d.ObjBytes(func(d *jx.Decoder, key []byte) error {
if found || string(key) != TypeField {
return d.Skip()
}
t, err := d.Str()
if err != nil {
return err
}
typ = t
found = true
return nil
})
}); err != nil {
return "", err
}
if !found {
return "", ErrTypeIDNotFound
}
return typ, nil
}
// ConsumeID deserializes given typeID.
func (b Decoder) ConsumeID(id string) error {
v, err := b.Decoder.Str()
if err != nil {
return err
}
if v != id {
return NewUnexpectedID(id)
}
return nil
}
// Int deserializes signed 32-bit integer.
func (b Decoder) Int() (int, error) {
return b.Decoder.Int()
}
// Bool deserializes boolean.
func (b Decoder) Bool() (bool, error) {
return b.Decoder.Bool()
}
// Uint16 deserializes unsigned 16-bit integer.
func (b Decoder) Uint16() (uint16, error) {
v, err := b.Decoder.UInt32()
if err != nil {
return 0, err
}
return uint16(v), nil
}
// Int32 deserializes signed 32-bit integer.
func (b Decoder) Int32() (int32, error) {
return b.Decoder.Int32()
}
// Uint32 deserializes unsigned 32-bit integer.
func (b Decoder) Uint32() (uint32, error) {
return b.Decoder.UInt32()
}
// Int53 deserializes int53.
func (b Decoder) Int53() (int64, error) {
return b.Decoder.Int64()
}
// Long deserializes int64.
func (b Decoder) Long() (int64, error) {
n, err := b.Decoder.Num()
if err != nil {
return 0, err
}
return n.Int64()
}
// Uint64 deserializes unsigned 64-bit integer.
func (b Decoder) Uint64() (uint64, error) {
return b.Decoder.UInt64()
}
// Double deserializes 64-bit floating point.
func (b Decoder) Double() (float64, error) {
return b.Decoder.Float64()
}
// Int128 deserializes 128-bit signed integer.
func (b Decoder) Int128() (bin.Int128, error) {
// FIXME(tdakkota): neither TDLib API not Telegram API has no Int128/Int256 fields
// so this encoding may incorrect.
v, err := b.Decoder.Str()
if err != nil {
return bin.Int128{}, err
}
var result bin.Int128
if l := hex.DecodedLen(len(v)); l != len(result) {
return bin.Int128{}, errors.Wrapf(err, "invalid length %d", l)
}
if _, err := hex.Decode(result[:], []byte(v)); err != nil {
return bin.Int128{}, err
}
return result, nil
}
// Int256 deserializes 256-bit signed integer.
func (b Decoder) Int256() (bin.Int256, error) {
// FIXME(tdakkota): neither TDLib API not Telegram API has no Int128/Int256 fields
// so this encoding may incorrect.
v, err := b.Decoder.StrBytes()
if err != nil {
return bin.Int256{}, err
}
var result bin.Int256
if l := hex.DecodedLen(len(v)); l != len(result) {
return bin.Int256{}, errors.Wrapf(err, "invalid length %d", l)
}
if _, err := hex.Decode(result[:], v); err != nil {
return bin.Int256{}, err
}
return result, nil
}
// String deserializes bare string.
func (b Decoder) String() (string, error) {
return b.Decoder.Str()
}
// Bytes deserializes bare byte string.
func (b Decoder) Bytes() ([]byte, error) {
// See https://core.telegram.org/tdlib/docs/td__json__client_8h.html
//
// ... fields of bytes type are base64 encoded and then stored as String ...
enc := base64.RawStdEncoding
v, err := b.Decoder.StrBytes()
if err != nil {
return nil, err
}
result := make([]byte, enc.DecodedLen(len(v)))
if _, err := enc.Decode(result, v); err != nil {
return nil, err
}
return result, nil
}
+109
View File
@@ -0,0 +1,109 @@
package tdjson
import (
"encoding/base64"
"encoding/hex"
"strconv"
"github.com/go-faster/jx"
"go.mau.fi/mautrix-telegram/pkg/gotd/bin"
)
// Encoder is a simple wrapper around jx.Encoder to conform TL type system.
type Encoder struct {
*jx.Writer
}
// PutID serializes given typeID.
func (b Encoder) PutID(typeID string) {
b.Writer.FieldStart(TypeField)
b.Writer.Str(typeID)
}
// PutInt serializes v as signed 32-bit integer.
func (b Encoder) PutInt(v int) {
b.Writer.Int(v)
}
// PutBool serializes boolean.
func (b Encoder) PutBool(v bool) {
b.Writer.Bool(v)
}
// PutUint16 serializes unsigned 16-bit integer.
func (b Encoder) PutUint16(v uint16) {
b.Writer.UInt32(uint32(v))
}
// PutInt32 serializes signed 32-bit integer.
func (b Encoder) PutInt32(v int32) {
b.Writer.Int32(v)
}
// PutUint32 serializes unsigned 32-bit integer.
func (b Encoder) PutUint32(v uint32) {
b.Writer.UInt32(v)
}
// PutInt53 serializes v as int53.
func (b Encoder) PutInt53(v int64) {
b.Writer.Int64(v)
}
// PutLong serializes v as int64.
func (b Encoder) PutLong(v int64) {
var buf [32]byte
r := append(buf[:0], '"')
r = strconv.AppendInt(r, v, 10)
r = append(r, '"')
b.Writer.Raw(r)
}
// PutUint64 serializes v as unsigned 64-bit integer.
func (b Encoder) PutUint64(v uint64) {
// FIXME(tdakkota): TDLib API has no uint64 fields
// so this encoding may incorrect.
b.Writer.UInt64(v)
}
// PutDouble serializes v as 64-bit floating point.
func (b Encoder) PutDouble(v float64) {
b.Writer.Float64(v)
}
// PutInt128 serializes v as 128-bit signed integer.
func (b Encoder) PutInt128(v bin.Int128) {
// FIXME(tdakkota): neither TDLib API nor Telegram API has no Int128/Int256 fields
// so this encoding may incorrect.
b.Writer.Str(hex.EncodeToString(v[:]))
}
// PutInt256 serializes v as 256-bit signed integer.
func (b Encoder) PutInt256(v bin.Int256) {
// FIXME(tdakkota): neither TDLib API not Telegram API has no Int128/Int256 fields
// so this encoding may incorrect.
b.Writer.Str(hex.EncodeToString(v[:]))
}
// PutString serializes bare string.
func (b Encoder) PutString(s string) {
b.Writer.Str(s)
}
// PutBytes serializes bare byte string.
func (b Encoder) PutBytes(v []byte) {
// See https://core.telegram.org/tdlib/docs/td__json__client_8h.html
//
// ... fields of bytes type are base64 encoded and then stored as String ...
b.Writer.Str(base64.RawURLEncoding.EncodeToString(v))
}
// StripComma deletes last comma, if any.
//
// Useful for code generation to avoid last field/element tracking.
func (b Encoder) StripComma() {
if l := len(b.Buf); l > 0 && b.Buf[l-1] == ',' {
b.Buf = b.Buf[:l-1]
}
}
+122
View File
@@ -0,0 +1,122 @@
package tdjson_test
import (
"encoding/json"
"math"
"strconv"
"testing"
"github.com/go-faster/jx"
"github.com/stretchr/testify/require"
"go.mau.fi/mautrix-telegram/pkg/gotd/tdapi"
"go.mau.fi/mautrix-telegram/pkg/gotd/tdjson"
"go.mau.fi/mautrix-telegram/pkg/gotd/tdp"
)
func TestEncodeDecode(t *testing.T) {
type obj interface {
tdjson.TDLibDecoder
tdjson.TDLibEncoder
TypeInfo() tdp.Type
}
test := func(create func() obj) func(t *testing.T) {
return func(t *testing.T) {
a := require.New(t)
req := create()
enc := tdjson.Encoder{
Writer: &jx.Writer{},
}
a.NoError(req.EncodeTDLibJSON(enc))
a.True(json.Valid(enc.Buf))
dec := tdjson.Decoder{
Decoder: jx.DecodeBytes(enc.Buf),
}
a.NoError(req.DecodeTDLibJSON(dec))
}
}
types := []obj{
&tdapi.SetTdlibParametersRequest{
UseTestDC: true,
DatabaseDirectory: "database",
FilesDirectory: "files",
UseFileDatabase: true,
UseChatInfoDatabase: true,
UseMessageDatabase: true,
UseSecretChats: true,
APIID: 10,
APIHash: "russcox",
SystemLanguageCode: "ru",
DeviceModel: "gotd",
SystemVersion: "10",
ApplicationVersion: "10",
},
&tdapi.ProfilePhoto{
ID: 1,
},
&tdapi.ReplyMarkupInlineKeyboard{
Rows: [][]tdapi.InlineKeyboardButton{
{
{
Text: "text",
Type: &tdapi.InlineKeyboardButtonTypeCallback{
Data: []byte("a"),
},
},
{
Text: "text2",
Type: &tdapi.InlineKeyboardButtonTypeCallback{
Data: []byte("b"),
},
},
},
{
{
Text: "text3",
Type: &tdapi.InlineKeyboardButtonTypeCallback{
Data: []byte("c"),
},
},
},
},
},
// Test empty array.
&tdapi.ReplyMarkupInlineKeyboard{
Rows: [][]tdapi.InlineKeyboardButton{},
},
}
for _, typ := range types {
t.Run(typ.TypeInfo().Name, test(func() obj {
return typ
}))
}
}
func TestEncoder_PutLong(t *testing.T) {
for _, tt := range []int64{
-1,
0,
1,
10,
math.MaxInt64,
math.MinInt64,
} {
t.Run(strconv.FormatInt(tt, 10), func(t *testing.T) {
a := require.New(t)
e := tdjson.Encoder{Writer: &jx.Writer{}}
e.PutLong(tt)
data := e.Buf
a.Equal(strconv.Quote(strconv.FormatInt(tt, 10)), string(data))
d := tdjson.Decoder{Decoder: jx.DecodeBytes(data)}
v, err := d.Long()
a.NoError(err)
a.Equal(tt, v)
})
}
}
+24
View File
@@ -0,0 +1,24 @@
package tdjson
import (
"fmt"
"github.com/go-faster/errors"
)
// UnexpectedIDError means that unknown or unexpected type id was decoded.
type UnexpectedIDError struct {
ID string
}
func (e *UnexpectedIDError) Error() string {
return fmt.Sprintf("unexpected id %s", e.ID)
}
// NewUnexpectedID return new UnexpectedIDError.
func NewUnexpectedID(id string) error {
return &UnexpectedIDError{ID: id}
}
// ErrTypeIDNotFound means that @type field is expected, but not found.
var ErrTypeIDNotFound = errors.New("@type field is expected, but not found")
+11
View File
@@ -0,0 +1,11 @@
package tdjson
// TDLibEncoder represents TDLib JSON API encoder.
type TDLibEncoder interface {
EncodeTDLibJSON(Encoder) error
}
// TDLibDecoder represents TDLib JSON API decoder.
type TDLibDecoder interface {
DecodeTDLibJSON(Decoder) error
}
+5
View File
@@ -0,0 +1,5 @@
// Package tdjson contains some helpers to use jx with TL objects.
package tdjson
// TypeField is a type field name.
const TypeField = "@type"