Repository: gotd/td (go)

Issue:
tg.Config: load: unmarshal: json: cannot unmarshal object into Go struct field
<!-- Please answer these questions before submitting your issue. Thanks! -->

### What version of gotd are you using?
v0.69.1-0.20220919134912-f8b8bd891200

### Can this issue be reproduced with the latest version?

Yes

### What did you do?

run the client repeatedly

### What did you expect to see?

It runs successfully.

### What did you see instead?

```
2022-09-21T09:24:33.320+0800	INFO	client.conn.mtproto.rpc	rpc/engine.go:42	Initialized	{"v": "v0.69.1-0.20220919134912-f8b8bd891200", "conn_id": 0, "dc_id": 2, "retry_interval": "1s", "max_retries": 10}
2022-09-21T09:24:33.320+0800	INFO	client	telegram/connect.go:114	Starting	{"v": "v0.69.1-0.20220919134912-f8b8bd891200"}
2022-09-21T09:24:33.321+0800	INFO	client	telegram/connect.go:131	Closed	{"v": "v0.69.1-0.20220919134912-f8b8bd891200"}
load: unmarshal: json: cannot unmarshal object into Go struct field Config.Data.Config.ReactionsDefault of type tg.ReactionClass
```

After https://github.com/gotd/td/pull/880, `tg.Config` has added a `ReactionDefault` interface type that cannot be parsed to go struct when json unmarshal.

### What Go version and environment are you using?
v1.18.5


Pull request:
fix(session): fix encoding, use tg.Config subset
The tg.Config cannot be marshalled from json now,
fixing it and reducing it to subset.

This should be backward compatible.

Fix #885 


Patch:
diff --git a/session/session.go b/session/session.go
index 719f18f5f..cdeba2f49 100644
--- a/session/session.go
+++ b/session/session.go
@@ -10,9 +10,91 @@ import (
 	"github.com/gotd/td/tg"
 )
 
