1
0
painor 4 жил өмнө
parent
commit
057be18646
100 өөрчлөгдсөн 5828 нэмэгдсэн , 2330 устгасан
  1. 5 0
      .bablerc
  2. 8 0
      .eslintignore
  3. 40 0
      .eslintrc.json
  4. 10 0
      .github/workflows/lint.yml
  5. 25 7
      .gitignore
  6. 31 0
      .npmignore
  7. 21 0
      LICENSE
  8. 21 0
      README.md
  9. 41 0
      __tests__/crypto/AES.spec.js
  10. 19 0
      __tests__/crypto/calcKey.spec.js
  11. 11 0
      __tests__/crypto/factorizator.spec.js
  12. 70 0
      __tests__/crypto/readBuffer.spec.js
  13. 107 0
      __tests__/extensions/HTML.spec.js
  14. 95 0
      __tests__/extensions/Markdown.spec.js
  15. 100 0
      __tests__/extensions/Scanner.spec.js
  16. 19 0
      babel.config.js
  17. 38 0
      examples/betterLogging.js
  18. 13 0
      examples/main.js
  19. 47 0
      examples/simpleLogin.html
  20. 78 0
      examples/simpleLogin.js
  21. 12 0
      gramjs/.editorconfig
  22. 14 5
      gramjs/Helpers.js
  23. 1 0
      gramjs/Password.js
  24. 7 15
      gramjs/Utils.js
  25. 1 0
      gramjs/Version.js
  26. 22 0
      gramjs/client/TelegramClient.d.ts
  27. 74 619
      gramjs/client/TelegramClient.js
  28. 138 42
      gramjs/client/auth.ts
  29. 189 0
      gramjs/client/downloadFile.ts
  30. 2 27
      gramjs/client/uploadFile.ts
  31. 0 0
      gramjs/crypto/AuthKey.js
  32. 0 0
      gramjs/crypto/CTR.js
  33. 0 0
      gramjs/crypto/Factorizator.js
  34. 37 0
      gramjs/crypto/IGE.js
  35. 22 45
      gramjs/crypto/RSA.js
  36. 55 0
      gramjs/crypto/converters.ts
  37. 124 0
      gramjs/crypto/crypto.js
  38. 51 0
      gramjs/crypto/words.ts
  39. 4 1
      gramjs/errors/Common.js
  40. 0 0
      gramjs/errors/RPCBaseErrors.js
  41. 0 0
      gramjs/errors/index.js
  42. 0 0
      gramjs/events/NewMessage.js
  43. 21 0
      gramjs/events/Raw.js
  44. 22 0
      gramjs/events/common.js
  45. 8 0
      gramjs/events/index.js
  46. 30 0
      gramjs/extensions/AsyncQueue.js
  47. 0 0
      gramjs/extensions/BinaryReader.js
  48. 15 0
      gramjs/extensions/BinaryWriter.js
  49. 9 9
      gramjs/extensions/Logger.js
  50. 0 0
      gramjs/extensions/MessagePacker.js
  51. 0 0
      gramjs/extensions/PromisedWebSockets.js
  52. 0 0
      gramjs/extensions/index.js
  53. 10 0
      gramjs/index.d.ts
  54. 16 0
      gramjs/index.js
  55. 7 5
      gramjs/network/Authenticator.js
  56. 0 0
      gramjs/network/MTProtoPlainSender.js
  57. 7 5
      gramjs/network/MTProtoSender.js
  58. 5 5
      gramjs/network/MTProtoState.js
  59. 0 0
      gramjs/network/RequestState.js
  60. 0 0
      gramjs/network/connection/Connection.js
  61. 0 0
      gramjs/network/connection/TCPAbridged.js
  62. 57 0
      gramjs/network/connection/TCPFull.js
  63. 0 0
      gramjs/network/connection/TCPObfuscated.js
  64. 11 0
      gramjs/network/connection/index.js
  65. 0 0
      gramjs/network/index.js
  66. 0 0
      gramjs/sessions/Abstract.js
  67. 0 0
      gramjs/sessions/CacheApiSession.js
  68. 2 2
      gramjs/sessions/Memory.js
  69. 18 0
      gramjs/sessions/StringSession.js
  70. 2 2
      gramjs/sessions/index.js
  71. 42 0
      gramjs/tl/MTProtoRequest.js
  72. 845 55
      gramjs/tl/api.d.ts
  73. 37 110
      gramjs/tl/api.js
  74. 9 2
      gramjs/tl/core/GZIPPacked.js
  75. 0 0
      gramjs/tl/core/MessageContainer.js
  76. 0 0
      gramjs/tl/core/RPCResult.js
  77. 0 0
      gramjs/tl/core/TLMessage.js
  78. 17 0
      gramjs/tl/core/index.js
  79. 326 0
      gramjs/tl/generationHelpers.js
  80. 4 3
      gramjs/tl/index.js
  81. 66 12
      gramjs/tl/static/api.reduced.tl
  82. 1500 0
      gramjs/tl/static/api.tl
  83. 37 0
      gramjs/tl/static/schema.reduced.tl
  84. 0 0
      gramjs/tl/static/schema.tl
  85. 57 0
      gramjs/tl/types-generator/generate.js
  86. 219 0
      gramjs/tl/types-generator/template.js
  87. 186 0
      index.js
  88. 188 0
      jest.config.js
  89. 462 362
      package-lock.json
  90. 41 79
      package.json
  91. 0 13
      src/@types/global.d.ts
  92. 0 64
      src/api/gramjs/builders/chats.ts
  93. 0 34
      src/api/gramjs/builders/common.ts
  94. 0 208
      src/api/gramjs/builders/messages.ts
  95. 0 13
      src/api/gramjs/builders/peers.ts
  96. 0 39
      src/api/gramjs/builders/users.ts
  97. 0 212
      src/api/gramjs/client.ts
  98. 0 122
      src/api/gramjs/connectors/auth.ts
  99. 0 129
      src/api/gramjs/connectors/chats.ts
  100. 0 84
      src/api/gramjs/connectors/files.ts

+ 5 - 0
.bablerc

@@ -0,0 +1,5 @@
+ {
+ "presets": [
+      "@babel/preset-env"
+    ]
+ }

+ 8 - 0
.eslintignore

@@ -0,0 +1,8 @@
+gramjs_generator/data/html/js/search.js
+gramjs/tl/functions
+gramjs/tl/patched
+gramjs/tl/types
+gramjs/tl/AllTLObjects.js
+gramjs/errors/RPCErrorList.js
+browser/
+example.js

+ 40 - 0
.eslintrc.json

@@ -0,0 +1,40 @@
+{
+    "env": {
+        "commonjs": true,
+        "es6": true,
+        "node": true,
+        "jest": true
+    },
+    "extends": ["eslint:recommended"],
+    "globals": {
+        "BigInt": true,
+        "Atomics": "readonly",
+        "SharedArrayBuffer": "readonly"
+    },
+    "parser": "babel-eslint",
+    "parserOptions": {
+        "ecmaVersion": 2018
+    },
+    "rules": {
+        "arrow-parens": ["error", "as-needed"],
+        "brace-style": ["error", "1tbs"],
+        "comma-dangle": ["error", "always-multiline"],
+        "curly": ["error", "all"],
+        "indent": ["error", 4],
+        "linebreak-style": ["error", "unix"],
+        "no-var": "error",
+        "operator-linebreak": ["error", "after"],
+        "prefer-const": ["error", { "destructuring": "any" }],
+        "quotes": ["error", "single", { "avoidEscape": true }],
+        "semi": ["error", "never"],
+        "max-len": [
+            "error",
+            {
+                "code": 120,
+                "ignoreUrls": true,
+                "ignoreStrings": true,
+                "ignoreTemplateLiterals": true
+            }
+        ]
+    }
+}

+ 10 - 0
.github/workflows/lint.yml

@@ -0,0 +1,10 @@
+name: Lint
+on: [push, pull_request]
+jobs:
+    lint:
+        runs-on: ubuntu-latest
+        steps:
+            - uses: actions/checkout@v2
+            - uses: Adzz/yarn_command_action@v1.0.0
+              with:
+                  command: lint

+ 25 - 7
.gitignore

@@ -1,8 +1,26 @@
-node_modules
-dist
-.cache
+/node_modules/
+/.vscode/
+/.idea/
+# Generated code
+/docs/
+/gramjs/tl/functions/
+/gramjs/tl/types/
+/gramjs/tl/patched/
+/gramjs/tl/AllTLObjects.js
+/gramjs/errors/RPCErrorList.js
+/dist/
+/coverage/
+
+# User session
+*.session
+usermedia/
+
+# Quick tests should live in this file
+example.js
+
+# dotenv
 .env
-src/lib/gramjs/build/
-build-contest/
-.idea
-*.iml
+
+settings
+
+/browser/

+ 31 - 0
.npmignore

@@ -0,0 +1,31 @@
+/node_modules/
+/.vscode/
+/.idea/
+# Generated code
+/docs/
+/gramjs/tl/functions/
+/gramjs/tl/types/
+/gramjs/tl/patched/
+/gramjs/tl/AllTLObjects.js
+/gramjs/errors/RPCErrorList.js
+/dist/
+
+# User session
+*.session
+usermedia/
+
+# Quick tests should live in this file
+example.js
+
+# dotenv
+.env
+
+settings
+
+/browser/
+
+.babelrc
+.eslintignore
+.eslintrc.json
+webpack.config.babel.js
+/examples/

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 GramJS
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 21 - 0
README.md

@@ -0,0 +1,21 @@
+# GramJS
+**GramJS** is Telegram client implementation in Javascript. This project's _core_ is **completely based** on
+[Telethon](https://github.com/LonamiWebs/Telethon). All the files which are fully based on it.
+don't forget to have a look to the original project.
+
+### Obtaining your `API ID` and `Hash`
+1. Follow [this link](https://my.telegram.org) and login with your phone number.
+2. Click under `API Development tools`.
+3. A `Create new application` window will appear. Fill in your application details.
+There is no need to enter any `URL`, and only the first two fields (`App title` and `Short name`)
+can be changed later as long as I'm aware.
+4. Click on `Create application` at the end. Now that you have the `API ID` and `Hash`
+
+## Running GramJS
+First of all, you need to run the `index.js` by issuing `node index.js gen`. This will generate all the
+TLObjects from the given `scheme.tl` file.
+Then check the `examples` folder to check how to use the library
+
+## Using raw api
+Currently you can use any raw api function using `await client.invoke(new RequestClass(args))` .
+you can find all the requests and types at https://gram.js.org/

+ 41 - 0
__tests__/crypto/AES.spec.js

@@ -0,0 +1,41 @@
+const AES = require('../../gramjs/crypto/AES')
+const AESModeCTR = require('../../gramjs/crypto/AESCTR')
+describe('IGE encrypt function', () => {
+    test('it should return 4a657a834edc2956ec95b2a42ec8c1f2d1f0a6028ac26fd830ed23855574b4e69dd1a2be2ba18a53a49b879b2' +
+        '45e1065e14b6e8ac5ba9b24befaff3209b77b5f', () => {
+        const plainText = Buffer.from('this should hold a 64 characters long string. only 10 more left.')
+        const iv = Buffer.from('the iv needs 32 characters long.')
+        const key = Buffer.from('the key needs 32 characters long')
+        const encrypted = Buffer.from('4a657a834edc2956ec95b2a42ec8c1f2d1f0a6028ac26fd830ed23855574b4e69dd1a2be' +
+            '2ba18a53a49b879b245e1065e14b6e8ac5ba9b24befaff3209b77b5f', 'hex')
+        expect(AES.encryptIge(plainText, key, iv)).toEqual(encrypted)
+    })
+})
+describe('IGE decrypt function', () => {
+    test('it should return "this should hold a 64 characters long string. only 10 more left."', () => {
+        const encrypted = Buffer.from('4a657a834edc2956ec95b2a42ec8c1f2d1f0a6028ac26fd830ed23855574b4e69dd1a2be' +
+            '2ba18a53a49b879b245e1065e14b6e8ac5ba9b24befaff3209b77b5f', 'hex')
+        const iv = Buffer.from('the iv needs 32 characters long.')
+        const key = Buffer.from('the key needs 32 characters long')
+        const plainText = Buffer.from('this should hold a 64 characters long string. only 10 more left.')
+        expect(AES.decryptIge(encrypted, key, iv)).toEqual(plainText)
+
+    })
+})
+describe('CTR encrypt function', () => {
+    test('it should return 5f40f14f8b70178f70e8045b44eff5f1b148714f23cd and' +
+        ' cd0779d148b466935cf573450212451692bc82fccd5b106e53', () => {
+        const encryptKey = Buffer.from('the key needs 32 characters long')
+        const encryptIv = Buffer.from('the iv does not.')
+        const encryptor = new AESModeCTR(encryptKey, encryptIv)
+        const firstData = Buffer.from('this can be any length')
+        const firstResult = encryptor.encrypt(firstData)
+        const secondData = Buffer.from('this also can be anything')
+        const secondResult = encryptor.encrypt(secondData)
+
+        const outputFirst = Buffer.from('5f40f14f8b70178f70e8045b44eff5f1b148714f23cd', 'hex')
+        const outputSecond = Buffer.from('cd0779d148b466935cf573450212451692bc82fccd5b106e53', 'hex')
+        expect([firstResult, secondResult]).toEqual([outputFirst, outputSecond])
+    })
+})
+

+ 19 - 0
__tests__/crypto/calcKey.spec.js

@@ -0,0 +1,19 @@
+const MTProtoState = require('../../gramjs/network/MTProtoState')
+
+describe('calcKey function', () => {
+    test('it should return 0x93355e3f1f50529b6fb93eaf97f29b69c16345f53621e9d45cd9a11ddfbebac9 and' +
+        ' 11e94363ad7145222e2fbac4aaa27f01a6d832fb8115e89395bc43e23f868e47', () => {
+        const authKey = Buffer.from('bbf38532a79cd64363b490b3bc5e258adfc1d1a67ef3c6d322caac603f90a15215b609' +
+            '0ccb2226b477b24eb3412757d078d53c72b81864d1376ff20eb405a591781726495407628d8d611e37ecd6e23c605b57c5' +
+            '3b40270bac7e7de0312a5deb3a1a16e65808b944fcf700d3788da10074d5c088e9e6aca119320d7f07c16d7e3c9fd48e9d' +
+            '3f50ccc5276a30002d9919831bf783c368ce4b3e6f25f95875ec9315523cfcaa3ee50b1e40e5552cee2e16eec86b46308c' +
+            '97f808d58f249479bb0ee1b7b08cf7f0fc047fbe38df6083558494e732dbf26d16b1538c22d361bf31d3dc4f2b2cb115b3' +
+            'bfac1ec45c960e0854221cf484533025fa679a9b7a8ae11a00', 'hex')
+        const msgKey = Buffer.from('00f285b0bf254b5242e075bf87806c51', 'hex')
+        const aesKey = Buffer.from('93355e3f1f50529b6fb93eaf97f29b69c16345f53621e9d45cd9a11ddfbebac9', 'hex')
+        const aesIv = Buffer.from('11e94363ad7145222e2fbac4aaa27f01a6d832fb8115e89395bc43e23f868e47', 'hex')
+        const { key, iv } = new MTProtoState()._calcKey(authKey, msgKey, false)
+
+        expect([aesKey, aesIv]).toEqual([key, iv])
+    })
+})

+ 11 - 0
__tests__/crypto/factorizator.spec.js

@@ -0,0 +1,11 @@
+const Factorizator = require('../../gramjs/crypto/Factorizator')
+
+describe('calcKey function', () => {
+    test('it should return 0x20a13b25e1726bfc', () => {
+        const input = BigInt('325672672642762197972197217945794795197912791579174576454600704764276407047277')
+        const { p, q } = Factorizator.factorize(input)
+        const outP = BigInt(19)
+        const outQ = BigInt('17140666981198010419589327260304989220942778504167082971294773934961916160383')
+        expect([p, q]).toEqual([outP, outQ])
+    })
+})

+ 70 - 0
__tests__/crypto/readBuffer.spec.js

@@ -0,0 +1,70 @@
+const Helpers = require('../../gramjs/Helpers')
+
+describe('readBufferFromBigInt 8 bytes function', () => {
+    test('it should return 0x20a13b25e1726bfc', () => {
+        const input = BigInt('-257986242325798624')
+        const output = Buffer.from('20a13b25e1726bfc', 'hex')
+        expect(Helpers.readBufferFromBigInt(input, 8, true, true)).toEqual(output)
+    })
+    test('it should return 0xe05ec4da1e8d9403', () => {
+        const input = BigInt('257986242325798624')
+        const output = Buffer.from('e05ec4da1e8d9403', 'hex')
+        expect(Helpers.readBufferFromBigInt(input, 8, true, false)).toEqual(output)
+    })
+    test('it should return 0xfc6b72e1253ba120', () => {
+        const input = BigInt('-257986242325798624')
+        const output = Buffer.from('fc6b72e1253ba120', 'hex')
+        expect(Helpers.readBufferFromBigInt(input, 8, false, true)).toEqual(output)
+    })
+    test('it should return 0x03948d1edac45ee0', () => {
+        const input = BigInt('257986242325798624')
+        const output = Buffer.from('03948d1edac45ee0', 'hex')
+        expect(Helpers.readBufferFromBigInt(input, 8, false, false)).toEqual(output)
+    })
+})
+
+describe('readBufferFromBigInt 16 bytes function', () => {
+    test('it should return 0x8416c07962dac053b4346df39e5d97ec', () => {
+        const input = BigInt('-25798624232579862436622316998984984956')
+        const output = Buffer.from('8416c07962dac053b4346df39e5d97ec', 'hex')
+        expect(Helpers.readBufferFromBigInt(input, 16, true, true)).toEqual(output)
+    })
+    test('it should return 0x7ce93f869d253fac4bcb920c61a26813', () => {
+        const input = BigInt('25798624232579862436622316998984984956')
+        const output = Buffer.from('7ce93f869d253fac4bcb920c61a26813', 'hex')
+        expect(Helpers.readBufferFromBigInt(input, 16, true, false)).toEqual(output)
+    })
+    test('it should return 0xec975d9ef36d34b453c0da6279c01684', () => {
+        const input = BigInt('-25798624232579862436622316998984984956')
+        const output = Buffer.from('ec975d9ef36d34b453c0da6279c01684', 'hex')
+        expect(Helpers.readBufferFromBigInt(input, 16, false, true)).toEqual(output)
+    })
+    test('it should return 0x1368a2610c92cb4bac3f259d863fe97c', () => {
+        const input = BigInt('25798624232579862436622316998984984956')
+        const output = Buffer.from('1368a2610c92cb4bac3f259d863fe97c', 'hex')
+        expect(Helpers.readBufferFromBigInt(input, 16, false, false)).toEqual(output)
+    })
+})
+
+describe('readBufferFromBigInt 32 bytes function', () => {
+    test('it should return 0x7f113f5e2096936ec90cc4c73cc7bd3c96d20c115bf9ceb05c34232c037ff6c6', () => {
+        const input = BigInt('-25798624232579862436622316998984984912345482145214526587420145210501554564737')
+        const output = Buffer.from('7f113f5e2096936ec90cc4c73cc7bd3c96d20c115bf9ceb05c34232c037ff6c6', 'hex')
+        expect(Helpers.readBufferFromBigInt(input, 32, true, true)).toEqual(output)
+    })
+    test('it should return 0x81eec0a1df696c9136f33b38c33842c3692df3eea406314fa3cbdcd3fc800939', () => {
+        const input = BigInt('25798624232579862436622316998984984912345482145214526587420145210501554564737')
+        const output = Buffer.from('81eec0a1df696c9136f33b38c33842c3692df3eea406314fa3cbdcd3fc800939', 'hex')
+        expect(Helpers.readBufferFromBigInt(input, 32, true, false)).toEqual(output)
+    })
+    test('it should return 0xc6f67f032c23345cb0cef95b110cd2963cbdc73cc7c40cc96e9396205e3f117f', () => {
+        const input = BigInt('-25798624232579862436622316998984984912345482145214526587420145210501554564737')
+        const output = Buffer.from('c6f67f032c23345cb0cef95b110cd2963cbdc73cc7c40cc96e9396205e3f117f', 'hex')
+        expect(Helpers.readBufferFromBigInt(input, 32, false, true)).toEqual(output)
+    })
+    test('it should return 0x390980fcd3dccba34f3106a4eef32d69c34238c3383bf336916c69dfa1c0ee81', () => {
+        const input = BigInt('25798624232579862436622316998984984912345482145214526587420145210501554564737')
+        const output = Buffer.from('390980fcd3dccba34f3106a4eef32d69c34238c3383bf336916c69dfa1c0ee81', 'hex')
+        expect(Helpers.readBufferFromBigInt(input, 32, false, false)).toEqual(output)
+    })
+})

+ 107 - 0
__tests__/extensions/HTML.spec.js

@@ -0,0 +1,107 @@
+const { HTMLParser } = require('../../gramjs/extensions/HTML')
+const types = require('../../gramjs/tl/types')
+
+describe('HTMLParser', () => {
+    test('it should construct a new HTMLParser', () => {
+        const parser = new HTMLParser('Hello world')
+        expect(parser.text).toEqual('')
+        expect(parser.entities).toEqual([])
+    })
+
+    describe('.parse', () => {
+        test('it should parse bold entities', () => {
+            const parser = new HTMLParser('Hello <strong>world</strong>')
+            const [text, entities] = parser.parse()
+            expect(text).toEqual('Hello world')
+            expect(entities.length).toEqual(1)
+            expect(entities[0]).toBeInstanceOf(types.MessageEntityBold)
+        })
+
+        test('it should parse italic entities', () => {
+            const parser = new HTMLParser('Hello <em>world</em>')
+            const [text, entities] = parser.parse()
+            expect(text).toEqual('Hello world')
+            expect(entities.length).toEqual(1)
+            expect(entities[0]).toBeInstanceOf(types.MessageEntityItalic)
+        })
+
+        test('it should parse code entities', () => {
+            const parser = new HTMLParser('Hello <code>world</code>')
+            const [text, entities] = parser.parse()
+            expect(text).toEqual('Hello world')
+            expect(entities.length).toEqual(1)
+            expect(entities[0]).toBeInstanceOf(types.MessageEntityCode)
+        })
+
+        test('it should parse pre entities', () => {
+            const parser = new HTMLParser('Hello <pre>world</pre>')
+            const [text, entities] = parser.parse()
+            expect(text).toEqual('Hello world')
+            expect(entities.length).toEqual(1)
+            expect(entities[0]).toBeInstanceOf(types.MessageEntityPre)
+        })
+
+        test('it should parse strike entities', () => {
+            const parser = new HTMLParser('Hello <del>world</del>')
+            const [text, entities] = parser.parse()
+            expect(text).toEqual('Hello world')
+            expect(entities.length).toEqual(1)
+            expect(entities[0]).toBeInstanceOf(types.MessageEntityStrike)
+        })
+
+        test('it should parse link entities', () => {
+            const parser = new HTMLParser('Hello <a href="https://hello.world">world</a>')
+            const [text, entities] = parser.parse()
+            expect(text).toEqual('Hello world')
+            expect(entities.length).toEqual(1)
+            expect(entities[0]).toBeInstanceOf(types.MessageEntityTextUrl)
+            expect(entities[0].url).toEqual('https://hello.world')
+        })
+
+        test('it should parse nested entities', () => {
+            const parser = new HTMLParser('Hello <strong><em>world</em></strong>')
+            const [text, entities] = parser.parse()
+            expect(text).toEqual('Hello world')
+            expect(entities.length).toEqual(2)
+            expect(entities[0]).toBeInstanceOf(types.MessageEntityItalic)
+            expect(entities[1]).toBeInstanceOf(types.MessageEntityBold)
+        })
+
+        test('it should parse multiple entities', () => {
+            const parser = new HTMLParser('<em>Hello</em> <strong>world</strong>')
+            const [text, entities] = parser.parse()
+            expect(text).toEqual('Hello world')
+            expect(entities.length).toEqual(2)
+            expect(entities[0]).toBeInstanceOf(types.MessageEntityItalic)
+            expect(entities[1]).toBeInstanceOf(types.MessageEntityBold)
+        })
+    })
+
+    describe('.unparse', () => {
+        test('it should create a markdown string from raw text and entities', () => {
+            const unparsed = '<strong>hello</strong> <em>hello</em> <del>hello</del> <code>hello</code> <pre>hello</pre> <a href="https://hello.world">hello</a>'
+            const strippedText = 'hello hello hello hello hello hello'
+            const rawEntities = [
+                new types.MessageEntityBold({ offset: 0, length: 5 }),
+                new types.MessageEntityItalic({ offset: 6, length: 5 }),
+                new types.MessageEntityStrike({ offset: 12, length: 5 }),
+                new types.MessageEntityCode({ offset: 18, length: 5 }),
+                new types.MessageEntityPre({ offset: 24, length: 5 }),
+                new types.MessageEntityTextUrl({ offset: 30, length: 5, url: 'https://hello.world' }),
+            ]
+            const text = HTMLParser.unparse(strippedText, rawEntities)
+            expect(text).toEqual(unparsed)
+        })
+
+        test('it should unparse nested entities', () => {
+            const unparsed = '<strong><em>Hello world</em></strong>'
+            const strippedText = 'Hello world'
+            const rawEntities = [
+                new types.MessageEntityBold({ offset: 0, length: 11 }),
+                new types.MessageEntityItalic({ offset: 0, length: 11 }),
+            ]
+            const text = HTMLParser.unparse(strippedText, rawEntities)
+            expect(text).toEqual(unparsed)
+        })
+    })
+})

+ 95 - 0
__tests__/extensions/Markdown.spec.js

@@ -0,0 +1,95 @@
+const { MarkdownParser } = require('../../gramjs/extensions/Markdown')
+const types = require('../../gramjs/tl/types')
+
+describe('MarkdownParser', () => {
+    test('it should construct a new MarkdownParser', () => {
+        const parser = new MarkdownParser('Hello world')
+        expect(parser.text).toEqual('')
+        expect(parser.entities).toEqual([])
+    })
+
+    describe('.parse', () => {
+        test('it should parse bold entities', () => {
+            const parser = new MarkdownParser('Hello **world**')
+            const [text, entities] = parser.parse()
+            expect(text).toEqual('Hello world')
+            expect(entities.length).toEqual(1)
+            expect(entities[0]).toBeInstanceOf(types.MessageEntityBold)
+        })
+
+        test('it should parse italic entities', () => {
+            const parser = new MarkdownParser('Hello __world__')
+            const [text, entities] = parser.parse()
+            expect(text).toEqual('Hello world')
+            expect(entities.length).toEqual(1)
+            expect(entities[0]).toBeInstanceOf(types.MessageEntityItalic)
+        })
+
+        test('it should parse code entities', () => {
+            const parser = new MarkdownParser('Hello `world`')
+            const [text, entities] = parser.parse()
+            expect(text).toEqual('Hello world')
+            expect(entities.length).toEqual(1)
+            expect(entities[0]).toBeInstanceOf(types.MessageEntityCode)
+        })
+
+        test('it should parse pre entities', () => {
+            const parser = new MarkdownParser('Hello ```world```')
+            const [text, entities] = parser.parse()
+            expect(text).toEqual('Hello world')
+            expect(entities.length).toEqual(1)
+            expect(entities[0]).toBeInstanceOf(types.MessageEntityPre)
+        })
+
+        test('it should parse strike entities', () => {
+            const parser = new MarkdownParser('Hello ~~world~~')
+            const [text, entities] = parser.parse()
+            expect(text).toEqual('Hello world')
+            expect(entities.length).toEqual(1)
+            expect(entities[0]).toBeInstanceOf(types.MessageEntityStrike)
+        })
+
+        test('it should parse link entities', () => {
+            const parser = new MarkdownParser('Hello [world](https://hello.world)')
+            const [text, entities] = parser.parse()
+            expect(text).toEqual('Hello world')
+            expect(entities.length).toEqual(1)
+            expect(entities[0]).toBeInstanceOf(types.MessageEntityTextUrl)
+            expect(entities[0].url).toEqual('https://hello.world')
+        })
+
+        test('it should not parse nested entities', () => {
+            const parser = new MarkdownParser('Hello **__world__**')
+            const [text, entities] = parser.parse()
+            expect(text).toEqual('Hello __world__')
+            expect(entities.length).toEqual(1)
+            expect(entities[0]).toBeInstanceOf(types.MessageEntityBold)
+        })
+
+        test('it should parse multiple entities', () => {
+            const parser = new MarkdownParser('__Hello__ **world**')
+            const [text, entities] = parser.parse()
+            expect(text).toEqual('Hello world')
+            expect(entities.length).toEqual(2)
+            expect(entities[0]).toBeInstanceOf(types.MessageEntityItalic)
+            expect(entities[1]).toBeInstanceOf(types.MessageEntityBold)
+        })
+    })
+
+    describe('.unparse', () => {
+        test('it should create a markdown string from raw text and entities', () => {
+            const unparsed = '**hello** __hello__ ~~hello~~ `hello` ```hello``` [hello](https://hello.world)'
+            const strippedText = 'hello hello hello hello hello hello'
+            const rawEntities = [
+                new types.MessageEntityBold({ offset: 0, length: 5 }),
+                new types.MessageEntityItalic({ offset: 6, length: 5 }),
+                new types.MessageEntityStrike({ offset: 12, length: 5 }),
+                new types.MessageEntityCode({ offset: 18, length: 5 }),
+                new types.MessageEntityPre({ offset: 24, length: 5 }),
+                new types.MessageEntityTextUrl({ offset: 30, length: 5, url: 'https://hello.world' }),
+            ]
+            const text = MarkdownParser.unparse(strippedText, rawEntities)
+            expect(text).toEqual(unparsed)
+        })
+    })
+})

+ 100 - 0
__tests__/extensions/Scanner.spec.js

@@ -0,0 +1,100 @@
+const Scanner = require('../../gramjs/extensions/Scanner')
+
+const helloScanner = new Scanner('Hello world')
+
+describe('Scanner', () => {
+    beforeEach(() => helloScanner.reset())
+
+    test('it should construct a new Scanner', () => {
+        expect(helloScanner.str).toEqual('Hello world')
+        expect(helloScanner.pos).toEqual(0)
+        expect(helloScanner.lastMatch).toBeNull()
+    })
+
+    describe('.chr', () => {
+        test('it should return the character at the current pos', () => {
+            expect(helloScanner.chr).toEqual('H')
+        })
+    })
+
+    describe('.peek', () => {
+        test('it should return the character at the current pos', () => {
+            expect(helloScanner.peek()).toEqual('H')
+        })
+
+        test('it should return the next n characters', () => {
+            expect(helloScanner.peek(3)).toEqual('Hel')
+            expect(helloScanner.peek(5)).toEqual('Hello')
+        })
+    })
+
+    describe('.consume', () => {
+        test('it should consume the current character', () => {
+            const char = helloScanner.consume()
+            expect(char).toEqual('H')
+            expect(helloScanner.pos).toEqual(1)
+        })
+
+        test('it should consume the next n characters', () => {
+            const chars = helloScanner.consume(5)
+            expect(chars).toEqual('Hello')
+            expect(helloScanner.pos).toEqual(5)
+        })
+    })
+
+    describe('.reverse', () => {
+        test('it should set pos back n characters', () => {
+            helloScanner.consume(5)
+            helloScanner.reverse(5)
+            expect(helloScanner.pos).toEqual(0)
+        })
+
+        test('it should not go back further than 0', () => {
+            helloScanner.reverse(10)
+            expect(helloScanner.pos).toEqual(0)
+        })
+    })
+
+    describe('.scanUntil', () => {
+        test('it should scan the string for a regular expression starting at the current pos', () => {
+            helloScanner.scanUntil(/w/)
+            expect(helloScanner.pos).toEqual(6)
+        })
+
+        test('it should do nothing if the pattern is not found', () => {
+            helloScanner.scanUntil(/G/)
+            expect(helloScanner.pos).toEqual(0)
+        })
+    })
+
+    describe('.rest', () => {
+        test('it should return the unconsumed input', () => {
+            helloScanner.consume(6)
+            expect(helloScanner.rest).toEqual('world')
+        })
+    })
+
+    describe('.reset', () => {
+        test('it should reset the pos to 0', () => {
+            helloScanner.consume(5)
+            helloScanner.reset()
+            expect(helloScanner.pos).toEqual(0)
+        })
+    })
+
+    describe('.eof', () => {
+        test('it should return true if the scanner has reached the end of the input', () => {
+            expect(helloScanner.eof()).toBe(false)
+            helloScanner.consume(11)
+            expect(helloScanner.eof()).toBe(true)
+        })
+    })
+
+    describe('.bof', () => {
+        test('it should return true if pos is 0', () => {
+            expect(helloScanner.bof()).toBe(true)
+            helloScanner.consume(11)
+            expect(helloScanner.bof()).toBe(false)
+        })
+    })
+})

+ 19 - 0
babel.config.js

@@ -0,0 +1,19 @@
+// babel.config.js
+module.exports = {
+    presets: [
+        [
+            '@babel/preset-env',
+            {
+                targets: {
+                    node: 'current',
+                },
+            },
+        ],
+    ],
+    plugins: [
+        [
+            '@babel/plugin-proposal-class-properties',
+        ],
+    ],
+
+}

+ 38 - 0
examples/betterLogging.js

@@ -0,0 +1,38 @@
+rewireLoggingToElement(
+  () => document.getElementById("log"),
+  () => document.getElementById("log-container"),
+  true
+);
+
+function rewireLoggingToElement(eleLocator, eleOverflowLocator, autoScroll) {
+  fixLoggingFunc("log");
+
+  function fixLoggingFunc(name) {
+      console.old = console.log
+    console.log = function(...arg) {
+      const output = produceOutput(name, arg);
+      const eleLog = eleLocator();
+
+      if (autoScroll) {
+        const eleContainerLog = eleOverflowLocator();
+        const isScrolledToBottom =
+          eleContainerLog.scrollHeight - eleContainerLog.clientHeight <=
+          eleContainerLog.scrollTop + 1;
+        eleLog.innerHTML += output + "<br>";
+        if (isScrolledToBottom) {
+          eleContainerLog.scrollTop =
+            eleContainerLog.scrollHeight - eleContainerLog.clientHeight;
+        }
+      } else {
+        eleLog.innerHTML += output + "<br>";
+      }
+
+    };
+  }
+
+  function produceOutput(name, args) {
+      arg =  args[0].replace("%c","")
+    return '<span style="'+ args[1]+'">'+arg+"</span>&nbsp;"
+    
+  }
+}

+ 13 - 0
examples/main.js

@@ -0,0 +1,13 @@
+const { TelegramClient } = require('../gramjs')
+
+
+(async () => {
+    console.log('Loading interactive example...')
+    const sessionName = 'anon'
+    const apiId = -1
+    const apiHash = ''
+    const client = new TelegramClient(sessionName, apiId, apiHash)
+    await client.connect()
+
+    console.log('You should now be connected.')
+})()

+ 47 - 0
examples/simpleLogin.html

@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <meta http-equiv="X-UA-Compatible" content="ie=edge">
+    <title>Telegram Login</title>
+    <style>
+        #log-container {
+            overflow: auto;
+            height: 150px;
+            width: 20%;
+            resize: both;
+        }
+    </style>
+</head>
+
+<body>
+<form>
+    <div id="phoneDiv">
+        <label>phone number</label> <input disabled type="text" id="phone">
+        <button type="button" disabled id="phoneSend">Send</button>
+    </div>
+    <div id="codeDiv" style="visibility: hidden;">
+        <label>code you recieved</label> <input type="text" id="code">
+        <button type="button" id="codeSend">Send</button>
+    </div>
+    <div id="passDiv" style="visibility: hidden;">
+        <label>password</label> <input type="text" id="pass">
+        <button type="button" id="passSend">Send</button>
+    </div>
+    <div id="log-container">
+        <pre id="log"></pre>
+    </div>
+
+
+</form>
+</body>
+<script src="https://painor.dev/gramjs.js?version=0.0.2"></script>
+<!--Loading the library-->
+<script src="betterLogging.js"></script>
+<!--beautifies the ouput (this rewrites console.log)-->
+
+<script src="simpleLogin.js"></script>
+
+</html>

+ 78 - 0
examples/simpleLogin.js

@@ -0,0 +1,78 @@
+const phoneDiv = document.getElementById('phoneDiv')
+const phone = document.getElementById('phone')
+const phoneSend = document.getElementById('phoneSend')
+const codeDiv = document.getElementById('codeDiv')
+const code = document.getElementById('code')
+const codeSend = document.getElementById('codeSend')
+const passDiv = document.getElementById('passDiv')
+const pass = document.getElementById('pass')
+const passSend = document.getElementById('passSend')
+const logger = document.getElementById('log')
+
+function phoneCallback() {
+    phone.disabled = false
+    phoneSend.disabled = false
+
+    return new Promise(resolve => {
+        phoneSend.addEventListener('click', function() {
+            phone.disabled = true
+            phoneSend.disabled = true
+            resolve(phone.value)
+        })
+    })
+}
+
+
+function passwordCallback() {
+    passDiv.style.visibility = 'visible'
+
+    return new Promise(resolve => {
+        passSend.addEventListener('click', function() {
+            code.disabled = true
+            codeSend.disabled = true
+
+            resolve(pass.value)
+            alert('welcome')
+
+        })
+    })
+}
+
+function codeCallback() {
+    code.disabled = false
+    codeSend.disabled = false
+
+    codeDiv.style.visibility = 'visible'
+
+    return new Promise(resolve => {
+        codeSend.addEventListener('click', function() {
+            code.disabled = true
+            codeSend.disabled = true
+            resolve(code.value)
+        })
+    })
+}
+
+
+const { TelegramClient } = gramjs
+const { StringSession } = gramjs.session
+const apiId = process.env.APP_ID // put your api id here [for example 123456789]
+const apiHash = process.env.APP_HASH // put your api hash here [for example '123456abcfghe']
+
+const client = new TelegramClient(new StringSession(''), apiId, apiHash) // you can pass a string session here from previous logins.
+// If you want to run this example in the test servers uncomment this line
+// client.session.setDC(2, '149.154.167.40', 80)
+
+client.start({
+    phone: phoneCallback,
+    password: passwordCallback,
+    code: codeCallback,
+}).then(() => {
+    console.log('%c you should now be connected', 'color:#B54128')
+    console.log('%c your string session is ' + client.session.save(), 'color:#B54128')
+    console.log('%c you can save it to login with it next time', 'color:#B54128')
+
+})
+
+
+

+ 12 - 0
gramjs/.editorconfig

@@ -0,0 +1,12 @@
+# EditorConfig is awesome: http://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+[*]
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+charset = utf-8
+indent_style = space
+indent_size = 4

+ 14 - 5
src/lib/gramjs/Helpers.js → gramjs/Helpers.js

@@ -1,7 +1,6 @@
 const crypto = require('./crypto/crypto')
 const BigInt = require('big-integer')
 
-
 /**
  * converts a buffer to big int
  * @param buffer
@@ -29,11 +28,11 @@ function readBigIntFromBuffer(buffer, little = true, signed = false) {
  * @param number
  * @returns {Buffer}
  */
-function toSignedLittleBuffer(big, number=8) {
+function toSignedLittleBuffer(big, number = 8) {
     const bigNumber = BigInt(big)
     const byteArray = []
     for (let i = 0; i < number; i++) {
-        byteArray[i] = bigNumber.shiftRight(8*i).and(255)
+        byteArray[i] = bigNumber.shiftRight(8 * i).and(255)
     }
     return Buffer.from(byteArray)
 }
@@ -135,7 +134,6 @@ function generateRandomBytes(count) {
     return Buffer.from(crypto.randomBytes(count))
 }
 
-
 /**
  * Calculate the key based on Telegram guidelines, specifying whether it's the client or not
  * @param sharedKey
@@ -185,6 +183,15 @@ async function generateKeyDataFromNonce(serverNonce, newNonce) {
     }
 }
 
+function convertToLittle(buf) {
+    const correct = Buffer.alloc(buf.length * 4);
+
+    for (let i = 0; i < buf.length; i++) {
+        correct.writeUInt32BE(buf[i], i * 4)
+    }
+    return correct;
+}
+
 /**
  * Calculates the SHA1 digest for the given data
  * @param data
@@ -335,5 +342,7 @@ module.exports = {
     sleep,
     getByteArray,
     //isArrayLike,
-    toSignedLittleBuffer
+    toSignedLittleBuffer,
+    convertToLittle
+
 }

+ 1 - 0
src/lib/gramjs/Password.js → gramjs/Password.js

@@ -1,3 +1,4 @@
+const BigInt = require('big-integer')
 const Factorizator = require('./crypto/Factorizator')
 const { constructors } = require('./tl')
 const { readBigIntFromBuffer, readBufferFromBigInt, sha256, bigIntMod, modExp,

+ 7 - 15
src/lib/gramjs/Utils.js → gramjs/Utils.js

@@ -19,7 +19,7 @@ function _raiseCastFail(entity, target) {
  Gets the input peer for the given "entity" (user, chat or channel).
 
  A ``TypeError`` is raised if the given entity isn't a supported type
- or if ``check_hash is True`` but the entity's ``access_hash is None``
+ or if ``check_hash is True`` but the entity's ``accessHash is None``
  *or* the entity contains ``min`` information. In this case, the hash
  cannot be used for general purposes, and thus is not returned to avoid
  any issues which can derive from invalid access hashes.
@@ -57,7 +57,7 @@ function getInputPeer(entity, allowSelf = true, checkHash = true) {
                 accessHash: entity.accessHash,
             })
         } else {
-            throw new Error('User without access_hash or min info cannot be input')
+            throw new Error('User without accessHash or min info cannot be input')
         }
     }
     if (entity instanceof constructors.Chat || entity instanceof constructors.ChatEmpty ||
@@ -71,7 +71,7 @@ function getInputPeer(entity, allowSelf = true, checkHash = true) {
                 accessHash: entity.accessHash
             })
         } else {
-            throw new TypeError('Channel without access_hash or min info cannot be input')
+            throw new TypeError('Channel without accessHash or min info cannot be input')
         }
     }
     if (entity instanceof constructors.ChannelForbidden) {
@@ -107,7 +107,7 @@ function getInputPeer(entity, allowSelf = true, checkHash = true) {
     }
 
     if (entity instanceof constructors.PeerChat) {
-        return new constructors.InputPeerChat(entity.chat_id)
+        return new constructors.InputPeerChat(entity.chatId)
     }
 
     _raiseCastFail(entity, 'InputPeer')
@@ -395,8 +395,8 @@ function getPeer(peer) {
  can be identified back. User ID is left unmodified, chat ID is negated,
  and channel ID is prefixed with -100:
 
- * ``user_id``
- * ``-chat_id``
+ * ``userId``
+ * ``-chatId``
  * ``-100channel_id``
 
  The original ID and the peer type class can be returned with
@@ -518,15 +518,7 @@ function getMessageId(message) {
  * @param phone
  */
 function parsePhone(phone) {
-    if (typeof phone === 'number') {
-        return phone.toString()
-    } else {
-        phone = phone.toString()
-            .replace(/[+()\s-]/gm, '')
-        if (!isNaN(phone)) {
-            return phone
-        }
-    }
+    return phone.toString().replace(/[+()\s-]/gm, '')
 }
 
 /**

+ 1 - 0
gramjs/Version.js

@@ -0,0 +1 @@
+module.exports = '0.0.2'

+ 22 - 0
gramjs/client/TelegramClient.d.ts

@@ -0,0 +1,22 @@
+import { Api } from '..';
+
+import { BotAuthParams, UserAuthParams } from './auth';
+import { uploadFile, UploadFileParams } from './uploadFile';
+import { downloadFile, DownloadFileParams } from './downloadFile';
+
+declare class TelegramClient {
+    constructor(...args: any)
+
+    async start(authParams: UserAuthParams | BotAuthParams);
+
+    async invoke<R extends Api.AnyRequest>(request: R): Promise<R['__response']>;
+
+    async uploadFile(uploadParams: UploadFileParams): ReturnType<typeof uploadFile>;
+
+    async downloadFile(uploadParams: DownloadFileParams): ReturnType<typeof downloadFile>;
+
+    // Untyped methods.
+    [prop: string]: any;
+}
+
+export default TelegramClient;

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 74 - 619
gramjs/client/TelegramClient.js


+ 138 - 42
src/lib/gramjs/client/auth.ts → gramjs/client/auth.ts

@@ -1,13 +1,15 @@
 import { default as Api } from '../tl/api';
 import TelegramClient from './TelegramClient';
 import utils from '../Utils';
+import { sleep } from '../Helpers';
 import { computeCheck as computePasswordSrpCheck } from '../Password';
 
 export interface UserAuthParams {
     phoneNumber: string | (() => Promise<string>);
-    phoneCode: () => Promise<string>;
-    password: () => Promise<string>;
+    phoneCode: (isCodeViaApp?: boolean) => Promise<string>;
+    password: (hint?: string) => Promise<string>;
     firstAndLastNames: () => Promise<[string, string?]>;
+    qrCode: (qrCode: { token: Buffer, expires: number }) => Promise<void>;
     onError: (err: Error) => void;
     forceSMS?: boolean;
 }
@@ -21,6 +23,8 @@ interface ApiCredentials {
     apiHash: string,
 }
 
+const QR_CODE_TIMEOUT = 30000;
+
 export async function authFlow(
     client: TelegramClient,
     apiCredentials: ApiCredentials,
@@ -47,17 +51,28 @@ export async function checkAuthorization(client: TelegramClient) {
 async function signInUser(
     client: TelegramClient, apiCredentials: ApiCredentials, authParams: UserAuthParams,
 ): Promise<Api.TypeUser> {
-    let me: Api.TypeUser;
-
     let phoneNumber;
     let phoneCodeHash;
+    let isCodeViaApp = false;
 
     while (1) {
         try {
-            phoneNumber = typeof authParams.phoneNumber === 'function'
-                ? await authParams.phoneNumber()
-                : authParams.phoneNumber;
-            phoneCodeHash = await sendCode(client, apiCredentials, phoneNumber, authParams.forceSMS);
+            if (typeof authParams.phoneNumber === 'function') {
+                try {
+                    phoneNumber = await authParams.phoneNumber();
+                } catch (err) {
+                    if (err.message === 'RESTART_AUTH_WITH_QR') {
+                        return signInUserWithQrCode(client, apiCredentials, authParams);
+                    }
+
+                    throw err;
+                }
+            } else {
+                phoneNumber = authParams.phoneNumber;
+            }
+            const sendCodeResult = await sendCode(client, apiCredentials, phoneNumber, authParams.forceSMS);
+            phoneCodeHash = sendCodeResult.phoneCodeHash;
+            isCodeViaApp = sendCodeResult.isCodeViaApp;
 
             if (typeof phoneCodeHash !== 'string') {
                 throw new Error('Failed to retrieve phone code hash');
@@ -74,14 +89,13 @@ async function signInUser(
     }
 
     let phoneCode;
-    let isPasswordRequired = false;
     let isRegistrationRequired = false;
     let termsOfService;
 
     while (1) {
         try {
             try {
-                phoneCode = await authParams.phoneCode();
+                phoneCode = await authParams.phoneCode(isCodeViaApp);
             } catch (err) {
                 // This is the support for changing phone number from the phone code screen.
                 if (err.message === 'RESTART_AUTH') {
@@ -104,43 +118,19 @@ async function signInUser(
             if (result instanceof Api.auth.AuthorizationSignUpRequired) {
                 isRegistrationRequired = true;
                 termsOfService = result.termsOfService;
-            } else {
-                me = result.user;
+                break;
             }
 
-            break;
+            return result.user;
         } catch (err) {
             if (err.message === 'SESSION_PASSWORD_NEEDED') {
-                isPasswordRequired = true;
-                break;
+                return signInWithPassword(client, apiCredentials, authParams);
             } else {
                 authParams.onError(err);
             }
         }
     }
 
-    if (isPasswordRequired) {
-        while (1) {
-            try {
-                const password = await authParams.password();
-                if (!password) {
-                    throw new Error('Password is empty');
-                }
-
-                const passwordSrpResult = await client.invoke(new Api.account.GetPassword());
-                const passwordSrpCheck = await computePasswordSrpCheck(passwordSrpResult, password);
-                const { user } = await client.invoke(new Api.auth.CheckPassword({
-                    password: passwordSrpCheck,
-                })) as Api.auth.Authorization;
-                me = user;
-
-                break;
-            } catch (err) {
-                authParams.onError(err);
-            }
-        }
-    }
-
     if (isRegistrationRequired) {
         while (1) {
             try {
@@ -161,19 +151,94 @@ async function signInUser(
                     await client.invoke(new Api.help.AcceptTermsOfService({ id: termsOfService.id }));
                 }
 
-                me = user;
+                return user;
             } catch (err) {
                 authParams.onError(err);
             }
         }
     }
 
-    return me!;
+    authParams.onError(new Error('Auth failed'));
+    return signInUser(client, apiCredentials, authParams);
+}
+
+async function signInUserWithQrCode(
+    client: TelegramClient, apiCredentials: ApiCredentials, authParams: UserAuthParams,
+): Promise<Api.TypeUser> {
+    const inputPromise = (async () => {
+        while (1) {
+            const result = await client.invoke(new Api.auth.ExportLoginToken({
+                apiId: Number(process.env.TELEGRAM_T_API_ID),
+                apiHash: process.env.TELEGRAM_T_API_HASH,
+                exceptIds: [],
+            }));
+
+            if (!(result instanceof Api.auth.LoginToken)) {
+                throw new Error('Unexpected');
+            }
+
+            const { token, expires } = result;
+
+            await Promise.race([
+                authParams.qrCode({ token, expires }),
+                sleep(QR_CODE_TIMEOUT),
+            ]);
+        }
+    })();
+
+    const updatePromise = new Promise((resolve) => {
+        client.addEventHandler((update: Api.TypeUpdate) => {
+            if (update instanceof Api.UpdateLoginToken) {
+                resolve();
+            }
+        }, { build: (update: object) => update });
+    });
+
+    try {
+        await Promise.race([updatePromise, inputPromise]);
+    } catch (err) {
+        if (err.message === 'RESTART_AUTH') {
+            return signInUser(client, apiCredentials, authParams);
+        }
+
+        throw err;
+    }
+
+    try {
+        const result2 = await client.invoke(new Api.auth.ExportLoginToken({
+            apiId: Number(process.env.TELEGRAM_T_API_ID),
+            apiHash: process.env.TELEGRAM_T_API_HASH,
+            exceptIds: [],
+        }));
+
+        if (result2 instanceof Api.auth.LoginTokenSuccess && result2.authorization instanceof Api.auth.Authorization) {
+            return result2.authorization.user;
+        } else if (result2 instanceof Api.auth.LoginTokenMigrateTo) {
+            await client._switchDC(result2.dcId);
+            const migratedResult = await client.invoke(new Api.auth.ImportLoginToken({
+                token: result2.token,
+            }));
+
+            if (migratedResult instanceof Api.auth.LoginTokenSuccess && migratedResult.authorization instanceof Api.auth.Authorization) {
+                return migratedResult.authorization.user;
+            }
+        }
+    } catch (err) {
+        if (err.message === 'SESSION_PASSWORD_NEEDED') {
+            return signInWithPassword(client, apiCredentials, authParams);
+        }
+    }
+
+    authParams.onError(new Error('QR auth failed'));
+    return signInUser(client, apiCredentials, authParams);
 }
 
 async function sendCode(
     client: TelegramClient, apiCredentials: ApiCredentials, phoneNumber: string, forceSMS = false,
-): Promise<string | undefined> {
+): Promise<{
+    phoneCodeHash: string;
+    isCodeViaApp: boolean;
+}> {
     try {
         const { apiId, apiHash } = apiCredentials;
         const sendResult = await client.invoke(new Api.auth.SendCode({
@@ -185,7 +250,10 @@ async function sendCode(
 
         // If we already sent a SMS, do not resend the phoneCode (hash may be empty)
         if (!forceSMS || (sendResult.type instanceof Api.auth.SentCodeTypeSms)) {
-            return sendResult.phoneCodeHash;
+            return {
+                phoneCodeHash: sendResult.phoneCodeHash,
+                isCodeViaApp: sendResult.type instanceof Api.auth.SentCodeTypeApp,
+            };
         }
 
         const resendResult = await client.invoke(new Api.auth.ResendCode({
@@ -193,7 +261,10 @@ async function sendCode(
             phoneCodeHash: sendResult.phoneCodeHash,
         }));
 
-        return resendResult.phoneCodeHash;
+        return {
+            phoneCodeHash: resendResult.phoneCodeHash,
+            isCodeViaApp: resendResult.type instanceof Api.auth.SentCodeTypeApp,
+        };
     } catch (err) {
         if (err.message === 'AUTH_RESTART') {
             return sendCode(client, apiCredentials, phoneNumber, forceSMS);
@@ -203,6 +274,31 @@ async function sendCode(
     }
 }
 
+async function signInWithPassword(
+    client: TelegramClient, apiCredentials: ApiCredentials, authParams: UserAuthParams,
+): Promise<Api.TypeUser> {
+    while (1) {
+        try {
+            const passwordSrpResult = await client.invoke(new Api.account.GetPassword());
+            const password = await authParams.password(passwordSrpResult.hint);
+            if (!password) {
+                throw new Error('Password is empty');
+            }
+
+            const passwordSrpCheck = await computePasswordSrpCheck(passwordSrpResult, password);
+            const { user } = await client.invoke(new Api.auth.CheckPassword({
+                password: passwordSrpCheck,
+            })) as Api.auth.Authorization;
+
+            return user;
+        } catch (err) {
+            authParams.onError(err);
+        }
+    }
+
+    return undefined!; // Never reached (TypeScript fix)
+}
+
 async function signInBot(client: TelegramClient, apiCredentials: ApiCredentials, authParams: BotAuthParams) {
     const { apiId, apiHash } = apiCredentials;
     const { botAuthToken } = authParams;

+ 189 - 0
gramjs/client/downloadFile.ts

@@ -0,0 +1,189 @@
+import { default as Api } from '../tl/api';
+import TelegramClient from './TelegramClient';
+import { getAppropriatedPartSize } from '../Utils';
+import { sleep } from '../Helpers';
+
+export interface progressCallback {
+    (
+        progress: number, // Float between 0 and 1.
+        ...args: any[]
+    ): void;
+
+    isCanceled?: boolean;
+    acceptsBuffer?: boolean;
+}
+
+export interface DownloadFileParams {
+    dcId: number;
+    fileSize: number;
+    workers?: number;
+    partSizeKb?: number;
+    start?: number;
+    end?: number;
+    progressCallback?: progressCallback;
+}
+
+interface Deferred {
+    promise: Promise<any>;
+    resolve: (value?: any) => void;
+}
+
+// Chunk sizes for `upload.getFile` must be multiple of the smallest size
+const MIN_CHUNK_SIZE = 4096;
+const DEFAULT_CHUNK_SIZE = 64; // kb
+const ONE_MB = 1024 * 1024;
+const REQUEST_TIMEOUT = 15000;
+
+export async function downloadFile(
+    client: TelegramClient,
+    inputLocation: Api.InputFileLocation,
+    fileParams: DownloadFileParams,
+) {
+    let { partSizeKb, fileSize, workers = 1, end } = fileParams;
+    const { dcId, progressCallback, start = 0 } = fileParams;
+
+    end = end && end < fileSize ? end : fileSize - 1;
+
+    if (!partSizeKb) {
+        partSizeKb = fileSize ? getAppropriatedPartSize(fileSize) : DEFAULT_CHUNK_SIZE;
+    }
+
+    const partSize = partSizeKb * 1024;
+    const partsCount = end ? Math.ceil((end - start) / partSize) : 1;
+
+    if (partSize % MIN_CHUNK_SIZE !== 0) {
+        throw new Error(`The part size must be evenly divisible by ${MIN_CHUNK_SIZE}`);
+    }
+
+    let sender: any;
+    if (dcId) {
+        try {
+            sender = await client._borrowExportedSender(dcId);
+        } catch (e) {
+            // This should never raise
+            client._log.error(e);
+            if (e.message === 'DC_ID_INVALID') {
+                // Can't export a sender for the ID we are currently in
+                sender = client._sender;
+            } else {
+                throw e;
+            }
+        }
+    } else {
+        sender = client._sender;
+    }
+
+    client._log.info(`Downloading file in chunks of ${partSize} bytes`);
+
+    const foreman = new Foreman(workers);
+    const promises: Promise<any>[] = [];
+    let offset = start;
+    // Used for files with unknown size and for manual cancellations
+    let hasEnded = false;
+
+    let progress = 0;
+    if (progressCallback) {
+        progressCallback(progress);
+    }
+
+    while (true) {
+        let limit = partSize;
+        let isPrecise = false;
+
+        if (Math.floor(offset / ONE_MB) !== Math.floor((offset + limit - 1) / ONE_MB)) {
+            limit = ONE_MB - offset % ONE_MB;
+            isPrecise = true;
+        }
+
+        await foreman.requestWorker();
+
+        if (hasEnded) {
+            await foreman.releaseWorker();
+            break;
+        }
+
+        promises.push((async () => {
+            try {
+                const result = await Promise.race([
+                    await sender.send(new Api.upload.GetFile({
+                        location: inputLocation,
+                        offset,
+                        limit,
+                        precise: isPrecise || undefined,
+                    })),
+                    sleep(REQUEST_TIMEOUT).then(() => Promise.reject(new Error('REQUEST_TIMEOUT'))),
+                ]);
+
+                if (progressCallback) {
+                    if (progressCallback.isCanceled) {
+                        throw new Error('USER_CANCELED');
+                    }
+
+                    progress += (1 / partsCount);
+                    progressCallback(progress);
+                }
+
+                if (!end && (result.bytes.length < limit)) {
+                    hasEnded = true;
+                }
+
+                return result.bytes;
+            } catch (err) {
+                hasEnded = true;
+                throw err;
+            } finally {
+                foreman.releaseWorker();
+            }
+        })());
+
+        offset += limit;
+
+        if (end && (offset > end)) {
+            break;
+        }
+    }
+
+    const results = await Promise.all(promises);
+    const buffers = results.filter(Boolean);
+    const totalLength = end ? (end + 1) - start : undefined;
+    return Buffer.concat(buffers, totalLength);
+}
+
+class Foreman {
+    private deferred: Deferred | undefined;
+    private activeWorkers = 0;
+
+    constructor(private maxWorkers: number) {
+    }
+
+    requestWorker() {
+        this.activeWorkers++;
+
+        if (this.activeWorkers > this.maxWorkers) {
+            this.deferred = createDeferred();
+            return this.deferred.promise;
+        }
+
+        return Promise.resolve();
+    }
+
+    releaseWorker() {
+        this.activeWorkers--;
+
+        if (this.deferred && (this.activeWorkers <= this.maxWorkers)) {
+            this.deferred.resolve();
+        }
+    }
+}
+
+function createDeferred(): Deferred {
+    let resolve: Deferred['resolve'];
+    const promise = new Promise((_resolve) => {
+        resolve = _resolve;
+    });
+
+    return {
+        promise,
+        resolve: resolve!,
+    };
+}

+ 2 - 27
src/lib/gramjs/client/uploadFile.ts → gramjs/client/uploadFile.ts

@@ -1,7 +1,7 @@
 import { default as Api } from '../tl/api';
 
 import TelegramClient from './TelegramClient';
-import { generateRandomBytes, readBigIntFromBuffer } from '../Helpers';
+import { generateRandomBytes, readBigIntFromBuffer, sleep } from '../Helpers';
 import { getAppropriatedPartSize } from '../Utils';
 
 interface OnProgress {
@@ -19,6 +19,7 @@ export interface UploadFileParams {
 
 const KB_TO_BYTES = 1024;
 const LARGE_FILE_THRESHOLD = 10 * 1024 * 1024;
+const UPLOAD_TIMEOUT = 15 * 1000;
 
 export async function uploadFile(
     client: TelegramClient,
@@ -50,31 +51,6 @@ export async function uploadFile(
         onProgress(progress);
     }
 
-<<<<<<< HEAD
-    for (let i = 0; i < partCount; i++) {
-        const bytes = buffer.slice(i * partSize, (i + 1) * partSize);
-        const result = await sender.send(
-            isLarge
-                ? new Api.upload.SaveBigFilePart({
-                    fileId,
-                    filePart: i,
-                    fileTotalParts: partCount,
-                    bytes,
-                })
-                : new Api.upload.SaveFilePart({
-                    fileId,
-                    filePart: i,
-                    bytes,
-                }),
-        );
-
-        if (result && onProgress) {
-            if (onProgress.isCanceled) {
-                throw new Error('USER_CANCELED')
-            }
-
-            onProgress((i + 1) / partCount);
-=======
     for (let i = 0; i < partCount; i += workers) {
         let sendingParts = [];
         let end = i + workers;
@@ -125,7 +101,6 @@ export async function uploadFile(
             }
 
             throw err;
->>>>>>> edef29da... [Perf] GramJs: Add parallel uploads (#659)
         }
     }
 

+ 0 - 0
src/lib/gramjs/crypto/AuthKey.js → gramjs/crypto/AuthKey.js


+ 0 - 0
src/lib/gramjs/crypto/CTR.js → gramjs/crypto/CTR.js


+ 0 - 0
src/lib/gramjs/crypto/Factorizator.js → gramjs/crypto/Factorizator.js


+ 37 - 0
gramjs/crypto/IGE.js

@@ -0,0 +1,37 @@
+const Helpers = require("../Helpers");
+
+const {IGE:aes_ige} = require('@cryptography/aes');
+
+class IGENEW {
+    constructor(key, iv) {
+        this.ige = new aes_ige(key,iv);
+    }
+
+    /**
+     * Decrypts the given text in 16-bytes blocks by using the given key and 32-bytes initialization vector
+     * @param cipherText {Buffer}
+     * @returns {Buffer}
+     */
+    decryptIge(cipherText) {
+        return Helpers.convertToLittle(this.ige.decrypt(cipherText));
+    }
+
+    /**
+     * Encrypts the given text in 16-bytes blocks by using the given key and 32-bytes initialization vector
+     * @param plainText {Buffer}
+     * @returns {Buffer}
+     */
+    encryptIge(plainText) {
+        const padding = plainText.length % 16
+        if (padding) {
+            plainText = Buffer.concat([plainText, Helpers.generateRandomBytes(16 - padding)])
+        }
+
+        return Helpers.convertToLittle(this.ige.encrypt(plainText));
+
+
+    }
+
+}
+
+module.exports = IGENEW

+ 22 - 45
src/lib/gramjs/crypto/RSA.js → gramjs/crypto/RSA.js

@@ -1,31 +1,29 @@
-const { serializeBytes } = require('../tl')
-const { readBigIntFromBuffer, readBufferFromBigInt, getByteArray, sha1, generateRandomBytes, modExp } = require('../Helpers')
-const _serverKeys = {}
 const BigInt = require('big-integer')
+const { readBigIntFromBuffer, readBufferFromBigInt, getByteArray, sha1, generateRandomBytes, modExp } = require('../Helpers')
 
+const PUBLIC_KEYS = [{
+    'fingerprint': [40, 85, 94, 156, 117, 240, 61, 22, 65, 244, 169, 2, 33, 107, 232, 108, 2, 43, 180, 195],
+    'n': BigInt('24403446649145068056824081744112065346446136066297307473868293895086332508101251964919587745984311372853053253457835208829824428441874946556659953519213382748319518214765985662663680818277989736779506318868003755216402538945900388706898101286548187286716959100102939636333452457308619454821845196109544157601096359148241435922125602449263164512290854366930013825808102403072317738266383237191313714482187326643144603633877219028262697593882410403273959074350849923041765639673335775605842311578109726403165298875058941765362622936097839775380070572921007586266115476975819175319995527916042178582540628652481530373407'),
+    'e': 65537
+}, {
+    'fingerprint': [140, 171, 9, 34, 146, 246, 166, 50, 10, 170, 229, 247, 155, 114, 28, 177, 29, 106, 153, 154],
+    'n': BigInt('25081407810410225030931722734886059247598515157516470397242545867550116598436968553551465554653745201634977779380884774534457386795922003815072071558370597290368737862981871277312823942822144802509055492512145589734772907225259038113414940384446493111736999668652848440655603157665903721517224934142301456312994547591626081517162758808439979745328030376796953660042629868902013177751703385501412640560275067171555763725421377065095231095517201241069856888933358280729674273422117201596511978645878544308102076746465468955910659145532699238576978901011112475698963666091510778777356966351191806495199073754705289253783'),
+    'e': 65537
+}, {
+    'fingerprint': [243, 218, 109, 239, 16, 202, 176, 78, 167, 8, 255, 209, 120, 234, 205, 112, 111, 42, 91, 176],
+    'n': BigInt('22347337644621997830323797217583448833849627595286505527328214795712874535417149457567295215523199212899872122674023936713124024124676488204889357563104452250187725437815819680799441376434162907889288526863223004380906766451781702435861040049293189979755757428366240570457372226323943522935844086838355728767565415115131238950994049041950699006558441163206523696546297006014416576123345545601004508537089192869558480948139679182328810531942418921113328804749485349441503927570568778905918696883174575510385552845625481490900659718413892216221539684717773483326240872061786759868040623935592404144262688161923519030977'),
+    'e': 65537
+}, {
+    'fingerprint': [128, 80, 214, 72, 77, 244, 98, 7, 201, 250, 37, 244, 227, 51, 96, 199, 182, 37, 224, 113],
+    'n': BigInt('24573455207957565047870011785254215390918912369814947541785386299516827003508659346069416840622922416779652050319196701077275060353178142796963682024347858398319926119639265555410256455471016400261630917813337515247954638555325280392998950756512879748873422896798579889820248358636937659872379948616822902110696986481638776226860777480684653756042166610633513404129518040549077551227082262066602286208338952016035637334787564972991208252928951876463555456715923743181359826124083963758009484867346318483872552977652588089928761806897223231500970500186019991032176060579816348322451864584743414550721639495547636008351'),
+    'e': 65537
+}]
 
-/**
- * Given a RSA key, computes its fingerprint like Telegram does.
- * @param key the RSA key.
- * @returns {bigInt.BigInteger|*} its 8-bytes-long fingerprint
- * @private
- */
-async function _computeFingerprint(key) {
+const _serverKeys = {}
 
-    const n = serializeBytes(getByteArray(key.n))
-    const e = serializeBytes(getByteArray(key.e))
-    // Telegram uses the last 8 bytes as the fingerprint
-    const sh = await sha1(Buffer.concat([n, e]))
-    return readBigIntFromBuffer(sh.slice(-8), true, true)
-}
-
-/**
- * Adds a new public key to be used when encrypting new data is needed
- * @param pub {{n:BigInt,e:BigInt}}
- */
-async function addKey(pub) {
-    _serverKeys[await _computeFingerprint(pub)] = pub
-}
+PUBLIC_KEYS.forEach(({ fingerprint, ...keyInfo }) => {
+    _serverKeys[readBigIntFromBuffer(fingerprint.slice(-8), true, true)] = keyInfo
+})
 
 /**
  * Encrypts the given data known the fingerprint to be used
@@ -54,27 +52,6 @@ async function encrypt(fingerprint, data) {
     return readBufferFromBigInt(encrypted, 256, false)
 }
 
-const publicKeys = [{
-    'n': BigInt('24403446649145068056824081744112065346446136066297307473868293895086332508101251964919587745984311372853053253457835208829824428441874946556659953519213382748319518214765985662663680818277989736779506318868003755216402538945900388706898101286548187286716959100102939636333452457308619454821845196109544157601096359148241435922125602449263164512290854366930013825808102403072317738266383237191313714482187326643144603633877219028262697593882410403273959074350849923041765639673335775605842311578109726403165298875058941765362622936097839775380070572921007586266115476975819175319995527916042178582540628652481530373407'),
-    'e': 65537
-}, {
-    'n': BigInt('25081407810410225030931722734886059247598515157516470397242545867550116598436968553551465554653745201634977779380884774534457386795922003815072071558370597290368737862981871277312823942822144802509055492512145589734772907225259038113414940384446493111736999668652848440655603157665903721517224934142301456312994547591626081517162758808439979745328030376796953660042629868902013177751703385501412640560275067171555763725421377065095231095517201241069856888933358280729674273422117201596511978645878544308102076746465468955910659145532699238576978901011112475698963666091510778777356966351191806495199073754705289253783'),
-    'e': 65537
-}, {
-    'n': BigInt('22347337644621997830323797217583448833849627595286505527328214795712874535417149457567295215523199212899872122674023936713124024124676488204889357563104452250187725437815819680799441376434162907889288526863223004380906766451781702435861040049293189979755757428366240570457372226323943522935844086838355728767565415115131238950994049041950699006558441163206523696546297006014416576123345545601004508537089192869558480948139679182328810531942418921113328804749485349441503927570568778905918696883174575510385552845625481490900659718413892216221539684717773483326240872061786759868040623935592404144262688161923519030977'),
-    'e': 65537
-}, {
-    'n': BigInt('24573455207957565047870011785254215390918912369814947541785386299516827003508659346069416840622922416779652050319196701077275060353178142796963682024347858398319926119639265555410256455471016400261630917813337515247954638555325280392998950756512879748873422896798579889820248358636937659872379948616822902110696986481638776226860777480684653756042166610633513404129518040549077551227082262066602286208338952016035637334787564972991208252928951876463555456715923743181359826124083963758009484867346318483872552977652588089928761806897223231500970500186019991032176060579816348322451864584743414550721639495547636008351'),
-    'e': 65537
-}]
-
-async function init() {
-    await Promise.all(publicKeys.map(addKey));
-}
-
 module.exports = {
     encrypt,
-    addKey,
-    init
 }
-

+ 55 - 0
gramjs/crypto/converters.ts

@@ -0,0 +1,55 @@
+/**
+ * Uint32Array -> ArrayBuffer (low-endian os)
+ */
+export function i2abLow(buf: Uint32Array): ArrayBuffer {
+    const uint8 = new Uint8Array(buf.length * 4);
+    let i = 0;
+
+    for (let j = 0; j < buf.length; j++) {
+        const int = buf[j];
+
+        uint8[i++] = int >>> 24;
+        uint8[i++] = (int >> 16) & 0xFF;
+        uint8[i++] = (int >> 8) & 0xFF;
+        uint8[i++] = int & 0xFF;
+    }
+
+    return uint8.buffer;
+}
+
+/**
+ * Uint32Array -> ArrayBuffer (big-endian os)
+ */
+export function i2abBig(buf: Uint32Array): ArrayBuffer {
+    return buf.buffer;
+}
+
+/**
+ * ArrayBuffer -> Uint32Array (low-endian os)
+ */
+export function ab2iLow(ab: ArrayBuffer | SharedArrayBuffer | Uint8Array): Uint32Array {
+    const uint8 = new Uint8Array(ab);
+    const buf = new Uint32Array(uint8.length / 4);
+
+    for (let i = 0; i < uint8.length; i += 4) {
+        buf[i / 4] = (
+            uint8[i] << 24
+            ^ uint8[i + 1] << 16
+            ^ uint8[i + 2] << 8
+            ^ uint8[i + 3]
+        );
+    }
+
+    return buf;
+}
+
+/**
+ * ArrayBuffer -> Uint32Array (big-endian os)
+ */
+export function ab2iBig(ab: ArrayBuffer | SharedArrayBuffer | Uint8Array): Uint32Array {
+    return new Uint32Array(ab);
+}
+
+export const isBigEndian = new Uint8Array(new Uint32Array([0x01020304]))[0] === 0x01;
+export const i2ab = isBigEndian ? i2abBig : i2abLow;
+export const ab2i = isBigEndian ? ab2iBig : ab2iLow;

+ 124 - 0
gramjs/crypto/crypto.js

@@ -0,0 +1,124 @@
+const AES = require('@cryptography/aes').default;
+const { i2ab, ab2i } = require('./converters');
+const { getWords } = require('./words');
+
+class Counter {
+    constructor(initialValue) {
+        this.setBytes(initialValue)
+    }
+
+    setBytes(bytes) {
+        bytes = Buffer.from(bytes)
+        this._counter = bytes
+    }
+
+    increment() {
+        for (let i = 15; i >= 0; i--) {
+            if (this._counter[i] === 255) {
+                this._counter[i] = 0
+            } else {
+                this._counter[i]++
+                break
+            }
+        }
+    }
+}
+
+class CTR {
+    constructor(key, counter) {
+
+        if (!(counter instanceof Counter)) {
+            counter = new Counter(counter)
+        }
+
+        this._counter = counter
+
+        this._remainingCounter = null
+        this._remainingCounterIndex = 16
+
+        this._aes = new AES(getWords(key))
+    }
+
+    update(plainText) {
+        return this.encrypt(plainText)
+    }
+
+    encrypt(plainText) {
+        const encrypted = Buffer.from(plainText)
+
+        for (let i = 0; i < encrypted.length; i++) {
+            if (this._remainingCounterIndex === 16) {
+                this._remainingCounter = Buffer.from(i2ab(this._aes.encrypt(ab2i(this._counter._counter))))
+                this._remainingCounterIndex = 0
+                this._counter.increment()
+            }
+            encrypted[i] ^= this._remainingCounter[this._remainingCounterIndex++]
+        }
+
+        return encrypted
+    }
+}
+
+// endregion
+function createDecipheriv(algorithm, key, iv) {
+    if (algorithm.includes('ECB')) {
+        throw new Error('Not supported');
+    } else {
+        return new CTR(key, iv)
+    }
+}
+
+function createCipheriv(algorithm, key, iv) {
+    if (algorithm.includes('ECB')) {
+        throw new Error('Not supported');
+    } else {
+        return new CTR(key, iv)
+    }
+}
+
+function randomBytes(count) {
+    const bytes = new Uint8Array(count)
+    crypto.getRandomValues(bytes)
+    return bytes
+}
+
+class Hash {
+    constructor(algorithm) {
+        this.algorithm = algorithm
+    }
+
+    update(data) {
+        //We shouldn't be needing new Uint8Array but it doesn't
+        //work without it
+        this.data = new Uint8Array(data)
+    }
+
+    async digest() {
+        if (this.algorithm === 'sha1') {
+            return Buffer.from(await self.crypto.subtle.digest('SHA-1', this.data))
+        } else if (this.algorithm === 'sha256') {
+            return Buffer.from(await self.crypto.subtle.digest('SHA-256', this.data))
+        }
+    }
+}
+
+async function pbkdf2(password, salt, iterations) {
+    const passwordKey = await crypto.subtle.importKey('raw', password,
+        {name: 'PBKDF2'}, false, ['deriveBits'])
+    return Buffer.from(await crypto.subtle.deriveBits({
+        name: 'PBKDF2',
+        hash: 'SHA-512', salt, iterations
+    }, passwordKey, 512));
+}
+
+function createHash(algorithm) {
+    return new Hash(algorithm)
+}
+
+module.exports = {
+    createCipheriv,
+    createDecipheriv,
+    randomBytes,
+    createHash,
+    pbkdf2
+}

+ 51 - 0
gramjs/crypto/words.ts

@@ -0,0 +1,51 @@
+/*
+ * Imported from https://github.com/spalt08/cryptography/blob/master/packages/aes/src/utils/words.ts
+ */
+
+export function s2i(str: string, pos: number) {
+    return (
+        str.charCodeAt(pos) << 24
+        ^ str.charCodeAt(pos + 1) << 16
+        ^ str.charCodeAt(pos + 2) << 8
+        ^ str.charCodeAt(pos + 3)
+    );
+}
+
+/**
+ * Helper function for transforming string key to Uint32Array
+ */
+export function getWords(key: string | Uint8Array | Uint32Array) {
+    if (key instanceof Uint32Array) {
+        return key;
+    }
+
+    if (typeof key === 'string') {
+        if (key.length % 4 !== 0) for (let i = key.length % 4; i <= 4; i++) key += '\0x00';
+
+        const buf = new Uint32Array(key.length / 4);
+        for (let i = 0; i < key.length; i += 4) buf[i / 4] = s2i(key, i);
+
+        return buf;
+    }
+
+    if (key instanceof Uint8Array) {
+        const buf = new Uint32Array(key.length / 4);
+
+        for (let i = 0; i < key.length; i += 4) {
+            buf[i / 4] = (
+                key[i] << 24
+                ^ key[i + 1] << 16
+                ^ key[i + 2] << 8
+                ^ key[i + 3]
+            );
+        }
+
+        return buf;
+    }
+
+    throw new Error('Unable to create 32-bit words');
+}
+
+export function xor(left: Uint32Array, right: Uint32Array, to = left) {
+    for (let i = 0; i < left.length; i++) to[i] = left[i] ^ right[i];
+}

+ 4 - 1
src/lib/gramjs/errors/Common.js → gramjs/errors/Common.js

@@ -20,7 +20,10 @@ class TypeNotFoundError extends Error {
     constructor(invalidConstructorId, remaining) {
         super(`Could not find a matching Constructor ID for the TLObject that was supposed to be
         read with ID ${invalidConstructorId}. Most likely, a TLObject was trying to be read when
-         it should not be read. Remaining bytes: ${remaining}`)
+         it should not be read. Remaining bytes: ${remaining.length}`)
+        if (typeof alert !== 'undefined') {
+            alert(`Missing MTProto Entity: Please, make sure to add TL definition for ID ${invalidConstructorId}`)
+        }
         this.invalidConstructorId = invalidConstructorId
         this.remaining = remaining
     }

+ 0 - 0
src/lib/gramjs/errors/RPCBaseErrors.js → gramjs/errors/RPCBaseErrors.js


+ 0 - 0
src/lib/gramjs/errors/index.js → gramjs/errors/index.js


+ 0 - 0
src/lib/gramjs/events/NewMessage.js → gramjs/events/NewMessage.js


+ 21 - 0
gramjs/events/Raw.js

@@ -0,0 +1,21 @@
+const { EventBuilder } = require('./common')
+
+class Raw extends EventBuilder {
+    constructor(args = {
+        types: null,
+        func: null,
+    }) {
+        super()
+        if (!args.types) {
+            this.types = true
+        } else {
+            this.types = args.types
+        }
+    }
+
+    build(update, others = null) {
+        return update
+    }
+}
+
+module.exports = Raw

+ 22 - 0
gramjs/events/common.js

@@ -0,0 +1,22 @@
+class EventBuilder {
+    constructor(args = {
+        chats: null, blacklistChats: null, func: null,
+    },
+    ) {
+        this.chats = args.chats
+        this.blacklistChats = Boolean(args.blacklistChats)
+        this.resolved = false
+        this.func = args.func
+    }
+
+    build(update, others = null) {
+
+    }
+}
+
+class EventCommon {
+
+}
+
+
+module.exports = { EventBuilder, EventCommon }

+ 8 - 0
gramjs/events/index.js

@@ -0,0 +1,8 @@
+const NewMessage = require('./NewMessage')
+const Raw = require('./Raw')
+
+class StopPropagation extends Error {
+
+}
+
+module.exports = { NewMessage, StopPropagation, Raw }

+ 30 - 0
gramjs/extensions/AsyncQueue.js

@@ -0,0 +1,30 @@
+class AsyncQueue {
+    constructor() {
+        this._queue = []
+        this.canGet = new Promise((resolve) => {
+            this.resolveGet = resolve
+        })
+        this.canPush = true
+    }
+
+    async push(value) {
+        await this.canPush
+        this._queue.push(value)
+        this.resolveGet(true)
+        this.canPush = new Promise((resolve) => {
+            this.resolvePush = resolve
+        })
+    }
+
+    async pop() {
+        await this.canGet
+        const returned = this._queue.pop()
+        this.resolvePush(true)
+        this.canGet = new Promise((resolve) => {
+            this.resolveGet = resolve
+        })
+        return returned
+    }
+}
+
+module.exports = AsyncQueue

+ 0 - 0
src/lib/gramjs/extensions/BinaryReader.js → gramjs/extensions/BinaryReader.js


+ 15 - 0
gramjs/extensions/BinaryWriter.js

@@ -0,0 +1,15 @@
+class BinaryWriter {
+    constructor(stream) {
+        this._stream = stream
+    }
+
+    write(buffer) {
+        this._stream = Buffer.concat([this._stream, buffer])
+    }
+
+    getValue() {
+        return this._stream
+    }
+}
+
+module.exports = BinaryWriter

+ 9 - 9
src/lib/gramjs/extensions/Logger.js → gramjs/extensions/Logger.js

@@ -1,10 +1,13 @@
-let logger = null
+let _level = null
 
 class Logger {
     static levels = ['error', 'warn', 'info', 'debug']
 
     constructor(level) {
-        this.level = level || 'debug'
+        if (!_level) {
+            _level = level || 'debug'
+        }
+
         this.isBrowser = typeof process === 'undefined' ||
             process.type === 'renderer' ||
             process.browser === true ||
@@ -37,7 +40,7 @@ class Logger {
      * @returns {boolean}
      */
     canSend(level) {
-        return (Logger.levels.indexOf(this.level) >= Logger.levels.indexOf(level))
+        return (Logger.levels.indexOf(_level) >= Logger.levels.indexOf(level))
     }
 
     /**
@@ -74,11 +77,8 @@ class Logger {
             .replace('%m', message)
     }
 
-    static getLogger() {
-        if (!logger) {
-            logger = new Logger('debug')
-        }
-        return logger
+    static setLevel(level) {
+        _level = level;
     }
 
     /**
@@ -87,7 +87,7 @@ class Logger {
      * @param color {string}
      */
     _log(level, message, color) {
-        if (!logger){
+        if (!_level){
             return
         }
         if (this.canSend(level)) {

+ 0 - 0
src/lib/gramjs/extensions/MessagePacker.js → gramjs/extensions/MessagePacker.js


+ 0 - 0
src/lib/gramjs/extensions/PromisedWebSockets.js → gramjs/extensions/PromisedWebSockets.js


+ 0 - 0
src/lib/gramjs/extensions/index.js → gramjs/extensions/index.js


+ 10 - 0
gramjs/index.d.ts

@@ -0,0 +1,10 @@
+export { default as Api } from './tl/api';
+export { default as TelegramClient } from './client/TelegramClient';
+export { default as connection } from './network';
+export { default as tl } from './tl';
+export { default as version } from './Version';
+export { default as events } from './events';
+export { default as utils } from './Utils';
+export { default as errors } from './errors';
+export { default as sessions } from './sessions';
+export { default as helpers } from './Helpers';

+ 16 - 0
gramjs/index.js

@@ -0,0 +1,16 @@
+const Api = require('./tl/api')
+const TelegramClient = require('./client/TelegramClient')
+const connection = require('./network')
+const tl = require('./tl')
+const version = require('./Version')
+const events = require('./events')
+const utils = require('./Utils')
+const errors = require('./errors')
+const sessions = require('./sessions')
+const extensions = require('./extensions')
+const helpers = require('./Helpers')
+
+module.exports = {
+    Api, TelegramClient, sessions, connection, extensions,
+    tl, version, events, utils, errors, helpers,
+}

+ 7 - 5
src/lib/gramjs/network/Authenticator.js → gramjs/network/Authenticator.js

@@ -20,7 +20,7 @@ async function doAuthentication(sender, log) {
 
     const nonce = Helpers.readBigIntFromBuffer(bytes, false, true)
 
-    const resPQ = await sender.send(new requests.ReqPqMultiRequest({ nonce: nonce }))
+    const resPQ = await sender.send(new requests.ReqPqMulti({ nonce: nonce }))
     log.debug('Starting authKey generation step 1')
 
     if (!(resPQ instanceof constructors.ResPQ)) {
@@ -66,7 +66,7 @@ async function doAuthentication(sender, log) {
     }
 
     const serverDhParams = await sender.send(
-        new requests.ReqDHParamsRequest({
+        new requests.ReqDHParams({
             nonce: resPQ.nonce,
             serverNonce: resPQ.serverNonce,
             p: p,
@@ -105,7 +105,8 @@ async function doAuthentication(sender, log) {
         // See PR#453
         throw new SecurityError('Step 3 AES block size mismatch')
     }
-    const plainTextAnswer = IGE.decryptIge(serverDhParams.encryptedAnswer, key, iv)
+    const ige = new IGE(key,iv)
+    const plainTextAnswer = ige.decryptIge(serverDhParams.encryptedAnswer)
     const reader = new BinaryReader(plainTextAnswer)
     reader.read(20) // hash sum
     const serverDhInner = reader.tgReadObject()
@@ -136,9 +137,10 @@ async function doAuthentication(sender, log) {
 
     const clientDdhInnerHashed = Buffer.concat([await Helpers.sha1(clientDhInner), clientDhInner])
     // Encryption
-    const clientDhEncrypted = IGE.encryptIge(clientDdhInnerHashed, key, iv)
+
+    const clientDhEncrypted = ige.encryptIge(clientDdhInnerHashed)
     const dhGen = await sender.send(
-        new requests.SetClientDHParamsRequest({
+        new requests.SetClientDHParams({
             nonce: resPQ.nonce,
             serverNonce: resPQ.serverNonce,
             encryptedData: clientDhEncrypted,

+ 0 - 0
src/lib/gramjs/network/MTProtoPlainSender.js → gramjs/network/MTProtoPlainSender.js


+ 7 - 5
src/lib/gramjs/network/MTProtoSender.js → gramjs/network/MTProtoSender.js

@@ -25,7 +25,7 @@ const {
 } = require('../tl').constructors
 const { SecurityError } = require('../errors/Common')
 const { InvalidBufferError } = require('../errors/Common')
-const { LogOutRequest } = require('../tl').requests.auth
+const { LogOut } = require('../tl').requests.auth
 const { RPCMessageToError } = require('../errors')
 const { TypeNotFoundError } = require('../errors/Common')
 
@@ -406,6 +406,7 @@ class MTProtoSender {
                 } else {
                     this._log.error('Unhandled error while receiving data')
                     this._log.error(e)
+                    console.log(e)
                     this._startReconnect()
                     return
                 }
@@ -414,6 +415,7 @@ class MTProtoSender {
                 await this._processMessage(message)
             } catch (e) {
                 this._log.error('Unhandled error while receiving data')
+                console.log(e)
                 this._log.error(e)
             }
         }
@@ -458,7 +460,7 @@ class MTProtoSender {
         const toPop = []
 
         for (state of Object.values(this._pending_state)) {
-            if (state.containerId.equals(msgId)) {
+            if (state.containerId && state.containerId.equals(msgId)) {
                 toPop.push(state.msgId)
             }
         }
@@ -489,7 +491,7 @@ class MTProtoSender {
      * @returns {Promise<void>}
      * @private
      */
-    async _handleRPCResult(message) {
+    _handleRPCResult(message) {
         const RPCResult = message.obj
         const state = this._pending_state[RPCResult.reqMsgId]
         if (state) {
@@ -524,7 +526,7 @@ class MTProtoSender {
             state.reject(error)
         } else {
             const reader = new BinaryReader(RPCResult.body)
-            const read = await state.request.readResult(reader)
+            const read = state.request.readResult(reader)
             state.resolve(read)
         }
     }
@@ -712,7 +714,7 @@ class MTProtoSender {
         this._log.debug(`Handling acknowledge for ${ack.msgIds}`)
         for (const msgId of ack.msgIds) {
             const state = this._pending_state[msgId]
-            if (state && state.request instanceof LogOutRequest) {
+            if (state && state.request instanceof LogOut) {
                 delete this._pending_state[msgId]
                 state.resolve(true)
             }

+ 5 - 5
src/lib/gramjs/network/MTProtoState.js → gramjs/network/MTProtoState.js

@@ -4,7 +4,7 @@ const BinaryReader = require('../extensions/BinaryReader')
 const GZIPPacked = require('../tl/core/GZIPPacked')
 const { TLMessage } = require('../tl/core')
 const { SecurityError, InvalidBufferError } = require('../errors/Common')
-const { InvokeAfterMsgRequest } = require('../tl').requests
+const { InvokeAfterMsg } = require('../tl').requests
 const BigInt = require('big-integer')
 const { toSignedLittleBuffer,readBufferFromBigInt } = require("../Helpers")
 const { readBigIntFromBuffer } = require("../Helpers")
@@ -97,7 +97,7 @@ class MTProtoState {
         if (!afterId) {
             body = await GZIPPacked.gzipIfSmaller(contentRelated, data)
         } else {
-            body = await GZIPPacked.gzipIfSmaller(contentRelated, new InvokeAfterMsgRequest(afterId, data).getBytes())
+            body = await GZIPPacked.gzipIfSmaller(contentRelated, new InvokeAfterMsg(afterId, data).getBytes())
         }
         const s = Buffer.alloc(4)
         s.writeInt32LE(seqNo, 0)
@@ -129,7 +129,7 @@ class MTProtoState {
         const { iv, key } = await this._calcKey(this.authKey.getKey(), msgKey, true)
 
         const keyId = Helpers.readBufferFromBigInt(this.authKey.keyId, 8)
-        return Buffer.concat([keyId, msgKey, IGE.encryptIge(Buffer.concat([data, padding]), key, iv)])
+        return Buffer.concat([keyId, msgKey, new IGE(key,iv).encryptIge(Buffer.concat([data, padding]))])
     }
 
     /**
@@ -149,7 +149,7 @@ class MTProtoState {
 
         const msgKey = body.slice(8, 24)
         const { iv, key } = await this._calcKey(this.authKey.getKey(), msgKey, false)
-        body = IGE.decryptIge(body.slice(24), key, iv)
+        body = new IGE(key,iv).decryptIge(body.slice(24))
 
         // https://core.telegram.org/mtproto/security_guidelines
         // Sections "checking sha256 hash" and "message length"
@@ -174,7 +174,7 @@ class MTProtoState {
         // We could read msg_len bytes and use those in a new reader to read
         // the next TLObject without including the padding, but since the
         // reader isn't used for anything else after this, it's unnecessary.
-        const obj = await reader.tgReadObject()
+        const obj = reader.tgReadObject()
 
         return new TLMessage(remoteMsgId, remoteSequence, obj)
     }

+ 0 - 0
src/lib/gramjs/network/RequestState.js → gramjs/network/RequestState.js


+ 0 - 0
src/lib/gramjs/network/connection/Connection.js → gramjs/network/connection/Connection.js


+ 0 - 0
src/lib/gramjs/network/connection/TCPAbridged.js → gramjs/network/connection/TCPAbridged.js


+ 57 - 0
gramjs/network/connection/TCPFull.js

@@ -0,0 +1,57 @@
+//CONTEST
+// const { Connection, PacketCodec } = require('./Connection')
+// const { crc32 } = require('../../Helpers')
+// const { InvalidChecksumError } = require('../../errors/Common')
+//
+// class FullPacketCodec extends PacketCodec {
+//     constructor(connection) {
+//         super(connection)
+//         this._sendCounter = 0 // Telegram will ignore us otherwise
+//     }
+//
+//     encodePacket(data) {
+//         // https://core.telegram.org/mtproto#tcp-transport
+//         // total length, sequence number, packet and checksum (CRC32)
+//         const length = data.length + 12
+//         const e = Buffer.alloc(8)
+//         e.writeInt32LE(length,0)
+//         e.writeInt32LE(this._sendCounter,4)
+//         data = Buffer.concat([e, data])
+//         const crc =  Buffer.alloc(4)
+//         crc.writeUInt32LE(crc32(data),0)
+//         this._sendCounter += 1
+//         return Buffer.concat([data, crc])
+//     }
+//
+//     /**
+//      *
+//      * @param reader {PromisedWebSockets}
+//      * @returns {Promise<*>}
+//      */
+//     async readPacket(reader) {
+//         const packetLenSeq = await reader.read(8) // 4 and 4
+//         // process.exit(0);
+//         if (packetLenSeq === undefined) {
+//             return false
+//         }
+//         const packetLen = packetLenSeq.readInt32LE(0)
+//         let body = await reader.read(packetLen - 8)
+//         const [checksum] = body.slice(-4).readUInt32LE(0)
+//         body = body.slice(0, -4)
+//
+//         const validChecksum = crc32(Buffer.concat([packetLenSeq, body]))
+//         if (!(validChecksum === checksum)) {
+//             throw new InvalidChecksumError(checksum, validChecksum)
+//         }
+//         return body
+//     }
+// }
+//
+// class ConnectionTCPFull extends Connection {
+//     PacketCodecClass = FullPacketCodec;
+// }
+//
+// module.exports = {
+//     FullPacketCodec,
+//     ConnectionTCPFull,
+// }

+ 0 - 0
src/lib/gramjs/network/connection/TCPObfuscated.js → gramjs/network/connection/TCPObfuscated.js


+ 11 - 0
gramjs/network/connection/index.js

@@ -0,0 +1,11 @@
+const { Connection } = require('./Connection')
+const { ConnectionTCPFull } = require('./TCPFull')
+const { ConnectionTCPAbridged } = require('./TCPAbridged')
+const { ConnectionTCPObfuscated } = require('./TCPObfuscated')
+
+module.exports = {
+    Connection,
+    ConnectionTCPFull,
+    ConnectionTCPAbridged,
+    ConnectionTCPObfuscated,
+}

+ 0 - 0
src/lib/gramjs/network/index.js → gramjs/network/index.js


+ 0 - 0
src/lib/gramjs/sessions/Abstract.js → gramjs/sessions/Abstract.js


+ 0 - 0
src/lib/gramjs/sessions/CacheApiSession.js → gramjs/sessions/CacheApiSession.js


+ 2 - 2
src/lib/gramjs/sessions/Memory.js → gramjs/sessions/Memory.js

@@ -88,9 +88,9 @@ class MemorySession extends Session {
             p = utils.getInputPeer(e, false)
             markedId = utils.getPeerId(p)
         } catch (e) {
-            // Note: `get_input_peer` already checks for non-zero `access_hash`.
+            // Note: `get_input_peer` already checks for non-zero `accessHash`.
             // See issues #354 and #392. It also checks that the entity
-            // is not `min`, because its `access_hash` cannot be used
+            // is not `min`, because its `accessHash` cannot be used
             // anywhere (since layer 102, there are two access hashes).
             return
         }

+ 18 - 0
src/lib/gramjs/sessions/StringSession.js → gramjs/sessions/StringSession.js

@@ -82,6 +82,24 @@ class StringSession extends MemorySession {
             this.authKey.getKey(),
         ]))
     }
+
+    getAuthKey(dcId) {
+        if (dcId && dcId !== this.dcId) {
+            // Not supported.
+            return undefined
+        }
+
+        return this.authKey
+    }
+
+    setAuthKey(authKey, dcId) {
+        if (dcId && dcId !== this.dcId) {
+            // Not supported.
+            return undefined
+        }
+
+        this.authKey = authKey
+    }
 }
 
 module.exports = StringSession

+ 2 - 2
src/lib/gramjs/sessions/index.js → gramjs/sessions/index.js

@@ -1,9 +1,9 @@
 const Memory = require('./Memory')
 const StringSession = require('./StringSession')
-const LocalStorageSession = require('./LocalStorageSession')
+const CacheApiSession = require('./CacheApiSession')
 
 module.exports = {
     Memory,
     StringSession,
-    LocalStorageSession
+    CacheApiSession
 }

+ 42 - 0
gramjs/tl/MTProtoRequest.js

@@ -0,0 +1,42 @@
+class MTProtoRequest {
+    constructor() {
+        this.sent = false
+        this.msgId = 0 // long
+        this.sequence = 0
+
+        this.dirty = false
+        this.sendTime = 0
+        this.confirmReceived = false
+
+        // These should be overrode
+
+        this.constructorId = 0
+        this.confirmed = false
+        this.responded = false
+    }
+
+    // these should not be overrode
+    onSendSuccess() {
+        this.sendTime = new Date().getTime()
+        this.sent = true
+    }
+
+    onConfirm() {
+        this.confirmReceived = true
+    }
+
+    needResend() {
+        return this.dirty || (this.confirmed && !this.confirmReceived && new Date().getTime() - this.sendTime > 3000)
+    }
+
+    // These should be overrode
+    onSend() {
+        throw Error('Not overload ' + this.constructor.name)
+    }
+
+    onResponse(buffer) {}
+
+    onException(exception) {}
+}
+
+module.exports = MTProtoRequest

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 845 - 55
gramjs/tl/api.d.ts


+ 37 - 110
src/lib/gramjs/tl/gramJsApi.js → gramjs/tl/api.js

@@ -1,25 +1,14 @@
-<<<<<<< HEAD:src/lib/gramjs/tl/gramJsApi.js
-//const { readFileSync } = require('fs')
-import { readFileSync } from 'fs'
-
-
-=======
-const { readFileSync } = require('fs')
->>>>>>> 1ab480fe... Gram JS: Remove deps; Fix Factorizator and remove leemon lib:src/lib/gramjs/tl/api.js
 const {
     parseTl,
     serializeBytes,
     serializeDate
 } = require('./generationHelpers')
-<<<<<<< HEAD:src/lib/gramjs/tl/gramJsApi.js
-=======
 const { readBufferFromBigInt,toSignedLittleBuffer } = require('../Helpers')
-<<<<<<< HEAD:src/lib/gramjs/tl/gramJsApi.js
->>>>>>> 1a0b5c54... GramJS: Fix images loading: add `async-mutex`; Fix signed LE ints; Bring back `readExactly`:src/lib/gramjs/tl/api.js
 
-=======
+const tlContent = require('./static/api.reduced.tl').default;
+const schemeContent = require('./static/schema.reduced.tl').default;
+
 /*CONTEST
->>>>>>> 3f7f5bfa... GramJS: Comment out unused things:src/lib/gramjs/tl/api.js
 const NAMED_AUTO_CASTS = new Set([
     'chatId,int'
 ])
@@ -36,21 +25,15 @@ const AUTO_CASTS = new Set([
     'InputPhoto',
     'InputMessage',
     'InputDocument',
-    'InputChatPhoto',
+    'InputChatPhoto'
 ])
-<<<<<<< HEAD:src/lib/gramjs/tl/gramJsApi.js
-const struct = require('python-struct')
-=======
 
  */
 const CACHING_SUPPORTED = typeof self !== 'undefined' && self.localStorage !== undefined
->>>>>>> 3f7f5bfa... GramJS: Comment out unused things:src/lib/gramjs/tl/api.js
 
-const { readBufferFromBigInt } = require('../Helpers')
+const CACHE_KEY = 'GramJs:apiCache'
 
 function buildApiFromTlSchema() {
-<<<<<<< HEAD:src/lib/gramjs/tl/gramJsApi.js
-=======
     let definitions;
     const fromCache = CACHING_SUPPORTED && loadFromCache()
 
@@ -76,19 +59,27 @@ function loadFromCache() {
 }
 
 function loadFromTlSchemas() {
->>>>>>> 48d2d818... Support reconnect and re-sync:src/lib/gramjs/tl/api.js
-    const tlContent = readFileSync('./static/api.tl', 'utf-8')
     const [constructorParamsApi, functionParamsApi] = extractParams(tlContent)
-    const schemeContent = readFileSync('./static/schema.tl', 'utf-8')
     const [constructorParamsSchema, functionParamsSchema] = extractParams(schemeContent)
-    const constructors = [...constructorParamsApi, ...constructorParamsSchema]
-    const requests = [...functionParamsApi, ...functionParamsSchema]
-    return {
-        constructors: createClasses('constructor', constructors),
-        requests: createClasses('request', requests),
-    }
+    const constructors = [].concat(constructorParamsApi, constructorParamsSchema)
+    const requests = [].concat(functionParamsApi, functionParamsSchema)
+
+    return { constructors, requests }
 }
 
+function mergeWithNamespaces(obj1, obj2) {
+    const result = { ...obj1 }
+
+    Object.keys(obj2).forEach((key) => {
+        if (typeof obj2[key] === 'function' || !result[key]) {
+            result[key] = obj2[key]
+        } else {
+            Object.assign(result[key], obj2[key])
+        }
+    })
+
+    return result
+}
 
 function extractParams(fileContent) {
     const f = parseTl(fileContent, 109)
@@ -100,7 +91,6 @@ function extractParams(fileContent) {
     return [constructors, functions]
 }
 
-
 function argToBytes(x, type) {
     switch (type) {
         case 'int':
@@ -128,7 +118,7 @@ function argToBytes(x, type) {
         case 'date':
             return serializeDate(x)
         default:
-            throw new Error("unsupported")
+            return x.getBytes()
     }
 }
 /*
@@ -159,9 +149,7 @@ async function getInputFromResolve(utils, client, peer, peerType) {
             return await client.getPeerId(peer, false)
         default:
             throw new Error('unsupported peer type : ' + peerType)
-
     }
-
 }
 */
 function getArgFromReader(reader, arg) {
@@ -202,31 +190,30 @@ function getArgFromReader(reader, arg) {
             case 'date':
                 return reader.tgReadDate()
             default:
-
                 if (!arg.skipConstructorId) {
                     return reader.tgReadObject()
                 } else {
-                    return gramJsApi.constructors[arg.type].fromReader(reader)
+                    return api.constructors[arg.type].fromReader(reader)
                 }
         }
     }
-
 }
 
 function createClasses(classesType, params) {
     const classes = {}
     for (const classParams of params) {
-        const { name, constructorId, subclassOfId, argsConfig, namespace ,result} = classParams
+        const { name, constructorId, subclassOfId, argsConfig, namespace, result } = classParams
+        const fullName = [namespace, name].join('.').replace(/^\./, '')
 
         class VirtualClass {
             static CONSTRUCTOR_ID = constructorId
             static SUBCLASS_OF_ID = subclassOfId
-            static className = name
+            static className = fullName
             static classType = classesType
 
             CONSTRUCTOR_ID = constructorId
             SUBCLASS_OF_ID = subclassOfId
-            className = name
+            className = fullName
             classType = classesType
 
             constructor(args) {
@@ -274,36 +261,18 @@ function createClasses(classesType, params) {
                 for (const arg in argsConfig) {
                     if (argsConfig.hasOwnProperty(arg)) {
                         if (argsConfig[arg].isFlag) {
-<<<<<<< HEAD:src/lib/gramjs/tl/gramJsApi.js
-                            const tempBuffers = []
-                            if (argsConfig[arg] === 'true') {
-
-                            } else if (argsConfig[arg].isVector) {
-                                if (argsConfig[arg].useVectorId) {
-                                    tempBuffers.push(Buffer.from('15c4b51c', 'hex'))
-                                }
-                                const l = Buffer.alloc(4)
-                                l.writeInt32LE(this[arg].length, 0)
-                                buffers.push((!this[arg] ? Buffer.alloc(0) : Buffer.concat([...tempBuffers,
-                                    l, Buffer.concat(this[arg].map(x => argToBytes(x, argsConfig[arg].type)))])))
-                            }
-                        } else if (argsConfig[arg].isVector && !argsConfig[arg].isFlag) {
-                            if (argsConfig[arg].isVector) {
-=======
                           if (this[arg]===false || this[arg]===null || this[arg]===undefined || argsConfig[arg].type==='true'){
                               continue
                           }
                         }
                         if (argsConfig[arg].isVector) {
                             if (argsConfig[arg].useVectorId) {
->>>>>>> 49c97f0a... GramJS : Fix flags and vector reading (#154):src/lib/gramjs/tl/api.js
                                 buffers.push(Buffer.from('15c4b51c', 'hex'))
                             }
                             const l = Buffer.alloc(4)
                             l.writeInt32LE(this[arg].length, 0)
                             buffers.push(l, Buffer.concat(this[arg].map(x => argToBytes(x, argsConfig[arg].type))))
                         } else if (argsConfig[arg].flagIndicator) {
-                            // @ts-ignore
                             if (!Object.values(argsConfig)
                               .some((f) => f.isFlag)) {
                                 buffers.push(Buffer.alloc(4))
@@ -323,48 +292,14 @@ function createClasses(classesType, params) {
                                 buffers.push(f)
                             }
                         } else {
+                            buffers.push(argToBytes(this[arg], argsConfig[arg].type))
 
-                            switch (argsConfig[arg].type) {
-                                case 'int':
-                                    const i = Buffer.alloc(4)
-                                    i.writeInt32LE(this[arg], 0)
-                                    buffers.push(i)
-                                    break
-                                case 'long':
-                                    buffers.push(toSignedLittleBuffer(this[arg], 8))
-                                    break
-                                case 'int128':
-                                    buffers.push(toSignedLittleBuffer(this[arg], 16))
-                                    break
-                                case 'int256':
-                                    buffers.push(toSignedLittleBuffer(this[arg], 32))
-                                    break
-                                case 'double':
-                                    const d = Buffer.alloc(8)
-                                    d.writeDoubleLE(this[arg].toString(), 0)
-                                    buffers.push(d)
-                                    break
-                                case 'string':
-                                    buffers.push(serializeBytes(this[arg]))
-                                    break
-                                case 'Bool':
-                                    buffers.push(this[arg] ? Buffer.from('b5757299', 'hex') : Buffer.from('379779bc', 'hex'))
-                                    break
-                                case 'true':
-                                    break
-                                case 'bytes':
-                                    buffers.push(serializeBytes(this[arg]))
-                                    break
-                                case 'date':
-                                    buffers.push(serializeDate(this[arg]))
-                                    break
-                                default:
-                                    buffers.push(this[arg].getBytes())
-                                    let boxed = (argsConfig[arg].type.charAt(argsConfig[arg].type.indexOf('.') + 1))
-                                    boxed = boxed === boxed.toUpperCase()
-                                    if (!boxed) {
-                                        buffers.shift()
-                                    }
+                            if (this[arg] && typeof this[arg].getBytes === 'function') {
+                                let boxed = (argsConfig[arg].type.charAt(argsConfig[arg].type.indexOf('.') + 1))
+                                boxed = boxed === boxed.toUpperCase()
+                                if (!boxed) {
+                                    buffers.shift()
+                                }
                             }
                         }
                     }
@@ -420,11 +355,11 @@ function createClasses(classesType, params) {
                         if (argsConfig[arg].isVector) {
                             const temp = []
                             for (const x of this[arg]) {
-                                temp.push(await getInputFromResolve(utils, client, this[arg], argsConfig[arg].type))
+                                temp.push(await getInputFromResolve(utils, client, x, argsConfig[arg].type))
                             }
                             this[arg] = temp
                         } else {
-                            this[arg] =await getInputFromResolve(utils, client, this[arg], argsConfig[arg].type)
+                            this[arg] = await getInputFromResolve(utils, client, this[arg], argsConfig[arg].type)
                         }
                     }
                 }
@@ -433,24 +368,16 @@ function createClasses(classesType, params) {
 
         if (namespace) {
             if (!classes[namespace]) {
-                // @ts-ignore
                 classes[namespace] = {}
             }
             classes[namespace][name] = VirtualClass
 
         } else {
             classes[name] = VirtualClass
-
         }
     }
 
     return classes
 }
 
-<<<<<<< HEAD:src/lib/gramjs/tl/gramJsApi.js
-const gramJsApi = buildApiFromTlSchema()
-module.exports = gramJsApi
-=======
 module.exports = buildApiFromTlSchema()
-console.log(module.exports)
->>>>>>> 1ab480fe... Gram JS: Remove deps; Fix Factorizator and remove leemon lib:src/lib/gramjs/tl/api.js

+ 9 - 2
src/lib/gramjs/tl/core/GZIPPacked.js → gramjs/tl/core/GZIPPacked.js

@@ -1,5 +1,6 @@
 const { serializeBytes } = require('../index')
 const { inflate } = require('pako/dist/pako_inflate')
+//CONTEST const { deflate } = require('pako/dist/pako_deflate')
 
 class GZIPPacked {
     static CONSTRUCTOR_ID = 0x3072cfa1
@@ -13,7 +14,7 @@ class GZIPPacked {
 
     static async gzipIfSmaller(contentRelated, data) {
         if (contentRelated && data.length > 512) {
-            const gzipped = await new GZIPPacked(data).toBytes()
+            const gzipped = await (new GZIPPacked(data)).toBytes()
             if (gzipped.length < data.length) {
                 return gzipped
             }
@@ -21,12 +22,18 @@ class GZIPPacked {
         return data
     }
 
+    static gzip(input) {
+        return Buffer.from(input)
+        // TODO this usually makes it faster for large requests
+        //return Buffer.from(deflate(input, { level: 9, gzip: true }))
+    }
+
     static ungzip(input) {
         return Buffer.from(inflate(input))
     }
 
     async toBytes() {
-        const g = Buffer.alloc(0)
+        const g = Buffer.alloc(4)
         g.writeUInt32LE(GZIPPacked.CONSTRUCTOR_ID, 0)
         return Buffer.concat([
             g,

+ 0 - 0
src/lib/gramjs/tl/core/MessageContainer.js → gramjs/tl/core/MessageContainer.js


+ 0 - 0
src/lib/gramjs/tl/core/RPCResult.js → gramjs/tl/core/RPCResult.js


+ 0 - 0
src/lib/gramjs/tl/core/TLMessage.js → gramjs/tl/core/TLMessage.js


+ 17 - 0
gramjs/tl/core/index.js

@@ -0,0 +1,17 @@
+const TLMessage = require('./TLMessage')
+const RPCResult = require('./RPCResult')
+const MessageContainer = require('./MessageContainer')
+const GZIPPacked = require('./GZIPPacked')
+const coreObjects = {
+    [RPCResult.CONSTRUCTOR_ID]: RPCResult,
+    [GZIPPacked.CONSTRUCTOR_ID]: GZIPPacked,
+    [MessageContainer.CONSTRUCTOR_ID]: MessageContainer,
+}
+
+module.exports = {
+    TLMessage,
+    RPCResult,
+    MessageContainer,
+    GZIPPacked,
+    coreObjects,
+}

+ 326 - 0
gramjs/tl/generationHelpers.js

@@ -0,0 +1,326 @@
+const { crc32 } = require('../Helpers')
+const snakeToCamelCase = (name) => {
+    const result = name.replace(/(?:^|_)([a-z])/g, (_, g) => g.toUpperCase())
+    return result.replace(/_/g, '')
+}
+const variableSnakeToCamelCase = (str) => str.replace(
+    /([-_][a-z])/g,
+    (group) => group.toUpperCase()
+        .replace('-', '')
+        .replace('_', '')
+)
+
+const CORE_TYPES = new Set([
+    0xbc799737, // boolFalse#bc799737 = Bool;
+    0x997275b5, // boolTrue#997275b5 = Bool;
+    0x3fedd339, // true#3fedd339 = True;
+    0xc4b9f9bb, // error#c4b9f9bb code:int text:string = Error;
+    0x56730bcc // null#56730bcc = Null;
+])
+const AUTH_KEY_TYPES = new Set([
+    0x05162463, // resPQ,
+    0x83c95aec, // p_q_inner_data
+    0xa9f55f95, // p_q_inner_data_dc
+    0x3c6a84d4, // p_q_inner_data_temp
+    0x56fddf88, // p_q_inner_data_temp_dc
+    0xd0e8075c, // server_DH_params_ok
+    0xb5890dba, // server_DH_inner_data
+    0x6643b654, // client_DH_inner_data
+    0xd712e4be, // req_DH_params
+    0xf5045f1f, // set_client_DH_params
+    0x3072cfa1 // gzip_packed
+])
+
+
+const fromLine = (line, isFunction) => {
+    const match = line.match(/([\w.]+)(?:#([0-9a-fA-F]+))?(?:\s{?\w+:[\w\d<>#.?!]+}?)*\s=\s([\w\d<>#.?]+);$/)
+    if (!match) {
+        // Probably "vector#1cb5c415 {t:Type} # [ t ] = Vector t;"
+        throw new Error(`Cannot parse TLObject ${line}`)
+    }
+
+    const argsMatch = findAll(/({)?(\w+):([\w\d<>#.?!]+)}?/, line)
+    const currentConfig = {
+        name: match[1],
+        constructorId: parseInt(match[2], 16),
+        argsConfig: {},
+        subclassOfId: crc32(match[3]),
+        result: match[3],
+        isFunction: isFunction,
+        namespace: null
+    }
+    if (!currentConfig.constructorId) {
+
+        let hexId = ''
+        let args
+
+        if (Object.values(currentConfig.argsConfig).length) {
+            args = ` ${Object.keys(currentConfig.argsConfig).map((arg) => arg.toString()).join(' ')}`
+        } else {
+            args = ''
+        }
+
+        const representation = `${currentConfig.name}${hexId}${args} = ${currentConfig.result}`
+            .replace(/(:|\?)bytes /g, '$1string ')
+            .replace(/</g, ' ')
+            .replace(/>|{|}/g, '')
+            .replace(/ \w+:flags\.\d+\?true/g, '')
+
+        if (currentConfig.name === 'inputMediaInvoice') {
+            // eslint-disable-next-line no-empty
+            if (currentConfig.name === 'inputMediaInvoice') {
+            }
+        }
+
+        currentConfig.constructorId = crc32(Buffer.from(representation, 'utf8'))
+    }
+    for (const [brace, name, argType] of argsMatch) {
+        if (brace === undefined) {
+            currentConfig.argsConfig[variableSnakeToCamelCase(name)] = buildArgConfig(name, argType)
+        }
+    }
+    if (currentConfig.name.includes('.')) {
+        [currentConfig.namespace, currentConfig.name] = currentConfig.name.split(/\.(.+)/)
+    }
+    currentConfig.name = snakeToCamelCase(currentConfig.name)
+    /*
+    for (const arg in currentConfig.argsConfig){
+      if (currentConfig.argsConfig.hasOwnProperty(arg)){
+        if (currentConfig.argsConfig[arg].flagIndicator){
+          delete  currentConfig.argsConfig[arg]
+        }
+      }
+    }*/
+    return currentConfig
+}
+
+function buildArgConfig(name, argType) {
+    name = name === 'self' ? 'is_self' : name
+    // Default values
+    const currentConfig = {
+        isVector: false,
+        isFlag: false,
+        skipConstructorId: false,
+        flagIndex: -1,
+        flagIndicator: true,
+        type: null,
+        useVectorId: null
+    }
+
+    // Special case: some types can be inferred, which makes it
+    // less annoying to type. Currently the only type that can
+    // be inferred is if the name is 'random_id', to which a
+    // random ID will be assigned if left as None (the default)
+    let canBeInferred = name === 'random_id'
+
+    // The type can be an indicator that other arguments will be flags
+    if (argType !== '#') {
+        currentConfig.flagIndicator = false
+        // Strip the exclamation mark always to have only the name
+        currentConfig.type = argType.replace(/^!+/, '')
+
+        // The type may be a flag (flags.IDX?REAL_TYPE)
+        // Note that 'flags' is NOT the flags name; this
+        // is determined by a previous argument
+        // However, we assume that the argument will always be called 'flags'
+        // @ts-ignore
+        const flagMatch = currentConfig.type.match(/flags.(\d+)\?([\w<>.]+)/)
+
+        if (flagMatch) {
+            currentConfig.isFlag = true
+            currentConfig.flagIndex = Number(flagMatch[1]);
+            // Update the type to match the exact type, not the "flagged" one
+            [, , currentConfig.type] = flagMatch
+        }
+
+        // Then check if the type is a Vector<REAL_TYPE>
+        // @ts-ignore
+        const vectorMatch = currentConfig.type.match(/[Vv]ector<([\w\d.]+)>/)
+
+        if (vectorMatch) {
+            currentConfig.isVector = true
+
+            // If the type's first letter is not uppercase, then
+            // it is a constructor and we use (read/write) its ID.
+            // @ts-ignore
+            currentConfig.useVectorId = currentConfig.type.charAt(0) === 'V';
+
+            // Update the type to match the one inside the vector
+            [, currentConfig.type] = vectorMatch
+        }
+
+        // See use_vector_id. An example of such case is ipPort in
+        // help.configSpecial
+        // @ts-ignore
+        if (/^[a-z]$/.test(currentConfig.type.split('.')
+            .pop()
+            .charAt(0)
+        )
+        ) {
+            currentConfig.skipConstructorId = true
+        }
+
+        // The name may contain "date" in it, if this is the case and
+        // the type is "int", we can safely assume that this should be
+        // treated as a "date" object. Note that this is not a valid
+        // Telegram object, but it's easier to work with
+        // if (
+        //     this.type === 'int' &&
+        //     (/(\b|_)([dr]ate|until|since)(\b|_)/.test(name) ||
+        //         ['expires', 'expires_at', 'was_online'].includes(name))
+        // ) {
+        //     this.type = 'date';
+        // }
+    }
+    return currentConfig
+}
+
+
+const parseTl = function* (content, layer, methods = [], ignoreIds = CORE_TYPES) {
+    const methodInfo = (methods || []).reduce((o, m) => ({ ...o, [m.name]: m }), {})
+    const objAll = []
+    const objByName = {}
+    const objByType = {}
+
+    const file = content
+
+    let isFunction = false
+
+    for (let line of file.split('\n')) {
+        const commentIndex = line.indexOf('//')
+
+        if (commentIndex !== -1) {
+            line = line.slice(0, commentIndex)
+        }
+
+        line = line.trim()
+
+        if (!line) {
+            continue
+        }
+
+        const match = line.match(/---(\w+)---/)
+
+        if (match) {
+            const [, followingTypes] = match
+            isFunction = followingTypes === 'functions'
+            continue
+        }
+
+        try {
+            const result = fromLine(line, isFunction)
+
+            if (ignoreIds.has(result.constructorId)) {
+                continue
+            }
+
+            objAll.push(result)
+
+            if (!result.isFunction) {
+                if (!objByType[result.result]) {
+                    objByType[result.result] = []
+                }
+
+                objByName[result.name] = result
+                objByType[result.result].push(result)
+            }
+        } catch (e) {
+            if (!e.toString().includes('vector#1cb5c415')) {
+                throw e
+            }
+        }
+    }
+
+    // Once all objects have been parsed, replace the
+    // string type from the arguments with references
+    for (const obj of objAll) {
+        //console.log(obj)
+        if (AUTH_KEY_TYPES.has(obj.constructorId)) {
+            for (const arg in obj.argsConfig) {
+                if (obj.argsConfig[arg].type === 'string') {
+                    obj.argsConfig[arg].type = 'bytes'
+                }
+            }
+        }
+    }
+
+    for (const obj of objAll) {
+        yield obj
+    }
+
+}
+
+const findAll = (regex, str, matches = []) => {
+    if (!regex.flags.includes(`g`)) {
+        regex = new RegExp(regex.source, `g`)
+    }
+
+    const res = regex.exec(str)
+
+    if (res) {
+        matches.push(res.slice(1))
+        findAll(regex, str, matches)
+    }
+
+    return matches
+}
+
+function serializeBytes(data) {
+    if (!(data instanceof Buffer)) {
+        if (typeof data == 'string') {
+            data = Buffer.from(data)
+        } else {
+            throw Error(`Bytes or str expected, not ${data.constructor.name}`)
+        }
+    }
+    const r = []
+    let padding
+    if (data.length < 254) {
+        padding = (data.length + 1) % 4
+        if (padding !== 0) {
+            padding = 4 - padding
+        }
+        r.push(Buffer.from([data.length]))
+        r.push(data)
+    } else {
+        padding = data.length % 4
+        if (padding !== 0) {
+            padding = 4 - padding
+        }
+        r.push(Buffer.from([254, data.length % 256, (data.length >> 8) % 256, (data.length >> 16) % 256]))
+        r.push(data)
+    }
+    r.push(Buffer.alloc(padding)
+        .fill(0))
+
+    return Buffer.concat(r)
+
+}
+
+function serializeDate(dt) {
+    if (!dt) {
+        return Buffer.alloc(4)
+            .fill(0)
+    }
+    if (dt instanceof Date) {
+        dt = Math.floor((Date.now() - dt.getTime()) / 1000)
+    }
+    if (typeof dt == 'number') {
+        const t = Buffer.alloc(4)
+        t.writeInt32LE(dt, 0)
+        return t
+    }
+    throw Error(`Cannot interpret "${dt}" as a date`)
+}
+
+module.exports = {
+    findAll,
+    parseTl,
+    buildArgConfig,
+    fromLine,
+    CORE_TYPES,
+    serializeDate,
+    serializeBytes,
+    snakeToCamelCase,
+    variableSnakeToCamelCase
+}

+ 4 - 3
src/lib/gramjs/tl/index.js → gramjs/tl/index.js

@@ -1,10 +1,11 @@
-const { constructors, requests } = require('./gramJsApi')
+const api = require('./api')
 const { serializeBytes, serializeDate } = require('./generationHelpers')
 const patched = null
 
 module.exports = {
-    constructors,
-    requests,
+    // TODO Refactor internal usages to always use `api`.
+    constructors: api,
+    requests: api,
     patched,
     serializeBytes,
     serializeDate

+ 66 - 12
src/lib/gramjs/tl/static/api.tl → gramjs/tl/static/api.reduced.tl

@@ -32,7 +32,8 @@ inputMediaDocumentExternal#fb52dc99 flags:# url:string ttl_seconds:flags.0?int =
 inputMediaGame#d33f43f3 id:InputGame = InputMedia;
 inputMediaInvoice#f4e096c3 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:string = InputMedia;
 inputMediaGeoLive#ce4e82fd flags:# stopped:flags.0?true geo_point:InputGeoPoint period:flags.1?int = InputMedia;
-inputMediaPoll#abe9ca25 flags:# poll:Poll correct_answers:flags.0?Vector<bytes> = InputMedia;
+inputMediaPoll#f94e5f1 flags:# poll:Poll correct_answers:flags.0?Vector<bytes> solution:flags.1?string solution_entities:flags.1?Vector<MessageEntity> = InputMedia;
+inputMediaDice#e66fbf7b emoticon:string = InputMedia;
 inputChatPhotoEmpty#1ca48f57 = InputChatPhoto;
 inputChatUploadedPhoto#927c55b4 file:InputFile = InputChatPhoto;
 inputChatPhoto#8953ad37 id:InputPhoto = InputChatPhoto;
@@ -78,7 +79,7 @@ chatForbidden#7328bdb id:int title:string = Chat;
 channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
 channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat;
 chatFull#1b7c9db3 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int = ChatFull;
-channelFull#2d895c74 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_view_stats:flags.12?true can_set_location:flags.16?true has_scheduled:flags.19?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?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<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int pts:int = ChatFull;
+channelFull#f0e6672a flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_view_stats:flags.12?true can_set_location:flags.16?true has_scheduled:flags.19?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?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<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int = ChatFull;
 chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant;
 chatParticipantCreator#da13538a user_id:int = ChatParticipant;
 chatParticipantAdmin#e2d6e436 user_id:int inviter_id:int date:int = ChatParticipant;
@@ -101,6 +102,7 @@ messageMediaGame#fdb19008 game:Game = MessageMedia;
 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;
 messageMediaGeoLive#7c3c2609 geo:GeoPoint period:int = MessageMedia;
 messageMediaPoll#4bd6e798 poll:Poll results:PollResults = MessageMedia;
+messageMediaDice#3f7ee58b value:int emoticon:string = MessageMedia;
 messageActionEmpty#b6aef7b0 = MessageAction;
 messageActionChatCreate#a6638b9a title:string users:Vector<int> = MessageAction;
 messageActionChatEditTitle#b5a1ce5a title:string = MessageAction;
@@ -268,6 +270,9 @@ updateTheme#8216fba3 theme:Theme = Update;
 updateGeoLiveViewed#871fb939 peer:Peer msg_id:int = Update;
 updateLoginToken#564fe691 = Update;
 updateMessagePollVote#42f88f2c poll_id:long user_id:int options:Vector<bytes> = Update;
+updateDialogFilter#26ffde7d flags:# id:int filter:flags.0?DialogFilter = Update;
+updateDialogFilterOrder#a5d72105 order:Vector<int> = Update;
+updateDialogFilters#3504914f = Update;
 updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
 updates.differenceEmpty#5d75a138 date:int seq:int = updates.Difference;
 updates.difference#f49ca0 new_messages:Vector<Message> new_encrypted_messages:Vector<EncryptedMessage> other_updates:Vector<Update> chats:Vector<Chat> users:Vector<User> state:updates.State = updates.Difference;
@@ -382,7 +387,7 @@ messages.affectedMessages#84d19185 pts:int pts_count:int = messages.AffectedMess
 webPageEmpty#eb1477e8 id:long = WebPage;
 webPagePending#c586da1c id:long date:int = WebPage;
 webPage#e89c45b2 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 attributes:flags.12?Vector<WebPageAttribute> = WebPage;
-webPageNotModified#85849473 = WebPage;
+webPageNotModified#7311ca11 flags:# cached_page_views:flags.0?int = WebPage;
 authorization#ad01d61d flags:# current:flags.0?true official_app:flags.1?true password_pending:flags.2?true hash:long 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;
 account.authorizations#1250abde authorizations:Vector<Authorization> = account.Authorizations;
 account.password#ad2641f8 flags:# has_recovery:flags.0?true has_secure_values:flags.1?true has_password:flags.2?true current_algo:flags.2?PasswordKdfAlgo srp_B:flags.2?bytes srp_id:flags.2?long hint:flags.3?string email_unconfirmed_pattern:flags.4?string new_algo:PasswordKdfAlgo new_secure_algo:SecurePasswordKdfAlgo secure_random:bytes = account.Password;
@@ -398,6 +403,7 @@ inputStickerSetEmpty#ffb62b95 = InputStickerSet;
 inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet;
 inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
 inputStickerSetAnimatedEmoji#28703c8 = InputStickerSet;
+inputStickerSetDice#e67f520e emoticon:string = InputStickerSet;
 stickerSet#eeb46f27 flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumb:flags.4?PhotoSize thumb_dc_id:flags.4?int count:int hash:int = StickerSet;
 messages.stickerSet#b60a24a6 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = messages.StickerSet;
 botCommand#c27ac8c7 command:string description:string = BotCommand;
@@ -436,6 +442,7 @@ messageEntityCashtag#4c4e743f offset:int length:int = MessageEntity;
 messageEntityUnderline#9c4e7e8b offset:int length:int = MessageEntity;
 messageEntityStrike#bf0693d4 offset:int length:int = MessageEntity;
 messageEntityBlockquote#20df5d0 offset:int length:int = MessageEntity;
+messageEntityBankCard#761e6af4 offset:int length:int = MessageEntity;
 inputChannelEmpty#ee8c1e86 = InputChannel;
 inputChannel#afeb712e channel_id:int access_hash:long = InputChannel;
 inputChannelFromMessage#2a286531 peer:InputPeer msg_id:int channel_id:int = InputChannel;
@@ -514,8 +521,8 @@ contacts.topPeers#70b772a8 categories:Vector<TopPeerCategoryPeers> chats:Vector<
 contacts.topPeersDisabled#b52c939d = contacts.TopPeers;
 draftMessageEmpty#1b0c841a flags:# date:flags.0?int = DraftMessage;
 draftMessage#fd8e711f flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int message:string entities:flags.3?Vector<MessageEntity> date:int = DraftMessage;
-messages.featuredStickersNotModified#4ede3cf = messages.FeaturedStickers;
-messages.featuredStickers#f89d88e5 hash:int sets:Vector<StickerSetCovered> unread:Vector<long> = messages.FeaturedStickers;
+messages.featuredStickersNotModified#c6dc0c66 count:int = messages.FeaturedStickers;
+messages.featuredStickers#b6abc341 hash:int count:int sets:Vector<StickerSetCovered> unread:Vector<long> = messages.FeaturedStickers;
 messages.recentStickersNotModified#b17f890 = messages.RecentStickers;
 messages.recentStickers#22f3afb3 hash:int packs:Vector<StickerPack> stickers:Vector<Document> dates:Vector<int> = messages.RecentStickers;
 messages.archivedStickers#4fcba9c8 count:int sets:Vector<StickerSetCovered> = messages.ArchivedStickers;
@@ -614,7 +621,7 @@ phoneCallAccepted#997c454a flags:# video:flags.5?true id:long access_hash:long d
 phoneCall#8742ae7f flags:# p2p_allowed:flags.5?true id:long access_hash:long date:int admin_id:int participant_id:int g_a_or_b:bytes key_fingerprint:long protocol:PhoneCallProtocol connections:Vector<PhoneConnection> start_date:int = PhoneCall;
 phoneCallDiscarded#50ca4de1 flags:# need_rating:flags.2?true need_debug:flags.3?true video:flags.5?true id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = PhoneCall;
 phoneConnection#9d4c17c0 id:long ip:string ipv6:string port:int peer_tag:bytes = PhoneConnection;
-phoneCallProtocol#a2bb35cb flags:# udp_p2p:flags.0?true udp_reflector:flags.1?true min_layer:int max_layer:int = PhoneCallProtocol;
+phoneCallProtocol#fc878fc8 flags:# udp_p2p:flags.0?true udp_reflector:flags.1?true min_layer:int max_layer:int library_versions:Vector<string> = PhoneCallProtocol;
 phone.phoneCall#ec82e140 phone_call:PhoneCall users:Vector<User> = phone.PhoneCall;
 upload.cdnFileReuploadNeeded#eea8e46e request_token:bytes = upload.CdnFile;
 upload.cdnFile#a99fca4f bytes:bytes = upload.CdnFile;
@@ -743,20 +750,21 @@ pageListItemBlocks#25e073fc blocks:Vector<PageBlock> = PageListItem;
 pageListOrderedItemText#5e068047 num:string text:RichText = PageListOrderedItem;
 pageListOrderedItemBlocks#98dd8936 num:string blocks:Vector<PageBlock> = PageListOrderedItem;
 pageRelatedArticle#b390dc08 flags:# url:string webpage_id:long title:flags.0?string description:flags.1?string photo_id:flags.2?long author:flags.3?string published_date:flags.4?int = PageRelatedArticle;
-page#ae891bec flags:# part:flags.0?true rtl:flags.1?true v2:flags.2?true url:string blocks:Vector<PageBlock> photos:Vector<Photo> documents:Vector<Document> = Page;
+page#98657f0d flags:# part:flags.0?true rtl:flags.1?true v2:flags.2?true url:string blocks:Vector<PageBlock> photos:Vector<Photo> documents:Vector<Document> views:flags.3?int = Page;
 help.supportName#8c05f1c9 name:string = help.SupportName;
 help.userInfoEmpty#f3ae2eed = help.UserInfo;
 help.userInfo#1eb3758 message:string entities:Vector<MessageEntity> author:string date:int = help.UserInfo;
 pollAnswer#6ca9c2e9 text:string option:bytes = PollAnswer;
-poll#d5529d06 id:long flags:# closed:flags.0?true public_voters:flags.1?true multiple_choice:flags.2?true quiz:flags.3?true question:string answers:Vector<PollAnswer> = Poll;
+poll#86e18161 id:long flags:# closed:flags.0?true public_voters:flags.1?true multiple_choice:flags.2?true quiz:flags.3?true question:string answers:Vector<PollAnswer> close_period:flags.4?int close_date:flags.5?int = Poll;
 pollAnswerVoters#3b6ddad2 flags:# chosen:flags.0?true correct:flags.1?true option:bytes voters:int = PollAnswerVoters;
-pollResults#c87024a2 flags:# min:flags.0?true results:flags.1?Vector<PollAnswerVoters> total_voters:flags.2?int recent_voters:flags.3?Vector<int> = PollResults;
+pollResults#badcc1a3 flags:# min:flags.0?true results:flags.1?Vector<PollAnswerVoters> total_voters:flags.2?int recent_voters:flags.3?Vector<int> solution:flags.4?string solution_entities:flags.4?Vector<MessageEntity> = PollResults;
 chatOnlines#f041e250 onlines:int = ChatOnlines;
 statsURL#47a971e0 url:string = StatsURL;
 chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true = ChatAdminRights;
 chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true until_date:int = ChatBannedRights;
 inputWallPaper#e630b979 id:long access_hash:long = InputWallPaper;
 inputWallPaperSlug#72091c80 slug:string = InputWallPaper;
+inputWallPaperNoFile#8427bbac = InputWallPaper;
 account.wallPapersNotModified#1c199183 = account.WallPapers;
 account.wallPapers#702b65a9 hash:int wallpapers:Vector<WallPaper> = account.WallPapers;
 codeSettings#debebe83 flags:# allow_flashcall:flags.0?true current_number:flags.1?true allow_app_hash:flags.4?true = CodeSettings;
@@ -779,6 +787,7 @@ urlAuthResultDefault#a9d6db1f = UrlAuthResult;
 channelLocationEmpty#bfb5ad8b = ChannelLocation;
 channelLocation#209b82db geo_point:GeoPoint address:string = ChannelLocation;
 peerLocated#ca461b5d peer:Peer expires:int distance:int = PeerLocated;
+peerSelfLocated#f8ec284b expires:int = PeerLocated;
 restrictionReason#d072acb4 platform:string reason:string text:string = RestrictionReason;
 inputTheme#3c5693e9 id:long access_hash:long = InputTheme;
 inputThemeSlug#f5890df1 slug:string = InputTheme;
@@ -802,9 +811,11 @@ messageUserVote#a28e5559 user_id:int option:bytes date:int = MessageUserVote;
 messageUserVoteInputOption#36377430 user_id:int date:int = MessageUserVote;
 messageUserVoteMultiple#e8fe0de user_id:int options:Vector<bytes> date:int = MessageUserVote;
 messages.votesList#823f649 flags:# count:int votes:Vector<MessageUserVote> users:Vector<User> next_offset:flags.0?string = messages.VotesList;
-
+dialogFilter#7438f7e8 flags:# contacts:flags.0?true non_contacts:flags.1?true groups:flags.2?true broadcasts:flags.3?true bots:flags.4?true exclude_muted:flags.11?true exclude_read:flags.12?true exclude_archived:flags.13?true id:int title:string emoticon:flags.25?string pinned_peers:Vector<InputPeer> include_peers:Vector<InputPeer> exclude_peers:Vector<InputPeer> = DialogFilter;
+dialogFilterSuggested#77744d4a filter:DialogFilter description:string = DialogFilterSuggested;
+// Some unused types were removed here
 ---functions---
-initConnection#785188b8 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy query:!X = X;
+initConnection#c1cd5ea9 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy params:flags.1?JSONValue query:!X = X;
 invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X;
 auth.sendCode#a677244f phone_number:string api_id:int api_hash:string settings:CodeSettings = auth.SentCode;
 auth.signUp#80eee427 phone_number:string phone_code_hash:string first_name:string last_name:string = auth.Authorization;
@@ -819,8 +830,13 @@ auth.requestPasswordRecovery#d897bc66 = auth.PasswordRecovery;
 auth.resendCode#3ef1a9bf phone_number:string phone_code_hash:string = auth.SentCode;
 auth.cancelCode#1f040578 phone_number:string phone_code_hash:string = Bool;
 auth.dropTempAuthKeys#8e48a188 except_auth_keys:Vector<long> = Bool;
+account.updateProfile#78515775 flags:# first_name:flags.0?string last_name:flags.1?string about:flags.2?string = User;
+account.getWallPapers#aabb1763 hash:int = account.WallPapers;
+auth.exportLoginToken#b1b41517 api_id:int api_hash:string except_ids:Vector<int> = auth.LoginToken;
+auth.importLoginToken#95ac5ce4 token:bytes = auth.LoginToken;
 account.checkUsername#2714d86c username:string = Bool;
 account.updateUsername#3e0bdd7c username:string = User;
+account.updateNotifySettings#84be5b93 peer:InputNotifyPeer settings:InputPeerNotifySettings = Bool;
 account.getAuthorizations#e320c158 = account.Authorizations;
 account.resetAuthorization#df77f3bc hash:long = Bool;
 account.getPassword#548a30f5 = account.Password;
@@ -830,39 +846,70 @@ account.getTmpPassword#449e0b51 password:InputCheckPasswordSRP period:int = acco
 account.sendVerifyPhoneCode#a5a356f9 phone_number:string settings:CodeSettings = auth.SentCode;
 users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
 users.getFullUser#ca30a5b1 id:InputUser = UserFull;
+contacts.getContacts#c023849f hash:int = contacts.Contacts;
 contacts.search#11f812d8 q:string limit:int = contacts.Found;
 contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer;
+contacts.getTopPeers#d4982db5 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true phone_calls:flags.3?true forward_users:flags.4?true forward_chats:flags.5?true groups:flags.10?true channels:flags.15?true offset:int limit:int hash:int = contacts.TopPeers;
 messages.getMessages#63c66506 id:Vector<InputMessage> = messages.Messages;
 messages.getDialogs#a0ee3b73 flags:# exclude_pinned:flags.0?true folder_id:flags.1?int offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:int = messages.Dialogs;
 messages.getHistory#dcbb8260 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages;
 messages.search#8614ef68 flags:# peer:InputPeer q:string from_id:flags.0?InputUser filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages;
 messages.readHistory#e306d3a peer:InputPeer max_id:int = messages.AffectedMessages;
+messages.deleteHistory#1c015b09 flags:# just_clear:flags.0?true revoke:flags.1?true peer:InputPeer max_id:int = messages.AffectedHistory;
 messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
 messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
 messages.setTyping#a3825e50 peer:InputPeer action:SendMessageAction = Bool;
 messages.sendMessage#520c3870 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<MessageEntity> schedule_date:flags.10?int = Updates;
 messages.sendMedia#3491eba9 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 message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
+messages.forwardMessages#d9fee60e flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true grouped:flags.9?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer schedule_date:flags.10?int = Updates;
 messages.getChats#3c6aa187 id:Vector<int> = messages.Chats;
 messages.getFullChat#3b831c66 chat_id:int = messages.ChatFull;
+messages.editChatPhoto#ca4c79d8 chat_id:int photo:InputChatPhoto = Updates;
 messages.createChat#9cb126e users:Vector<InputUser> title:string = Updates;
 messages.getDhConfig#26cf8950 version:int random_length:int = messages.DhConfig;
+messages.readMessageContents#36a73f77 id:Vector<int> = messages.AffectedMessages;
+messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers;
 messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector<MessageEntity> = MessageMedia;
+messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet;
+messages.installStickerSet#c78fe460 stickerset:InputStickerSet archived:Bool = messages.StickerSetInstallResult;
+messages.uninstallStickerSet#f96e55de stickerset:InputStickerSet = Bool;
 messages.searchGlobal#bf7225a4 flags:# folder_id:flags.0?int q:string offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
 messages.getDocumentByHash#338e2464 sha256:bytes size:int mime_type:string = Document;
+messages.searchGifs#bf9a776b q:string offset:int = messages.FoundGifs;
+messages.getSavedGifs#83bf3d52 hash:int = messages.SavedGifs;
+messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults;
 messages.editMessage#48f71778 flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.15?int = Updates;
 messages.getPeerDialogs#e470bcfd peers:Vector<InputDialogPeer> = messages.PeerDialogs;
+messages.saveDraft#bc39e14b flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int peer:InputPeer message:string entities:flags.3?Vector<MessageEntity> = Bool;
+messages.getFeaturedStickers#2dacca4f hash:int = messages.FeaturedStickers;
+messages.readFeaturedStickers#5b118126 id:Vector<long> = Bool;
+messages.getRecentStickers#5ea192c9 flags:# attached:flags.0?true hash:int = messages.RecentStickers;
 messages.getWebPage#32ca8f91 url:string hash:int = WebPage;
+messages.toggleDialogPin#a731e257 flags:# pinned:flags.0?true peer:InputDialogPeer = Bool;
+messages.getPinnedDialogs#d6b94df2 folder_id:int = messages.PeerDialogs;
 messages.uploadMedia#519bc2b1 peer:InputPeer media:InputMedia = MessageMedia;
+messages.getFavedStickers#21ce0b0e hash:int = messages.FavedStickers;
+messages.faveSticker#b9ffc55b id:InputDocument unfave:Bool = Bool;
+messages.sendMultiMedia#cc0110cb flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int = Updates;
+messages.searchStickerSets#c2b7d08b flags:# exclude_featured:flags.0?true q:string hash:int = messages.FoundStickerSets;
+messages.markDialogUnread#c286d98f flags:# unread:flags.0?true peer:InputDialogPeer = Bool;
 messages.updatePinnedMessage#d2aaf7ec flags:# silent:flags.0?true peer:InputPeer id:int = Updates;
+messages.sendVote#10ea6184 peer:InputPeer msg_id:int options:Vector<bytes> = Updates;
 messages.getOnlines#6e2be050 peer:InputPeer = ChatOnlines;
+messages.getPollVotes#b86e380e flags:# peer:InputPeer id:int option:flags.0?bytes offset:flags.1?string limit:int = messages.VotesList;
+messages.getDialogFilters#f19ed96d = Vector<DialogFilter>;
+messages.getSuggestedDialogFilters#a29cd42c = Vector<DialogFilterSuggested>;
+messages.updateDialogFilter#1ad4a04a flags:# id:int filter:flags.0?DialogFilter = Bool;
 updates.getState#edd4882a = updates.State;
 updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
 updates.getChannelDifference#3173d78 flags:# force:flags.0?true channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference;
+photos.uploadProfilePhoto#4f32c098 file:InputFile = photos.Photo;
 upload.saveFilePart#b304a621 file_id:long file_part:int bytes:bytes = Bool;
 upload.getFile#b15a9afc flags:# precise:flags.0?true cdn_supported:flags.1?true location:InputFileLocation offset:int limit:int = upload.File;
 upload.saveBigFilePart#de7b673d file_id:long file_part:int file_total_parts:int bytes:bytes = Bool;
 help.getConfig#c4f9186b = Config;
 help.getNearestDc#1fb33026 = NearestDc;
+help.getSupport#9cdf08cd = help.Support;
 help.acceptTermsOfService#ee72f79a id:DataJSON = Bool;
 channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool;
 channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector<int> = messages.AffectedMessages;
@@ -871,4 +918,11 @@ channels.getParticipants#123e05e9 channel:InputChannel filter:ChannelParticipant
 channels.getParticipant#546dd7a6 channel:InputChannel user_id:InputUser = channels.ChannelParticipant;
 channels.getChannels#a7f6bbb id:Vector<InputChannel> = messages.Chats;
 channels.getFullChannel#8736a09 channel:InputChannel = messages.ChatFull;
-// LAYER 109
+channels.createChannel#3d5fb10f flags:# broadcast:flags.0?true megagroup:flags.1?true title:string about:string geo_point:flags.2?InputGeoPoint address:flags.2?string = Updates;
+channels.editPhoto#f12e57c9 channel:InputChannel photo:InputChatPhoto = Updates;
+channels.joinChannel#24b524c5 channel:InputChannel = Updates;
+channels.leaveChannel#f836aa95 channel:InputChannel = Updates;
+channels.deleteChannel#c0111fe3 channel:InputChannel = Updates;
+channels.readMessageContents#eab5dc38 channel:InputChannel id:Vector<int> = Bool;
+folders.editPeerFolders#6847d0ab folder_peers:Vector<InputFolderPeer> = Updates;
+// LAYER 112

+ 1500 - 0
gramjs/tl/static/api.tl

@@ -0,0 +1,1500 @@
+///////////////////////////////
+/////////////////// Layer cons
+///////////////////////////////
+
+//invokeAfterMsg#cb9f372d msg_id:long query:!X = X;
+//invokeAfterMsgs#3dc4b4f0 msg_ids:Vector<long> query:!X = X;
+//invokeWithLayer1#53835315 query:!X = X;
+//invokeWithLayer2#289dd1f6 query:!X = X;
+//invokeWithLayer3#b7475268 query:!X = X;
+//invokeWithLayer4#dea0d430 query:!X = X;
+//invokeWithLayer5#417a57ae query:!X = X;
+//invokeWithLayer6#3a64d54d query:!X = X;
+//invokeWithLayer7#a5be56d3 query:!X = X;
+//invokeWithLayer8#e9abd9fd query:!X = X;
+//invokeWithLayer9#76715a63 query:!X = X;
+//invokeWithLayer10#39620c41 query:!X = X;
+//invokeWithLayer11#a6b88fdf query:!X = X;
+//invokeWithLayer12#dda60d3c query:!X = X;
+//invokeWithLayer13#427c8ea2 query:!X = X;
+//invokeWithLayer14#2b9b08fa query:!X = X;
+//invokeWithLayer15#b4418b64 query:!X = X;
+//invokeWithLayer16#cf5f0987 query:!X = X;
+//invokeWithLayer17#50858a19 query:!X = X;
+//invokeWithLayer18#1c900537 query:!X = X;
+//invokeWithLayer#da9b0d0d layer:int query:!X = X; // after 18 layer
+
+///////////////////////////////
+///////// Main application API
+///////////////////////////////
+
+boolFalse#bc799737 = Bool;
+boolTrue#997275b5 = Bool;
+
+true#3fedd339 = True;
+
+vector#1cb5c415 {t:Type} # [ t ] = Vector t;
+
+error#c4b9f9bb code:int text:string = Error;
+
+null#56730bcc = Null;
+
+inputPeerEmpty#7f3b18ea = InputPeer;
+inputPeerSelf#7da07ec9 = InputPeer;
+inputPeerChat#179be863 chat_id:int = InputPeer;
+inputPeerUser#7b8e7de6 user_id:int access_hash:long = InputPeer;
+inputPeerChannel#20adaef8 channel_id:int access_hash:long = InputPeer;
+inputPeerUserFromMessage#17bae2e6 peer:InputPeer msg_id:int user_id:int = InputPeer;
+inputPeerChannelFromMessage#9c95f7bb peer:InputPeer msg_id:int channel_id:int = InputPeer;
+
+inputUserEmpty#b98886cf = InputUser;
+inputUserSelf#f7c1b13f = InputUser;
+inputUser#d8292816 user_id:int access_hash:long = InputUser;
+inputUserFromMessage#2d117597 peer:InputPeer msg_id:int user_id:int = InputUser;
+
+inputPhoneContact#f392b7f4 client_id:long phone:string first_name:string last_name:string = InputContact;
+
+inputFile#f52ff27f id:long parts:int name:string md5_checksum:string = InputFile;
+inputFileBig#fa4f0bb5 id:long parts:int name:string = InputFile;
+
+inputMediaEmpty#9664f57f = InputMedia;
+inputMediaUploadedPhoto#1e287d04 flags:# file:InputFile stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
+inputMediaPhoto#b3ba0635 flags:# id:InputPhoto ttl_seconds:flags.0?int = InputMedia;
+inputMediaGeoPoint#f9c44144 geo_point:InputGeoPoint = InputMedia;
+inputMediaContact#f8ab7dfb phone_number:string first_name:string last_name:string vcard:string = InputMedia;
+inputMediaUploadedDocument#5b38c6c1 flags:# nosound_video:flags.3?true file:InputFile thumb:flags.2?InputFile mime_type:string attributes:Vector<DocumentAttribute> stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
+inputMediaDocument#23ab23d2 flags:# id:InputDocument ttl_seconds:flags.0?int = InputMedia;
+inputMediaVenue#c13d1c11 geo_point:InputGeoPoint title:string address:string provider:string venue_id:string venue_type:string = InputMedia;
+inputMediaGifExternal#4843b0fd url:string q:string = InputMedia;
+inputMediaPhotoExternal#e5bbfe1a flags:# url:string ttl_seconds:flags.0?int = InputMedia;
+inputMediaDocumentExternal#fb52dc99 flags:# url:string ttl_seconds:flags.0?int = InputMedia;
+inputMediaGame#d33f43f3 id:InputGame = InputMedia;
+inputMediaInvoice#f4e096c3 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:string = InputMedia;
+inputMediaGeoLive#ce4e82fd flags:# stopped:flags.0?true geo_point:InputGeoPoint period:flags.1?int = InputMedia;
+inputMediaPoll#f94e5f1 flags:# poll:Poll correct_answers:flags.0?Vector<bytes> solution:flags.1?string solution_entities:flags.1?Vector<MessageEntity> = InputMedia;
+inputMediaDice#e66fbf7b emoticon:string = InputMedia;
+
+inputChatPhotoEmpty#1ca48f57 = InputChatPhoto;
+inputChatUploadedPhoto#927c55b4 file:InputFile = InputChatPhoto;
+inputChatPhoto#8953ad37 id:InputPhoto = InputChatPhoto;
+
+inputGeoPointEmpty#e4c123d6 = InputGeoPoint;
+inputGeoPoint#f3b7acc9 lat:double long:double = InputGeoPoint;
+
+inputPhotoEmpty#1cd7bf0d = InputPhoto;
+inputPhoto#3bb3b94a id:long access_hash:long file_reference:bytes = InputPhoto;
+
+inputFileLocation#dfdaabe1 volume_id:long local_id:int secret:long file_reference:bytes = InputFileLocation;
+inputEncryptedFileLocation#f5235d55 id:long access_hash:long = InputFileLocation;
+inputDocumentFileLocation#bad07584 id:long access_hash:long file_reference:bytes thumb_size:string = InputFileLocation;
+inputSecureFileLocation#cbc7ee28 id:long access_hash:long = InputFileLocation;
+inputTakeoutFileLocation#29be5899 = InputFileLocation;
+inputPhotoFileLocation#40181ffe id:long access_hash:long file_reference:bytes thumb_size:string = InputFileLocation;
+inputPhotoLegacyFileLocation#d83466f3 id:long access_hash:long file_reference:bytes volume_id:long local_id:int secret:long = InputFileLocation;
+inputPeerPhotoFileLocation#27d69997 flags:# big:flags.0?true peer:InputPeer volume_id:long local_id:int = InputFileLocation;
+inputStickerSetThumb#dbaeae9 stickerset:InputStickerSet volume_id:long local_id:int = InputFileLocation;
+
+peerUser#9db1bc6d user_id:int = Peer;
+peerChat#bad0e5bb chat_id:int = Peer;
+peerChannel#bddde532 channel_id:int = Peer;
+
+storage.fileUnknown#aa963b05 = storage.FileType;
+storage.filePartial#40bc6f52 = storage.FileType;
+storage.fileJpeg#7efe0e = storage.FileType;
+storage.fileGif#cae1aadf = storage.FileType;
+storage.filePng#a4f63c0 = storage.FileType;
+storage.filePdf#ae1e508d = storage.FileType;
+storage.fileMp3#528a0677 = storage.FileType;
+storage.fileMov#4b09ebbc = storage.FileType;
+storage.fileMp4#b3cea0e4 = storage.FileType;
+storage.fileWebp#1081464c = storage.FileType;
+
+userEmpty#200250ba id:int = User;
+user#938458c1 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 support:flags.23?true scam:flags.24?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?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User;
+
+userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
+userProfilePhoto#ecd75d8c photo_id:long photo_small:FileLocation photo_big:FileLocation dc_id:int = UserProfilePhoto;
+
+userStatusEmpty#9d05049 = UserStatus;
+userStatusOnline#edb93949 expires:int = UserStatus;
+userStatusOffline#8c703f was_online:int = UserStatus;
+userStatusRecently#e26f42f1 = UserStatus;
+userStatusLastWeek#7bf09fc = UserStatus;
+userStatusLastMonth#77ebc742 = UserStatus;
+
+chatEmpty#9ba2d800 id:int = Chat;
+chat#3bda1bde flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true deactivated:flags.5?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat;
+chatForbidden#7328bdb id:int title:string = Chat;
+channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
+channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat;
+
+chatFull#1b7c9db3 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int = ChatFull;
+channelFull#f0e6672a flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_view_stats:flags.12?true can_set_location:flags.16?true has_scheduled:flags.19?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?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<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int = ChatFull;
+
+chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant;
+chatParticipantCreator#da13538a user_id:int = ChatParticipant;
+chatParticipantAdmin#e2d6e436 user_id:int inviter_id:int date:int = ChatParticipant;
+
+chatParticipantsForbidden#fc900c2b flags:# chat_id:int self_participant:flags.0?ChatParticipant = ChatParticipants;
+chatParticipants#3f460fed chat_id:int participants:Vector<ChatParticipant> version:int = ChatParticipants;
+
+chatPhotoEmpty#37c1011c = ChatPhoto;
+chatPhoto#475cdbd5 photo_small:FileLocation photo_big:FileLocation dc_id:int = ChatPhoto;
+
+messageEmpty#83e5de54 id:int = Message;
+message#452c0e65 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?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<MessageEntity> views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> = Message;
+messageService#9e19a1f6 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?int to_id:Peer reply_to_msg_id:flags.3?int date:int action:MessageAction = Message;
+
+messageMediaEmpty#3ded6320 = MessageMedia;
+messageMediaPhoto#695150d7 flags:# photo:flags.0?Photo ttl_seconds:flags.2?int = MessageMedia;
+messageMediaGeo#56e0d474 geo:GeoPoint = MessageMedia;
+messageMediaContact#cbf24940 phone_number:string first_name:string last_name:string vcard:string user_id:int = MessageMedia;
+messageMediaUnsupported#9f84f49e = MessageMedia;
+messageMediaDocument#9cb070d7 flags:# document:flags.0?Document ttl_seconds:flags.2?int = MessageMedia;
+messageMediaWebPage#a32dd600 webpage:WebPage = MessageMedia;
+messageMediaVenue#2ec0533f geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string = MessageMedia;
+messageMediaGame#fdb19008 game:Game = MessageMedia;
+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;
+messageMediaGeoLive#7c3c2609 geo:GeoPoint period:int = MessageMedia;
+messageMediaPoll#4bd6e798 poll:Poll results:PollResults = MessageMedia;
+messageMediaDice#3f7ee58b value:int emoticon:string = MessageMedia;
+
+messageActionEmpty#b6aef7b0 = MessageAction;
+messageActionChatCreate#a6638b9a title:string users:Vector<int> = MessageAction;
+messageActionChatEditTitle#b5a1ce5a title:string = MessageAction;
+messageActionChatEditPhoto#7fcb13a8 photo:Photo = MessageAction;
+messageActionChatDeletePhoto#95e3fbef = MessageAction;
+messageActionChatAddUser#488a7337 users:Vector<int> = MessageAction;
+messageActionChatDeleteUser#b2ae9b0c user_id:int = MessageAction;
+messageActionChatJoinedByLink#f89cf5e8 inviter_id:int = MessageAction;
+messageActionChannelCreate#95d2ac92 title:string = MessageAction;
+messageActionChatMigrateTo#51bdb021 channel_id:int = MessageAction;
+messageActionChannelMigrateFrom#b055eaee title:string chat_id:int = MessageAction;
+messageActionPinMessage#94bd38ed = MessageAction;
+messageActionHistoryClear#9fbab604 = MessageAction;
+messageActionGameScore#92a72876 game_id:long score:int = MessageAction;
+messageActionPaymentSentMe#8f31b327 flags:# currency:string total_amount:long payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string charge:PaymentCharge = MessageAction;
+messageActionPaymentSent#40699cd0 currency:string total_amount:long = MessageAction;
+messageActionPhoneCall#80e11a7f flags:# video:flags.2?true call_id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = MessageAction;
+messageActionScreenshotTaken#4792929b = MessageAction;
+messageActionCustomAction#fae69f56 message:string = MessageAction;
+messageActionBotAllowed#abe9affe domain:string = MessageAction;
+messageActionSecureValuesSentMe#1b287353 values:Vector<SecureValue> credentials:SecureCredentialsEncrypted = MessageAction;
+messageActionSecureValuesSent#d95c6154 types:Vector<SecureValueType> = MessageAction;
+messageActionContactSignUp#f3f25f76 = MessageAction;
+
+dialog#2c171f72 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int = Dialog;
+dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
+
+photoEmpty#2331b22d id:long = Photo;
+photo#d07504a5 flags:# has_stickers:flags.0?true id:long access_hash:long file_reference:bytes date:int sizes:Vector<PhotoSize> dc_id:int = Photo;
+
+photoSizeEmpty#e17e23c type:string = PhotoSize;
+photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize;
+photoCachedSize#e9a734fa type:string location:FileLocation w:int h:int bytes:bytes = PhotoSize;
+photoStrippedSize#e0b0bc2e type:string bytes:bytes = PhotoSize;
+
+geoPointEmpty#1117dd5f = GeoPoint;
+geoPoint#296f104 long:double lat:double access_hash:long = GeoPoint;
+
+auth.sentCode#5e002502 flags:# type:auth.SentCodeType phone_code_hash:string next_type:flags.1?auth.CodeType timeout:flags.2?int = auth.SentCode;
+
+auth.authorization#cd050916 flags:# tmp_sessions:flags.0?int user:User = auth.Authorization;
+auth.authorizationSignUpRequired#44747e9a flags:# terms_of_service:flags.0?help.TermsOfService = auth.Authorization;
+
+auth.exportedAuthorization#df969c2d id:int bytes:bytes = auth.ExportedAuthorization;
+
+inputNotifyPeer#b8bc5b0c peer:InputPeer = InputNotifyPeer;
+inputNotifyUsers#193b4417 = InputNotifyPeer;
+inputNotifyChats#4a95e84e = InputNotifyPeer;
+inputNotifyBroadcasts#b1db7c7e = InputNotifyPeer;
+
+inputPeerNotifySettings#9c3d198e flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int sound:flags.3?string = InputPeerNotifySettings;
+
+peerNotifySettings#af509d20 flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int sound:flags.3?string = PeerNotifySettings;
+
+peerSettings#818426cd flags:# report_spam:flags.0?true add_contact:flags.1?true block_contact:flags.2?true share_contact:flags.3?true need_contacts_exception:flags.4?true report_geo:flags.5?true = PeerSettings;
+
+wallPaper#a437c3ed id:long flags:# creator:flags.0?true default:flags.1?true pattern:flags.3?true dark:flags.4?true access_hash:long slug:string document:Document settings:flags.2?WallPaperSettings = WallPaper;
+wallPaperNoFile#8af40b25 flags:# default:flags.1?true dark:flags.4?true settings:flags.2?WallPaperSettings = WallPaper;
+
+inputReportReasonSpam#58dbcab8 = ReportReason;
+inputReportReasonViolence#1e22c78d = ReportReason;
+inputReportReasonPornography#2e59d922 = ReportReason;
+inputReportReasonChildAbuse#adf44ee3 = ReportReason;
+inputReportReasonOther#e1746d0a text:string = ReportReason;
+inputReportReasonCopyright#9b89f93a = ReportReason;
+inputReportReasonGeoIrrelevant#dbd4feed = ReportReason;
+
+userFull#edf17c12 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int = UserFull;
+
+contact#f911c994 user_id:int mutual:Bool = Contact;
+
+importedContact#d0028438 user_id:int client_id:long = ImportedContact;
+
+contactBlocked#561bc879 user_id:int date:int = ContactBlocked;
+
+contactStatus#d3680c61 user_id:int status:UserStatus = ContactStatus;
+
+contacts.contactsNotModified#b74ba9d2 = contacts.Contacts;
+contacts.contacts#eae87e42 contacts:Vector<Contact> saved_count:int users:Vector<User> = contacts.Contacts;
+
+contacts.importedContacts#77d01c3b imported:Vector<ImportedContact> popular_invites:Vector<PopularContact> retry_contacts:Vector<long> users:Vector<User> = contacts.ImportedContacts;
+
+contacts.blocked#1c138d15 blocked:Vector<ContactBlocked> users:Vector<User> = contacts.Blocked;
+contacts.blockedSlice#900802a1 count:int blocked:Vector<ContactBlocked> users:Vector<User> = contacts.Blocked;
+
+messages.dialogs#15ba6c40 dialogs:Vector<Dialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Dialogs;
+messages.dialogsSlice#71e094f3 count:int dialogs:Vector<Dialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Dialogs;
+messages.dialogsNotModified#f0e3e596 count:int = messages.Dialogs;
+
+messages.messages#8c718e87 messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
+messages.messagesSlice#c8edce1e flags:# inexact:flags.1?true count:int next_rate:flags.0?int messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
+messages.channelMessages#99262e37 flags:# inexact:flags.1?true pts:int count:int messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
+messages.messagesNotModified#74535f21 count:int = messages.Messages;
+
+messages.chats#64ff9fd5 chats:Vector<Chat> = messages.Chats;
+messages.chatsSlice#9cd81144 count:int chats:Vector<Chat> = messages.Chats;
+
+messages.chatFull#e5d7d19c full_chat:ChatFull chats:Vector<Chat> users:Vector<User> = messages.ChatFull;
+
+messages.affectedHistory#b45c69d1 pts:int pts_count:int offset:int = messages.AffectedHistory;
+
+inputMessagesFilterEmpty#57e2f66c = MessagesFilter;
+inputMessagesFilterPhotos#9609a51c = MessagesFilter;
+inputMessagesFilterVideo#9fc00e65 = MessagesFilter;
+inputMessagesFilterPhotoVideo#56e9f0e4 = MessagesFilter;
+inputMessagesFilterDocument#9eddf188 = MessagesFilter;
+inputMessagesFilterUrl#7ef0dd87 = MessagesFilter;
+inputMessagesFilterGif#ffc86587 = MessagesFilter;
+inputMessagesFilterVoice#50f5c392 = MessagesFilter;
+inputMessagesFilterMusic#3751b49e = MessagesFilter;
+inputMessagesFilterChatPhotos#3a20ecb8 = MessagesFilter;
+inputMessagesFilterPhoneCalls#80c99768 flags:# missed:flags.0?true = MessagesFilter;
+inputMessagesFilterRoundVoice#7a7c17a4 = MessagesFilter;
+inputMessagesFilterRoundVideo#b549da53 = MessagesFilter;
+inputMessagesFilterMyMentions#c1f8e69a = MessagesFilter;
+inputMessagesFilterGeo#e7026d0d = MessagesFilter;
+inputMessagesFilterContacts#e062db83 = MessagesFilter;
+
+updateNewMessage#1f2b0afd message:Message pts:int pts_count:int = Update;
+updateMessageID#4e90bfd6 id:int random_id:long = Update;
+updateDeleteMessages#a20db0e5 messages:Vector<int> pts:int pts_count:int = Update;
+updateUserTyping#5c486927 user_id:int action:SendMessageAction = Update;
+updateChatUserTyping#9a65ea1f chat_id:int user_id:int action:SendMessageAction = Update;
+updateChatParticipants#7761198 participants:ChatParticipants = Update;
+updateUserStatus#1bfbd823 user_id:int status:UserStatus = Update;
+updateUserName#a7332b73 user_id:int first_name:string last_name:string username:string = Update;
+updateUserPhoto#95313b0c user_id:int date:int photo:UserProfilePhoto previous:Bool = Update;
+updateNewEncryptedMessage#12bcbd9a message:EncryptedMessage qts:int = Update;
+updateEncryptedChatTyping#1710f156 chat_id:int = Update;
+updateEncryption#b4a2e88d chat:EncryptedChat date:int = Update;
+updateEncryptedMessagesRead#38fe25b7 chat_id:int max_date:int date:int = Update;
+updateChatParticipantAdd#ea4b0e5c chat_id:int user_id:int inviter_id:int date:int version:int = Update;
+updateChatParticipantDelete#6e5f8c22 chat_id:int user_id:int version:int = Update;
+updateDcOptions#8e5e9873 dc_options:Vector<DcOption> = Update;
+updateUserBlocked#80ece81a user_id:int blocked:Bool = Update;
+updateNotifySettings#bec268ef peer:NotifyPeer notify_settings:PeerNotifySettings = Update;
+updateServiceNotification#ebe46819 flags:# popup:flags.0?true inbox_date:flags.1?int type:string message:string media:MessageMedia entities:Vector<MessageEntity> = Update;
+updatePrivacy#ee3b272a key:PrivacyKey rules:Vector<PrivacyRule> = Update;
+updateUserPhone#12b9417b user_id:int phone:string = Update;
+updateReadHistoryInbox#9c974fdf flags:# folder_id:flags.0?int peer:Peer max_id:int still_unread_count:int pts:int pts_count:int = Update;
+updateReadHistoryOutbox#2f2f21bf peer:Peer max_id:int pts:int pts_count:int = Update;
+updateWebPage#7f891213 webpage:WebPage pts:int pts_count:int = Update;
+updateReadMessagesContents#68c13933 messages:Vector<int> pts:int pts_count:int = Update;
+updateChannelTooLong#eb0467fb flags:# channel_id:int pts:flags.0?int = Update;
+updateChannel#b6d45656 channel_id:int = Update;
+updateNewChannelMessage#62ba04d9 message:Message pts:int pts_count:int = Update;
+updateReadChannelInbox#330b5424 flags:# folder_id:flags.0?int channel_id:int max_id:int still_unread_count:int pts:int = Update;
+updateDeleteChannelMessages#c37521c9 channel_id:int messages:Vector<int> pts:int pts_count:int = Update;
+updateChannelMessageViews#98a12b4b channel_id:int id:int views:int = Update;
+updateChatParticipantAdmin#b6901959 chat_id:int user_id:int is_admin:Bool version:int = Update;
+updateNewStickerSet#688a30aa stickerset:messages.StickerSet = Update;
+updateStickerSetsOrder#bb2d201 flags:# masks:flags.0?true order:Vector<long> = Update;
+updateStickerSets#43ae3dec = Update;
+updateSavedGifs#9375341e = Update;
+updateBotInlineQuery#54826690 flags:# query_id:long user_id:int query:string geo:flags.0?GeoPoint offset:string = Update;
+updateBotInlineSend#e48f964 flags:# user_id:int query:string geo:flags.0?GeoPoint id:string msg_id:flags.1?InputBotInlineMessageID = Update;
+updateEditChannelMessage#1b3f4df7 message:Message pts:int pts_count:int = Update;
+updateChannelPinnedMessage#98592475 channel_id:int id:int = Update;
+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;
+updateEditMessage#e40370a3 message:Message pts:int pts_count:int = Update;
+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;
+updateReadChannelOutbox#25d6c9c7 channel_id:int max_id:int = Update;
+updateDraftMessage#ee2bb969 peer:Peer draft:DraftMessage = Update;
+updateReadFeaturedStickers#571d2742 = Update;
+updateRecentStickers#9a422c20 = Update;
+updateConfig#a229dd06 = Update;
+updatePtsChanged#3354678f = Update;
+updateChannelWebPage#40771900 channel_id:int webpage:WebPage pts:int pts_count:int = Update;
+updateDialogPinned#6e6fe51c flags:# pinned:flags.0?true folder_id:flags.1?int peer:DialogPeer = Update;
+updatePinnedDialogs#fa0f3ca2 flags:# folder_id:flags.1?int order:flags.0?Vector<DialogPeer> = Update;
+updateBotWebhookJSON#8317c0c3 data:DataJSON = Update;
+updateBotWebhookJSONQuery#9b9240a6 query_id:long data:DataJSON timeout:int = Update;
+updateBotShippingQuery#e0cdc940 query_id:long user_id:int payload:bytes shipping_address:PostAddress = Update;
+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;
+updatePhoneCall#ab0f6b1e phone_call:PhoneCall = Update;
+updateLangPackTooLong#46560264 lang_code:string = Update;
+updateLangPack#56022f4d difference:LangPackDifference = Update;
+updateFavedStickers#e511996d = Update;
+updateChannelReadMessagesContents#89893b45 channel_id:int messages:Vector<int> = Update;
+updateContactsReset#7084a7be = Update;
+updateChannelAvailableMessages#70db6837 channel_id:int available_min_id:int = Update;
+updateDialogUnreadMark#e16459c3 flags:# unread:flags.0?true peer:DialogPeer = Update;
+updateUserPinnedMessage#4c43da18 user_id:int id:int = Update;
+updateChatPinnedMessage#e10db349 chat_id:int id:int version:int = Update;
+updateMessagePoll#aca1657b flags:# poll_id:long poll:flags.0?Poll results:PollResults = Update;
+updateChatDefaultBannedRights#54c01850 peer:Peer default_banned_rights:ChatBannedRights version:int = Update;
+updateFolderPeers#19360dc0 folder_peers:Vector<FolderPeer> pts:int pts_count:int = Update;
+updatePeerSettings#6a7e7366 peer:Peer settings:PeerSettings = Update;
+updatePeerLocated#b4afcfb0 peers:Vector<PeerLocated> = Update;
+updateNewScheduledMessage#39a51dfb message:Message = Update;
+updateDeleteScheduledMessages#90866cee peer:Peer messages:Vector<int> = Update;
+updateTheme#8216fba3 theme:Theme = Update;
+updateGeoLiveViewed#871fb939 peer:Peer msg_id:int = Update;
+updateLoginToken#564fe691 = Update;
+updateMessagePollVote#42f88f2c poll_id:long user_id:int options:Vector<bytes> = Update;
+updateDialogFilter#26ffde7d flags:# id:int filter:flags.0?DialogFilter = Update;
+updateDialogFilterOrder#a5d72105 order:Vector<int> = Update;
+updateDialogFilters#3504914f = Update;
+
+updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
+
+updates.differenceEmpty#5d75a138 date:int seq:int = updates.Difference;
+updates.difference#f49ca0 new_messages:Vector<Message> new_encrypted_messages:Vector<EncryptedMessage> other_updates:Vector<Update> chats:Vector<Chat> users:Vector<User> state:updates.State = updates.Difference;
+updates.differenceSlice#a8fb1981 new_messages:Vector<Message> new_encrypted_messages:Vector<EncryptedMessage> other_updates:Vector<Update> chats:Vector<Chat> users:Vector<User> intermediate_state:updates.State = updates.Difference;
+updates.differenceTooLong#4afe8f6d pts:int = updates.Difference;
+
+updatesTooLong#e317af7e = Updates;
+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<MessageEntity> = Updates;
+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<MessageEntity> = Updates;
+updateShort#78d4dec1 update:Update date:int = Updates;
+updatesCombined#725b04c3 updates:Vector<Update> users:Vector<User> chats:Vector<Chat> date:int seq_start:int seq:int = Updates;
+updates#74ae4240 updates:Vector<Update> users:Vector<User> chats:Vector<Chat> date:int seq:int = Updates;
+updateShortSentMessage#11f1331c flags:# out:flags.1?true id:int pts:int pts_count:int date:int media:flags.9?MessageMedia entities:flags.7?Vector<MessageEntity> = Updates;
+
+photos.photos#8dca6aa5 photos:Vector<Photo> users:Vector<User> = photos.Photos;
+photos.photosSlice#15051f54 count:int photos:Vector<Photo> users:Vector<User> = photos.Photos;
+
+photos.photo#20212ca8 photo:Photo users:Vector<User> = photos.Photo;
+
+upload.file#96a18d5 type:storage.FileType mtime:int bytes:bytes = upload.File;
+upload.fileCdnRedirect#f18cda44 dc_id:int file_token:bytes encryption_key:bytes encryption_iv:bytes file_hashes:Vector<FileHash> = upload.File;
+
+dcOption#18b7a10d flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true cdn:flags.3?true static:flags.4?true id:int ip_address:string port:int secret:flags.10?bytes = DcOption;
+
+config#330b4067 flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true blocked_mode:flags.8?true pfs_enabled:flags.13?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> dc_txt_domain_name:string 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 push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int pinned_infolder_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 autoupdate_url_prefix:flags.7?string gif_search_username:flags.9?string venue_search_username:flags.10?string img_search_username:flags.11?string static_maps_provider:flags.12?string caption_length_max:int message_length_max:int webfile_dc_id:int suggested_lang_code:flags.2?string lang_pack_version:flags.2?int base_lang_pack_version:flags.2?int = Config;
+
+nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc;
+
+help.appUpdate#1da7158f flags:# can_not_skip:flags.0?true id:int version:string text:string entities:Vector<MessageEntity> document:flags.1?Document url:flags.2?string = help.AppUpdate;
+help.noAppUpdate#c45a6536 = help.AppUpdate;
+
+help.inviteText#18cb9f78 message:string = help.InviteText;
+
+encryptedChatEmpty#ab7ec0a0 id:int = EncryptedChat;
+encryptedChatWaiting#3bf703dc id:int access_hash:long date:int admin_id:int participant_id:int = EncryptedChat;
+encryptedChatRequested#c878527e id:int access_hash:long date:int admin_id:int participant_id:int g_a:bytes = EncryptedChat;
+encryptedChat#fa56ce36 id:int access_hash:long date:int admin_id:int participant_id:int g_a_or_b:bytes key_fingerprint:long = EncryptedChat;
+encryptedChatDiscarded#13d6dd27 id:int = EncryptedChat;
+
+inputEncryptedChat#f141b5e1 chat_id:int access_hash:long = InputEncryptedChat;
+
+encryptedFileEmpty#c21f497e = EncryptedFile;
+encryptedFile#4a70994c id:long access_hash:long size:int dc_id:int key_fingerprint:int = EncryptedFile;
+
+inputEncryptedFileEmpty#1837c364 = InputEncryptedFile;
+inputEncryptedFileUploaded#64bd0306 id:long parts:int md5_checksum:string key_fingerprint:int = InputEncryptedFile;
+inputEncryptedFile#5a17b5e5 id:long access_hash:long = InputEncryptedFile;
+inputEncryptedFileBigUploaded#2dc173c8 id:long parts:int key_fingerprint:int = InputEncryptedFile;
+
+encryptedMessage#ed18c118 random_id:long chat_id:int date:int bytes:bytes file:EncryptedFile = EncryptedMessage;
+encryptedMessageService#23734b06 random_id:long chat_id:int date:int bytes:bytes = EncryptedMessage;
+
+messages.dhConfigNotModified#c0e24635 random:bytes = messages.DhConfig;
+messages.dhConfig#2c221edd g:int p:bytes version:int random:bytes = messages.DhConfig;
+
+messages.sentEncryptedMessage#560f8935 date:int = messages.SentEncryptedMessage;
+messages.sentEncryptedFile#9493ff32 date:int file:EncryptedFile = messages.SentEncryptedMessage;
+
+inputDocumentEmpty#72f0eaae = InputDocument;
+inputDocument#1abfb575 id:long access_hash:long file_reference:bytes = InputDocument;
+
+documentEmpty#36f8c871 id:long = Document;
+document#9ba29cc1 flags:# id:long access_hash:long file_reference:bytes date:int mime_type:string size:int thumbs:flags.0?Vector<PhotoSize> dc_id:int attributes:Vector<DocumentAttribute> = Document;
+
+help.support#17c6b5f6 phone_number:string user:User = help.Support;
+
+notifyPeer#9fd40bd8 peer:Peer = NotifyPeer;
+notifyUsers#b4c83b4c = NotifyPeer;
+notifyChats#c007cec3 = NotifyPeer;
+notifyBroadcasts#d612e8ef = NotifyPeer;
+
+sendMessageTypingAction#16bf744e = SendMessageAction;
+sendMessageCancelAction#fd5ec8f5 = SendMessageAction;
+sendMessageRecordVideoAction#a187d66f = SendMessageAction;
+sendMessageUploadVideoAction#e9763aec progress:int = SendMessageAction;
+sendMessageRecordAudioAction#d52f73f7 = SendMessageAction;
+sendMessageUploadAudioAction#f351d7ab progress:int = SendMessageAction;
+sendMessageUploadPhotoAction#d1d34a26 progress:int = SendMessageAction;
+sendMessageUploadDocumentAction#aa0cd9e4 progress:int = SendMessageAction;
+sendMessageGeoLocationAction#176f8ba1 = SendMessageAction;
+sendMessageChooseContactAction#628cbc6f = SendMessageAction;
+sendMessageGamePlayAction#dd6a8f48 = SendMessageAction;
+sendMessageRecordRoundAction#88f27fbc = SendMessageAction;
+sendMessageUploadRoundAction#243e1c66 progress:int = SendMessageAction;
+
+contacts.found#b3134d9d my_results:Vector<Peer> results:Vector<Peer> chats:Vector<Chat> users:Vector<User> = contacts.Found;
+
+inputPrivacyKeyStatusTimestamp#4f96cb18 = InputPrivacyKey;
+inputPrivacyKeyChatInvite#bdfb0426 = InputPrivacyKey;
+inputPrivacyKeyPhoneCall#fabadc5f = InputPrivacyKey;
+inputPrivacyKeyPhoneP2P#db9e70d2 = InputPrivacyKey;
+inputPrivacyKeyForwards#a4dd4c08 = InputPrivacyKey;
+inputPrivacyKeyProfilePhoto#5719bacc = InputPrivacyKey;
+inputPrivacyKeyPhoneNumber#352dafa = InputPrivacyKey;
+inputPrivacyKeyAddedByPhone#d1219bdd = InputPrivacyKey;
+
+privacyKeyStatusTimestamp#bc2eab30 = PrivacyKey;
+privacyKeyChatInvite#500e6dfa = PrivacyKey;
+privacyKeyPhoneCall#3d662b7b = PrivacyKey;
+privacyKeyPhoneP2P#39491cc8 = PrivacyKey;
+privacyKeyForwards#69ec56a3 = PrivacyKey;
+privacyKeyProfilePhoto#96151fed = PrivacyKey;
+privacyKeyPhoneNumber#d19ae46d = PrivacyKey;
+privacyKeyAddedByPhone#42ffd42b = PrivacyKey;
+
+inputPrivacyValueAllowContacts#d09e07b = InputPrivacyRule;
+inputPrivacyValueAllowAll#184b35ce = InputPrivacyRule;
+inputPrivacyValueAllowUsers#131cc67f users:Vector<InputUser> = InputPrivacyRule;
+inputPrivacyValueDisallowContacts#ba52007 = InputPrivacyRule;
+inputPrivacyValueDisallowAll#d66b66c9 = InputPrivacyRule;
+inputPrivacyValueDisallowUsers#90110467 users:Vector<InputUser> = InputPrivacyRule;
+inputPrivacyValueAllowChatParticipants#4c81c1ba chats:Vector<int> = InputPrivacyRule;
+inputPrivacyValueDisallowChatParticipants#d82363af chats:Vector<int> = InputPrivacyRule;
+
+privacyValueAllowContacts#fffe1bac = PrivacyRule;
+privacyValueAllowAll#65427b82 = PrivacyRule;
+privacyValueAllowUsers#4d5bbe0c users:Vector<int> = PrivacyRule;
+privacyValueDisallowContacts#f888fa1a = PrivacyRule;
+privacyValueDisallowAll#8b73e763 = PrivacyRule;
+privacyValueDisallowUsers#c7f49b7 users:Vector<int> = PrivacyRule;
+privacyValueAllowChatParticipants#18be796b chats:Vector<int> = PrivacyRule;
+privacyValueDisallowChatParticipants#acae0690 chats:Vector<int> = PrivacyRule;
+
+account.privacyRules#50a04e45 rules:Vector<PrivacyRule> chats:Vector<Chat> users:Vector<User> = account.PrivacyRules;
+
+accountDaysTTL#b8d0afdf days:int = AccountDaysTTL;
+
+documentAttributeImageSize#6c37c15c w:int h:int = DocumentAttribute;
+documentAttributeAnimated#11b58939 = DocumentAttribute;
+documentAttributeSticker#6319d612 flags:# mask:flags.1?true alt:string stickerset:InputStickerSet mask_coords:flags.0?MaskCoords = DocumentAttribute;
+documentAttributeVideo#ef02ce6 flags:# round_message:flags.0?true supports_streaming:flags.1?true duration:int w:int h:int = DocumentAttribute;
+documentAttributeAudio#9852f9c6 flags:# voice:flags.10?true duration:int title:flags.0?string performer:flags.1?string waveform:flags.2?bytes = DocumentAttribute;
+documentAttributeFilename#15590068 file_name:string = DocumentAttribute;
+documentAttributeHasStickers#9801d2f7 = DocumentAttribute;
+
+messages.stickersNotModified#f1749a22 = messages.Stickers;
+messages.stickers#e4599bbd hash:int stickers:Vector<Document> = messages.Stickers;
+
+stickerPack#12b299d4 emoticon:string documents:Vector<long> = StickerPack;
+
+messages.allStickersNotModified#e86602c3 = messages.AllStickers;
+messages.allStickers#edfd405f hash:int sets:Vector<StickerSet> = messages.AllStickers;
+
+messages.affectedMessages#84d19185 pts:int pts_count:int = messages.AffectedMessages;
+
+webPageEmpty#eb1477e8 id:long = WebPage;
+webPagePending#c586da1c id:long date:int = WebPage;
+webPage#e89c45b2 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 attributes:flags.12?Vector<WebPageAttribute> = WebPage;
+webPageNotModified#7311ca11 flags:# cached_page_views:flags.0?int = WebPage;
+
+authorization#ad01d61d flags:# current:flags.0?true official_app:flags.1?true password_pending:flags.2?true hash:long 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;
+
+account.authorizations#1250abde authorizations:Vector<Authorization> = account.Authorizations;
+
+account.password#ad2641f8 flags:# has_recovery:flags.0?true has_secure_values:flags.1?true has_password:flags.2?true current_algo:flags.2?PasswordKdfAlgo srp_B:flags.2?bytes srp_id:flags.2?long hint:flags.3?string email_unconfirmed_pattern:flags.4?string new_algo:PasswordKdfAlgo new_secure_algo:SecurePasswordKdfAlgo secure_random:bytes = account.Password;
+
+account.passwordSettings#9a5c33e5 flags:# email:flags.0?string secure_settings:flags.1?SecureSecretSettings = account.PasswordSettings;
+
+account.passwordInputSettings#c23727c9 flags:# new_algo:flags.0?PasswordKdfAlgo new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string new_secure_settings:flags.2?SecureSecretSettings = account.PasswordInputSettings;
+
+auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery;
+
+receivedNotifyMessage#a384b779 id:int flags:int = ReceivedNotifyMessage;
+
+chatInviteEmpty#69df3769 = ExportedChatInvite;
+chatInviteExported#fc2e05bc link:string = ExportedChatInvite;
+
+chatInviteAlready#5a686d7c chat:Chat = ChatInvite;
+chatInvite#dfc2f58e flags:# channel:flags.0?true broadcast:flags.1?true public:flags.2?true megagroup:flags.3?true title:string photo:Photo participants_count:int participants:flags.4?Vector<User> = ChatInvite;
+
+inputStickerSetEmpty#ffb62b95 = InputStickerSet;
+inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet;
+inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
+inputStickerSetAnimatedEmoji#28703c8 = InputStickerSet;
+inputStickerSetDice#e67f520e emoticon:string = InputStickerSet;
+
+stickerSet#eeb46f27 flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumb:flags.4?PhotoSize thumb_dc_id:flags.4?int count:int hash:int = StickerSet;
+
+messages.stickerSet#b60a24a6 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = messages.StickerSet;
+
+botCommand#c27ac8c7 command:string description:string = BotCommand;
+
+botInfo#98e81d3a user_id:int description:string commands:Vector<BotCommand> = BotInfo;
+
+keyboardButton#a2fa4880 text:string = KeyboardButton;
+keyboardButtonUrl#258aff05 text:string url:string = KeyboardButton;
+keyboardButtonCallback#683a5e46 text:string data:bytes = KeyboardButton;
+keyboardButtonRequestPhone#b16a6c29 text:string = KeyboardButton;
+keyboardButtonRequestGeoLocation#fc796b3f text:string = KeyboardButton;
+keyboardButtonSwitchInline#568a748 flags:# same_peer:flags.0?true text:string query:string = KeyboardButton;
+keyboardButtonGame#50f41ccf text:string = KeyboardButton;
+keyboardButtonBuy#afd93fbb text:string = KeyboardButton;
+keyboardButtonUrlAuth#10b78d29 flags:# text:string fwd_text:flags.0?string url:string button_id:int = KeyboardButton;
+inputKeyboardButtonUrlAuth#d02e7fd4 flags:# request_write_access:flags.0?true text:string fwd_text:flags.1?string url:string bot:InputUser = KeyboardButton;
+keyboardButtonRequestPoll#bbc7515d flags:# quiz:flags.0?Bool text:string = KeyboardButton;
+
+keyboardButtonRow#77608b83 buttons:Vector<KeyboardButton> = KeyboardButtonRow;
+
+replyKeyboardHide#a03e5b85 flags:# selective:flags.2?true = ReplyMarkup;
+replyKeyboardForceReply#f4108aa0 flags:# single_use:flags.1?true selective:flags.2?true = ReplyMarkup;
+replyKeyboardMarkup#3502758c flags:# resize:flags.0?true single_use:flags.1?true selective:flags.2?true rows:Vector<KeyboardButtonRow> = ReplyMarkup;
+replyInlineMarkup#48a30254 rows:Vector<KeyboardButtonRow> = ReplyMarkup;
+
+messageEntityUnknown#bb92ba95 offset:int length:int = MessageEntity;
+messageEntityMention#fa04579d offset:int length:int = MessageEntity;
+messageEntityHashtag#6f635b0d offset:int length:int = MessageEntity;
+messageEntityBotCommand#6cef8ac7 offset:int length:int = MessageEntity;
+messageEntityUrl#6ed02538 offset:int length:int = MessageEntity;
+messageEntityEmail#64e475c2 offset:int length:int = MessageEntity;
+messageEntityBold#bd610bc9 offset:int length:int = MessageEntity;
+messageEntityItalic#826f8b60 offset:int length:int = MessageEntity;
+messageEntityCode#28a20571 offset:int length:int = MessageEntity;
+messageEntityPre#73924be0 offset:int length:int language:string = MessageEntity;
+messageEntityTextUrl#76a6d327 offset:int length:int url:string = MessageEntity;
+messageEntityMentionName#352dca58 offset:int length:int user_id:int = MessageEntity;
+inputMessageEntityMentionName#208e68c9 offset:int length:int user_id:InputUser = MessageEntity;
+messageEntityPhone#9b69e34b offset:int length:int = MessageEntity;
+messageEntityCashtag#4c4e743f offset:int length:int = MessageEntity;
+messageEntityUnderline#9c4e7e8b offset:int length:int = MessageEntity;
+messageEntityStrike#bf0693d4 offset:int length:int = MessageEntity;
+messageEntityBlockquote#20df5d0 offset:int length:int = MessageEntity;
+messageEntityBankCard#761e6af4 offset:int length:int = MessageEntity;
+
+inputChannelEmpty#ee8c1e86 = InputChannel;
+inputChannel#afeb712e channel_id:int access_hash:long = InputChannel;
+inputChannelFromMessage#2a286531 peer:InputPeer msg_id:int channel_id:int = InputChannel;
+
+contacts.resolvedPeer#7f077ad9 peer:Peer chats:Vector<Chat> users:Vector<User> = contacts.ResolvedPeer;
+
+messageRange#ae30253 min_id:int max_id:int = MessageRange;
+
+updates.channelDifferenceEmpty#3e11affb flags:# final:flags.0?true pts:int timeout:flags.1?int = updates.ChannelDifference;
+updates.channelDifferenceTooLong#a4bcc6fe flags:# final:flags.0?true timeout:flags.1?int dialog:Dialog messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = updates.ChannelDifference;
+updates.channelDifference#2064674e flags:# final:flags.0?true pts:int timeout:flags.1?int new_messages:Vector<Message> other_updates:Vector<Update> chats:Vector<Chat> users:Vector<User> = updates.ChannelDifference;
+
+channelMessagesFilterEmpty#94d42ee7 = ChannelMessagesFilter;
+channelMessagesFilter#cd77d957 flags:# exclude_new_messages:flags.1?true ranges:Vector<MessageRange> = ChannelMessagesFilter;
+
+channelParticipant#15ebac1d user_id:int date:int = ChannelParticipant;
+channelParticipantSelf#a3289a6d user_id:int inviter_id:int date:int = ChannelParticipant;
+channelParticipantCreator#808d15a4 flags:# user_id:int rank:flags.0?string = ChannelParticipant;
+channelParticipantAdmin#ccbebbaf flags:# can_edit:flags.0?true self:flags.1?true user_id:int inviter_id:flags.1?int promoted_by:int date:int admin_rights:ChatAdminRights rank:flags.2?string = ChannelParticipant;
+channelParticipantBanned#1c0facaf flags:# left:flags.0?true user_id:int kicked_by:int date:int banned_rights:ChatBannedRights = ChannelParticipant;
+
+channelParticipantsRecent#de3f3c79 = ChannelParticipantsFilter;
+channelParticipantsAdmins#b4608969 = ChannelParticipantsFilter;
+channelParticipantsKicked#a3b54985 q:string = ChannelParticipantsFilter;
+channelParticipantsBots#b0d1865b = ChannelParticipantsFilter;
+channelParticipantsBanned#1427a5e1 q:string = ChannelParticipantsFilter;
+channelParticipantsSearch#656ac4b q:string = ChannelParticipantsFilter;
+channelParticipantsContacts#bb6ae88d q:string = ChannelParticipantsFilter;
+
+channels.channelParticipants#f56ee2a8 count:int participants:Vector<ChannelParticipant> users:Vector<User> = channels.ChannelParticipants;
+channels.channelParticipantsNotModified#f0173fe9 = channels.ChannelParticipants;
+
+channels.channelParticipant#d0d9b163 participant:ChannelParticipant users:Vector<User> = channels.ChannelParticipant;
+
+help.termsOfService#780a0310 flags:# popup:flags.0?true id:DataJSON text:string entities:Vector<MessageEntity> min_age_confirm:flags.1?int = help.TermsOfService;
+
+foundGif#162ecc1f url:string thumb_url:string content_url:string content_type:string w:int h:int = FoundGif;
+foundGifCached#9c750409 url:string photo:Photo document:Document = FoundGif;
+
+messages.foundGifs#450a1c0a next_offset:int results:Vector<FoundGif> = messages.FoundGifs;
+
+messages.savedGifsNotModified#e8025ca2 = messages.SavedGifs;
+messages.savedGifs#2e0709a5 hash:int gifs:Vector<Document> = messages.SavedGifs;
+
+inputBotInlineMessageMediaAuto#3380c786 flags:# message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
+inputBotInlineMessageText#3dcd7a87 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
+inputBotInlineMessageMediaGeo#c1b15d65 flags:# geo_point:InputGeoPoint period:int reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
+inputBotInlineMessageMediaVenue#417bbf11 flags:# geo_point:InputGeoPoint title:string address:string provider:string venue_id:string venue_type:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
+inputBotInlineMessageMediaContact#a6edbffd flags:# phone_number:string first_name:string last_name:string vcard:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
+inputBotInlineMessageGame#4b425864 flags:# reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
+
+inputBotInlineResult#88bf9319 flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?InputWebDocument content:flags.5?InputWebDocument send_message:InputBotInlineMessage = InputBotInlineResult;
+inputBotInlineResultPhoto#a8d864a7 id:string type:string photo:InputPhoto send_message:InputBotInlineMessage = InputBotInlineResult;
+inputBotInlineResultDocument#fff8fdc4 flags:# id:string type:string title:flags.1?string description:flags.2?string document:InputDocument send_message:InputBotInlineMessage = InputBotInlineResult;
+inputBotInlineResultGame#4fa417f2 id:string short_name:string send_message:InputBotInlineMessage = InputBotInlineResult;
+
+botInlineMessageMediaAuto#764cf810 flags:# message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
+botInlineMessageText#8c7f65e2 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
+botInlineMessageMediaGeo#b722de65 flags:# geo:GeoPoint period:int reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
+botInlineMessageMediaVenue#8a86659c flags:# geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
+botInlineMessageMediaContact#18d1cdc2 flags:# phone_number:string first_name:string last_name:string vcard:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
+
+botInlineResult#11965f3a flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?WebDocument content:flags.5?WebDocument send_message:BotInlineMessage = BotInlineResult;
+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;
+
+messages.botResults#947ca848 flags:# gallery:flags.0?true query_id:long next_offset:flags.1?string switch_pm:flags.2?InlineBotSwitchPM results:Vector<BotInlineResult> cache_time:int users:Vector<User> = messages.BotResults;
+
+exportedMessageLink#5dab1af4 link:string html:string = ExportedMessageLink;
+
+messageFwdHeader#ec338270 flags:# from_id:flags.0?int from_name:flags.5?string date:int channel_id:flags.1?int channel_post:flags.2?int post_author:flags.3?string saved_from_peer:flags.4?Peer saved_from_msg_id:flags.4?int = MessageFwdHeader;
+
+auth.codeTypeSms#72a3158c = auth.CodeType;
+auth.codeTypeCall#741cd3e3 = auth.CodeType;
+auth.codeTypeFlashCall#226ccefb = auth.CodeType;
+
+auth.sentCodeTypeApp#3dbb5986 length:int = auth.SentCodeType;
+auth.sentCodeTypeSms#c000bba2 length:int = auth.SentCodeType;
+auth.sentCodeTypeCall#5353e5a7 length:int = auth.SentCodeType;
+auth.sentCodeTypeFlashCall#ab03c6d9 pattern:string = auth.SentCodeType;
+
+messages.botCallbackAnswer#36585ea4 flags:# alert:flags.1?true has_url:flags.3?true native_ui:flags.4?true message:flags.0?string url:flags.2?string cache_time:int = messages.BotCallbackAnswer;
+
+messages.messageEditData#26b5dde6 flags:# caption:flags.0?true = messages.MessageEditData;
+
+inputBotInlineMessageID#890c3d89 dc_id:int id:long access_hash:long = InputBotInlineMessageID;
+
+inlineBotSwitchPM#3c20629f text:string start_param:string = InlineBotSwitchPM;
+
+messages.peerDialogs#3371c354 dialogs:Vector<Dialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> state:updates.State = messages.PeerDialogs;
+
+topPeer#edcdc05b peer:Peer rating:double = TopPeer;
+
+topPeerCategoryBotsPM#ab661b5b = TopPeerCategory;
+topPeerCategoryBotsInline#148677e2 = TopPeerCategory;
+topPeerCategoryCorrespondents#637b7ed = TopPeerCategory;
+topPeerCategoryGroups#bd17a14a = TopPeerCategory;
+topPeerCategoryChannels#161d9628 = TopPeerCategory;
+topPeerCategoryPhoneCalls#1e76a78c = TopPeerCategory;
+topPeerCategoryForwardUsers#a8406ca9 = TopPeerCategory;
+topPeerCategoryForwardChats#fbeec0f0 = TopPeerCategory;
+
+topPeerCategoryPeers#fb834291 category:TopPeerCategory count:int peers:Vector<TopPeer> = TopPeerCategoryPeers;
+
+contacts.topPeersNotModified#de266ef5 = contacts.TopPeers;
+contacts.topPeers#70b772a8 categories:Vector<TopPeerCategoryPeers> chats:Vector<Chat> users:Vector<User> = contacts.TopPeers;
+contacts.topPeersDisabled#b52c939d = contacts.TopPeers;
+
+draftMessageEmpty#1b0c841a flags:# date:flags.0?int = DraftMessage;
+draftMessage#fd8e711f flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int message:string entities:flags.3?Vector<MessageEntity> date:int = DraftMessage;
+
+messages.featuredStickersNotModified#c6dc0c66 count:int = messages.FeaturedStickers;
+messages.featuredStickers#b6abc341 hash:int count:int sets:Vector<StickerSetCovered> unread:Vector<long> = messages.FeaturedStickers;
+
+messages.recentStickersNotModified#b17f890 = messages.RecentStickers;
+messages.recentStickers#22f3afb3 hash:int packs:Vector<StickerPack> stickers:Vector<Document> dates:Vector<int> = messages.RecentStickers;
+
+messages.archivedStickers#4fcba9c8 count:int sets:Vector<StickerSetCovered> = messages.ArchivedStickers;
+
+messages.stickerSetInstallResultSuccess#38641628 = messages.StickerSetInstallResult;
+messages.stickerSetInstallResultArchive#35e410a8 sets:Vector<StickerSetCovered> = messages.StickerSetInstallResult;
+
+stickerSetCovered#6410a5d2 set:StickerSet cover:Document = StickerSetCovered;
+stickerSetMultiCovered#3407e51b set:StickerSet covers:Vector<Document> = StickerSetCovered;
+
+maskCoords#aed6dbb2 n:int x:double y:double zoom:double = MaskCoords;
+
+inputStickeredMediaPhoto#4a992157 id:InputPhoto = InputStickeredMedia;
+inputStickeredMediaDocument#438865b id:InputDocument = InputStickeredMedia;
+
+game#bdf9653b flags:# id:long access_hash:long short_name:string title:string description:string photo:Photo document:flags.0?Document = Game;
+
+inputGameID#32c3e77 id:long access_hash:long = InputGame;
+inputGameShortName#c331e80a bot_id:InputUser short_name:string = InputGame;
+
+highScore#58fffcd0 pos:int user_id:int score:int = HighScore;
+
+messages.highScores#9a3bfd99 scores:Vector<HighScore> users:Vector<User> = messages.HighScores;
+
+textEmpty#dc3d824f = RichText;
+textPlain#744694e0 text:string = RichText;
+textBold#6724abc4 text:RichText = RichText;
+textItalic#d912a59c text:RichText = RichText;
+textUnderline#c12622c4 text:RichText = RichText;
+textStrike#9bf8bb95 text:RichText = RichText;
+textFixed#6c3f19b9 text:RichText = RichText;
+textUrl#3c2884c1 text:RichText url:string webpage_id:long = RichText;
+textEmail#de5a0dd6 text:RichText email:string = RichText;
+textConcat#7e6260d7 texts:Vector<RichText> = RichText;
+textSubscript#ed6a8504 text:RichText = RichText;
+textSuperscript#c7fb5e01 text:RichText = RichText;
+textMarked#34b8621 text:RichText = RichText;
+textPhone#1ccb966a text:RichText phone:string = RichText;
+textImage#81ccf4f document_id:long w:int h:int = RichText;
+textAnchor#35553762 text:RichText name:string = RichText;
+
+pageBlockUnsupported#13567e8a = PageBlock;
+pageBlockTitle#70abc3fd text:RichText = PageBlock;
+pageBlockSubtitle#8ffa9a1f text:RichText = PageBlock;
+pageBlockAuthorDate#baafe5e0 author:RichText published_date:int = PageBlock;
+pageBlockHeader#bfd064ec text:RichText = PageBlock;
+pageBlockSubheader#f12bb6e1 text:RichText = PageBlock;
+pageBlockParagraph#467a0766 text:RichText = PageBlock;
+pageBlockPreformatted#c070d93e text:RichText language:string = PageBlock;
+pageBlockFooter#48870999 text:RichText = PageBlock;
+pageBlockDivider#db20b188 = PageBlock;
+pageBlockAnchor#ce0d37b0 name:string = PageBlock;
+pageBlockList#e4e88011 items:Vector<PageListItem> = PageBlock;
+pageBlockBlockquote#263d7c26 text:RichText caption:RichText = PageBlock;
+pageBlockPullquote#4f4456d3 text:RichText caption:RichText = PageBlock;
+pageBlockPhoto#1759c560 flags:# photo_id:long caption:PageCaption url:flags.0?string webpage_id:flags.0?long = PageBlock;
+pageBlockVideo#7c8fe7b6 flags:# autoplay:flags.0?true loop:flags.1?true video_id:long caption:PageCaption = PageBlock;
+pageBlockCover#39f23300 cover:PageBlock = PageBlock;
+pageBlockEmbed#a8718dc5 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:flags.5?int h:flags.5?int caption:PageCaption = PageBlock;
+pageBlockEmbedPost#f259a80b url:string webpage_id:long author_photo_id:long author:string date:int blocks:Vector<PageBlock> caption:PageCaption = PageBlock;
+pageBlockCollage#65a0fa4d items:Vector<PageBlock> caption:PageCaption = PageBlock;
+pageBlockSlideshow#31f9590 items:Vector<PageBlock> caption:PageCaption = PageBlock;
+pageBlockChannel#ef1751b5 channel:Chat = PageBlock;
+pageBlockAudio#804361ea audio_id:long caption:PageCaption = PageBlock;
+pageBlockKicker#1e148390 text:RichText = PageBlock;
+pageBlockTable#bf4dea82 flags:# bordered:flags.0?true striped:flags.1?true title:RichText rows:Vector<PageTableRow> = PageBlock;
+pageBlockOrderedList#9a8ae1e1 items:Vector<PageListOrderedItem> = PageBlock;
+pageBlockDetails#76768bed flags:# open:flags.0?true blocks:Vector<PageBlock> title:RichText = PageBlock;
+pageBlockRelatedArticles#16115a96 title:RichText articles:Vector<PageRelatedArticle> = PageBlock;
+pageBlockMap#a44f3ef6 geo:GeoPoint zoom:int w:int h:int caption:PageCaption = PageBlock;
+
+phoneCallDiscardReasonMissed#85e42301 = PhoneCallDiscardReason;
+phoneCallDiscardReasonDisconnect#e095c1a0 = PhoneCallDiscardReason;
+phoneCallDiscardReasonHangup#57adc690 = PhoneCallDiscardReason;
+phoneCallDiscardReasonBusy#faf7e8c9 = PhoneCallDiscardReason;
+
+dataJSON#7d748d04 data:string = DataJSON;
+
+labeledPrice#cb296bf8 label:string amount:long = LabeledPrice;
+
+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 phone_to_provider:flags.6?true email_to_provider:flags.7?true currency:string prices:Vector<LabeledPrice> = Invoice;
+
+paymentCharge#ea02c27e id:string provider_charge_id:string = PaymentCharge;
+
+postAddress#1e8caaeb street_line1:string street_line2:string city:string state:string country_iso2:string post_code:string = PostAddress;
+
+paymentRequestedInfo#909c3f94 flags:# name:flags.0?string phone:flags.1?string email:flags.2?string shipping_address:flags.3?PostAddress = PaymentRequestedInfo;
+
+paymentSavedCredentialsCard#cdc27a1f id:string title:string = PaymentSavedCredentials;
+
+webDocument#1c570ed1 url:string access_hash:long size:int mime_type:string attributes:Vector<DocumentAttribute> = WebDocument;
+webDocumentNoProxy#f9c8bcc6 url:string size:int mime_type:string attributes:Vector<DocumentAttribute> = WebDocument;
+
+inputWebDocument#9bed434d url:string size:int mime_type:string attributes:Vector<DocumentAttribute> = InputWebDocument;
+
+inputWebFileLocation#c239d686 url:string access_hash:long = InputWebFileLocation;
+inputWebFileGeoPointLocation#9f2221c9 geo_point:InputGeoPoint access_hash:long w:int h:int zoom:int scale:int = InputWebFileLocation;
+
+upload.webFile#21e753bc size:int mime_type:string file_type:storage.FileType mtime:int bytes:bytes = upload.WebFile;
+
+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<User> = payments.PaymentForm;
+
+payments.validatedRequestedInfo#d1451883 flags:# id:flags.0?string shipping_options:flags.1?Vector<ShippingOption> = payments.ValidatedRequestedInfo;
+
+payments.paymentResult#4e5f810d updates:Updates = payments.PaymentResult;
+payments.paymentVerificationNeeded#d8411139 url:string = payments.PaymentResult;
+
+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<User> = payments.PaymentReceipt;
+
+payments.savedInfo#fb8fe43c flags:# has_saved_credentials:flags.1?true saved_info:flags.0?PaymentRequestedInfo = payments.SavedInfo;
+
+inputPaymentCredentialsSaved#c10eb2cf id:string tmp_password:bytes = InputPaymentCredentials;
+inputPaymentCredentials#3417d728 flags:# save:flags.0?true data:DataJSON = InputPaymentCredentials;
+inputPaymentCredentialsApplePay#aa1c39f payment_data:DataJSON = InputPaymentCredentials;
+inputPaymentCredentialsAndroidPay#ca05d50e payment_token:DataJSON google_transaction_id:string = InputPaymentCredentials;
+
+account.tmpPassword#db64fd34 tmp_password:bytes valid_until:int = account.TmpPassword;
+
+shippingOption#b6213cdf id:string title:string prices:Vector<LabeledPrice> = ShippingOption;
+
+inputStickerSetItem#ffa0a496 flags:# document:InputDocument emoji:string mask_coords:flags.0?MaskCoords = InputStickerSetItem;
+
+inputPhoneCall#1e36fded id:long access_hash:long = InputPhoneCall;
+
+phoneCallEmpty#5366c915 id:long = PhoneCall;
+phoneCallWaiting#1b8f4ad1 flags:# video:flags.5?true id:long access_hash:long date:int admin_id:int participant_id:int protocol:PhoneCallProtocol receive_date:flags.0?int = PhoneCall;
+phoneCallRequested#87eabb53 flags:# video:flags.5?true id:long access_hash:long date:int admin_id:int participant_id:int g_a_hash:bytes protocol:PhoneCallProtocol = PhoneCall;
+phoneCallAccepted#997c454a flags:# video:flags.5?true id:long access_hash:long date:int admin_id:int participant_id:int g_b:bytes protocol:PhoneCallProtocol = PhoneCall;
+phoneCall#8742ae7f flags:# p2p_allowed:flags.5?true id:long access_hash:long date:int admin_id:int participant_id:int g_a_or_b:bytes key_fingerprint:long protocol:PhoneCallProtocol connections:Vector<PhoneConnection> start_date:int = PhoneCall;
+phoneCallDiscarded#50ca4de1 flags:# need_rating:flags.2?true need_debug:flags.3?true video:flags.5?true id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = PhoneCall;
+
+phoneConnection#9d4c17c0 id:long ip:string ipv6:string port:int peer_tag:bytes = PhoneConnection;
+
+phoneCallProtocol#fc878fc8 flags:# udp_p2p:flags.0?true udp_reflector:flags.1?true min_layer:int max_layer:int library_versions:Vector<string> = PhoneCallProtocol;
+
+phone.phoneCall#ec82e140 phone_call:PhoneCall users:Vector<User> = phone.PhoneCall;
+
+upload.cdnFileReuploadNeeded#eea8e46e request_token:bytes = upload.CdnFile;
+upload.cdnFile#a99fca4f bytes:bytes = upload.CdnFile;
+
+cdnPublicKey#c982eaba dc_id:int public_key:string = CdnPublicKey;
+
+cdnConfig#5725e40a public_keys:Vector<CdnPublicKey> = CdnConfig;
+
+langPackString#cad181f6 key:string value:string = LangPackString;
+langPackStringPluralized#6c47ac9f flags:# key:string zero_value:flags.0?string one_value:flags.1?string two_value:flags.2?string few_value:flags.3?string many_value:flags.4?string other_value:string = LangPackString;
+langPackStringDeleted#2979eeb2 key:string = LangPackString;
+
+langPackDifference#f385c1f6 lang_code:string from_version:int version:int strings:Vector<LangPackString> = LangPackDifference;
+
+langPackLanguage#eeca5ce3 flags:# official:flags.0?true rtl:flags.2?true beta:flags.3?true name:string native_name:string lang_code:string base_lang_code:flags.1?string plural_code:string strings_count:int translated_count:int translations_url:string = LangPackLanguage;
+
+channelAdminLogEventActionChangeTitle#e6dfb825 prev_value:string new_value:string = ChannelAdminLogEventAction;
+channelAdminLogEventActionChangeAbout#55188a2e prev_value:string new_value:string = ChannelAdminLogEventAction;
+channelAdminLogEventActionChangeUsername#6a4afc38 prev_value:string new_value:string = ChannelAdminLogEventAction;
+channelAdminLogEventActionChangePhoto#434bd2af prev_photo:Photo new_photo:Photo = ChannelAdminLogEventAction;
+channelAdminLogEventActionToggleInvites#1b7907ae new_value:Bool = ChannelAdminLogEventAction;
+channelAdminLogEventActionToggleSignatures#26ae0971 new_value:Bool = ChannelAdminLogEventAction;
+channelAdminLogEventActionUpdatePinned#e9e82c18 message:Message = ChannelAdminLogEventAction;
+channelAdminLogEventActionEditMessage#709b2405 prev_message:Message new_message:Message = ChannelAdminLogEventAction;
+channelAdminLogEventActionDeleteMessage#42e047bb message:Message = ChannelAdminLogEventAction;
+channelAdminLogEventActionParticipantJoin#183040d3 = ChannelAdminLogEventAction;
+channelAdminLogEventActionParticipantLeave#f89777f2 = ChannelAdminLogEventAction;
+channelAdminLogEventActionParticipantInvite#e31c34d8 participant:ChannelParticipant = ChannelAdminLogEventAction;
+channelAdminLogEventActionParticipantToggleBan#e6d83d7e prev_participant:ChannelParticipant new_participant:ChannelParticipant = ChannelAdminLogEventAction;
+channelAdminLogEventActionParticipantToggleAdmin#d5676710 prev_participant:ChannelParticipant new_participant:ChannelParticipant = ChannelAdminLogEventAction;
+channelAdminLogEventActionChangeStickerSet#b1c3caa7 prev_stickerset:InputStickerSet new_stickerset:InputStickerSet = ChannelAdminLogEventAction;
+channelAdminLogEventActionTogglePreHistoryHidden#5f5c95f1 new_value:Bool = ChannelAdminLogEventAction;
+channelAdminLogEventActionDefaultBannedRights#2df5fc0a prev_banned_rights:ChatBannedRights new_banned_rights:ChatBannedRights = ChannelAdminLogEventAction;
+channelAdminLogEventActionStopPoll#8f079643 message:Message = ChannelAdminLogEventAction;
+channelAdminLogEventActionChangeLinkedChat#a26f881b prev_value:int new_value:int = ChannelAdminLogEventAction;
+channelAdminLogEventActionChangeLocation#e6b76ae prev_value:ChannelLocation new_value:ChannelLocation = ChannelAdminLogEventAction;
+channelAdminLogEventActionToggleSlowMode#53909779 prev_value:int new_value:int = ChannelAdminLogEventAction;
+
+channelAdminLogEvent#3b5a3e40 id:long date:int user_id:int action:ChannelAdminLogEventAction = ChannelAdminLogEvent;
+
+channels.adminLogResults#ed8af74d events:Vector<ChannelAdminLogEvent> chats:Vector<Chat> users:Vector<User> = channels.AdminLogResults;
+
+channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true = ChannelAdminLogEventsFilter;
+
+popularContact#5ce14175 client_id:long importers:int = PopularContact;
+
+messages.favedStickersNotModified#9e8fa6d3 = messages.FavedStickers;
+messages.favedStickers#f37f2f16 hash:int packs:Vector<StickerPack> stickers:Vector<Document> = messages.FavedStickers;
+
+recentMeUrlUnknown#46e1d13d url:string = RecentMeUrl;
+recentMeUrlUser#8dbc3336 url:string user_id:int = RecentMeUrl;
+recentMeUrlChat#a01b22f9 url:string chat_id:int = RecentMeUrl;
+recentMeUrlChatInvite#eb49081d url:string chat_invite:ChatInvite = RecentMeUrl;
+recentMeUrlStickerSet#bc0a57dc url:string set:StickerSetCovered = RecentMeUrl;
+
+help.recentMeUrls#e0310d7 urls:Vector<RecentMeUrl> chats:Vector<Chat> users:Vector<User> = help.RecentMeUrls;
+
+inputSingleMedia#1cc6e91f flags:# media:InputMedia random_id:long message:string entities:flags.0?Vector<MessageEntity> = InputSingleMedia;
+
+webAuthorization#cac943f2 hash:long bot_id:int domain:string browser:string platform:string date_created:int date_active:int ip:string region:string = WebAuthorization;
+
+account.webAuthorizations#ed56c9fc authorizations:Vector<WebAuthorization> users:Vector<User> = account.WebAuthorizations;
+
+inputMessageID#a676a322 id:int = InputMessage;
+inputMessageReplyTo#bad88395 id:int = InputMessage;
+inputMessagePinned#86872538 = InputMessage;
+
+inputDialogPeer#fcaafeb7 peer:InputPeer = InputDialogPeer;
+inputDialogPeerFolder#64600527 folder_id:int = InputDialogPeer;
+
+dialogPeer#e56dbf05 peer:Peer = DialogPeer;
+dialogPeerFolder#514519e2 folder_id:int = DialogPeer;
+
+messages.foundStickerSetsNotModified#d54b65d = messages.FoundStickerSets;
+messages.foundStickerSets#5108d648 hash:int sets:Vector<StickerSetCovered> = messages.FoundStickerSets;
+
+fileHash#6242c773 offset:int limit:int hash:bytes = FileHash;
+
+inputClientProxy#75588b3f address:string port:int = InputClientProxy;
+
+help.proxyDataEmpty#e09e1fb8 expires:int = help.ProxyData;
+help.proxyDataPromo#2bf7ee23 expires:int peer:Peer chats:Vector<Chat> users:Vector<User> = help.ProxyData;
+
+help.termsOfServiceUpdateEmpty#e3309f7f expires:int = help.TermsOfServiceUpdate;
+help.termsOfServiceUpdate#28ecf961 expires:int terms_of_service:help.TermsOfService = help.TermsOfServiceUpdate;
+
+inputSecureFileUploaded#3334b0f0 id:long parts:int md5_checksum:string file_hash:bytes secret:bytes = InputSecureFile;
+inputSecureFile#5367e5be id:long access_hash:long = InputSecureFile;
+
+secureFileEmpty#64199744 = SecureFile;
+secureFile#e0277a62 id:long access_hash:long size:int dc_id:int date:int file_hash:bytes secret:bytes = SecureFile;
+
+secureData#8aeabec3 data:bytes data_hash:bytes secret:bytes = SecureData;
+
+securePlainPhone#7d6099dd phone:string = SecurePlainData;
+securePlainEmail#21ec5a5f email:string = SecurePlainData;
+
+secureValueTypePersonalDetails#9d2a81e3 = SecureValueType;
+secureValueTypePassport#3dac6a00 = SecureValueType;
+secureValueTypeDriverLicense#6e425c4 = SecureValueType;
+secureValueTypeIdentityCard#a0d0744b = SecureValueType;
+secureValueTypeInternalPassport#99a48f23 = SecureValueType;
+secureValueTypeAddress#cbe31e26 = SecureValueType;
+secureValueTypeUtilityBill#fc36954e = SecureValueType;
+secureValueTypeBankStatement#89137c0d = SecureValueType;
+secureValueTypeRentalAgreement#8b883488 = SecureValueType;
+secureValueTypePassportRegistration#99e3806a = SecureValueType;
+secureValueTypeTemporaryRegistration#ea02ec33 = SecureValueType;
+secureValueTypePhone#b320aadb = SecureValueType;
+secureValueTypeEmail#8e3ca7ee = SecureValueType;
+
+secureValue#187fa0ca flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?SecureFile reverse_side:flags.2?SecureFile selfie:flags.3?SecureFile translation:flags.6?Vector<SecureFile> files:flags.4?Vector<SecureFile> plain_data:flags.5?SecurePlainData hash:bytes = SecureValue;
+
+inputSecureValue#db21d0a7 flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?InputSecureFile reverse_side:flags.2?InputSecureFile selfie:flags.3?InputSecureFile translation:flags.6?Vector<InputSecureFile> files:flags.4?Vector<InputSecureFile> plain_data:flags.5?SecurePlainData = InputSecureValue;
+
+secureValueHash#ed1ecdb0 type:SecureValueType hash:bytes = SecureValueHash;
+
+secureValueErrorData#e8a40bd9 type:SecureValueType data_hash:bytes field:string text:string = SecureValueError;
+secureValueErrorFrontSide#be3dfa type:SecureValueType file_hash:bytes text:string = SecureValueError;
+secureValueErrorReverseSide#868a2aa5 type:SecureValueType file_hash:bytes text:string = SecureValueError;
+secureValueErrorSelfie#e537ced6 type:SecureValueType file_hash:bytes text:string = SecureValueError;
+secureValueErrorFile#7a700873 type:SecureValueType file_hash:bytes text:string = SecureValueError;
+secureValueErrorFiles#666220e9 type:SecureValueType file_hash:Vector<bytes> text:string = SecureValueError;
+secureValueError#869d758f type:SecureValueType hash:bytes text:string = SecureValueError;
+secureValueErrorTranslationFile#a1144770 type:SecureValueType file_hash:bytes text:string = SecureValueError;
+secureValueErrorTranslationFiles#34636dd8 type:SecureValueType file_hash:Vector<bytes> text:string = SecureValueError;
+
+secureCredentialsEncrypted#33f0ea47 data:bytes hash:bytes secret:bytes = SecureCredentialsEncrypted;
+
+account.authorizationForm#ad2e1cd8 flags:# required_types:Vector<SecureRequiredType> values:Vector<SecureValue> errors:Vector<SecureValueError> users:Vector<User> privacy_policy_url:flags.0?string = account.AuthorizationForm;
+
+account.sentEmailCode#811f854f email_pattern:string length:int = account.SentEmailCode;
+
+help.deepLinkInfoEmpty#66afa166 = help.DeepLinkInfo;
+help.deepLinkInfo#6a4ee832 flags:# update_app:flags.0?true message:string entities:flags.1?Vector<MessageEntity> = help.DeepLinkInfo;
+
+savedPhoneContact#1142bd56 phone:string first_name:string last_name:string date:int = SavedContact;
+
+account.takeout#4dba4501 id:long = account.Takeout;
+
+passwordKdfAlgoUnknown#d45ab096 = PasswordKdfAlgo;
+passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow#3a912d4a salt1:bytes salt2:bytes g:int p:bytes = PasswordKdfAlgo;
+
+securePasswordKdfAlgoUnknown#4a8537 = SecurePasswordKdfAlgo;
+securePasswordKdfAlgoPBKDF2HMACSHA512iter100000#bbf2dda0 salt:bytes = SecurePasswordKdfAlgo;
+securePasswordKdfAlgoSHA512#86471d92 salt:bytes = SecurePasswordKdfAlgo;
+
+secureSecretSettings#1527bcac secure_algo:SecurePasswordKdfAlgo secure_secret:bytes secure_secret_id:long = SecureSecretSettings;
+
+inputCheckPasswordEmpty#9880f658 = InputCheckPasswordSRP;
+inputCheckPasswordSRP#d27ff082 srp_id:long A:bytes M1:bytes = InputCheckPasswordSRP;
+
+secureRequiredType#829d99da flags:# native_names:flags.0?true selfie_required:flags.1?true translation_required:flags.2?true type:SecureValueType = SecureRequiredType;
+secureRequiredTypeOneOf#27477b4 types:Vector<SecureRequiredType> = SecureRequiredType;
+
+help.passportConfigNotModified#bfb9f457 = help.PassportConfig;
+help.passportConfig#a098d6af hash:int countries_langs:DataJSON = help.PassportConfig;
+
+inputAppEvent#1d1b1245 time:double type:string peer:long data:JSONValue = InputAppEvent;
+
+jsonObjectValue#c0de1bd9 key:string value:JSONValue = JSONObjectValue;
+
+jsonNull#3f6d7b68 = JSONValue;
+jsonBool#c7345e6a value:Bool = JSONValue;
+jsonNumber#2be0dfa4 value:double = JSONValue;
+jsonString#b71e767a value:string = JSONValue;
+jsonArray#f7444763 value:Vector<JSONValue> = JSONValue;
+jsonObject#99c1d49d value:Vector<JSONObjectValue> = JSONValue;
+
+pageTableCell#34566b6a flags:# header:flags.0?true align_center:flags.3?true align_right:flags.4?true valign_middle:flags.5?true valign_bottom:flags.6?true text:flags.7?RichText colspan:flags.1?int rowspan:flags.2?int = PageTableCell;
+
+pageTableRow#e0c0c5e5 cells:Vector<PageTableCell> = PageTableRow;
+
+pageCaption#6f747657 text:RichText credit:RichText = PageCaption;
+
+pageListItemText#b92fb6cd text:RichText = PageListItem;
+pageListItemBlocks#25e073fc blocks:Vector<PageBlock> = PageListItem;
+
+pageListOrderedItemText#5e068047 num:string text:RichText = PageListOrderedItem;
+pageListOrderedItemBlocks#98dd8936 num:string blocks:Vector<PageBlock> = PageListOrderedItem;
+
+pageRelatedArticle#b390dc08 flags:# url:string webpage_id:long title:flags.0?string description:flags.1?string photo_id:flags.2?long author:flags.3?string published_date:flags.4?int = PageRelatedArticle;
+
+page#98657f0d flags:# part:flags.0?true rtl:flags.1?true v2:flags.2?true url:string blocks:Vector<PageBlock> photos:Vector<Photo> documents:Vector<Document> views:flags.3?int = Page;
+
+help.supportName#8c05f1c9 name:string = help.SupportName;
+
+help.userInfoEmpty#f3ae2eed = help.UserInfo;
+help.userInfo#1eb3758 message:string entities:Vector<MessageEntity> author:string date:int = help.UserInfo;
+
+pollAnswer#6ca9c2e9 text:string option:bytes = PollAnswer;
+
+poll#86e18161 id:long flags:# closed:flags.0?true public_voters:flags.1?true multiple_choice:flags.2?true quiz:flags.3?true question:string answers:Vector<PollAnswer> close_period:flags.4?int close_date:flags.5?int = Poll;
+
+pollAnswerVoters#3b6ddad2 flags:# chosen:flags.0?true correct:flags.1?true option:bytes voters:int = PollAnswerVoters;
+
+pollResults#badcc1a3 flags:# min:flags.0?true results:flags.1?Vector<PollAnswerVoters> total_voters:flags.2?int recent_voters:flags.3?Vector<int> solution:flags.4?string solution_entities:flags.4?Vector<MessageEntity> = PollResults;
+
+chatOnlines#f041e250 onlines:int = ChatOnlines;
+
+statsURL#47a971e0 url:string = StatsURL;
+
+chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true = ChatAdminRights;
+
+chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true until_date:int = ChatBannedRights;
+
+inputWallPaper#e630b979 id:long access_hash:long = InputWallPaper;
+inputWallPaperSlug#72091c80 slug:string = InputWallPaper;
+inputWallPaperNoFile#8427bbac = InputWallPaper;
+
+account.wallPapersNotModified#1c199183 = account.WallPapers;
+account.wallPapers#702b65a9 hash:int wallpapers:Vector<WallPaper> = account.WallPapers;
+
+codeSettings#debebe83 flags:# allow_flashcall:flags.0?true current_number:flags.1?true allow_app_hash:flags.4?true = CodeSettings;
+
+wallPaperSettings#5086cf8 flags:# blur:flags.1?true motion:flags.2?true background_color:flags.0?int second_background_color:flags.4?int intensity:flags.3?int rotation:flags.4?int = WallPaperSettings;
+
+autoDownloadSettings#e04232f3 flags:# disabled:flags.0?true video_preload_large:flags.1?true audio_preload_next:flags.2?true phonecalls_less_data:flags.3?true photo_size_max:int video_size_max:int file_size_max:int video_upload_maxbitrate:int = AutoDownloadSettings;
+
+account.autoDownloadSettings#63cacf26 low:AutoDownloadSettings medium:AutoDownloadSettings high:AutoDownloadSettings = account.AutoDownloadSettings;
+
+emojiKeyword#d5b3b9f9 keyword:string emoticons:Vector<string> = EmojiKeyword;
+emojiKeywordDeleted#236df622 keyword:string emoticons:Vector<string> = EmojiKeyword;
+
+emojiKeywordsDifference#5cc761bd lang_code:string from_version:int version:int keywords:Vector<EmojiKeyword> = EmojiKeywordsDifference;
+
+emojiURL#a575739d url:string = EmojiURL;
+
+emojiLanguage#b3fb5361 lang_code:string = EmojiLanguage;
+
+fileLocationToBeDeprecated#bc7fc6cd volume_id:long local_id:int = FileLocation;
+
+folder#ff544e65 flags:# autofill_new_broadcasts:flags.0?true autofill_public_groups:flags.1?true autofill_new_correspondents:flags.2?true id:int title:string photo:flags.3?ChatPhoto = Folder;
+
+inputFolderPeer#fbd2c296 peer:InputPeer folder_id:int = InputFolderPeer;
+
+folderPeer#e9baa668 peer:Peer folder_id:int = FolderPeer;
+
+messages.searchCounter#e844ebff flags:# inexact:flags.1?true filter:MessagesFilter count:int = messages.SearchCounter;
+
+urlAuthResultRequest#92d33a0e flags:# request_write_access:flags.0?true bot:User domain:string = UrlAuthResult;
+urlAuthResultAccepted#8f8c0e4e url:string = UrlAuthResult;
+urlAuthResultDefault#a9d6db1f = UrlAuthResult;
+
+channelLocationEmpty#bfb5ad8b = ChannelLocation;
+channelLocation#209b82db geo_point:GeoPoint address:string = ChannelLocation;
+
+peerLocated#ca461b5d peer:Peer expires:int distance:int = PeerLocated;
+peerSelfLocated#f8ec284b expires:int = PeerLocated;
+
+restrictionReason#d072acb4 platform:string reason:string text:string = RestrictionReason;
+
+inputTheme#3c5693e9 id:long access_hash:long = InputTheme;
+inputThemeSlug#f5890df1 slug:string = InputTheme;
+
+theme#28f1114 flags:# creator:flags.0?true default:flags.1?true id:long access_hash:long slug:string title:string document:flags.2?Document settings:flags.3?ThemeSettings installs_count:int = Theme;
+
+account.themesNotModified#f41eb622 = account.Themes;
+account.themes#7f676421 hash:int themes:Vector<Theme> = account.Themes;
+
+auth.loginToken#629f1980 expires:int token:bytes = auth.LoginToken;
+auth.loginTokenMigrateTo#68e9916 dc_id:int token:bytes = auth.LoginToken;
+auth.loginTokenSuccess#390d5c5e authorization:auth.Authorization = auth.LoginToken;
+
+account.contentSettings#57e28221 flags:# sensitive_enabled:flags.0?true sensitive_can_change:flags.1?true = account.ContentSettings;
+
+messages.inactiveChats#a927fec5 dates:Vector<int> chats:Vector<Chat> users:Vector<User> = messages.InactiveChats;
+
+baseThemeClassic#c3a12462 = BaseTheme;
+baseThemeDay#fbd81688 = BaseTheme;
+baseThemeNight#b7b31ea8 = BaseTheme;
+baseThemeTinted#6d5f77ee = BaseTheme;
+baseThemeArctic#5b11125a = BaseTheme;
+
+inputThemeSettings#bd507cd1 flags:# base_theme:BaseTheme accent_color:int message_top_color:flags.0?int message_bottom_color:flags.0?int wallpaper:flags.1?InputWallPaper wallpaper_settings:flags.1?WallPaperSettings = InputThemeSettings;
+
+themeSettings#9c14984a flags:# base_theme:BaseTheme accent_color:int message_top_color:flags.0?int message_bottom_color:flags.0?int wallpaper:flags.1?WallPaper = ThemeSettings;
+
+webPageAttributeTheme#54b56617 flags:# documents:flags.0?Vector<Document> settings:flags.1?ThemeSettings = WebPageAttribute;
+
+messageUserVote#a28e5559 user_id:int option:bytes date:int = MessageUserVote;
+messageUserVoteInputOption#36377430 user_id:int date:int = MessageUserVote;
+messageUserVoteMultiple#e8fe0de user_id:int options:Vector<bytes> date:int = MessageUserVote;
+
+messages.votesList#823f649 flags:# count:int votes:Vector<MessageUserVote> users:Vector<User> next_offset:flags.0?string = messages.VotesList;
+
+bankCardOpenUrl#f568028a url:string name:string = BankCardOpenUrl;
+
+payments.bankCardData#3e24e573 title:string open_urls:Vector<BankCardOpenUrl> = payments.BankCardData;
+
+dialogFilter#7438f7e8 flags:# contacts:flags.0?true non_contacts:flags.1?true groups:flags.2?true broadcasts:flags.3?true bots:flags.4?true exclude_muted:flags.11?true exclude_read:flags.12?true exclude_archived:flags.13?true id:int title:string emoticon:flags.25?string pinned_peers:Vector<InputPeer> include_peers:Vector<InputPeer> exclude_peers:Vector<InputPeer> = DialogFilter;
+
+dialogFilterSuggested#77744d4a filter:DialogFilter description:string = DialogFilterSuggested;
+
+statsDateRangeDays#b637edaf min_date:int max_date:int = StatsDateRangeDays;
+
+statsAbsValueAndPrev#cb43acde current:double previous:double = StatsAbsValueAndPrev;
+
+statsPercentValue#cbce2fe0 part:double total:double = StatsPercentValue;
+
+statsGraphAsync#4a27eb2d token:string = StatsGraph;
+statsGraphError#bedc9822 error:string = StatsGraph;
+statsGraph#8ea464b6 flags:# json:DataJSON zoom_token:flags.0?string = StatsGraph;
+
+messageInteractionCounters#ad4fc9bd msg_id:int views:int forwards:int = MessageInteractionCounters;
+
+stats.broadcastStats#bdf78394 period:StatsDateRangeDays followers:StatsAbsValueAndPrev views_per_post:StatsAbsValueAndPrev shares_per_post:StatsAbsValueAndPrev enabled_notifications:StatsPercentValue growth_graph:StatsGraph followers_graph:StatsGraph mute_graph:StatsGraph top_hours_graph:StatsGraph interactions_graph:StatsGraph iv_interactions_graph:StatsGraph views_by_source_graph:StatsGraph new_followers_by_source_graph:StatsGraph languages_graph:StatsGraph recent_message_interactions:Vector<MessageInteractionCounters> = stats.BroadcastStats;
+
+---functions---
+
+invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
+invokeAfterMsgs#3dc4b4f0 {X:Type} msg_ids:Vector<long> query:!X = X;
+initConnection#c1cd5ea9 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy params:flags.1?JSONValue query:!X = X;
+invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X;
+invokeWithoutUpdates#bf9459b7 {X:Type} query:!X = X;
+invokeWithMessagesRange#365275f2 {X:Type} range:MessageRange query:!X = X;
+invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X;
+
+auth.sendCode#a677244f phone_number:string api_id:int api_hash:string settings:CodeSettings = auth.SentCode;
+auth.signUp#80eee427 phone_number:string phone_code_hash:string first_name:string last_name:string = auth.Authorization;
+auth.signIn#bcd51581 phone_number:string phone_code_hash:string phone_code:string = auth.Authorization;
+auth.logOut#5717da40 = Bool;
+auth.resetAuthorizations#9fab0d1a = Bool;
+auth.exportAuthorization#e5bfffcd dc_id:int = auth.ExportedAuthorization;
+auth.importAuthorization#e3ef9613 id:int bytes:bytes = auth.Authorization;
+auth.bindTempAuthKey#cdd42a05 perm_auth_key_id:long nonce:long expires_at:int encrypted_message:bytes = Bool;
+auth.importBotAuthorization#67a3ff2c flags:int api_id:int api_hash:string bot_auth_token:string = auth.Authorization;
+auth.checkPassword#d18b4d16 password:InputCheckPasswordSRP = auth.Authorization;
+auth.requestPasswordRecovery#d897bc66 = auth.PasswordRecovery;
+auth.recoverPassword#4ea56e92 code:string = auth.Authorization;
+auth.resendCode#3ef1a9bf phone_number:string phone_code_hash:string = auth.SentCode;
+auth.cancelCode#1f040578 phone_number:string phone_code_hash:string = Bool;
+auth.dropTempAuthKeys#8e48a188 except_auth_keys:Vector<long> = Bool;
+auth.exportLoginToken#b1b41517 api_id:int api_hash:string except_ids:Vector<int> = auth.LoginToken;
+auth.importLoginToken#95ac5ce4 token:bytes = auth.LoginToken;
+auth.acceptLoginToken#e894ad4d token:bytes = Authorization;
+
+account.registerDevice#68976c6f flags:# no_muted:flags.0?true token_type:int token:string app_sandbox:Bool secret:bytes other_uids:Vector<int> = Bool;
+account.unregisterDevice#3076c4bf token_type:int token:string other_uids:Vector<int> = Bool;
+account.updateNotifySettings#84be5b93 peer:InputNotifyPeer settings:InputPeerNotifySettings = Bool;
+account.getNotifySettings#12b3ad31 peer:InputNotifyPeer = PeerNotifySettings;
+account.resetNotifySettings#db7e1747 = Bool;
+account.updateProfile#78515775 flags:# first_name:flags.0?string last_name:flags.1?string about:flags.2?string = User;
+account.updateStatus#6628562c offline:Bool = Bool;
+account.getWallPapers#aabb1763 hash:int = account.WallPapers;
+account.reportPeer#ae189d5f peer:InputPeer reason:ReportReason = Bool;
+account.checkUsername#2714d86c username:string = Bool;
+account.updateUsername#3e0bdd7c username:string = User;
+account.getPrivacy#dadbc950 key:InputPrivacyKey = account.PrivacyRules;
+account.setPrivacy#c9f81ce8 key:InputPrivacyKey rules:Vector<InputPrivacyRule> = account.PrivacyRules;
+account.deleteAccount#418d4e0b reason:string = Bool;
+account.getAccountTTL#8fc711d = AccountDaysTTL;
+account.setAccountTTL#2442485e ttl:AccountDaysTTL = Bool;
+account.sendChangePhoneCode#82574ae5 phone_number:string settings:CodeSettings = auth.SentCode;
+account.changePhone#70c32edb phone_number:string phone_code_hash:string phone_code:string = User;
+account.updateDeviceLocked#38df3532 period:int = Bool;
+account.getAuthorizations#e320c158 = account.Authorizations;
+account.resetAuthorization#df77f3bc hash:long = Bool;
+account.getPassword#548a30f5 = account.Password;
+account.getPasswordSettings#9cd4eaf9 password:InputCheckPasswordSRP = account.PasswordSettings;
+account.updatePasswordSettings#a59b102f password:InputCheckPasswordSRP new_settings:account.PasswordInputSettings = Bool;
+account.sendConfirmPhoneCode#1b3faa88 hash:string settings:CodeSettings = auth.SentCode;
+account.confirmPhone#5f2178c3 phone_code_hash:string phone_code:string = Bool;
+account.getTmpPassword#449e0b51 password:InputCheckPasswordSRP period:int = account.TmpPassword;
+account.getWebAuthorizations#182e6d6f = account.WebAuthorizations;
+account.resetWebAuthorization#2d01b9ef hash:long = Bool;
+account.resetWebAuthorizations#682d2594 = Bool;
+account.getAllSecureValues#b288bc7d = Vector<SecureValue>;
+account.getSecureValue#73665bc2 types:Vector<SecureValueType> = Vector<SecureValue>;
+account.saveSecureValue#899fe31d value:InputSecureValue secure_secret_id:long = SecureValue;
+account.deleteSecureValue#b880bc4b types:Vector<SecureValueType> = Bool;
+account.getAuthorizationForm#b86ba8e1 bot_id:int scope:string public_key:string = account.AuthorizationForm;
+account.acceptAuthorization#e7027c94 bot_id:int scope:string public_key:string value_hashes:Vector<SecureValueHash> credentials:SecureCredentialsEncrypted = Bool;
+account.sendVerifyPhoneCode#a5a356f9 phone_number:string settings:CodeSettings = auth.SentCode;
+account.verifyPhone#4dd3a7f6 phone_number:string phone_code_hash:string phone_code:string = Bool;
+account.sendVerifyEmailCode#7011509f email:string = account.SentEmailCode;
+account.verifyEmail#ecba39db email:string code:string = Bool;
+account.initTakeoutSession#f05b4804 flags:# contacts:flags.0?true message_users:flags.1?true message_chats:flags.2?true message_megagroups:flags.3?true message_channels:flags.4?true files:flags.5?true file_max_size:flags.5?int = account.Takeout;
+account.finishTakeoutSession#1d2652ee flags:# success:flags.0?true = Bool;
+account.confirmPasswordEmail#8fdf1920 code:string = Bool;
+account.resendPasswordEmail#7a7f2a15 = Bool;
+account.cancelPasswordEmail#c1cbd5b6 = Bool;
+account.getContactSignUpNotification#9f07c728 = Bool;
+account.setContactSignUpNotification#cff43f61 silent:Bool = Bool;
+account.getNotifyExceptions#53577479 flags:# compare_sound:flags.1?true peer:flags.0?InputNotifyPeer = Updates;
+account.getWallPaper#fc8ddbea wallpaper:InputWallPaper = WallPaper;
+account.uploadWallPaper#dd853661 file:InputFile mime_type:string settings:WallPaperSettings = WallPaper;
+account.saveWallPaper#6c5a5b37 wallpaper:InputWallPaper unsave:Bool settings:WallPaperSettings = Bool;
+account.installWallPaper#feed5769 wallpaper:InputWallPaper settings:WallPaperSettings = Bool;
+account.resetWallPapers#bb3b9804 = Bool;
+account.getAutoDownloadSettings#56da0b3f = account.AutoDownloadSettings;
+account.saveAutoDownloadSettings#76f36233 flags:# low:flags.0?true high:flags.1?true settings:AutoDownloadSettings = Bool;
+account.uploadTheme#1c3db333 flags:# file:InputFile thumb:flags.0?InputFile file_name:string mime_type:string = Document;
+account.createTheme#8432c21f flags:# slug:string title:string document:flags.2?InputDocument settings:flags.3?InputThemeSettings = Theme;
+account.updateTheme#5cb367d5 flags:# format:string theme:InputTheme slug:flags.0?string title:flags.1?string document:flags.2?InputDocument settings:flags.3?InputThemeSettings = Theme;
+account.saveTheme#f257106c theme:InputTheme unsave:Bool = Bool;
+account.installTheme#7ae43737 flags:# dark:flags.0?true format:flags.1?string theme:flags.1?InputTheme = Bool;
+account.getTheme#8d9d742b format:string theme:InputTheme document_id:long = Theme;
+account.getThemes#285946f8 format:string hash:int = account.Themes;
+account.setContentSettings#b574b16b flags:# sensitive_enabled:flags.0?true = Bool;
+account.getContentSettings#8b9b4dae = account.ContentSettings;
+account.getMultiWallPapers#65ad71dc wallpapers:Vector<InputWallPaper> = Vector<WallPaper>;
+
+users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
+users.getFullUser#ca30a5b1 id:InputUser = UserFull;
+users.setSecureValueErrors#90c894b5 id:InputUser errors:Vector<SecureValueError> = Bool;
+
+contacts.getContactIDs#2caa4a42 hash:int = Vector<int>;
+contacts.getStatuses#c4a353ee = Vector<ContactStatus>;
+contacts.getContacts#c023849f hash:int = contacts.Contacts;
+contacts.importContacts#2c800be5 contacts:Vector<InputContact> = contacts.ImportedContacts;
+contacts.deleteContacts#96a0e00 id:Vector<InputUser> = Updates;
+contacts.deleteByPhones#1013fd9e phones:Vector<string> = Bool;
+contacts.block#332b49fc id:InputUser = Bool;
+contacts.unblock#e54100bd id:InputUser = Bool;
+contacts.getBlocked#f57c350f offset:int limit:int = contacts.Blocked;
+contacts.search#11f812d8 q:string limit:int = contacts.Found;
+contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer;
+contacts.getTopPeers#d4982db5 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true phone_calls:flags.3?true forward_users:flags.4?true forward_chats:flags.5?true groups:flags.10?true channels:flags.15?true offset:int limit:int hash:int = contacts.TopPeers;
+contacts.resetTopPeerRating#1ae373ac category:TopPeerCategory peer:InputPeer = Bool;
+contacts.resetSaved#879537f1 = Bool;
+contacts.getSaved#82f1e39f = Vector<SavedContact>;
+contacts.toggleTopPeers#8514bdda enabled:Bool = Bool;
+contacts.addContact#e8f463d0 flags:# add_phone_privacy_exception:flags.0?true id:InputUser first_name:string last_name:string phone:string = Updates;
+contacts.acceptContact#f831a20f id:InputUser = Updates;
+contacts.getLocated#d348bc44 flags:# background:flags.1?true geo_point:InputGeoPoint self_expires:flags.0?int = Updates;
+
+messages.getMessages#63c66506 id:Vector<InputMessage> = messages.Messages;
+messages.getDialogs#a0ee3b73 flags:# exclude_pinned:flags.0?true folder_id:flags.1?int offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:int = messages.Dialogs;
+messages.getHistory#dcbb8260 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages;
+messages.search#8614ef68 flags:# peer:InputPeer q:string from_id:flags.0?InputUser filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages;
+messages.readHistory#e306d3a peer:InputPeer max_id:int = messages.AffectedMessages;
+messages.deleteHistory#1c015b09 flags:# just_clear:flags.0?true revoke:flags.1?true peer:InputPeer max_id:int = messages.AffectedHistory;
+messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
+messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
+messages.setTyping#a3825e50 peer:InputPeer action:SendMessageAction = Bool;
+messages.sendMessage#520c3870 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<MessageEntity> schedule_date:flags.10?int = Updates;
+messages.sendMedia#3491eba9 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 message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
+messages.forwardMessages#d9fee60e flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true grouped:flags.9?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer schedule_date:flags.10?int = Updates;
+messages.reportSpam#cf1592db peer:InputPeer = Bool;
+messages.getPeerSettings#3672e09c peer:InputPeer = PeerSettings;
+messages.report#bd82b658 peer:InputPeer id:Vector<int> reason:ReportReason = Bool;
+messages.getChats#3c6aa187 id:Vector<int> = messages.Chats;
+messages.getFullChat#3b831c66 chat_id:int = messages.ChatFull;
+messages.editChatTitle#dc452855 chat_id:int title:string = Updates;
+messages.editChatPhoto#ca4c79d8 chat_id:int photo:InputChatPhoto = Updates;
+messages.addChatUser#f9a0aa09 chat_id:int user_id:InputUser fwd_limit:int = Updates;
+messages.deleteChatUser#e0611f16 chat_id:int user_id:InputUser = Updates;
+messages.createChat#9cb126e users:Vector<InputUser> title:string = Updates;
+messages.getDhConfig#26cf8950 version:int random_length:int = messages.DhConfig;
+messages.requestEncryption#f64daf43 user_id:InputUser random_id:int g_a:bytes = EncryptedChat;
+messages.acceptEncryption#3dbc0415 peer:InputEncryptedChat g_b:bytes key_fingerprint:long = EncryptedChat;
+messages.discardEncryption#edd923c5 chat_id:int = Bool;
+messages.setEncryptedTyping#791451ed peer:InputEncryptedChat typing:Bool = Bool;
+messages.readEncryptedHistory#7f4b690a peer:InputEncryptedChat max_date:int = Bool;
+messages.sendEncrypted#a9776773 peer:InputEncryptedChat random_id:long data:bytes = messages.SentEncryptedMessage;
+messages.sendEncryptedFile#9a901b66 peer:InputEncryptedChat random_id:long data:bytes file:InputEncryptedFile = messages.SentEncryptedMessage;
+messages.sendEncryptedService#32d439a4 peer:InputEncryptedChat random_id:long data:bytes = messages.SentEncryptedMessage;
+messages.receivedQueue#55a5bb66 max_qts:int = Vector<long>;
+messages.reportEncryptedSpam#4b0c8c0f peer:InputEncryptedChat = Bool;
+messages.readMessageContents#36a73f77 id:Vector<int> = messages.AffectedMessages;
+messages.getStickers#43d4f2c emoticon:string hash:int = messages.Stickers;
+messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers;
+messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector<MessageEntity> = MessageMedia;
+messages.exportChatInvite#df7534c peer:InputPeer = ExportedChatInvite;
+messages.checkChatInvite#3eadb1bb hash:string = ChatInvite;
+messages.importChatInvite#6c50051c hash:string = Updates;
+messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet;
+messages.installStickerSet#c78fe460 stickerset:InputStickerSet archived:Bool = messages.StickerSetInstallResult;
+messages.uninstallStickerSet#f96e55de stickerset:InputStickerSet = Bool;
+messages.startBot#e6df7378 bot:InputUser peer:InputPeer random_id:long start_param:string = Updates;
+messages.getMessagesViews#c4c8a55d peer:InputPeer id:Vector<int> increment:Bool = Vector<int>;
+messages.editChatAdmin#a9e69f2e chat_id:int user_id:InputUser is_admin:Bool = Bool;
+messages.migrateChat#15a3b8e3 chat_id:int = Updates;
+messages.searchGlobal#bf7225a4 flags:# folder_id:flags.0?int q:string offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
+messages.reorderStickerSets#78337739 flags:# masks:flags.0?true order:Vector<long> = Bool;
+messages.getDocumentByHash#338e2464 sha256:bytes size:int mime_type:string = Document;
+messages.searchGifs#bf9a776b q:string offset:int = messages.FoundGifs;
+messages.getSavedGifs#83bf3d52 hash:int = messages.SavedGifs;
+messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool;
+messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults;
+messages.setInlineBotResults#eb5ea206 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector<InputBotInlineResult> cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM = Bool;
+messages.sendInlineBotResult#220815b0 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string schedule_date:flags.10?int = Updates;
+messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData;
+messages.editMessage#48f71778 flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.15?int = Updates;
+messages.editInlineBotMessage#83557dba flags:# no_webpage:flags.1?true id:InputBotInlineMessageID message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Bool;
+messages.getBotCallbackAnswer#810a9fec flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes = messages.BotCallbackAnswer;
+messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool;
+messages.getPeerDialogs#e470bcfd peers:Vector<InputDialogPeer> = messages.PeerDialogs;
+messages.saveDraft#bc39e14b flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int peer:InputPeer message:string entities:flags.3?Vector<MessageEntity> = Bool;
+messages.getAllDrafts#6a3f8d65 = Updates;
+messages.getFeaturedStickers#2dacca4f hash:int = messages.FeaturedStickers;
+messages.readFeaturedStickers#5b118126 id:Vector<long> = Bool;
+messages.getRecentStickers#5ea192c9 flags:# attached:flags.0?true hash:int = messages.RecentStickers;
+messages.saveRecentSticker#392718f8 flags:# attached:flags.0?true id:InputDocument unsave:Bool = Bool;
+messages.clearRecentStickers#8999602d flags:# attached:flags.0?true = Bool;
+messages.getArchivedStickers#57f17692 flags:# masks:flags.0?true offset_id:long limit:int = messages.ArchivedStickers;
+messages.getMaskStickers#65b8c79f hash:int = messages.AllStickers;
+messages.getAttachedStickers#cc5b67cc media:InputStickeredMedia = Vector<StickerSetCovered>;
+messages.setGameScore#8ef8ecc0 flags:# edit_message:flags.0?true force:flags.1?true peer:InputPeer id:int user_id:InputUser score:int = Updates;
+messages.setInlineGameScore#15ad9f64 flags:# edit_message:flags.0?true force:flags.1?true id:InputBotInlineMessageID user_id:InputUser score:int = Bool;
+messages.getGameHighScores#e822649d peer:InputPeer id:int user_id:InputUser = messages.HighScores;
+messages.getInlineGameHighScores#f635e1b id:InputBotInlineMessageID user_id:InputUser = messages.HighScores;
+messages.getCommonChats#d0a48c4 user_id:InputUser max_id:int limit:int = messages.Chats;
+messages.getAllChats#eba80ff0 except_ids:Vector<int> = messages.Chats;
+messages.getWebPage#32ca8f91 url:string hash:int = WebPage;
+messages.toggleDialogPin#a731e257 flags:# pinned:flags.0?true peer:InputDialogPeer = Bool;
+messages.reorderPinnedDialogs#3b1adf37 flags:# force:flags.0?true folder_id:int order:Vector<InputDialogPeer> = Bool;
+messages.getPinnedDialogs#d6b94df2 folder_id:int = messages.PeerDialogs;
+messages.setBotShippingResults#e5f672fa flags:# query_id:long error:flags.0?string shipping_options:flags.1?Vector<ShippingOption> = Bool;
+messages.setBotPrecheckoutResults#9c2dd95 flags:# success:flags.1?true query_id:long error:flags.0?string = Bool;
+messages.uploadMedia#519bc2b1 peer:InputPeer media:InputMedia = MessageMedia;
+messages.sendScreenshotNotification#c97df020 peer:InputPeer reply_to_msg_id:int random_id:long = Updates;
+messages.getFavedStickers#21ce0b0e hash:int = messages.FavedStickers;
+messages.faveSticker#b9ffc55b id:InputDocument unfave:Bool = Bool;
+messages.getUnreadMentions#46578472 peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
+messages.readMentions#f0189d3 peer:InputPeer = messages.AffectedHistory;
+messages.getRecentLocations#bbc45b09 peer:InputPeer limit:int hash:int = messages.Messages;
+messages.sendMultiMedia#cc0110cb flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int = Updates;
+messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile;
+messages.searchStickerSets#c2b7d08b flags:# exclude_featured:flags.0?true q:string hash:int = messages.FoundStickerSets;
+messages.getSplitRanges#1cff7e08 = Vector<MessageRange>;
+messages.markDialogUnread#c286d98f flags:# unread:flags.0?true peer:InputDialogPeer = Bool;
+messages.getDialogUnreadMarks#22e24e22 = Vector<DialogPeer>;
+messages.clearAllDrafts#7e58ee9c = Bool;
+messages.updatePinnedMessage#d2aaf7ec flags:# silent:flags.0?true peer:InputPeer id:int = Updates;
+messages.sendVote#10ea6184 peer:InputPeer msg_id:int options:Vector<bytes> = Updates;
+messages.getPollResults#73bb643b peer:InputPeer msg_id:int = Updates;
+messages.getOnlines#6e2be050 peer:InputPeer = ChatOnlines;
+messages.getStatsURL#812c2ae6 flags:# dark:flags.0?true peer:InputPeer params:string = StatsURL;
+messages.editChatAbout#def60797 peer:InputPeer about:string = Bool;
+messages.editChatDefaultBannedRights#a5866b41 peer:InputPeer banned_rights:ChatBannedRights = Updates;
+messages.getEmojiKeywords#35a0e062 lang_code:string = EmojiKeywordsDifference;
+messages.getEmojiKeywordsDifference#1508b6af lang_code:string from_version:int = EmojiKeywordsDifference;
+messages.getEmojiKeywordsLanguages#4e9963b2 lang_codes:Vector<string> = Vector<EmojiLanguage>;
+messages.getEmojiURL#d5b10c26 lang_code:string = EmojiURL;
+messages.getSearchCounters#732eef00 peer:InputPeer filters:Vector<MessagesFilter> = Vector<messages.SearchCounter>;
+messages.requestUrlAuth#e33f5613 peer:InputPeer msg_id:int button_id:int = UrlAuthResult;
+messages.acceptUrlAuth#f729ea98 flags:# write_allowed:flags.0?true peer:InputPeer msg_id:int button_id:int = UrlAuthResult;
+messages.hidePeerSettingsBar#4facb138 peer:InputPeer = Bool;
+messages.getScheduledHistory#e2c2685b peer:InputPeer hash:int = messages.Messages;
+messages.getScheduledMessages#bdbb0464 peer:InputPeer id:Vector<int> = messages.Messages;
+messages.sendScheduledMessages#bd38850a peer:InputPeer id:Vector<int> = Updates;
+messages.deleteScheduledMessages#59ae2b16 peer:InputPeer id:Vector<int> = Updates;
+messages.getPollVotes#b86e380e flags:# peer:InputPeer id:int option:flags.0?bytes offset:flags.1?string limit:int = messages.VotesList;
+messages.toggleStickerSets#b5052fea flags:# uninstall:flags.0?true archive:flags.1?true unarchive:flags.2?true stickersets:Vector<InputStickerSet> = Bool;
+messages.getDialogFilters#f19ed96d = Vector<DialogFilter>;
+messages.getSuggestedDialogFilters#a29cd42c = Vector<DialogFilterSuggested>;
+messages.updateDialogFilter#1ad4a04a flags:# id:int filter:flags.0?DialogFilter = Bool;
+messages.updateDialogFiltersOrder#c563c1e4 order:Vector<int> = Bool;
+messages.getOldFeaturedStickers#5fe7025b offset:int limit:int hash:int = messages.FeaturedStickers;
+
+updates.getState#edd4882a = updates.State;
+updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
+updates.getChannelDifference#3173d78 flags:# force:flags.0?true channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference;
+
+photos.updateProfilePhoto#f0bb5152 id:InputPhoto = UserProfilePhoto;
+photos.uploadProfilePhoto#4f32c098 file:InputFile = photos.Photo;
+photos.deletePhotos#87cf7f2f id:Vector<InputPhoto> = Vector<long>;
+photos.getUserPhotos#91cd32a8 user_id:InputUser offset:int max_id:long limit:int = photos.Photos;
+
+upload.saveFilePart#b304a621 file_id:long file_part:int bytes:bytes = Bool;
+upload.getFile#b15a9afc flags:# precise:flags.0?true cdn_supported:flags.1?true location:InputFileLocation offset:int limit:int = upload.File;
+upload.saveBigFilePart#de7b673d file_id:long file_part:int file_total_parts:int bytes:bytes = Bool;
+upload.getWebFile#24e6818d location:InputWebFileLocation offset:int limit:int = upload.WebFile;
+upload.getCdnFile#2000bcc3 file_token:bytes offset:int limit:int = upload.CdnFile;
+upload.reuploadCdnFile#9b2754a8 file_token:bytes request_token:bytes = Vector<FileHash>;
+upload.getCdnFileHashes#4da54231 file_token:bytes offset:int = Vector<FileHash>;
+upload.getFileHashes#c7025931 location:InputFileLocation offset:int = Vector<FileHash>;
+
+help.getConfig#c4f9186b = Config;
+help.getNearestDc#1fb33026 = NearestDc;
+help.getAppUpdate#522d5a7d source:string = help.AppUpdate;
+help.getInviteText#4d392343 = help.InviteText;
+help.getSupport#9cdf08cd = help.Support;
+help.getAppChangelog#9010ef6f prev_app_version:string = Updates;
+help.setBotUpdatesStatus#ec22cfcd pending_updates_count:int message:string = Bool;
+help.getCdnConfig#52029342 = CdnConfig;
+help.getRecentMeUrls#3dc0f114 referer:string = help.RecentMeUrls;
+help.getProxyData#3d7758e1 = help.ProxyData;
+help.getTermsOfServiceUpdate#2ca51fd1 = help.TermsOfServiceUpdate;
+help.acceptTermsOfService#ee72f79a id:DataJSON = Bool;
+help.getDeepLinkInfo#3fedc75f path:string = help.DeepLinkInfo;
+help.getAppConfig#98914110 = JSONValue;
+help.saveAppLog#6f02f748 events:Vector<InputAppEvent> = Bool;
+help.getPassportConfig#c661ad08 hash:int = help.PassportConfig;
+help.getSupportName#d360e72c = help.SupportName;
+help.getUserInfo#38a08d3 user_id:InputUser = help.UserInfo;
+help.editUserInfo#66b91b70 user_id:InputUser message:string entities:Vector<MessageEntity> = help.UserInfo;
+
+channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool;
+channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector<int> = messages.AffectedMessages;
+channels.deleteUserHistory#d10dd71b channel:InputChannel user_id:InputUser = messages.AffectedHistory;
+channels.reportSpam#fe087810 channel:InputChannel user_id:InputUser id:Vector<int> = Bool;
+channels.getMessages#ad8c9a23 channel:InputChannel id:Vector<InputMessage> = messages.Messages;
+channels.getParticipants#123e05e9 channel:InputChannel filter:ChannelParticipantsFilter offset:int limit:int hash:int = channels.ChannelParticipants;
+channels.getParticipant#546dd7a6 channel:InputChannel user_id:InputUser = channels.ChannelParticipant;
+channels.getChannels#a7f6bbb id:Vector<InputChannel> = messages.Chats;
+channels.getFullChannel#8736a09 channel:InputChannel = messages.ChatFull;
+channels.createChannel#3d5fb10f flags:# broadcast:flags.0?true megagroup:flags.1?true title:string about:string geo_point:flags.2?InputGeoPoint address:flags.2?string = Updates;
+channels.editAdmin#d33c8902 channel:InputChannel user_id:InputUser admin_rights:ChatAdminRights rank:string = Updates;
+channels.editTitle#566decd0 channel:InputChannel title:string = Updates;
+channels.editPhoto#f12e57c9 channel:InputChannel photo:InputChatPhoto = Updates;
+channels.checkUsername#10e6bd2c channel:InputChannel username:string = Bool;
+channels.updateUsername#3514b3de channel:InputChannel username:string = Bool;
+channels.joinChannel#24b524c5 channel:InputChannel = Updates;
+channels.leaveChannel#f836aa95 channel:InputChannel = Updates;
+channels.inviteToChannel#199f3a6c channel:InputChannel users:Vector<InputUser> = Updates;
+channels.deleteChannel#c0111fe3 channel:InputChannel = Updates;
+channels.exportMessageLink#ceb77163 channel:InputChannel id:int grouped:Bool = ExportedMessageLink;
+channels.toggleSignatures#1f69b606 channel:InputChannel enabled:Bool = Updates;
+channels.getAdminedPublicChannels#f8b036af flags:# by_location:flags.0?true check_limit:flags.1?true = messages.Chats;
+channels.editBanned#72796912 channel:InputChannel user_id:InputUser banned_rights:ChatBannedRights = Updates;
+channels.getAdminLog#33ddf480 flags:# channel:InputChannel q:string events_filter:flags.0?ChannelAdminLogEventsFilter admins:flags.1?Vector<InputUser> max_id:long min_id:long limit:int = channels.AdminLogResults;
+channels.setStickers#ea8ca4f9 channel:InputChannel stickerset:InputStickerSet = Bool;
+channels.readMessageContents#eab5dc38 channel:InputChannel id:Vector<int> = Bool;
+channels.deleteHistory#af369d42 channel:InputChannel max_id:int = Bool;
+channels.togglePreHistoryHidden#eabbb94c channel:InputChannel enabled:Bool = Updates;
+channels.getLeftChannels#8341ecc0 offset:int = messages.Chats;
+channels.getGroupsForDiscussion#f5dad378 = messages.Chats;
+channels.setDiscussionGroup#40582bb2 broadcast:InputChannel group:InputChannel = Bool;
+channels.editCreator#8f38cd1f channel:InputChannel user_id:InputUser password:InputCheckPasswordSRP = Updates;
+channels.editLocation#58e63f6d channel:InputChannel geo_point:InputGeoPoint address:string = Bool;
+channels.toggleSlowMode#edd49ef0 channel:InputChannel seconds:int = Updates;
+channels.getInactiveChannels#11e831ee = messages.InactiveChats;
+
+bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
+bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
+bots.setBotCommands#805d46f6 commands:Vector<BotCommand> = Bool;
+
+payments.getPaymentForm#99f09745 msg_id:int = payments.PaymentForm;
+payments.getPaymentReceipt#a092a980 msg_id:int = payments.PaymentReceipt;
+payments.validateRequestedInfo#770a8e74 flags:# save:flags.0?true msg_id:int info:PaymentRequestedInfo = payments.ValidatedRequestedInfo;
+payments.sendPaymentForm#2b8879b3 flags:# msg_id:int requested_info_id:flags.0?string shipping_option_id:flags.1?string credentials:InputPaymentCredentials = payments.PaymentResult;
+payments.getSavedInfo#227d824b = payments.SavedInfo;
+payments.clearSavedInfo#d83d70c1 flags:# credentials:flags.0?true info:flags.1?true = Bool;
+payments.getBankCardData#2e79d779 number:string = payments.BankCardData;
+
+stickers.createStickerSet#f1036780 flags:# masks:flags.0?true animated:flags.1?true user_id:InputUser title:string short_name:string thumb:flags.2?InputDocument stickers:Vector<InputStickerSetItem> = messages.StickerSet;
+stickers.removeStickerFromSet#f7760f51 sticker:InputDocument = messages.StickerSet;
+stickers.changeStickerPosition#ffb6d4ca sticker:InputDocument position:int = messages.StickerSet;
+stickers.addStickerToSet#8653febe stickerset:InputStickerSet sticker:InputStickerSetItem = messages.StickerSet;
+stickers.setStickerSetThumb#9a364e30 stickerset:InputStickerSet thumb:InputDocument = messages.StickerSet;
+
+phone.getCallConfig#55451fa9 = DataJSON;
+phone.requestCall#42ff96ed flags:# video:flags.0?true user_id:InputUser random_id:int g_a_hash:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
+phone.acceptCall#3bd2b4a0 peer:InputPhoneCall g_b:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
+phone.confirmCall#2efe1722 peer:InputPhoneCall g_a:bytes key_fingerprint:long protocol:PhoneCallProtocol = phone.PhoneCall;
+phone.receivedCall#17d54f61 peer:InputPhoneCall = Bool;
+phone.discardCall#b2cbc1c0 flags:# video:flags.0?true peer:InputPhoneCall duration:int reason:PhoneCallDiscardReason connection_id:long = Updates;
+phone.setCallRating#59ead627 flags:# user_initiative:flags.0?true peer:InputPhoneCall rating:int comment:string = Updates;
+phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool;
+
+langpack.getLangPack#f2f2330a lang_pack:string lang_code:string = LangPackDifference;
+langpack.getStrings#efea3803 lang_pack:string lang_code:string keys:Vector<string> = Vector<LangPackString>;
+langpack.getDifference#cd984aa5 lang_pack:string lang_code:string from_version:int = LangPackDifference;
+langpack.getLanguages#42c6978f lang_pack:string = Vector<LangPackLanguage>;
+langpack.getLanguage#6a596502 lang_pack:string lang_code:string = LangPackLanguage;
+
+folders.editPeerFolders#6847d0ab folder_peers:Vector<InputFolderPeer> = Updates;
+folders.deleteFolder#1c295881 folder_id:int = Updates;
+
+stats.getBroadcastStats#e6300dba flags:# dark:flags.0?true channel:InputChannel tz_offset:int = stats.BroadcastStats;
+stats.loadAsyncGraph#621d5fa0 flags:# token:string x:flags.0?long = StatsGraph;
+
+// LAYER 112

+ 37 - 0
gramjs/tl/static/schema.reduced.tl

@@ -0,0 +1,37 @@
+resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector<long> = ResPQ;
+p_q_inner_data#83c95aec pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 = P_Q_inner_data;
+server_DH_params_fail#79cb045d nonce:int128 server_nonce:int128 new_nonce_hash:int128 = Server_DH_Params;
+server_DH_params_ok#d0e8075c nonce:int128 server_nonce:int128 encrypted_answer:string = Server_DH_Params;
+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;
+client_DH_inner_data#6643b654 nonce:int128 server_nonce:int128 retry_id:long g_b:string = Client_DH_Inner_Data;
+dh_gen_ok#3bcbf734 nonce:int128 server_nonce:int128 new_nonce_hash1:int128 = Set_client_DH_params_answer;
+dh_gen_retry#46dc1fb9 nonce:int128 server_nonce:int128 new_nonce_hash2:int128 = Set_client_DH_params_answer;
+dh_gen_fail#a69dae02 nonce:int128 server_nonce:int128 new_nonce_hash3:int128 = Set_client_DH_params_answer;
+destroy_auth_key_ok#f660e1d4 = DestroyAuthKeyRes;
+destroy_auth_key_none#0a9f2259 = DestroyAuthKeyRes;
+destroy_auth_key_fail#ea109b13 = DestroyAuthKeyRes;
+---functions---
+req_pq#60469778 nonce:int128 = ResPQ;
+req_pq_multi#be7e8ef1 nonce:int128 = ResPQ;
+req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:string q:string public_key_fingerprint:long encrypted_data:string = Server_DH_Params;
+set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:string = Set_client_DH_params_answer;
+destroy_auth_key#d1435160 = DestroyAuthKeyRes;
+---types---
+msgs_ack#62d6b459 msg_ids:Vector<long> = MsgsAck;
+bad_msg_notification#a7eff811 bad_msg_id:long bad_msg_seqno:int error_code:int = BadMsgNotification;
+bad_server_salt#edab447b bad_msg_id:long bad_msg_seqno:int error_code:int new_server_salt:long = BadMsgNotification;
+msgs_state_req#da69fb52 msg_ids:Vector<long> = MsgsStateReq;
+msgs_state_info#04deb57d req_msg_id:long info:string = MsgsStateInfo;
+msgs_all_info#8cc0d131 msg_ids:Vector<long> info:string = MsgsAllInfo;
+msg_detailed_info#276d3ec6 msg_id:long answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
+msg_new_detailed_info#809db6df answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
+msg_resend_req#7d861a08 msg_ids:Vector<long> = MsgResendReq;
+rpc_error#2144ca19 error_code:int error_message:string = RpcError;
+future_salt#0949d9dc valid_since:int valid_until:int salt:long = FutureSalt;
+future_salts#ae500895 req_msg_id:long now:int salts:vector<future_salt> = FutureSalts;
+pong#347773c5 msg_id:long ping_id:long = Pong;
+destroy_session_ok#e22045fc session_id:long = DestroySessionRes;
+destroy_session_none#62d350c9 session_id:long = DestroySessionRes;
+new_session_created#9ec20908 first_msg_id:long unique_id:long server_salt:long = NewSession;
+---functions---
+ping#7abe77ec ping_id:long = Pong;

+ 0 - 0
src/lib/gramjs/tl/static/schema.tl → gramjs/tl/static/schema.tl


+ 57 - 0
gramjs/tl/types-generator/generate.js

@@ -0,0 +1,57 @@
+const path = require('path')
+const fs = require('fs')
+
+const { parseTl } = require('../generationHelpers')
+const templateFn = require('./template')
+
+const INPUT_FILE = path.resolve(__dirname, '../static/api.tl')
+const SCHEMA_FILE = path.resolve(__dirname, '../static/schema.tl')
+
+const OUTPUT_FILE = path.resolve(__dirname, '../api.d.ts')
+
+function main() {
+    const tlContent = fs.readFileSync(INPUT_FILE, 'utf-8')
+    const apiConfig = extractParams(tlContent)
+    const schemeContent = fs.readFileSync(SCHEMA_FILE, 'utf-8')
+    const schemeConfig = extractParams(schemeContent)
+    const types = [...apiConfig.types, ...schemeConfig.types]
+    const functions = [...apiConfig.functions, ...schemeConfig.functions]
+    const constructors = [...apiConfig.constructors, ...schemeConfig.constructors]
+    const generated = templateFn({ types: types, functions: functions, constructors: constructors })
+
+    fs.writeFileSync(OUTPUT_FILE, generated)
+}
+
+function extractParams(fileContent) {
+    const defInterator = parseTl(fileContent, 109)
+    const types = {}
+    const constructors = []
+    const functions = []
+
+    for (const def of defInterator) {
+        if (def.isFunction) {
+            functions.push(def)
+        } else {
+            if (!types[def.result]) {
+                let [namespace, name] = def.result.includes('.') ? def.result.split('.') : [undefined, def.result]
+
+                types[def.result] = {
+                    namespace,
+                    name,
+                    constructors: []
+                }
+            }
+
+            types[def.result].constructors.push(def.namespace ? `${def.namespace}.${def.name}` : def.name)
+            constructors.push(def)
+        }
+    }
+
+    return {
+        types: Object.values(types),
+        constructors,
+        functions
+    }
+}
+
+main()

+ 219 - 0
gramjs/tl/types-generator/template.js

@@ -0,0 +1,219 @@
+// Not sure what they are for.
+const WEIRD_TYPES = new Set(['Bool', 'X', 'Type'])
+
+module.exports = ({ types, constructors, functions }) => {
+    function groupByKey(collection, key) {
+        return collection.reduce((byKey, member) => {
+            const keyValue = member[key] || '_'
+
+            if (!byKey[keyValue]) {
+                byKey[keyValue] = [member]
+            } else {
+                byKey[keyValue].push(member)
+            }
+
+            return byKey
+        }, {})
+    }
+
+    function renderTypes(types, indent) {
+        return types.map(({ name, constructors }) => `
+      ${!constructors.length ? '// ' : ''}export type Type${upperFirst(name)} = ${constructors.map((name) => name)
+            .join(' | ')};
+    `.trim())
+            .join(`\n${indent}`)
+    }
+
+    function renderConstructors(constructors, indent) {
+        return constructors.map(({ name, argsConfig }) => {
+            const argKeys = Object.keys(argsConfig)
+
+            if (!argKeys.length) {
+                return `export class ${upperFirst(name)} extends VirtualClass<void> {};`
+            }
+
+            let hasRequiredArgs = argKeys.some((argName) => argName !== 'flags' && !argsConfig[argName].isFlag)
+
+            return `
+      export class ${upperFirst(name)} extends VirtualClass<{
+${indent}  ${Object.keys(argsConfig)
+            .map((argName) => `
+        ${renderArg(argName, argsConfig[argName])};
+      `.trim())
+            .join(`\n${indent}  `)}
+${indent}}${!hasRequiredArgs ? ` | void` : ''}> {
+${indent}  ${Object.keys(argsConfig)
+            .map((argName) => `
+        ${renderArg(argName, argsConfig[argName])};
+      `.trim())
+            .join(`\n${indent}  `)}
+${indent}};`.trim()
+        })
+        .join(`\n${indent}`)
+    }
+
+    function renderRequests(requests, indent) {
+        return requests.map(({ name, argsConfig, result }) => {
+            const argKeys = Object.keys(argsConfig)
+
+            if (!argKeys.length) {
+                return `export class ${upperFirst(name)} extends Request<void, ${renderResult(result)}> {};`
+            }
+
+            let hasRequiredArgs = argKeys.some((argName) => argName !== 'flags' && !argsConfig[argName].isFlag)
+
+            return `
+      export class ${upperFirst(name)} extends Request<Partial<{
+${indent}  ${argKeys.map((argName) => `
+        ${renderArg(argName, argsConfig[argName])};
+      `.trim())
+            .join(`\n${indent}  `)}
+${indent}}${!hasRequiredArgs ? ` | void` : ''}>, ${renderResult(result)}> {
+${indent}  ${argKeys.map((argName) => `
+        ${renderArg(argName, argsConfig[argName])};
+      `.trim())
+            .join(`\n${indent}  `)}
+${indent}};`.trim()
+        })
+        .join(`\n${indent}`)
+    }
+
+    function renderResult(result) {
+        const vectorMatch = result.match(/[Vv]ector<([\w\d.]+)>/)
+        const isVector = Boolean(vectorMatch)
+        const scalarValue = isVector ? vectorMatch[1] : result
+        const isTlType = Boolean(scalarValue.match(/^[A-Z]/)) || scalarValue.includes('.')
+
+        return renderValueType(scalarValue, isVector, isTlType)
+    }
+
+    function renderArg(argName, argConfig) {
+        const {
+            isVector, isFlag, skipConstructorId, type
+        } = argConfig
+
+        const valueType = renderValueType(type, isVector, !skipConstructorId)
+
+        return `${argName === 'flags' ? '// ' : ''}${argName}${isFlag ? '?' : ''}: ${valueType}`
+    }
+
+    function renderValueType(type, isVector, isTlType) {
+        if (WEIRD_TYPES.has(type)) {
+            return type
+        }
+
+        let resType
+
+        if (typeof type === 'string' && isTlType) {
+            resType = renderTypeName(type)
+        } else {
+            resType = type
+        }
+
+        if (isVector) {
+            resType = `${resType}[]`
+        }
+
+        return resType
+    }
+
+    function renderTypeName(typeName) {
+        return typeName.includes('.') ? typeName.replace('.', '.Type') : `Api.Type${typeName}`
+    }
+
+    function upperFirst(str) {
+        return `${str[0].toUpperCase()}${str.slice(1)}`
+    }
+
+    const typesByNs = groupByKey(types, 'namespace')
+    const constructorsByNs = groupByKey(constructors, 'namespace')
+    const requestsByNs = groupByKey(functions, 'namespace')
+
+    // language=TypeScript
+    return `
+// This file is autogenerated. All changes will be overwritten.
+
+import { BigInteger } from 'big-integer';
+
+export default Api;
+
+namespace Api {
+
+  type AnyClass = new (...args: any[]) => any;
+  type I<T extends AnyClass> = InstanceType<T>;
+  type ValuesOf<T> = T[keyof T];
+  type AnyLiteral = Record<string, any>;
+
+  type Reader = any; // To be defined.
+  type Client = any; // To be defined.
+  type Utils = any; // To be defined.
+
+  type X = unknown;
+  type Type = unknown;
+  type Bool = boolean;
+  type int = number;
+  type int128 = number;
+  type int256 = number;
+  type long = BigInteger;
+  type bytes = Buffer;
+
+  class VirtualClass<Args extends AnyLiteral> {
+    static CONSTRUCTOR_ID: number;
+    static SUBCLASS_OF_ID: number;
+    static className: string;
+    static classType: 'constructor' | 'request';
+
+    static serializeBytes(data: Buffer | string): Buffer;
+    static serializeDate(date: Date | number): Buffer;
+    static fromReader(reader: Reader): VirtualClass<Args>;
+
+    CONSTRUCTOR_ID: number;
+    SUBCLASS_OF_ID: number;
+    className: string;
+    classType: 'constructor' | 'request';
+
+    constructor(args: Args);
+  }
+
+  class Request<Args, Response> extends VirtualClass<Partial<Args>> {
+    static readResult(reader: Reader): Buffer;
+    static resolve(client: Client, utils: Utils): Promise<void>;
+
+    __response: Response;
+  }
+
+  ${renderTypes(typesByNs._, '  ')}
+  ${Object.keys(typesByNs)
+        .map(namespace => namespace !== '_' ? `
+  export namespace ${namespace} {
+    ${renderTypes(typesByNs[namespace], '    ')}
+  }` : '')
+        .join('\n')}
+
+  ${renderConstructors(constructorsByNs._, '  ')}
+  ${Object.keys(constructorsByNs)
+        .map(namespace => namespace !== '_' ? `
+  export namespace ${namespace} {
+    ${renderConstructors(constructorsByNs[namespace], '    ')}
+  }` : '')
+        .join('\n')}
+
+  ${renderRequests(requestsByNs._, '  ')}
+  ${Object.keys(requestsByNs)
+        .map(namespace => namespace !== '_' ? `
+  export namespace ${namespace} {
+    ${renderRequests(requestsByNs[namespace], '    ')}
+  }` : '')
+        .join('\n')}
+
+  export type AnyRequest = ${requestsByNs._.map(({ name }) => upperFirst(name))
+        .join(' | ')}
+    | ${Object.keys(requestsByNs)
+        .filter(ns => ns !== '_')
+        .map(ns => requestsByNs[ns].map(({ name }) => `${ns}.${upperFirst(name)}`)
+            .join(' | '))
+        .join('\n    | ')};
+
+}
+`
+}

+ 186 - 0
index.js

@@ -0,0 +1,186 @@
+const fs = require('fs')
+const path = require('path')
+const glob = require('glob')
+
+class TempWorkDir {
+    /**
+     * Switches the working directory to be the one on which this file lives.
+     */
+    constructor(dir) {
+        this.original = null
+        this.dir = dir || path.join(__filename, '..')
+    }
+
+    open() {
+        this.original = __dirname
+        fs.mkdirSync(this.dir, { recursive: true })
+        process.chdir(this.dir)
+        return this
+    }
+
+    close() {
+        process.chdir(this.original)
+    }
+}
+
+const GENERATOR_DIR = './gramjs_generator'
+const LIBRARY_DIR = './gramjs'
+
+const ERRORS_IN = `${GENERATOR_DIR}/data/errors.csv`
+const ERRORS_OUT = `${LIBRARY_DIR}/errors/RPCErrorList.js`
+
+const METHODS_IN = `${GENERATOR_DIR}/data/methods.csv`
+
+// Which raw API methods are covered by *friendly* methods in the client?
+const FRIENDLY_IN = `${GENERATOR_DIR}/data/friendly.csv`
+
+const TLOBJECT_IN_TLS = glob.sync(`${GENERATOR_DIR}/data/*.tl`)
+const TLOBJECT_OUT = `${LIBRARY_DIR}/tl`
+const IMPORT_DEPTH = 2
+
+const DOCS_IN_RES = `../${GENERATOR_DIR}/data/html`
+const DOCS_OUT = `./docs`
+
+const generate = (which, action = 'gen') => {
+    const {
+        parseErrors,
+        parseMethods,
+        parseTl,
+        findLayer,
+    } = require('./gramjs_generator/parsers')
+
+    const {
+        generateErrors,
+        generateTLObjects,
+        generateDocs,
+        cleanTLObjects,
+    } = require('./gramjs_generator/generators')
+
+    const [layer] = TLOBJECT_IN_TLS.map(findLayer).filter(Boolean)
+    const errors = [...parseErrors(ERRORS_IN)]
+    const methods = [
+        ...parseMethods(
+            METHODS_IN,
+            FRIENDLY_IN,
+            errors.reduce((errors, error) => {
+                errors[error.stringCode] = error
+                return errors
+            }, {})
+        ),
+    ]
+
+    const tlobjects = TLOBJECT_IN_TLS.reduce(
+        (files, file) => [...files, ...parseTl(file, layer, methods)],
+        []
+    )
+
+    if (!which || which.length === 0) {
+        which.push('tl', 'errors')
+    }
+
+    const clean = action === 'clean'
+    action = clean ? 'Cleaning' : 'Generating'
+
+    if (which.includes('all')) {
+        which.splice(which.indexOf('all'), 1)
+
+        for (const x of ['tl', 'errors', 'docs']) {
+            if (!which.includes(x)) {
+                which.push(x)
+            }
+        }
+    }
+
+    if (which.includes('tl')) {
+        which.splice(which.indexOf('tl'), 1)
+        console.log(action, 'TLObjects...')
+
+        if (clean) {
+            cleanTLObjects(TLOBJECT_OUT)
+        } else {
+            generateTLObjects(tlobjects, layer, IMPORT_DEPTH, TLOBJECT_OUT)
+        }
+    }
+
+    if (which.includes('errors')) {
+        which.splice(which.indexOf('errors'), 1)
+        console.log(action, 'RPCErrors...')
+
+        if (clean) {
+            if (fs.statSync(ERRORS_OUT).isFile()) {
+                fs.unlinkSync(ERRORS_OUT)
+            }
+        } else {
+            const file = fs.createWriteStream(ERRORS_OUT)
+            generateErrors(errors, file)
+        }
+    }
+
+    if (which.includes('docs')) {
+        which.splice(which.indexOf('docs'), 1)
+        console.log(action, 'documentation...')
+
+        if (clean) {
+            if (fs.statSync(DOCS_OUT)) {
+                fs.rmdirSync(DOCS_OUT)
+            }
+        } else {
+            const tmp = new TempWorkDir(DOCS_OUT).open()
+            generateDocs(tlobjects, methods, layer, DOCS_IN_RES)
+            tmp.close()
+        }
+    }
+
+    if (which.includes('json')) {
+        which.splice(which.indexOf('json'), 1)
+        console.log(action, 'JSON schema...')
+
+        const jsonFiles = TLOBJECT_IN_TLS.map(
+            (x) => x.slice(0, x.lastIndexOf('.')) + '.json'
+        )
+
+        if (clean) {
+            for (const file of jsonFiles) {
+                if (fs.statSync(file).isFile()) {
+                    fs.unlinkSync(file)
+                }
+            }
+        } else {
+            const genJson = (fin, fout) => {
+                const meths = []
+                const constructors = []
+
+                for (const tl of parseTl(fin, layer)) {
+                    if (tl.isFunction) {
+                        meths.push(tl.toJson())
+                    } else {
+                        constructors.push(tl.toJson())
+                    }
+                }
+
+                const what = { constructors, methods }
+                fs.writeFileSync(fout, JSON.stringify(what, null, 2))
+            }
+
+            for (let i = 0; i < TLOBJECT_IN_TLS.length; i++) {
+                const fin = TLOBJECT_IN_TLS[i]
+                const fout = jsonFiles[i]
+                genJson(fin, fout)
+            }
+        }
+    }
+
+    if (which.length) {
+        console.log('The following items were not understood:', which)
+        console.log('  Consider using only "tl", "errors" and/or "docs".')
+        console.log(
+            '  Using only "clean" will clean them. "all" to act on all.'
+        )
+        console.log('  For instance "gen tl errors".')
+    }
+}
+
+const { argv } = process
+if (argv.length > 2 && argv[2] === 'gen') {
+    generate(argv.slice(3))
+}

+ 188 - 0
jest.config.js

@@ -0,0 +1,188 @@
+// For a detailed explanation regarding each configuration property, visit:
+// https://jestjs.io/docs/en/configuration.html
+
+module.exports = {
+    // All imported modules in your tests should be mocked automatically
+    // automock: false,
+
+    // Stop running tests after `n` failures
+    // bail: 0,
+
+    // Respect "browser" field in package.json when resolving modules
+    // browser: false,
+
+    // The directory where Jest should store its cached dependency information
+    // cacheDirectory: "C:\\Users\\painor\\AppData\\Local\\Temp\\jest",
+
+    // Automatically clear mock calls and instances between every test
+    clearMocks: true,
+
+    // Indicates whether the coverage information should be collected while executing the test
+    // collectCoverage: false,
+
+    // An array of glob patterns indicating a set of files for which coverage information should be collected
+    // collectCoverageFrom: null,
+
+    // The directory where Jest should output its coverage files
+    coverageDirectory: 'coverage',
+
+    // An array of regexp pattern strings used to skip coverage collection
+    // coveragePathIgnorePatterns: [
+    //   "\\\\node_modules\\\\"
+    // ],
+
+    // A list of reporter names that Jest uses when writing coverage reports
+    // coverageReporters: [
+    //   "json",
+    //   "text",
+    //   "lcov",
+    //   "clover"
+    // ],
+
+    // An object that configures minimum threshold enforcement for coverage results
+    // coverageThreshold: null,
+
+    // A path to a custom dependency extractor
+    // dependencyExtractor: null,
+
+    // Make calling deprecated APIs throw helpful error messages
+    // errorOnDeprecated: false,
+
+    // Force coverage collection from ignored files using an array of glob patterns
+    // forceCoverageMatch: [],
+
+    // A path to a module which exports an async function that is triggered once before all test suites
+    // globalSetup: null,
+
+    // A path to a module which exports an async function that is triggered once after all test suites
+    // globalTeardown: null,
+
+    // A set of global variables that need to be available in all test environments
+    // globals: {},
+
+    // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
+    // maxWorkers: "50%",
+
+    // An array of directory names to be searched recursively up from the requiring module's location
+    // moduleDirectories: [
+    //   "node_modules"
+    // ],
+
+    // An array of file extensions your modules use
+    // moduleFileExtensions: [
+    //   "js",
+    //   "json",
+    //   "jsx",
+    //   "ts",
+    //   "tsx",
+    //   "node"
+    // ],
+
+    // A map from regular expressions to module names that allow to stub out resources with a single module
+    // moduleNameMapper: {},
+
+    // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
+    // modulePathIgnorePatterns: [],
+
+    // Activates notifications for test results
+    // notify: false,
+
+    // An enum that specifies notification mode. Requires { notify: true }
+    // notifyMode: "failure-change",
+
+    // A preset that is used as a base for Jest's configuration
+    // preset: null,
+
+    // Run tests from one or more projects
+    // projects: null,
+
+    // Use this configuration option to add custom reporters to Jest
+    // reporters: undefined,
+
+    // Automatically reset mock state between every test
+    // resetMocks: false,
+
+    // Reset the module registry before running each individual test
+    // resetModules: false,
+
+    // A path to a custom resolver
+    // resolver: null,
+
+    // Automatically restore mock state between every test
+    // restoreMocks: false,
+
+    // The root directory that Jest should scan for tests and modules within
+    // rootDir: null,
+
+    // A list of paths to directories that Jest should use to search for files in
+    // roots: [
+    //   "<rootDir>"
+    // ],
+
+    // Allows you to use a custom runner instead of Jest's default test runner
+    // runner: "jest-runner",
+
+    // The paths to modules that run some code to configure or set up the testing environment before each test
+    // setupFiles: [],
+
+    // A list of paths to modules that run some code to configure or set up the testing framework before each test
+    // setupFilesAfterEnv: [],
+
+    // A list of paths to snapshot serializer modules Jest should use for snapshot testing
+    // snapshotSerializers: [],
+
+    // The test environment that will be used for testing
+    testEnvironment: 'node',
+
+    // Options that will be passed to the testEnvironment
+    // testEnvironmentOptions: {},
+
+    // Adds a location field to test results
+    // testLocationInResults: false,
+
+    // The glob patterns Jest uses to detect test files
+    // testMatch: [
+    //   "**/__tests__/**/*.[jt]s?(x)",
+    //   "**/?(*.)+(spec|test).[tj]s?(x)"
+    // ],
+
+    // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
+    // testPathIgnorePatterns: [
+    //   "\\\\node_modules\\\\"
+    // ],
+
+    // The regexp pattern or array of patterns that Jest uses to detect test files
+    // testRegex: [],
+
+    // This option allows the use of a custom results processor
+    // testResultsProcessor: null,
+
+    // This option allows use of a custom test runner
+    // testRunner: "jasmine2",
+
+    // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
+    // testURL: "http://localhost",
+
+    // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
+    // timers: "real",
+
+    // A map from regular expressions to paths to transformers
+    // transform: null,
+
+    // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
+    // transformIgnorePatterns: [
+    //   "\\\\node_modules\\\\"
+    // ],
+
+    // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
+    // unmockedModulePathPatterns: undefined,
+
+    // Indicates whether each individual test should be reported during the run
+    // verbose: null,
+
+    // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
+    // watchPathIgnorePatterns: [],
+
+    // Whether to use watchman for file crawling
+    // watchman: true,
+}

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 462 - 362
package-lock.json


+ 41 - 79
package.json

@@ -1,95 +1,57 @@
 {
-  "name": "telegram-t",
-  "version": "1.0.0-0",
-  "description": "",
+  "name": "telegram",
+  "version": "1.0.0-alpha.2",
+  "description": "NodeJS MTProto API Telegram client library,",
   "main": "index.js",
   "scripts": {
-    "dev": "shx mkdir -p dist/ && shx cp node_modules/tdweb/dist/* dist/ && parcel src/index.html",
-    "build:staging:gramjs": "parcel build src/index.html --public-url ./",
-    "build:contest:tdlib": "./deploy/contest-tdlib.sh",
-    "build:contest:gramjs": "./deploy/contest-gramjs.sh",
-    "build:github:all": "./deploy/github.sh",
-    "test": "echo \"Error: no test specified\" && exit 1",
-    "lint": "eslint . --ext .ts,.tsx",
-    "lint:fix": "eslint . --ext .ts,.tsx --fix",
-    "gramjs:types": "node ./src/lib/gramjs/tl/types-generator/generate.js"
+    "test": "jest",
+    "lint": "eslint .",
+    "postinstall": "node index.js gen tl errors"
   },
-  "husky": {
-    "hooks": {
-      "pre-commit": "lint-staged -- --fix"
-    }
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/gram-js/gramjs.git"
   },
-  "lint-staged": {
-    "*.{ts,tsx}": "eslint"
+  "browser": {
+    "fs": false
   },
-  "author": "Alexander Zinchuk (alexander@zinchuk.com)",
-  "license": "GPL-3.0-or-later",
-  "devDependencies": {
-    "@babel/core": "^7.7.2",
-    "@babel/plugin-proposal-class-properties": "^7.7.0",
-    "@babel/plugin-transform-runtime": "^7.6.2",
-    "@types/react": "^16.9.11",
-    "@types/react-dom": "^16.9.3",
-    "@typescript-eslint/eslint-plugin": "^2.6.1",
-    "@typescript-eslint/parser": "^2.6.1",
-    "babel-eslint": "^10.0.3",
-    "babel-preset-env": "^1.7.0",
-    "eslint": "^6.6.0",
-    "eslint-config-airbnb-typescript": "^6.0.0",
-    "eslint-config-react-app": "^5.0.2",
-    "eslint-plugin-flowtype": "^3.13.0",
-    "eslint-plugin-import": "^2.18.2",
-    "eslint-plugin-jsx-a11y": "^6.2.3",
-    "eslint-plugin-no-async-without-await": "^1.2.0",
-    "eslint-plugin-react": "^7.16.0",
-    "eslint-plugin-react-hooks": "^1.7.0",
-    "husky": "^3.0.9",
-    "lint-staged": "^9.4.2",
-    "parcel": "^1.12.4",
-    "react": "^16.11.0",
-    "sass": "^1.23.3",
-    "shx": "^0.3.2",
-    "terser": "^4.3.9",
-    "typescript": "^3.6.4"
+  "license": "MIT",
+  "bugs": {
+    "url": "https://github.com/gram-js/gramjs/issues"
   },
+  "homepage": "https://github.com/gram-js/gramjs#readme",
   "dependencies": {
-<<<<<<< HEAD
-<<<<<<< HEAD
+    "@babel/core": "^7.7.7",
+    "@babel/plugin-proposal-class-properties": "^7.7.0",
+    "@babel/preset-env": "^7.7.7",
     "aes-js": "^3.1.2",
+    "babel-loader": "^8.0.6",
+    "babel-register": "^6.26.0",
+    "better-sqlite3": "^5.4.3",
+    "chalk": "^2.4.2",
     "crc": "^3.8.0",
-    "crypto": "^1.0.1",
-    "emoji-regex": "^8.0.0",
-    "events": "^3.0.0",
+    "csv-parse": "^4.4.6",
+    "fast-csv": "^3.4.0",
+    "glob": "^7.1.4",
+    "mime-types": "^2.1.25",
+    "net": "^1.0.2",
     "node-gzip": "^1.1.2",
     "node-rsa": "^1.0.6",
-    "python-struct": "^1.1.2",
+    "python-struct": "^1.1.1",
+    "regenerator-runtime": "^0.13.3",
+    "sqlite3": "^4.1.1",
+    "stack-trace": "0.0.10",
     "string-format": "^2.0.0",
-    "td": "^0.3.2",
-=======
-=======
-    "async-mutex": "^0.1.4",
-<<<<<<< HEAD
->>>>>>> 1a0b5c54... GramJS: Fix images loading: add `async-mutex`; Fix signed LE ints; Bring back `readExactly`
-    "big-integer": "latest",
-=======
-    "big-integer": "painor/BigInteger.js",
->>>>>>> ddb2a0ed... GramJS: Fix downloading images of uncommon (small) size
-    "croppie": "^2.6.4",
-    "emoji-regex": "^8.0.0",
-    "events": "^3.0.0",
-<<<<<<< HEAD
->>>>>>> 1ab480fe... Gram JS: Remove deps; Fix Factorizator and remove leemon lib
-    "tdweb": "^1.5.0",
-    "websocket": "^1.0.30",
-<<<<<<< HEAD
-    "big-integer": "latest"
-=======
-    "pako": "latest"
->>>>>>> f8f1a405... GramJS : Remove zlib and other small fixes.
-=======
-    "pako": "latest",
-    "tdweb": "^1.5.0",
     "websocket": "^1.0.30"
->>>>>>> 1a0b5c54... GramJS: Fix images loading: add `async-mutex`; Fix signed LE ints; Bring back `readExactly`
+  },
+  "devDependencies": {
+    "@babel/plugin-transform-async-to-generator": "^7.7.0",
+    "babel-eslint": "^10.0.3",
+    "babel-jest": "^24.9.0",
+    "eslint": "^6.5.1",
+    "jest": "^24.9.0",
+    "webpack": "^4.41.2",
+    "webpack-cli": "^3.3.10",
+    "websocket-as-promised": "^0.10.1"
   }
 }

+ 0 - 13
src/@types/global.d.ts

@@ -1,13 +0,0 @@
-declare const process: { env: Record<string, string> };
-
-type AnyLiteral = Record<string, any>;
-type AnyClass = new (...args: any[]) => any;
-
-type Country = {
-  id: string;
-  name: string;
-  flag: string;
-  code: string;
-};
-
-declare module '*.png';

+ 0 - 64
src/api/gramjs/builders/chats.ts

@@ -1,64 +0,0 @@
-import { MTProto } from '../../../lib/gramjs';
-import { ApiChat } from '../../types';
-import { isPeerChat, isPeerUser } from './peers';
-import { buildApiPhotoLocations } from './common';
-
-export function buildApiChatFromDialog(dialog: MTProto.dialog, peerEntity: MTProto.user | MTProto.chat): ApiChat {
-  return {
-    id: getApiChatIdFromMtpPeer(dialog.peer),
-    type: {
-      '@type': getApiChatTypeFromMtpPeer(dialog.peer),
-      ...(isPeerUser(dialog.peer) && { user_id: dialog.peer.userId }),
-    },
-    title: getApiChatTitleFromMtpPeer(dialog.peer, peerEntity),
-    photo_locations: buildApiPhotoLocations(peerEntity),
-    last_read_outbox_message_id: dialog.readOutboxMaxId,
-    last_read_inbox_message_id: dialog.readInboxMaxId,
-    unread_count: dialog.unreadCount,
-    unread_mention_count: 0, // TODO
-    is_pinned: dialog.pinned || false,
-  };
-}
-
-export function getApiChatIdFromMtpPeer(peer: MTProto.Peer) {
-  if (isPeerUser(peer)) {
-    return peer.userId;
-  } else if (isPeerChat(peer)) {
-    return -peer.chatId;
-  } else {
-    return -peer.channelId;
-  }
-}
-
-export function getApiChatTypeFromMtpPeer(peer: MTProto.Peer) {
-  if (isPeerUser(peer)) {
-    return 'chatTypePrivate';
-  } else if (isPeerChat(peer)) {
-    return 'chatTypeBasicGroup';
-  } else {
-    // TODO Support channels, supergroups, etc.
-    return 'chatTypeBasicGroup';
-  }
-}
-
-export function getPeerKey(peer: MTProto.Peer) {
-  if (isPeerUser(peer)) {
-    return `user${peer.userId}`;
-  } else if (isPeerChat(peer)) {
-    return `chat${peer.chatId}`;
-  } else {
-    return `chat${peer.channelId}`;
-  }
-}
-
-export function getApiChatTitleFromMtpPeer(peer: MTProto.Peer, peerEntity: MTProto.user | MTProto.chat) {
-  if (isPeerUser(peer)) {
-    return getUserName(peerEntity as MTProto.user);
-  } else {
-    return (peerEntity as MTProto.chat).title;
-  }
-}
-
-function getUserName(user: MTProto.user) {
-  return user.firstName ? `${user.firstName}${user.lastName ? ` ${user.lastName}` : ''}` : undefined;
-}

+ 0 - 34
src/api/gramjs/builders/common.ts

@@ -1,34 +0,0 @@
-import { MTProto } from '../../../lib/gramjs';
-import { ApiFileLocation } from '../../types';
-
-export function buildApiPhotoLocations(entity: MTProto.user | MTProto.chat): {
-  small: ApiFileLocation;
-  big: ApiFileLocation;
-} | undefined {
-  if (!entity.photo) {
-    return undefined;
-  }
-
-  const { photoSmall, photoBig, dcId } = entity.photo as (MTProto.userProfilePhoto | MTProto.chatPhoto);
-
-  return {
-    small: {
-      ...photoSmall,
-      dcId,
-    },
-    big: {
-      ...photoBig,
-      dcId,
-    },
-  };
-}
-
-export function bytesToDataUri(bytes: Uint8Array, shouldOmitPrefix = false, mimeType: string = 'image/jpg') {
-  const prefix = shouldOmitPrefix ? '' : `data:${mimeType};base64,`;
-
-  return `${prefix}${btoa(
-    bytes.reduce((data, byte) => {
-      return data + String.fromCharCode(byte);
-    }, ''),
-  )}`;
-}

+ 0 - 208
src/api/gramjs/builders/messages.ts

@@ -1,208 +0,0 @@
-import { gramJsApi, MTProto } from '../../../lib/gramjs';
-import { strippedPhotoToJpg } from '../../../lib/gramjs/Utils';
-import {
-  ApiMessage, ApiMessageForwardInfo, ApiPhoto, ApiPhotoCachedSize, ApiPhotoSize, ApiSticker,
-} from '../../types';
-
-import { getApiChatIdFromMtpPeer } from './chats';
-import { isPeerUser } from './peers';
-import { bytesToDataUri } from './common';
-
-const ctors = gramJsApi.constructors;
-
-// TODO Maybe we do not need it.
-const DEFAULT_USER_ID = 0;
-
-export function buildApiMessage(mtpMessage: MTProto.Message): ApiMessage {
-  if (
-    !(mtpMessage instanceof ctors.Message)
-    && !(mtpMessage instanceof ctors.MessageService)) {
-    throw new Error('Not supported');
-  }
-
-  const isPrivateToMe = mtpMessage.out !== true && isPeerUser(mtpMessage.toId);
-  const chatId = isPrivateToMe
-    ? (mtpMessage.fromId || DEFAULT_USER_ID)
-    : getApiChatIdFromMtpPeer(mtpMessage.toId);
-
-  return buildApiMessageWithChatId(chatId, mtpMessage);
-}
-
-export function buildApiMessageFromShort(
-  mtpMessage: MTProto.updateShortMessage,
-): ApiMessage {
-  const chatId = getApiChatIdFromMtpPeer({ userId: mtpMessage.userId } as MTProto.Peer);
-
-  return buildApiMessageWithChatId(chatId, {
-    ...mtpMessage,
-    // TODO Current user ID needed here.
-    fromId: mtpMessage.out ? DEFAULT_USER_ID : mtpMessage.userId,
-  });
-}
-
-export function buildApiMessageFromShortChat(
-  mtpMessage: MTProto.updateShortChatMessage,
-): ApiMessage {
-  const chatId = getApiChatIdFromMtpPeer({ chatId: mtpMessage.chatId } as MTProto.Peer);
-
-  return buildApiMessageWithChatId(chatId, mtpMessage);
-}
-
-export function buildApiMessageWithChatId(
-  chatId: number,
-  mtpMessage: Pick<MTProto.message, (
-    'id' | 'out' | 'message' | 'date' | 'fromId' | 'fwdFrom' | 'replyToMsgId' | 'media'
-  )>,
-): ApiMessage {
-  const sticker = mtpMessage.media && buildSticker(mtpMessage.media);
-  const photo = mtpMessage.media && buildPhoto(mtpMessage.media);
-  const textContent = mtpMessage.message && {
-    '@type': 'formattedText' as 'formattedText',
-    text: mtpMessage.message,
-  };
-  const caption = textContent && photo ? textContent : null;
-  const text = textContent && !photo ? textContent : null;
-
-  return {
-    id: mtpMessage.id,
-    chat_id: chatId,
-    is_outgoing: Boolean(mtpMessage.out),
-    content: {
-      '@type': 'message',
-      ...(text && { text }),
-      ...(sticker && { sticker }),
-      ...(photo && { photo }),
-      ...(caption && { caption }),
-    },
-    date: mtpMessage.date,
-    sender_user_id: mtpMessage.fromId || DEFAULT_USER_ID,
-    reply_to_message_id: mtpMessage.replyToMsgId,
-    ...(mtpMessage.fwdFrom && { forward_info: buildApiMessageForwardInfo(mtpMessage.fwdFrom) }),
-  };
-}
-
-function buildApiMessageForwardInfo(fwdFrom: MTProto.messageFwdHeader): ApiMessageForwardInfo {
-  return {
-    '@type': 'messageForwardInfo',
-    from_chat_id: fwdFrom.fromId,
-    origin: {
-      '@type': 'messageForwardOriginUser',
-      // TODO Handle when empty `fromId`.
-      sender_user_id: fwdFrom.fromId,
-      // TODO @gramjs Not supported?
-      // sender_user_name: fwdFrom.fromName,
-    },
-  };
-}
-
-function buildSticker(media: MTProto.MessageMedia): ApiSticker | null {
-  if (
-    !(media instanceof ctors.MessageMediaDocument)
-    || !media.document
-    || !(media.document instanceof ctors.Document)
-  ) {
-    return null;
-  }
-
-  const stickerAttribute = media.document.attributes
-    .find((attr: any): attr is MTProto.documentAttributeSticker => (
-      attr instanceof ctors.DocumentAttributeSticker
-    ));
-
-  if (!stickerAttribute) {
-    return null;
-  }
-
-  const emoji = stickerAttribute.alt;
-  const isAnimated = media.document.mimeType === 'application/x-tgsticker';
-  const thumb = media.document.thumbs && media.document.thumbs.find((s: any) => s instanceof ctors.PhotoCachedSize);
-  const thumbnail = thumb && buildApiPhotoCachedSize(thumb as MTProto.photoCachedSize);
-  const { width, height } = thumbnail || {};
-
-  return {
-    '@type': 'sticker',
-    emoji,
-    is_animated: isAnimated,
-    width,
-    height,
-    thumbnail,
-  };
-}
-
-function buildPhoto(media: MTProto.MessageMedia): ApiPhoto | null {
-  if (!(media instanceof ctors.MessageMediaPhoto) || !media.photo || !(media.photo instanceof ctors.Photo)) {
-    return null;
-  }
-
-  const hasStickers = Boolean(media.photo.hasStickers);
-  const thumb = media.photo.sizes.find((s: any) => s instanceof ctors.PhotoStrippedSize);
-  const sizes = media.photo.sizes
-    .filter((s: any): s is MTProto.photoSize => s instanceof ctors.PhotoSize)
-    .map(buildApiPhotoSize);
-  const mSize = sizes.find((s: any) => s.type === 'm');
-  const { width, height } = mSize as ApiPhotoSize;
-  const minithumbnail: ApiPhoto['minithumbnail'] = thumb && {
-    '@type': 'minithumbnail',
-    data: bytesToDataUri(strippedPhotoToJpg((thumb as MTProto.photoStrippedSize).bytes as Buffer), true),
-    width,
-    height,
-  };
-
-  return {
-    '@type': 'photo',
-    has_stickers: hasStickers,
-    minithumbnail,
-    sizes,
-  };
-}
-
-function buildApiPhotoCachedSize(photoSize: MTProto.photoCachedSize): ApiPhotoCachedSize {
-  const {
-    w, h, type, bytes,
-  } = photoSize;
-  const dataUri = bytesToDataUri(strippedPhotoToJpg(bytes as Buffer));
-
-  return {
-    '@type': 'photoCachedSize',
-    width: w,
-    height: h,
-    type: type as ('m' | 'x' | 'y'),
-    dataUri,
-  };
-}
-
-function buildApiPhotoSize(photoSize: MTProto.photoSize): ApiPhotoSize {
-  const { w, h, type } = photoSize;
-
-  return {
-    '@type': 'photoSize',
-    width: w,
-    height: h,
-    type: type as ('m' | 'x' | 'y'),
-  };
-}
-
-// We only support 100000 local pending messages here and expect it will not interfere with real IDs.
-let localMessageCounter = -1;
-
-export function buildLocalMessage(chatId: number, text: string): ApiMessage {
-  const localId = localMessageCounter--;
-
-  return {
-    id: localId,
-    chat_id: chatId,
-    content: {
-      '@type': 'message',
-      text: {
-        '@type': 'formattedText',
-        text,
-      },
-    },
-    date: Math.round(Date.now() / 1000),
-    is_outgoing: true,
-    sender_user_id: DEFAULT_USER_ID, // TODO
-    sending_state: {
-      '@type': 'messageSendingStatePending',
-    },
-  };
-}

+ 0 - 13
src/api/gramjs/builders/peers.ts

@@ -1,13 +0,0 @@
-import { MTProto } from '../../../lib/gramjs';
-
-export function isPeerUser(peer: MTProto.Peer): peer is MTProto.peerUser {
-  return peer.hasOwnProperty('userId');
-}
-
-export function isPeerChat(peer: MTProto.Peer): peer is MTProto.peerChat {
-  return peer.hasOwnProperty('chatId');
-}
-
-export function isPeerChannel(peer: MTProto.Peer): peer is MTProto.peerChannel {
-  return peer.hasOwnProperty('channelId');
-}

+ 0 - 39
src/api/gramjs/builders/users.ts

@@ -1,39 +0,0 @@
-import { gramJsApi, MTProto } from '../../../lib/gramjs';
-import { ApiUser, ApiUserStatus } from '../../types';
-import { buildApiPhotoLocations } from './common';
-
-const ctors = gramJsApi.constructors;
-
-export function buildApiUser(mtpUser: MTProto.user): ApiUser {
-  return {
-    id: mtpUser.id,
-    type: {
-      // TODO Support other user types.
-      '@type': 'userTypeRegular',
-    },
-    first_name: mtpUser.firstName,
-    last_name: mtpUser.lastName,
-    username: mtpUser.username || '',
-    phone_number: mtpUser.phone || '',
-    profile_photo_locations: buildApiPhotoLocations(mtpUser),
-    status: buildApiUserStatus(mtpUser.status),
-  };
-}
-
-export function buildApiUserStatus(mtpStatus?: MTProto.UserStatus): ApiUserStatus | undefined {
-  if (!mtpStatus || mtpStatus instanceof ctors.UserStatusEmpty) {
-    return { '@type': 'userStatusEmpty' };
-  } else if (mtpStatus instanceof ctors.UserStatusOnline) {
-    return { '@type': 'userStatusOnline' };
-  } else if (mtpStatus instanceof ctors.UserStatusOffline) {
-    return { '@type': 'userStatusOffline', was_online: mtpStatus.wasOnline };
-  } else if (mtpStatus instanceof ctors.UserStatusRecently) {
-    return { '@type': 'userStatusRecently' };
-  } else if (mtpStatus instanceof ctors.UserStatusLastWeek) {
-    return { '@type': 'userStatusLastWeek' };
-  } else if (mtpStatus instanceof ctors.UserStatusLastMonth) {
-    return { '@type': 'userStatusLastMonth' };
-  }
-
-  return undefined;
-}

+ 0 - 212
src/api/gramjs/client.ts

@@ -1,212 +0,0 @@
-<<<<<<< HEAD
-<<<<<<< HEAD
-import {
-  TelegramClient, session, GramJsApi, MTProto,
-} from '../../lib/gramjs';
-=======
-import { TelegramClient, sessions, Api as GramJs } from '../../lib/gramjs';
->>>>>>> 42589b8b... GramJS: Add `LocalStorageSession` with keys and hashes for all DCs
-=======
-import {
-  TelegramClient, sessions, Api as GramJs, connection,
-} from '../../lib/gramjs';
->>>>>>> 48d2d818... Support reconnect and re-sync
-import { Logger as GramJsLogger } from '../../lib/gramjs/extensions';
-
-import { DEBUG } from '../../config';
-import {
-  onAuthReady, onRequestCode, onRequestPassword, onRequestPhoneNumber, onRequestRegistration,
-} from './connectors/auth';
-import { onGramJsUpdate } from './onGramJsUpdate';
-<<<<<<< HEAD
-<<<<<<< HEAD
-import localDb from './localDb';
-import { buildInputPeerPhotoFileLocation } from './inputHelpers';
-import { ApiFileLocation } from '../types';
-<<<<<<< HEAD
-=======
-=======
->>>>>>> f70d85dd... Gram JS: Replace generated `tl/*` contents with runtime logic; TypeScript typings
-
-GramJsLogger.getLogger().level = 'debug';
->>>>>>> dda7e47e... Fix images loading; Various Gram JS fixes; Refactor Gram JS Logger
-=======
-import queuedDownloadMedia from './connectors/media';
-
-GramJsLogger.setLevel(DEBUG ? 'debug' : 'warn');
-<<<<<<< HEAD
->>>>>>> ddb2a0ed... GramJS: Fix downloading images of uncommon (small) size
-=======
->>>>>>> 073c3e12... GramJS: Implement signup
-
-const gramJsUpdateEventBuilder = { build: (update: object) => update };
-
-let client: TelegramClient;
-let isConnected = false;
-
-export async function init(sessionId: string) {
-  const session = new sessions.LocalStorageSession(sessionId);
-  client = new TelegramClient(
-    session,
-    process.env.TELEGRAM_T_API_ID,
-    process.env.TELEGRAM_T_API_HASH,
-    { useWSS: true } as any,
-  );
-
-  client.addEventHandler(onGramJsUpdate, gramJsUpdateEventBuilder);
-  client.addEventHandler(onUpdate, gramJsUpdateEventBuilder);
-
-  try {
-    if (DEBUG) {
-      // eslint-disable-next-line no-console
-      console.log('[GramJs/worker] CONNECTING');
-    }
-
-    await client.start({
-      phone: onRequestPhoneNumber,
-      code: onRequestCode,
-      password: onRequestPassword,
-      firstAndLastNames: onRequestRegistration,
-    } as any);
-
-    const newSessionId = session.save();
-
-    if (DEBUG) {
-      // eslint-disable-next-line no-console
-      console.log('[GramJs/worker] CONNECTED as ', newSessionId);
-    }
-
-    onAuthReady(newSessionId);
-  } catch (err) {
-    if (DEBUG) {
-      // eslint-disable-next-line no-console
-      console.log('[GramJs/worker] CONNECTING ERROR', err);
-    }
-
-    throw err;
-  }
-}
-
-<<<<<<< HEAD
-<<<<<<< HEAD
-export async function invokeRequest<T extends InstanceType<GramJsApi.AnyRequest>>(request: T) {
-=======
-=======
-export async function destroy() {
-  await client.destroy();
-}
-
->>>>>>> 5143ac4c... Fixes for Log Out
-function onUpdate(update: any) {
-  if (update instanceof connection.UpdateConnectionState) {
-    isConnected = update.state === connection.UpdateConnectionState.states.connected;
-  }
-}
-
-export async function invokeRequest<T extends GramJs.AnyRequest>(request: T, shouldHandleUpdates = false) {
->>>>>>> 48d2d818... Support reconnect and re-sync
-  if (DEBUG) {
-    if (!isConnected) {
-      // eslint-disable-next-line no-console
-      console.warn(`[GramJs/client] INVOKE ${request.className} ERROR: Client is not connected`);
-      return undefined;
-    }
-
-    // eslint-disable-next-line no-console
-    console.log(`[GramJs/client] INVOKE ${request.className}`);
-  }
-
-  const result = await client.invoke(request);
-
-  if (DEBUG) {
-    // eslint-disable-next-line no-console
-    console.log(`[GramJs/client] INVOKE RESPONSE ${request.className}`, result);
-  }
-
-  return result;
-}
-
-<<<<<<< HEAD
-<<<<<<< HEAD
-export function downloadFile(id: number, fileLocation: ApiFileLocation, dcId?: number) {
-  return client.downloadFile(
-    buildInputPeerPhotoFileLocation({ id, fileLocation }),
-    true,
-=======
-export function downloadFile(chatOrUserId: number, fileLocation: ApiFileLocation) {
-  const { dcId, volumeId, localId } = fileLocation;
-
-  return client.downloadFile(
-    buildInputPeerPhotoFileLocation(chatOrUserId, volumeId, localId),
->>>>>>> dda7e47e... Fix images loading; Various Gram JS fixes; Refactor Gram JS Logger
-    { dcId },
-  );
-}
-
-<<<<<<< HEAD
-=======
-export function downloadMessageImage(message: MTP.message) {
-  return client.downloadMedia(message, { sizeType: 'x' });
-}
-
->>>>>>> dda7e47e... Fix images loading; Various Gram JS fixes; Refactor Gram JS Logger
-function postProcess(name: string, anyResult: any, args: AnyLiteral) {
-  switch (name) {
-    case 'GetDialogsRequest': {
-      const result: MTP.messages$Dialogs = anyResult;
-
-      if (!result || !result.dialogs) {
-        return;
-      }
-
-      result.users.forEach((user) => {
-        localDb.users[user.id] = user as MTP.user;
-      });
-
-      result.chats.forEach((chat) => {
-        localDb.chats[chat.id] = chat as MTP.chat | MTP.channel;
-      });
-
-      break;
-    }
-
-    case 'SendMessageRequest': {
-      const result = anyResult;
-
-      if (!result) {
-        return;
-      }
-
-      // TODO Support this.
-      if (result instanceof gramJsApi.UpdatesTooLong) {
-        return;
-      }
-
-      const updates = result.hasOwnProperty('updates') ? result.updates as MTP.Updates[] : [result as MTP.Updates];
-
-      const originRequest = {
-        name,
-        args,
-      };
-      updates.forEach((update) => onGramJsUpdate(update, originRequest));
-    }
-  }
-}
-=======
-export function downloadAvatar(entity: MTProto.chat | MTProto.user, isBig = false) {
-  return client.downloadProfilePhoto(entity, isBig);
-}
-
-<<<<<<< HEAD
-export function downloadMessageImage(message: MTProto.message) {
-  return client.downloadMedia(message, { sizeType: 'x' });
-=======
-export function downloadMedia(url: string) {
-  if (!isConnected) {
-    throw new Error('ERROR: Client is not connected');
-  }
-
-  return queuedDownloadMedia(client, url);
->>>>>>> 48d2d818... Support reconnect and re-sync
-}
->>>>>>> f70d85dd... Gram JS: Replace generated `tl/*` contents with runtime logic; TypeScript typings

+ 0 - 122
src/api/gramjs/connectors/auth.ts

@@ -1,122 +0,0 @@
-import { ApiUpdateAuthorizationState, ApiUpdateAuthorizationStateType } from '../../types';
-import { OnApiUpdate } from '../types';
-
-const authPromiseResolvers: {
-  resolvePhoneNumber: null | Function;
-  resolveCode: null | Function;
-  resolvePassword: null | Function;
-  resolveRegistration: null | Function;
-} = {
-  resolvePhoneNumber: null,
-  resolveCode: null,
-  resolvePassword: null,
-  resolveRegistration: null,
-};
-
-let onUpdate: OnApiUpdate;
-
-export function init(_onUpdate: OnApiUpdate) {
-  onUpdate = _onUpdate;
-}
-
-export function onRequestPhoneNumber() {
-  if (!onUpdate) {
-    return null;
-  }
-
-  onUpdate(buildAuthState('authorizationStateWaitPhoneNumber'));
-
-  return new Promise((resolve) => {
-    authPromiseResolvers.resolvePhoneNumber = resolve;
-  });
-}
-
-export function onRequestCode() {
-  if (!onUpdate) {
-    return null;
-  }
-
-  onUpdate(buildAuthState('authorizationStateWaitCode'));
-
-  return new Promise((resolve) => {
-    authPromiseResolvers.resolveCode = resolve;
-  });
-}
-
-export function onRequestPassword() {
-  if (!onUpdate) {
-    return null;
-  }
-
-  onUpdate(buildAuthState('authorizationStateWaitPassword'));
-
-  return new Promise((resolve) => {
-    authPromiseResolvers.resolvePassword = resolve;
-  });
-}
-
-export function onRequestRegistration() {
-  if (!onUpdate) {
-    return null;
-  }
-
-  onUpdate(buildAuthState('authorizationStateWaitRegistration'));
-
-  return new Promise((resolve) => {
-    authPromiseResolvers.resolveRegistration = resolve;
-  });
-}
-
-export function onAuthReady(sessionId: string) {
-  if (!onUpdate) {
-    return;
-  }
-
-  onUpdate({
-    ...buildAuthState('authorizationStateReady'),
-    session_id: sessionId,
-  });
-}
-
-export function buildAuthState(authState: ApiUpdateAuthorizationStateType): ApiUpdateAuthorizationState {
-  return {
-    '@type': 'updateAuthorizationState',
-    authorization_state: {
-      '@type': authState,
-    },
-  };
-}
-
-export function provideAuthPhoneNumber(phoneNumber: string) {
-  if (!authPromiseResolvers.resolvePhoneNumber) {
-    return;
-  }
-
-  authPromiseResolvers.resolvePhoneNumber(phoneNumber);
-}
-
-export function provideAuthCode(code: string) {
-  if (!authPromiseResolvers.resolveCode) {
-    return;
-  }
-
-  authPromiseResolvers.resolveCode(code);
-}
-
-export function provideAuthPassword(password: string) {
-  if (!authPromiseResolvers.resolvePassword) {
-    return;
-  }
-
-  authPromiseResolvers.resolvePassword(password);
-}
-
-export function provideAuthRegistration(registration: { firstName: string; lastName: string }) {
-  const { firstName, lastName } = registration;
-
-  if (!authPromiseResolvers.resolveRegistration) {
-    return;
-  }
-
-  authPromiseResolvers.resolveRegistration([firstName, lastName]);
-}

+ 0 - 129
src/api/gramjs/connectors/chats.ts

@@ -1,129 +0,0 @@
-import { gramJsApi, MTProto } from '../../../lib/gramjs';
-
-import { OnApiUpdate } from '../types';
-import { invokeRequest } from '../client';
-import { buildApiChatFromDialog, getApiChatIdFromMtpPeer, getPeerKey } from '../builders/chats';
-import { buildApiMessage } from '../builders/messages';
-import { buildApiUser } from '../builders/users';
-import { buildCollectionByKey } from '../../../util/iteratees';
-import { loadAvatar } from './files';
-import localDb from '../localDb';
-import { UNSUPPORTED_RESPONSE } from '../utils';
-
-const { constructors: ctors, requests } = gramJsApi;
-
-let onUpdate: OnApiUpdate;
-
-export function init(_onUpdate: OnApiUpdate) {
-  onUpdate = _onUpdate;
-}
-
-export async function fetchChats(
-  {
-    limit,
-    offsetDate,
-  }: {
-    limit: number;
-    offsetDate?: number;
-  },
-): Promise<{ chat_ids: number[] } | null> {
-  const result = await invokeRequest(new requests.messages.GetDialogsRequest({
-    offsetPeer: new ctors.InputPeerEmpty({}),
-    limit,
-    offsetDate,
-  }));
-
-<<<<<<< HEAD
-  if (!result || !(result instanceof ctors.messages.DialogsSlice) || !result.dialogs.length) {
-=======
-  // I had to change it to Dialogs to work for me.
-  if (!result || !(result instanceof GramJs.messages.DialogsSlice
-      || result instanceof GramJs.messages.Dialogs) || !result.dialogs.length) {
->>>>>>> 42589b8b... GramJS: Add `LocalStorageSession` with keys and hashes for all DCs
-    throw new Error(UNSUPPORTED_RESPONSE);
-  }
-
-  updateLocalDb(result);
-
-  const lastMessagesByChatId = buildCollectionByKey(result.messages.map(buildApiMessage), 'chat_id');
-  const peersByKey = preparePeers(result);
-  const chats = result.dialogs.map((dialog) => {
-    const peerEntity = peersByKey[getPeerKey(dialog.peer)];
-    const chat = buildApiChatFromDialog(dialog as MTProto.dialog, peerEntity);
-    chat.last_message = lastMessagesByChatId[chat.id];
-    return chat;
-  });
-
-  onUpdate({
-    '@type': 'chats',
-    chats,
-  });
-
-  const users = (result.users as MTProto.user[]).map(buildApiUser);
-  onUpdate({
-    '@type': 'users',
-    users,
-  });
-
-  loadAvatars(result);
-
-  const chatIds = chats.map((chat) => chat.id);
-
-  return {
-    chat_ids: chatIds,
-  };
-}
-
-function preparePeers(result: MTProto.messages_dialogsSlice) {
-  const store: Record<string, MTProto.chat | MTProto.user> = {};
-
-  result.chats.forEach((chat) => {
-    store[`chat${chat.id}`] = chat as MTProto.chat;
-  });
-
-  result.users.forEach((user) => {
-    store[`user${user.id}`] = user as MTProto.user;
-  });
-
-  return store;
-}
-
-function updateLocalDb(result: MTProto.messages_dialogsSlice) {
-  result.users.forEach((user) => {
-    localDb.users[user.id] = user as MTProto.user;
-  });
-
-  result.chats.forEach((chat) => {
-    localDb.chats[chat.id] = chat as MTProto.chat | MTProto.channel;
-  });
-}
-
-function loadAvatars(result: MTProto.messages_dialogsSlice) {
-  result.users.forEach((user) => {
-    loadAvatar(user as MTProto.user).then((dataUri) => {
-      if (!dataUri) {
-        return;
-      }
-
-      onUpdate({
-        '@type': 'updateAvatar',
-        chat_id: getApiChatIdFromMtpPeer({ userId: user.id } as MTProto.Peer),
-        data_uri: dataUri,
-      });
-    });
-  });
-
-  result.chats.forEach((chat) => {
-    loadAvatar(chat as MTProto.chat).then((dataUri) => {
-      if (!dataUri) {
-        return;
-      }
-
-      onUpdate({
-        '@type': 'updateAvatar',
-        chat_id: getApiChatIdFromMtpPeer({ chatId: chat.id } as MTProto.Peer),
-        data_uri: dataUri,
-      });
-    });
-  });
-}

+ 0 - 84
src/api/gramjs/connectors/files.ts

@@ -1,84 +0,0 @@
-<<<<<<< HEAD
-import { ApiFileLocation } from '../../types';
-
-<<<<<<< HEAD
-import { downloadFile } from '../client';
-=======
-import { downloadFile, downloadMessageImage } from '../client';
-=======
-import { MTProto } from '../../../lib/gramjs';
-import { downloadAvatar, downloadMessageImage } from '../client';
->>>>>>> f70d85dd... Gram JS: Replace generated `tl/*` contents with runtime logic; TypeScript typings
-import localDb from '../localDb';
-import { bytesToDataUri } from '../builders/common';
->>>>>>> dda7e47e... Fix images loading; Various Gram JS fixes; Refactor Gram JS Logger
-
-export function init() {
-}
-
-<<<<<<< HEAD
-export async function loadFile(id: any, fileLocation: ApiFileLocation): Promise<string | null> {
-  const fileBuffer = await downloadFile(id, fileLocation);
-=======
-export function loadAvatar(entity: MTProto.user | MTProto.chat): Promise<string | null> {
-  const entityId = entity.id;
-
-  if (!localDb.avatarRequests[entityId]) {
-    localDb.avatarRequests[entityId] = downloadAvatar(entity)
-      .then(
-        (fileBuffer: Buffer) => {
-          if (fileBuffer) {
-            return bytesToDataUri(fileBuffer);
-          } else {
-            delete localDb.avatarRequests[entityId];
-            return null;
-          }
-        },
-        () => {
-          delete localDb.avatarRequests[entityId];
-          return null;
-        },
-      );
-  }
->>>>>>> f70d85dd... Gram JS: Replace generated `tl/*` contents with runtime logic; TypeScript typings
-
-  return fileBuffer ? bytesToUrl(fileBuffer) : null;
-}
-
-<<<<<<< HEAD
-<<<<<<< HEAD
-function bytesToUrl(bytes: Uint8Array, mimeType?: string) {
-  if (!mimeType) {
-    mimeType = 'image/jpg';
-=======
-export function loadMessageMedia(message: MTP.message): Promise<string | null> {
-=======
-export function loadMessageMedia(message: MTProto.message): Promise<string | null> {
->>>>>>> f70d85dd... Gram JS: Replace generated `tl/*` contents with runtime logic; TypeScript typings
-  const messageId = message.id;
-
-  if (!localDb.mediaRequests[messageId]) {
-    localDb.mediaRequests[messageId] = downloadMessageImage(message)
-      .then(
-        (fileBuffer: Buffer) => {
-          if (fileBuffer) {
-            return bytesToDataUri(fileBuffer);
-          } else {
-            delete localDb.mediaRequests[messageId];
-            return null;
-          }
-        },
-        () => {
-          delete localDb.mediaRequests[messageId];
-          return null;
-        },
-      );
->>>>>>> dda7e47e... Fix images loading; Various Gram JS fixes; Refactor Gram JS Logger
-  }
-
-  return `data:${mimeType};base64,${btoa(
-    bytes.reduce((data, byte) => {
-      return data + String.fromCharCode(byte);
-    }, ''),
-  )}`;
-}

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно