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:
@@ -0,0 +1,401 @@
|
||||
package tljson
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-faster/errors"
|
||||
"github.com/go-faster/jx"
|
||||
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
||||
)
|
||||
|
||||
// EmojiSound represents emoji sound file location.
|
||||
//
|
||||
// See https://core.telegram.org/api/animated-emojis#emojis-with-sounds.
|
||||
type EmojiSound struct {
|
||||
ID int64 `json:"id"`
|
||||
AccessHash int64 `json:"access_hash"`
|
||||
FileReference []byte `json:"file_reference_base64"`
|
||||
}
|
||||
|
||||
// DecodeJSON decodes EmojiSound.
|
||||
func (e *EmojiSound) DecodeJSON(d *jx.Decoder) error {
|
||||
return d.ObjBytes(func(d *jx.Decoder, key []byte) error {
|
||||
switch string(key) {
|
||||
case "id":
|
||||
v, err := d.Str()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode id")
|
||||
}
|
||||
|
||||
id, err := strconv.ParseInt(v, 10, 64)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "parse id")
|
||||
}
|
||||
e.ID = id
|
||||
case "access_hash":
|
||||
v, err := d.Str()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode access_hash")
|
||||
}
|
||||
|
||||
accessHash, err := strconv.ParseInt(v, 10, 64)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "parse access_hash")
|
||||
}
|
||||
e.AccessHash = accessHash
|
||||
case "file_reference_base64":
|
||||
encoded, err := d.StrBytes()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode file_reference_base64")
|
||||
}
|
||||
encoding := base64.RawURLEncoding
|
||||
|
||||
e.FileReference = make([]byte, encoding.DecodedLen(len(encoded)))
|
||||
n, err := encoding.Decode(e.FileReference, encoded)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "un-base64 file_reference_base64")
|
||||
}
|
||||
e.FileReference = e.FileReference[:n]
|
||||
default:
|
||||
return d.Skip()
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// EmojiSendDiceSuccess represents the winning dice value and the final frame of the animated sticker.
|
||||
//
|
||||
// See https://core.telegram.org/api/dice.
|
||||
type EmojiSendDiceSuccess struct {
|
||||
Value int `json:"value"`
|
||||
FrameStart int `json:"frame_start"`
|
||||
}
|
||||
|
||||
// DecodeJSON decodes EmojiSendDiceSuccess.
|
||||
func (e *EmojiSendDiceSuccess) DecodeJSON(d *jx.Decoder) error {
|
||||
return d.ObjBytes(func(d *jx.Decoder, key []byte) error {
|
||||
switch string(key) {
|
||||
case "value":
|
||||
v, err := d.Int()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode value")
|
||||
}
|
||||
e.Value = v
|
||||
case "frame_start":
|
||||
v, err := d.Int()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode frame_start")
|
||||
}
|
||||
e.FrameStart = v
|
||||
default:
|
||||
return d.Skip()
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// RoundVideoEncoding represents a set of recommended codec parameters for round videos.
|
||||
type RoundVideoEncoding struct {
|
||||
Diameter int `json:"diameter"`
|
||||
VideoBitrate int `json:"video_bitrate"`
|
||||
AudioBitrate int `json:"audio_bitrate"`
|
||||
MaxSize int `json:"max_size"`
|
||||
}
|
||||
|
||||
// DecodeJSON decodes RoundVideoEncoding.
|
||||
func (e *RoundVideoEncoding) DecodeJSON(d *jx.Decoder) error {
|
||||
return d.ObjBytes(func(d *jx.Decoder, key []byte) error {
|
||||
switch string(key) {
|
||||
case "diameter":
|
||||
v, err := d.Int()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode diameter")
|
||||
}
|
||||
e.Diameter = v
|
||||
case "video_bitrate":
|
||||
v, err := d.Int()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode video_bitrate")
|
||||
}
|
||||
e.VideoBitrate = v
|
||||
case "audio_bitrate":
|
||||
v, err := d.Int()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode audio_bitrate")
|
||||
}
|
||||
e.AudioBitrate = v
|
||||
case "max_size":
|
||||
v, err := d.Int()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode max_size")
|
||||
}
|
||||
e.MaxSize = v
|
||||
default:
|
||||
return d.Skip()
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// AppConfig represents app config structure.
|
||||
//
|
||||
// See https://core.telegram.org/api/config#client-configuration.
|
||||
type AppConfig struct {
|
||||
Test int `json:"test"`
|
||||
// Animated emojis and animated dice should be scaled by this factor before being shown to the user.
|
||||
EmojiesAnimatedZoom float64 `json:"emojies_animated_zoom"`
|
||||
// A list of supported animated dice stickers.
|
||||
EmojiesSendDice []string `json:"emojies_send_dice"`
|
||||
// For animated dice emojis other than the basic 🎲, indicates the winning dice value
|
||||
// and the final frame of the animated sticker, at which to show the fireworks.
|
||||
EmojiesSendDiceSuccess map[string]EmojiSendDiceSuccess `json:"emojies_send_dice_success"`
|
||||
// A map of soundbites to be played when the user clicks on the specified animated emoji.
|
||||
//
|
||||
// The file reference field should be base64-decoded before downloading the file.
|
||||
EmojiesSounds map[string]EmojiSound `json:"emojies_sounds"`
|
||||
// Specifies the name of the service providing GIF search through gif_search_username.
|
||||
GIFSearchBranding string `json:"gif_search_branding"`
|
||||
// Specifies a list of emojies that should be suggested as search term in a bar above the GIF search box.
|
||||
GIFSearchEmojies []string `json:"gif_search_emojies"`
|
||||
// Specifies that the app should not display local sticker suggestions
|
||||
// for emojis at all and just use the result of messages.getStickers.
|
||||
StickersEmojiSuggestOnlyAPI bool `json:"stickers_emoji_suggest_only_api"`
|
||||
// Specifies the validity period of the local cache of messages.getStickers,
|
||||
// also relevant when generating the pagination hash when invoking the method.
|
||||
StickersEmojiCacheTime int `json:"stickers_emoji_cache_time"`
|
||||
GroupCallVideoParticipantsMax int `json:"groupcall_video_participants_max"`
|
||||
YoutubePIP string `json:"youtube_pip"`
|
||||
// Whether the Settings->Devices menu should show an option to scan a QR login code.
|
||||
QRLoginCamera bool `json:"qr_login_camera"`
|
||||
// Whether the login screen should show a QR code login option, possibly
|
||||
// as default login method ("disabled", "primary" or "secondary")
|
||||
QRLoginCode string `json:"qr_login_code"`
|
||||
// Whether clients should show an option for managing dialog filters AKA folders.
|
||||
DialogFiltersEnabled bool `json:"dialog_filters_enabled"`
|
||||
// Whether clients should actively show a tooltip, inviting the user to configure dialog filters AKA folders.
|
||||
//
|
||||
// Typically, this happens when the chat list is long enough to start getting cluttered.
|
||||
DialogFiltersTooltip bool `json:"dialog_filters_tooltip"`
|
||||
// Whether clients can invoke account.setGlobalPrivacySettings
|
||||
// with globalPrivacySettings.archive_and_mute_new_noncontact_peers = boolTrue,
|
||||
// to automatically archive and mute new incoming chats from non-contacts.
|
||||
AutoArchiveSettingAvailable bool `json:"autoarchive_setting_available"`
|
||||
// Contains a list of suggestions that should be actively shown as a tooltip to the user.
|
||||
PendingSuggestions []string `json:"pending_suggestions"`
|
||||
// Autologin token.
|
||||
//
|
||||
// See https://core.telegram.org/api/url-authorization#link-url-authorization.
|
||||
AutologinToken string `json:"autologin_token"`
|
||||
// A list of Telegram domains that support automatic login with no user confirmation.
|
||||
//
|
||||
// See https://core.telegram.org/api/url-authorization#link-url-authorization.
|
||||
AutologinDomains []string `json:"autologin_domains"`
|
||||
// A list of domains that support automatic login with manual user confirmation.
|
||||
//
|
||||
// See https://core.telegram.org/api/url-authorization#link-url-authorization.
|
||||
URLAuthDomains []string `json:"url_auth_domains"`
|
||||
// Contains a set of recommended codec parameters for round videos.
|
||||
RoundVideoEncoding RoundVideoEncoding `json:"round_video_encoding"`
|
||||
// To protect user privacy, read receipts are only stored for
|
||||
// chat_read_mark_expire_period seconds after the message was sent.
|
||||
ChatReadMarkExpirePeriod int `json:"chat_read_mark_expire_period"`
|
||||
// Per-user read receipts, fetchable using messages.getMessageReadParticipants
|
||||
// will be available in groups with less than chat_read_mark_size_threshold participants.
|
||||
ChatReadMarkSizeThreshold int `json:"chat_read_mark_size_threshold"`
|
||||
|
||||
// Unparsed is map of unknown unparsed fields.
|
||||
Unparsed map[string]tg.JSONValueClass
|
||||
}
|
||||
|
||||
func decodeStringArray(d *jx.Decoder, array *[]string) error {
|
||||
return d.Arr(func(d *jx.Decoder) error {
|
||||
v, err := d.Str()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "decode %d", len(*array))
|
||||
}
|
||||
*array = append(*array, v)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// DecodeJSON decodes AppConfig.
|
||||
func (e *AppConfig) DecodeJSON(d *jx.Decoder) error {
|
||||
// TODO(tdakkota): use schema-based parser
|
||||
return d.ObjBytes(func(d *jx.Decoder, key []byte) error {
|
||||
switch string(key) {
|
||||
case "test":
|
||||
v, err := d.Int()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode test")
|
||||
}
|
||||
e.Test = v
|
||||
case "emojies_animated_zoom":
|
||||
v, err := d.Float64()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode emojies_animated_zoom")
|
||||
}
|
||||
e.EmojiesAnimatedZoom = v
|
||||
case "emojies_send_dice":
|
||||
if err := decodeStringArray(d, &e.EmojiesSendDice); err != nil {
|
||||
return errors.Wrap(err, "decode emojies_send_dice")
|
||||
}
|
||||
case "emojies_send_dice_success":
|
||||
if e.EmojiesSendDiceSuccess == nil {
|
||||
e.EmojiesSendDiceSuccess = map[string]EmojiSendDiceSuccess{}
|
||||
}
|
||||
if err := d.Obj(func(d *jx.Decoder, key string) error {
|
||||
var v EmojiSendDiceSuccess
|
||||
if err := v.DecodeJSON(d); err != nil {
|
||||
return errors.Wrapf(err, "decode %q", key)
|
||||
}
|
||||
e.EmojiesSendDiceSuccess[key] = v
|
||||
return nil
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "decode emojies_send_dice_success")
|
||||
}
|
||||
case "emojies_sounds":
|
||||
if e.EmojiesSounds == nil {
|
||||
e.EmojiesSounds = map[string]EmojiSound{}
|
||||
}
|
||||
if err := d.Obj(func(d *jx.Decoder, key string) error {
|
||||
var v EmojiSound
|
||||
if err := v.DecodeJSON(d); err != nil {
|
||||
return errors.Wrapf(err, "decode %q", key)
|
||||
}
|
||||
e.EmojiesSounds[key] = v
|
||||
return nil
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "decode emojies_sounds")
|
||||
}
|
||||
case "gif_search_branding":
|
||||
v, err := d.Str()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode gif_search_branding")
|
||||
}
|
||||
e.GIFSearchBranding = v
|
||||
case "gif_search_emojies":
|
||||
if err := decodeStringArray(d, &e.GIFSearchEmojies); err != nil {
|
||||
return errors.Wrap(err, "decode gif_search_emojies")
|
||||
}
|
||||
case "stickers_emoji_suggest_only_api":
|
||||
v, err := d.Bool()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode stickers_emoji_suggest_only_api")
|
||||
}
|
||||
e.StickersEmojiSuggestOnlyAPI = v
|
||||
case "stickers_emoji_cache_time":
|
||||
v, err := d.Int()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode stickers_emoji_cache_time")
|
||||
}
|
||||
e.StickersEmojiCacheTime = v
|
||||
case "groupcall_video_participants_max":
|
||||
v, err := d.Int()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode groupcall_video_participants_max")
|
||||
}
|
||||
e.GroupCallVideoParticipantsMax = v
|
||||
case "youtube_pip":
|
||||
v, err := d.Str()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode youtube_pip")
|
||||
}
|
||||
e.YoutubePIP = v
|
||||
case "qr_login_camera":
|
||||
v, err := d.Bool()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode qr_login_camera")
|
||||
}
|
||||
e.QRLoginCamera = v
|
||||
case "qr_login_code":
|
||||
v, err := d.Str()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode qr_login_code")
|
||||
}
|
||||
e.QRLoginCode = v
|
||||
case "dialog_filters_enabled":
|
||||
v, err := d.Bool()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode dialog_filters_enabled")
|
||||
}
|
||||
e.DialogFiltersEnabled = v
|
||||
case "dialog_filters_tooltip":
|
||||
v, err := d.Bool()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode dialog_filters_tooltip")
|
||||
}
|
||||
e.DialogFiltersTooltip = v
|
||||
case "autoarchive_setting_available":
|
||||
v, err := d.Bool()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode autoarchive_setting_available")
|
||||
}
|
||||
e.AutoArchiveSettingAvailable = v
|
||||
case "pending_suggestions":
|
||||
if err := decodeStringArray(d, &e.PendingSuggestions); err != nil {
|
||||
return errors.Wrap(err, "decode pending_suggestions")
|
||||
}
|
||||
case "autologin_token":
|
||||
v, err := d.Str()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode autologin_token")
|
||||
}
|
||||
e.AutologinToken = v
|
||||
case "autologin_domains":
|
||||
if err := decodeStringArray(d, &e.AutologinDomains); err != nil {
|
||||
return errors.Wrap(err, "decode autologin_domains")
|
||||
}
|
||||
case "url_auth_domains":
|
||||
if err := decodeStringArray(d, &e.URLAuthDomains); err != nil {
|
||||
return errors.Wrap(err, "decode url_auth_domains")
|
||||
}
|
||||
case "round_video_encoding":
|
||||
if err := e.RoundVideoEncoding.DecodeJSON(d); err != nil {
|
||||
return errors.Wrap(err, "decode round_video_encoding")
|
||||
}
|
||||
case "chat_read_mark_expire_period":
|
||||
v, err := d.Int()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode chat_read_mark_expire_period")
|
||||
}
|
||||
e.ChatReadMarkExpirePeriod = v
|
||||
case "chat_read_mark_size_threshold":
|
||||
v, err := d.Int()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "decode chat_read_mark_size_threshold")
|
||||
}
|
||||
e.ChatReadMarkSizeThreshold = v
|
||||
default:
|
||||
if e.Unparsed == nil {
|
||||
e.Unparsed = map[string]tg.JSONValueClass{}
|
||||
}
|
||||
v, err := Decode(d)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "decode %q", key)
|
||||
}
|
||||
e.Unparsed[string(key)] = v
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// DecodeJSONValue decodes AppConfig from tg.JSONValueClass.
|
||||
func (e *AppConfig) DecodeJSONValue(val tg.JSONValueClass) error {
|
||||
v, ok := val.(*tg.JSONObject)
|
||||
if !ok {
|
||||
return errors.Errorf("unexpected type %T", val)
|
||||
}
|
||||
|
||||
// TODO(tdakkota): decode directly
|
||||
encoder := jx.GetEncoder()
|
||||
Encode(v, encoder)
|
||||
|
||||
return e.DecodeJSON(jx.DecodeBytes(encoder.Bytes()))
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
package tljson
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-faster/jx"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
||||
)
|
||||
|
||||
const example = `{
|
||||
"test": 1,
|
||||
"emojies_animated_zoom": 0.625,
|
||||
"emojies_send_dice": [
|
||||
"\ud83c\udfb2",
|
||||
"\ud83c\udfaf",
|
||||
"\ud83c\udfc0",
|
||||
"\u26bd",
|
||||
"\u26bd\ufe0f",
|
||||
"\ud83c\udfb0",
|
||||
"\ud83c\udfb3"
|
||||
],
|
||||
"emojies_send_dice_success": {
|
||||
"\ud83c\udfaf": {
|
||||
"value": 6,
|
||||
"frame_start": 62
|
||||
},
|
||||
"\ud83c\udfc0": {
|
||||
"value": 5,
|
||||
"frame_start": 110
|
||||
},
|
||||
"\u26bd": {
|
||||
"value": 5,
|
||||
"frame_start": 110
|
||||
},
|
||||
"\u26bd\ufe0f": {
|
||||
"value": 5,
|
||||
"frame_start": 110
|
||||
},
|
||||
"\ud83c\udfb0": {
|
||||
"value": 64,
|
||||
"frame_start": 110
|
||||
},
|
||||
"\ud83c\udfb3": {
|
||||
"value": 6,
|
||||
"frame_start": 110
|
||||
}
|
||||
},
|
||||
"emojies_sounds": {
|
||||
"\ud83c\udf83": {
|
||||
"id": "4956223179606458539",
|
||||
"access_hash": "-2107001400913062971",
|
||||
"file_reference_base64": "AGFhvoKbftK5O9K9RpgN1ZtgSzWy"
|
||||
},
|
||||
"\u26b0": {
|
||||
"id": "4956223179606458540",
|
||||
"access_hash": "-1498869544183595185",
|
||||
"file_reference_base64": "AGFhvoJIm8Uz0qSMIdm3AsKlK7wJ"
|
||||
},
|
||||
"\ud83e\udddf\u200d\u2642": {
|
||||
"id": "4960929110848176331",
|
||||
"access_hash": "3986395821757915468",
|
||||
"file_reference_base64": "AGFhvoLtXSSIclmvfg6ePz3KsHQF"
|
||||
},
|
||||
"\ud83e\udddf": {
|
||||
"id": "4960929110848176332",
|
||||
"access_hash": "-8929417974289765626",
|
||||
"file_reference_base64": "AGFhvoImaz5Umt4GvMUD5nocIu0W"
|
||||
},
|
||||
"\ud83e\udddf\u200d\u2640": {
|
||||
"id": "4960929110848176333",
|
||||
"access_hash": "9161696144162881753",
|
||||
"file_reference_base64": "AGFhvoIm1QZsb48xlpRfh4Mq7EMG"
|
||||
},
|
||||
"\ud83c\udf51": {
|
||||
"id": "4963180910661861548",
|
||||
"access_hash": "-7431729439735063448",
|
||||
"file_reference_base64": "AGFhvoKLrwl_WKr5LR0Jjs7o3RyT"
|
||||
},
|
||||
"\ud83c\udf8a": {
|
||||
"id": "5094064004578410732",
|
||||
"access_hash": "8518192996098758509",
|
||||
"file_reference_base64": "AGFhvoKMNffRV2J3vKED0O6d8e42"
|
||||
},
|
||||
"\ud83c\udf84": {
|
||||
"id": "5094064004578410733",
|
||||
"access_hash": "-4142643820629256996",
|
||||
"file_reference_base64": "AGFhvoJ1ulPBbXEURlTZWwJFx6xZ"
|
||||
},
|
||||
"\ud83e\uddbe": {
|
||||
"id": "5094064004578410734",
|
||||
"access_hash": "-8934384022571962340",
|
||||
"file_reference_base64": "AGFhvoL4zdMRmYv9z3L8KPaX4JQL"
|
||||
}
|
||||
},
|
||||
"gif_search_branding": "tenor",
|
||||
"gif_search_emojies": [
|
||||
"\ud83d\udc4d",
|
||||
"\ud83d\ude18",
|
||||
"\ud83d\ude0d",
|
||||
"\ud83d\ude21",
|
||||
"\ud83e\udd73",
|
||||
"\ud83d\ude02",
|
||||
"\ud83d\ude2e",
|
||||
"\ud83d\ude44",
|
||||
"\ud83d\ude0e",
|
||||
"\ud83d\udc4e"
|
||||
],
|
||||
"stickers_emoji_suggest_only_api": false,
|
||||
"stickers_emoji_cache_time": 86400,
|
||||
"qr_login_camera": false,
|
||||
"qr_login_code": "disabled",
|
||||
"dialog_filters_enabled": true,
|
||||
"dialog_filters_tooltip": false,
|
||||
"autoarchive_setting_available": false,
|
||||
"pending_suggestions": [
|
||||
"AUTOARCHIVE_POPULAR",
|
||||
"VALIDATE_PASSWORD",
|
||||
"VALIDATE_PHONE_NUMBER",
|
||||
"NEWCOMER_TICKS"
|
||||
],
|
||||
"autologin_token": "string",
|
||||
"autologin_domains": [
|
||||
"instantview.telegram.org",
|
||||
"translations.telegram.org",
|
||||
"contest.dev",
|
||||
"contest.com",
|
||||
"bugs.telegram.org",
|
||||
"suggestions.telegram.org",
|
||||
"themes.telegram.org"
|
||||
],
|
||||
"youtube_pip": "abc",
|
||||
"groupcall_video_participants_max": 1234,
|
||||
"url_auth_domains": [
|
||||
"somedomain.telegram.org"
|
||||
],
|
||||
"round_video_encoding": {
|
||||
"diameter": 384,
|
||||
"video_bitrate": 1000,
|
||||
"audio_bitrate": 64,
|
||||
"max_size": 12582912
|
||||
},
|
||||
"chat_read_mark_size_threshold": 50,
|
||||
"chat_read_mark_expire_period": 604800,
|
||||
"unknown": null
|
||||
}`
|
||||
|
||||
func TestDecodeAppConfig(t *testing.T) {
|
||||
a := require.New(t)
|
||||
|
||||
var appConfig AppConfig
|
||||
|
||||
obj, err := Decode(jx.DecodeStr(example))
|
||||
a.NoError(err)
|
||||
a.NoError(appConfig.DecodeJSONValue(obj))
|
||||
a.Equal(AppConfig{
|
||||
Test: 1,
|
||||
EmojiesAnimatedZoom: 0.625000,
|
||||
EmojiesSendDice: []string{
|
||||
"🎲",
|
||||
"🎯",
|
||||
"🏀",
|
||||
"⚽",
|
||||
"⚽️",
|
||||
"🎰",
|
||||
"🎳",
|
||||
},
|
||||
EmojiesSendDiceSuccess: map[string]EmojiSendDiceSuccess{
|
||||
"⚽": {
|
||||
Value: 5,
|
||||
FrameStart: 110,
|
||||
},
|
||||
"⚽️": {
|
||||
Value: 5,
|
||||
FrameStart: 110,
|
||||
},
|
||||
"🎯": {
|
||||
Value: 6,
|
||||
FrameStart: 62,
|
||||
},
|
||||
"🎰": {
|
||||
Value: 64,
|
||||
FrameStart: 110,
|
||||
},
|
||||
"🎳": {
|
||||
Value: 6,
|
||||
FrameStart: 110,
|
||||
},
|
||||
"🏀": {
|
||||
Value: 5,
|
||||
FrameStart: 110,
|
||||
},
|
||||
},
|
||||
EmojiesSounds: map[string]EmojiSound{
|
||||
"⚰": {
|
||||
ID: 4956223179606458540,
|
||||
AccessHash: -1498869544183595185,
|
||||
FileReference: []uint8{
|
||||
0x00, 0x61, 0x61, 0xbe, 0x82, 0x48, 0x9b, 0xc5, 0x33, 0xd2, 0xa4, 0x8c, 0x21, 0xd9, 0xb7, 0x02,
|
||||
0xc2, 0xa5, 0x2b, 0xbc, 0x09,
|
||||
},
|
||||
},
|
||||
"🍑": {
|
||||
ID: 4963180910661861548,
|
||||
AccessHash: -7431729439735063448,
|
||||
FileReference: []uint8{
|
||||
0x00, 0x61, 0x61, 0xbe, 0x82, 0x8b, 0xaf, 0x09, 0x7f, 0x58, 0xaa, 0xf9, 0x2d, 0x1d, 0x09, 0x8e,
|
||||
0xce, 0xe8, 0xdd, 0x1c, 0x93,
|
||||
},
|
||||
},
|
||||
"🎃": {
|
||||
ID: 4956223179606458539,
|
||||
AccessHash: -2107001400913062971,
|
||||
FileReference: []uint8{
|
||||
0x00, 0x61, 0x61, 0xbe, 0x82, 0x9b, 0x7e, 0xd2, 0xb9, 0x3b, 0xd2, 0xbd, 0x46, 0x98, 0x0d, 0xd5,
|
||||
0x9b, 0x60, 0x4b, 0x35, 0xb2,
|
||||
},
|
||||
},
|
||||
"🎄": {
|
||||
ID: 5094064004578410733,
|
||||
AccessHash: -4142643820629256996,
|
||||
FileReference: []uint8{
|
||||
0x00, 0x61, 0x61, 0xbe, 0x82, 0x75, 0xba, 0x53, 0xc1, 0x6d, 0x71, 0x14, 0x46, 0x54, 0xd9, 0x5b,
|
||||
0x02, 0x45, 0xc7, 0xac, 0x59,
|
||||
},
|
||||
},
|
||||
"🎊": {
|
||||
ID: 5094064004578410732,
|
||||
AccessHash: 8518192996098758509,
|
||||
FileReference: []uint8{
|
||||
0x00, 0x61, 0x61, 0xbe, 0x82, 0x8c, 0x35, 0xf7, 0xd1, 0x57, 0x62, 0x77, 0xbc, 0xa1, 0x03, 0xd0,
|
||||
0xee, 0x9d, 0xf1, 0xee, 0x36,
|
||||
},
|
||||
},
|
||||
"🦾": {
|
||||
ID: 5094064004578410734,
|
||||
AccessHash: -8934384022571962340,
|
||||
FileReference: []uint8{
|
||||
0x00, 0x61, 0x61, 0xbe, 0x82, 0xf8, 0xcd, 0xd3, 0x11, 0x99, 0x8b, 0xfd, 0xcf, 0x72, 0xfc, 0x28,
|
||||
0xf6, 0x97, 0xe0, 0x94, 0x0b,
|
||||
},
|
||||
},
|
||||
"🧟": {
|
||||
ID: 4960929110848176332,
|
||||
AccessHash: -8929417974289765626,
|
||||
FileReference: []uint8{
|
||||
0x00, 0x61, 0x61, 0xbe, 0x82, 0x26, 0x6b, 0x3e, 0x54, 0x9a, 0xde, 0x06, 0xbc, 0xc5, 0x03, 0xe6,
|
||||
0x7a, 0x1c, 0x22, 0xed, 0x16,
|
||||
},
|
||||
},
|
||||
"🧟\u200d♀": {
|
||||
ID: 4960929110848176333,
|
||||
AccessHash: 9161696144162881753,
|
||||
FileReference: []uint8{
|
||||
0x00, 0x61, 0x61, 0xbe, 0x82, 0x26, 0xd5, 0x06, 0x6c, 0x6f, 0x8f, 0x31, 0x96, 0x94, 0x5f, 0x87,
|
||||
0x83, 0x2a, 0xec, 0x43, 0x06,
|
||||
},
|
||||
},
|
||||
"🧟\u200d♂": {
|
||||
ID: 4960929110848176331,
|
||||
AccessHash: 3986395821757915468,
|
||||
FileReference: []uint8{
|
||||
0x00, 0x61, 0x61, 0xbe, 0x82, 0xed, 0x5d, 0x24, 0x88, 0x72, 0x59, 0xaf, 0x7e, 0x0e, 0x9e, 0x3f,
|
||||
0x3d, 0xca, 0xb0, 0x74, 0x05,
|
||||
},
|
||||
},
|
||||
},
|
||||
GIFSearchBranding: "tenor",
|
||||
GIFSearchEmojies: []string{
|
||||
"👍",
|
||||
"😘",
|
||||
"😍",
|
||||
"😡",
|
||||
"🥳",
|
||||
"😂",
|
||||
"😮",
|
||||
"🙄",
|
||||
"😎",
|
||||
"👎",
|
||||
},
|
||||
StickersEmojiSuggestOnlyAPI: false,
|
||||
StickersEmojiCacheTime: 86400,
|
||||
GroupCallVideoParticipantsMax: 1234,
|
||||
YoutubePIP: "abc",
|
||||
QRLoginCamera: false,
|
||||
QRLoginCode: "disabled",
|
||||
DialogFiltersEnabled: true,
|
||||
DialogFiltersTooltip: false,
|
||||
AutoArchiveSettingAvailable: false,
|
||||
PendingSuggestions: []string{
|
||||
"AUTOARCHIVE_POPULAR",
|
||||
"VALIDATE_PASSWORD",
|
||||
"VALIDATE_PHONE_NUMBER",
|
||||
"NEWCOMER_TICKS",
|
||||
},
|
||||
AutologinToken: "string",
|
||||
AutologinDomains: []string{
|
||||
"instantview.telegram.org",
|
||||
"translations.telegram.org",
|
||||
"contest.dev",
|
||||
"contest.com",
|
||||
"bugs.telegram.org",
|
||||
"suggestions.telegram.org",
|
||||
"themes.telegram.org",
|
||||
},
|
||||
URLAuthDomains: []string{
|
||||
"somedomain.telegram.org",
|
||||
},
|
||||
RoundVideoEncoding: RoundVideoEncoding{
|
||||
Diameter: 384,
|
||||
VideoBitrate: 1000,
|
||||
AudioBitrate: 64,
|
||||
MaxSize: 12582912,
|
||||
},
|
||||
ChatReadMarkExpirePeriod: 604800,
|
||||
ChatReadMarkSizeThreshold: 50,
|
||||
Unparsed: map[string]tg.JSONValueClass{
|
||||
"unknown": &tg.JSONNull{},
|
||||
},
|
||||
}, appConfig)
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package tljson
|
||||
|
||||
import (
|
||||
"github.com/go-faster/errors"
|
||||
"github.com/go-faster/jx"
|
||||
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
||||
)
|
||||
|
||||
// Decode decodes JSON and converts it to tg.JSONValueClass.
|
||||
func Decode(d *jx.Decoder) (tg.JSONValueClass, error) {
|
||||
switch tt := d.Next(); tt {
|
||||
case jx.String:
|
||||
s, err := d.Str()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tg.JSONString{Value: s}, nil
|
||||
case jx.Number:
|
||||
f, err := d.Float64()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tg.JSONNumber{Value: f}, nil
|
||||
case jx.Null:
|
||||
if err := d.Null(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tg.JSONNull{}, nil
|
||||
case jx.Bool:
|
||||
b, err := d.Bool()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tg.JSONBool{Value: b}, nil
|
||||
case jx.Array:
|
||||
var r []tg.JSONValueClass
|
||||
if err := d.Arr(func(d *jx.Decoder) error {
|
||||
obj, err := Decode(d)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "decode %d element", len(r))
|
||||
}
|
||||
|
||||
r = append(r, obj)
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tg.JSONArray{Value: r}, nil
|
||||
case jx.Object:
|
||||
var r []tg.JSONObjectValue
|
||||
if err := d.Obj(func(d *jx.Decoder, key string) error {
|
||||
obj, err := Decode(d)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "decode %q", key)
|
||||
}
|
||||
|
||||
r = append(r, tg.JSONObjectValue{
|
||||
Key: key,
|
||||
Value: obj,
|
||||
})
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tg.JSONObject{Value: r}, nil
|
||||
default:
|
||||
return nil, errors.Errorf("unexpected type %v", tt)
|
||||
}
|
||||
}
|
||||
|
||||
// Encode writes given value to Encoder.
|
||||
func Encode(obj tg.JSONValueClass, e *jx.Encoder) {
|
||||
switch obj := obj.(type) {
|
||||
case *tg.JSONNull:
|
||||
e.Null()
|
||||
case *tg.JSONBool:
|
||||
e.Bool(obj.Value)
|
||||
case *tg.JSONNumber:
|
||||
if v := int64(obj.Value); float64(v) == obj.Value {
|
||||
e.Int64(v)
|
||||
} else {
|
||||
e.Float64(obj.Value)
|
||||
}
|
||||
case *tg.JSONString:
|
||||
e.Str(obj.Value)
|
||||
case *tg.JSONArray:
|
||||
e.ArrStart()
|
||||
for _, v := range obj.Value {
|
||||
Encode(v, e)
|
||||
}
|
||||
e.ArrEnd()
|
||||
case *tg.JSONObject:
|
||||
e.ObjStart()
|
||||
for _, pair := range obj.Value {
|
||||
e.FieldStart(pair.Key)
|
||||
Encode(pair.Value, e)
|
||||
}
|
||||
e.ObjEnd()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package tljson
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-faster/jx"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
||||
)
|
||||
|
||||
func TestDecodeEncodeDecode(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
wantErr bool
|
||||
expect tg.JSONValueClass
|
||||
}{
|
||||
{"Empty", "", true, nil},
|
||||
{"InvalidNull", "nul", true, nil},
|
||||
{"InvalidTrue", "tru", true, nil},
|
||||
{"InvalidFalse", "falsy", true, nil},
|
||||
{"InvalidInt", `[1a]"`, true, nil},
|
||||
{"InvalidFloat", "1.", true, nil},
|
||||
{"InvalidString", `"hello`, true, nil},
|
||||
{"InvalidArray", "[1, 2, 3.]", true, nil},
|
||||
{"InvalidObject", `{"abc":"def}`, true, nil},
|
||||
|
||||
{"Null", "null", false, nil},
|
||||
{"True", "true", false, nil},
|
||||
{"False", "false", false, nil},
|
||||
{"Int", "10", false, nil},
|
||||
{"Float", "1.1", false, nil},
|
||||
{"String", `"hello"`, false, nil},
|
||||
{"EmptyArray", "[]", false, nil},
|
||||
{"Array", "[1, 2, 3]", false, nil},
|
||||
{"EmptyObject", `{}`, false, nil},
|
||||
{"Object", `{"abc":"def"}`, false, nil},
|
||||
{"Tree", `{"a":1,"b":true,"c":null,"sub":{"abc":"def"}}`, false, nil},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := require.New(t)
|
||||
|
||||
// Decode.
|
||||
d := jx.DecodeStr(tt.input)
|
||||
obj, err := Decode(d)
|
||||
if tt.wantErr {
|
||||
a.Error(err)
|
||||
return
|
||||
}
|
||||
a.NoError(err)
|
||||
|
||||
// Encode.
|
||||
e := jx.GetEncoder()
|
||||
Encode(obj, e)
|
||||
|
||||
// Decode.
|
||||
d.ResetBytes(e.Bytes())
|
||||
obj2, err := Decode(d)
|
||||
a.NoError(err)
|
||||
a.Equal(obj, obj2)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
// Package tljson contains some helpers to work with JSONValue class.
|
||||
package tljson
|
||||
Reference in New Issue
Block a user