+// Config is subset of tg.Config.
+type Config struct {
+	// Indicates that telegram is probably censored by governments/ISPs in the current region
+	BlockedMode bool
+	// Whether pfs¹ was used
+	//
+	// Links:
+	//  1) https://core.telegram.org/api/pfs
+	PFSEnabled bool
+	// Whether to forcefully try connecting using IPv6 dcOptions¹
+	//
+	// Links:
+	//  1) https://core.telegram.org/type/DcOption
+	ForceTryIpv6 bool
+	// Current date at the server
+	Date int
+	// Expiration date of this config: when it expires it'll have to be refetched using help
+	// getConfig¹
+	//
+	// Links:
+	//  1) https://core.telegram.org/method/help.getConfig
+	Expires int
+	// Whether we're connected to the test DCs
+	TestMode bool
+	// ID of the DC that returned the reply
+	ThisDC int
+	// DC IP list
+	DCOptions []tg.DCOption
+	// Domain name for fetching encrypted DC list from DNS TXT record
+	DCTxtDomainName string
+	// Temporary passport¹ sessions
+	//
+	// Links:
+	//  1) https://core.telegram.org/passport
+	//
+	// Use SetTmpSessions and GetTmpSessions helpers.
+	TmpSessions int
+	// DC ID to use to download webfiles¹
+	//
+	// Links:
+	//  1) https://core.telegram.org/api/files#downloading-webfiles
+	WebfileDCID int
+}
+
+// ConfigFromTG converts tg.Config to Config.
+//
+// Note that Config is the subset of tg.Config, so data loss is possible.
+func ConfigFromTG(c tg.Config) Config {
+	return Config{
+		BlockedMode:     c.BlockedMode,
+		ForceTryIpv6:    c.ForceTryIpv6,
+		PFSEnabled:      c.PFSEnabled,
+		Date:            c.Date,
+		Expires:         c.Expires,
+		TestMode:        c.TestMode,
+		ThisDC:          c.ThisDC,
+		DCOptions:       c.DCOptions,
+		DCTxtDomainName: c.DCTxtDomainName,
+		WebfileDCID:     c.WebfileDCID,
+		TmpSessions:     c.TmpSessions,
+	}
+}
+
+// TG returns tg.Config from Config.
+//
+// Note that config is the subset of tg.Config, so some fields will be unset.
+func (c Config) TG() tg.Config {
+	return tg.Config{
+		BlockedMode:     c.BlockedMode,
+		ForceTryIpv6:    c.ForceTryIpv6,
+		PFSEnabled:      c.PFSEnabled,
+		Date:            c.Date,
+		Expires:         c.Expires,
+		TestMode:        c.TestMode,
+		ThisDC:          c.ThisDC,
+		DCOptions:       c.DCOptions,
+		DCTxtDomainName: c.DCTxtDomainName,
+		WebfileDCID:     c.WebfileDCID,
+		TmpSessions:     c.TmpSessions,
+	}
+}
+
 // Data of session.
 type Data struct {
-	Config    tg.Config
+	Config    Config
 	DC        int
 	Addr      string
 	AuthKey   []byte
diff --git a/session/tdesktop.go b/session/tdesktop.go
index 3b7881462..cf2431afb 100644
--- a/session/tdesktop.go
+++ b/session/tdesktop.go
@@ -25,7 +25,7 @@ func findDCAddr(list []tg.DCOption, dcID int) string {
 	return ""
 }
 
-func mapConfig(mainDC int, cfg tdesktop.MTPConfig) tg.Config {
+func mapConfig(mainDC int, cfg tdesktop.MTPConfig) Config {
 	var opts []tg.DCOption
 	for _, dc := range cfg.DCOptions.Options {
 		opts = append(opts, tg.DCOption{
@@ -41,38 +41,12 @@ func mapConfig(mainDC int, cfg tdesktop.MTPConfig) tg.Config {
 			Secret:            dc.Secret,
 		})
 	}
-	return tg.Config{
-		DCOptions:               opts,
-		ThisDC:                  mainDC,
-		ChatSizeMax:             int(cfg.ChatSizeMax),
-		MegagroupSizeMax:        int(cfg.MegagroupSizeMax),
-		ForwardedCountMax:       int(cfg.ForwardedCountMax),
-		OnlineUpdatePeriodMs:    int(cfg.OnlineUpdatePeriod),
-		OfflineBlurTimeoutMs:    int(cfg.OfflineBlurTimeout),
-		OfflineIdleTimeoutMs:    int(cfg.OfflineIdleTimeout),
-		OnlineCloudTimeoutMs:    int(cfg.OnlineCloudTimeout),
-		NotifyCloudDelayMs:      int(cfg.NotifyCloudDelay),
-		NotifyDefaultDelayMs:    int(cfg.NotifyDefaultDelay),
-		SavedGifsLimit:          int(cfg.SavedGifsLimit),
-		EditTimeLimit:           int(cfg.EditTimeLimit),
-		RevokeTimeLimit:         int(cfg.RevokeTimeLimit),
-		RevokePmTimeLimit:       int(cfg.RevokePrivateTimeLimit),
-		RevokePmInbox:           cfg.RevokePrivateInbox,
-		StickersRecentLimit:     int(cfg.StickersRecentLimit),
-		StickersFavedLimit:      int(cfg.StickersFavedLimit),
-		PinnedDialogsCountMax:   int(cfg.PinnedDialogsCountMax),
-		PinnedInfolderCountMax:  int(cfg.PinnedDialogsInFolderMax),
-		MeURLPrefix:             cfg.InternalLinksDomain,
-		ChannelsReadMediaPeriod: int(cfg.ChannelsReadMediaPeriod),
-		CallReceiveTimeoutMs:    int(cfg.CallReceiveTimeoutMs),
-		CallRingTimeoutMs:       int(cfg.CallRingTimeoutMs),
-		CallConnectTimeoutMs:    int(cfg.CallConnectTimeoutMs),
-		CallPacketTimeoutMs:     int(cfg.CallPacketTimeoutMs),
-		WebfileDCID:             int(cfg.WebFileDCID),
-		DCTxtDomainName:         cfg.TxtDomainString,
-		PhonecallsEnabled:       cfg.PhoneCallsEnabled,
-		BlockedMode:             cfg.BlockedMode,
-		CaptionLengthMax:        int(cfg.CaptionLengthMax),
+	return Config{
+		DCOptions:       opts,
+		ThisDC:          mainDC,
+		WebfileDCID:     int(cfg.WebFileDCID),
+		DCTxtDomainName: cfg.TxtDomainString,
+		BlockedMode:     cfg.BlockedMode,
 	}
 }
 
diff --git a/telegram/session.go b/telegram/session.go
index 000aebf2c..18e65dada 100644
--- a/telegram/session.go
+++ b/telegram/session.go
@@ -76,7 +76,7 @@ func (c *Client) saveSession(cfg tg.Config, s mtproto.Session) error {
 	}
 
 	// Updating previous data.
-	data.Config = cfg
+	data.Config = session.ConfigFromTG(cfg)
 	data.AuthKey = s.Key.Value[:]
 	data.AuthKeyID = s.Key.ID[:]
 	data.DC = cfg.ThisDC


Test patch:
diff --git a/session/session_test.go b/session/session_test.go
index 75f2a1c08..aa75a3700 100644
--- a/session/session_test.go
+++ b/session/session_test.go
@@ -6,8 +6,6 @@ import (
 	"testing"
 
 	"github.com/stretchr/testify/require"
-
-	"github.com/gotd/td/tg"
 )
 
 func testStorage(storage Storage) func(t *testing.T) {
@@ -23,7 +21,7 @@ func testStorage(storage Storage) func(t *testing.T) {
 		a.ErrorIs(err, ErrNotFound)
 
 		data := &Data{
-			Config:    tg.Config{},
+			Config:    Config{},
 			DC:        2,
 			Addr:      "localhost:8080",
 			AuthKey:   bytes.Repeat([]byte{'a'}, 256),
diff --git a/session/tdesktop_test.go b/session/tdesktop_test.go
index 78cafbe0b..552feddd9 100644
--- a/session/tdesktop_test.go
+++ b/session/tdesktop_test.go
@@ -8,7 +8,6 @@ import (
 	"github.com/gotd/td/internal/crypto"
 	"github.com/gotd/td/session/tdesktop"
 	"github.com/gotd/td/telegram/dcs"
-	"github.com/gotd/td/tg"
 )
 
 func TestTDesktopSession(t *testing.T) {
@@ -30,7 +29,7 @@ func TestTDesktopSession(t *testing.T) {
 			},
 		}, &Data{
 			DC: 2,
-			Config: tg.Config{
+			Config: Config{
 				ThisDC: 2,
 			},
 			Addr:      findDCAddr(dcs.Prod().Options, 2),