├── .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 messages:Vector chats:Vector users:Vector = messages.Dialogs; 335 | messages.dialogsSlice#71e094f3 count:int dialogs:Vector messages:Vector chats:Vector users:Vector = messages.Dialogs; 336 | 337 | messages.messages#8c718e87 messages:Vector chats:Vector users:Vector = messages.Messages; 338 | messages.messagesSlice#b446ae3 count:int messages:Vector chats:Vector users:Vector = messages.Messages; 339 | messages.channelMessages#99262e37 flags:# pts:int count:int messages:Vector chats:Vector users:Vector = messages.Messages; 340 | 341 | messages.chats#64ff9fd5 chats:Vector = messages.Chats; 342 | messages.chatsSlice#9cd81144 count:int chats:Vector = messages.Chats; 343 | 344 | messages.chatFull#e5d7d19c full_chat:ChatFull chats:Vector users:Vector = messages.ChatFull; 345 | 346 | messages.affectedHistory#b45c69d1 pts:int pts_count:int offset:int = messages.AffectedHistory; 347 | 348 | inputMessagesFilterEmpty#57e2f66c = MessagesFilter; 349 | inputMessagesFilterPhotos#9609a51c = MessagesFilter; 350 | inputMessagesFilterVideo#9fc00e65 = MessagesFilter; 351 | inputMessagesFilterPhotoVideo#56e9f0e4 = MessagesFilter; 352 | inputMessagesFilterPhotoVideoDocuments#d95e73bb = MessagesFilter; 353 | inputMessagesFilterDocument#9eddf188 = MessagesFilter; 354 | inputMessagesFilterUrl#7ef0dd87 = MessagesFilter; 355 | inputMessagesFilterGif#ffc86587 = MessagesFilter; 356 | inputMessagesFilterVoice#50f5c392 = MessagesFilter; 357 | inputMessagesFilterMusic#3751b49e = MessagesFilter; 358 | inputMessagesFilterChatPhotos#3a20ecb8 = MessagesFilter; 359 | inputMessagesFilterPhoneCalls#80c99768 flags:# missed:flags.0?true = MessagesFilter; 360 | 361 | updateNewMessage#1f2b0afd message:Message pts:int pts_count:int = Update; 362 | updateMessageID#4e90bfd6 id:int random_id:long = Update; 363 | updateDeleteMessages#a20db0e5 messages:Vector pts:int pts_count:int = Update; 364 | updateUserTyping#5c486927 user_id:int action:SendMessageAction = Update; 365 | updateChatUserTyping#9a65ea1f chat_id:int user_id:int action:SendMessageAction = Update; 366 | updateChatParticipants#7761198 participants:ChatParticipants = Update; 367 | updateUserStatus#1bfbd823 user_id:int status:UserStatus = Update; 368 | updateUserName#a7332b73 user_id:int first_name:string last_name:string username:string = Update; 369 | updateUserPhoto#95313b0c user_id:int date:int photo:UserProfilePhoto previous:Bool = Update; 370 | updateContactRegistered#2575bbb9 user_id:int date:int = Update; 371 | updateContactLink#9d2e67c5 user_id:int my_link:ContactLink foreign_link:ContactLink = Update; 372 | updateNewEncryptedMessage#12bcbd9a message:EncryptedMessage qts:int = Update; 373 | updateEncryptedChatTyping#1710f156 chat_id:int = Update; 374 | updateEncryption#b4a2e88d chat:EncryptedChat date:int = Update; 375 | updateEncryptedMessagesRead#38fe25b7 chat_id:int max_date:int date:int = Update; 376 | updateChatParticipantAdd#ea4b0e5c chat_id:int user_id:int inviter_id:int date:int version:int = Update; 377 | updateChatParticipantDelete#6e5f8c22 chat_id:int user_id:int version:int = Update; 378 | updateDcOptions#8e5e9873 dc_options:Vector = Update; 379 | updateUserBlocked#80ece81a user_id:int blocked:Bool = Update; 380 | updateNotifySettings#bec268ef peer:NotifyPeer notify_settings:PeerNotifySettings = Update; 381 | updateServiceNotification#ebe46819 flags:# popup:flags.0?true inbox_date:flags.1?int type:string message:string media:MessageMedia entities:Vector = Update; 382 | updatePrivacy#ee3b272a key:PrivacyKey rules:Vector = Update; 383 | updateUserPhone#12b9417b user_id:int phone:string = Update; 384 | updateReadHistoryInbox#9961fd5c peer:Peer max_id:int pts:int pts_count:int = Update; 385 | updateReadHistoryOutbox#2f2f21bf peer:Peer max_id:int pts:int pts_count:int = Update; 386 | updateWebPage#7f891213 webpage:WebPage pts:int pts_count:int = Update; 387 | updateReadMessagesContents#68c13933 messages:Vector pts:int pts_count:int = Update; 388 | updateChannelTooLong#eb0467fb flags:# channel_id:int pts:flags.0?int = Update; 389 | updateChannel#b6d45656 channel_id:int = Update; 390 | updateNewChannelMessage#62ba04d9 message:Message pts:int pts_count:int = Update; 391 | updateReadChannelInbox#4214f37f channel_id:int max_id:int = Update; 392 | updateDeleteChannelMessages#c37521c9 channel_id:int messages:Vector pts:int pts_count:int = Update; 393 | updateChannelMessageViews#98a12b4b channel_id:int id:int views:int = Update; 394 | updateChatAdmins#6e947941 chat_id:int enabled:Bool version:int = Update; 395 | updateChatParticipantAdmin#b6901959 chat_id:int user_id:int is_admin:Bool version:int = Update; 396 | updateNewStickerSet#688a30aa stickerset:messages.StickerSet = Update; 397 | updateStickerSetsOrder#bb2d201 flags:# masks:flags.0?true order:Vector = Update; 398 | updateStickerSets#43ae3dec = Update; 399 | updateSavedGifs#9375341e = Update; 400 | updateBotInlineQuery#54826690 flags:# query_id:long user_id:int query:string geo:flags.0?GeoPoint offset:string = Update; 401 | updateBotInlineSend#e48f964 flags:# user_id:int query:string geo:flags.0?GeoPoint id:string msg_id:flags.1?InputBotInlineMessageID = Update; 402 | updateEditChannelMessage#1b3f4df7 message:Message pts:int pts_count:int = Update; 403 | updateChannelPinnedMessage#98592475 channel_id:int id:int = Update; 404 | updateBotCallbackQuery#e73547e1 flags:# query_id:long user_id:int peer:Peer msg_id:int chat_instance:long data:flags.0?bytes game_short_name:flags.1?string = Update; 405 | updateEditMessage#e40370a3 message:Message pts:int pts_count:int = Update; 406 | updateInlineBotCallbackQuery#f9d27a5a flags:# query_id:long user_id:int msg_id:InputBotInlineMessageID chat_instance:long data:flags.0?bytes game_short_name:flags.1?string = Update; 407 | updateReadChannelOutbox#25d6c9c7 channel_id:int max_id:int = Update; 408 | updateDraftMessage#ee2bb969 peer:Peer draft:DraftMessage = Update; 409 | updateReadFeaturedStickers#571d2742 = Update; 410 | updateRecentStickers#9a422c20 = Update; 411 | updateConfig#a229dd06 = Update; 412 | updatePtsChanged#3354678f = Update; 413 | updateChannelWebPage#40771900 channel_id:int webpage:WebPage pts:int pts_count:int = Update; 414 | updateDialogPinned#d711a2cc flags:# pinned:flags.0?true peer:Peer = Update; 415 | updatePinnedDialogs#d8caf68d flags:# order:flags.0?Vector = Update; 416 | updateBotWebhookJSON#8317c0c3 data:DataJSON = Update; 417 | updateBotWebhookJSONQuery#9b9240a6 query_id:long data:DataJSON timeout:int = Update; 418 | updateBotShippingQuery#e0cdc940 query_id:long user_id:int payload:bytes shipping_address:PostAddress = Update; 419 | updateBotPrecheckoutQuery#5d2f3aa9 flags:# query_id:long user_id:int payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string currency:string total_amount:long = Update; 420 | updatePhoneCall#ab0f6b1e phone_call:PhoneCall = Update; 421 | 422 | updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; 423 | 424 | updates.differenceEmpty#5d75a138 date:int seq:int = updates.Difference; 425 | updates.difference#f49ca0 new_messages:Vector new_encrypted_messages:Vector other_updates:Vector chats:Vector users:Vector state:updates.State = updates.Difference; 426 | updates.differenceSlice#a8fb1981 new_messages:Vector new_encrypted_messages:Vector other_updates:Vector chats:Vector users:Vector intermediate_state:updates.State = updates.Difference; 427 | updates.differenceTooLong#4afe8f6d pts:int = updates.Difference; 428 | 429 | updatesTooLong#e317af7e = Updates; 430 | updateShortMessage#914fbf11 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int user_id:int message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int entities:flags.7?Vector = Updates; 431 | updateShortChatMessage#16812688 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int from_id:int chat_id:int message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int entities:flags.7?Vector = Updates; 432 | updateShort#78d4dec1 update:Update date:int = Updates; 433 | updatesCombined#725b04c3 updates:Vector users:Vector chats:Vector date:int seq_start:int seq:int = Updates; 434 | updates#74ae4240 updates:Vector users:Vector chats:Vector date:int seq:int = Updates; 435 | updateShortSentMessage#11f1331c flags:# out:flags.1?true id:int pts:int pts_count:int date:int media:flags.9?MessageMedia entities:flags.7?Vector = Updates; 436 | 437 | photos.photos#8dca6aa5 photos:Vector users:Vector = photos.Photos; 438 | photos.photosSlice#15051f54 count:int photos:Vector users:Vector = photos.Photos; 439 | 440 | photos.photo#20212ca8 photo:Photo users:Vector = photos.Photo; 441 | 442 | upload.file#96a18d5 type:storage.FileType mtime:int bytes:bytes = upload.File; 443 | 444 | dcOption#5d8c6cc flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true id:int ip_address:string port:int = DcOption; 445 | 446 | config#cb601684 flags:# phonecalls_enabled:flags.1?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int chat_big_size:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int rating_e_decay:int stickers_recent_limit:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string disabled_features:Vector = Config; 447 | 448 | nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc; 449 | 450 | help.appUpdate#8987f311 id:int critical:Bool url:string text:string = help.AppUpdate; 451 | help.noAppUpdate#c45a6536 = help.AppUpdate; 452 | 453 | help.inviteText#18cb9f78 message:string = help.InviteText; 454 | 455 | encryptedChatEmpty#ab7ec0a0 id:int = EncryptedChat; 456 | encryptedChatWaiting#3bf703dc id:int access_hash:long date:int admin_id:int participant_id:int = EncryptedChat; 457 | encryptedChatRequested#c878527e id:int access_hash:long date:int admin_id:int participant_id:int g_a:bytes = EncryptedChat; 458 | encryptedChat#fa56ce36 id:int access_hash:long date:int admin_id:int participant_id:int g_a_or_b:bytes key_fingerprint:long = EncryptedChat; 459 | encryptedChatDiscarded#13d6dd27 id:int = EncryptedChat; 460 | 461 | inputEncryptedChat#f141b5e1 chat_id:int access_hash:long = InputEncryptedChat; 462 | 463 | encryptedFileEmpty#c21f497e = EncryptedFile; 464 | encryptedFile#4a70994c id:long access_hash:long size:int dc_id:int key_fingerprint:int = EncryptedFile; 465 | 466 | inputEncryptedFileEmpty#1837c364 = InputEncryptedFile; 467 | inputEncryptedFileUploaded#64bd0306 id:long parts:int md5_checksum:string key_fingerprint:int = InputEncryptedFile; 468 | inputEncryptedFile#5a17b5e5 id:long access_hash:long = InputEncryptedFile; 469 | inputEncryptedFileBigUploaded#2dc173c8 id:long parts:int key_fingerprint:int = InputEncryptedFile; 470 | 471 | encryptedMessage#ed18c118 random_id:long chat_id:int date:int bytes:bytes file:EncryptedFile = EncryptedMessage; 472 | encryptedMessageService#23734b06 random_id:long chat_id:int date:int bytes:bytes = EncryptedMessage; 473 | 474 | messages.dhConfigNotModified#c0e24635 random:bytes = messages.DhConfig; 475 | messages.dhConfig#2c221edd g:int p:bytes version:int random:bytes = messages.DhConfig; 476 | 477 | messages.sentEncryptedMessage#560f8935 date:int = messages.SentEncryptedMessage; 478 | messages.sentEncryptedFile#9493ff32 date:int file:EncryptedFile = messages.SentEncryptedMessage; 479 | 480 | inputDocumentEmpty#72f0eaae = InputDocument; 481 | inputDocument#18798952 id:long access_hash:long = InputDocument; 482 | 483 | documentEmpty#36f8c871 id:long = Document; 484 | document#87232bc7 id:long access_hash:long date:int mime_type:string size:int thumb:PhotoSize dc_id:int version:int attributes:Vector = Document; 485 | 486 | help.support#17c6b5f6 phone_number:string user:User = help.Support; 487 | 488 | notifyPeer#9fd40bd8 peer:Peer = NotifyPeer; 489 | notifyUsers#b4c83b4c = NotifyPeer; 490 | notifyChats#c007cec3 = NotifyPeer; 491 | notifyAll#74d07c60 = NotifyPeer; 492 | 493 | sendMessageTypingAction#16bf744e = SendMessageAction; 494 | sendMessageCancelAction#fd5ec8f5 = SendMessageAction; 495 | sendMessageRecordVideoAction#a187d66f = SendMessageAction; 496 | sendMessageUploadVideoAction#e9763aec progress:int = SendMessageAction; 497 | sendMessageRecordAudioAction#d52f73f7 = SendMessageAction; 498 | sendMessageUploadAudioAction#f351d7ab progress:int = SendMessageAction; 499 | sendMessageUploadPhotoAction#d1d34a26 progress:int = SendMessageAction; 500 | sendMessageUploadDocumentAction#aa0cd9e4 progress:int = SendMessageAction; 501 | sendMessageGeoLocationAction#176f8ba1 = SendMessageAction; 502 | sendMessageChooseContactAction#628cbc6f = SendMessageAction; 503 | sendMessageGamePlayAction#dd6a8f48 = SendMessageAction; 504 | 505 | contacts.found#1aa1f784 results:Vector chats:Vector users:Vector = contacts.Found; 506 | 507 | inputPrivacyKeyStatusTimestamp#4f96cb18 = InputPrivacyKey; 508 | inputPrivacyKeyChatInvite#bdfb0426 = InputPrivacyKey; 509 | inputPrivacyKeyPhoneCall#fabadc5f = InputPrivacyKey; 510 | 511 | privacyKeyStatusTimestamp#bc2eab30 = PrivacyKey; 512 | privacyKeyChatInvite#500e6dfa = PrivacyKey; 513 | privacyKeyPhoneCall#3d662b7b = PrivacyKey; 514 | 515 | inputPrivacyValueAllowContacts#d09e07b = InputPrivacyRule; 516 | inputPrivacyValueAllowAll#184b35ce = InputPrivacyRule; 517 | inputPrivacyValueAllowUsers#131cc67f users:Vector = InputPrivacyRule; 518 | inputPrivacyValueDisallowContacts#ba52007 = InputPrivacyRule; 519 | inputPrivacyValueDisallowAll#d66b66c9 = InputPrivacyRule; 520 | inputPrivacyValueDisallowUsers#90110467 users:Vector = InputPrivacyRule; 521 | 522 | privacyValueAllowContacts#fffe1bac = PrivacyRule; 523 | privacyValueAllowAll#65427b82 = PrivacyRule; 524 | privacyValueAllowUsers#4d5bbe0c users:Vector = PrivacyRule; 525 | privacyValueDisallowContacts#f888fa1a = PrivacyRule; 526 | privacyValueDisallowAll#8b73e763 = PrivacyRule; 527 | privacyValueDisallowUsers#c7f49b7 users:Vector = PrivacyRule; 528 | 529 | account.privacyRules#554abb6f rules:Vector users:Vector = account.PrivacyRules; 530 | 531 | accountDaysTTL#b8d0afdf days:int = AccountDaysTTL; 532 | 533 | documentAttributeImageSize#6c37c15c w:int h:int = DocumentAttribute; 534 | documentAttributeAnimated#11b58939 = DocumentAttribute; 535 | documentAttributeSticker#6319d612 flags:# mask:flags.1?true alt:string stickerset:InputStickerSet mask_coords:flags.0?MaskCoords = DocumentAttribute; 536 | documentAttributeVideo#5910cccb duration:int w:int h:int = DocumentAttribute; 537 | documentAttributeAudio#9852f9c6 flags:# voice:flags.10?true duration:int title:flags.0?string performer:flags.1?string waveform:flags.2?bytes = DocumentAttribute; 538 | documentAttributeFilename#15590068 file_name:string = DocumentAttribute; 539 | documentAttributeHasStickers#9801d2f7 = DocumentAttribute; 540 | 541 | messages.stickersNotModified#f1749a22 = messages.Stickers; 542 | messages.stickers#8a8ecd32 hash:string stickers:Vector = messages.Stickers; 543 | 544 | stickerPack#12b299d4 emoticon:string documents:Vector = StickerPack; 545 | 546 | messages.allStickersNotModified#e86602c3 = messages.AllStickers; 547 | messages.allStickers#edfd405f hash:int sets:Vector = messages.AllStickers; 548 | 549 | disabledFeature#ae636f24 feature:string description:string = DisabledFeature; 550 | 551 | messages.affectedMessages#84d19185 pts:int pts_count:int = messages.AffectedMessages; 552 | 553 | contactLinkUnknown#5f4f9247 = ContactLink; 554 | contactLinkNone#feedd3ad = ContactLink; 555 | contactLinkHasPhone#268f3f59 = ContactLink; 556 | contactLinkContact#d502c2d0 = ContactLink; 557 | 558 | webPageEmpty#eb1477e8 id:long = WebPage; 559 | webPagePending#c586da1c id:long date:int = WebPage; 560 | webPage#5f07b4bc flags:# id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document cached_page:flags.10?Page = WebPage; 561 | webPageNotModified#85849473 = WebPage; 562 | 563 | authorization#7bf2e6f6 hash:long flags:int device_model:string platform:string system_version:string api_id:int app_name:string app_version:string date_created:int date_active:int ip:string country:string region:string = Authorization; 564 | 565 | account.authorizations#1250abde authorizations:Vector = account.Authorizations; 566 | 567 | account.noPassword#96dabc18 new_salt:bytes email_unconfirmed_pattern:string = account.Password; 568 | account.password#7c18141c current_salt:bytes new_salt:bytes hint:string has_recovery:Bool email_unconfirmed_pattern:string = account.Password; 569 | 570 | account.passwordSettings#b7b72ab3 email:string = account.PasswordSettings; 571 | 572 | account.passwordInputSettings#86916deb flags:# new_salt:flags.0?bytes new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string = account.PasswordInputSettings; 573 | 574 | auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery; 575 | 576 | receivedNotifyMessage#a384b779 id:int flags:int = ReceivedNotifyMessage; 577 | 578 | chatInviteEmpty#69df3769 = ExportedChatInvite; 579 | chatInviteExported#fc2e05bc link:string = ExportedChatInvite; 580 | 581 | chatInviteAlready#5a686d7c chat:Chat = ChatInvite; 582 | chatInvite#db74f558 flags:# channel:flags.0?true broadcast:flags.1?true public:flags.2?true megagroup:flags.3?true title:string photo:ChatPhoto participants_count:int participants:flags.4?Vector = ChatInvite; 583 | 584 | inputStickerSetEmpty#ffb62b95 = InputStickerSet; 585 | inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet; 586 | inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet; 587 | 588 | stickerSet#cd303b41 flags:# installed:flags.0?true archived:flags.1?true official:flags.2?true masks:flags.3?true id:long access_hash:long title:string short_name:string count:int hash:int = StickerSet; 589 | 590 | messages.stickerSet#b60a24a6 set:StickerSet packs:Vector documents:Vector = messages.StickerSet; 591 | 592 | botCommand#c27ac8c7 command:string description:string = BotCommand; 593 | 594 | botInfo#98e81d3a user_id:int description:string commands:Vector = BotInfo; 595 | 596 | keyboardButton#a2fa4880 text:string = KeyboardButton; 597 | keyboardButtonUrl#258aff05 text:string url:string = KeyboardButton; 598 | keyboardButtonCallback#683a5e46 text:string data:bytes = KeyboardButton; 599 | keyboardButtonRequestPhone#b16a6c29 text:string = KeyboardButton; 600 | keyboardButtonRequestGeoLocation#fc796b3f text:string = KeyboardButton; 601 | keyboardButtonSwitchInline#568a748 flags:# same_peer:flags.0?true text:string query:string = KeyboardButton; 602 | keyboardButtonGame#50f41ccf text:string = KeyboardButton; 603 | keyboardButtonBuy#afd93fbb text:string = KeyboardButton; 604 | 605 | keyboardButtonRow#77608b83 buttons:Vector = KeyboardButtonRow; 606 | 607 | replyKeyboardHide#a03e5b85 flags:# selective:flags.2?true = ReplyMarkup; 608 | replyKeyboardForceReply#f4108aa0 flags:# single_use:flags.1?true selective:flags.2?true = ReplyMarkup; 609 | replyKeyboardMarkup#3502758c flags:# resize:flags.0?true single_use:flags.1?true selective:flags.2?true rows:Vector = ReplyMarkup; 610 | replyInlineMarkup#48a30254 rows:Vector = ReplyMarkup; 611 | 612 | messageEntityUnknown#bb92ba95 offset:int length:int = MessageEntity; 613 | messageEntityMention#fa04579d offset:int length:int = MessageEntity; 614 | messageEntityHashtag#6f635b0d offset:int length:int = MessageEntity; 615 | messageEntityBotCommand#6cef8ac7 offset:int length:int = MessageEntity; 616 | messageEntityUrl#6ed02538 offset:int length:int = MessageEntity; 617 | messageEntityEmail#64e475c2 offset:int length:int = MessageEntity; 618 | messageEntityBold#bd610bc9 offset:int length:int = MessageEntity; 619 | messageEntityItalic#826f8b60 offset:int length:int = MessageEntity; 620 | messageEntityCode#28a20571 offset:int length:int = MessageEntity; 621 | messageEntityPre#73924be0 offset:int length:int language:string = MessageEntity; 622 | messageEntityTextUrl#76a6d327 offset:int length:int url:string = MessageEntity; 623 | messageEntityMentionName#352dca58 offset:int length:int user_id:int = MessageEntity; 624 | inputMessageEntityMentionName#208e68c9 offset:int length:int user_id:InputUser = MessageEntity; 625 | 626 | inputChannelEmpty#ee8c1e86 = InputChannel; 627 | inputChannel#afeb712e channel_id:int access_hash:long = InputChannel; 628 | 629 | contacts.resolvedPeer#7f077ad9 peer:Peer chats:Vector users:Vector = contacts.ResolvedPeer; 630 | 631 | messageRange#ae30253 min_id:int max_id:int = MessageRange; 632 | 633 | updates.channelDifferenceEmpty#3e11affb flags:# final:flags.0?true pts:int timeout:flags.1?int = updates.ChannelDifference; 634 | updates.channelDifferenceTooLong#410dee07 flags:# final:flags.0?true pts:int timeout:flags.1?int top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int messages:Vector chats:Vector users:Vector = updates.ChannelDifference; 635 | updates.channelDifference#2064674e flags:# final:flags.0?true pts:int timeout:flags.1?int new_messages:Vector other_updates:Vector chats:Vector users:Vector = updates.ChannelDifference; 636 | 637 | channelMessagesFilterEmpty#94d42ee7 = ChannelMessagesFilter; 638 | channelMessagesFilter#cd77d957 flags:# exclude_new_messages:flags.1?true ranges:Vector = ChannelMessagesFilter; 639 | 640 | channelParticipant#15ebac1d user_id:int date:int = ChannelParticipant; 641 | channelParticipantSelf#a3289a6d user_id:int inviter_id:int date:int = ChannelParticipant; 642 | channelParticipantModerator#91057fef user_id:int inviter_id:int date:int = ChannelParticipant; 643 | channelParticipantEditor#98192d61 user_id:int inviter_id:int date:int = ChannelParticipant; 644 | channelParticipantKicked#8cc5e69a user_id:int kicked_by:int date:int = ChannelParticipant; 645 | channelParticipantCreator#e3e2e1f9 user_id:int = ChannelParticipant; 646 | 647 | channelParticipantsRecent#de3f3c79 = ChannelParticipantsFilter; 648 | channelParticipantsAdmins#b4608969 = ChannelParticipantsFilter; 649 | channelParticipantsKicked#3c37bb7a = ChannelParticipantsFilter; 650 | channelParticipantsBots#b0d1865b = ChannelParticipantsFilter; 651 | 652 | channelRoleEmpty#b285a0c6 = ChannelParticipantRole; 653 | channelRoleModerator#9618d975 = ChannelParticipantRole; 654 | channelRoleEditor#820bfe8c = ChannelParticipantRole; 655 | 656 | channels.channelParticipants#f56ee2a8 count:int participants:Vector users:Vector = channels.ChannelParticipants; 657 | 658 | channels.channelParticipant#d0d9b163 participant:ChannelParticipant users:Vector = channels.ChannelParticipant; 659 | 660 | help.termsOfService#f1ee3e90 text:string = help.TermsOfService; 661 | 662 | foundGif#162ecc1f url:string thumb_url:string content_url:string content_type:string w:int h:int = FoundGif; 663 | foundGifCached#9c750409 url:string photo:Photo document:Document = FoundGif; 664 | 665 | messages.foundGifs#450a1c0a next_offset:int results:Vector = messages.FoundGifs; 666 | 667 | messages.savedGifsNotModified#e8025ca2 = messages.SavedGifs; 668 | messages.savedGifs#2e0709a5 hash:int gifs:Vector = messages.SavedGifs; 669 | 670 | inputBotInlineMessageMediaAuto#292fed13 flags:# caption:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage; 671 | inputBotInlineMessageText#3dcd7a87 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage; 672 | inputBotInlineMessageMediaGeo#f4a59de1 flags:# geo_point:InputGeoPoint reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage; 673 | inputBotInlineMessageMediaVenue#aaafadc8 flags:# geo_point:InputGeoPoint title:string address:string provider:string venue_id:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage; 674 | inputBotInlineMessageMediaContact#2daf01a7 flags:# phone_number:string first_name:string last_name:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage; 675 | inputBotInlineMessageGame#4b425864 flags:# reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage; 676 | 677 | inputBotInlineResult#2cbbe15a flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb_url:flags.4?string content_url:flags.5?string content_type:flags.5?string w:flags.6?int h:flags.6?int duration:flags.7?int send_message:InputBotInlineMessage = InputBotInlineResult; 678 | inputBotInlineResultPhoto#a8d864a7 id:string type:string photo:InputPhoto send_message:InputBotInlineMessage = InputBotInlineResult; 679 | inputBotInlineResultDocument#fff8fdc4 flags:# id:string type:string title:flags.1?string description:flags.2?string document:InputDocument send_message:InputBotInlineMessage = InputBotInlineResult; 680 | inputBotInlineResultGame#4fa417f2 id:string short_name:string send_message:InputBotInlineMessage = InputBotInlineResult; 681 | 682 | botInlineMessageMediaAuto#a74b15b flags:# caption:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage; 683 | botInlineMessageText#8c7f65e2 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector reply_markup:flags.2?ReplyMarkup = BotInlineMessage; 684 | botInlineMessageMediaGeo#3a8fd8b8 flags:# geo:GeoPoint reply_markup:flags.2?ReplyMarkup = BotInlineMessage; 685 | botInlineMessageMediaVenue#4366232e flags:# geo:GeoPoint title:string address:string provider:string venue_id:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage; 686 | botInlineMessageMediaContact#35edb4d4 flags:# phone_number:string first_name:string last_name:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage; 687 | 688 | botInlineResult#9bebaeb9 flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb_url:flags.4?string content_url:flags.5?string content_type:flags.5?string w:flags.6?int h:flags.6?int duration:flags.7?int send_message:BotInlineMessage = BotInlineResult; 689 | botInlineMediaResult#17db940b flags:# id:string type:string photo:flags.0?Photo document:flags.1?Document title:flags.2?string description:flags.3?string send_message:BotInlineMessage = BotInlineResult; 690 | 691 | messages.botResults#ccd3563d flags:# gallery:flags.0?true query_id:long next_offset:flags.1?string switch_pm:flags.2?InlineBotSwitchPM results:Vector cache_time:int = messages.BotResults; 692 | 693 | exportedMessageLink#1f486803 link:string = ExportedMessageLink; 694 | 695 | messageFwdHeader#c786ddcb flags:# from_id:flags.0?int date:int channel_id:flags.1?int channel_post:flags.2?int = MessageFwdHeader; 696 | 697 | auth.codeTypeSms#72a3158c = auth.CodeType; 698 | auth.codeTypeCall#741cd3e3 = auth.CodeType; 699 | auth.codeTypeFlashCall#226ccefb = auth.CodeType; 700 | 701 | auth.sentCodeTypeApp#3dbb5986 length:int = auth.SentCodeType; 702 | auth.sentCodeTypeSms#c000bba2 length:int = auth.SentCodeType; 703 | auth.sentCodeTypeCall#5353e5a7 length:int = auth.SentCodeType; 704 | auth.sentCodeTypeFlashCall#ab03c6d9 pattern:string = auth.SentCodeType; 705 | 706 | messages.botCallbackAnswer#36585ea4 flags:# alert:flags.1?true has_url:flags.3?true message:flags.0?string url:flags.2?string cache_time:int = messages.BotCallbackAnswer; 707 | 708 | messages.messageEditData#26b5dde6 flags:# caption:flags.0?true = messages.MessageEditData; 709 | 710 | inputBotInlineMessageID#890c3d89 dc_id:int id:long access_hash:long = InputBotInlineMessageID; 711 | 712 | inlineBotSwitchPM#3c20629f text:string start_param:string = InlineBotSwitchPM; 713 | 714 | messages.peerDialogs#3371c354 dialogs:Vector messages:Vector chats:Vector users:Vector state:updates.State = messages.PeerDialogs; 715 | 716 | topPeer#edcdc05b peer:Peer rating:double = TopPeer; 717 | 718 | topPeerCategoryBotsPM#ab661b5b = TopPeerCategory; 719 | topPeerCategoryBotsInline#148677e2 = TopPeerCategory; 720 | topPeerCategoryCorrespondents#637b7ed = TopPeerCategory; 721 | topPeerCategoryGroups#bd17a14a = TopPeerCategory; 722 | topPeerCategoryChannels#161d9628 = TopPeerCategory; 723 | 724 | topPeerCategoryPeers#fb834291 category:TopPeerCategory count:int peers:Vector = TopPeerCategoryPeers; 725 | 726 | contacts.topPeersNotModified#de266ef5 = contacts.TopPeers; 727 | contacts.topPeers#70b772a8 categories:Vector chats:Vector users:Vector = contacts.TopPeers; 728 | 729 | draftMessageEmpty#ba4baec5 = DraftMessage; 730 | draftMessage#fd8e711f flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int message:string entities:flags.3?Vector date:int = DraftMessage; 731 | 732 | messages.featuredStickersNotModified#4ede3cf = messages.FeaturedStickers; 733 | messages.featuredStickers#f89d88e5 hash:int sets:Vector unread:Vector = messages.FeaturedStickers; 734 | 735 | messages.recentStickersNotModified#b17f890 = messages.RecentStickers; 736 | messages.recentStickers#5ce20970 hash:int stickers:Vector = messages.RecentStickers; 737 | 738 | messages.archivedStickers#4fcba9c8 count:int sets:Vector = messages.ArchivedStickers; 739 | 740 | messages.stickerSetInstallResultSuccess#38641628 = messages.StickerSetInstallResult; 741 | messages.stickerSetInstallResultArchive#35e410a8 sets:Vector = messages.StickerSetInstallResult; 742 | 743 | stickerSetCovered#6410a5d2 set:StickerSet cover:Document = StickerSetCovered; 744 | stickerSetMultiCovered#3407e51b set:StickerSet covers:Vector = StickerSetCovered; 745 | 746 | maskCoords#aed6dbb2 n:int x:double y:double zoom:double = MaskCoords; 747 | 748 | inputStickeredMediaPhoto#4a992157 id:InputPhoto = InputStickeredMedia; 749 | inputStickeredMediaDocument#438865b id:InputDocument = InputStickeredMedia; 750 | 751 | game#bdf9653b flags:# id:long access_hash:long short_name:string title:string description:string photo:Photo document:flags.0?Document = Game; 752 | 753 | inputGameID#32c3e77 id:long access_hash:long = InputGame; 754 | inputGameShortName#c331e80a bot_id:InputUser short_name:string = InputGame; 755 | 756 | highScore#58fffcd0 pos:int user_id:int score:int = HighScore; 757 | 758 | messages.highScores#9a3bfd99 scores:Vector users:Vector = messages.HighScores; 759 | 760 | textEmpty#dc3d824f = RichText; 761 | textPlain#744694e0 text:string = RichText; 762 | textBold#6724abc4 text:RichText = RichText; 763 | textItalic#d912a59c text:RichText = RichText; 764 | textUnderline#c12622c4 text:RichText = RichText; 765 | textStrike#9bf8bb95 text:RichText = RichText; 766 | textFixed#6c3f19b9 text:RichText = RichText; 767 | textUrl#3c2884c1 text:RichText url:string webpage_id:long = RichText; 768 | textEmail#de5a0dd6 text:RichText email:string = RichText; 769 | textConcat#7e6260d7 texts:Vector = RichText; 770 | 771 | pageBlockUnsupported#13567e8a = PageBlock; 772 | pageBlockTitle#70abc3fd text:RichText = PageBlock; 773 | pageBlockSubtitle#8ffa9a1f text:RichText = PageBlock; 774 | pageBlockAuthorDate#baafe5e0 author:RichText published_date:int = PageBlock; 775 | pageBlockHeader#bfd064ec text:RichText = PageBlock; 776 | pageBlockSubheader#f12bb6e1 text:RichText = PageBlock; 777 | pageBlockParagraph#467a0766 text:RichText = PageBlock; 778 | pageBlockPreformatted#c070d93e text:RichText language:string = PageBlock; 779 | pageBlockFooter#48870999 text:RichText = PageBlock; 780 | pageBlockDivider#db20b188 = PageBlock; 781 | pageBlockAnchor#ce0d37b0 name:string = PageBlock; 782 | pageBlockList#3a58c7f4 ordered:Bool items:Vector = PageBlock; 783 | pageBlockBlockquote#263d7c26 text:RichText caption:RichText = PageBlock; 784 | pageBlockPullquote#4f4456d3 text:RichText caption:RichText = PageBlock; 785 | pageBlockPhoto#e9c69982 photo_id:long caption:RichText = PageBlock; 786 | pageBlockVideo#d9d71866 flags:# autoplay:flags.0?true loop:flags.1?true video_id:long caption:RichText = PageBlock; 787 | pageBlockCover#39f23300 cover:PageBlock = PageBlock; 788 | pageBlockEmbed#cde200d1 flags:# full_width:flags.0?true allow_scrolling:flags.3?true url:flags.1?string html:flags.2?string poster_photo_id:flags.4?long w:int h:int caption:RichText = PageBlock; 789 | pageBlockEmbedPost#292c7be9 url:string webpage_id:long author_photo_id:long author:string date:int blocks:Vector caption:RichText = PageBlock; 790 | pageBlockCollage#8b31c4f items:Vector caption:RichText = PageBlock; 791 | pageBlockSlideshow#130c8963 items:Vector caption:RichText = PageBlock; 792 | 793 | pagePart#8dee6c44 blocks:Vector photos:Vector videos:Vector = Page; 794 | pageFull#d7a19d69 blocks:Vector photos:Vector videos:Vector = Page; 795 | 796 | phoneCallDiscardReasonMissed#85e42301 = PhoneCallDiscardReason; 797 | phoneCallDiscardReasonDisconnect#e095c1a0 = PhoneCallDiscardReason; 798 | phoneCallDiscardReasonHangup#57adc690 = PhoneCallDiscardReason; 799 | phoneCallDiscardReasonBusy#faf7e8c9 = PhoneCallDiscardReason; 800 | 801 | dataJSON#7d748d04 data:string = DataJSON; 802 | 803 | labeledPrice#cb296bf8 label:string amount:long = LabeledPrice; 804 | 805 | invoice#c30aa358 flags:# test:flags.0?true name_requested:flags.1?true phone_requested:flags.2?true email_requested:flags.3?true shipping_address_requested:flags.4?true flexible:flags.5?true currency:string prices:Vector = Invoice; 806 | 807 | paymentCharge#ea02c27e id:string provider_charge_id:string = PaymentCharge; 808 | 809 | postAddress#1e8caaeb street_line1:string street_line2:string city:string state:string country_iso2:string post_code:string = PostAddress; 810 | 811 | paymentRequestedInfo#909c3f94 flags:# name:flags.0?string phone:flags.1?string email:flags.2?string shipping_address:flags.3?PostAddress = PaymentRequestedInfo; 812 | 813 | paymentSavedCredentialsCard#cdc27a1f id:string title:string = PaymentSavedCredentials; 814 | 815 | webDocument#c61acbd8 url:string access_hash:long size:int mime_type:string attributes:Vector dc_id:int = WebDocument; 816 | 817 | inputWebDocument#9bed434d url:string size:int mime_type:string attributes:Vector = InputWebDocument; 818 | 819 | inputWebFileLocation#c239d686 url:string access_hash:long = InputWebFileLocation; 820 | 821 | upload.webFile#21e753bc size:int mime_type:string file_type:storage.FileType mtime:int bytes:bytes = upload.WebFile; 822 | 823 | payments.paymentForm#3f56aea3 flags:# can_save_credentials:flags.2?true password_missing:flags.3?true bot_id:int invoice:Invoice provider_id:int url:string native_provider:flags.4?string native_params:flags.4?DataJSON saved_info:flags.0?PaymentRequestedInfo saved_credentials:flags.1?PaymentSavedCredentials users:Vector = payments.PaymentForm; 824 | 825 | payments.validatedRequestedInfo#d1451883 flags:# id:flags.0?string shipping_options:flags.1?Vector = payments.ValidatedRequestedInfo; 826 | 827 | payments.paymentResult#4e5f810d updates:Updates = payments.PaymentResult; 828 | payments.paymentVerficationNeeded#6b56b921 url:string = payments.PaymentResult; 829 | 830 | payments.paymentReceipt#500911e1 flags:# date:int bot_id:int invoice:Invoice provider_id:int info:flags.0?PaymentRequestedInfo shipping:flags.1?ShippingOption currency:string total_amount:long credentials_title:string users:Vector = payments.PaymentReceipt; 831 | 832 | payments.savedInfo#fb8fe43c flags:# has_saved_credentials:flags.1?true saved_info:flags.0?PaymentRequestedInfo = payments.SavedInfo; 833 | 834 | inputPaymentCredentialsSaved#c10eb2cf id:string tmp_password:bytes = InputPaymentCredentials; 835 | inputPaymentCredentials#3417d728 flags:# save:flags.0?true data:DataJSON = InputPaymentCredentials; 836 | 837 | account.tmpPassword#db64fd34 tmp_password:bytes valid_until:int = account.TmpPassword; 838 | 839 | shippingOption#b6213cdf id:string title:string prices:Vector = ShippingOption; 840 | 841 | inputPhoneCall#1e36fded id:long access_hash:long = InputPhoneCall; 842 | 843 | phoneCallEmpty#5366c915 id:long = PhoneCall; 844 | phoneCallWaiting#1b8f4ad1 flags:# id:long access_hash:long date:int admin_id:int participant_id:int protocol:PhoneCallProtocol receive_date:flags.0?int = PhoneCall; 845 | phoneCallRequested#83761ce4 id:long access_hash:long date:int admin_id:int participant_id:int g_a_hash:bytes protocol:PhoneCallProtocol = PhoneCall; 846 | phoneCallAccepted#6d003d3f id:long access_hash:long date:int admin_id:int participant_id:int g_b:bytes protocol:PhoneCallProtocol = PhoneCall; 847 | phoneCall#ffe6ab67 id:long access_hash:long date:int admin_id:int participant_id:int g_a_or_b:bytes key_fingerprint:long protocol:PhoneCallProtocol connection:PhoneConnection alternative_connections:Vector start_date:int = PhoneCall; 848 | phoneCallDiscarded#50ca4de1 flags:# need_rating:flags.2?true need_debug:flags.3?true id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = PhoneCall; 849 | 850 | phoneConnection#9d4c17c0 id:long ip:string ipv6:string port:int peer_tag:bytes = PhoneConnection; 851 | 852 | phoneCallProtocol#a2bb35cb flags:# udp_p2p:flags.0?true udp_reflector:flags.1?true min_layer:int max_layer:int = PhoneCallProtocol; 853 | 854 | phone.phoneCall#ec82e140 phone_call:PhoneCall users:Vector = phone.PhoneCall; 855 | 856 | ---functions--- 857 | 858 | invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; 859 | invokeAfterMsgs#3dc4b4f0 {X:Type} msg_ids:Vector query:!X = X; 860 | initConnection#69796de9 {X:Type} api_id:int device_model:string system_version:string app_version:string lang_code:string query:!X = X; 861 | invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X; 862 | invokeWithoutUpdates#bf9459b7 {X:Type} query:!X = X; 863 | 864 | auth.checkPhone#6fe51dfb phone_number:string = auth.CheckedPhone; 865 | auth.sendCode#86aef0ec flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool api_id:int api_hash:string = auth.SentCode; 866 | auth.signUp#1b067634 phone_number:string phone_code_hash:string phone_code:string first_name:string last_name:string = auth.Authorization; 867 | auth.signIn#bcd51581 phone_number:string phone_code_hash:string phone_code:string = auth.Authorization; 868 | auth.logOut#5717da40 = Bool; 869 | auth.resetAuthorizations#9fab0d1a = Bool; 870 | auth.sendInvites#771c1d97 phone_numbers:Vector message:string = Bool; 871 | auth.exportAuthorization#e5bfffcd dc_id:int = auth.ExportedAuthorization; 872 | auth.importAuthorization#e3ef9613 id:int bytes:bytes = auth.Authorization; 873 | auth.bindTempAuthKey#cdd42a05 perm_auth_key_id:long nonce:long expires_at:int encrypted_message:bytes = Bool; 874 | auth.importBotAuthorization#67a3ff2c flags:int api_id:int api_hash:string bot_auth_token:string = auth.Authorization; 875 | auth.checkPassword#a63011e password_hash:bytes = auth.Authorization; 876 | auth.requestPasswordRecovery#d897bc66 = auth.PasswordRecovery; 877 | auth.recoverPassword#4ea56e92 code:string = auth.Authorization; 878 | auth.resendCode#3ef1a9bf phone_number:string phone_code_hash:string = auth.SentCode; 879 | auth.cancelCode#1f040578 phone_number:string phone_code_hash:string = Bool; 880 | auth.dropTempAuthKeys#8e48a188 except_auth_keys:Vector = Bool; 881 | 882 | account.registerDevice#637ea878 token_type:int token:string = Bool; 883 | account.unregisterDevice#65c55b40 token_type:int token:string = Bool; 884 | account.updateNotifySettings#84be5b93 peer:InputNotifyPeer settings:InputPeerNotifySettings = Bool; 885 | account.getNotifySettings#12b3ad31 peer:InputNotifyPeer = PeerNotifySettings; 886 | account.resetNotifySettings#db7e1747 = Bool; 887 | account.updateProfile#78515775 flags:# first_name:flags.0?string last_name:flags.1?string about:flags.2?string = User; 888 | account.updateStatus#6628562c offline:Bool = Bool; 889 | account.getWallPapers#c04cfac2 = Vector; 890 | account.reportPeer#ae189d5f peer:InputPeer reason:ReportReason = Bool; 891 | account.checkUsername#2714d86c username:string = Bool; 892 | account.updateUsername#3e0bdd7c username:string = User; 893 | account.getPrivacy#dadbc950 key:InputPrivacyKey = account.PrivacyRules; 894 | account.setPrivacy#c9f81ce8 key:InputPrivacyKey rules:Vector = account.PrivacyRules; 895 | account.deleteAccount#418d4e0b reason:string = Bool; 896 | account.getAccountTTL#8fc711d = AccountDaysTTL; 897 | account.setAccountTTL#2442485e ttl:AccountDaysTTL = Bool; 898 | account.sendChangePhoneCode#8e57deb flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool = auth.SentCode; 899 | account.changePhone#70c32edb phone_number:string phone_code_hash:string phone_code:string = User; 900 | account.updateDeviceLocked#38df3532 period:int = Bool; 901 | account.getAuthorizations#e320c158 = account.Authorizations; 902 | account.resetAuthorization#df77f3bc hash:long = Bool; 903 | account.getPassword#548a30f5 = account.Password; 904 | account.getPasswordSettings#bc8d11bb current_password_hash:bytes = account.PasswordSettings; 905 | account.updatePasswordSettings#fa7c4b86 current_password_hash:bytes new_settings:account.PasswordInputSettings = Bool; 906 | account.sendConfirmPhoneCode#1516d7bd flags:# allow_flashcall:flags.0?true hash:string current_number:flags.0?Bool = auth.SentCode; 907 | account.confirmPhone#5f2178c3 phone_code_hash:string phone_code:string = Bool; 908 | account.getTmpPassword#4a82327e password_hash:bytes period:int = account.TmpPassword; 909 | 910 | users.getUsers#d91a548 id:Vector = Vector; 911 | users.getFullUser#ca30a5b1 id:InputUser = UserFull; 912 | 913 | contacts.getStatuses#c4a353ee = Vector; 914 | contacts.getContacts#22c6aa08 hash:string = contacts.Contacts; 915 | contacts.importContacts#da30b32d contacts:Vector replace:Bool = contacts.ImportedContacts; 916 | contacts.deleteContact#8e953744 id:InputUser = contacts.Link; 917 | contacts.deleteContacts#59ab389e id:Vector = Bool; 918 | contacts.block#332b49fc id:InputUser = Bool; 919 | contacts.unblock#e54100bd id:InputUser = Bool; 920 | contacts.getBlocked#f57c350f offset:int limit:int = contacts.Blocked; 921 | contacts.exportCard#84e53737 = Vector; 922 | contacts.importCard#4fe196fe export_card:Vector = User; 923 | contacts.search#11f812d8 q:string limit:int = contacts.Found; 924 | contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer; 925 | contacts.getTopPeers#d4982db5 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true groups:flags.10?true channels:flags.15?true offset:int limit:int hash:int = contacts.TopPeers; 926 | contacts.resetTopPeerRating#1ae373ac category:TopPeerCategory peer:InputPeer = Bool; 927 | 928 | messages.getMessages#4222fa74 id:Vector = messages.Messages; 929 | messages.getDialogs#191ba9c5 flags:# exclude_pinned:flags.0?true offset_date:int offset_id:int offset_peer:InputPeer limit:int = messages.Dialogs; 930 | messages.getHistory#afa92846 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int = messages.Messages; 931 | messages.search#d4569248 flags:# peer:InputPeer q:string filter:MessagesFilter min_date:int max_date:int offset:int max_id:int limit:int = messages.Messages; 932 | messages.readHistory#e306d3a peer:InputPeer max_id:int = messages.AffectedMessages; 933 | messages.deleteHistory#1c015b09 flags:# just_clear:flags.0?true peer:InputPeer max_id:int = messages.AffectedHistory; 934 | messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector = messages.AffectedMessages; 935 | messages.receivedMessages#5a954c0 max_id:int = Vector; 936 | messages.setTyping#a3825e50 peer:InputPeer action:SendMessageAction = Bool; 937 | messages.sendMessage#fa88427a flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector = Updates; 938 | messages.sendMedia#c8f16791 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia random_id:long reply_markup:flags.2?ReplyMarkup = Updates; 939 | messages.forwardMessages#708e0195 flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true from_peer:InputPeer id:Vector random_id:Vector to_peer:InputPeer = Updates; 940 | messages.reportSpam#cf1592db peer:InputPeer = Bool; 941 | messages.hideReportSpam#a8f1709b peer:InputPeer = Bool; 942 | messages.getPeerSettings#3672e09c peer:InputPeer = PeerSettings; 943 | messages.getChats#3c6aa187 id:Vector = messages.Chats; 944 | messages.getFullChat#3b831c66 chat_id:int = messages.ChatFull; 945 | messages.editChatTitle#dc452855 chat_id:int title:string = Updates; 946 | messages.editChatPhoto#ca4c79d8 chat_id:int photo:InputChatPhoto = Updates; 947 | messages.addChatUser#f9a0aa09 chat_id:int user_id:InputUser fwd_limit:int = Updates; 948 | messages.deleteChatUser#e0611f16 chat_id:int user_id:InputUser = Updates; 949 | messages.createChat#9cb126e users:Vector title:string = Updates; 950 | messages.forwardMessage#33963bf9 peer:InputPeer id:int random_id:long = Updates; 951 | messages.getDhConfig#26cf8950 version:int random_length:int = messages.DhConfig; 952 | messages.requestEncryption#f64daf43 user_id:InputUser random_id:int g_a:bytes = EncryptedChat; 953 | messages.acceptEncryption#3dbc0415 peer:InputEncryptedChat g_b:bytes key_fingerprint:long = EncryptedChat; 954 | messages.discardEncryption#edd923c5 chat_id:int = Bool; 955 | messages.setEncryptedTyping#791451ed peer:InputEncryptedChat typing:Bool = Bool; 956 | messages.readEncryptedHistory#7f4b690a peer:InputEncryptedChat max_date:int = Bool; 957 | messages.sendEncrypted#a9776773 peer:InputEncryptedChat random_id:long data:bytes = messages.SentEncryptedMessage; 958 | messages.sendEncryptedFile#9a901b66 peer:InputEncryptedChat random_id:long data:bytes file:InputEncryptedFile = messages.SentEncryptedMessage; 959 | messages.sendEncryptedService#32d439a4 peer:InputEncryptedChat random_id:long data:bytes = messages.SentEncryptedMessage; 960 | messages.receivedQueue#55a5bb66 max_qts:int = Vector; 961 | messages.reportEncryptedSpam#4b0c8c0f peer:InputEncryptedChat = Bool; 962 | messages.readMessageContents#36a73f77 id:Vector = messages.AffectedMessages; 963 | messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers; 964 | messages.getWebPagePreview#25223e24 message:string = MessageMedia; 965 | messages.exportChatInvite#7d885289 chat_id:int = ExportedChatInvite; 966 | messages.checkChatInvite#3eadb1bb hash:string = ChatInvite; 967 | messages.importChatInvite#6c50051c hash:string = Updates; 968 | messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet; 969 | messages.installStickerSet#c78fe460 stickerset:InputStickerSet archived:Bool = messages.StickerSetInstallResult; 970 | messages.uninstallStickerSet#f96e55de stickerset:InputStickerSet = Bool; 971 | messages.startBot#e6df7378 bot:InputUser peer:InputPeer random_id:long start_param:string = Updates; 972 | messages.getMessagesViews#c4c8a55d peer:InputPeer id:Vector increment:Bool = Vector; 973 | messages.toggleChatAdmins#ec8bd9e1 chat_id:int enabled:Bool = Updates; 974 | messages.editChatAdmin#a9e69f2e chat_id:int user_id:InputUser is_admin:Bool = Bool; 975 | messages.migrateChat#15a3b8e3 chat_id:int = Updates; 976 | messages.searchGlobal#9e3cacb0 q:string offset_date:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages; 977 | messages.reorderStickerSets#78337739 flags:# masks:flags.0?true order:Vector = Bool; 978 | messages.getDocumentByHash#338e2464 sha256:bytes size:int mime_type:string = Document; 979 | messages.searchGifs#bf9a776b q:string offset:int = messages.FoundGifs; 980 | messages.getSavedGifs#83bf3d52 hash:int = messages.SavedGifs; 981 | messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool; 982 | messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults; 983 | messages.setInlineBotResults#eb5ea206 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM = Bool; 984 | messages.sendInlineBotResult#b16e06fe flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string = Updates; 985 | messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData; 986 | messages.editMessage#ce91e4ca flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector = Updates; 987 | messages.editInlineBotMessage#130c2c85 flags:# no_webpage:flags.1?true id:InputBotInlineMessageID message:flags.11?string reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector = Bool; 988 | messages.getBotCallbackAnswer#810a9fec flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes = messages.BotCallbackAnswer; 989 | messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool; 990 | messages.getPeerDialogs#2d9776b9 peers:Vector = messages.PeerDialogs; 991 | messages.saveDraft#bc39e14b flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int peer:InputPeer message:string entities:flags.3?Vector = Bool; 992 | messages.getAllDrafts#6a3f8d65 = Updates; 993 | messages.getFeaturedStickers#2dacca4f hash:int = messages.FeaturedStickers; 994 | messages.readFeaturedStickers#5b118126 id:Vector = Bool; 995 | messages.getRecentStickers#5ea192c9 flags:# attached:flags.0?true hash:int = messages.RecentStickers; 996 | messages.saveRecentSticker#392718f8 flags:# attached:flags.0?true id:InputDocument unsave:Bool = Bool; 997 | messages.clearRecentStickers#8999602d flags:# attached:flags.0?true = Bool; 998 | messages.getArchivedStickers#57f17692 flags:# masks:flags.0?true offset_id:long limit:int = messages.ArchivedStickers; 999 | messages.getMaskStickers#65b8c79f hash:int = messages.AllStickers; 1000 | messages.getAttachedStickers#cc5b67cc media:InputStickeredMedia = Vector; 1001 | messages.setGameScore#8ef8ecc0 flags:# edit_message:flags.0?true force:flags.1?true peer:InputPeer id:int user_id:InputUser score:int = Updates; 1002 | messages.setInlineGameScore#15ad9f64 flags:# edit_message:flags.0?true force:flags.1?true id:InputBotInlineMessageID user_id:InputUser score:int = Bool; 1003 | messages.getGameHighScores#e822649d peer:InputPeer id:int user_id:InputUser = messages.HighScores; 1004 | messages.getInlineGameHighScores#f635e1b id:InputBotInlineMessageID user_id:InputUser = messages.HighScores; 1005 | messages.getCommonChats#d0a48c4 user_id:InputUser max_id:int limit:int = messages.Chats; 1006 | messages.getAllChats#eba80ff0 except_ids:Vector = messages.Chats; 1007 | messages.getWebPage#32ca8f91 url:string hash:int = WebPage; 1008 | messages.toggleDialogPin#3289be6a flags:# pinned:flags.0?true peer:InputPeer = Bool; 1009 | messages.reorderPinnedDialogs#959ff644 flags:# force:flags.0?true order:Vector = Bool; 1010 | messages.getPinnedDialogs#e254d64e = messages.PeerDialogs; 1011 | messages.setBotShippingResults#e5f672fa flags:# query_id:long error:flags.0?string shipping_options:flags.1?Vector = Bool; 1012 | messages.setBotPrecheckoutResults#9c2dd95 flags:# success:flags.1?true query_id:long error:flags.0?string = Bool; 1013 | 1014 | updates.getState#edd4882a = updates.State; 1015 | updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; 1016 | updates.getChannelDifference#3173d78 flags:# force:flags.0?true channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference; 1017 | 1018 | photos.updateProfilePhoto#f0bb5152 id:InputPhoto = UserProfilePhoto; 1019 | photos.uploadProfilePhoto#4f32c098 file:InputFile = photos.Photo; 1020 | photos.deletePhotos#87cf7f2f id:Vector = Vector; 1021 | photos.getUserPhotos#91cd32a8 user_id:InputUser offset:int max_id:long limit:int = photos.Photos; 1022 | 1023 | upload.saveFilePart#b304a621 file_id:long file_part:int bytes:bytes = Bool; 1024 | upload.getFile#e3a6cfb5 location:InputFileLocation offset:int limit:int = upload.File; 1025 | upload.saveBigFilePart#de7b673d file_id:long file_part:int file_total_parts:int bytes:bytes = Bool; 1026 | upload.getWebFile#24e6818d location:InputWebFileLocation offset:int limit:int = upload.WebFile; 1027 | 1028 | help.getConfig#c4f9186b = Config; 1029 | help.getNearestDc#1fb33026 = NearestDc; 1030 | help.getAppUpdate#ae2de196 = help.AppUpdate; 1031 | help.saveAppLog#6f02f748 events:Vector = Bool; 1032 | help.getInviteText#4d392343 = help.InviteText; 1033 | help.getSupport#9cdf08cd = help.Support; 1034 | help.getAppChangelog#9010ef6f prev_app_version:string = Updates; 1035 | help.getTermsOfService#350170f3 = help.TermsOfService; 1036 | help.setBotUpdatesStatus#ec22cfcd pending_updates_count:int message:string = Bool; 1037 | 1038 | channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool; 1039 | channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector = messages.AffectedMessages; 1040 | channels.deleteUserHistory#d10dd71b channel:InputChannel user_id:InputUser = messages.AffectedHistory; 1041 | channels.reportSpam#fe087810 channel:InputChannel user_id:InputUser id:Vector = Bool; 1042 | channels.getMessages#93d7b347 channel:InputChannel id:Vector = messages.Messages; 1043 | channels.getParticipants#24d98f92 channel:InputChannel filter:ChannelParticipantsFilter offset:int limit:int = channels.ChannelParticipants; 1044 | channels.getParticipant#546dd7a6 channel:InputChannel user_id:InputUser = channels.ChannelParticipant; 1045 | channels.getChannels#a7f6bbb id:Vector = messages.Chats; 1046 | channels.getFullChannel#8736a09 channel:InputChannel = messages.ChatFull; 1047 | channels.createChannel#f4893d7f flags:# broadcast:flags.0?true megagroup:flags.1?true title:string about:string = Updates; 1048 | channels.editAbout#13e27f1e channel:InputChannel about:string = Bool; 1049 | channels.editAdmin#eb7611d0 channel:InputChannel user_id:InputUser role:ChannelParticipantRole = Updates; 1050 | channels.editTitle#566decd0 channel:InputChannel title:string = Updates; 1051 | channels.editPhoto#f12e57c9 channel:InputChannel photo:InputChatPhoto = Updates; 1052 | channels.checkUsername#10e6bd2c channel:InputChannel username:string = Bool; 1053 | channels.updateUsername#3514b3de channel:InputChannel username:string = Bool; 1054 | channels.joinChannel#24b524c5 channel:InputChannel = Updates; 1055 | channels.leaveChannel#f836aa95 channel:InputChannel = Updates; 1056 | channels.inviteToChannel#199f3a6c channel:InputChannel users:Vector = Updates; 1057 | channels.kickFromChannel#a672de14 channel:InputChannel user_id:InputUser kicked:Bool = Updates; 1058 | channels.exportInvite#c7560885 channel:InputChannel = ExportedChatInvite; 1059 | channels.deleteChannel#c0111fe3 channel:InputChannel = Updates; 1060 | channels.toggleInvites#49609307 channel:InputChannel enabled:Bool = Updates; 1061 | channels.exportMessageLink#c846d22d channel:InputChannel id:int = ExportedMessageLink; 1062 | channels.toggleSignatures#1f69b606 channel:InputChannel enabled:Bool = Updates; 1063 | channels.updatePinnedMessage#a72ded52 flags:# silent:flags.0?true channel:InputChannel id:int = Updates; 1064 | channels.getAdminedPublicChannels#8d8d82d7 = messages.Chats; 1065 | 1066 | bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON; 1067 | bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool; 1068 | 1069 | payments.getPaymentForm#99f09745 msg_id:int = payments.PaymentForm; 1070 | payments.getPaymentReceipt#a092a980 msg_id:int = payments.PaymentReceipt; 1071 | payments.validateRequestedInfo#770a8e74 flags:# save:flags.0?true msg_id:int info:PaymentRequestedInfo = payments.ValidatedRequestedInfo; 1072 | payments.sendPaymentForm#2b8879b3 flags:# msg_id:int requested_info_id:flags.0?string shipping_option_id:flags.1?string credentials:InputPaymentCredentials = payments.PaymentResult; 1073 | payments.getSavedInfo#227d824b = payments.SavedInfo; 1074 | payments.clearSavedInfo#d83d70c1 flags:# credentials:flags.0?true info:flags.1?true = Bool; 1075 | 1076 | phone.getCallConfig#55451fa9 = DataJSON; 1077 | phone.requestCall#5b95b3d4 user_id:InputUser random_id:int g_a_hash:bytes protocol:PhoneCallProtocol = phone.PhoneCall; 1078 | phone.acceptCall#3bd2b4a0 peer:InputPhoneCall g_b:bytes protocol:PhoneCallProtocol = phone.PhoneCall; 1079 | phone.confirmCall#2efe1722 peer:InputPhoneCall g_a:bytes key_fingerprint:long protocol:PhoneCallProtocol = phone.PhoneCall; 1080 | phone.receivedCall#17d54f61 peer:InputPhoneCall = Bool; 1081 | phone.discardCall#78d413a6 peer:InputPhoneCall duration:int reason:PhoneCallDiscardReason connection_id:long = Updates; 1082 | phone.setCallRating#1c536a34 peer:InputPhoneCall rating:int comment:string = Updates; 1083 | phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool; 1084 | 1085 | // LAYER 65 1086 | -------------------------------------------------------------------------------- /session.go: -------------------------------------------------------------------------------- 1 | package mtproto 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "math/rand" 7 | "time" 8 | ) 9 | 10 | // Session storage interface 11 | type ISession interface { 12 | // Load is deserialization method 13 | Load() error 14 | // Save is serialization method 15 | Save() error 16 | 17 | IsIPv6() bool 18 | // IsEncrypted returns true if AuthKey, ServerSalt and SessionID fields aren't empty 19 | IsEncrypted() bool 20 | 21 | GetAddress() string 22 | GetAuthKey() []byte 23 | GetAuthKeyHash() []byte 24 | GetServerSalt() []byte 25 | GetSessionID() int64 26 | 27 | SetAddress(string) 28 | SetAuthKey([]byte) 29 | SetAuthKeyHash([]byte) 30 | SetServerSalt([]byte) 31 | SetSessionID(int64) 32 | 33 | UseIPv6(bool) 34 | Encrypted(bool) 35 | } 36 | 37 | type Session struct { 38 | // TODO: ReaderWriter interface 39 | file *os.File 40 | 41 | address string 42 | authKey []byte 43 | authKeyHash []byte 44 | serverSalt []byte 45 | sessionId int64 46 | useIPv6 bool 47 | encrypted bool 48 | } 49 | 50 | func NewSession(file *os.File) ISession { 51 | session := &Session{ 52 | file: file, 53 | } 54 | 55 | rand.Seed(time.Now().UnixNano()) 56 | session.SetSessionID(rand.Int63()) 57 | 58 | return session 59 | } 60 | 61 | func (s *Session) Load() error { 62 | // TODO: Magic number 63 | buffer := make([]byte, 1024*4) 64 | n, _ := s.file.ReadAt(buffer, 0) 65 | if n <= 0 { 66 | return errors.New("New session") 67 | } 68 | 69 | decoder := NewDecodeBuf(buffer) 70 | s.authKey = decoder.StringBytes() 71 | s.authKeyHash = decoder.StringBytes() 72 | s.serverSalt = decoder.StringBytes() 73 | s.address = decoder.String() 74 | s.useIPv6 = false 75 | if decoder.UInt() == 1 { 76 | s.useIPv6 = true 77 | } 78 | 79 | if decoder.err != nil { 80 | return decoder.err 81 | } 82 | 83 | return nil 84 | } 85 | 86 | func (s Session) Save() error { 87 | // TODO: Magic number 88 | buffer := NewEncodeBuf(1024) 89 | buffer.StringBytes(s.authKey) 90 | buffer.StringBytes(s.authKeyHash) 91 | buffer.StringBytes(s.serverSalt) 92 | buffer.String(s.address) 93 | 94 | var useIPv6UInt uint32 95 | if s.useIPv6 { 96 | useIPv6UInt = 1 97 | } 98 | buffer.UInt(useIPv6UInt) 99 | 100 | err := s.file.Truncate(0) 101 | if err != nil { 102 | return err 103 | } 104 | 105 | _, err = s.file.WriteAt(buffer.buf, 0) 106 | if err != nil { 107 | return err 108 | } 109 | 110 | return nil 111 | } 112 | 113 | func (s Session) IsIPv6() bool { 114 | return s.useIPv6 115 | } 116 | 117 | func (s Session) IsEncrypted() bool { 118 | return s.encrypted 119 | } 120 | 121 | func (s Session) GetAddress() string { 122 | return s.address 123 | } 124 | 125 | func (s Session) GetAuthKey() []byte { 126 | return s.authKey 127 | } 128 | 129 | func (s Session) GetAuthKeyHash() []byte { 130 | return s.authKeyHash 131 | } 132 | 133 | func (s Session) GetServerSalt() []byte { 134 | return s.serverSalt 135 | } 136 | 137 | func (s Session) GetSessionID() int64 { 138 | return s.sessionId 139 | } 140 | 141 | func (s *Session) SetAddress(address string) { 142 | s.address = address 143 | } 144 | 145 | func (s *Session) SetAuthKey(authKey []byte) { 146 | s.authKey = make([]byte, len(authKey)) 147 | copy(s.authKey, authKey) 148 | } 149 | 150 | func (s *Session) SetAuthKeyHash(authKeyHash []byte) { 151 | s.authKeyHash = make([]byte, len(authKeyHash)) 152 | copy(s.authKeyHash, authKeyHash) 153 | } 154 | 155 | func (s *Session) SetServerSalt(salt []byte) { 156 | s.serverSalt = make([]byte, len(salt)) 157 | copy(s.serverSalt, salt) 158 | } 159 | 160 | func (s *Session) SetSessionID(ID int64) { 161 | s.sessionId = ID 162 | } 163 | 164 | func (s *Session) UseIPv6(useIPv6 bool) { 165 | s.useIPv6 = useIPv6 166 | } 167 | 168 | func (s *Session) Encrypted(encrypted bool) { 169 | s.encrypted = encrypted 170 | } 171 | -------------------------------------------------------------------------------- /types.go: -------------------------------------------------------------------------------- 1 | package mtproto 2 | 3 | import "math/big" 4 | 5 | type TL interface { 6 | encode() []byte 7 | } 8 | 9 | const crc_vector = 0x1cb5c415 // Processed manually 10 | 11 | const crc_msg_container = 0x73f1f8dc 12 | 13 | type TL_msg_container struct { 14 | Items []TL_MT_message 15 | } 16 | 17 | type TL_MT_message struct { 18 | Msg_id int64 19 | Seq_no int32 20 | Size int32 21 | Data interface{} 22 | } 23 | 24 | const crc_req_pq = 0x60469778 25 | 26 | type TL_req_pq struct { 27 | Nonce []byte 28 | } 29 | 30 | const crc_p_q_inner_data = 0x83c95aec 31 | 32 | type TL_p_q_inner_data struct { 33 | Pq *big.Int 34 | P *big.Int 35 | Q *big.Int 36 | Nonce []byte 37 | Server_nonce []byte 38 | New_nonce []byte 39 | } 40 | 41 | const crc_req_DH_params = 0xd712e4be 42 | 43 | type TL_req_DH_params struct { 44 | Nonce []byte 45 | Server_nonce []byte 46 | P *big.Int 47 | Q *big.Int 48 | Fp uint64 49 | Encdata []byte 50 | } 51 | 52 | const crc_client_DH_inner_data = 0x6643b654 53 | 54 | type TL_client_DH_inner_data struct { 55 | Nonce []byte 56 | Server_nonce []byte 57 | Retry int64 58 | G_b *big.Int 59 | } 60 | 61 | const crc_set_client_DH_params = 0xf5045f1f 62 | 63 | type TL_set_client_DH_params struct { 64 | Nonce []byte 65 | Server_nonce []byte 66 | Encdata []byte 67 | } 68 | 69 | const crc_resPQ = 0x05162463 70 | 71 | type TL_resPQ struct { 72 | Nonce []byte 73 | Server_nonce []byte 74 | Pq *big.Int 75 | Fingerprints []int64 76 | } 77 | 78 | const crc_server_DH_params_ok = 0xd0e8075c 79 | 80 | type TL_server_DH_params_ok struct { 81 | Nonce []byte 82 | Server_nonce []byte 83 | Encrypted_answer []byte 84 | } 85 | 86 | const crc_server_DH_params_fail = 0x79cb045d 87 | 88 | type TL_server_DH_params_fail struct { 89 | Nonce []byte 90 | Server_nonce []byte 91 | New_nonce_hash []byte 92 | } 93 | 94 | const crc_server_DH_inner_data = 0xb5890dba 95 | 96 | type TL_server_DH_inner_data struct { 97 | Nonce []byte 98 | Server_nonce []byte 99 | G int32 100 | Dh_prime *big.Int 101 | G_a *big.Int 102 | Server_time int32 103 | } 104 | 105 | const crc_new_session_created = 0x9ec20908 106 | 107 | type TL_new_session_created struct { 108 | First_msg_id int64 109 | Unique_id int64 110 | Server_salt []byte 111 | } 112 | 113 | const crc_bad_server_salt = 0xedab447b 114 | 115 | type TL_bad_server_salt struct { 116 | Bad_msg_id int64 117 | Bad_msg_seqno int32 118 | Error_code int32 119 | New_server_salt []byte 120 | } 121 | 122 | const crc_bad_msg_notification = 0xa7eff811 123 | 124 | type TL_bad_msg_notification struct { 125 | Bad_msg_id int64 126 | Bad_msg_seqno int32 127 | Error_code int32 128 | } 129 | 130 | const crc_msgs_ack = 0x62d6b459 131 | 132 | type TL_msgs_ack struct { 133 | MsgIds []int64 134 | } 135 | 136 | const crc_rpc_result = 0xf35c6d01 137 | 138 | type TL_rpc_result struct { 139 | Req_msg_id int64 140 | Obj interface{} 141 | } 142 | 143 | const crc_rpc_error = 0x2144ca19 144 | 145 | type TL_rpc_error struct { 146 | Error_code int32 147 | Error_message string 148 | } 149 | 150 | func (err TL_rpc_error) Error() string { 151 | return err.Error_message 152 | } 153 | 154 | const crc_dh_gen_ok = 0x3bcbf734 155 | 156 | type TL_dh_gen_ok struct { 157 | Nonce []byte 158 | Server_nonce []byte 159 | New_nonce_hash1 []byte 160 | } 161 | 162 | const crc_ping = 0x7abe77ec 163 | 164 | type TL_ping struct { 165 | Ping_id int64 166 | } 167 | 168 | const crc_pong = 0x347773c5 169 | 170 | type TL_pong struct { 171 | Msg_id int64 172 | Ping_id int64 173 | } 174 | 175 | const crc_gzip_packed = 0x3072cfa1 176 | -------------------------------------------------------------------------------- /updates.go: -------------------------------------------------------------------------------- 1 | package mtproto 2 | 3 | func (m *MTProto) UpdatesGetState() (*TL, error) { 4 | return m.InvokeSync(TL_updates_getState{}) 5 | } 6 | 7 | func (m *MTProto) UpdatesGetDifference(pts, ptsTotalLimit, date, qts int32) (*TL, error) { 8 | return m.InvokeSync(TL_updates_getDifference{ 9 | Pts: pts, 10 | Pts_total_limit: ptsTotalLimit, 11 | Date: date, 12 | Qts: qts, 13 | }) 14 | } 15 | 16 | func (m *MTProto) UpdatesGetChannelDifference(force bool, channel, filter TL, pts, limit int32) (*TL, error) { 17 | return m.InvokeSync(TL_updates_getChannelDifference{ 18 | Force: force, 19 | Channel: channel, 20 | Filter: filter, 21 | Pts: pts, 22 | Limit: limit, 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /users.go: -------------------------------------------------------------------------------- 1 | package mtproto 2 | 3 | import "fmt" 4 | 5 | func (m *MTProto) UsersGetFullUsers(id TL) (*TL_userFull, error) { 6 | var user TL_userFull 7 | tl, err := m.InvokeSync(TL_users_getFullUser{ 8 | Id: id, 9 | }) 10 | if err != nil { 11 | return nil, err 12 | } 13 | 14 | switch (*tl).(type) { 15 | case TL_userFull: 16 | user = (*tl).(TL_userFull) 17 | default: 18 | return nil, fmt.Errorf("Got: %T", *tl) 19 | } 20 | 21 | return &user, nil 22 | } 23 | --------------------------------------------------------------------------------