├── .gitignore
├── README.md
├── api.go
├── auth.go
├── contacts.go
├── decode.go
├── encode.go
├── math.go
├── messages.go
├── mtproto.go
├── network.go
├── schemes
└── api-layer-65.tl
├── session.go
├── types.go
├── updates.go
└── users.go
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | *.iml
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### Deprecated!!!
2 | This library is deprecated and I don't have enough time to support it.
3 | If you want to use golang in your project you should check golang binding to official library:
4 | https://github.com/Arman92/go-tdlib
5 |
6 | ## Telegram
7 |
8 | MTProto implementation in Go language
9 |
10 | This library is a fork of https://github.com/sdidyk/mtproto
11 |
12 | **Telegram API layer: 65**
13 | ## Examples
14 | [TelegramGo](https://github.com/shelomentsevd/telegramgo) - simple CLI client for telegram
15 | ## License
16 |
17 | MIT
18 |
--------------------------------------------------------------------------------
/auth.go:
--------------------------------------------------------------------------------
1 | package mtproto
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | )
7 |
8 | func (m *MTProto) AuthSendCode(phonenumber string) (*TL_auth_sentCode, error) {
9 | var authSentCode TL_auth_sentCode
10 | tl, err := m.InvokeSync(TL_auth_sendCode{
11 | Allow_flashcall: false,
12 | Phone_number: phonenumber,
13 | Current_number: TL_boolTrue{},
14 | Api_id: m.id,
15 | Api_hash: m.hash,
16 | })
17 |
18 | if err != nil {
19 | return nil, err
20 | }
21 |
22 | switch (*tl).(type) {
23 | case TL_auth_sentCode:
24 | authSentCode = (*tl).(TL_auth_sentCode)
25 | default:
26 | return nil, fmt.Errorf("Got: %T", *tl)
27 | }
28 |
29 | return &authSentCode, nil
30 | }
31 |
32 | func (m *MTProto) AuthSignIn(phoneNumber, phoneCode, phoneCodeHash string) (*TL_auth_authorization, error) {
33 | if phoneNumber == "" || phoneCode == "" || phoneCodeHash == "" {
34 | return nil, errors.New("MRProto::AuthSignIn one of function parameters is empty")
35 | }
36 |
37 | tl, err := m.InvokeSync(TL_auth_signIn{
38 | Phone_number: phoneNumber,
39 | Phone_code_hash: phoneCodeHash,
40 | Phone_code: phoneCode,
41 | })
42 |
43 | if err != nil {
44 | return nil, err
45 | }
46 |
47 | auth, ok := (*tl).(TL_auth_authorization)
48 |
49 | if !ok {
50 | return nil, fmt.Errorf("RPC: %#v", *tl)
51 | }
52 |
53 | return &auth, nil
54 | }
55 |
56 | func (m *MTProto) AuthLogOut() (bool, error) {
57 | var result bool
58 |
59 | tl, err := m.InvokeSync(TL_auth_logOut{})
60 | if err != nil {
61 | return result, err
62 | }
63 |
64 | result, err = ToBool(*tl)
65 | if err != nil {
66 | return result, err
67 | }
68 |
69 | return result, err
70 | }
71 |
--------------------------------------------------------------------------------
/contacts.go:
--------------------------------------------------------------------------------
1 | package mtproto
2 |
3 | import "errors"
4 |
5 | func (m *MTProto) ContactsGetContacts(hash string) (*TL, error) {
6 | return m.InvokeSync(TL_contacts_getContacts{
7 | Hash: hash,
8 | })
9 | }
10 |
11 | func (m *MTProto) ContactsGetTopPeers(correspondents, botsPM, botsInline, groups, channels bool, offset, limit, hash int32) (*TL, error) {
12 | tl, err := m.InvokeSync(TL_contacts_getTopPeers{
13 | Correspondents: correspondents,
14 | Bots_pm: botsPM,
15 | Bots_inline: botsInline,
16 | Groups: groups,
17 | Channels: channels,
18 | Offset: offset,
19 | Limit: limit,
20 | Hash: hash,
21 | })
22 |
23 | if err != nil {
24 | return nil, err
25 | }
26 |
27 | switch (*tl).(type) {
28 | case TL_contacts_topPeersNotModified:
29 | case TL_contacts_topPeers:
30 | default:
31 | return nil, errors.New("MTProto::ContactsGetTopPeers error: Unknown type")
32 | }
33 |
34 | return tl, nil
35 | }
36 |
--------------------------------------------------------------------------------
/decode.go:
--------------------------------------------------------------------------------
1 | package mtproto
2 |
3 | import (
4 | "bytes"
5 | "compress/gzip"
6 | "encoding/binary"
7 | "encoding/hex"
8 | "errors"
9 | "fmt"
10 | "math"
11 | "math/big"
12 | )
13 |
14 | type DecodeBuf struct {
15 | buf []byte
16 | off int
17 | size int
18 | err error
19 | }
20 |
21 | func NewDecodeBuf(b []byte) *DecodeBuf {
22 | return &DecodeBuf{b, 0, len(b), nil}
23 | }
24 |
25 | func (m *DecodeBuf) Long() int64 {
26 | if m.err != nil {
27 | return 0
28 | }
29 | if m.off+8 > m.size {
30 | m.err = errors.New("DecodeLong")
31 | return 0
32 | }
33 | x := int64(binary.LittleEndian.Uint64(m.buf[m.off : m.off+8]))
34 | m.off += 8
35 | return x
36 | }
37 |
38 | func (m *DecodeBuf) Double() float64 {
39 | if m.err != nil {
40 | return 0
41 | }
42 | if m.off+8 > m.size {
43 | m.err = errors.New("DecodeDouble")
44 | return 0
45 | }
46 | x := math.Float64frombits(binary.LittleEndian.Uint64(m.buf[m.off : m.off+8]))
47 | m.off += 8
48 | return x
49 | }
50 |
51 | func (m *DecodeBuf) Int() int32 {
52 | if m.err != nil {
53 | return 0
54 | }
55 | if m.off+4 > m.size {
56 | m.err = errors.New("DecodeInt")
57 | return 0
58 | }
59 | x := binary.LittleEndian.Uint32(m.buf[m.off : m.off+4])
60 | m.off += 4
61 | return int32(x)
62 | }
63 |
64 | func (m *DecodeBuf) UInt() uint32 {
65 | if m.err != nil {
66 | return 0
67 | }
68 | if m.off+4 > m.size {
69 | m.err = errors.New("DecodeUInt")
70 | return 0
71 | }
72 | x := binary.LittleEndian.Uint32(m.buf[m.off : m.off+4])
73 | m.off += 4
74 | return x
75 | }
76 |
77 | func (m *DecodeBuf) Bytes(size int) []byte {
78 | if m.err != nil {
79 | return nil
80 | }
81 | if m.off+size > m.size {
82 | m.err = errors.New("DecodeBytes")
83 | return nil
84 | }
85 | x := make([]byte, size)
86 | copy(x, m.buf[m.off:m.off+size])
87 | m.off += size
88 | return x
89 | }
90 |
91 | func (m *DecodeBuf) StringBytes() []byte {
92 | if m.err != nil {
93 | return nil
94 | }
95 | var size, padding int
96 |
97 | if m.off+1 > m.size {
98 | m.err = errors.New("DecodeStringBytes")
99 | return nil
100 | }
101 | size = int(m.buf[m.off])
102 | m.off++
103 | padding = (4 - ((size + 1) % 4)) & 3
104 | if size == 254 {
105 | if m.off+3 > m.size {
106 | m.err = errors.New("DecodeStringBytes")
107 | return nil
108 | }
109 | size = int(m.buf[m.off]) | int(m.buf[m.off+1])<<8 | int(m.buf[m.off+2])<<16
110 | m.off += 3
111 | padding = (4 - size%4) & 3
112 | }
113 |
114 | if m.off+size > m.size {
115 | m.err = errors.New("DecodeStringBytes: Wrong Size")
116 | return nil
117 | }
118 | x := make([]byte, size)
119 | copy(x, m.buf[m.off:m.off+size])
120 | m.off += size
121 |
122 | if m.off+padding > m.size {
123 | m.err = errors.New("DecodeStringBytes: Wrong padding")
124 | return nil
125 | }
126 | m.off += padding
127 |
128 | return x
129 | }
130 |
131 | func (m *DecodeBuf) String() string {
132 | b := m.StringBytes()
133 | if m.err != nil {
134 | return ""
135 | }
136 | x := string(b)
137 | return x
138 | }
139 |
140 | func (m *DecodeBuf) BigInt() *big.Int {
141 | b := m.StringBytes()
142 | if m.err != nil {
143 | return nil
144 | }
145 | y := make([]byte, len(b)+1)
146 | y[0] = 0
147 | copy(y[1:], b)
148 | x := new(big.Int).SetBytes(y)
149 | return x
150 | }
151 |
152 | func (m *DecodeBuf) VectorInt() []int32 {
153 | constructor := m.UInt()
154 | if m.err != nil {
155 | return nil
156 | }
157 | if constructor != crc_vector {
158 | m.err = fmt.Errorf("DecodeVectorInt: Wrong constructor (0x%08x)", constructor)
159 | return nil
160 | }
161 | size := m.Int()
162 | if m.err != nil {
163 | return nil
164 | }
165 | if size < 0 {
166 | m.err = errors.New("DecodeVectorInt: Wrong Size")
167 | return nil
168 | }
169 | x := make([]int32, size)
170 | i := int32(0)
171 | for i < size {
172 | y := m.Int()
173 | if m.err != nil {
174 | return nil
175 | }
176 | x[i] = y
177 | i++
178 | }
179 | return x
180 | }
181 |
182 | func (m *DecodeBuf) VectorLong() []int64 {
183 | constructor := m.UInt()
184 | if m.err != nil {
185 | return nil
186 | }
187 | if constructor != crc_vector {
188 | m.err = fmt.Errorf("DecodeVectorLong: Wrong constructor (0x%08x)", constructor)
189 | return nil
190 | }
191 | size := m.Int()
192 | if m.err != nil {
193 | return nil
194 | }
195 | if size < 0 {
196 | m.err = errors.New("DecodeVectorLong: Wrong Size")
197 | return nil
198 | }
199 | x := make([]int64, size)
200 | i := int32(0)
201 | for i < size {
202 | y := m.Long()
203 | if m.err != nil {
204 | return nil
205 | }
206 | x[i] = y
207 | i++
208 | }
209 | return x
210 | }
211 |
212 | func (m *DecodeBuf) VectorString() []string {
213 | constructor := m.UInt()
214 | if m.err != nil {
215 | return nil
216 | }
217 | if constructor != crc_vector {
218 | m.err = fmt.Errorf("DecodeVectorString: Wrong constructor (0x%08x)", constructor)
219 | return nil
220 | }
221 | size := m.Int()
222 | if m.err != nil {
223 | return nil
224 | }
225 | if size < 0 {
226 | m.err = errors.New("DecodeVectorString: Wrong Size")
227 | return nil
228 | }
229 | x := make([]string, size)
230 | i := int32(0)
231 | for i < size {
232 | y := m.String()
233 | if m.err != nil {
234 | return nil
235 | }
236 | x[i] = y
237 | i++
238 | }
239 | return x
240 | }
241 |
242 | func (m *DecodeBuf) Bool() bool {
243 | constructor := m.UInt()
244 | if m.err != nil {
245 | return false
246 | }
247 | switch constructor {
248 | case crc_boolFalse:
249 | return false
250 | case crc_boolTrue:
251 | return true
252 | }
253 | return false
254 | }
255 |
256 | func (m *DecodeBuf) Vector() []TL {
257 | constructor := m.UInt()
258 | if m.err != nil {
259 | return nil
260 | }
261 | if constructor != crc_vector {
262 | m.err = fmt.Errorf("DecodeVector: Wrong constructor (0x%08x)", constructor)
263 | return nil
264 | }
265 | size := m.Int()
266 | if m.err != nil {
267 | return nil
268 | }
269 | if size < 0 {
270 | m.err = errors.New("DecodeVector: Wrong Size")
271 | return nil
272 | }
273 | x := make([]TL, size)
274 | i := int32(0)
275 | for i < size {
276 | y := m.Object()
277 | if m.err != nil {
278 | return nil
279 | }
280 | x[i] = y
281 | i++
282 | }
283 | return x
284 | }
285 |
286 | func (m *DecodeBuf) Object() (r TL) {
287 | constructor := m.UInt()
288 | if m.err != nil {
289 | return nil
290 | }
291 |
292 | // TODO: How to make it available only in DEBUG mode?
293 | //fmt.Printf("[%08x]\n", constructor)
294 | //m.dump()
295 |
296 | switch constructor {
297 |
298 | case crc_resPQ:
299 | r = TL_resPQ{m.Bytes(16), m.Bytes(16), m.BigInt(), m.VectorLong()}
300 |
301 | case crc_server_DH_params_ok:
302 | r = TL_server_DH_params_ok{m.Bytes(16), m.Bytes(16), m.StringBytes()}
303 |
304 | case crc_server_DH_params_fail:
305 | r = TL_server_DH_params_fail{m.Bytes(16), m.Bytes(16), m.StringBytes()}
306 |
307 | case crc_server_DH_inner_data:
308 | r = TL_server_DH_inner_data{
309 | m.Bytes(16), m.Bytes(16), m.Int(),
310 | m.BigInt(), m.BigInt(), m.Int(),
311 | }
312 |
313 | case crc_dh_gen_ok:
314 | r = TL_dh_gen_ok{m.Bytes(16), m.Bytes(16), m.Bytes(16)}
315 |
316 | case crc_ping:
317 | r = TL_ping{m.Long()}
318 |
319 | case crc_pong:
320 | r = TL_pong{m.Long(), m.Long()}
321 |
322 | case crc_msg_container:
323 | size := m.Int()
324 | arr := make([]TL_MT_message, size)
325 | for i := int32(0); i < size; i++ {
326 | arr[i] = TL_MT_message{m.Long(), m.Int(), m.Int(), m.Object()}
327 | if m.err != nil {
328 | return nil
329 | }
330 | }
331 | r = TL_msg_container{arr}
332 |
333 | case crc_rpc_result:
334 | r = TL_rpc_result{m.Long(), m.Object()}
335 |
336 | case crc_rpc_error:
337 | r = TL_rpc_error{m.Int(), m.String()}
338 |
339 | case crc_new_session_created:
340 | r = TL_new_session_created{m.Long(), m.Long(), m.Bytes(8)}
341 |
342 | case crc_bad_server_salt:
343 | r = TL_bad_server_salt{m.Long(), m.Int(), m.Int(), m.Bytes(8)}
344 |
345 | case crc_bad_msg_notification:
346 | r = TL_bad_msg_notification{m.Long(), m.Int(), m.Int()}
347 |
348 | case crc_msgs_ack:
349 | r = TL_msgs_ack{m.VectorLong()}
350 |
351 | case crc_gzip_packed:
352 | obj := make([]byte, 0, 4096)
353 |
354 | var buf bytes.Buffer
355 | _, _ = buf.Write(m.StringBytes())
356 | gz, _ := gzip.NewReader(&buf)
357 |
358 | b := make([]byte, 4096)
359 | for true {
360 | n, _ := gz.Read(b)
361 | obj = append(obj, b[0:n]...)
362 | if n <= 0 {
363 | break
364 | }
365 | }
366 | d := NewDecodeBuf(obj)
367 | r = d.Object()
368 | default:
369 | r = m.ObjectGenerated(constructor)
370 |
371 | }
372 |
373 | if m.err != nil {
374 | return nil
375 | }
376 |
377 | return
378 | }
379 |
380 | func (d *DecodeBuf) dump() {
381 | fmt.Println(hex.Dump(d.buf[d.off:d.size]))
382 | }
383 |
384 | func ToBool(x TL) (bool, error) {
385 | switch x.(type) {
386 | case TL_boolTrue:
387 | return true, nil
388 | case TL_boolFalse:
389 | return false, nil
390 | default:
391 | return false, fmt.Errorf("Type %T can't convert to bool", x)
392 | }
393 | }
394 |
--------------------------------------------------------------------------------
/encode.go:
--------------------------------------------------------------------------------
1 | package mtproto
2 |
3 | import (
4 | "crypto/rand"
5 | "encoding/binary"
6 | "math"
7 | "math/big"
8 | "time"
9 | )
10 |
11 | func GenerateNonce(size int) []byte {
12 | b := make([]byte, size)
13 | _, _ = rand.Read(b)
14 | return b
15 | }
16 |
17 | func GenerateMessageId() int64 {
18 | const nano = 1000 * 1000 * 1000
19 | unixnano := time.Now().UnixNano()
20 |
21 | return ((unixnano / nano) << 32) | ((unixnano % nano) & -4)
22 | }
23 |
24 | type EncodeBuf struct {
25 | buf []byte
26 | }
27 |
28 | func NewEncodeBuf(cap int) *EncodeBuf {
29 | return &EncodeBuf{make([]byte, 0, cap)}
30 | }
31 |
32 | func (e *EncodeBuf) Int(s int32) {
33 | e.buf = append(e.buf, 0, 0, 0, 0)
34 | binary.LittleEndian.PutUint32(e.buf[len(e.buf)-4:], uint32(s))
35 | }
36 |
37 | func (e *EncodeBuf) UInt(s uint32) {
38 | e.buf = append(e.buf, 0, 0, 0, 0)
39 | binary.LittleEndian.PutUint32(e.buf[len(e.buf)-4:], s)
40 | }
41 |
42 | func (e *EncodeBuf) Long(s int64) {
43 | e.buf = append(e.buf, 0, 0, 0, 0, 0, 0, 0, 0)
44 | binary.LittleEndian.PutUint64(e.buf[len(e.buf)-8:], uint64(s))
45 | }
46 |
47 | func (e *EncodeBuf) Double(s float64) {
48 | e.buf = append(e.buf, 0, 0, 0, 0, 0, 0, 0, 0)
49 | binary.LittleEndian.PutUint64(e.buf[len(e.buf)-8:], math.Float64bits(s))
50 | }
51 |
52 | func (e *EncodeBuf) String(s string) {
53 | e.StringBytes([]byte(s))
54 | }
55 |
56 | func (e *EncodeBuf) BigInt(s *big.Int) {
57 | e.StringBytes(s.Bytes())
58 | }
59 |
60 | func (e *EncodeBuf) StringBytes(s []byte) {
61 | var res []byte
62 | size := len(s)
63 | if size < 254 {
64 | nl := 1 + size + (4-(size+1)%4)&3
65 | res = make([]byte, nl)
66 | res[0] = byte(size)
67 | copy(res[1:], s)
68 |
69 | } else {
70 | nl := 4 + size + (4-size%4)&3
71 | res = make([]byte, nl)
72 | binary.LittleEndian.PutUint32(res, uint32(size<<8|254))
73 | copy(res[4:], s)
74 |
75 | }
76 | e.buf = append(e.buf, res...)
77 | }
78 |
79 | func (e *EncodeBuf) Bytes(s []byte) {
80 | e.buf = append(e.buf, s...)
81 | }
82 |
83 | func (e *EncodeBuf) VectorInt(v []int32) {
84 | x := make([]byte, 4+4+len(v)*4)
85 | binary.LittleEndian.PutUint32(x, crc_vector)
86 | binary.LittleEndian.PutUint32(x[4:], uint32(len(v)))
87 | i := 8
88 | for _, v := range v {
89 | binary.LittleEndian.PutUint32(x[i:], uint32(v))
90 | i += 4
91 | }
92 | e.buf = append(e.buf, x...)
93 | }
94 |
95 | func (e *EncodeBuf) VectorLong(v []int64) {
96 | x := make([]byte, 4+4+len(v)*8)
97 | binary.LittleEndian.PutUint32(x, crc_vector)
98 | binary.LittleEndian.PutUint32(x[4:], uint32(len(v)))
99 | i := 8
100 | for _, v := range v {
101 | binary.LittleEndian.PutUint64(x[i:], uint64(v))
102 | i += 8
103 | }
104 | e.buf = append(e.buf, x...)
105 | }
106 |
107 | func (e *EncodeBuf) VectorString(v []string) {
108 | x := make([]byte, 8)
109 | binary.LittleEndian.PutUint32(x, crc_vector)
110 | binary.LittleEndian.PutUint32(x[4:], uint32(len(v)))
111 | e.buf = append(e.buf, x...)
112 | for _, v := range v {
113 | e.String(v)
114 | }
115 | }
116 |
117 | func (e *EncodeBuf) Vector(v []TL) {
118 | x := make([]byte, 8)
119 | binary.LittleEndian.PutUint32(x, crc_vector)
120 | binary.LittleEndian.PutUint32(x[4:], uint32(len(v)))
121 | e.buf = append(e.buf, x...)
122 | for _, v := range v {
123 | e.buf = append(e.buf, v.encode()...)
124 | }
125 | }
126 |
127 | // TODO: Does only server send messages below?
128 | func (e TL_msg_container) encode() []byte { return nil }
129 | func (e TL_resPQ) encode() []byte { return nil }
130 | func (e TL_server_DH_params_ok) encode() []byte { return nil }
131 | func (e TL_server_DH_params_fail) encode() []byte { return nil }
132 | func (e TL_server_DH_inner_data) encode() []byte { return nil }
133 | func (e TL_dh_gen_ok) encode() []byte { return nil }
134 | func (e TL_rpc_result) encode() []byte { return nil }
135 | func (e TL_rpc_error) encode() []byte { return nil }
136 | func (e TL_new_session_created) encode() []byte { return nil }
137 | func (e TL_bad_server_salt) encode() []byte { return nil }
138 | func (e TL_bad_msg_notification) encode() []byte { return nil }
139 |
140 | func (e TL_req_pq) encode() []byte {
141 | x := NewEncodeBuf(20)
142 | x.UInt(crc_req_pq)
143 | x.Bytes(e.Nonce)
144 | return x.buf
145 | }
146 |
147 | func (e TL_p_q_inner_data) encode() []byte {
148 | x := NewEncodeBuf(256)
149 | x.UInt(crc_p_q_inner_data)
150 | x.BigInt(e.Pq)
151 | x.BigInt(e.P)
152 | x.BigInt(e.Q)
153 | x.Bytes(e.Nonce)
154 | x.Bytes(e.Server_nonce)
155 | x.Bytes(e.New_nonce)
156 | return x.buf
157 | }
158 |
159 | func (e TL_req_DH_params) encode() []byte {
160 | x := NewEncodeBuf(512)
161 | x.UInt(crc_req_DH_params)
162 | x.Bytes(e.Nonce)
163 | x.Bytes(e.Server_nonce)
164 | x.BigInt(e.P)
165 | x.BigInt(e.Q)
166 | x.Long(int64(e.Fp))
167 | x.StringBytes(e.Encdata)
168 | return x.buf
169 | }
170 |
171 | func (e TL_client_DH_inner_data) encode() []byte {
172 | x := NewEncodeBuf(512)
173 | x.UInt(crc_client_DH_inner_data)
174 | x.Bytes(e.Nonce)
175 | x.Bytes(e.Server_nonce)
176 | x.Long(e.Retry)
177 | x.BigInt(e.G_b)
178 | return x.buf
179 | }
180 |
181 | func (e TL_set_client_DH_params) encode() []byte {
182 | x := NewEncodeBuf(256)
183 | x.UInt(crc_set_client_DH_params)
184 | x.Bytes(e.Nonce)
185 | x.Bytes(e.Server_nonce)
186 | x.StringBytes(e.Encdata)
187 | return x.buf
188 | }
189 |
190 | func (e TL_ping) encode() []byte {
191 | x := NewEncodeBuf(32)
192 | x.UInt(crc_ping)
193 | x.Long(e.Ping_id)
194 | return x.buf
195 | }
196 |
197 | func (e TL_pong) encode() []byte {
198 | x := NewEncodeBuf(32)
199 | x.UInt(crc_pong)
200 | x.Long(e.Msg_id)
201 | x.Long(e.Ping_id)
202 | return x.buf
203 | }
204 |
205 | func (e TL_msgs_ack) encode() []byte {
206 | x := NewEncodeBuf(64)
207 | x.UInt(crc_msgs_ack)
208 | x.VectorLong(e.MsgIds)
209 | return x.buf
210 | }
211 |
212 | func (e TL_boolFalse) encode() []byte {
213 | x := NewEncodeBuf(4)
214 | x.UInt(crc_boolFalse)
215 | return x.buf
216 | }
217 |
218 | func (e TL_boolTrue) encode() []byte {
219 | x := NewEncodeBuf(4)
220 | x.UInt(crc_boolTrue)
221 | return x.buf
222 | }
223 |
224 | func (e TL_null) encode() []byte {
225 | x := NewEncodeBuf(4)
226 | x.UInt(crc_null)
227 | return x.buf
228 | }
229 |
--------------------------------------------------------------------------------
/math.go:
--------------------------------------------------------------------------------
1 | package mtproto
2 |
3 | import (
4 | "crypto/aes"
5 | "crypto/rsa"
6 | sha1lib "crypto/sha1"
7 | "errors"
8 | "math/big"
9 | "math/rand"
10 | "time"
11 | )
12 |
13 | // TODO: Think about configurable public key
14 | const (
15 | telegramPublicKey_N = "24403446649145068056824081744112065346446136066297307473868293895086332508101251964919587745984311372853053253457835208829824428441874946556659953519213382748319518214765985662663680818277989736779506318868003755216402538945900388706898101286548187286716959100102939636333452457308619454821845196109544157601096359148241435922125602449263164512290854366930013825808102403072317738266383237191313714482187326643144603633877219028262697593882410403273959074350849923041765639673335775605842311578109726403165298875058941765362622936097839775380070572921007586266115476975819175319995527916042178582540628652481530373407"
16 | telegramPublicKey_E = 65537
17 | telegramPublicKey_FP = 14101943622620965665
18 | )
19 |
20 | var telegramPublicKey rsa.PublicKey
21 |
22 | func init() {
23 | telegramPublicKey.N, _ = new(big.Int).SetString(telegramPublicKey_N, 10)
24 | telegramPublicKey.E = telegramPublicKey_E
25 | }
26 |
27 | func sha1(data []byte) []byte {
28 | r := sha1lib.Sum(data)
29 | return r[:]
30 | }
31 |
32 | func doRSAencrypt(em []byte) []byte {
33 | z := make([]byte, 255)
34 | copy(z, em)
35 |
36 | c := new(big.Int)
37 | c.Exp(new(big.Int).SetBytes(z), big.NewInt(int64(telegramPublicKey.E)), telegramPublicKey.N)
38 |
39 | res := make([]byte, 256)
40 | copy(res, c.Bytes())
41 |
42 | return res
43 | }
44 |
45 | func splitPQ(pq *big.Int) (p1, p2 *big.Int) {
46 | value_0 := big.NewInt(0)
47 | value_1 := big.NewInt(1)
48 | value_15 := big.NewInt(15)
49 | value_17 := big.NewInt(17)
50 | rndmax := big.NewInt(0).SetBit(big.NewInt(0), 64, 1)
51 |
52 | what := big.NewInt(0).Set(pq)
53 | rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
54 | g := big.NewInt(0)
55 | i := 0
56 | for !(g.Cmp(value_1) == 1 && g.Cmp(what) == -1) {
57 | q := big.NewInt(0).Rand(rnd, rndmax)
58 | q = q.And(q, value_15)
59 | q = q.Add(q, value_17)
60 | q = q.Mod(q, what)
61 |
62 | x := big.NewInt(0).Rand(rnd, rndmax)
63 | whatnext := big.NewInt(0).Sub(what, value_1)
64 | x = x.Mod(x, whatnext)
65 | x = x.Add(x, value_1)
66 |
67 | y := big.NewInt(0).Set(x)
68 | lim := 1 << (uint(i) + 18)
69 | j := 1
70 | flag := true
71 |
72 | for j < lim && flag {
73 | a := big.NewInt(0).Set(x)
74 | b := big.NewInt(0).Set(x)
75 | c := big.NewInt(0).Set(q)
76 |
77 | for b.Cmp(value_0) == 1 {
78 | b2 := big.NewInt(0)
79 | if b2.And(b, value_1).Cmp(value_0) == 1 {
80 | c.Add(c, a)
81 | if c.Cmp(what) >= 0 {
82 | c.Sub(c, what)
83 | }
84 | }
85 | a.Add(a, a)
86 | if a.Cmp(what) >= 0 {
87 | a.Sub(a, what)
88 | }
89 | b.Rsh(b, 1)
90 | }
91 | x.Set(c)
92 |
93 | z := big.NewInt(0)
94 | if x.Cmp(y) == -1 {
95 | z.Add(what, x)
96 | z.Sub(z, y)
97 | } else {
98 | z.Sub(x, y)
99 | }
100 | g.GCD(nil, nil, z, what)
101 |
102 | if (j & (j - 1)) == 0 {
103 | y.Set(x)
104 | }
105 | j = j + 1
106 |
107 | if g.Cmp(value_1) != 0 {
108 | flag = false
109 | }
110 | }
111 | i = i + 1
112 | }
113 |
114 | p1 = big.NewInt(0).Set(g)
115 | p2 = big.NewInt(0).Div(what, g)
116 |
117 | if p1.Cmp(p2) == 1 {
118 | p1, p2 = p2, p1
119 | }
120 |
121 | return
122 | }
123 |
124 | func makeGAB(g int32, g_a, dh_prime *big.Int) (b, g_b, g_ab *big.Int) {
125 | rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
126 | rndmax := big.NewInt(0).SetBit(big.NewInt(0), 2048, 1)
127 | b = big.NewInt(0).Rand(rnd, rndmax)
128 | g_b = big.NewInt(0).Exp(big.NewInt(int64(g)), b, dh_prime)
129 | g_ab = big.NewInt(0).Exp(g_a, b, dh_prime)
130 |
131 | return
132 | }
133 |
134 | func generateAES(msg_key, auth_key []byte, decode bool) ([]byte, []byte) {
135 | var x int
136 | if decode {
137 | x = 8
138 | } else {
139 | x = 0
140 | }
141 | aes_key := make([]byte, 0, 32)
142 | aes_iv := make([]byte, 0, 32)
143 | t_a := make([]byte, 0, 48)
144 | t_b := make([]byte, 0, 48)
145 | t_c := make([]byte, 0, 48)
146 | t_d := make([]byte, 0, 48)
147 |
148 | t_a = append(t_a, msg_key...)
149 | t_a = append(t_a, auth_key[x:x+32]...)
150 |
151 | t_b = append(t_b, auth_key[32+x:32+x+16]...)
152 | t_b = append(t_b, msg_key...)
153 | t_b = append(t_b, auth_key[48+x:48+x+16]...)
154 |
155 | t_c = append(t_c, auth_key[64+x:64+x+32]...)
156 | t_c = append(t_c, msg_key...)
157 |
158 | t_d = append(t_d, msg_key...)
159 | t_d = append(t_d, auth_key[96+x:96+x+32]...)
160 |
161 | sha1_a := sha1(t_a)
162 | sha1_b := sha1(t_b)
163 | sha1_c := sha1(t_c)
164 | sha1_d := sha1(t_d)
165 |
166 | aes_key = append(aes_key, sha1_a[0:8]...)
167 | aes_key = append(aes_key, sha1_b[8:8+12]...)
168 | aes_key = append(aes_key, sha1_c[4:4+12]...)
169 |
170 | aes_iv = append(aes_iv, sha1_a[8:8+12]...)
171 | aes_iv = append(aes_iv, sha1_b[0:8]...)
172 | aes_iv = append(aes_iv, sha1_c[16:16+4]...)
173 | aes_iv = append(aes_iv, sha1_d[0:8]...)
174 |
175 | return aes_key, aes_iv
176 | }
177 |
178 | func doAES256IGEencrypt(data, key, iv []byte) ([]byte, error) {
179 | block, err := aes.NewCipher(key)
180 | if err != nil {
181 | return nil, err
182 | }
183 | if len(data) < aes.BlockSize {
184 | return nil, errors.New("AES256IGE: Data too small to encrypt")
185 | }
186 | if len(data)%aes.BlockSize != 0 {
187 | return nil, errors.New("AES256IGE: Data not divisible by block Size")
188 | }
189 |
190 | t := make([]byte, aes.BlockSize)
191 | x := make([]byte, aes.BlockSize)
192 | y := make([]byte, aes.BlockSize)
193 | copy(x, iv[:aes.BlockSize])
194 | copy(y, iv[aes.BlockSize:])
195 | encrypted := make([]byte, len(data))
196 |
197 | i := 0
198 | for i < len(data) {
199 | xor(x, data[i:i+aes.BlockSize])
200 | block.Encrypt(t, x)
201 | xor(t, y)
202 | x, y = t, data[i:i+aes.BlockSize]
203 | copy(encrypted[i:], t)
204 | i += aes.BlockSize
205 | }
206 |
207 | return encrypted, nil
208 | }
209 |
210 | func doAES256IGEdecrypt(data, key, iv []byte) ([]byte, error) {
211 | block, err := aes.NewCipher(key)
212 | if err != nil {
213 | return nil, err
214 | }
215 | if len(data) < aes.BlockSize {
216 | return nil, errors.New("AES256IGE: Data too small to decrypt")
217 | }
218 | if len(data)%aes.BlockSize != 0 {
219 | return nil, errors.New("AES256IGE: Data not divisible by block Size")
220 | }
221 |
222 | t := make([]byte, aes.BlockSize)
223 | x := make([]byte, aes.BlockSize)
224 | y := make([]byte, aes.BlockSize)
225 | copy(x, iv[:aes.BlockSize])
226 | copy(y, iv[aes.BlockSize:])
227 | decrypted := make([]byte, len(data))
228 |
229 | i := 0
230 | for i < len(data) {
231 | xor(y, data[i:i+aes.BlockSize])
232 | block.Decrypt(t, y)
233 | xor(t, x)
234 | y, x = t, data[i:i+aes.BlockSize]
235 | copy(decrypted[i:], t)
236 | i += aes.BlockSize
237 | }
238 |
239 | return decrypted, nil
240 |
241 | }
242 |
243 | func xor(dst, src []byte) {
244 | for i := range dst {
245 | dst[i] = dst[i] ^ src[i]
246 | }
247 | }
248 |
--------------------------------------------------------------------------------
/messages.go:
--------------------------------------------------------------------------------
1 | package mtproto
2 |
3 | func (m *MTProto) MessagesGetHistory(peer TL, offsetId, offsetDate, addOffset, limit, maxId, minId int32) (*TL, error) {
4 | return m.InvokeSync(TL_messages_getHistory{
5 | Peer: peer,
6 | Offset_id: offsetId,
7 | Offset_date: offsetDate,
8 | Add_offset: addOffset,
9 | Limit: limit,
10 | Max_id: maxId,
11 | Min_id: minId,
12 | })
13 | }
14 |
15 | func (m *MTProto) MessagesGetDialogs(excludePinned bool, offsetDate, offsetId int32, offsetPeer TL, limit int32) (*TL, error) {
16 | return m.InvokeSync(TL_messages_getDialogs{
17 | Exclude_pinned: excludePinned,
18 | Offset_date: offsetDate,
19 | Offset_id: offsetId,
20 | Offset_peer: offsetPeer,
21 | Limit: limit,
22 | })
23 | }
24 |
25 | func (m *MTProto) MessagesSendMessage(no_webpage, silent, background, clear_draft bool, peer TL, reply_to_msg_id int32, message string, random_id int64, reply_markup TL, entities []TL) (*TL, error) {
26 | return m.InvokeSync(TL_messages_sendMessage{
27 | No_webpage: no_webpage,
28 | Silent: silent,
29 | Background: background,
30 | Clear_draft: clear_draft,
31 | Peer: peer,
32 | Reply_to_msg_id: reply_to_msg_id,
33 | Message: message,
34 | Random_id: random_id,
35 | Reply_markup: reply_markup,
36 | Entities: entities,
37 | })
38 | }
39 |
--------------------------------------------------------------------------------
/mtproto.go:
--------------------------------------------------------------------------------
1 | package mtproto
2 |
3 | import (
4 | "fmt"
5 | "io"
6 | "log"
7 | "os"
8 | "runtime"
9 | "sync"
10 | "time"
11 | )
12 |
13 | type MTProto struct {
14 | queueSend chan packetToSend
15 | stopRoutines chan struct{}
16 | allDone sync.WaitGroup
17 |
18 | network INetwork
19 |
20 | IPv6 bool
21 | authkeyfile string
22 | id int32
23 | hash string
24 | version string
25 | device string
26 | system string
27 | language string
28 |
29 | dclist map[int32]string
30 | }
31 |
32 | type packetToSend struct {
33 | msg TL
34 | resp chan response
35 | }
36 |
37 | type response struct {
38 | data TL
39 | err error
40 | }
41 |
42 | type Option func(*options)
43 |
44 | type options struct {
45 | Version string
46 | DeviceModel string
47 | SystemVersion string
48 | Language string
49 | IPv6 bool
50 | AuthkeyFile string
51 | ServerAddress string
52 | NewSession bool
53 | }
54 |
55 | func WithVersion(version string) Option {
56 | return func(opts *options) {
57 | opts.Version = version
58 | }
59 | }
60 |
61 | func WithDevice(device string) Option {
62 | return func(opts *options) {
63 | opts.DeviceModel = device
64 | }
65 | }
66 |
67 | func WithSystem(system string) Option {
68 | return func(opts *options) {
69 | opts.SystemVersion = system
70 | }
71 | }
72 |
73 | func WithLanguage(language string) Option {
74 | return func(opts *options) {
75 | opts.Language = language
76 | }
77 | }
78 |
79 | func WithServer(server string, ipv6 bool) Option {
80 | return func(opts *options) {
81 | opts.ServerAddress = server
82 | opts.IPv6 = ipv6
83 | }
84 | }
85 |
86 | func WithAuthFile(authfile string, newSession bool) Option {
87 | return func(opts *options) {
88 | opts.AuthkeyFile = authfile
89 | opts.NewSession = newSession
90 | }
91 | }
92 |
93 | var defaultOptions = options{
94 | DeviceModel: "Unknown",
95 | SystemVersion: runtime.GOOS + "/" + runtime.GOARCH,
96 | Language: "en",
97 | IPv6: false,
98 | AuthkeyFile: os.Getenv("HOME") + "/mtproto.auth",
99 | ServerAddress: "149.154.167.50:443",
100 | Version: "0.0.1",
101 | NewSession: false,
102 | }
103 |
104 | // API Errors
105 | const (
106 | errorSeeOther = 303
107 | errorBadRequest = 400
108 | errorUnauthorized = 401
109 | errorForbidden = 403
110 | errorNotFound = 404
111 | errorFlood = 420
112 | errorInternal = 500
113 | )
114 |
115 | // Current API Layer Version
116 | const layer = 65
117 |
118 | func NewMTProto(id int32, hash string, opts ...Option) (*MTProto, error) {
119 | var err error
120 |
121 | if id == 0 {
122 | return nil, fmt.Errorf("can't initialize mtproto: wrong application id")
123 | }
124 |
125 | if len(hash) == 0 {
126 | return nil, fmt.Errorf("can't initialize mtpoto: wrong application hash")
127 | }
128 |
129 | configuration := defaultOptions
130 | for _, option := range opts {
131 | option(&configuration)
132 | }
133 |
134 | m := new(MTProto)
135 |
136 | m.queueSend = make(chan packetToSend, 64)
137 | m.stopRoutines = make(chan struct{})
138 | m.allDone = sync.WaitGroup{}
139 |
140 | m.id = id
141 | m.hash = hash
142 | m.version = configuration.Version
143 | m.device = configuration.DeviceModel
144 | m.system = configuration.SystemVersion
145 | m.language = configuration.Language
146 | m.authkeyfile = configuration.AuthkeyFile
147 | m.IPv6 = configuration.IPv6
148 |
149 | if m.network, err = NewNetwork(configuration.NewSession, m.authkeyfile, m.queueSend, configuration.ServerAddress, m.IPv6); err != nil {
150 | return nil, err
151 | }
152 |
153 | return m, nil
154 | }
155 |
156 | func (m *MTProto) Connect() (err error) {
157 | m.network.Connect()
158 |
159 | // start goroutines
160 | go m.sendRoutine()
161 | go m.readRoutine()
162 |
163 | var data *TL
164 |
165 | // (help_getConfig)
166 | if data, err = m.InvokeSync(TL_invokeWithLayer{
167 | Layer: layer,
168 | Query: TL_initConnection{
169 | Api_id: m.id,
170 | Device_model: m.device,
171 | System_version: m.system,
172 | App_version: m.version,
173 | Lang_code: m.language,
174 | Query: TL_help_getConfig{},
175 | },
176 | }); err != nil {
177 | return
178 | }
179 |
180 | switch (*data).(type) {
181 | case TL_config:
182 | m.dclist = make(map[int32]string, 5)
183 | for _, v := range (*data).(TL_config).Dc_options {
184 | v := v.(TL_dcOption)
185 | if m.IPv6 && v.Ipv6 {
186 | m.dclist[v.Id] = fmt.Sprintf("[%s]:%d", v.Ip_address, v.Port)
187 | } else if !v.Ipv6 {
188 | m.dclist[v.Id] = fmt.Sprintf("%s:%d", v.Ip_address, v.Port)
189 | }
190 | }
191 | default:
192 | err = fmt.Errorf("Connection error: got: %T", data)
193 | }
194 |
195 | // start keep alive ping
196 | go m.pingRoutine()
197 |
198 | return
199 | }
200 |
201 | func (m *MTProto) Disconnect() error {
202 | // stop ping, send and read routine by closing channel stopRoutines
203 | close(m.stopRoutines)
204 |
205 | // Wait until all goroutines stopped
206 | m.allDone.Wait()
207 |
208 | // close send queue
209 | close(m.queueSend)
210 |
211 | return m.network.Disconnect()
212 | }
213 |
214 | func (m *MTProto) reconnect(newaddr string) error {
215 | err := m.Disconnect()
216 | if err != nil {
217 | return err
218 | }
219 |
220 | // renew connection
221 | if newaddr != m.network.Address() {
222 | m.network, err = NewNetwork(true, m.authkeyfile, m.queueSend, newaddr, m.IPv6)
223 | }
224 |
225 | err = m.Connect()
226 | return err
227 | }
228 |
229 | func (m *MTProto) pingRoutine() {
230 | m.allDone.Add(1)
231 | defer func() { m.allDone.Done() }()
232 | for {
233 | select {
234 | case <-m.stopRoutines:
235 | return
236 | case <-time.After(60 * time.Second):
237 | // TODO: m.InvokeSync()?
238 | m.InvokeAsync(TL_ping{0xCADACAD})
239 | }
240 | }
241 | }
242 |
243 | func (m *MTProto) sendRoutine() {
244 | m.allDone.Add(1)
245 | defer func() { m.allDone.Done() }()
246 | for {
247 | select {
248 | case <-m.stopRoutines:
249 | return
250 | case x := <-m.queueSend:
251 | err := m.network.Send(x.msg, x.resp)
252 | if err != nil {
253 | log.Fatalln("SendRoutine:", err)
254 | }
255 | }
256 | }
257 | }
258 |
259 | func (m *MTProto) readRoutine() {
260 | m.allDone.Add(1)
261 | defer func() { m.allDone.Done() }()
262 | for {
263 | // Run async wait for data from server
264 | ch := make(chan interface{}, 1)
265 | go func(ch chan<- interface{}) {
266 | data, err := m.network.Read()
267 | if err == io.EOF {
268 | // TODO: Last message to the server was lost. Fix it.
269 | // Connection closed by server, trying to reconnect
270 | err = m.reconnect(m.network.Address())
271 | if err != nil {
272 | log.Fatalln("ReadRoutine: ", err)
273 | }
274 | }
275 | if err != nil {
276 | log.Fatalln("ReadRoutine: ", err)
277 | }
278 | ch <- data
279 | }(ch)
280 |
281 | select {
282 | case <-m.stopRoutines:
283 | return
284 | case data := <-ch:
285 | if data == nil {
286 | return
287 | }
288 | m.network.Process(data)
289 | }
290 | }
291 | }
292 |
293 | func (m *MTProto) InvokeSync(msg TL) (*TL, error) {
294 | x := <-m.InvokeAsync(msg)
295 |
296 | if x.err != nil {
297 | if err, ok := x.err.(TL_rpc_error); ok {
298 | switch err.Error_code {
299 | case errorSeeOther:
300 | var newDc int32
301 | n, _ := fmt.Sscanf(err.Error_message, "PHONE_MIGRATE_%d", &newDc)
302 | if n != 1 {
303 | n, _ := fmt.Sscanf(err.Error_message, "NETWORK_MIGRATE_%d", &newDc)
304 | if n != 1 {
305 | return nil, fmt.Errorf("RPC error_string: %s", err.Error_message)
306 | }
307 | }
308 | newDcAddr, ok := m.dclist[newDc]
309 | if !ok {
310 | return nil, fmt.Errorf("wrong DC index: %d", newDc)
311 | }
312 | err := m.reconnect(newDcAddr)
313 | if err != nil {
314 | return nil, err
315 | }
316 | default:
317 | return nil, x.err
318 | }
319 | }
320 |
321 | return nil, x.err
322 | }
323 |
324 | return &x.data, nil
325 | }
326 |
327 | func (m *MTProto) InvokeAsync(msg TL) chan response {
328 | resp := make(chan response, 1)
329 | m.queueSend <- packetToSend{
330 | msg: msg,
331 | resp: resp,
332 | }
333 | return resp
334 | }
335 |
--------------------------------------------------------------------------------
/network.go:
--------------------------------------------------------------------------------
1 | package mtproto
2 |
3 | import (
4 | "bytes"
5 | "encoding/binary"
6 | "errors"
7 | "os"
8 | "fmt"
9 | "net"
10 | "sync"
11 | "time"
12 | )
13 |
14 | // low-level communication with telegram server
15 | type INetwork interface {
16 | Connect() error
17 | Disconnect() error
18 |
19 | Send(msg TL, resp chan response) error
20 | Read() (interface{}, error)
21 | Process(data interface{}) interface{}
22 |
23 | Address() string
24 | }
25 |
26 | type Network struct {
27 | session ISession
28 |
29 | useIPv6 bool
30 | address string
31 |
32 | conn *net.TCPConn
33 |
34 | mutex *sync.Mutex
35 | msgsIdToAck map[int64]packetToSend
36 | msgsIdToResp map[int64]chan response
37 |
38 | queueSend chan packetToSend
39 | lastSeqNo int32
40 | seqNo int32
41 | msgId int64
42 | }
43 |
44 | func NewNetwork(newSession bool, authkeyfile string, queueSend chan packetToSend, address string, useIPv6 bool) (INetwork, error) {
45 | nw := new(Network)
46 |
47 | nw.queueSend = queueSend
48 | nw.msgsIdToAck = make(map[int64]packetToSend)
49 | nw.msgsIdToResp = make(map[int64]chan response)
50 | nw.mutex = &sync.Mutex{}
51 |
52 | nw.useIPv6 = useIPv6
53 | nw.address = address
54 |
55 | var err error
56 | if newSession {
57 | err = nw.CreateSession(authkeyfile)
58 | } else {
59 | err = nw.LoadSession(authkeyfile)
60 | }
61 |
62 | if err != nil {
63 | return nil, err
64 | }
65 |
66 | return nw, nil
67 | }
68 |
69 | // Accepts path to file where to store session key
70 | func (nw *Network) CreateSession(session string) error {
71 | file, err := os.OpenFile(session, os.O_RDWR|os.O_CREATE, 0600)
72 | if err != nil {
73 | return err
74 | }
75 |
76 | nw.session = NewSession(file)
77 | nw.session.SetAddress(nw.address)
78 | nw.session.UseIPv6(nw.useIPv6)
79 | nw.session.Encrypted(false)
80 |
81 | return nil
82 | }
83 |
84 | // Accepts path to file where to store session key
85 | func (nw *Network) LoadSession(session string) error {
86 | file, err := os.OpenFile(session, os.O_RDWR|os.O_CREATE, 0600)
87 | if err != nil {
88 | return err
89 | }
90 |
91 | nw.session = NewSession(file)
92 | if err = nw.session.Load(); err != nil {
93 | return nw.CreateSession(session)
94 | }
95 |
96 | nw.session.Encrypted(true)
97 |
98 | return nil
99 | }
100 |
101 | func (nw *Network) Connect() error {
102 | var err error
103 | var tcpAddr *net.TCPAddr
104 |
105 | // connect
106 | tcpAddr, err = net.ResolveTCPAddr("tcp", nw.session.GetAddress())
107 | if err != nil {
108 | return err
109 | }
110 | nw.conn, err = net.DialTCP("tcp", nil, tcpAddr)
111 | if err != nil {
112 | return err
113 | }
114 | // Packet Length is encoded by a single byte (see: https://core.telegram.org/mtproto)
115 | _, err = nw.conn.Write([]byte{0xef})
116 | if err != nil {
117 | return err
118 | }
119 | // get new authKey if need
120 | if !nw.session.IsEncrypted() {
121 | err = nw.makeAuthKey()
122 | if err != nil {
123 | return err
124 | }
125 | }
126 |
127 | return nil
128 | }
129 |
130 | func (nw *Network) Disconnect() error {
131 |
132 | return nw.conn.Close()
133 | }
134 |
135 | func (nw *Network) Send(msg TL, resp chan response) error {
136 | obj := msg.encode()
137 |
138 | x := NewEncodeBuf(256)
139 |
140 | // padding for tcpsize
141 | x.Int(0)
142 |
143 | if nw.session.IsEncrypted() {
144 | needAck := true
145 | switch msg.(type) {
146 | case TL_ping, TL_msgs_ack:
147 | needAck = false
148 | }
149 | z := NewEncodeBuf(256)
150 | newMsgId := GenerateMessageId()
151 | z.Bytes(nw.session.GetServerSalt())
152 | z.Long(nw.session.GetSessionID())
153 | z.Long(newMsgId)
154 | if needAck {
155 | z.Int(nw.lastSeqNo | 1)
156 | } else {
157 | z.Int(nw.lastSeqNo)
158 | }
159 | z.Int(int32(len(obj)))
160 | z.Bytes(obj)
161 |
162 | msgKey := sha1(z.buf)[4:20]
163 | aesKey, aesIV := generateAES(msgKey, nw.session.GetAuthKey(), false)
164 |
165 | y := make([]byte, len(z.buf)+((16-(len(obj)%16))&15))
166 | copy(y, z.buf)
167 | encryptedData, err := doAES256IGEencrypt(y, aesKey, aesIV)
168 | if err != nil {
169 | return err
170 | }
171 |
172 | nw.lastSeqNo += 2
173 | if needAck {
174 | nw.mutex.Lock()
175 | nw.msgsIdToAck[newMsgId] = packetToSend{msg, resp}
176 | nw.mutex.Unlock()
177 | }
178 |
179 | x.Bytes(nw.session.GetAuthKeyHash())
180 | x.Bytes(msgKey)
181 | x.Bytes(encryptedData)
182 |
183 | if resp != nil {
184 | nw.mutex.Lock()
185 | nw.msgsIdToResp[newMsgId] = resp
186 | nw.mutex.Unlock()
187 | }
188 |
189 | } else {
190 | x.Long(0)
191 | x.Long(GenerateMessageId())
192 | x.Int(int32(len(obj)))
193 | x.Bytes(obj)
194 |
195 | }
196 |
197 | // minus padding
198 | size := len(x.buf)/4 - 1
199 |
200 | if size < 127 {
201 | x.buf[3] = byte(size)
202 | x.buf = x.buf[3:]
203 | } else {
204 | binary.LittleEndian.PutUint32(x.buf, uint32(size<<8|127))
205 | }
206 | _, err := nw.conn.Write(x.buf)
207 | if err != nil {
208 | return err
209 | }
210 |
211 | return nil
212 | }
213 |
214 | func (nw *Network) Read() (interface{}, error) {
215 | var err error
216 | var n int
217 | var size int
218 | var data interface{}
219 |
220 | err = nw.conn.SetReadDeadline(time.Now().Add(300 * time.Second))
221 | if err != nil {
222 | return nil, err
223 | }
224 | b := make([]byte, 1)
225 | n, err = nw.conn.Read(b)
226 | if err != nil {
227 | return nil, err
228 | }
229 |
230 | if b[0] < 127 {
231 | size = int(b[0]) << 2
232 | } else {
233 | b := make([]byte, 3)
234 | n, err = nw.conn.Read(b)
235 | if err != nil {
236 | return nil, err
237 | }
238 | size = (int(b[0]) | int(b[1])<<8 | int(b[2])<<16) << 2
239 | }
240 |
241 | left := size
242 | buf := make([]byte, size)
243 | for left > 0 {
244 | n, err = nw.conn.Read(buf[size-left:])
245 | if err != nil {
246 | return nil, err
247 | }
248 | left -= n
249 | }
250 |
251 | if size == 4 {
252 | return nil, fmt.Errorf("Server response error: %d", int32(binary.LittleEndian.Uint32(buf)))
253 | }
254 |
255 | dbuf := NewDecodeBuf(buf)
256 |
257 | authKeyHash := dbuf.Bytes(8)
258 | if binary.LittleEndian.Uint64(authKeyHash) == 0 {
259 | nw.msgId = dbuf.Long()
260 | messageLen := dbuf.Int()
261 | if int(messageLen) != dbuf.size-20 {
262 | return nil, fmt.Errorf("Message len: %d (need %d)", messageLen, dbuf.size-20)
263 | }
264 | nw.seqNo = 0
265 |
266 | data = dbuf.Object()
267 | if dbuf.err != nil {
268 | return nil, dbuf.err
269 | }
270 |
271 | } else {
272 | msgKey := dbuf.Bytes(16)
273 | encryptedData := dbuf.Bytes(dbuf.size - 24)
274 | aesKey, aesIV := generateAES(msgKey, nw.session.GetAuthKey(), true)
275 | x, err := doAES256IGEdecrypt(encryptedData, aesKey, aesIV)
276 | if err != nil {
277 | return nil, err
278 | }
279 | dbuf = NewDecodeBuf(x)
280 | _ = dbuf.Long() // salt
281 | _ = dbuf.Long() // session_id
282 | nw.msgId = dbuf.Long()
283 | nw.seqNo = dbuf.Int()
284 | messageLen := dbuf.Int()
285 | if int(messageLen) > dbuf.size-32 {
286 | return nil, fmt.Errorf("Message len: %d (need less than %d)", messageLen, dbuf.size-32)
287 | }
288 | if !bytes.Equal(sha1(dbuf.buf[0 : 32+messageLen])[4:20], msgKey) {
289 | return nil, errors.New("Wrong msg_key")
290 | }
291 |
292 | data = dbuf.Object()
293 | if dbuf.err != nil {
294 | return nil, dbuf.err
295 | }
296 |
297 | }
298 | mod := nw.msgId & 3
299 | if mod != 1 && mod != 3 {
300 | return nil, fmt.Errorf("Wrong bits of message_id: %d", mod)
301 | }
302 |
303 | return data, nil
304 | }
305 |
306 | func (nw *Network) makeAuthKey() error {
307 | var x []byte
308 | var err error
309 | var data interface{}
310 |
311 | // (send) req_pq
312 | nonceFirst := GenerateNonce(16)
313 | err = nw.Send(TL_req_pq{nonceFirst}, nil)
314 | if err != nil {
315 | return err
316 | }
317 |
318 | // (parse) resPQ
319 | data, err = nw.Read()
320 | if err != nil {
321 | return err
322 | }
323 | res, ok := data.(TL_resPQ)
324 | if !ok {
325 | return errors.New("Handshake: Need resPQ")
326 | }
327 | if !bytes.Equal(nonceFirst, res.Nonce) {
328 | return errors.New("Handshake: Wrong Nonce")
329 | }
330 | found := false
331 | for _, b := range res.Fingerprints {
332 | if uint64(b) == telegramPublicKey_FP {
333 | found = true
334 | break
335 | }
336 | }
337 | if !found {
338 | return errors.New("Handshake: No fingerprint")
339 | }
340 |
341 | // (encoding) p_q_inner_data
342 | p, q := splitPQ(res.Pq)
343 | nonceSecond := GenerateNonce(32)
344 | nonceServer := res.Server_nonce
345 | innerData1 := (TL_p_q_inner_data{res.Pq, p, q, nonceFirst, nonceServer, nonceSecond}).encode()
346 |
347 | x = make([]byte, 255)
348 | copy(x[0:], sha1(innerData1))
349 | copy(x[20:], innerData1)
350 | encryptedData1 := doRSAencrypt(x)
351 | // (send) req_DH_params
352 | err = nw.Send(TL_req_DH_params{nonceFirst, nonceServer, p, q, telegramPublicKey_FP, encryptedData1}, nil)
353 | if err != nil {
354 | return err
355 | }
356 |
357 | // (parse) server_DH_params_{ok, fail}
358 | data, err = nw.Read()
359 | if err != nil {
360 | return err
361 | }
362 | dh, ok := data.(TL_server_DH_params_ok)
363 | if !ok {
364 | return errors.New("Handshake: Need server_DH_params_ok")
365 | }
366 | if !bytes.Equal(nonceFirst, dh.Nonce) {
367 | return errors.New("Handshake: Wrong Nonce")
368 | }
369 | if !bytes.Equal(nonceServer, dh.Server_nonce) {
370 | return errors.New("Handshake: Wrong Server_nonce")
371 | }
372 | t1 := make([]byte, 48)
373 | copy(t1[0:], nonceSecond)
374 | copy(t1[32:], nonceServer)
375 | hash1 := sha1(t1)
376 |
377 | t2 := make([]byte, 48)
378 | copy(t2[0:], nonceServer)
379 | copy(t2[16:], nonceSecond)
380 | hash2 := sha1(t2)
381 |
382 | t3 := make([]byte, 64)
383 | copy(t3[0:], nonceSecond)
384 | copy(t3[32:], nonceSecond)
385 | hash3 := sha1(t3)
386 |
387 | tmpAESKey := make([]byte, 32)
388 | tmpAESIV := make([]byte, 32)
389 |
390 | copy(tmpAESKey[0:], hash1)
391 | copy(tmpAESKey[20:], hash2[0:12])
392 |
393 | copy(tmpAESIV[0:], hash2[12:20])
394 | copy(tmpAESIV[8:], hash3)
395 | copy(tmpAESIV[28:], nonceSecond[0:4])
396 |
397 | // (parse-thru) server_DH_inner_data
398 | decodedData, err := doAES256IGEdecrypt(dh.Encrypted_answer, tmpAESKey, tmpAESIV)
399 | if err != nil {
400 | return err
401 | }
402 | innerbuf := NewDecodeBuf(decodedData[20:])
403 | data = innerbuf.Object()
404 | if innerbuf.err != nil {
405 | return innerbuf.err
406 | }
407 | dhi, ok := data.(TL_server_DH_inner_data)
408 | if !ok {
409 | return errors.New("Handshake: Need server_DH_inner_data")
410 | }
411 | if !bytes.Equal(nonceFirst, dhi.Nonce) {
412 | return errors.New("Handshake: Wrong Nonce")
413 | }
414 | if !bytes.Equal(nonceServer, dhi.Server_nonce) {
415 | return errors.New("Handshake: Wrong Server_nonce")
416 | }
417 |
418 | _, g_b, g_ab := makeGAB(dhi.G, dhi.G_a, dhi.Dh_prime)
419 | authKey := g_ab.Bytes()
420 | if authKey[0] == 0 {
421 | authKey = authKey[1:]
422 | }
423 | authKeyHash := sha1(authKey)[12:20]
424 | t4 := make([]byte, 32+1+8)
425 | copy(t4[0:], nonceSecond)
426 | t4[32] = 1
427 | copy(t4[33:], sha1(authKey)[0:8])
428 | nonceHash1 := sha1(t4)[4:20]
429 | serverSalt := make([]byte, 8)
430 | copy(serverSalt, nonceSecond[:8])
431 | xor(serverSalt, nonceServer[:8])
432 |
433 | nw.session.SetAuthKey(authKey)
434 | nw.session.SetAuthKeyHash(authKeyHash)
435 | nw.session.SetServerSalt(serverSalt)
436 |
437 | // (encoding) client_DH_inner_data
438 | innerData2 := (TL_client_DH_inner_data{nonceFirst, nonceServer, 0, g_b}).encode()
439 | x = make([]byte, 20+len(innerData2)+(16-((20+len(innerData2))%16))&15)
440 | copy(x[0:], sha1(innerData2))
441 | copy(x[20:], innerData2)
442 | encryptedData2, err := doAES256IGEencrypt(x, tmpAESKey, tmpAESIV)
443 |
444 | // (send) set_client_DH_params
445 | err = nw.Send(TL_set_client_DH_params{nonceFirst, nonceServer, encryptedData2}, nil)
446 | if err != nil {
447 | return err
448 | }
449 |
450 | // (parse) dh_gen_{ok, Retry, fail}
451 | data, err = nw.Read()
452 | if err != nil {
453 | return err
454 | }
455 | dhg, ok := data.(TL_dh_gen_ok)
456 | if !ok {
457 | return errors.New("Handshake: Need dh_gen_ok")
458 | }
459 | if !bytes.Equal(nonceFirst, dhg.Nonce) {
460 | return errors.New("Handshake: Wrong Nonce")
461 | }
462 | if !bytes.Equal(nonceServer, dhg.Server_nonce) {
463 | return errors.New("Handshake: Wrong Server_nonce")
464 | }
465 | if !bytes.Equal(nonceHash1, dhg.New_nonce_hash1) {
466 | return errors.New("Handshake: Wrong New_nonce_hash1")
467 | }
468 |
469 | // (all ok)
470 | err = nw.session.Save()
471 | if err != nil {
472 | return err
473 | }
474 | nw.session.Encrypted(true)
475 |
476 | return nil
477 | }
478 |
479 | func (nw *Network) Process(data interface{}) interface{} {
480 | return nw.process(nw.msgId, nw.seqNo, data)
481 | }
482 |
483 | func (nw *Network) process(msgId int64, seqNo int32, data interface{}) interface{} {
484 | switch data.(type) {
485 | case TL_msg_container:
486 | data := data.(TL_msg_container).Items
487 | for _, v := range data {
488 | nw.process(v.Msg_id, v.Seq_no, v.Data)
489 | }
490 |
491 | case TL_bad_server_salt:
492 | data := data.(TL_bad_server_salt)
493 | nw.session.SetServerSalt(data.New_server_salt)
494 | _ = nw.session.Save()
495 | nw.mutex.Lock()
496 | defer nw.mutex.Unlock()
497 | for k, v := range nw.msgsIdToAck {
498 | delete(nw.msgsIdToAck, k)
499 | nw.queueSend <- v
500 | }
501 |
502 | case TL_new_session_created:
503 | data := data.(TL_new_session_created)
504 | nw.session.SetServerSalt(data.Server_salt)
505 | _ = nw.session.Save()
506 |
507 | case TL_ping:
508 | data := data.(TL_ping)
509 | nw.queueSend <- packetToSend{TL_pong{msgId, data.Ping_id}, nil}
510 |
511 | case TL_pong:
512 | // ignore
513 |
514 | case TL_msgs_ack:
515 | data := data.(TL_msgs_ack)
516 | nw.mutex.Lock()
517 | defer nw.mutex.Unlock()
518 | for _, v := range data.MsgIds {
519 | delete(nw.msgsIdToAck, v)
520 | }
521 |
522 | case TL_rpc_result:
523 | data := data.(TL_rpc_result)
524 | x := nw.process(msgId, seqNo, data.Obj)
525 | nw.mutex.Lock()
526 | defer nw.mutex.Unlock()
527 | if v, ok := nw.msgsIdToResp[data.Req_msg_id]; ok {
528 | var resp response
529 | if rpcError, ok := x.(TL_rpc_error); ok {
530 | resp.err = rpcError
531 | }
532 | resp.data = x.(TL)
533 | v <- resp
534 |
535 | close(v)
536 | }
537 | delete(nw.msgsIdToAck, data.Req_msg_id)
538 | default:
539 | return data
540 | }
541 |
542 | // TODO: Check why I should do this
543 | if (seqNo & 1) == 1 {
544 | nw.queueSend <- packetToSend{TL_msgs_ack{[]int64{msgId}}, nil}
545 | }
546 |
547 | return nil
548 | }
549 |
550 | func (nw Network) Address() string {
551 | return nw.address
552 | }
--------------------------------------------------------------------------------
/schemes/api-layer-65.tl:
--------------------------------------------------------------------------------
1 | // Core types (no need to gen)
2 |
3 | //vector#1cb5c415 {t:Type} # [ t ] = Vector t;
4 |
5 | ///////////////////////////////
6 | /////////////////// Layer cons
7 | ///////////////////////////////
8 |
9 | //invokeAfterMsg#cb9f372d msg_id:long query:!X = X;
10 | //invokeAfterMsgs#3dc4b4f0 msg_ids:Vector query:!X = X;
11 | //invokeWithLayer1#53835315 query:!X = X;
12 | //invokeWithLayer2#289dd1f6 query:!X = X;
13 | //invokeWithLayer3#b7475268 query:!X = X;
14 | //invokeWithLayer4#dea0d430 query:!X = X;
15 | //invokeWithLayer5#417a57ae query:!X = X;
16 | //invokeWithLayer6#3a64d54d query:!X = X;
17 | //invokeWithLayer7#a5be56d3 query:!X = X;
18 | //invokeWithLayer8#e9abd9fd query:!X = X;
19 | //invokeWithLayer9#76715a63 query:!X = X;
20 | //invokeWithLayer10#39620c41 query:!X = X;
21 | //invokeWithLayer11#a6b88fdf query:!X = X;
22 | //invokeWithLayer12#dda60d3c query:!X = X;
23 | //invokeWithLayer13#427c8ea2 query:!X = X;
24 | //invokeWithLayer14#2b9b08fa query:!X = X;
25 | //invokeWithLayer15#b4418b64 query:!X = X;
26 | //invokeWithLayer16#cf5f0987 query:!X = X;
27 | //invokeWithLayer17#50858a19 query:!X = X;
28 | //invokeWithLayer18#1c900537 query:!X = X;
29 | //invokeWithLayer#da9b0d0d layer:int query:!X = X; // after 18 layer
30 |
31 | ///////////////////////////////
32 | /// Authorization key creation
33 | ///////////////////////////////
34 |
35 | resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector = ResPQ;
36 |
37 | p_q_inner_data#83c95aec pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 = P_Q_inner_data;
38 |
39 | server_DH_params_fail#79cb045d nonce:int128 server_nonce:int128 new_nonce_hash:int128 = Server_DH_Params;
40 | server_DH_params_ok#d0e8075c nonce:int128 server_nonce:int128 encrypted_answer:string = Server_DH_Params;
41 |
42 | server_DH_inner_data#b5890dba nonce:int128 server_nonce:int128 g:int dh_prime:string g_a:string server_time:int = Server_DH_inner_data;
43 |
44 | client_DH_inner_data#6643b654 nonce:int128 server_nonce:int128 retry_id:long g_b:string = Client_DH_Inner_Data;
45 |
46 | dh_gen_ok#3bcbf734 nonce:int128 server_nonce:int128 new_nonce_hash1:int128 = Set_client_DH_params_answer;
47 | dh_gen_retry#46dc1fb9 nonce:int128 server_nonce:int128 new_nonce_hash2:int128 = Set_client_DH_params_answer;
48 | dh_gen_fail#a69dae02 nonce:int128 server_nonce:int128 new_nonce_hash3:int128 = Set_client_DH_params_answer;
49 |
50 | destroy_auth_key_ok#f660e1d4 = DestroyAuthKeyRes;
51 | destroy_auth_key_none#0a9f2259 = DestroyAuthKeyRes;
52 | destroy_auth_key_fail#ea109b13 = DestroyAuthKeyRes;
53 |
54 | ---functions---
55 |
56 | req_pq#60469778 nonce:int128 = ResPQ;
57 |
58 | req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:string q:string public_key_fingerprint:long encrypted_data:string = Server_DH_Params;
59 |
60 | set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:string = Set_client_DH_params_answer;
61 |
62 | destroy_auth_key#d1435160 = DestroyAuthKeyRes;
63 |
64 | ///////////////////////////////
65 | ////////////// System messages
66 | ///////////////////////////////
67 |
68 | ---types---
69 |
70 | msgs_ack#62d6b459 msg_ids:Vector = MsgsAck;
71 |
72 | bad_msg_notification#a7eff811 bad_msg_id:long bad_msg_seqno:int error_code:int = BadMsgNotification;
73 | bad_server_salt#edab447b bad_msg_id:long bad_msg_seqno:int error_code:int new_server_salt:long = BadMsgNotification;
74 |
75 | msgs_state_req#da69fb52 msg_ids:Vector = MsgsStateReq;
76 | msgs_state_info#04deb57d req_msg_id:long info:string = MsgsStateInfo;
77 | msgs_all_info#8cc0d131 msg_ids:Vector info:string = MsgsAllInfo;
78 |
79 | msg_detailed_info#276d3ec6 msg_id:long answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
80 | msg_new_detailed_info#809db6df answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
81 |
82 | msg_resend_req#7d861a08 msg_ids:Vector = MsgResendReq;
83 |
84 | //rpc_result#f35c6d01 req_msg_id:long result:Object = RpcResult; // parsed manually
85 |
86 | rpc_error#2144ca19 error_code:int error_message:string = RpcError;
87 |
88 | rpc_answer_unknown#5e2ad36e = RpcDropAnswer;
89 | rpc_answer_dropped_running#cd78e586 = RpcDropAnswer;
90 | rpc_answer_dropped#a43ad8b7 msg_id:long seq_no:int bytes:int = RpcDropAnswer;
91 |
92 | future_salt#0949d9dc valid_since:int valid_until:int salt:long = FutureSalt;
93 | future_salts#ae500895 req_msg_id:long now:int salts:vector = FutureSalts;
94 |
95 | pong#347773c5 msg_id:long ping_id:long = Pong;
96 |
97 | destroy_session_ok#e22045fc session_id:long = DestroySessionRes;
98 | destroy_session_none#62d350c9 session_id:long = DestroySessionRes;
99 |
100 | new_session_created#9ec20908 first_msg_id:long unique_id:long server_salt:long = NewSession;
101 |
102 | //message msg_id:long seqno:int bytes:int body:Object = Message; // parsed manually
103 | //msg_container#73f1f8dc messages:vector = MessageContainer; // parsed manually
104 | //msg_copy#e06046b2 orig_message:Message = MessageCopy; // parsed manually, not used - use msg_container
105 | //gzip_packed#3072cfa1 packed_data:string = Object; // parsed manually
106 |
107 | http_wait#9299359f max_delay:int wait_after:int max_wait:int = HttpWait;
108 |
109 | ---functions---
110 |
111 | rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer;
112 |
113 | get_future_salts#b921bd04 num:int = FutureSalts;
114 |
115 | ping#7abe77ec ping_id:long = Pong;
116 | ping_delay_disconnect#f3427b8c ping_id:long disconnect_delay:int = Pong;
117 |
118 | destroy_session#e7512126 session_id:long = DestroySessionRes;
119 |
120 | contest.saveDeveloperInfo#9a5f6e95 vk_id:int name:string phone_number:string age:int city:string = Bool;
121 |
122 | ///////////////////////////////
123 | ///////// Main application API
124 | ///////////////////////////////
125 |
126 | ---types---
127 |
128 | boolFalse#bc799737 = Bool;
129 | boolTrue#997275b5 = Bool;
130 |
131 | true#3fedd339 = True;
132 |
133 | vector#1cb5c415 {t:Type} # [ t ] = Vector t;
134 |
135 | error#c4b9f9bb code:int text:string = Error;
136 |
137 | null#56730bcc = Null;
138 |
139 | inputPeerEmpty#7f3b18ea = InputPeer;
140 | inputPeerSelf#7da07ec9 = InputPeer;
141 | inputPeerChat#179be863 chat_id:int = InputPeer;
142 | inputPeerUser#7b8e7de6 user_id:int access_hash:long = InputPeer;
143 | inputPeerChannel#20adaef8 channel_id:int access_hash:long = InputPeer;
144 |
145 | inputUserEmpty#b98886cf = InputUser;
146 | inputUserSelf#f7c1b13f = InputUser;
147 | inputUser#d8292816 user_id:int access_hash:long = InputUser;
148 |
149 | inputPhoneContact#f392b7f4 client_id:long phone:string first_name:string last_name:string = InputContact;
150 |
151 | inputFile#f52ff27f id:long parts:int name:string md5_checksum:string = InputFile;
152 | inputFileBig#fa4f0bb5 id:long parts:int name:string = InputFile;
153 |
154 | inputMediaEmpty#9664f57f = InputMedia;
155 | inputMediaUploadedPhoto#630c9af1 flags:# file:InputFile caption:string stickers:flags.0?Vector = InputMedia;
156 | inputMediaPhoto#e9bfb4f3 id:InputPhoto caption:string = InputMedia;
157 | inputMediaGeoPoint#f9c44144 geo_point:InputGeoPoint = InputMedia;
158 | inputMediaContact#a6e45987 phone_number:string first_name:string last_name:string = InputMedia;
159 | inputMediaUploadedDocument#d070f1e9 flags:# file:InputFile mime_type:string attributes:Vector caption:string stickers:flags.0?Vector = InputMedia;
160 | inputMediaUploadedThumbDocument#50d88cae flags:# file:InputFile thumb:InputFile mime_type:string attributes:Vector caption:string stickers:flags.0?Vector = InputMedia;
161 | inputMediaDocument#1a77f29c id:InputDocument caption:string = InputMedia;
162 | inputMediaVenue#2827a81a geo_point:InputGeoPoint title:string address:string provider:string venue_id:string = InputMedia;
163 | inputMediaGifExternal#4843b0fd url:string q:string = InputMedia;
164 | inputMediaPhotoExternal#b55f4f18 url:string caption:string = InputMedia;
165 | inputMediaDocumentExternal#e5e9607c url:string caption:string = InputMedia;
166 | inputMediaGame#d33f43f3 id:InputGame = InputMedia;
167 | inputMediaInvoice#92153685 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string start_param:string = InputMedia;
168 |
169 | inputChatPhotoEmpty#1ca48f57 = InputChatPhoto;
170 | inputChatUploadedPhoto#927c55b4 file:InputFile = InputChatPhoto;
171 | inputChatPhoto#8953ad37 id:InputPhoto = InputChatPhoto;
172 |
173 | inputGeoPointEmpty#e4c123d6 = InputGeoPoint;
174 | inputGeoPoint#f3b7acc9 lat:double long:double = InputGeoPoint;
175 |
176 | inputPhotoEmpty#1cd7bf0d = InputPhoto;
177 | inputPhoto#fb95c6c4 id:long access_hash:long = InputPhoto;
178 |
179 | inputFileLocation#14637196 volume_id:long local_id:int secret:long = InputFileLocation;
180 | inputEncryptedFileLocation#f5235d55 id:long access_hash:long = InputFileLocation;
181 | inputDocumentFileLocation#430f0724 id:long access_hash:long version:int = InputFileLocation;
182 |
183 | inputAppEvent#770656a8 time:double type:string peer:long data:string = InputAppEvent;
184 |
185 | peerUser#9db1bc6d user_id:int = Peer;
186 | peerChat#bad0e5bb chat_id:int = Peer;
187 | peerChannel#bddde532 channel_id:int = Peer;
188 |
189 | storage.fileUnknown#aa963b05 = storage.FileType;
190 | storage.filePartial#40bc6f52 = storage.FileType;
191 | storage.fileJpeg#7efe0e = storage.FileType;
192 | storage.fileGif#cae1aadf = storage.FileType;
193 | storage.filePng#a4f63c0 = storage.FileType;
194 | storage.filePdf#ae1e508d = storage.FileType;
195 | storage.fileMp3#528a0677 = storage.FileType;
196 | storage.fileMov#4b09ebbc = storage.FileType;
197 | storage.fileMp4#b3cea0e4 = storage.FileType;
198 | storage.fileWebp#1081464c = storage.FileType;
199 |
200 | fileLocationUnavailable#7c596b46 volume_id:long local_id:int secret:long = FileLocation;
201 | fileLocation#53d69076 dc_id:int volume_id:long local_id:int secret:long = FileLocation;
202 |
203 | userEmpty#200250ba id:int = User;
204 | user#d10d979a flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?string bot_inline_placeholder:flags.19?string = User;
205 |
206 | userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
207 | userProfilePhoto#d559d8c8 photo_id:long photo_small:FileLocation photo_big:FileLocation = UserProfilePhoto;
208 |
209 | userStatusEmpty#9d05049 = UserStatus;
210 | userStatusOnline#edb93949 expires:int = UserStatus;
211 | userStatusOffline#8c703f was_online:int = UserStatus;
212 | userStatusRecently#e26f42f1 = UserStatus;
213 | userStatusLastWeek#7bf09fc = UserStatus;
214 | userStatusLastMonth#77ebc742 = UserStatus;
215 |
216 | chatEmpty#9ba2d800 id:int = Chat;
217 | chat#d91cdd54 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true admins_enabled:flags.3?true admin:flags.4?true deactivated:flags.5?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel = Chat;
218 | chatForbidden#7328bdb id:int title:string = Chat;
219 | channel#a14dca52 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true editor:flags.3?true moderator:flags.4?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true democracy:flags.10?true signatures:flags.11?true min:flags.12?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string = Chat;
220 | channelForbidden#8537784f flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string = Chat;
221 |
222 | chatFull#2e02a614 id:int participants:ChatParticipants chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector = ChatFull;
223 | channelFull#c3d5512f flags:# can_view_participants:flags.3?true can_set_username:flags.6?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int = ChatFull;
224 |
225 | chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant;
226 | chatParticipantCreator#da13538a user_id:int = ChatParticipant;
227 | chatParticipantAdmin#e2d6e436 user_id:int inviter_id:int date:int = ChatParticipant;
228 |
229 | chatParticipantsForbidden#fc900c2b flags:# chat_id:int self_participant:flags.0?ChatParticipant = ChatParticipants;
230 | chatParticipants#3f460fed chat_id:int participants:Vector version:int = ChatParticipants;
231 |
232 | chatPhotoEmpty#37c1011c = ChatPhoto;
233 | chatPhoto#6153276a photo_small:FileLocation photo_big:FileLocation = ChatPhoto;
234 |
235 | messageEmpty#83e5de54 id:int = Message;
236 | message#c09be45f flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector views:flags.10?int edit_date:flags.15?int = Message;
237 | messageService#9e19a1f6 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer reply_to_msg_id:flags.3?int date:int action:MessageAction = Message;
238 |
239 | messageMediaEmpty#3ded6320 = MessageMedia;
240 | messageMediaPhoto#3d8ce53d photo:Photo caption:string = MessageMedia;
241 | messageMediaGeo#56e0d474 geo:GeoPoint = MessageMedia;
242 | messageMediaContact#5e7d2f39 phone_number:string first_name:string last_name:string user_id:int = MessageMedia;
243 | messageMediaUnsupported#9f84f49e = MessageMedia;
244 | messageMediaDocument#f3e02ea8 document:Document caption:string = MessageMedia;
245 | messageMediaWebPage#a32dd600 webpage:WebPage = MessageMedia;
246 | messageMediaVenue#7912b71f geo:GeoPoint title:string address:string provider:string venue_id:string = MessageMedia;
247 | messageMediaGame#fdb19008 game:Game = MessageMedia;
248 | messageMediaInvoice#84551347 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument receipt_msg_id:flags.2?int currency:string total_amount:long start_param:string = MessageMedia;
249 |
250 | messageActionEmpty#b6aef7b0 = MessageAction;
251 | messageActionChatCreate#a6638b9a title:string users:Vector = MessageAction;
252 | messageActionChatEditTitle#b5a1ce5a title:string = MessageAction;
253 | messageActionChatEditPhoto#7fcb13a8 photo:Photo = MessageAction;
254 | messageActionChatDeletePhoto#95e3fbef = MessageAction;
255 | messageActionChatAddUser#488a7337 users:Vector = MessageAction;
256 | messageActionChatDeleteUser#b2ae9b0c user_id:int = MessageAction;
257 | messageActionChatJoinedByLink#f89cf5e8 inviter_id:int = MessageAction;
258 | messageActionChannelCreate#95d2ac92 title:string = MessageAction;
259 | messageActionChatMigrateTo#51bdb021 channel_id:int = MessageAction;
260 | messageActionChannelMigrateFrom#b055eaee title:string chat_id:int = MessageAction;
261 | messageActionPinMessage#94bd38ed = MessageAction;
262 | messageActionHistoryClear#9fbab604 = MessageAction;
263 | messageActionGameScore#92a72876 game_id:long score:int = MessageAction;
264 | messageActionPaymentSentMe#8f31b327 flags:# currency:string total_amount:long payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string charge:PaymentCharge = MessageAction;
265 | messageActionPaymentSent#40699cd0 currency:string total_amount:long = MessageAction;
266 | messageActionPhoneCall#80e11a7f flags:# call_id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = MessageAction;
267 |
268 | dialog#66ffba14 flags:# pinned:flags.2?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage = Dialog;
269 |
270 | photoEmpty#2331b22d id:long = Photo;
271 | photo#9288dd29 flags:# has_stickers:flags.0?true id:long access_hash:long date:int sizes:Vector = Photo;
272 |
273 | photoSizeEmpty#e17e23c type:string = PhotoSize;
274 | photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize;
275 | photoCachedSize#e9a734fa type:string location:FileLocation w:int h:int bytes:bytes = PhotoSize;
276 |
277 | geoPointEmpty#1117dd5f = GeoPoint;
278 | geoPoint#2049d70c long:double lat:double = GeoPoint;
279 |
280 | auth.checkedPhone#811ea28e phone_registered:Bool = auth.CheckedPhone;
281 |
282 | auth.sentCode#5e002502 flags:# phone_registered:flags.0?true type:auth.SentCodeType phone_code_hash:string next_type:flags.1?auth.CodeType timeout:flags.2?int = auth.SentCode;
283 |
284 | auth.authorization#cd050916 flags:# tmp_sessions:flags.0?int user:User = auth.Authorization;
285 |
286 | auth.exportedAuthorization#df969c2d id:int bytes:bytes = auth.ExportedAuthorization;
287 |
288 | inputNotifyPeer#b8bc5b0c peer:InputPeer = InputNotifyPeer;
289 | inputNotifyUsers#193b4417 = InputNotifyPeer;
290 | inputNotifyChats#4a95e84e = InputNotifyPeer;
291 | inputNotifyAll#a429b886 = InputNotifyPeer;
292 |
293 | inputPeerNotifyEventsEmpty#f03064d8 = InputPeerNotifyEvents;
294 | inputPeerNotifyEventsAll#e86a2c74 = InputPeerNotifyEvents;
295 |
296 | inputPeerNotifySettings#38935eb2 flags:# show_previews:flags.0?true silent:flags.1?true mute_until:int sound:string = InputPeerNotifySettings;
297 |
298 | peerNotifyEventsEmpty#add53cb3 = PeerNotifyEvents;
299 | peerNotifyEventsAll#6d1ded88 = PeerNotifyEvents;
300 |
301 | peerNotifySettingsEmpty#70a68512 = PeerNotifySettings;
302 | peerNotifySettings#9acda4c0 flags:# show_previews:flags.0?true silent:flags.1?true mute_until:int sound:string = PeerNotifySettings;
303 |
304 | peerSettings#818426cd flags:# report_spam:flags.0?true = PeerSettings;
305 |
306 | wallPaper#ccb03657 id:int title:string sizes:Vector color:int = WallPaper;
307 | wallPaperSolid#63117f24 id:int title:string bg_color:int color:int = WallPaper;
308 |
309 | inputReportReasonSpam#58dbcab8 = ReportReason;
310 | inputReportReasonViolence#1e22c78d = ReportReason;
311 | inputReportReasonPornography#2e59d922 = ReportReason;
312 | inputReportReasonOther#e1746d0a text:string = ReportReason;
313 |
314 | userFull#f220f3f flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true user:User about:flags.1?string link:contacts.Link profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo common_chats_count:int = UserFull;
315 |
316 | contact#f911c994 user_id:int mutual:Bool = Contact;
317 |
318 | importedContact#d0028438 user_id:int client_id:long = ImportedContact;
319 |
320 | contactBlocked#561bc879 user_id:int date:int = ContactBlocked;
321 |
322 | contactStatus#d3680c61 user_id:int status:UserStatus = ContactStatus;
323 |
324 | contacts.link#3ace484c my_link:ContactLink foreign_link:ContactLink user:User = contacts.Link;
325 |
326 | contacts.contactsNotModified#b74ba9d2 = contacts.Contacts;
327 | contacts.contacts#6f8b8cb2 contacts:Vector users:Vector = contacts.Contacts;
328 |
329 | contacts.importedContacts#ad524315 imported:Vector retry_contacts:Vector users:Vector = contacts.ImportedContacts;
330 |
331 | contacts.blocked#1c138d15 blocked:Vector users:Vector = contacts.Blocked;
332 | contacts.blockedSlice#900802a1 count:int blocked:Vector users:Vector = contacts.Blocked;
333 |
334 | messages.dialogs#15ba6c40 dialogs:Vector