Parcourir la source

Add friendly method getDialogs and iterDialogs

painor il y a 3 ans
Parent
commit
cf4adfb77b
100 fichiers modifiés avec 5618 ajouts et 4077 suppressions
  1. 2 0
      .prettierignore
  2. 1 0
      .prettierrc.json
  3. 9 4
      README.md
  4. 60 40
      __tests__/crypto/AES.spec.js
  5. 29 17
      __tests__/crypto/calcKey.spec.js
  6. 14 10
      __tests__/crypto/factorizator.spec.js
  7. 101 67
      __tests__/crypto/readBuffer.spec.js
  8. 103 96
      __tests__/extensions/HTML.spec.js
  9. 90 85
      __tests__/extensions/Markdown.spec.js
  10. 100 100
      __tests__/extensions/Scanner.spec.js
  11. 11 16
      babel.config.js
  12. 27 29
      examples/betterLogging.js
  13. 35 30
      examples/main.ts
  14. 16 18
      examples/mainjs.js
  15. 31 35
      examples/simpleLogin.html
  16. 62 61
      examples/simpleLogin.js
  17. 114 82
      gramjs/Helpers.ts
  18. 103 68
      gramjs/Password.ts
  19. 347 251
      gramjs/Utils.ts
  20. 194 114
      gramjs/client/TelegramClient.ts
  21. 158 104
      gramjs/client/auth.ts
  22. 26 15
      gramjs/client/bots.ts
  23. 26 17
      gramjs/client/buttons.ts
  24. 162 109
      gramjs/client/chats.ts
  25. 231 0
      gramjs/client/dialogs.ts
  26. 149 99
      gramjs/client/downloads.ts
  27. 40 26
      gramjs/client/messageParse.ts
  28. 241 136
      gramjs/client/messages.ts
  29. 94 82
      gramjs/client/telegramBaseClient.ts
  30. 69 41
      gramjs/client/updates.ts
  31. 194 172
      gramjs/client/uploads.ts
  32. 184 133
      gramjs/client/users.ts
  33. 30 16
      gramjs/crypto/AuthKey.ts
  34. 4 4
      gramjs/crypto/CTR.ts
  35. 17 20
      gramjs/crypto/Factorizator.ts
  36. 10 10
      gramjs/crypto/IGE.ts
  37. 60 22
      gramjs/crypto/RSA.ts
  38. 16 12
      gramjs/crypto/converters.ts
  39. 55 39
      gramjs/crypto/crypto.ts
  40. 13 13
      gramjs/crypto/words.ts
  41. 31 19
      gramjs/define.d.ts
  42. 24 25
      gramjs/entityCache.ts
  43. 39 34
      gramjs/errors/Common.ts
  44. 18 18
      gramjs/errors/RPCBaseErrors.ts
  45. 64 25
      gramjs/errors/RPCErrorList.ts
  46. 12 9
      gramjs/errors/index.ts
  47. 96 63
      gramjs/events/NewMessage.ts
  48. 9 10
      gramjs/events/Raw.ts
  49. 55 39
      gramjs/events/common.ts
  50. 2 2
      gramjs/events/index.ts
  51. 16 17
      gramjs/extensions/AsyncQueue.ts
  52. 43 42
      gramjs/extensions/BinaryReader.ts
  53. 3 3
      gramjs/extensions/BinaryWriter.ts
  54. 46 32
      gramjs/extensions/Logger.ts
  55. 45 31
      gramjs/extensions/MessagePacker.ts
  56. 29 32
      gramjs/extensions/PromisedNetSockets.ts
  57. 40 43
      gramjs/extensions/PromisedWebSockets.ts
  58. 71 66
      gramjs/extensions/html.ts
  59. 7 7
      gramjs/extensions/index.ts
  60. 12 13
      gramjs/extensions/markdown.ts
  61. 10 10
      gramjs/index.d.ts
  62. 10 16
      gramjs/index.ts
  63. 85 53
      gramjs/network/Authenticator.ts
  64. 28 26
      gramjs/network/MTProtoPlainSender.ts
  65. 210 161
      gramjs/network/MTProtoSender.ts
  66. 72 43
      gramjs/network/MTProtoState.ts
  67. 2 4
      gramjs/network/RequestState.ts
  68. 42 41
      gramjs/network/connection/Connection.ts
  69. 19 12
      gramjs/network/connection/TCPAbridged.ts
  70. 11 9
      gramjs/network/connection/TCPFull.ts
  71. 28 17
      gramjs/network/connection/TCPObfuscated.ts
  72. 4 4
      gramjs/network/connection/index.ts
  73. 8 9
      gramjs/network/index.ts
  74. 18 17
      gramjs/requestIter.ts
  75. 9 15
      gramjs/sessions/Abstract.ts
  76. 55 53
      gramjs/sessions/CacheApiSession.js
  77. 99 83
      gramjs/sessions/Memory.ts
  78. 7 8
      gramjs/sessions/StoreSession.ts
  79. 33 30
      gramjs/sessions/StringSession.ts
  80. 3 4
      gramjs/sessions/index.ts
  81. 5 6
      gramjs/tl/AllTLObjects.ts
  82. 12 10
      gramjs/tl/MTProtoRequest.ts
  83. 151 155
      gramjs/tl/api.d.ts
  84. 345 283
      gramjs/tl/api.js
  85. 14 14
      gramjs/tl/core/GZIPPacked.ts
  86. 6 8
      gramjs/tl/core/MessageContainer.ts
  87. 20 11
      gramjs/tl/core/RPCResult.ts
  88. 4 5
      gramjs/tl/core/TLMessage.ts
  89. 5 5
      gramjs/tl/core/index.ts
  90. 79 23
      gramjs/tl/custom/button.ts
  91. 33 23
      gramjs/tl/custom/chatGetter.ts
  92. 40 30
      gramjs/tl/custom/dialog.ts
  93. 15 9
      gramjs/tl/custom/draft.ts
  94. 21 16
      gramjs/tl/custom/file.ts
  95. 24 19
      gramjs/tl/custom/forward.ts
  96. 1 1
      gramjs/tl/custom/index.ts
  97. 31 18
      gramjs/tl/custom/inlineResult.ts
  98. 11 9
      gramjs/tl/custom/inlineResults.ts
  99. 123 73
      gramjs/tl/custom/message.ts
  100. 35 21
      gramjs/tl/custom/messageButton.ts

+ 2 - 0
.prettierignore

@@ -0,0 +1,2 @@
+dist
+node_modules

+ 1 - 0
.prettierrc.json

@@ -0,0 +1 @@
+{}

+ 9 - 4
README.md

@@ -1,26 +1,31 @@
 # 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.
+   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
-If you want to run in it in a browser just use webpack (a configuration file is already present). 
+
+If you want to run in it in a browser just use webpack (a configuration file is already present).
 The output will be in `browser` folder.
 In the browser gramjs will use localstorage to not regenerate api methods each run.
 check the `examples` folder for more info.
 
 ## Generate Session String
-To generate session string , simply run ` npx tgsession ` and provide required details 
+
+To generate session string , simply run `npx tgsession` and provide required details
 
 ## 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/
 

+ 60 - 40
__tests__/crypto/AES.spec.js

@@ -1,41 +1,61 @@
-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])
-    })
-})
+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]);
+    }
+  );
+});

+ 29 - 17
__tests__/crypto/calcKey.spec.js

@@ -1,19 +1,31 @@
-const MTProtoState = require('../../gramjs/network/MTProtoState')
+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)
+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])
-    })
-})
+      expect([aesKey, aesIv]).toEqual([key, iv]);
+    }
+  );
+});

+ 14 - 10
__tests__/crypto/factorizator.spec.js

@@ -1,11 +1,15 @@
-const Factorizator = require('../../gramjs/crypto/Factorizator')
+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])
-    })
-})
+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]);
+  });
+});

+ 101 - 67
__tests__/crypto/readBuffer.spec.js

@@ -1,70 +1,104 @@
-const Helpers = require('../../gramjs/Helpers')
+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 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 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)
-    })
-})
+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
+    );
+  });
+});

+ 103 - 96
__tests__/extensions/HTML.spec.js

@@ -1,107 +1,114 @@
-const { HTMLParser } = require('../../gramjs/extensions/HTML')
-const types = require('../../gramjs/tl/types')
+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("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)
-        })
+  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 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 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 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 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 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 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)
-        })
-    })
+    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)
-        })
+  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)
-        })
-    })
-})
+    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);
+    });
+  });
+});

+ 90 - 85
__tests__/extensions/Markdown.spec.js

@@ -1,95 +1,100 @@
-const { MarkdownParser } = require('../../gramjs/extensions/Markdown')
-const types = require('../../gramjs/tl/types')
+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("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)
-        })
+  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 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 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 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 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 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 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)
-        })
-    })
+    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)
-        })
-    })
-})
+  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 - 100
__tests__/extensions/Scanner.spec.js

@@ -1,100 +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)
-        })
-    })
-})
+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);
+    });
+  });
+});

+ 11 - 16
babel.config.js

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

+ 27 - 29
examples/betterLogging.js

@@ -1,38 +1,36 @@
 rewireLoggingToElement(
-    () => document.getElementById('log'),
-    () => document.getElementById('log-container'),
-    true,
-)
+  () => document.getElementById("log"),
+  () => document.getElementById("log-container"),
+  true
+);
 
 function rewireLoggingToElement(eleLocator, eleOverflowLocator, autoScroll) {
-    fixLoggingFunc('log')
+  fixLoggingFunc("log");
 
-    function fixLoggingFunc(name) {
-        console.old = console.log
-        console.log = function(...arg) {
-            const output = produceOutput(name, arg)
-            const eleLog = eleLocator()
+  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 =
+      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>'
-            }
-
+          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;'
-    
-    }
+  function produceOutput(name, args) {
+    arg = args[0].replace("%c", "");
+    return '<span style="' + args[1] + '">' + arg + "</span>&nbsp;";
+  }
 }

+ 35 - 30
examples/main.ts

@@ -1,42 +1,47 @@
-import {Logger} from "telegram/extensions";
-import {TelegramClient} from "telegram";
-import {StringSession} from "telegram/sessions";
-import {NewMessage} from "telegram/events";
-import {NewMessageEvent} from "telegram/events/NewMessage";
-import {Message} from 'telegram/tl/custom/message';
+import { Logger } from "telegram/extensions";
+import { TelegramClient } from "telegram";
+import { StringSession } from "telegram/sessions";
+import { NewMessage } from "telegram/events";
+import { NewMessageEvent } from "telegram/events/NewMessage";
+import { Message } from "telegram/tl/custom/message";
 
-const apiId = ;
-const apiHash = '';
-const stringSession = '';
+const apiId = 0;
+const apiHash = "";
+const stringSession = "";
 
 async function eventPrint(event: NewMessageEvent) {
-    const message = event.message as Message;
+  const message = event.message as Message;
 
-    // Checks if it's a private message (from user or bot)
-    if (event.isPrivate){
-        // prints sender id
-        console.log(message.senderId);
-        // read message
-        if (message.text=="hello"){
-            const sender = await message.getSender();
-            console.log("sender is",sender);
-            await client.sendMessage(sender,{
-                message:`hi your id is ${message.senderId}`
-            });
-        }
+  // Checks if it's a private message (from user or bot)
+  if (event.isPrivate) {
+    // prints sender id
+    console.log(message.senderId);
+    // read message
+    if (message.text == "hello") {
+      const sender = await message.getSender();
+      console.log("sender is", sender);
+      await client.sendMessage(sender, {
+        message: `hi your id is ${message.senderId}`,
+      });
     }
+  }
 }
-const client = new TelegramClient(new StringSession(stringSession), apiId, apiHash, {connectionRetries: 5});
+const client = new TelegramClient(
+  new StringSession(stringSession),
+  apiId,
+  apiHash,
+  { connectionRetries: 5 }
+);
 
 (async () => {
-    Logger.setLevel("debug");
-    console.log('Loading interactive example...');
-    await client.start({
-        botAuthToken: ""
-    });
+  Logger.setLevel("debug");
+  console.log("Loading interactive example...");
+  await client.start({
+    botAuthToken: "",
+  });
 
-    console.log(await client.getEntity("me"));
-    console.log(client.session.save())
+  console.log(await client.getEntity("me"));
+  console.log(client.session.save());
 })();
 
 // adds an event handler for new messages

+ 16 - 18
examples/mainjs.js

@@ -1,20 +1,18 @@
-
-const { TelegramClient } = require('telegram/dist')
-const { StringSession } = require('telegram/dist/sessions');
+const { TelegramClient } = require("telegram/dist");
+const { StringSession } = require("telegram/dist/sessions");
 
 (async () => {
-    console.log('Loading interactive example...')
-    const apiId = -1 // put your api id here [for example 123456789]
-    const apiHash = '' // put your api hash here [for example '123456abcfghe']
-    const client = new TelegramClient(new StringSession(''), apiId, apiHash, {
-        connectionRetries: 3,
-    })
-    await client.start({
-        botAuthToken: 'YOUR BOT TOKEN',
-
-    })
-    console.log('You should now be connected.')
-    console.log(await client.getMe())
-    // USE THIS STRING TO AVOID RELOGGING EACH TIME
-    console.log(await client.session.save())
-})()
+  console.log("Loading interactive example...");
+  const apiId = -1; // put your api id here [for example 123456789]
+  const apiHash = ""; // put your api hash here [for example '123456abcfghe']
+  const client = new TelegramClient(new StringSession(""), apiId, apiHash, {
+    connectionRetries: 3,
+  });
+  await client.start({
+    botAuthToken: "YOUR BOT TOKEN",
+  });
+  console.log("You should now be connected.");
+  console.log(await client.getMe());
+  // USE THIS STRING TO AVOID RELOGGING EACH TIME
+  console.log(await client.session.save());
+})();

+ 31 - 35
examples/simpleLogin.html

@@ -1,47 +1,43 @@
 <!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">
+  <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;
-        }
+      #log-container {
+        overflow: auto;
+        height: 150px;
+        width: 20%;
+        resize: both;
+      }
     </style>
-</head>
+  </head>
 
-<body>
-<form>
-    <div id="phoneDiv">
-        <label>phone number</label> <input disabled type="text" id="phone">
+  <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">
+      </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">
+      </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">
+      </div>
+      <div id="log-container">
         <pre id="log"></pre>
-    </div>
-
-
-</form>
-</body>
-<script src="../browser/gramjs.js"></script>
-<!--Loading the library-->
-<script src="betterLogging.js"></script>
-<!--beautifies the ouput (this rewrites console.log)-->
-
-<script src="simpleLogin.js"></script>
+      </div>
+    </form>
+  </body>
+  <script src="../browser/gramjs.js"></script>
+  <!--Loading the library-->
+  <script src="betterLogging.js"></script>
+  <!--beautifies the ouput (this rewrites console.log)-->
 
+  <script src="simpleLogin.js"></script>
 </html>

+ 62 - 61
examples/simpleLogin.js

@@ -1,78 +1,79 @@
-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')
+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)
-        })
-    })
+  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
+  passDiv.style.visibility = "visible";
 
-            resolve(pass.value)
-            alert('welcome')
+  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)
-        })
-    })
+  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.sessions;
+const apiId = 1; // put your api id here [for example 123456789]
+const apiHash = "1  "; // put your api hash here [for example '123456abcfghe']
 
-const { TelegramClient } = gramjs
-const { StringSession } = gramjs.sessions
-const apiId = 1 // put your api id here [for example 123456789]
-const apiHash = "1  " // put your api hash here [for example '123456abcfghe']
-
-const client = new TelegramClient(new StringSession(''), apiId, apiHash, {
-    connectionRetries: 3,
-}) // you can pass a string session here from previous logins.
+const client = new TelegramClient(new StringSession(""), apiId, apiHash, {
+  connectionRetries: 3,
+}); // 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({
+client
+  .start({
     botAuthToken: phoneCallback,
-}).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')
-
-})
-
-
-
+  })
+  .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"
+    );
+  });

+ 114 - 82
gramjs/Helpers.ts

@@ -1,11 +1,10 @@
-import {isNode} from 'browser-or-node';
+import { isNode } from "browser-or-node";
 import bigInt from "big-integer";
-import type {EntityLike} from "./define";
-import type {Api} from "./tl";
-
+import type { EntityLike } from "./define";
+import type { Api } from "./tl";
 
 export const IS_NODE = isNode;
-const crypto = require(isNode ? 'crypto' : './crypto/crypto');
+const crypto = require(isNode ? "crypto" : "./crypto/crypto");
 
 /**
  * converts a buffer to big int
@@ -14,17 +13,20 @@ const crypto = require(isNode ? 'crypto' : './crypto/crypto');
  * @param signed
  * @returns {bigInt.BigInteger}
  */
-export function readBigIntFromBuffer(buffer: Buffer, little = true, signed = false): bigInt.BigInteger {
+export function readBigIntFromBuffer(
+    buffer: Buffer,
+    little = true,
+    signed = false
+): bigInt.BigInteger {
     let randBuffer = Buffer.from(buffer);
     const bytesNumber = randBuffer.length;
     if (little) {
-        randBuffer = randBuffer.reverse()
+        randBuffer = randBuffer.reverse();
     }
-    let bigIntVar = bigInt(randBuffer.toString('hex'), 16) as bigInt.BigInteger;
+    let bigIntVar = bigInt(randBuffer.toString("hex"), 16) as bigInt.BigInteger;
 
     if (signed && Math.floor(bigIntVar.toString(2).length / 8) >= bytesNumber) {
-        bigIntVar = bigIntVar.subtract(bigInt(2)
-            .pow(bigInt(bytesNumber * 8)));
+        bigIntVar = bigIntVar.subtract(bigInt(2).pow(bigInt(bytesNumber * 8)));
     }
     return bigIntVar;
 }
@@ -34,13 +36,17 @@ export function generateRandomBigInt() {
 }
 
 export function escapeRegex(string: string) {
-    return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
+    return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
 }
 
 /**
  * Helper to find if a given object is an array (or similar)
  */
-export const isArrayLike = (<T>(x: any): x is ArrayLike<T> => x && typeof x.length === 'number' && typeof x !== 'function' && typeof x !== 'string');
+export const isArrayLike = <T>(x: any): x is ArrayLike<T> =>
+    x &&
+    typeof x.length === "number" &&
+    typeof x !== "function" &&
+    typeof x !== "string";
 
 /*
 export function addSurrogate(text: string) {
@@ -66,7 +72,10 @@ export function addSurrogate(text: string) {
  * @param number
  * @returns {Buffer}
  */
-export function toSignedLittleBuffer(big: bigInt.BigInteger, number = 8): Buffer {
+export function toSignedLittleBuffer(
+    big: bigInt.BigInteger,
+    number = 8
+): Buffer {
     const bigNumber = bigInt(big);
     const byteArray = [];
     for (let i = 0; i < number; i++) {
@@ -76,7 +85,6 @@ export function toSignedLittleBuffer(big: bigInt.BigInteger, number = 8): Buffer
     return Buffer.from(byteArray as unknown as number[]);
 }
 
-
 /**
  * converts a big int to a buffer
  * @param bigIntVar {BigInteger}
@@ -85,16 +93,21 @@ export function toSignedLittleBuffer(big: bigInt.BigInteger, number = 8): Buffer
  * @param signed
  * @returns {Buffer}
  */
-export function readBufferFromBigInt(bigIntVar: bigInt.BigInteger, bytesNumber: number, little = true, signed = false): Buffer {
+export function readBufferFromBigInt(
+    bigIntVar: bigInt.BigInteger,
+    bytesNumber: number,
+    little = true,
+    signed = false
+): Buffer {
     bigIntVar = bigInt(bigIntVar);
     const bitLength = bigIntVar.bitLength().toJSNumber();
 
     const bytes = Math.ceil(bitLength / 8);
     if (bytesNumber < bytes) {
-        throw new Error('OverflowError: int too big to convert')
+        throw new Error("OverflowError: int too big to convert");
     }
     if (!signed && bigIntVar.lesser(BigInt(0))) {
-        throw new Error('Cannot convert to unsigned')
+        throw new Error("Cannot convert to unsigned");
     }
     let below = false;
     if (bigIntVar.lesser(BigInt(0))) {
@@ -102,9 +115,8 @@ export function readBufferFromBigInt(bigIntVar: bigInt.BigInteger, bytesNumber:
         bigIntVar = bigIntVar.abs();
     }
 
-    const hex = bigIntVar.toString(16)
-        .padStart(bytesNumber * 2, '0');
-    let littleBuffer = Buffer.from(hex, 'hex');
+    const hex = bigIntVar.toString(16).padStart(bytesNumber * 2, "0");
+    let littleBuffer = Buffer.from(hex, "hex");
     if (little) {
         littleBuffer = littleBuffer.reverse();
     }
@@ -124,10 +136,11 @@ export function readBufferFromBigInt(bigIntVar: bigInt.BigInteger, bytesNumber:
                     littleBuffer[i] -= 1;
                     reminder = false;
                 }
-                littleBuffer[i] = 255 - littleBuffer[i]
+                littleBuffer[i] = 255 - littleBuffer[i];
             }
         } else {
-            littleBuffer[littleBuffer.length - 1] = 256 - littleBuffer[littleBuffer.length - 1];
+            littleBuffer[littleBuffer.length - 1] =
+                256 - littleBuffer[littleBuffer.length - 1];
             for (let i = 0; i < littleBuffer.length - 1; i++) {
                 littleBuffer[i] = 255 - littleBuffer[i];
             }
@@ -141,7 +154,7 @@ export function readBufferFromBigInt(bigIntVar: bigInt.BigInteger, bytesNumber:
  * @returns {BigInteger}
  */
 export function generateRandomLong(signed = true) {
-    return readBigIntFromBuffer(generateRandomBytes(8), true, signed)
+    return readBigIntFromBuffer(generateRandomBytes(8), true, signed);
 }
 
 /**
@@ -151,7 +164,7 @@ export function generateRandomLong(signed = true) {
  * @returns {number}
  */
 export function mod(n: number, m: number) {
-    return ((n % m) + m) % m
+    return ((n % m) + m) % m;
 }
 
 /**
@@ -160,8 +173,11 @@ export function mod(n: number, m: number) {
  * @param m {BigInt}
  * @returns {BigInt}
  */
-export function bigIntMod(n: bigInt.BigInteger, m: bigInt.BigInteger): bigInt.BigInteger {
-    return ((n.remainder(m)).add(m)).remainder(m)
+export function bigIntMod(
+    n: bigInt.BigInteger,
+    m: bigInt.BigInteger
+): bigInt.BigInteger {
+    return n.remainder(m).add(m).remainder(m);
 }
 
 /**
@@ -170,7 +186,7 @@ export function bigIntMod(n: bigInt.BigInteger, m: bigInt.BigInteger): bigInt.Bi
  * @returns {Buffer}
  */
 export function generateRandomBytes(count: number) {
-    return Buffer.from(crypto.randomBytes(count))
+    return Buffer.from(crypto.randomBytes(count));
 }
 
 /**
@@ -206,7 +222,7 @@ export function stripText(text: string, entities: Api.TypeMessageEntity[]) {
     }
     while (text && text[text.length - 1].trim() === "") {
         const e = entities[entities.length - 1];
-        if ((e.offset + e.length) == text.length) {
+        if (e.offset + e.length == text.length) {
             if (e.length == 1) {
                 entities.pop();
                 if (!entities.length) {
@@ -239,15 +255,16 @@ export function stripText(text: string, entities: Api.TypeMessageEntity[]) {
     return text;
 }
 
-
 /**
  * Generates the key data corresponding to the given nonces
  * @param serverNonceBigInt
  * @param newNonceBigInt
  * @returns {{key: Buffer, iv: Buffer}}
  */
-export async function generateKeyDataFromNonce(serverNonceBigInt: bigInt.BigInteger, newNonceBigInt: bigInt.BigInteger) {
-
+export async function generateKeyDataFromNonce(
+    serverNonceBigInt: bigInt.BigInteger,
+    newNonceBigInt: bigInt.BigInteger
+) {
     const serverNonce = toSignedLittleBuffer(serverNonceBigInt, 16);
     const newNonce = toSignedLittleBuffer(newNonceBigInt, 32);
     const [hash1, hash2, hash3] = await Promise.all([
@@ -256,20 +273,24 @@ export async function generateKeyDataFromNonce(serverNonceBigInt: bigInt.BigInte
         sha1(Buffer.concat([newNonce, newNonce])),
     ]);
     const keyBuffer = Buffer.concat([hash1, hash2.slice(0, 12)]);
-    const ivBuffer = Buffer.concat([hash2.slice(12, 20), hash3, newNonce.slice(0, 4)]);
+    const ivBuffer = Buffer.concat([
+        hash2.slice(12, 20),
+        hash3,
+        newNonce.slice(0, 4),
+    ]);
     return {
         key: keyBuffer,
         iv: ivBuffer,
-    }
+    };
 }
 
 export function convertToLittle(buf: Buffer) {
     const correct = Buffer.alloc(buf.length * 4);
 
     for (let i = 0; i < buf.length; i++) {
-        correct.writeUInt32BE(buf[i], i * 4)
+        correct.writeUInt32BE(buf[i], i * 4);
     }
-    return correct
+    return correct;
 }
 
 /**
@@ -278,21 +299,20 @@ export function convertToLittle(buf: Buffer) {
  * @returns {Promise}
  */
 export function sha1(data: Buffer): Promise<Buffer> {
-    const shaSum = crypto.createHash('sha1');
+    const shaSum = crypto.createHash("sha1");
     shaSum.update(data);
-    return shaSum.digest()
+    return shaSum.digest();
 }
 
-
 /**
  * Calculates the SHA256 digest for the given data
  * @param data
  * @returns {Promise}
  */
 export function sha256(data: Buffer): Promise<Buffer> {
-    const shaSum = crypto.createHash('sha256');
+    const shaSum = crypto.createHash("sha256");
     shaSum.update(data);
-    return shaSum.digest()
+    return shaSum.digest();
 }
 
 /**
@@ -302,7 +322,11 @@ export function sha256(data: Buffer): Promise<Buffer> {
  * @param n
  * @returns {bigInt.BigInteger}
  */
-export function modExp(a: bigInt.BigInteger, b: bigInt.BigInteger, n: bigInt.BigInteger): bigInt.BigInteger {
+export function modExp(
+    a: bigInt.BigInteger,
+    b: bigInt.BigInteger,
+    n: bigInt.BigInteger
+): bigInt.BigInteger {
     a = a.remainder(n);
     let result = bigInt.one;
     let x = a;
@@ -311,25 +335,32 @@ export function modExp(a: bigInt.BigInteger, b: bigInt.BigInteger, n: bigInt.Big
         b = b.divide(BigInt(2));
         if (leastSignificantBit.eq(bigInt.one)) {
             result = result.multiply(x);
-            result = result.remainder(n)
+            result = result.remainder(n);
         }
         x = x.multiply(x);
-        x = x.remainder(n)
+        x = x.remainder(n);
     }
-    return result
+    return result;
 }
 
-
 /**
  * Gets the arbitrary-length byte array corresponding to the given integer
  * @param integer {number,BigInteger}
  * @param signed {boolean}
  * @returns {Buffer}
  */
-export function getByteArray(integer: bigInt.BigInteger | number, signed = false) {
+export function getByteArray(
+    integer: bigInt.BigInteger | number,
+    signed = false
+) {
     const bits = integer.toString(2).length;
     const byteLength = Math.floor((bits + 8 - 1) / 8);
-    return readBufferFromBigInt(typeof integer == "number" ? bigInt(integer) : integer, byteLength, false, signed);
+    return readBufferFromBigInt(
+        typeof integer == "number" ? bigInt(integer) : integer,
+        byteLength,
+        false,
+        signed
+    );
 }
 
 /**
@@ -369,7 +400,8 @@ export function getRandomInt(min: number, max: number) {
  * @param ms time in milliseconds
  * @returns {Promise}
  */
-export const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
+export const sleep = (ms: number) =>
+    new Promise((resolve) => setTimeout(resolve, ms));
 
 /**
  * Checks if the obj is an array
@@ -384,29 +416,29 @@ function makeCRCTable() {
     for (let n = 0; n < 256; n++) {
         c = n;
         for (let k = 0; k < 8; k++) {
-            c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1))
+            c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
         }
-        crcTable[n] = c
+        crcTable[n] = c;
     }
-    return crcTable
+    return crcTable;
 }
 
 let crcTable: number[] | undefined = undefined;
 
 export function crc32(buf: Buffer | string) {
     if (!crcTable) {
-        crcTable = makeCRCTable()
+        crcTable = makeCRCTable();
     }
     if (!Buffer.isBuffer(buf)) {
-        buf = Buffer.from(buf)
+        buf = Buffer.from(buf);
     }
     let crc = -1;
 
     for (let index = 0; index < buf.length; index++) {
         const byte = buf[index];
-        crc = crcTable[(crc ^ byte) & 0xff] ^ (crc >>> 8)
+        crc = crcTable[(crc ^ byte) & 0xff] ^ (crc >>> 8);
     }
-    return (crc ^ (-1)) >>> 0
+    return (crc ^ -1) >>> 0;
 }
 
 export class TotalList<T> extends Array<T> {
@@ -414,11 +446,10 @@ export class TotalList<T> extends Array<T> {
 
     constructor() {
         super();
-        this.total = 0
+        this.total = 0;
     }
 }
 
-
 export const _EntityType = {
     USER: 0,
     CHAT: 1,
@@ -427,34 +458,35 @@ export const _EntityType = {
 Object.freeze(_EntityType);
 
 export function _entityType(entity: EntityLike) {
-
-    if (typeof entity !== 'object' || !('SUBCLASS_OF_ID' in entity)) {
-        throw new Error(`${entity} is not a TLObject, cannot determine entity type`)
+    if (typeof entity !== "object" || !("SUBCLASS_OF_ID" in entity)) {
+        throw new Error(
+            `${entity} is not a TLObject, cannot determine entity type`
+        );
     }
-    if (![
-        0x2d45687,  // crc32('Peer')
-        0xc91c90b6,  // crc32('InputPeer')
-        0xe669bf46,  // crc32('InputUser')
-        0x40f202fd,  // crc32('InputChannel')
-        0x2da17977,  // crc32('User')
-        0xc5af5d94,  // crc32('Chat')
-        0x1f4661b9,  // crc32('UserFull')
-        0xd49a2697,  // crc32('ChatFull')
-    ].includes(entity.SUBCLASS_OF_ID)) {
-        throw new Error(`${entity} does not have any entity type`)
+    if (
+        ![
+            0x2d45687, // crc32('Peer')
+            0xc91c90b6, // crc32('InputPeer')
+            0xe669bf46, // crc32('InputUser')
+            0x40f202fd, // crc32('InputChannel')
+            0x2da17977, // crc32('User')
+            0xc5af5d94, // crc32('Chat')
+            0x1f4661b9, // crc32('UserFull')
+            0xd49a2697, // crc32('ChatFull')
+        ].includes(entity.SUBCLASS_OF_ID)
+    ) {
+        throw new Error(`${entity} does not have any entity type`);
     }
     const name = entity.className;
-    if (name.includes('User')) {
-        return _EntityType.USER
-    } else if (name.includes('Chat')) {
-        return _EntityType.CHAT
-    } else if (name.includes('Channel')) {
-        return _EntityType.CHANNEL
-    } else if (name.includes('Self')) {
-        return _EntityType.USER
+    if (name.includes("User")) {
+        return _EntityType.USER;
+    } else if (name.includes("Chat")) {
+        return _EntityType.CHAT;
+    } else if (name.includes("Channel")) {
+        return _EntityType.CHANNEL;
+    } else if (name.includes("Self")) {
+        return _EntityType.USER;
     }
     // 'Empty' in name or not found, we don't care, not a valid entity.
-    throw new Error(`${entity} does not have any entity type`)
-
+    throw new Error(`${entity} does not have any entity type`);
 }
-

+ 103 - 68
gramjs/Password.ts

@@ -1,8 +1,15 @@
-import {Api} from "./tl";
-import {bigIntMod, generateRandomBytes, modExp, readBigIntFromBuffer, readBufferFromBigInt, sha256} from "./Helpers";
-import bigInt from 'big-integer';
-import {isBrowser, isNode} from "browser-or-node";
-const crypto = require(isNode ? 'crypto' : './crypto/crypto');
+import { Api } from "./tl";
+import {
+    bigIntMod,
+    generateRandomBytes,
+    modExp,
+    readBigIntFromBuffer,
+    readBufferFromBigInt,
+    sha256,
+} from "./Helpers";
+import bigInt from "big-integer";
+import { isBrowser, isNode } from "browser-or-node";
+const crypto = require(isNode ? "crypto" : "./crypto/crypto");
 
 const SIZE_FOR_HASH = 256;
 
@@ -65,29 +72,35 @@ function checkPrimeAndGoodCheck(prime, g) {
  */
 function checkPrimeAndGood(primeBytes: Buffer, g: number) {
     const goodPrime = Buffer.from([
-        0xC7, 0x1C, 0xAE, 0xB9, 0xC6, 0xB1, 0xC9, 0x04, 0x8E, 0x6C, 0x52, 0x2F, 0x70, 0xF1, 0x3F, 0x73,
-        0x98, 0x0D, 0x40, 0x23, 0x8E, 0x3E, 0x21, 0xC1, 0x49, 0x34, 0xD0, 0x37, 0x56, 0x3D, 0x93, 0x0F,
-        0x48, 0x19, 0x8A, 0x0A, 0xA7, 0xC1, 0x40, 0x58, 0x22, 0x94, 0x93, 0xD2, 0x25, 0x30, 0xF4, 0xDB,
-        0xFA, 0x33, 0x6F, 0x6E, 0x0A, 0xC9, 0x25, 0x13, 0x95, 0x43, 0xAE, 0xD4, 0x4C, 0xCE, 0x7C, 0x37,
-        0x20, 0xFD, 0x51, 0xF6, 0x94, 0x58, 0x70, 0x5A, 0xC6, 0x8C, 0xD4, 0xFE, 0x6B, 0x6B, 0x13, 0xAB,
-        0xDC, 0x97, 0x46, 0x51, 0x29, 0x69, 0x32, 0x84, 0x54, 0xF1, 0x8F, 0xAF, 0x8C, 0x59, 0x5F, 0x64,
-        0x24, 0x77, 0xFE, 0x96, 0xBB, 0x2A, 0x94, 0x1D, 0x5B, 0xCD, 0x1D, 0x4A, 0xC8, 0xCC, 0x49, 0x88,
-        0x07, 0x08, 0xFA, 0x9B, 0x37, 0x8E, 0x3C, 0x4F, 0x3A, 0x90, 0x60, 0xBE, 0xE6, 0x7C, 0xF9, 0xA4,
-        0xA4, 0xA6, 0x95, 0x81, 0x10, 0x51, 0x90, 0x7E, 0x16, 0x27, 0x53, 0xB5, 0x6B, 0x0F, 0x6B, 0x41,
-        0x0D, 0xBA, 0x74, 0xD8, 0xA8, 0x4B, 0x2A, 0x14, 0xB3, 0x14, 0x4E, 0x0E, 0xF1, 0x28, 0x47, 0x54,
-        0xFD, 0x17, 0xED, 0x95, 0x0D, 0x59, 0x65, 0xB4, 0xB9, 0xDD, 0x46, 0x58, 0x2D, 0xB1, 0x17, 0x8D,
-        0x16, 0x9C, 0x6B, 0xC4, 0x65, 0xB0, 0xD6, 0xFF, 0x9C, 0xA3, 0x92, 0x8F, 0xEF, 0x5B, 0x9A, 0xE4,
-        0xE4, 0x18, 0xFC, 0x15, 0xE8, 0x3E, 0xBE, 0xA0, 0xF8, 0x7F, 0xA9, 0xFF, 0x5E, 0xED, 0x70, 0x05,
-        0x0D, 0xED, 0x28, 0x49, 0xF4, 0x7B, 0xF9, 0x59, 0xD9, 0x56, 0x85, 0x0C, 0xE9, 0x29, 0x85, 0x1F,
-        0x0D, 0x81, 0x15, 0xF6, 0x35, 0xB1, 0x05, 0xEE, 0x2E, 0x4E, 0x15, 0xD0, 0x4B, 0x24, 0x54, 0xBF,
-        0x6F, 0x4F, 0xAD, 0xF0, 0x34, 0xB1, 0x04, 0x03, 0x11, 0x9C, 0xD8, 0xE3, 0xB9, 0x2F, 0xCC, 0x5B,
+        0xc7, 0x1c, 0xae, 0xb9, 0xc6, 0xb1, 0xc9, 0x04, 0x8e, 0x6c, 0x52, 0x2f,
+        0x70, 0xf1, 0x3f, 0x73, 0x98, 0x0d, 0x40, 0x23, 0x8e, 0x3e, 0x21, 0xc1,
+        0x49, 0x34, 0xd0, 0x37, 0x56, 0x3d, 0x93, 0x0f, 0x48, 0x19, 0x8a, 0x0a,
+        0xa7, 0xc1, 0x40, 0x58, 0x22, 0x94, 0x93, 0xd2, 0x25, 0x30, 0xf4, 0xdb,
+        0xfa, 0x33, 0x6f, 0x6e, 0x0a, 0xc9, 0x25, 0x13, 0x95, 0x43, 0xae, 0xd4,
+        0x4c, 0xce, 0x7c, 0x37, 0x20, 0xfd, 0x51, 0xf6, 0x94, 0x58, 0x70, 0x5a,
+        0xc6, 0x8c, 0xd4, 0xfe, 0x6b, 0x6b, 0x13, 0xab, 0xdc, 0x97, 0x46, 0x51,
+        0x29, 0x69, 0x32, 0x84, 0x54, 0xf1, 0x8f, 0xaf, 0x8c, 0x59, 0x5f, 0x64,
+        0x24, 0x77, 0xfe, 0x96, 0xbb, 0x2a, 0x94, 0x1d, 0x5b, 0xcd, 0x1d, 0x4a,
+        0xc8, 0xcc, 0x49, 0x88, 0x07, 0x08, 0xfa, 0x9b, 0x37, 0x8e, 0x3c, 0x4f,
+        0x3a, 0x90, 0x60, 0xbe, 0xe6, 0x7c, 0xf9, 0xa4, 0xa4, 0xa6, 0x95, 0x81,
+        0x10, 0x51, 0x90, 0x7e, 0x16, 0x27, 0x53, 0xb5, 0x6b, 0x0f, 0x6b, 0x41,
+        0x0d, 0xba, 0x74, 0xd8, 0xa8, 0x4b, 0x2a, 0x14, 0xb3, 0x14, 0x4e, 0x0e,
+        0xf1, 0x28, 0x47, 0x54, 0xfd, 0x17, 0xed, 0x95, 0x0d, 0x59, 0x65, 0xb4,
+        0xb9, 0xdd, 0x46, 0x58, 0x2d, 0xb1, 0x17, 0x8d, 0x16, 0x9c, 0x6b, 0xc4,
+        0x65, 0xb0, 0xd6, 0xff, 0x9c, 0xa3, 0x92, 0x8f, 0xef, 0x5b, 0x9a, 0xe4,
+        0xe4, 0x18, 0xfc, 0x15, 0xe8, 0x3e, 0xbe, 0xa0, 0xf8, 0x7f, 0xa9, 0xff,
+        0x5e, 0xed, 0x70, 0x05, 0x0d, 0xed, 0x28, 0x49, 0xf4, 0x7b, 0xf9, 0x59,
+        0xd9, 0x56, 0x85, 0x0c, 0xe9, 0x29, 0x85, 0x1f, 0x0d, 0x81, 0x15, 0xf6,
+        0x35, 0xb1, 0x05, 0xee, 0x2e, 0x4e, 0x15, 0xd0, 0x4b, 0x24, 0x54, 0xbf,
+        0x6f, 0x4f, 0xad, 0xf0, 0x34, 0xb1, 0x04, 0x03, 0x11, 0x9c, 0xd8, 0xe3,
+        0xb9, 0x2f, 0xcc, 0x5b,
     ]);
     if (goodPrime.equals(primeBytes)) {
         if ([3, 4, 5, 7].includes(g)) {
-            return // It's good
+            return; // It's good
         }
     }
-    throw new Error('Changing passwords unsupported')
+    throw new Error("Changing passwords unsupported");
     //checkPrimeAndGoodCheck(readBigIntFromBuffer(primeBytes, false), g)
 }
 
@@ -98,7 +111,7 @@ function checkPrimeAndGood(primeBytes: Buffer, g: number) {
  * @returns {boolean}
  */
 function isGoodLarge(number: bigInt.BigInteger, p: bigInt.BigInteger) {
-    return (number.greater(BigInt(0)) && (p.subtract(number).greater(BigInt(0))))
+    return number.greater(BigInt(0)) && p.subtract(number).greater(BigInt(0));
 }
 
 /**
@@ -107,7 +120,7 @@ function isGoodLarge(number: bigInt.BigInteger, p: bigInt.BigInteger) {
  * @returns {Buffer}
  */
 function numBytesForHash(number: Buffer) {
-    return Buffer.concat([Buffer.alloc(SIZE_FOR_HASH - number.length), number])
+    return Buffer.concat([Buffer.alloc(SIZE_FOR_HASH - number.length), number]);
 }
 
 /**
@@ -116,7 +129,7 @@ function numBytesForHash(number: Buffer) {
  * @returns {Buffer}
  */
 function bigNumForHash(g: bigInt.BigInteger) {
-    return readBufferFromBigInt(g, SIZE_FOR_HASH, false)
+    return readBufferFromBigInt(g, SIZE_FOR_HASH, false);
 }
 
 /**
@@ -125,25 +138,31 @@ function bigNumForHash(g: bigInt.BigInteger) {
  * @param prime {BigInteger}
  * @returns {Boolean}
  */
-function isGoodModExpFirst(modexp: bigInt.BigInteger, prime: bigInt.BigInteger) {
+function isGoodModExpFirst(
+    modexp: bigInt.BigInteger,
+    prime: bigInt.BigInteger
+) {
     const diff = prime.subtract(modexp);
 
     const minDiffBitsCount = 2048 - 64;
     const maxModExpSize = 256;
 
-    return !(diff.lesser(BigInt(0)) || diff.bitLength().toJSNumber() < minDiffBitsCount ||
+    return !(
+        diff.lesser(BigInt(0)) ||
+        diff.bitLength().toJSNumber() < minDiffBitsCount ||
         modexp.bitLength().toJSNumber() < minDiffBitsCount ||
-        Math.floor((modexp.bitLength().toJSNumber() + 7) / 8) > maxModExpSize)
+        Math.floor((modexp.bitLength().toJSNumber() + 7) / 8) > maxModExpSize
+    );
 }
 
 function xor(a: Buffer, b: Buffer) {
     const length = Math.min(a.length, b.length);
 
     for (let i = 0; i < length; i++) {
-        a[i] = a[i] ^ b[i]
+        a[i] = a[i] ^ b[i];
     }
 
-    return a
+    return a;
 }
 
 /**
@@ -155,8 +174,7 @@ function xor(a: Buffer, b: Buffer) {
  */
 
 function pbkdf2sha512(password: Buffer, salt: Buffer, iterations: number) {
-
-    return crypto.pbkdf2Sync(password, salt, iterations, 64, 'sha512');
+    return crypto.pbkdf2Sync(password, salt, iterations, 64, "sha512");
 }
 
 /**
@@ -165,11 +183,16 @@ function pbkdf2sha512(password: Buffer, salt: Buffer, iterations: number) {
  * @param password
  * @returns {Buffer|*}
  */
-async function computeHash(algo: Api.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow, password: string) {
-    const hash1 = await sha256(Buffer.concat([algo.salt1, Buffer.from(password, 'utf-8'), algo.salt1]));
+async function computeHash(
+    algo: Api.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow,
+    password: string
+) {
+    const hash1 = await sha256(
+        Buffer.concat([algo.salt1, Buffer.from(password, "utf-8"), algo.salt1])
+    );
     const hash2 = await sha256(Buffer.concat([algo.salt2, hash1, algo.salt2]));
     const hash3 = await pbkdf2sha512(hash2, algo.salt1, 100000);
-    return sha256(Buffer.concat([algo.salt2, hash3, algo.salt2]))
+    return sha256(Buffer.concat([algo.salt2, hash3, algo.salt2]));
 }
 
 /**
@@ -177,17 +200,22 @@ async function computeHash(algo: Api.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512
  * @param algo {constructors.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow}
  * @param password
  */
-async function computeDigest(algo: Api.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow, password: string) {
+async function computeDigest(
+    algo: Api.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow,
+    password: string
+) {
     try {
-        checkPrimeAndGood(algo.p, algo.g)
+        checkPrimeAndGood(algo.p, algo.g);
     } catch (e) {
-        throw new Error('bad p/g in password')
+        throw new Error("bad p/g in password");
     }
 
-    const value = modExp(bigInt(algo.g),
+    const value = modExp(
+        bigInt(algo.g),
         readBigIntFromBuffer(await computeHash(algo, password), false),
-        readBigIntFromBuffer(algo.p, false));
-    return bigNumForHash(value)
+        readBigIntFromBuffer(algo.p, false)
+    );
+    return bigNumForHash(value);
 }
 
 /**
@@ -197,32 +225,40 @@ async function computeDigest(algo: Api.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA5
  */
 async function computeCheck(request: Api.account.Password, password: string) {
     const algo = request.currentAlgo;
-    if (!(algo instanceof Api.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow)) {
-        throw new Error(`Unsupported password algorithm ${algo?.className}`)
+    if (
+        !(
+            algo instanceof
+            Api.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow
+        )
+    ) {
+        throw new Error(`Unsupported password algorithm ${algo?.className}`);
     }
     const srp_B = request.srp_B;
     const srpId = request.srpId;
     if (!srp_B || !srpId) {
-        throw new Error(`Undefined srp_b  ${request}`)
+        throw new Error(`Undefined srp_b  ${request}`);
     }
     const pwHash = await computeHash(algo, password);
     const p = readBigIntFromBuffer(algo.p, false);
     const g = algo.g;
     const B = readBigIntFromBuffer(srp_B, false);
     try {
-        checkPrimeAndGood(algo.p, g)
+        checkPrimeAndGood(algo.p, g);
     } catch (e) {
-        throw new Error('bad /g in password')
+        throw new Error("bad /g in password");
     }
     if (!isGoodLarge(B, p)) {
-        throw new Error('bad b in check')
+        throw new Error("bad b in check");
     }
     const x = readBigIntFromBuffer(pwHash, false);
     const pForHash = numBytesForHash(algo.p);
     const gForHash = bigNumForHash(bigInt(g));
     const bForHash = numBytesForHash(srp_B);
     const gX = modExp(bigInt(g), x, p);
-    const k = readBigIntFromBuffer(await sha256(Buffer.concat([pForHash, gForHash])), false);
+    const k = readBigIntFromBuffer(
+        await sha256(Buffer.concat([pForHash, gForHash])),
+        false
+    );
     const kgX = bigIntMod(k.multiply(gX), p);
     const generateAndCheckRandom = async () => {
         const randomSize = 256;
@@ -233,7 +269,10 @@ async function computeCheck(request: Api.account.Password, password: string) {
             const A = modExp(bigInt(g), a, p);
             if (isGoodModExpFirst(A, p)) {
                 const aForHash = bigNumForHash(A);
-                const u = readBigIntFromBuffer(await sha256(Buffer.concat([aForHash, bForHash])), false);
+                const u = readBigIntFromBuffer(
+                    await sha256(Buffer.concat([aForHash, bForHash])),
+                    false
+                );
                 if (u.greater(BigInt(0))) {
                     return {
                         a: a,
@@ -244,10 +283,10 @@ async function computeCheck(request: Api.account.Password, password: string) {
             }
         }
     };
-    const {a, aForHash, u} = await generateAndCheckRandom();
+    const { a, aForHash, u } = await generateAndCheckRandom();
     const gB = bigIntMod(B.subtract(kgX), p);
     if (!isGoodModExpFirst(gB, p)) {
-        throw new Error('bad gB')
+        throw new Error("bad gB");
     }
 
     const ux = u.multiply(x);
@@ -260,26 +299,22 @@ async function computeCheck(request: Api.account.Password, password: string) {
         sha256(algo.salt1),
         sha256(algo.salt2),
     ]);
-    const M1 = await sha256(Buffer.concat([
-        xor(pSha, gSha),
-        salt1Sha,
-        salt2Sha,
-        aForHash,
-        bForHash,
-        K,
-    ]));
-
+    const M1 = await sha256(
+        Buffer.concat([
+            xor(pSha, gSha),
+            salt1Sha,
+            salt2Sha,
+            aForHash,
+            bForHash,
+            K,
+        ])
+    );
 
     return new Api.InputCheckPasswordSRP({
         srpId: srpId,
         A: Buffer.from(aForHash),
         M1: M1,
-
-    })
-}
-
-export {
-    computeCheck,
-    computeDigest,
+    });
 }
 
+export { computeCheck, computeDigest };

Fichier diff supprimé car celui-ci est trop grand
+ 347 - 251
gramjs/Utils.ts


+ 194 - 114
gramjs/client/TelegramClient.ts

@@ -1,4 +1,4 @@
-import {TelegramBaseClient} from "./telegramBaseClient";
+import { TelegramBaseClient } from "./telegramBaseClient";
 
 import * as authMethods from "./auth";
 import * as botMethods from "./bots";
@@ -10,20 +10,20 @@ import * as updateMethods from "./updates";
 import * as uploadMethods from "./uploads";
 import * as userMethods from "./users";
 import * as chatMethods from "./chats";
-import type {ButtonLike, EntityLike, MarkupLike} from "../define";
-import {Api} from "../tl";
-import {sanitizeParseMode} from "../Utils";
-import {MarkdownParser} from "../extensions/markdown";
-import type  {EventBuilder} from "../events/common";
-import {MTProtoSender, UpdateConnectionState} from "../network";
-
-import {LAYER} from "../tl/AllTLObjects";
-import {IS_NODE} from "../Helpers";
-import {DownloadMediaInterface} from "./downloads";
-import type {Message} from "../tl/custom/message";
+import * as dialogMethods from "./dialogs";
+import type { ButtonLike, EntityLike, MarkupLike } from "../define";
+import { Api } from "../tl";
+import { sanitizeParseMode } from "../Utils";
+import { MarkdownParser } from "../extensions/markdown";
+import type { EventBuilder } from "../events/common";
+import { MTProtoSender, UpdateConnectionState } from "../network";
+
+import { LAYER } from "../tl/AllTLObjects";
+import { IS_NODE } from "../Helpers";
+import { DownloadMediaInterface } from "./downloads";
+import type { Message } from "../tl/custom/message";
 
 export class TelegramClient extends TelegramBaseClient {
-
     // region auth
 
     start(authParams: authMethods.UserAuthParams | authMethods.BotAuthParams) {
@@ -36,33 +36,52 @@ export class TelegramClient extends TelegramBaseClient {
 
     signInUser(
         apiCredentials: authMethods.ApiCredentials,
-        authParams: authMethods.UserAuthParams,
+        authParams: authMethods.UserAuthParams
     ) {
         return authMethods.signInUser(this, apiCredentials, authParams);
     }
 
     signInUserWithQrCode(
         apiCredentials: authMethods.ApiCredentials,
-        authParams: authMethods.UserAuthParams,
+        authParams: authMethods.UserAuthParams
     ) {
-        return authMethods.signInUserWithQrCode(this, apiCredentials, authParams);
+        return authMethods.signInUserWithQrCode(
+            this,
+            apiCredentials,
+            authParams
+        );
     }
 
-    sendCode(apiCredentials: authMethods.ApiCredentials, phoneNumber: string, forceSMS = false) {
-        return authMethods.sendCode(this, apiCredentials, phoneNumber, forceSMS);
+    sendCode(
+        apiCredentials: authMethods.ApiCredentials,
+        phoneNumber: string,
+        forceSMS = false
+    ) {
+        return authMethods.sendCode(
+            this,
+            apiCredentials,
+            phoneNumber,
+            forceSMS
+        );
     }
 
-    signInWithPassword(apiCredentials: authMethods.ApiCredentials, authParams: authMethods.UserAuthParams) {
+    signInWithPassword(
+        apiCredentials: authMethods.ApiCredentials,
+        authParams: authMethods.UserAuthParams
+    ) {
         return authMethods.signInWithPassword(this, apiCredentials, authParams);
     }
 
-    signInBot(apiCredentials: authMethods.ApiCredentials, authParams: authMethods.BotAuthParams) {
+    signInBot(
+        apiCredentials: authMethods.ApiCredentials,
+        authParams: authMethods.BotAuthParams
+    ) {
         return authMethods.signInBot(this, apiCredentials, authParams);
     }
 
     authFlow(
         apiCredentials: authMethods.ApiCredentials,
-        authParams: authMethods.UserAuthParams | authMethods.BotAuthParams,
+        authParams: authMethods.UserAuthParams | authMethods.BotAuthParams
     ) {
         return authMethods.authFlow(this, apiCredentials, authParams);
     }
@@ -70,16 +89,35 @@ export class TelegramClient extends TelegramBaseClient {
     //endregion auth
 
     //region bot
-    inlineQuery(bot: EntityLike, query: string,
-                entity?: Api.InputPeerSelf | null,
-                offset?: string, geoPoint?: Api.GeoPoint) {
-        return botMethods.inlineQuery(this, bot, query, entity, offset, geoPoint);
+    inlineQuery(
+        bot: EntityLike,
+        query: string,
+        entity?: Api.InputPeerSelf | null,
+        offset?: string,
+        geoPoint?: Api.GeoPoint
+    ) {
+        return botMethods.inlineQuery(
+            this,
+            bot,
+            query,
+            entity,
+            offset,
+            geoPoint
+        );
     }
 
     //endregion
 
     //region buttons
-    buildReplyMarkup(buttons: Api.TypeReplyMarkup | undefined | ButtonLike | ButtonLike[] | ButtonLike[][], inlineOnly: boolean = false) {
+    buildReplyMarkup(
+        buttons:
+            | Api.TypeReplyMarkup
+            | undefined
+            | ButtonLike
+            | ButtonLike[]
+            | ButtonLike[][],
+        inlineOnly: boolean = false
+    ) {
         return buttonsMethods.buildReplyMarkup(buttons, inlineOnly);
     }
 
@@ -88,35 +126,49 @@ export class TelegramClient extends TelegramBaseClient {
     //region download
     downloadFile(
         inputLocation: Api.TypeInputFileLocation,
-        fileParams: downloadMethods.DownloadFileParams,
+        fileParams: downloadMethods.DownloadFileParams
     ) {
-        return downloadMethods.downloadFile(this,
-            inputLocation,
-            fileParams,
-        )
+        return downloadMethods.downloadFile(this, inputLocation, fileParams);
     }
 
-    _downloadPhoto(photo: Api.MessageMediaPhoto | Api.Photo, args: DownloadMediaInterface) {
+    _downloadPhoto(
+        photo: Api.MessageMediaPhoto | Api.Photo,
+        args: DownloadMediaInterface
+    ) {
         return downloadMethods._downloadPhoto(this, photo, args);
     }
 
-    _downloadCachedPhotoSize(size: Api.PhotoCachedSize | Api.PhotoStrippedSize) {
+    _downloadCachedPhotoSize(
+        size: Api.PhotoCachedSize | Api.PhotoStrippedSize
+    ) {
         return downloadMethods._downloadCachedPhotoSize(this, size);
     }
 
-    _downloadDocument(media: Api.MessageMediaDocument | Api.Document, args: DownloadMediaInterface) {
+    _downloadDocument(
+        media: Api.MessageMediaDocument | Api.Document,
+        args: DownloadMediaInterface
+    ) {
         return downloadMethods._downloadDocument(this, media, args);
     }
 
-    _downloadContact(contact: Api.MessageMediaContact, args: DownloadMediaInterface) {
+    _downloadContact(
+        contact: Api.MessageMediaContact,
+        args: DownloadMediaInterface
+    ) {
         return downloadMethods._downloadContact(this, contact, args);
     }
 
-    _downloadWebDocument(webDocument: Api.WebDocument | Api.WebDocumentNoProxy, args: DownloadMediaInterface) {
+    _downloadWebDocument(
+        webDocument: Api.WebDocument | Api.WebDocumentNoProxy,
+        args: DownloadMediaInterface
+    ) {
         return downloadMethods._downloadWebDocument(this, webDocument, args);
     }
 
-    downloadMedia(messageOrMedia: Api.Message | Api.TypeMessageMedia | Message, args: DownloadMediaInterface) {
+    downloadMedia(
+        messageOrMedia: Api.Message | Api.TypeMessageMedia | Message,
+        args: DownloadMediaInterface
+    ) {
         return downloadMethods.downloadMedia(this, messageOrMedia, args);
     }
 
@@ -132,7 +184,11 @@ export class TelegramClient extends TelegramBaseClient {
     }
 
     // private methods
-    _replaceWithMention(entities: Api.TypeMessageEntity[], i: number, user: EntityLike) {
+    _replaceWithMention(
+        entities: Api.TypeMessageEntity[],
+        i: number,
+        user: EntityLike
+    ) {
         return parseMethods._replaceWithMention(this, entities, i, user);
     }
 
@@ -142,36 +198,56 @@ export class TelegramClient extends TelegramBaseClient {
 
     //endregion
     // region messages
-    iterMessages(entity: EntityLike | undefined, params: messageMethods.IterMessagesParams) {
-        return messageMethods.iterMessages(this, entity, params)
+    iterMessages(
+        entity: EntityLike | undefined,
+        params: messageMethods.IterMessagesParams
+    ) {
+        return messageMethods.iterMessages(this, entity, params);
     }
 
-    getMessages(entity: EntityLike | undefined, params: messageMethods.IterMessagesParams) {
+    getMessages(
+        entity: EntityLike | undefined,
+        params: messageMethods.IterMessagesParams
+    ) {
         return messageMethods.getMessages(this, entity, params);
     }
 
     sendMessage(entity: EntityLike, params: messageMethods.SendMessageParams) {
-        return messageMethods.sendMessage(this, entity, params)
+        return messageMethods.sendMessage(this, entity, params);
     }
 
     editMessage(entity: EntityLike, params: messageMethods.EditMessageParams) {
-        return messageMethods.editMessage(this, entity, params)
+        return messageMethods.editMessage(this, entity, params);
     }
 
     //endregion
+    //region dialogs
+    iterDialogs(params: dialogMethods.IterDialogsParams) {
+        return dialogMethods.iterDialogs(this, params);
+    }
 
+    getDialogs(params: dialogMethods.IterDialogsParams) {
+        return dialogMethods.getDialogs(this, params);
+    }
+
+    //endregion
     //region chats
-    iterParticipants(entity: EntityLike, params: chatMethods.IterParticipantsParams) {
+    iterParticipants(
+        entity: EntityLike,
+        params: chatMethods.IterParticipantsParams
+    ) {
         return chatMethods.iterParticipants(this, entity, params);
     }
 
-    getParticipants(entity: EntityLike, params: chatMethods.IterParticipantsParams) {
+    getParticipants(
+        entity: EntityLike,
+        params: chatMethods.IterParticipantsParams
+    ) {
         return chatMethods.getParticipants(this, entity, params);
     }
 
     //endregion
 
-
     //region updates
     on(event: any) {
         return updateMethods.on(this, event);
@@ -179,38 +255,31 @@ export class TelegramClient extends TelegramBaseClient {
 
     addEventHandler(callback: CallableFunction, event?: EventBuilder) {
         return updateMethods.addEventHandler(this, callback, event);
-
     }
 
     removeEventHandler(callback: CallableFunction, event: EventBuilder) {
         return updateMethods.removeEventHandler(this, callback, event);
-
     }
 
     listEventHandlers() {
         return updateMethods.listEventHandlers(this);
-
     }
 
     // private methods
     _handleUpdate(update: Api.TypeUpdate | number) {
         return updateMethods._handleUpdate(this, update);
-
     }
 
     _processUpdate(update: any, others: any, entities?: any) {
         return updateMethods._processUpdate(this, update, others, entities);
-
     }
 
     _dispatchUpdate(args: { update: UpdateConnectionState | any }) {
         return updateMethods._dispatchUpdate(this, args);
-
     }
 
     _updateLoop() {
         return updateMethods._updateLoop(this);
-
     }
 
     //endregion
@@ -227,7 +296,7 @@ export class TelegramClient extends TelegramBaseClient {
     // endregion
 
     //region user methods
-    invoke<R extends Api.AnyRequest>(request: R): Promise<R['__response']> {
+    invoke<R extends Api.AnyRequest>(request: R): Promise<R["__response"]> {
         return userMethods.invoke(this, request);
     }
 
@@ -260,7 +329,6 @@ export class TelegramClient extends TelegramBaseClient {
         return userMethods._getEntityFromString(this, string);
     }
 
-
     _getPeer(peer: EntityLike) {
         return userMethods._getPeer(this, peer);
     }
@@ -292,23 +360,32 @@ export class TelegramClient extends TelegramBaseClient {
             isMainSender: true,
         });
 
-        const connection = new this._connection(this.session.serverAddress
-            , this.session.port, this.session.dcId, this._log);
-        if (!await this._sender.connect(connection, this._dispatchUpdate.bind(this))) {
-            return
+        const connection = new this._connection(
+            this.session.serverAddress,
+            this.session.port,
+            this.session.dcId,
+            this._log
+        );
+        if (
+            !(await this._sender.connect(
+                connection,
+                this._dispatchUpdate.bind(this)
+            ))
+        ) {
+            return;
         }
         this.session.setAuthKey(this._sender.authKey);
         await this.session.save();
         this._initRequest.query = new Api.help.GetConfig();
-        await this._sender.send(new Api.InvokeWithLayer(
-            {
+        await this._sender.send(
+            new Api.InvokeWithLayer({
                 layer: LAYER,
-                query: this._initRequest
-            }
-        ));
+                query: this._initRequest,
+            })
+        );
 
-        this._dispatchUpdate({update: new UpdateConnectionState(1)});
-        this._updateLoop()
+        this._dispatchUpdate({ update: new UpdateConnectionState(1) });
+        this._updateLoop();
     }
 
     //endregion
@@ -323,94 +400,99 @@ export class TelegramClient extends TelegramBaseClient {
         await this._sender.authKey.setKey();
         this.session.setAuthKey();
         await this.disconnect();
-        return this.connect()
+        return this.connect();
     }
 
-    async _createExportedSender(dcId: number, retries: number): Promise<MTProtoSender> {
+    async _createExportedSender(
+        dcId: number,
+        retries: number
+    ): Promise<MTProtoSender> {
         const dc = await this.getDC(dcId);
-        const sender = new MTProtoSender(this.session.getAuthKey(dcId),
-            {
-                logger: this._log,
-                dcId: dcId,
-                retries: this._connectionRetries,
-                delay: this._retryDelay,
-                autoReconnect: this._autoReconnect,
-                connectTimeout: this._timeout,
-                authKeyCallback: this._authKeyCallback.bind(this),
-                isMainSender: dcId === this.session.dcId,
-                senderCallback: this.removeSender.bind(this),
-            });
+        const sender = new MTProtoSender(this.session.getAuthKey(dcId), {
+            logger: this._log,
+            dcId: dcId,
+            retries: this._connectionRetries,
+            delay: this._retryDelay,
+            autoReconnect: this._autoReconnect,
+            connectTimeout: this._timeout,
+            authKeyCallback: this._authKeyCallback.bind(this),
+            isMainSender: dcId === this.session.dcId,
+            senderCallback: this.removeSender.bind(this),
+        });
         for (let i = 0; i < retries; i++) {
             try {
-                await sender.connect(new this._connection(
-                    dc.ipAddress,
-                    dc.port,
-                    dcId,
-                    this._log,
-                ));
+                await sender.connect(
+                    new this._connection(dc.ipAddress, dc.port, dcId, this._log)
+                );
                 if (this.session.dcId !== dcId) {
-                    this._log.info(`Exporting authorization for data center ${dc.ipAddress}`);
-                    const auth = await this.invoke(new Api.auth.ExportAuthorization({dcId: dcId}));
+                    this._log.info(
+                        `Exporting authorization for data center ${dc.ipAddress}`
+                    );
+                    const auth = await this.invoke(
+                        new Api.auth.ExportAuthorization({ dcId: dcId })
+                    );
                     this._initRequest.query = new Api.auth.ImportAuthorization({
-                            id: auth.id,
-                            bytes: auth.bytes,
-                        },
-                    )
+                        id: auth.id,
+                        bytes: auth.bytes,
+                    });
                     const req = new Api.InvokeWithLayer({
                         layer: LAYER,
-                        query: this._initRequest
+                        query: this._initRequest,
                     });
-                    await sender.send(req)
+                    await sender.send(req);
                 }
                 sender.dcId = dcId;
-                return sender
+                return sender;
             } catch (e) {
-
-                await sender.disconnect()
+                await sender.disconnect();
             }
         }
-        throw new Error("Could not create sender for DC " + dcId)
+        throw new Error("Could not create sender for DC " + dcId);
     }
 
-    async getDC(dcId: number): Promise<{ id: number, ipAddress: string, port: number }> {
+    async getDC(
+        dcId: number
+    ): Promise<{ id: number; ipAddress: string; port: number }> {
         if (!IS_NODE) {
             switch (dcId) {
                 case 1:
                     return {
                         id: 1,
-                        ipAddress: 'pluto.web.telegram.org',
+                        ipAddress: "pluto.web.telegram.org",
                         port: 443,
                     };
                 case 2:
                     return {
                         id: 2,
-                        ipAddress: 'venus.web.telegram.org',
+                        ipAddress: "venus.web.telegram.org",
                         port: 443,
                     };
                 case 3:
                     return {
                         id: 3,
-                        ipAddress: 'aurora.web.telegram.org',
+                        ipAddress: "aurora.web.telegram.org",
                         port: 443,
                     };
                 case 4:
                     return {
                         id: 4,
-                        ipAddress: 'vesta.web.telegram.org',
+                        ipAddress: "vesta.web.telegram.org",
                         port: 443,
                     };
                 case 5:
                     return {
                         id: 5,
-                        ipAddress: 'flora.web.telegram.org',
+                        ipAddress: "flora.web.telegram.org",
                         port: 443,
                     };
                 default:
-                    throw new Error(`Cannot find the DC with the ID of ${dcId}`)
+                    throw new Error(
+                        `Cannot find the DC with the ID of ${dcId}`
+                    );
             }
         }
         if (!this._config) {
-            this._config = await this.invoke(new Api.help.GetConfig())
+            this._config = await this.invoke(new Api.help.GetConfig());
         }
         for (const DC of this._config.dcOptions) {
             if (DC.id === dcId && !!DC.ipv6 === this._useIPV6) {
@@ -418,14 +500,14 @@ export class TelegramClient extends TelegramBaseClient {
                     id: DC.id,
                     ipAddress: DC.ipAddress,
                     port: 443,
-                }
+                };
             }
         }
-        throw new Error(`Cannot find the DC with the ID of ${dcId}`)
+        throw new Error(`Cannot find the DC with the ID of ${dcId}`);
     }
 
     removeSender(dcId: number) {
-        delete this._borrowedSenderPromises[dcId]
+        delete this._borrowedSenderPromises[dcId];
     }
 
     async _borrowExportedSender(dcId: number, retries = this._requestRetries) {
@@ -436,14 +518,12 @@ export class TelegramClient extends TelegramBaseClient {
 
             senderPromise.then((sender: any) => {
                 if (!sender) {
-                    delete this._borrowedSenderPromises[dcId]
+                    delete this._borrowedSenderPromises[dcId];
                 }
-            })
+            });
         }
-        return senderPromise
+        return senderPromise;
     }
 
     // endregion
-
-
 }

+ 158 - 104
gramjs/client/auth.ts

@@ -1,21 +1,21 @@
-import {Api} from '../tl';
-import * as utils from '../Utils';
-import {sleep} from '../Helpers';
-import {computeCheck as computePasswordSrpCheck} from '../Password';
-import type {TelegramClient} from "./TelegramClient";
+import { Api } from "../tl";
+import * as utils from "../Utils";
+import { sleep } from "../Helpers";
+import { computeCheck as computePasswordSrpCheck } from "../Password";
+import type { TelegramClient } from "./TelegramClient";
 
 export interface UserAuthParams {
     phoneNumber: string | (() => Promise<string>);
     phoneCode: (isCodeViaApp?: boolean) => Promise<string>;
     password: (hint?: string) => Promise<string>;
     firstAndLastNames?: () => Promise<[string, string?]>;
-    qrCode?: (qrCode: { token: Buffer, expires: number }) => Promise<void>;
+    qrCode?: (qrCode: { token: Buffer; expires: number }) => Promise<void>;
     onError: (err: Error) => Promise<boolean>;
     forceSMS?: boolean;
 }
 
 interface ReturnString {
-    (): string
+    (): string;
 }
 
 export interface BotAuthParams {
@@ -23,16 +23,18 @@ export interface BotAuthParams {
 }
 
 export interface ApiCredentials {
-    apiId: number,
-    apiHash: string,
+    apiId: number;
+    apiHash: string;
 }
 
 const QR_CODE_TIMEOUT = 30000;
 
-
 // region public methods
 
-export async function start(client: TelegramClient, authParams: UserAuthParams | BotAuthParams) {
+export async function start(
+    client: TelegramClient,
+    authParams: UserAuthParams | BotAuthParams
+) {
     if (!client.connected) {
         await client.connect();
     }
@@ -61,7 +63,7 @@ export async function checkAuthorization(client: TelegramClient) {
 export async function signInUser(
     client: TelegramClient,
     apiCredentials: ApiCredentials,
-    authParams: UserAuthParams,
+    authParams: UserAuthParams
 ): Promise<Api.TypeUser> {
     let phoneNumber;
     let phoneCodeHash;
@@ -69,12 +71,15 @@ export async function signInUser(
 
     while (1) {
         try {
-            if (typeof authParams.phoneNumber === 'function') {
+            if (typeof authParams.phoneNumber === "function") {
                 try {
                     phoneNumber = await authParams.phoneNumber();
                 } catch (err) {
-                    if (err.message === 'RESTART_AUTH_WITH_QR') {
-                        return client.signInUserWithQrCode(apiCredentials, authParams);
+                    if (err.message === "RESTART_AUTH_WITH_QR") {
+                        return client.signInUserWithQrCode(
+                            apiCredentials,
+                            authParams
+                        );
                     }
 
                     throw err;
@@ -82,23 +87,27 @@ export async function signInUser(
             } else {
                 phoneNumber = authParams.phoneNumber;
             }
-            const sendCodeResult = await client.sendCode(apiCredentials, phoneNumber, authParams.forceSMS);
+            const sendCodeResult = await client.sendCode(
+                apiCredentials,
+                phoneNumber,
+                authParams.forceSMS
+            );
             phoneCodeHash = sendCodeResult.phoneCodeHash;
             isCodeViaApp = sendCodeResult.isCodeViaApp;
 
-            if (typeof phoneCodeHash !== 'string') {
-                throw new Error('Failed to retrieve phone code hash');
+            if (typeof phoneCodeHash !== "string") {
+                throw new Error("Failed to retrieve phone code hash");
             }
 
             break;
         } catch (err) {
-            if (typeof authParams.phoneNumber !== 'function') {
+            if (typeof authParams.phoneNumber !== "function") {
                 throw err;
             }
 
             const shouldWeStop = await authParams.onError(err);
-            if (shouldWeStop){
-               throw new Error("AUTH_USER_CANCEL")
+            if (shouldWeStop) {
+                throw new Error("AUTH_USER_CANCEL");
             }
         }
     }
@@ -113,22 +122,24 @@ export async function signInUser(
                 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') {
+                if (err.message === "RESTART_AUTH") {
                     return client.signInUser(apiCredentials, authParams);
                 }
             }
 
             if (!phoneCode) {
-                throw new Error('Code is empty');
+                throw new Error("Code is empty");
             }
 
             // May raise PhoneCodeEmptyError, PhoneCodeExpiredError,
             // PhoneCodeHashEmptyError or PhoneCodeInvalidError.
-            const result = await client.invoke(new Api.auth.SignIn({
-                phoneNumber,
-                phoneCodeHash,
-                phoneCode,
-            }));
+            const result = await client.invoke(
+                new Api.auth.SignIn({
+                    phoneNumber,
+                    phoneCodeHash,
+                    phoneCode,
+                })
+            );
 
             if (result instanceof Api.auth.AuthorizationSignUpRequired) {
                 isRegistrationRequired = true;
@@ -138,12 +149,12 @@ export async function signInUser(
 
             return result.user;
         } catch (err) {
-            if (err.message === 'SESSION_PASSWORD_NEEDED') {
+            if (err.message === "SESSION_PASSWORD_NEEDED") {
                 return client.signInWithPassword(apiCredentials, authParams);
             } else {
                 const shouldWeStop = await authParams.onError(err);
-                if (shouldWeStop){
-                    throw new Error("AUTH_USER_CANCEL")
+                if (shouldWeStop) {
+                    throw new Error("AUTH_USER_CANCEL");
                 }
             }
         }
@@ -160,56 +171,64 @@ export async function signInUser(
                     lastName = result[1];
                 }
                 if (!firstName) {
-                    throw new Error('First name is required');
+                    throw new Error("First name is required");
                 }
 
-                const {user} = await client.invoke(new Api.auth.SignUp({
-                    phoneNumber,
-                    phoneCodeHash,
-                    firstName,
-                    lastName,
-                })) as Api.auth.Authorization;
+                const { user } = (await client.invoke(
+                    new Api.auth.SignUp({
+                        phoneNumber,
+                        phoneCodeHash,
+                        firstName,
+                        lastName,
+                    })
+                )) as Api.auth.Authorization;
 
                 if (termsOfService) {
                     // This is a violation of Telegram rules: the user should be presented with and accept TOS.
-                    await client.invoke(new Api.help.AcceptTermsOfService({id: termsOfService.id}));
+                    await client.invoke(
+                        new Api.help.AcceptTermsOfService({
+                            id: termsOfService.id,
+                        })
+                    );
                 }
 
                 return user;
             } catch (err) {
                 const shouldWeStop = await authParams.onError(err);
-                if (shouldWeStop){
-                    throw new Error("AUTH_USER_CANCEL")
+                if (shouldWeStop) {
+                    throw new Error("AUTH_USER_CANCEL");
                 }
             }
         }
     }
 
-    await authParams.onError(new Error('Auth failed'));
+    await authParams.onError(new Error("Auth failed"));
     return client.signInUser(apiCredentials, authParams);
 }
 
 export async function signInUserWithQrCode(
     client: TelegramClient,
     apiCredentials: ApiCredentials,
-    authParams: UserAuthParams,
+    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: [],
-            }));
+            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');
+                throw new Error("Unexpected");
             }
 
-            const {token, expires} = result;
+            const { token, expires } = result;
             if (authParams.qrCode) {
                 await Promise.race([
-                    authParams.qrCode({token, expires}),
+                    authParams.qrCode({ token, expires }),
                     sleep(QR_CODE_TIMEOUT),
                 ]);
             }
@@ -227,7 +246,7 @@ export async function signInUserWithQrCode(
     try {
         await Promise.race([updatePromise, inputPromise]);
     } catch (err) {
-        if (err.message === 'RESTART_AUTH') {
+        if (err.message === "RESTART_AUTH") {
             return client.signInUser(apiCredentials, authParams);
         }
 
@@ -235,67 +254,86 @@ export async function signInUserWithQrCode(
     }
 
     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: [],
-        }));
+        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) {
+        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) {
+            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') {
+        if (err.message === "SESSION_PASSWORD_NEEDED") {
             return client.signInWithPassword(apiCredentials, authParams);
         }
     }
 
-    await authParams.onError(new Error('QR auth failed'));
+    await authParams.onError(new Error("QR auth failed"));
     return client.signInUser(apiCredentials, authParams);
 }
 
-export async function sendCode(client: TelegramClient, apiCredentials: ApiCredentials, phoneNumber: string, forceSMS = false,
+export async function sendCode(
+    client: TelegramClient,
+    apiCredentials: ApiCredentials,
+    phoneNumber: string,
+    forceSMS = false
 ): Promise<{
     phoneCodeHash: string;
     isCodeViaApp: boolean;
 }> {
     try {
-        const {apiId, apiHash} = apiCredentials;
-        const sendResult = await client.invoke(new Api.auth.SendCode({
-            phoneNumber,
-            apiId,
-            apiHash,
-            settings: new Api.CodeSettings(),
-        }));
+        const { apiId, apiHash } = apiCredentials;
+        const sendResult = await client.invoke(
+            new Api.auth.SendCode({
+                phoneNumber,
+                apiId,
+                apiHash,
+                settings: new Api.CodeSettings(),
+            })
+        );
 
         // If we already sent a SMS, do not resend the phoneCode (hash may be empty)
-        if (!forceSMS || (sendResult.type instanceof Api.auth.SentCodeTypeSms)) {
+        if (!forceSMS || sendResult.type instanceof Api.auth.SentCodeTypeSms) {
             return {
                 phoneCodeHash: sendResult.phoneCodeHash,
-                isCodeViaApp: sendResult.type instanceof Api.auth.SentCodeTypeApp,
+                isCodeViaApp:
+                    sendResult.type instanceof Api.auth.SentCodeTypeApp,
             };
         }
 
-        const resendResult = await client.invoke(new Api.auth.ResendCode({
-            phoneNumber,
-            phoneCodeHash: sendResult.phoneCodeHash,
-        }));
+        const resendResult = await client.invoke(
+            new Api.auth.ResendCode({
+                phoneNumber,
+                phoneCodeHash: sendResult.phoneCodeHash,
+            })
+        );
 
         return {
             phoneCodeHash: resendResult.phoneCodeHash,
             isCodeViaApp: resendResult.type instanceof Api.auth.SentCodeTypeApp,
         };
     } catch (err) {
-        if (err.message === 'AUTH_RESTART') {
+        if (err.message === "AUTH_RESTART") {
             return client.sendCode(apiCredentials, phoneNumber, forceSMS);
         } else {
             throw err;
@@ -303,26 +341,36 @@ export async function sendCode(client: TelegramClient, apiCredentials: ApiCreden
     }
 }
 
-export async function signInWithPassword(client: TelegramClient, apiCredentials: ApiCredentials, authParams: UserAuthParams,
+export 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 passwordSrpResult = await client.invoke(
+                new Api.account.GetPassword()
+            );
             const password = await authParams.password(passwordSrpResult.hint);
             if (!password) {
-                throw new Error('Password is empty');
+                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;
+            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) {
             const shouldWeStop = await authParams.onError(err);
-            if (shouldWeStop){
-                throw new Error("AUTH_USER_CANCEL")
+            if (shouldWeStop) {
+                throw new Error("AUTH_USER_CANCEL");
             }
         }
     }
@@ -330,11 +378,15 @@ export async function signInWithPassword(client: TelegramClient, apiCredentials:
     return undefined!; // Never reached (TypeScript fix)
 }
 
-export async function signInBot(client: TelegramClient, apiCredentials: ApiCredentials, authParams: BotAuthParams) {
-    const {apiId, apiHash} = apiCredentials;
-    let {botAuthToken} = authParams;
+export async function signInBot(
+    client: TelegramClient,
+    apiCredentials: ApiCredentials,
+    authParams: BotAuthParams
+) {
+    const { apiId, apiHash } = apiCredentials;
+    let { botAuthToken } = authParams;
     if (!botAuthToken) {
-        throw new Error('a valid BotToken is required');
+        throw new Error("a valid BotToken is required");
     }
     if (typeof botAuthToken === "function") {
         let token;
@@ -347,24 +399,26 @@ export async function signInBot(client: TelegramClient, apiCredentials: ApiCrede
         }
     }
 
-    const {user} = await client.invoke(new Api.auth.ImportBotAuthorization({
-        apiId,
-        apiHash,
-        botAuthToken,
-    })) as Api.auth.Authorization;
+    const { user } = (await client.invoke(
+        new Api.auth.ImportBotAuthorization({
+            apiId,
+            apiHash,
+            botAuthToken,
+        })
+    )) as Api.auth.Authorization;
     return user;
 }
 
 export async function authFlow(
     client: TelegramClient,
     apiCredentials: ApiCredentials,
-    authParams: UserAuthParams | BotAuthParams,
+    authParams: UserAuthParams | BotAuthParams
 ) {
-    const me = 'phoneNumber' in authParams
-        ? await client.signInUser(apiCredentials, authParams)
-        : await client.signInBot(apiCredentials, authParams);
+    const me =
+        "phoneNumber" in authParams
+            ? await client.signInUser(apiCredentials, authParams)
+            : await client.signInBot(apiCredentials, authParams);
 
     // TODO @logger
-    client._log.info('Signed in successfully as ' + utils.getDisplayName(me));
+    client._log.info("Signed in successfully as " + utils.getDisplayName(me));
 }
-

+ 26 - 15
gramjs/client/bots.ts

@@ -1,24 +1,35 @@
-import type {EntityLike} from "../define";
-import {Api} from "../tl";
-import {InlineResults} from "../tl/custom/inlineResults";
+import type { EntityLike } from "../define";
+import { Api } from "../tl";
+import { InlineResults } from "../tl/custom/inlineResults";
 import GetInlineBotResults = Api.messages.GetInlineBotResults;
-import type {TelegramClient} from "./TelegramClient";
+import type { TelegramClient } from "./TelegramClient";
 
 // BotMethods
-export async function inlineQuery(client: TelegramClient, bot: EntityLike, query: string, entity?: Api.InputPeerSelf | null,
-                                  offset?: string, geoPoint?: Api.GeoPoint): Promise<InlineResults<Api.messages.BotResults>> {
+export async function inlineQuery(
+    client: TelegramClient,
+    bot: EntityLike,
+    query: string,
+    entity?: Api.InputPeerSelf | null,
+    offset?: string,
+    geoPoint?: Api.GeoPoint
+): Promise<InlineResults<Api.messages.BotResults>> {
     bot = await client.getInputEntity(bot);
     let peer = new Api.InputPeerSelf();
     if (entity) {
         peer = await client.getInputEntity(entity);
     }
-    const result = await client.invoke(new GetInlineBotResults({
-        bot: bot,
-        peer: peer,
-        query: query,
-        offset: offset || '',
-        geoPoint: geoPoint
-    }));
-    return new InlineResults(client, result, entity = entity ? peer : undefined);
+    const result = await client.invoke(
+        new GetInlineBotResults({
+            bot: bot,
+            peer: peer,
+            query: query,
+            offset: offset || "",
+            geoPoint: geoPoint,
+        })
+    );
+    return new InlineResults(
+        client,
+        result,
+        (entity = entity ? peer : undefined)
+    );
 }
-

+ 26 - 17
gramjs/client/buttons.ts

@@ -1,11 +1,19 @@
-import {Api} from "../tl";
-import type {ButtonLike, MarkupLike} from "../define";
-import {Button} from "../tl/custom/button";
-import {MessageButton} from "../tl/custom/messageButton";
-import {isArrayLike} from "../Helpers";
+import { Api } from "../tl";
+import type { ButtonLike, MarkupLike } from "../define";
+import { Button } from "../tl/custom/button";
+import { MessageButton } from "../tl/custom/messageButton";
+import { isArrayLike } from "../Helpers";
 
 // ButtonMethods
-export function buildReplyMarkup(buttons: Api.TypeReplyMarkup | undefined | ButtonLike | ButtonLike[] | ButtonLike[][], inlineOnly: boolean = false): Api.TypeReplyMarkup | undefined {
+export function buildReplyMarkup(
+    buttons:
+        | Api.TypeReplyMarkup
+        | undefined
+        | ButtonLike
+        | ButtonLike[]
+        | ButtonLike[][],
+    inlineOnly: boolean = false
+): Api.TypeReplyMarkup | undefined {
     if (buttons == undefined) {
         return undefined;
     }
@@ -59,25 +67,26 @@ export function buildReplyMarkup(buttons: Api.TypeReplyMarkup | undefined | Butt
             }
         }
         if (current) {
-            rows.push(new Api.KeyboardButtonRow({
-                buttons: current,
-            }))
+            rows.push(
+                new Api.KeyboardButtonRow({
+                    buttons: current,
+                })
+            );
         }
     }
     if (inlineOnly && isNormal) {
-        throw new Error('You cannot use non-inline buttons here');
+        throw new Error("You cannot use non-inline buttons here");
     } else if (isInline === isNormal && isNormal) {
-        throw new Error('You cannot mix inline with normal buttons');
+        throw new Error("You cannot mix inline with normal buttons");
     } else if (isInline) {
         return new Api.ReplyInlineMarkup({
-            rows: rows
-        })
+            rows: rows,
+        });
     }
-    return new Api.ReplyKeyboardMarkup(({
+    return new Api.ReplyKeyboardMarkup({
         rows: rows,
         resize: resize,
         singleUse: singleUse,
-        selective: selective
-    }))
+        selective: selective,
+    });
 }
-

+ 162 - 109
gramjs/client/chats.ts

@@ -1,44 +1,43 @@
-import type{TelegramClient} from "./TelegramClient";
-import type{EntitiesLike, Entity, EntityLike, ValueOf} from "../define";
-import {sleep, getMinBigInt, TotalList} from '../Helpers';
-import {RequestIter} from "../requestIter";
-import {helpers, utils} from "../";
-import {Api} from "../tl";
-import bigInt, {BigInteger} from "big-integer";
-
+import type { TelegramClient } from "./TelegramClient";
+import type { EntitiesLike, Entity, EntityLike, ValueOf } from "../define";
+import { sleep, getMinBigInt, TotalList } from "../Helpers";
+import { RequestIter } from "../requestIter";
+import { helpers, utils } from "../";
+import { Api } from "../tl";
+import bigInt, { BigInteger } from "big-integer";
 
 const _MAX_PARTICIPANTS_CHUNK_SIZE = 200;
 const _MAX_ADMIN_LOG_CHUNK_SIZE = 100;
 const _MAX_PROFILE_PHOTO_CHUNK_SIZE = 100;
 
 interface ChatActionInterface {
-    delay: number,
-    autoCancel: boolean,
+    delay: number;
+    autoCancel: boolean;
 }
 
 class _ChatAction {
     static _str_mapping = {
-        'typing': new Api.SendMessageTypingAction(),
-        'contact': new Api.SendMessageChooseContactAction(),
-        'game': new Api.SendMessageGamePlayAction(),
-        'location': new Api.SendMessageGeoLocationAction(),
-
-        'record-audio': new Api.SendMessageRecordAudioAction(),
-        'record-voice': new Api.SendMessageRecordAudioAction(),  //alias
-        'record-round': new Api.SendMessageRecordRoundAction(),
-        'record-video': new Api.SendMessageRecordVideoAction(),
-
-        'audio': new Api.SendMessageUploadAudioAction({progress: 1,}),
-        'voice': new Api.SendMessageUploadAudioAction({progress: 1,}),  // alias
-        'song': new Api.SendMessageUploadAudioAction({progress: 1,}), // alias
-        'round': new Api.SendMessageUploadRoundAction({progress: 1,}),
-        'video': new Api.SendMessageUploadVideoAction({progress: 1,}),
-
-        'photo': new Api.SendMessageUploadPhotoAction({progress: 1,}),
-        'document': new Api.SendMessageUploadDocumentAction({progress: 1,}),
-        'file': new Api.SendMessageUploadDocumentAction({progress: 1,}),  // alias
-
-        'cancel': new Api.SendMessageCancelAction()
+        typing: new Api.SendMessageTypingAction(),
+        contact: new Api.SendMessageChooseContactAction(),
+        game: new Api.SendMessageGamePlayAction(),
+        location: new Api.SendMessageGeoLocationAction(),
+
+        "record-audio": new Api.SendMessageRecordAudioAction(),
+        "record-voice": new Api.SendMessageRecordAudioAction(), //alias
+        "record-round": new Api.SendMessageRecordRoundAction(),
+        "record-video": new Api.SendMessageRecordVideoAction(),
+
+        audio: new Api.SendMessageUploadAudioAction({ progress: 1 }),
+        voice: new Api.SendMessageUploadAudioAction({ progress: 1 }), // alias
+        song: new Api.SendMessageUploadAudioAction({ progress: 1 }), // alias
+        round: new Api.SendMessageUploadRoundAction({ progress: 1 }),
+        video: new Api.SendMessageUploadVideoAction({ progress: 1 }),
+
+        photo: new Api.SendMessageUploadPhotoAction({ progress: 1 }),
+        document: new Api.SendMessageUploadDocumentAction({ progress: 1 }),
+        file: new Api.SendMessageUploadDocumentAction({ progress: 1 }), // alias
+
+        cancel: new Api.SendMessageCancelAction(),
     };
 
     private _client: TelegramClient;
@@ -50,10 +49,15 @@ class _ChatAction {
     private _task: null;
     private _running: boolean;
 
-    constructor(client: TelegramClient, chat: EntityLike, action: ValueOf<typeof _ChatAction._str_mapping>, params: ChatActionInterface = {
-        delay: 4,
-        autoCancel: true,
-    }) {
+    constructor(
+        client: TelegramClient,
+        chat: EntityLike,
+        action: ValueOf<typeof _ChatAction._str_mapping>,
+        params: ChatActionInterface = {
+            delay: 4,
+            autoCancel: true,
+        }
+    ) {
         this._client = client;
         this._chat = chat;
         this._action = action;
@@ -67,7 +71,7 @@ class _ChatAction {
     async start() {
         this._request = new Api.messages.SetTyping({
             peer: this._chat,
-            action: this._action
+            action: this._action,
         });
         this._running = true;
         this._update();
@@ -76,10 +80,12 @@ class _ChatAction {
     async stop() {
         this._running = false;
         if (this.autoCancel) {
-            await this._client.invoke(new Api.messages.SetTyping({
-                peer: this._chat,
-                action: new Api.SendMessageCancelAction()
-            }));
+            await this._client.invoke(
+                new Api.messages.SetTyping({
+                    peer: this._chat,
+                    action: new Api.SendMessageCancelAction(),
+                })
+            );
         }
     }
 
@@ -90,32 +96,41 @@ class _ChatAction {
             }
             await sleep(this._delay * 1000);
         }
-
     }
 
     progress(current: number, total: number) {
-        if ('progress' in this._action) {
-            this._action.progress = 100 * Math.round(current / total)
+        if ("progress" in this._action) {
+            this._action.progress = 100 * Math.round(current / total);
         }
-
     }
 }
 
 interface ParticipantsIterInterface {
-    entity: EntityLike,
-    filter: any,
-    search?: string
+    entity: EntityLike;
+    filter: any;
+    search?: string;
 }
 
 export class _ParticipantsIter extends RequestIter {
     private filterEntity: ((entity: Entity) => boolean) | undefined;
     private requests?: Api.channels.GetParticipants[];
 
-    async _init({entity, filter, search}: ParticipantsIterInterface): Promise<boolean | void> {
+    async _init({
+        entity,
+        filter,
+        search,
+    }: ParticipantsIterInterface): Promise<boolean | void> {
         if (filter && filter.constructor === Function) {
-            if ([Api.ChannelParticipantsBanned, Api.ChannelParticipantsKicked, Api.ChannelParticipantsSearch, Api.ChannelParticipantsContacts].includes(filter)) {
+            if (
+                [
+                    Api.ChannelParticipantsBanned,
+                    Api.ChannelParticipantsKicked,
+                    Api.ChannelParticipantsSearch,
+                    Api.ChannelParticipantsContacts,
+                ].includes(filter)
+            ) {
                 filter = new filter({
-                    q: '',
+                    q: "",
                 });
             } else {
                 filter = new filter();
@@ -127,43 +142,65 @@ export class _ParticipantsIter extends RequestIter {
             // We need to 'search' ourselves unless we have a PeerChannel
             search = search.toLowerCase();
             this.filterEntity = (entity: Entity) => {
-                return utils.getDisplayName(entity).toLowerCase().includes(<string>search) ||
-                    ('username' in entity ? entity.username || '' : '').toLowerCase().includes(<string>search)
-            }
+                return (
+                    utils
+                        .getDisplayName(entity)
+                        .toLowerCase()
+                        .includes(<string>search) ||
+                    ("username" in entity ? entity.username || "" : "")
+                        .toLowerCase()
+                        .includes(<string>search)
+                );
+            };
         } else {
             this.filterEntity = (entity: Entity) => true;
         }
         // Only used for channels, but we should always set the attribute
         this.requests = [];
         if (ty == helpers._EntityType.CHANNEL) {
-            const channel = (await this.client.invoke(new Api.channels.GetFullChannel({
-                channel: entity
-            })));
+            const channel = await this.client.invoke(
+                new Api.channels.GetFullChannel({
+                    channel: entity,
+                })
+            );
             if (!(channel.fullChat instanceof Api.ChatFull)) {
                 this.total = channel.fullChat.participantsCount;
             }
             if (this.total && this.total <= 0) {
                 return false;
             }
-            this.requests.push(new Api.channels.GetParticipants({
-                channel: entity,
-                filter: filter || new Api.ChannelParticipantsSearch({
-                    q: search || '',
-                }),
-                offset: 0,
-                limit: _MAX_PARTICIPANTS_CHUNK_SIZE,
-                hash: 0,
-            }))
+            this.requests.push(
+                new Api.channels.GetParticipants({
+                    channel: entity,
+                    filter:
+                        filter ||
+                        new Api.ChannelParticipantsSearch({
+                            q: search || "",
+                        }),
+                    offset: 0,
+                    limit: _MAX_PARTICIPANTS_CHUNK_SIZE,
+                    hash: 0,
+                })
+            );
         } else if (ty == helpers._EntityType.CHAT) {
             if (!("chatId" in entity)) {
-                throw new Error("Found chat without id " + JSON.stringify(entity));
+                throw new Error(
+                    "Found chat without id " + JSON.stringify(entity)
+                );
             }
-            const full = await this.client.invoke(new Api.messages.GetFullChat({
-                chatId: entity.chatId
-            }));
+            const full = await this.client.invoke(
+                new Api.messages.GetFullChat({
+                    chatId: entity.chatId,
+                })
+            );
 
             if (full.fullChat instanceof Api.ChatFull) {
-                if (!(full.fullChat.participants instanceof Api.ChatParticipantsForbidden)) {
+                if (
+                    !(
+                        full.fullChat.participants instanceof
+                        Api.ChatParticipantsForbidden
+                    )
+                ) {
                     this.total = full.fullChat.participants.participants.length;
                 } else {
                     this.total = 0;
@@ -174,7 +211,8 @@ export class _ParticipantsIter extends RequestIter {
                 for (const user of full.users) {
                     users.set(user.id, user);
                 }
-                for (const participant of full.fullChat.participants.participants) {
+                for (const participant of full.fullChat.participants
+                    .participants) {
                     const user = users.get(participant.userId);
                     if (!this.filterEntity(user)) {
                         continue;
@@ -184,7 +222,6 @@ export class _ParticipantsIter extends RequestIter {
                 }
                 return true;
             }
-
         } else {
             this.total = 1;
             if (this.limit != 0) {
@@ -204,21 +241,24 @@ export class _ParticipantsIter extends RequestIter {
             return true;
         }
         this.requests[0].limit = Math.min(
-            this.limit - this.requests[0].offset, _MAX_PARTICIPANTS_CHUNK_SIZE
+            this.limit - this.requests[0].offset,
+            _MAX_PARTICIPANTS_CHUNK_SIZE
         );
         if (this.requests[0].offset > this.limit) {
             return true;
         }
         const results = [];
         for (const request of this.requests) {
-            results.push(
-                await this.client.invoke(request)
-            );
+            results.push(await this.client.invoke(request));
         }
 
         for (let i = this.requests.length - 1; i >= 0; i--) {
             const participants = results[i];
-            if (participants instanceof Api.channels.ChannelParticipantsNotModified || !participants.users.length) {
+            if (
+                participants instanceof
+                    Api.channels.ChannelParticipantsNotModified ||
+                !participants.users.length
+            ) {
                 this.requests.splice(i, 1);
                 continue;
             }
@@ -229,7 +269,7 @@ export class _ParticipantsIter extends RequestIter {
                 users.set(user.id, user);
             }
             for (const participant of participants.participants) {
-                if (!("userId" in participant)){
+                if (!("userId" in participant)) {
                     continue;
                 }
                 const user = users.get(participant.userId);
@@ -246,7 +286,6 @@ export class _ParticipantsIter extends RequestIter {
     [Symbol.asyncIterator](): AsyncIterator<Api.User, any, undefined> {
         return super[Symbol.asyncIterator]();
     }
-
 }
 
 interface _AdminLogFilterInterface {
@@ -278,31 +317,37 @@ class _AdminLogIter extends RequestIter {
     private entity?: Api.TypeInputPeer;
     private request?: Api.channels.GetAdminLog;
 
-    async _init(entity: EntityLike, searchArgs?: _AdminLogSearchInterface, filterArgs?: _AdminLogFilterInterface) {
+    async _init(
+        entity: EntityLike,
+        searchArgs?: _AdminLogSearchInterface,
+        filterArgs?: _AdminLogFilterInterface
+    ) {
         let eventsFilter = undefined;
 
-        if (filterArgs && Object.values(filterArgs).find(element => element === true)) {
+        if (
+            filterArgs &&
+            Object.values(filterArgs).find((element) => element === true)
+        ) {
             eventsFilter = new Api.ChannelAdminLogEventsFilter({
-                ...filterArgs
+                ...filterArgs,
             });
         }
         this.entity = await this.client.getInputEntity(entity);
-        const adminList = []
+        const adminList = [];
         if (searchArgs && searchArgs.admins) {
             for (const admin of searchArgs.admins) {
-                adminList.push(await this.client.getInputEntity(admin))
+                adminList.push(await this.client.getInputEntity(admin));
             }
         }
         this.request = new Api.channels.GetAdminLog({
-                channel: this.entity,
-                q: searchArgs?.search || '',
-                minId: searchArgs?.minId,
-                maxId: searchArgs?.maxId,
-                limit: 0,
-                eventsFilter: eventsFilter,
-                admins: adminList || undefined,
-            }
-        )
+            channel: this.entity,
+            q: searchArgs?.search || "",
+            minId: searchArgs?.minId,
+            maxId: searchArgs?.maxId,
+            limit: 0,
+            eventsFilter: eventsFilter,
+            admins: adminList || undefined,
+        });
     }
 
     async _loadNextChunk() {
@@ -321,38 +366,46 @@ class _AdminLogIter extends RequestIter {
         }
         this.request.maxId = getMinBigInt([bigInt.zero, ...eventIds]);
         for (const ev of r.events) {
-            if (ev.action instanceof Api.ChannelAdminLogEventActionEditMessage) {
+            if (
+                ev.action instanceof Api.ChannelAdminLogEventActionEditMessage
+            ) {
                 // @ts-ignore
                 // TODO ev.action.prevMessage._finishInit(this.client, entities, this.entity);
                 // @ts-ignore
                 // TODO ev.action.newMessage._finishInit(this.client, entities, this.entity);
-
             }
         }
     }
 }
 
-
 export interface IterParticipantsParams {
-    limit?: number,
-    search?: string,
-    filter?: Api.TypeChannelParticipantsFilter,
+    limit?: number;
+    search?: string;
+    filter?: Api.TypeChannelParticipantsFilter;
 }
 
-export function iterParticipants(client: TelegramClient, entity: EntityLike, {
-    limit,
-    search,
-    filter,
-}: IterParticipantsParams) {
-    return new _ParticipantsIter(client, limit ?? Number.MAX_SAFE_INTEGER, {},
+export function iterParticipants(
+    client: TelegramClient,
+    entity: EntityLike,
+    { limit, search, filter }: IterParticipantsParams
+) {
+    return new _ParticipantsIter(
+        client,
+        limit ?? Number.MAX_SAFE_INTEGER,
+        {},
         {
             entity: entity,
             filter: filter,
-            search: search
-        });
+            search: search,
+        }
+    );
 }
 
-export async function getParticipants(client: TelegramClient, entity: EntityLike, params: IterParticipantsParams) {
+export async function getParticipants(
+    client: TelegramClient,
+    entity: EntityLike,
+    params: IterParticipantsParams
+) {
     const it = client.iterParticipants(entity, params);
-    return await it.collect() as TotalList<Api.User>;
+    return (await it.collect()) as TotalList<Api.User>;
 }

+ 231 - 0
gramjs/client/dialogs.ts

@@ -0,0 +1,231 @@
+import { Api } from "../tl";
+import { RequestIter } from "../requestIter";
+import { TelegramClient, utils } from "../index";
+import { Message } from "../tl/custom/message";
+import { Dialog } from "../tl/custom/dialog";
+import { DateLike, EntityLike, FileLike, MarkupLike } from "../define";
+import { IterMessagesParams } from "./messages";
+import { TotalList } from "../Helpers";
+
+const _MAX_CHUNK_SIZE = 100;
+
+/**
+ Get the key to get messages from a dialog.
+
+ We cannot just use the message ID because channels share message IDs,
+ and the peer ID is required to distinguish between them. But it is not
+ necessary in small group chats and private chats.
+ * @param {Api.TypePeer} [peer] the dialog peer
+ * @param {number} [messageId] the message id
+ * @return {[number,number]} the channel id and message id
+ */
+function _dialogMessageKey(peer: Api.TypePeer, messageId: number): string {
+    // can't use arrays as keys for map :( need to convert to string.
+    return (
+        "" +
+        [
+            peer instanceof Api.PeerChannel ? peer.channelId : undefined,
+            messageId,
+        ]
+    );
+}
+
+interface DialogsIterInterface {
+    offsetDate: number;
+    offsetId: number;
+    offsetPeer: Api.TypePeer;
+    ignorePinned: boolean;
+    ignoreMigrated: boolean;
+    folder: number;
+}
+
+export class _DialogsIter extends RequestIter {
+    private request?: Api.messages.GetDialogs;
+    private seen?: Set<any>;
+    private offsetDate?: number;
+    private ignoreMigrated?: boolean;
+
+    async _init({
+        offsetDate,
+        offsetId,
+        offsetPeer,
+        ignorePinned,
+        ignoreMigrated,
+        folder,
+    }: DialogsIterInterface) {
+        this.request = new Api.messages.GetDialogs({
+            offsetDate,
+            offsetId,
+            offsetPeer,
+            limit: 1,
+            hash: 0,
+            excludePinned: ignorePinned,
+            folderId: folder,
+        });
+        if (this.limit <= 0) {
+            // Special case, get a single dialog and determine count
+            const dialogs = await this.client.invoke(this.request);
+            if ("count" in dialogs) {
+                this.total = dialogs.count;
+            } else {
+                this.total = dialogs.dialogs.length;
+            }
+
+            return true;
+        }
+
+        this.seen = new Set();
+        this.offsetDate = offsetDate;
+        this.ignoreMigrated = ignoreMigrated;
+    }
+
+    async _loadNextChunk(): Promise<boolean | undefined> {
+        if (!this.request || !this.seen || !this.buffer) {
+            return;
+        }
+        this.request.limit = Math.min(this.left, _MAX_CHUNK_SIZE);
+        const r = await this.client.invoke(this.request);
+        if (r instanceof Api.messages.DialogsNotModified) {
+            return;
+        }
+        if ("count" in r) {
+            this.total = r.count;
+        } else {
+            this.total = r.dialogs.length;
+        }
+        const entities = new Map<number, Api.TypeUser | Api.TypeChat>();
+        const messages = new Map<string, Message>();
+
+        for (const entity of [...r.users, ...r.chats]) {
+            if (
+                entity instanceof Api.UserEmpty ||
+                entity instanceof Api.ChatEmpty
+            ) {
+                continue;
+            }
+            entities.set(utils.getPeerId(entity), entity);
+        }
+        for (const m of r.messages) {
+            let message = m as unknown as Message;
+            try {
+                // todo make sure this never fails
+                message._finishInit(this.client, entities, undefined);
+            } catch (e) {
+                this.client._log.error(
+                    "Got error while trying to finish init message with id " +
+                        m.id
+                );
+                if (this.client._log.canSend("error")) {
+                    console.error(e);
+                }
+            }
+            messages.set(
+                _dialogMessageKey(message.peerId, message.id),
+                message
+            );
+        }
+
+        for (const d of r.dialogs) {
+            if (d instanceof Api.DialogFolder) {
+                continue;
+            }
+            const message = messages.get(
+                _dialogMessageKey(d.peer, d.topMessage)
+            );
+            if (this.offsetDate != undefined) {
+                const date = message?.date;
+                if (date != undefined || date > this.offsetDate) {
+                    continue;
+                }
+            }
+            const peerId = utils.getPeerId(d.peer);
+            if (!this.seen.has(peerId)) {
+                this.seen.add(peerId);
+                if (!entities.has(peerId)) {
+                    /*
+                     > In which case can a UserEmpty appear in the list of banned members?
+                     > In a very rare cases. This is possible but isn't an expected behavior.
+                     Real world example: https://t.me/TelethonChat/271471
+                     */
+                    continue;
+                }
+                const cd = new Dialog(this.client, d, entities, message);
+                if (
+                    !this.ignoreMigrated ||
+                    (cd.entity != undefined && "migratedTo" in cd.entity)
+                ) {
+                    this.buffer.push(cd);
+                }
+            }
+        }
+        if (
+            r.dialogs.length < this.request.limit ||
+            !(r instanceof Api.messages.DialogsSlice)
+        ) {
+            return true;
+        }
+        let lastMessage;
+        for (let dialog of r.dialogs.reverse()) {
+            lastMessage = messages.get(
+                _dialogMessageKey(dialog.peer, dialog.topMessage)
+            );
+            if (lastMessage) {
+                break;
+            }
+        }
+        this.request.excludePinned = true;
+        this.request.offsetId = lastMessage ? lastMessage.id : 0;
+        this.request.offsetDate = lastMessage ? lastMessage.date : undefined;
+        this.request.offsetPeer =
+            this.buffer[this.buffer.length - 1].inputEntity;
+    }
+}
+
+export interface IterDialogsParams {
+    limit?: number;
+    offsetDate?: DateLike;
+    offsetId?: number;
+    offsetPeer?: EntityLike;
+    ignorePinned?: boolean;
+    ignoreMigrated?: boolean;
+    folder?: number;
+    archived?: boolean;
+}
+
+export function iterDialogs(
+    client: TelegramClient,
+    {
+        limit = undefined,
+        offsetDate = undefined,
+        offsetId = 0,
+        offsetPeer = new Api.InputPeerEmpty(),
+        ignorePinned = false,
+        ignoreMigrated = false,
+        folder = undefined,
+        archived = undefined,
+    }: IterDialogsParams
+): _DialogsIter {
+    if (archived != undefined) {
+        folder = archived ? 1 : 0;
+    }
+
+    return new _DialogsIter(
+        client,
+        limit,
+        {},
+        {
+            offsetDate,
+            offsetId,
+            offsetPeer,
+            ignorePinned,
+            ignoreMigrated,
+            folder,
+        }
+    );
+}
+export async function getDialogs(
+    client: TelegramClient,
+    params: IterDialogsParams
+): Promise<TotalList<Dialog>> {
+    return (await client.iterDialogs(params).collect()) as TotalList<Dialog>;
+}

+ 149 - 99
gramjs/client/downloads.ts

@@ -1,10 +1,9 @@
-import {Api} from '../tl';
-import type {TelegramClient} from './TelegramClient';
-import {getAppropriatedPartSize, strippedPhotoToJpg} from '../Utils';
-import {sleep} from '../Helpers';
-import {MTProtoSender} from "../network";
-import type {Message} from "../tl/custom/message";
-
+import { Api } from "../tl";
+import type { TelegramClient } from "./TelegramClient";
+import { getAppropriatedPartSize, strippedPhotoToJpg } from "../Utils";
+import { sleep } from "../Helpers";
+import { MTProtoSender } from "../network";
+import type { Message } from "../tl/custom/message";
 
 export interface progressCallback {
     (
@@ -32,7 +31,7 @@ interface Deferred {
 }
 
 // All types
-const sizeTypes = ['w', 'y', 'd', 'x', 'c', 'm', 'b', 'a', 's'];
+const sizeTypes = ["w", "y", "d", "x", "c", "m", "b", "a", "s"];
 
 // Chunk sizes for `upload.getFile` must be multiple of the smallest size
 const MIN_CHUNK_SIZE = 4096;
@@ -43,15 +42,17 @@ const REQUEST_TIMEOUT = 15000;
 export async function downloadFile(
     client: TelegramClient,
     inputLocation: Api.TypeInputFileLocation,
-    fileParams: DownloadFileParams,
+    fileParams: DownloadFileParams
 ) {
-    let {partSizeKb, fileSize, workers = 1, end} = fileParams;
-    const {dcId, progressCallback, start = 0} = fileParams;
+    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;
+        partSizeKb = fileSize
+            ? getAppropriatedPartSize(fileSize)
+            : DEFAULT_CHUNK_SIZE;
     }
 
     // @ts-ignore
@@ -59,18 +60,20 @@ export async function downloadFile(
     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}`);
+        throw new Error(
+            `The part size must be evenly divisible by ${MIN_CHUNK_SIZE}`
+        );
     }
 
     let sender: MTProtoSender;
     if (dcId) {
         try {
             sender = await client._borrowExportedSender(dcId);
-            client._log.debug(`Finished creating sender for ${dcId}`)
+            client._log.debug(`Finished creating sender for ${dcId}`);
         } catch (e) {
             // This should never raise
             client._log.error(e);
-            if (e.message === 'DC_ID_INVALID') {
+            if (e.message === "DC_ID_INVALID") {
                 // Can't export a sender for the ID we are currently in
                 sender = client._sender;
             } else {
@@ -98,8 +101,11 @@ export async function downloadFile(
         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;
+        if (
+            Math.floor(offset / ONE_MB) !==
+            Math.floor((offset + limit - 1) / ONE_MB)
+        ) {
+            limit = ONE_MB - (offset % ONE_MB);
             isPrecise = true;
         }
 
@@ -109,50 +115,56 @@ export async function downloadFile(
             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');
+        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);
                     }
 
-                    progress += (1 / partsCount);
-                    progressCallback(progress);
-                }
+                    if (!end && result.bytes.length < limit) {
+                        hasEnded = true;
+                    }
 
-                if (!end && (result.bytes.length < limit)) {
+                    return result.bytes;
+                } catch (err) {
                     hasEnded = true;
+                    throw err;
+                } finally {
+                    foreman.releaseWorker();
                 }
-
-                return result.bytes;
-            } catch (err) {
-                hasEnded = true;
-                throw err;
-            } finally {
-                foreman.releaseWorker();
-            }
-        })());
+            })()
+        );
 
         offset += limit;
 
-        if (end && (offset > end)) {
+        if (end && offset > end) {
             break;
         }
     }
 
     const results = await Promise.all(promises);
     const buffers = results.filter(Boolean);
-    const totalLength = end ? (end + 1) - start : undefined;
+    const totalLength = end ? end + 1 - start : undefined;
     return Buffer.concat(buffers, totalLength);
 }
 
@@ -160,8 +172,7 @@ class Foreman {
     private deferred: Deferred | undefined;
     private activeWorkers = 0;
 
-    constructor(private maxWorkers: number) {
-    }
+    constructor(private maxWorkers: number) {}
 
     requestWorker() {
         this.activeWorkers++;
@@ -177,14 +188,14 @@ class Foreman {
     releaseWorker() {
         this.activeWorkers--;
 
-        if (this.deferred && (this.activeWorkers <= this.maxWorkers)) {
+        if (this.deferred && this.activeWorkers <= this.maxWorkers) {
             this.deferred.resolve();
         }
     }
 }
 
 function createDeferred(): Deferred {
-    let resolve: Deferred['resolve'];
+    let resolve: Deferred["resolve"];
     const promise = new Promise((_resolve) => {
         resolve = _resolve;
     });
@@ -196,54 +207,66 @@ function createDeferred(): Deferred {
 }
 
 export interface DownloadMediaInterface {
-    sizeType?: string,
+    sizeType?: string;
     /** where to start downloading **/
-    start?: number,
+    start?: number;
     /** where to stop downloading **/
-    end?: number,
-    progressCallback?: progressCallback,
-    workers?: number,
-
+    end?: number;
+    progressCallback?: progressCallback;
+    workers?: number;
 }
 
-export async function downloadMedia(client: TelegramClient, messageOrMedia: Api.Message | Api.TypeMessageMedia | Message , args: DownloadMediaInterface): Promise<Buffer> {
+export async function downloadMedia(
+    client: TelegramClient,
+    messageOrMedia: Api.Message | Api.TypeMessageMedia | Message,
+    args: DownloadMediaInterface
+): Promise<Buffer> {
     let date;
     let media;
     if (messageOrMedia instanceof Api.Message) {
-        media = messageOrMedia.media
+        media = messageOrMedia.media;
     } else {
-        media = messageOrMedia
+        media = messageOrMedia;
     }
-    if (typeof media == 'string') {
-        throw new Error('not implemented')
+    if (typeof media == "string") {
+        throw new Error("not implemented");
     }
 
     if (media instanceof Api.MessageMediaWebPage) {
         if (media.webpage instanceof Api.WebPage) {
-            media = media.webpage.document || media.webpage.photo
+            media = media.webpage.document || media.webpage.photo;
         }
     }
     if (media instanceof Api.MessageMediaPhoto || media instanceof Api.Photo) {
         return client._downloadPhoto(media, args);
-    } else if (media instanceof Api.MessageMediaDocument || media instanceof Api.Document) {
-        return client._downloadDocument(media, args)
+    } else if (
+        media instanceof Api.MessageMediaDocument ||
+        media instanceof Api.Document
+    ) {
+        return client._downloadDocument(media, args);
     } else if (media instanceof Api.MessageMediaContact) {
-        return client._downloadContact(media, args)
-    } else if (media instanceof Api.WebDocument || media instanceof Api.WebDocumentNoProxy) {
-        return client._downloadWebDocument(media, args)
+        return client._downloadContact(media, args);
+    } else if (
+        media instanceof Api.WebDocument ||
+        media instanceof Api.WebDocumentNoProxy
+    ) {
+        return client._downloadWebDocument(media, args);
     } else {
         return Buffer.alloc(0);
     }
 }
 
-export async function _downloadDocument(client: TelegramClient, doc: Api.MessageMediaDocument | Api.Document, args: DownloadMediaInterface): Promise<Buffer> {
+export async function _downloadDocument(
+    client: TelegramClient,
+    doc: Api.MessageMediaDocument | Api.Document,
+    args: DownloadMediaInterface
+): Promise<Buffer> {
     if (doc instanceof Api.MessageMediaDocument) {
         if (!doc.document) {
             return Buffer.alloc(0);
         }
 
-        doc = doc.document
-
+        doc = doc.document;
     }
     if (!(doc instanceof Api.Document)) {
         return Buffer.alloc(0);
@@ -252,12 +275,16 @@ export async function _downloadDocument(client: TelegramClient, doc: Api.Message
     let size = undefined;
     if (args.sizeType) {
         size = doc.thumbs ? pickFileSize(doc.thumbs, args.sizeType) : undefined;
-        if (!size && doc.mimeType.startsWith('video/')) {
+        if (!size && doc.mimeType.startsWith("video/")) {
             return Buffer.alloc(0);
         }
 
-        if (size && (size instanceof Api.PhotoCachedSize || size instanceof Api.PhotoStrippedSize)) {
-            return client._downloadCachedPhotoSize(size)
+        if (
+            size &&
+            (size instanceof Api.PhotoCachedSize ||
+                size instanceof Api.PhotoStrippedSize)
+        ) {
+            return client._downloadCachedPhotoSize(size);
         }
     }
     return client.downloadFile(
@@ -265,26 +292,36 @@ export async function _downloadDocument(client: TelegramClient, doc: Api.Message
             id: doc.id,
             accessHash: doc.accessHash,
             fileReference: doc.fileReference,
-            thumbSize: size ? size.type : '',
+            thumbSize: size ? size.type : "",
         }),
         {
-            fileSize: (size && !(size instanceof Api.PhotoSizeEmpty)) ? size.size : doc.size,
+            fileSize:
+                size && !(size instanceof Api.PhotoSizeEmpty)
+                    ? size.size
+                    : doc.size,
             progressCallback: args.progressCallback,
             start: args.start,
             end: args.end,
             dcId: doc.dcId,
             workers: args.workers,
-        },
-    )
-
+        }
+    );
 }
 
-export async function _downloadContact(client: TelegramClient, media: Api.MessageMediaContact, args: DownloadMediaInterface): Promise<Buffer> {
-    throw new Error('not implemented')
+export async function _downloadContact(
+    client: TelegramClient,
+    media: Api.MessageMediaContact,
+    args: DownloadMediaInterface
+): Promise<Buffer> {
+    throw new Error("not implemented");
 }
 
-export async function _downloadWebDocument(client: TelegramClient, media: Api.WebDocument | Api.WebDocumentNoProxy, args: DownloadMediaInterface): Promise<Buffer> {
-    throw new Error('not implemented')
+export async function _downloadWebDocument(
+    client: TelegramClient,
+    media: Api.WebDocument | Api.WebDocumentNoProxy,
+    args: DownloadMediaInterface
+): Promise<Buffer> {
+    throw new Error("not implemented");
 }
 
 function pickFileSize(sizes: Api.TypePhotoSize[], sizeType: string) {
@@ -295,43 +332,57 @@ function pickFileSize(sizes: Api.TypePhotoSize[], sizeType: string) {
     let size;
     for (let i = indexOfSize; i < sizeTypes.length; i++) {
         size = sizes.find((s) => s.type === sizeTypes[i]);
-        if (size && !(size instanceof Api.PhotoSizeProgressive || size instanceof Api.PhotoPathSize)) {
-            return size
+        if (
+            size &&
+            !(
+                size instanceof Api.PhotoSizeProgressive ||
+                size instanceof Api.PhotoPathSize
+            )
+        ) {
+            return size;
         }
     }
     return undefined;
-
 }
 
-export function _downloadCachedPhotoSize(client: TelegramClient, size: Api.PhotoCachedSize | Api.PhotoStrippedSize) {
+export function _downloadCachedPhotoSize(
+    client: TelegramClient,
+    size: Api.PhotoCachedSize | Api.PhotoStrippedSize
+) {
     // No need to download anything, simply write the bytes
     let data;
     if (size instanceof Api.PhotoStrippedSize) {
-        data = strippedPhotoToJpg(size.bytes)
+        data = strippedPhotoToJpg(size.bytes);
     } else {
-        data = size.bytes
+        data = size.bytes;
     }
-    return data
+    return data;
 }
 
-export async function _downloadPhoto(client: TelegramClient, photo: Api.MessageMediaPhoto | Api.Photo, args: DownloadMediaInterface): Promise<Buffer> {
+export async function _downloadPhoto(
+    client: TelegramClient,
+    photo: Api.MessageMediaPhoto | Api.Photo,
+    args: DownloadMediaInterface
+): Promise<Buffer> {
     if (photo instanceof Api.MessageMediaPhoto) {
         if (photo.photo instanceof Api.PhotoEmpty || !photo.photo) {
             return Buffer.alloc(0);
         }
-        photo = photo.photo
-
+        photo = photo.photo;
     }
     if (!(photo instanceof Api.Photo)) {
         return Buffer.alloc(0);
     }
     const size = pickFileSize(photo.sizes, args.sizeType || sizeTypes[0]);
-    if (!size || (size instanceof Api.PhotoSizeEmpty)) {
+    if (!size || size instanceof Api.PhotoSizeEmpty) {
         return Buffer.alloc(0);
     }
 
-    if (size instanceof Api.PhotoCachedSize || size instanceof Api.PhotoStrippedSize) {
-        return client._downloadCachedPhotoSize(size)
+    if (
+        size instanceof Api.PhotoCachedSize ||
+        size instanceof Api.PhotoStrippedSize
+    ) {
+        return client._downloadCachedPhotoSize(size);
     }
     return client.downloadFile(
         new Api.InputPhotoFileLocation({
@@ -344,7 +395,6 @@ export async function _downloadPhoto(client: TelegramClient, photo: Api.MessageM
             dcId: photo.dcId,
             fileSize: size.size,
             progressCallback: args.progressCallback,
-        },
-    )
-
+        }
+    );
 }

+ 40 - 26
gramjs/client/messageParse.ts

@@ -1,47 +1,61 @@
-import {sanitizeParseMode} from "../Utils";
-import {Api} from "../tl";
-import type {EntityLike, ValueOf} from "../define";
-import type {TelegramClient} from "./TelegramClient";
+import { sanitizeParseMode } from "../Utils";
+import { Api } from "../tl";
+import type { EntityLike, ValueOf } from "../define";
+import type { TelegramClient } from "./TelegramClient";
 
-export type messageEntities = typeof Api.MessageEntityBold | typeof Api.MessageEntityItalic |
-    typeof Api.MessageEntityStrike | typeof Api.MessageEntityCode | typeof Api.MessageEntityPre;
+export type messageEntities =
+    | typeof Api.MessageEntityBold
+    | typeof Api.MessageEntityItalic
+    | typeof Api.MessageEntityStrike
+    | typeof Api.MessageEntityCode
+    | typeof Api.MessageEntityPre;
 export const DEFAULT_DELIMITERS: {
-    [key: string]: messageEntities
+    [key: string]: messageEntities;
 } = {
-    '**': Api.MessageEntityBold,
-    '__': Api.MessageEntityItalic,
-    '~~': Api.MessageEntityStrike,
-    '`': Api.MessageEntityCode,
-    '```': Api.MessageEntityPre
+    "**": Api.MessageEntityBold,
+    __: Api.MessageEntityItalic,
+    "~~": Api.MessageEntityStrike,
+    "`": Api.MessageEntityCode,
+    "```": Api.MessageEntityPre,
 };
 
 // export class MessageParseMethods {
 
 export interface ParseInterface {
-    parse: (message: string) => [string, Api.TypeMessageEntity[]],
-    unparse: (text: string, entities: Api.TypeMessageEntity[] | undefined) => string
+    parse: (message: string) => [string, Api.TypeMessageEntity[]];
+    unparse: (
+        text: string,
+        entities: Api.TypeMessageEntity[] | undefined
+    ) => string;
 }
 
-export async function _replaceWithMention(client: TelegramClient, entities: Api.TypeMessageEntity[], i: number, user: EntityLike) {
+export async function _replaceWithMention(
+    client: TelegramClient,
+    entities: Api.TypeMessageEntity[],
+    i: number,
+    user: EntityLike
+) {
     try {
-        entities[i] = new Api.InputMessageEntityMentionName(
-            {
-                offset: entities[i].offset,
-                length: entities[i].length,
-                userId: await client.getInputEntity(user)
-            }
-        )
+        entities[i] = new Api.InputMessageEntityMentionName({
+            offset: entities[i].offset,
+            length: entities[i].length,
+            userId: await client.getInputEntity(user),
+        });
         return true;
     } catch (e) {
         return false;
     }
 }
 
-export function _parseMessageText(client: TelegramClient, message: string, parseMode: any) {
-    if (parseMode==false) {
-        return [message, []]
+export function _parseMessageText(
+    client: TelegramClient,
+    message: string,
+    parseMode: any
+) {
+    if (parseMode == false) {
+        return [message, []];
     }
-    if (parseMode==undefined) {
+    if (parseMode == undefined) {
         parseMode = client.parseMode;
     } else if (typeof parseMode === "string") {
         parseMode = sanitizeParseMode(parseMode);

+ 241 - 136
gramjs/client/messages.ts

@@ -1,15 +1,21 @@
-import {Api} from "../tl";
-import type {Message} from '../tl/custom/message';
-import type {DateLike, EntityLike, FileLike, MarkupLike, MessageIDLike, MessageLike} from "../define";
-import {RequestIter} from "../requestIter";
-import {_EntityType, _entityType, TotalList, isArrayLike} from "../Helpers";
-import {getMessageId, getPeerId} from "../Utils";
-import type {TelegramClient} from "../";
-import {utils} from "../";
+import { Api } from "../tl";
+import type { Message } from "../tl/custom/message";
+import type {
+    DateLike,
+    EntityLike,
+    FileLike,
+    MarkupLike,
+    MessageIDLike,
+    MessageLike,
+} from "../define";
+import { RequestIter } from "../requestIter";
+import { _EntityType, _entityType, TotalList, isArrayLike } from "../Helpers";
+import { getMessageId, getPeerId } from "../Utils";
+import type { TelegramClient } from "../";
+import { utils } from "../";
 
 const _MAX_CHUNK_SIZE = 100;
 
-
 interface MessageIterParams {
     entity: EntityLike;
     offsetId: number;
@@ -24,11 +30,30 @@ interface MessageIterParams {
 }
 
 export class _MessagesIter extends RequestIter {
-    private entity?: Api.TypeInputPeer;
-    private request?: Api.messages.SearchGlobal | Api.messages.GetReplies | Api.messages.GetHistory | Api.messages.Search;
-
-    async _init({entity, offsetId, minId, maxId, fromUser, offsetDate, addOffset, filter, search, replyTo}: MessageIterParams) {
-
+    entity?: Api.TypeInputPeer;
+    request?:
+        | Api.messages.SearchGlobal
+        | Api.messages.GetReplies
+        | Api.messages.GetHistory
+        | Api.messages.Search;
+    fromId?: number;
+    addOffset?: number;
+    maxId?: number;
+    minId?: number;
+    lastId?: number;
+
+    async _init({
+        entity,
+        offsetId,
+        minId,
+        maxId,
+        fromUser,
+        offsetDate,
+        addOffset,
+        filter,
+        search,
+        replyTo,
+    }: MessageIterParams) {
         if (entity) {
             this.entity = await this.client.getInputEntity(entity);
         } else {
@@ -66,7 +91,7 @@ export class _MessagesIter extends RequestIter {
             fromUser = await this.client.getInputEntity(fromUser);
             this.fromId = await this.client.getPeerId(fromUser);
         } else {
-            this.fromId = null;
+            this.fromId = undefined;
         }
 
         if (!this.entity && fromUser) {
@@ -77,7 +102,7 @@ export class _MessagesIter extends RequestIter {
         }
         if (!this.entity) {
             this.request = new Api.messages.SearchGlobal({
-                q: search || '',
+                q: search || "",
                 filter: filter,
                 minDate: undefined,
                 // TODO fix this smh
@@ -86,7 +111,7 @@ export class _MessagesIter extends RequestIter {
                 offsetPeer: new Api.InputPeerEmpty(),
                 offsetId: offsetId,
                 limit: 1,
-            })
+            });
         } else if (replyTo !== undefined) {
             this.request = new Api.messages.GetReplies({
                 peer: this.entity,
@@ -97,9 +122,13 @@ export class _MessagesIter extends RequestIter {
                 limit: 0,
                 maxId: 0,
                 minId: 0,
-                hash: 0
+                hash: 0,
             });
-        } else if (search !== undefined || filter !== undefined || fromUser !== undefined) {
+        } else if (
+            search !== undefined ||
+            filter !== undefined ||
+            fromUser !== undefined
+        ) {
             const ty = _entityType(this.entity);
             if (ty == _EntityType.USER) {
                 fromUser = undefined;
@@ -108,8 +137,8 @@ export class _MessagesIter extends RequestIter {
             }
             this.request = new Api.messages.Search({
                 peer: this.entity,
-                q: search || '',
-                filter: typeof filter === 'function' ? new filter() : filter,
+                q: search || "",
+                filter: typeof filter === "function" ? new filter() : filter,
                 minDate: undefined,
                 maxDate: offsetDate,
                 offsetId: offsetId,
@@ -118,10 +147,18 @@ export class _MessagesIter extends RequestIter {
                 maxId: 0,
                 minId: 0,
                 hash: 0,
-                fromId: fromUser
+                fromId: fromUser,
             });
-            if (filter instanceof Api.InputMessagesFilterEmpty && offsetDate && !search && !offsetId) {
-                for await (const m of this.client.iterMessages(this.entity, {limit: 1, offsetDate: offsetDate})) {
+            if (
+                filter instanceof Api.InputMessagesFilterEmpty &&
+                offsetDate &&
+                !search &&
+                !offsetId
+            ) {
+                for await (const m of this.client.iterMessages(this.entity, {
+                    limit: 1,
+                    offsetDate: offsetDate,
+                })) {
                     this.request.offsetId = m.id + 1;
                 }
             }
@@ -134,8 +171,8 @@ export class _MessagesIter extends RequestIter {
                 minId: 0,
                 maxId: 0,
                 addOffset: addOffset,
-                hash: 0
-            })
+                hash: 0,
+            });
         }
         if (this.limit <= 0) {
             const result = await this.client.invoke(this.request);
@@ -143,9 +180,9 @@ export class _MessagesIter extends RequestIter {
                 this.total = result.count;
             } else {
                 if ("count" in result) {
-                    this.total = result.count
+                    this.total = result.count;
                 } else {
-                    this.total = result.messages.length
+                    this.total = result.messages.length;
                 }
             }
             return false;
@@ -153,14 +190,16 @@ export class _MessagesIter extends RequestIter {
         if (!this.waitTime) {
             this.waitTime = this.limit > 3000 ? 1 : 0;
         }
-        if (this.reverse && !(this.request instanceof Api.messages.SearchGlobal)) {
+        if (
+            this.reverse &&
+            !(this.request instanceof Api.messages.SearchGlobal)
+        ) {
             this.request.addOffset -= _MAX_CHUNK_SIZE;
         }
         this.addOffset = addOffset;
         this.maxId = maxId;
         this.minId = minId;
         this.lastId = this.reverse ? 0 : Number.MAX_SAFE_INTEGER;
-
     }
 
     async _loadNextChunk() {
@@ -170,7 +209,7 @@ export class _MessagesIter extends RequestIter {
         this.request.limit = Math.min(this.left, _MAX_CHUNK_SIZE);
         if (this.reverse && this.request.limit != _MAX_CHUNK_SIZE) {
             if (!(this.request instanceof Api.messages.SearchGlobal)) {
-                this.request.addOffset = this.addOffset - this.request.limit;
+                this.request.addOffset = this.addOffset! - this.request.limit;
             }
         }
         const r = await this.client.invoke(this.request);
@@ -178,9 +217,9 @@ export class _MessagesIter extends RequestIter {
             return true;
         }
         if ("count" in r) {
-            this.total = r.count
+            this.total = r.count;
         } else {
-            this.total = r.messages.length
+            this.total = r.messages.length;
         }
 
         const entities = new Map();
@@ -188,9 +227,11 @@ export class _MessagesIter extends RequestIter {
         for (const x of [...r.users, ...r.chats]) {
             entities.set(getPeerId(x), x);
         }
-        const messages: Message[] = this.reverse ? r.messages.reverse() as unknown as Message[] : r.messages as unknown as Message[];
+        const messages: Message[] = this.reverse
+            ? (r.messages.reverse() as unknown as Message[])
+            : (r.messages as unknown as Message[]);
         for (const message of messages) {
-            if ((this.fromId && message.senderId != this.fromId)) {
+            if (this.fromId && message.senderId != this.fromId) {
                 continue;
             }
             if (!this._messageInRange(message)) {
@@ -200,9 +241,7 @@ export class _MessagesIter extends RequestIter {
             try {
                 // if this fails it shouldn't be a big problem
                 message._finishInit(this.client, entities, this.entity);
-            } catch (e) {
-
-            }
+            } catch (e) {}
             message._entities = entities;
             this.buffer?.push(message);
         }
@@ -211,7 +250,7 @@ export class _MessagesIter extends RequestIter {
         }
 
         if (this.buffer) {
-            this._updateOffset(this.buffer[this.buffer.length - 1], r)
+            this._updateOffset(this.buffer[this.buffer.length - 1], r);
         } else {
             return true;
         }
@@ -220,11 +259,11 @@ export class _MessagesIter extends RequestIter {
     _messageInRange(message: Message) {
         if (this.entity) {
             if (this.reverse) {
-                if (message.id <= this.lastId || message.id >= this.maxId) {
+                if (message.id <= this.lastId! || message.id >= this.maxId!) {
                     return false;
                 }
             } else {
-                if (message.id >= this.lastId || message.id <= this.minId) {
+                if (message.id >= this.lastId! || message.id <= this.minId!) {
                     return false;
                 }
             }
@@ -263,21 +302,26 @@ export class _MessagesIter extends RequestIter {
 }
 
 interface IDsIterInterface {
-    entity: EntityLike,
-    ids: MessageLike[]
+    entity: EntityLike;
+    ids: Api.TypeInputMessage[];
 }
 
 export class _IDsIter extends RequestIter {
-    async _init({entity, ids}: IDsIterInterface) {
+    _ids?: Api.TypeInputMessage[];
+    _offset?: number;
+    _ty: number | undefined;
+    private _entity: Api.TypeInputPeer | undefined;
+    async _init({ entity, ids }: IDsIterInterface) {
         this.total = ids.length;
         this._ids = this.reverse ? ids.reverse() : ids;
         this._offset = 0;
-        this._entity = entity ? (await this.client.getInputEntity(entity)) : undefined;
+        this._entity = entity
+            ? await this.client.getInputEntity(entity)
+            : undefined;
         this._ty = this._entity ? _entityType(this._entity) : undefined;
 
-
         if (!this.waitTime) {
-            this.waitTile = this.limit > 300 ? 10 : 0;
+            this.waitTime = this.limit > 300 ? 10 : 0;
         }
     }
 
@@ -286,40 +330,47 @@ export class _IDsIter extends RequestIter {
     }
 
     async _loadNextChunk() {
-        const ids = this._ids.slice(this._offset, this._offset + _MAX_CHUNK_SIZE);
+        const ids = this._ids!.slice(
+            this._offset,
+            this._offset! + _MAX_CHUNK_SIZE
+        );
         if (!ids.length) {
             return false;
         }
-        this._offset += _MAX_CHUNK_SIZE;
+        this._offset! += _MAX_CHUNK_SIZE;
         let fromId;
         let r;
 
         if (this._ty == _EntityType.CHANNEL) {
             try {
-                r = await this.client.invoke(new Api.channels.GetMessages({
-                    channel: this._entity,
-                    id: ids
-                }));
+                r = await this.client.invoke(
+                    new Api.channels.GetMessages({
+                        channel: this._entity,
+                        id: ids,
+                    })
+                );
             } catch (e) {
                 if (e.message == "MESSAGE_IDS_EMPTY") {
                     r = new Api.messages.MessagesNotModified({
-                        count: ids.length
+                        count: ids.length,
                     });
                 } else {
                     throw e;
                 }
             }
         } else {
-            r = await this.client.invoke(new Api.messages.GetMessages({
-                id: ids
-            }));
+            r = await this.client.invoke(
+                new Api.messages.GetMessages({
+                    id: ids,
+                })
+            );
             if (this._entity) {
-                fromId = await this.client._getPeer(this.entity);
+                fromId = await this.client._getPeer(this._entity);
             }
         }
         if (r instanceof Api.messages.MessagesNotModified) {
             this.buffer?.push(...Array(ids.length));
-            return
+            return;
         }
         const entities = new Map();
         for (const entity of [...r.users, ...r.chats]) {
@@ -327,15 +378,20 @@ export class _IDsIter extends RequestIter {
         }
         let message: Api.TypeMessage;
         for (message of r.messages) {
-            if (message instanceof Api.MessageEmpty || fromId && message.peerId != fromId) {
-                this.buffer?.push(undefined)
+            if (
+                message instanceof Api.MessageEmpty ||
+                (fromId && message.peerId != fromId)
+            ) {
+                this.buffer?.push(undefined);
             } else {
                 const temp: Message = message as unknown as Message;
                 try {
                     temp._finishInit(this.client, entities, this._entity);
                 } catch (e) {
                     // we don't care about errors here
-                    this.client._log.warn("Failed to finish entities for message " + temp.id);
+                    this.client._log.warn(
+                        "Failed to finish entities for message " + temp.id
+                    );
                 }
                 temp._entities = entities;
                 this.buffer?.push(temp);
@@ -388,37 +444,69 @@ export interface EditMessageParams {
 
 //  MessageMethods {
 
-export function iterMessages(client: TelegramClient, entity: EntityLike | undefined, {limit, offsetDate, offsetId, maxId, minId, addOffset, search, filter, fromUser, waitTime, ids, reverse = false, replyTo}: IterMessagesParams) {
+export function iterMessages(
+    client: TelegramClient,
+    entity: EntityLike | undefined,
+    {
+        limit,
+        offsetDate,
+        offsetId,
+        maxId,
+        minId,
+        addOffset,
+        search,
+        filter,
+        fromUser,
+        waitTime,
+        ids,
+        reverse = false,
+        replyTo,
+    }: IterMessagesParams
+) {
     if (ids) {
-        if (typeof ids == 'number') {
-            ids = [ids]
-        }
-        return new _IDsIter(client, ids.length, {
+        if (typeof ids == "number") {
+            ids = [ids];
+        }
+        return new _IDsIter(
+            client,
+            ids.length,
+            {
+                reverse: reverse,
+                waitTime: waitTime,
+            },
+            {
+                entity: entity,
+                ids: ids,
+            }
+        );
+    }
+    return new _MessagesIter(
+        client,
+        limit || 1,
+        {
+            waitTime: waitTime,
             reverse: reverse,
-            waitTime: waitTime
-        }, {
+        },
+        {
             entity: entity,
-            ids: ids
-        });
-    }
-    return new _MessagesIter(client, limit || 1, {
-        waitTime: waitTime,
-        reverse: reverse
-    }, {
-        entity: entity,
-        offsetId: offsetId,
-        minId: minId,
-        maxId: maxId,
-        fromUser: fromUser,
-        offsetDate: offsetDate,
-        addOffset: addOffset,
-        filter: filter,
-        search: search,
-        replyTo: replyTo
-    })
+            offsetId: offsetId,
+            minId: minId,
+            maxId: maxId,
+            fromUser: fromUser,
+            offsetDate: offsetDate,
+            addOffset: addOffset,
+            filter: filter,
+            search: search,
+            replyTo: replyTo,
+        }
+    );
 }
 
-export async function getMessages(client: TelegramClient, entity: EntityLike | undefined, params: IterMessagesParams): Promise<TotalList<Message>> {
+export async function getMessages(
+    client: TelegramClient,
+    entity: EntityLike | undefined,
+    params: IterMessagesParams
+): Promise<TotalList<Message>> {
     if (Object.keys(params).length == 1 && params.limit === undefined) {
         if (params.minId === undefined && params.maxId === undefined) {
             params.limit = undefined;
@@ -434,26 +522,29 @@ export async function getMessages(client: TelegramClient, entity: EntityLike | u
             return [message];
         }
         return [];
-
     }
-    return await it.collect() as TotalList<Message>;
+    return (await it.collect()) as TotalList<Message>;
 }
 
 // region Message
 
-export async function sendMessage(client: TelegramClient,
-                                  entity: EntityLike,
-                                  {
-                                      message,
-                                      replyTo,
-                                      parseMode, formattingEntities,
-                                      linkPreview = true,
-                                      file, forceDocument,
-                                      clearDraft,
-                                      buttons,
-                                      silent,
-                                      schedule
-                                  }: SendMessageParams) {
+export async function sendMessage(
+    client: TelegramClient,
+    entity: EntityLike,
+    {
+        message,
+        replyTo,
+        parseMode,
+        formattingEntities,
+        linkPreview = true,
+        file,
+        forceDocument,
+        clearDraft,
+        buttons,
+        silent,
+        schedule,
+    }: SendMessageParams
+) {
     if (file) {
         throw new Error("Not Supported Yet");
         //return this.sendFile();
@@ -469,7 +560,10 @@ export async function sendMessage(client: TelegramClient,
         if (silent == undefined) {
             silent = message.silent;
         }
-        if (message.media && !(message.media instanceof Api.MessageMediaWebPage)) {
+        if (
+            message.media &&
+            !(message.media instanceof Api.MessageMediaWebPage)
+        ) {
             throw new Error("Not Supported Yet");
             /*
                             return this.sendFile(entity, message.media, {
@@ -485,22 +579,27 @@ export async function sendMessage(client: TelegramClient,
         }
         request = new Api.messages.SendMessage({
             peer: entity,
-            message: message.message || '',
+            message: message.message || "",
             silent: silent,
             replyToMsgId: getMessageId(replyTo),
             replyMarkup: markup,
             entities: message.entities,
             clearDraft: clearDraft,
             noWebpage: !(message.media instanceof Api.MessageMediaWebPage),
-            scheduleDate: schedule
-        })
+            scheduleDate: schedule,
+        });
         message = message.message;
     } else {
         if (formattingEntities == undefined) {
-            [message, formattingEntities] = await client._parseMessageText(message, parseMode);
+            [message, formattingEntities] = await client._parseMessageText(
+                message,
+                parseMode
+            );
         }
         if (!message) {
-            throw new Error("The message cannot be empty unless a file is provided");
+            throw new Error(
+                "The message cannot be empty unless a file is provided"
+            );
         }
         request = new Api.messages.SendMessage({
             peer: entity,
@@ -511,8 +610,8 @@ export async function sendMessage(client: TelegramClient,
             clearDraft: clearDraft,
             silent: silent,
             replyMarkup: client.buildReplyMarkup(buttons),
-            scheduleDate: schedule
-        })
+            scheduleDate: schedule,
+        });
     }
     const result = await client.invoke(request);
     return result;
@@ -524,34 +623,40 @@ export async function sendMessage(client: TelegramClient,
  * message refers to the message to be edited not what to edit
  * text refers to the new text
  */
-export async function editMessage(client: TelegramClient,
-                                  entity: EntityLike,
-                                  {
-                                      message,
-                                      text,
-                                      parseMode,
-                                      formattingEntities,
-                                      linkPreview = true,
-                                      file,
-                                      forceDocument,
-                                      buttons,
-                                      schedule
-                                  }: EditMessageParams) {
-
+export async function editMessage(
+    client: TelegramClient,
+    entity: EntityLike,
+    {
+        message,
+        text,
+        parseMode,
+        formattingEntities,
+        linkPreview = true,
+        file,
+        forceDocument,
+        buttons,
+        schedule,
+    }: EditMessageParams
+) {
     entity = await client.getInputEntity(entity);
     if (formattingEntities == undefined) {
-        [text, formattingEntities] = await client._parseMessageText(text, parseMode);
+        [text, formattingEntities] = await client._parseMessageText(
+            text,
+            parseMode
+        );
     }
-    const msg = await client.invoke(new Api.messages.EditMessage({
-        peer: entity,
-        id: utils.getMessageId(message),
-        message: text,
-        noWebpage: !linkPreview,
-        entities: formattingEntities,
-        //media: no media for now,
-        replyMarkup: client.buildReplyMarkup(buttons),
-        scheduleDate: schedule,
-    }));
+    const msg = await client.invoke(
+        new Api.messages.EditMessage({
+            peer: entity,
+            id: utils.getMessageId(message),
+            message: text,
+            noWebpage: !linkPreview,
+            entities: formattingEntities,
+            //media: no media for now,
+            replyMarkup: client.buildReplyMarkup(buttons),
+            scheduleDate: schedule,
+        })
+    );
     return msg;
     //return client._getResponseMessage(request, result, entity);
 }

+ 94 - 82
gramjs/client/telegramBaseClient.ts

@@ -1,47 +1,47 @@
-import {version} from "../";
-import {IS_NODE} from "../Helpers";
-import {ConnectionTCPFull, ConnectionTCPObfuscated} from "../network/connection";
-import type {Session} from "../sessions/Abstract";
-import {Logger} from "../extensions";
-import {StoreSession, StringSession} from "../sessions";
-import {Api} from "../tl";
-
-
-import os from 'os';
-import type {AuthKey} from "../crypto/AuthKey";
-import {EntityCache} from "../entityCache";
-import type {ParseInterface} from "./messageParse";
-import type {EventBuilder} from "../events/common";
+import { version } from "../";
+import { IS_NODE } from "../Helpers";
+import {
+    ConnectionTCPFull,
+    ConnectionTCPObfuscated,
+} from "../network/connection";
+import type { Session } from "../sessions/Abstract";
+import { Logger } from "../extensions";
+import { StoreSession, StringSession } from "../sessions";
+import { Api } from "../tl";
+
+import os from "os";
+import type { AuthKey } from "../crypto/AuthKey";
+import { EntityCache } from "../entityCache";
+import type { ParseInterface } from "./messageParse";
+import type { EventBuilder } from "../events/common";
 
 const DEFAULT_DC_ID = 1;
-const DEFAULT_IPV4_IP = IS_NODE ? '149.154.167.51' : 'pluto.web.telegram.org';
-const DEFAULT_IPV6_IP = '2001:67c:4e8:f002::a';
+const DEFAULT_IPV4_IP = IS_NODE ? "149.154.167.51" : "pluto.web.telegram.org";
+const DEFAULT_IPV6_IP = "2001:67c:4e8:f002::a";
 
 export interface TelegramClientParams {
-    connection?: any,
-    useIPV6?: boolean,
-    timeout?: number,
-    requestRetries?: number,
-    connectionRetries?: number,
-    downloadRetries?: number,
-    retryDelay?: number,
-    autoReconnect?: boolean,
-    sequentialUpdates?: boolean,
-    floodSleepThreshold?: number,
-    deviceModel?: string,
-    systemVersion?: string,
-    appVersion?: string,
-    langCode?: 'en',
-    systemLangCode?: 'en',
-    baseLogger?: string | any,
-    useWSS?: boolean,
+    connection?: any;
+    useIPV6?: boolean;
+    timeout?: number;
+    requestRetries?: number;
+    connectionRetries?: number;
+    downloadRetries?: number;
+    retryDelay?: number;
+    autoReconnect?: boolean;
+    sequentialUpdates?: boolean;
+    floodSleepThreshold?: number;
+    deviceModel?: string;
+    systemVersion?: string;
+    appVersion?: string;
+    langCode?: "en";
+    systemLangCode?: "en";
+    baseLogger?: string | any;
+    useWSS?: boolean;
 }
 
 export class TelegramBaseClient {
-
-
     __version__ = version;
-    _config ?: Api.Config;
+    _config?: Api.Config;
     public _log: Logger;
     public _floodSleepThreshold: number;
     public session: Session;
@@ -67,36 +67,48 @@ export class TelegramBaseClient {
     public _lastRequest?: number;
     public _parseMode?: ParseInterface;
 
-    constructor(session: string | Session, apiId: number, apiHash: string, {
-        connection = IS_NODE ? ConnectionTCPFull : ConnectionTCPObfuscated,
-        useIPV6 = false,
-        timeout = 10,
-        requestRetries = 5,
-        connectionRetries = Infinity,
-        retryDelay = 1000,
-        downloadRetries = 5,
-        autoReconnect = true,
-        sequentialUpdates = false,
-        floodSleepThreshold = 60,
-        deviceModel = '',
-        systemVersion = '',
-        appVersion = '',
-        langCode = 'en',
-        systemLangCode = 'en',
-        baseLogger = 'gramjs',
-        useWSS = typeof window !== 'undefined' ? window.location.protocol == 'https:' : false,
-    }: TelegramClientParams) {
+    constructor(
+        session: string | Session,
+        apiId: number,
+        apiHash: string,
+        {
+            connection = IS_NODE ? ConnectionTCPFull : ConnectionTCPObfuscated,
+            useIPV6 = false,
+            timeout = 10,
+            requestRetries = 5,
+            connectionRetries = Infinity,
+            retryDelay = 1000,
+            downloadRetries = 5,
+            autoReconnect = true,
+            sequentialUpdates = false,
+            floodSleepThreshold = 60,
+            deviceModel = "",
+            systemVersion = "",
+            appVersion = "",
+            langCode = "en",
+            systemLangCode = "en",
+            baseLogger = "gramjs",
+            useWSS = typeof window !== "undefined"
+                ? window.location.protocol == "https:"
+                : false,
+        }: TelegramClientParams
+    ) {
         if (!apiId || !apiHash) {
             throw new Error("Your API ID or Hash cannot be empty or undefined");
         }
-        if (typeof baseLogger == 'string') {
-            this._log = new Logger()
+        if (typeof baseLogger == "string") {
+            this._log = new Logger();
         } else {
-            this._log = baseLogger
+            this._log = baseLogger;
         }
-        this._log.debug("Running gramJS version "+version);
-        if (!(session instanceof StoreSession) && !(session instanceof StringSession)) {
-            throw new Error("Only StringSession and StoreSessions are supported currently :( ");
+        this._log.debug("Running gramJS version " + version);
+        if (
+            !(session instanceof StoreSession) &&
+            !(session instanceof StringSession)
+        ) {
+            throw new Error(
+                "Only StringSession and StoreSessions are supported currently :( "
+            );
         }
         this._floodSleepThreshold = floodSleepThreshold;
         this.session = session;
@@ -115,13 +127,11 @@ export class TelegramBaseClient {
         this._connection = connection;
         this._initRequest = new Api.InitConnection({
             apiId: this.apiId,
-            deviceModel: deviceModel || os.type()
-                .toString() || 'Unknown',
-            systemVersion: systemVersion || os.release()
-                .toString() || '1.0',
-            appVersion: appVersion || '1.0',
+            deviceModel: deviceModel || os.type().toString() || "Unknown",
+            systemVersion: systemVersion || os.release().toString() || "1.0",
+            appVersion: appVersion || "1.0",
             langCode: langCode,
-            langPack: '', // this should be left empty.
+            langPack: "", // this should be left empty.
             systemLangCode: systemLangCode,
             proxy: undefined, // no proxies yet.
         });
@@ -132,11 +142,9 @@ export class TelegramBaseClient {
         this._bot = undefined;
         this._selfInputPeer = undefined;
         this.useWSS = useWSS;
-        this._entityCache = new EntityCache()
-
+        this._entityCache = new EntityCache();
     }
 
-
     get floodSleepThreshold() {
         return this._floodSleepThreshold;
     }
@@ -149,19 +157,25 @@ export class TelegramBaseClient {
     async _initSession() {
         await this.session.load();
 
-        if (!this.session.serverAddress || this.session.serverAddress.includes(":") !== this._useIPV6) {
-            this.session.setDC(DEFAULT_DC_ID, this._useIPV6 ? DEFAULT_IPV6_IP : DEFAULT_IPV4_IP, this.useWSS ? 443 : 80)
+        if (
+            !this.session.serverAddress ||
+            this.session.serverAddress.includes(":") !== this._useIPV6
+        ) {
+            this.session.setDC(
+                DEFAULT_DC_ID,
+                this._useIPV6 ? DEFAULT_IPV6_IP : DEFAULT_IPV4_IP,
+                this.useWSS ? 443 : 80
+            );
         }
     }
 
-
     get connected() {
         return this._sender && this._sender.isConnected();
     }
 
     async disconnect() {
         if (this._sender) {
-            await this._sender.disconnect()
+            await this._sender.disconnect();
         }
     }
 
@@ -173,22 +187,20 @@ export class TelegramBaseClient {
         await Promise.all([
             this.disconnect(),
             this.session.delete(),
-            ...Object.values(this._borrowedSenderPromises).map((promise: any) => {
-                return promise
-                    .then((sender: any) => sender.disconnect())
-            }),
+            ...Object.values(this._borrowedSenderPromises).map(
+                (promise: any) => {
+                    return promise.then((sender: any) => sender.disconnect());
+                }
+            ),
         ]);
 
-        this._eventBuilders = []
+        this._eventBuilders = [];
     }
 
-
     async _authKeyCallback(authKey: AuthKey, dcId: number) {
         this.session.setAuthKey(authKey, dcId);
         await this.session.save();
     }
 
     // endregion
-
-
 }

+ 69 - 41
gramjs/client/updates.ts

@@ -1,33 +1,41 @@
-import type {EventBuilder, EventCommon} from "../events/common";
-import {Api} from "../tl";
-import {helpers} from "../";
-import type {TelegramClient} from "../";
-import bigInt from 'big-integer';
-import {UpdateConnectionState} from "../network";
-import type {Raw} from "../events";
-import {utils} from "../index";
+import type { EventBuilder, EventCommon } from "../events/common";
+import { Api } from "../tl";
+import { helpers } from "../";
+import type { TelegramClient } from "../";
+import bigInt from "big-integer";
+import { UpdateConnectionState } from "../network";
+import type { Raw } from "../events";
+import { utils } from "../index";
 
 // export class UpdateMethods {
 export function on(client: TelegramClient, event: any) {
     return (f: CallableFunction) => {
         client.addEventHandler(f, event);
         return f;
-    }
+    };
 }
 
-export function addEventHandler(client: TelegramClient, callback: CallableFunction, event?: EventBuilder) {
+export function addEventHandler(
+    client: TelegramClient,
+    callback: CallableFunction,
+    event?: EventBuilder
+) {
     if (event == undefined) {
         // recursive imports :(
         const raw = require("../events/Raw").Raw;
         event = new raw({}) as Raw;
     }
-    client._eventBuilders.push([event, callback])
+    client._eventBuilders.push([event, callback]);
 }
 
-export function removeEventHandler(client: TelegramClient, callback: CallableFunction, event: EventBuilder) {
+export function removeEventHandler(
+    client: TelegramClient,
+    callback: CallableFunction,
+    event: EventBuilder
+) {
     client._eventBuilders = client._eventBuilders.filter(function (item) {
-        return item !== [event, callback]
-    })
+        return item !== [event, callback];
+    });
 }
 
 export function listEventHandlers(client: TelegramClient) {
@@ -38,11 +46,16 @@ export function catchUp() {
     // TODO
 }
 
-export function _handleUpdate(client: TelegramClient, update: Api.TypeUpdate | number): void {
-    if (typeof update === 'number') {
+export function _handleUpdate(
+    client: TelegramClient,
+    update: Api.TypeUpdate | number
+): void {
+    if (typeof update === "number") {
         if ([-1, 0, 1].includes(update)) {
-            client._dispatchUpdate({update: new UpdateConnectionState(update)})
-            return
+            client._dispatchUpdate({
+                update: new UpdateConnectionState(update),
+            });
+            return;
         }
     }
 
@@ -50,33 +63,44 @@ export function _handleUpdate(client: TelegramClient, update: Api.TypeUpdate | n
     client._entityCache.add(update);
     client.session.processEntities(update);
 
-    if (update instanceof Api.Updates || update instanceof Api.UpdatesCombined) {
+    if (
+        update instanceof Api.Updates ||
+        update instanceof Api.UpdatesCombined
+    ) {
         // TODO deal with entities
         const entities = new Map();
         for (const x of [...update.users, ...update.chats]) {
             entities.set(utils.getPeerId(x), x);
         }
         for (const u of update.updates) {
-            client._processUpdate(u, update.updates, entities)
+            client._processUpdate(u, update.updates, entities);
         }
     } else if (update instanceof Api.UpdateShort) {
-        client._processUpdate(update.update, null)
+        client._processUpdate(update.update, null);
     } else {
-        client._processUpdate(update, null)
+        client._processUpdate(update, null);
     }
 }
 
-export function _processUpdate(client: TelegramClient, update: any, others: any, entities?: any) {
+export function _processUpdate(
+    client: TelegramClient,
+    update: any,
+    others: any,
+    entities?: any
+) {
     update._entities = entities || new Map();
     const args = {
         update: update,
         others: others,
     };
 
-    client._dispatchUpdate(args)
+    client._dispatchUpdate(args);
 }
 
-export async function _dispatchUpdate(client: TelegramClient, args: { update: UpdateConnectionState | any }): Promise<void> {
+export async function _dispatchUpdate(
+    client: TelegramClient,
+    args: { update: UpdateConnectionState | any }
+): Promise<void> {
     for (const [builder, callback] of client._eventBuilders) {
         if (!builder.resolved) {
             await builder.resolve(client);
@@ -84,7 +108,7 @@ export async function _dispatchUpdate(client: TelegramClient, args: { update: Up
         let event = args.update;
         if (event) {
             if (!client._selfInputPeer) {
-                await client.getMe(true)
+                await client.getMe(true);
             }
             if (!(event instanceof UpdateConnectionState)) {
                 // TODO fix me
@@ -94,11 +118,11 @@ export async function _dispatchUpdate(client: TelegramClient, args: { update: Up
                 if ("_eventName" in event) {
                     event._setClient(client);
                     event.originalUpdate = args.update;
-                    event._entities = args.update._entities
+                    event._entities = args.update._entities;
                 }
                 const filter = await builder.filter(event);
                 if (!filter) {
-                    continue
+                    continue;
                 }
                 try {
                     await callback(event);
@@ -112,29 +136,33 @@ export async function _dispatchUpdate(client: TelegramClient, args: { update: Up
 
 export async function _updateLoop(client: TelegramClient): Promise<void> {
     while (client.connected) {
-        const rnd = helpers.getRandomInt(Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER)
-        await helpers.sleep(1000 * 60)
+        const rnd = helpers.getRandomInt(
+            Number.MIN_SAFE_INTEGER,
+            Number.MAX_SAFE_INTEGER
+        );
+        await helpers.sleep(1000 * 60);
         // We don't care about the result we just want to send it every
         // 60 seconds so telegram doesn't stop the connection
         try {
-            client._sender.send(new Api.Ping({
-                pingId: bigInt(rnd),
-            }))
-        } catch (e) {
-
-        }
+            client._sender.send(
+                new Api.Ping({
+                    pingId: bigInt(rnd),
+                })
+            );
+        } catch (e) {}
 
         // We need to send some content-related request at least hourly
         // for Telegram to keep delivering updates, otherwise they will
         // just stop even if we're connected. Do so every 30 minutes.
 
         // TODO Call getDifference instead since it's more relevant
-        if (!client._lastRequest || new Date().getTime() - client._lastRequest > 30 * 60 * 1000) {
+        if (
+            !client._lastRequest ||
+            new Date().getTime() - client._lastRequest > 30 * 60 * 1000
+        ) {
             try {
-                await client.invoke(new Api.updates.GetState())
-            } catch (e) {
-
-            }
+                await client.invoke(new Api.updates.GetState());
+            } catch (e) {}
         }
     }
 }

+ 194 - 172
gramjs/client/uploads.ts

@@ -1,12 +1,12 @@
-import {Api} from '../tl';
+import { Api } from "../tl";
 
-import {TelegramClient} from './TelegramClient';
-import {generateRandomBytes, readBigIntFromBuffer, sleep} from '../Helpers';
-import {getAppropriatedPartSize, getAttributes} from '../Utils';
-import {EntityLike, FileLike, MarkupLike, MessageIDLike} from "../define";
+import { TelegramClient } from "./TelegramClient";
+import { generateRandomBytes, readBigIntFromBuffer, sleep } from "../Helpers";
+import { getAppropriatedPartSize, getAttributes } from "../Utils";
+import { EntityLike, FileLike, MarkupLike, MessageIDLike } from "../define";
 import path from "path";
-import {promises as fs} from "fs";
-import {utils} from "../index";
+import { promises as fs } from "fs";
+import { utils } from "../index";
 
 interface OnProgress {
     // Float between 0 and 1.
@@ -41,12 +41,12 @@ const UPLOAD_TIMEOUT = 15 * 1000;
 
 export async function uploadFile(
     client: TelegramClient,
-    fileParams: UploadFileParams,
+    fileParams: UploadFileParams
 ): Promise<Api.InputFile | Api.InputFileBig> {
-    const {file, onProgress} = fileParams;
-    let {workers} = fileParams;
+    const { file, onProgress } = fileParams;
+    let { workers } = fileParams;
 
-    const {name, size} = file;
+    const { name, size } = file;
     const fileId = readBigIntFromBuffer(generateRandomBytes(8), true, true);
     const isLarge = size > LARGE_FILE_THRESHOLD;
 
@@ -79,41 +79,44 @@ export async function uploadFile(
         for (let j = i; j < end; j++) {
             const bytes = buffer.slice(j * partSize, (j + 1) * partSize);
 
-            sendingParts.push((async () => {
-                await sender.send(
-                    isLarge
-                        ? new Api.upload.SaveBigFilePart({
-                            fileId,
-                            filePart: j,
-                            fileTotalParts: partCount,
-                            bytes,
-                        })
-                        : new Api.upload.SaveFilePart({
-                            fileId,
-                            filePart: j,
-                            bytes,
-                        }),
-                );
-
-                if (onProgress) {
-                    if (onProgress.isCanceled) {
-                        throw new Error('USER_CANCELED');
+            sendingParts.push(
+                (async () => {
+                    await sender.send(
+                        isLarge
+                            ? new Api.upload.SaveBigFilePart({
+                                  fileId,
+                                  filePart: j,
+                                  fileTotalParts: partCount,
+                                  bytes,
+                              })
+                            : new Api.upload.SaveFilePart({
+                                  fileId,
+                                  filePart: j,
+                                  bytes,
+                              })
+                    );
+
+                    if (onProgress) {
+                        if (onProgress.isCanceled) {
+                            throw new Error("USER_CANCELED");
+                        }
+
+                        progress += 1 / partCount;
+                        onProgress(progress);
                     }
-
-                    progress += (1 / partCount);
-                    onProgress(progress);
-                }
-            })());
-
+                })()
+            );
         }
         try {
             await Promise.race([
                 await Promise.all(sendingParts),
-                sleep(UPLOAD_TIMEOUT * workers).then(() => Promise.reject(new Error('TIMEOUT'))),
+                sleep(UPLOAD_TIMEOUT * workers).then(() =>
+                    Promise.reject(new Error("TIMEOUT"))
+                ),
             ]);
         } catch (err) {
-            if (err.message === 'TIMEOUT') {
-                console.warn('Upload timeout. Retrying...');
+            if (err.message === "TIMEOUT") {
+                console.warn("Upload timeout. Retrying...");
                 i -= workers;
                 continue;
             }
@@ -123,33 +126,33 @@ export async function uploadFile(
     }
     return isLarge
         ? new Api.InputFileBig({
-            id: fileId,
-            parts: partCount,
-            name,
-        })
+              id: fileId,
+              parts: partCount,
+              name,
+          })
         : new Api.InputFile({
-            id: fileId,
-            parts: partCount,
-            name,
-            md5Checksum: '', // This is not a "flag", so not sure if we can make it optional.
-        });
+              id: fileId,
+              parts: partCount,
+              name,
+              md5Checksum: "", // This is not a "flag", so not sure if we can make it optional.
+          });
 }
 
 export interface SendFileInterface {
-    file: FileLike,
-    caption?: string,
-    forceDocument?: boolean,
-    fileSize?: number,
+    file: FileLike;
+    caption?: string;
+    forceDocument?: boolean;
+    fileSize?: number;
     clearDraft?: boolean;
-    progressCallback?: OnProgress,
-    replyTo?: MessageIDLike,
-    attributes?: Api.TypeDocumentAttribute[],
-    thumb?: FileLike,
-    voiceNote?: boolean,
-    videoNote?: boolean,
-    supportsStreaming?: boolean,
-    parseMode?: any,
-    formattingEntities?: Api.TypeMessageEntity[],
+    progressCallback?: OnProgress;
+    replyTo?: MessageIDLike;
+    attributes?: Api.TypeDocumentAttribute[];
+    thumb?: FileLike;
+    voiceNote?: boolean;
+    videoNote?: boolean;
+    supportsStreaming?: boolean;
+    parseMode?: any;
+    formattingEntities?: Api.TypeMessageEntity[];
     silent?: boolean;
     background?: boolean;
     replyMarkup?: Api.TypeReplyMarkup;
@@ -159,67 +162,74 @@ export interface SendFileInterface {
 }
 
 interface FileToMediaInterface {
-    file: FileLike,
-    forceDocument?: boolean,
-    fileSize?: number,
-    progressCallback?: OnProgress,
-    attributes?: Api.TypeDocumentAttribute[],
-    thumb?: FileLike,
-    voiceNote?: boolean,
-    videoNote?: boolean,
-    supportsStreaming?: boolean,
-    mimeType?: string,
-    asImage?: boolean,
-    workers?:number,
+    file: FileLike;
+    forceDocument?: boolean;
+    fileSize?: number;
+    progressCallback?: OnProgress;
+    attributes?: Api.TypeDocumentAttribute[];
+    thumb?: FileLike;
+    voiceNote?: boolean;
+    videoNote?: boolean;
+    supportsStreaming?: boolean;
+    mimeType?: string;
+    asImage?: boolean;
+    workers?: number;
 }
 
-async function _fileToMedia(client: TelegramClient, {
-    file,
-    forceDocument,
-    fileSize,
-    progressCallback,
-    attributes,
-    thumb,
-    voiceNote = false,
-    videoNote = false,
-    supportsStreaming = false,
-    mimeType,
-    asImage,
-    workers=1,
-}: FileToMediaInterface): Promise<{
-    fileHandle?: any,
-    media?: Api.TypeInputMedia,
-    image?: boolean
+async function _fileToMedia(
+    client: TelegramClient,
+    {
+        file,
+        forceDocument,
+        fileSize,
+        progressCallback,
+        attributes,
+        thumb,
+        voiceNote = false,
+        videoNote = false,
+        supportsStreaming = false,
+        mimeType,
+        asImage,
+        workers = 1,
+    }: FileToMediaInterface
+): Promise<{
+    fileHandle?: any;
+    media?: Api.TypeInputMedia;
+    image?: boolean;
 }> {
     if (!file) {
-        return {fileHandle: undefined, media: undefined, image: undefined}
+        return { fileHandle: undefined, media: undefined, image: undefined };
     }
     const isImage = utils.isImage(file);
     if (asImage == undefined) {
-        asImage = isImage && (!forceDocument);
+        asImage = isImage && !forceDocument;
     }
-    if ((typeof file == "object") && !Buffer.isBuffer(file) && !(file instanceof Api.InputFile) && !(file instanceof Api.InputFileBig)
-        && "read" in file) {
+    if (
+        typeof file == "object" &&
+        !Buffer.isBuffer(file) &&
+        !(file instanceof Api.InputFile) &&
+        !(file instanceof Api.InputFileBig) &&
+        "read" in file
+    ) {
         try {
             return {
                 fileHandle: undefined,
-                media: utils.getInputMedia(file,
-                    {
-                        isPhoto: asImage,
-                        attributes: attributes,
-                        forceDocument: forceDocument,
-                        voiceNote: voiceNote,
-                        videoNote: videoNote,
-                        supportsStreaming: supportsStreaming,
-                    }),
-                image: asImage
-            }
+                media: utils.getInputMedia(file, {
+                    isPhoto: asImage,
+                    attributes: attributes,
+                    forceDocument: forceDocument,
+                    voiceNote: voiceNote,
+                    videoNote: videoNote,
+                    supportsStreaming: supportsStreaming,
+                }),
+                image: asImage,
+            };
         } catch (e) {
             return {
                 fileHandle: undefined,
                 media: undefined,
-                image: isImage
-            }
+                image: isImage,
+            };
         }
     }
     let media;
@@ -230,8 +240,12 @@ async function _fileToMedia(client: TelegramClient, {
         fileHandle = file;
     } else if (!(typeof file == "string") || (await fs.lstat(file)).isFile()) {
         if (typeof file == "string") {
-            createdFile = new CustomFile(path.basename(file), (await fs.stat(file)).size, file)
-        } else if (typeof File !== 'undefined' && file instanceof File) {
+            createdFile = new CustomFile(
+                path.basename(file),
+                (await fs.stat(file)).size,
+                file
+            );
+        } else if (typeof File !== "undefined" && file instanceof File) {
             createdFile = file;
         } else {
             let name;
@@ -241,36 +255,36 @@ async function _fileToMedia(client: TelegramClient, {
                 name = "unnamed";
             }
             if (file instanceof Buffer) {
-                createdFile = new CustomFile(name, file.length, "", file)
+                createdFile = new CustomFile(name, file.length, "", file);
             }
         }
         if (!createdFile) {
-            throw new Error(`Could not create file from ${file}`)
+            throw new Error(`Could not create file from ${file}`);
         }
         fileHandle = await uploadFile(client, {
             file: createdFile,
             onProgress: progressCallback,
             workers: workers,
-        })
+        });
     } else if (file.startsWith("https://") || file.startsWith("http://")) {
         if (asImage) {
-            media = new Api.InputMediaPhotoExternal({url: file})
+            media = new Api.InputMediaPhotoExternal({ url: file });
         } else {
-            media = new Api.InputMediaPhotoExternal({url: file})
+            media = new Api.InputMediaPhotoExternal({ url: file });
         }
     } else {
         throw new Error(`"Not a valid path nor a url ${file}`);
     }
     if (media != undefined) {
-
     } else if (fileHandle == undefined) {
-        throw new Error(`Failed to convert ${file} to media. Not an existing file or an HTTP URL`)
+        throw new Error(
+            `Failed to convert ${file} to media. Not an existing file or an HTTP URL`
+        );
     } else if (asImage) {
         media = new Api.InputMediaUploadedPhoto({
             file: fileHandle,
         });
-    } else  {
-
+    } else {
         // @ts-ignore
         let res = utils.getAttributes(file, {
             mimeType: mimeType,
@@ -279,8 +293,8 @@ async function _fileToMedia(client: TelegramClient, {
             voiceNote: voiceNote,
             videoNote: videoNote,
             supportsStreaming: supportsStreaming,
-            thumb: thumb
-        })
+            thumb: thumb,
+        });
         attributes = res.attrs;
         mimeType = res.mimeType;
 
@@ -290,8 +304,12 @@ async function _fileToMedia(client: TelegramClient, {
         } else {
             // todo refactor
             if (typeof thumb == "string") {
-                uploadedThumb = new CustomFile(path.basename(thumb), (await fs.stat(thumb)).size, thumb)
-            } else if (typeof File !== 'undefined' && thumb instanceof File) {
+                uploadedThumb = new CustomFile(
+                    path.basename(thumb),
+                    (await fs.stat(thumb)).size,
+                    thumb
+                );
+            } else if (typeof File !== "undefined" && thumb instanceof File) {
                 uploadedThumb = thumb;
             } else {
                 let name;
@@ -301,61 +319,66 @@ async function _fileToMedia(client: TelegramClient, {
                     name = "unnamed";
                 }
                 if (thumb instanceof Buffer) {
-                    uploadedThumb = new CustomFile(name, thumb.length, "", thumb)
+                    uploadedThumb = new CustomFile(
+                        name,
+                        thumb.length,
+                        "",
+                        thumb
+                    );
                 }
-
             }
             if (!uploadedThumb) {
-                throw new Error(`Could not create file from ${file}`)
+                throw new Error(`Could not create file from ${file}`);
             }
             uploadedThumb = await uploadFile(client, {
                 file: uploadedThumb,
                 workers: 1,
-
             });
-
         }
         media = new Api.InputMediaUploadedDocument({
             file: fileHandle,
             mimeType: mimeType,
             attributes: attributes,
             thumb: uploadedThumb,
-            forceFile: forceDocument && !isImage
-        })
+            forceFile: forceDocument && !isImage,
+        });
     }
     return {
         fileHandle: fileHandle,
         media: media,
-        image: asImage
-    }
+        image: asImage,
+    };
 }
 
-export async function sendFile(client: TelegramClient, entity: EntityLike,
-                               {
-                                   file,
-                                   caption,
-                                   forceDocument = false,
-                                   fileSize,
-                                   clearDraft = false,
-                                   progressCallback,
-                                   replyTo,
-                                   attributes,
-                                   thumb,
-                                   parseMode,
-                                   formattingEntities,
-                                   voiceNote = false,
-                                   videoNote = false,
-                                   buttons,
-                                   silent,
-                                   supportsStreaming = false,
-                                   scheduleDate,
-                                   workers = 1,
-                               }: SendFileInterface) {
+export async function sendFile(
+    client: TelegramClient,
+    entity: EntityLike,
+    {
+        file,
+        caption,
+        forceDocument = false,
+        fileSize,
+        clearDraft = false,
+        progressCallback,
+        replyTo,
+        attributes,
+        thumb,
+        parseMode,
+        formattingEntities,
+        voiceNote = false,
+        videoNote = false,
+        buttons,
+        silent,
+        supportsStreaming = false,
+        scheduleDate,
+        workers = 1,
+    }: SendFileInterface
+) {
     if (!file) {
         throw new Error("You need to specify a file");
     }
     if (!caption) {
-        caption = ""
+        caption = "";
     }
     entity = await client.getInputEntity(entity);
     replyTo = utils.getMessageId(replyTo);
@@ -364,14 +387,13 @@ export async function sendFile(client: TelegramClient, entity: EntityLike,
     if (formattingEntities != undefined) {
         msgEntities = formattingEntities;
     } else {
-        [caption, formattingEntities] = await client._parseMessageText(caption, parseMode);
+        [caption, formattingEntities] = await client._parseMessageText(
+            caption,
+            parseMode
+        );
     }
 
-    const {
-        fileHandle,
-        media,
-        image
-    } = await _fileToMedia(client, {
+    const { fileHandle, media, image } = await _fileToMedia(client, {
         file: file,
         forceDocument: forceDocument,
         fileSize: fileSize,
@@ -381,29 +403,29 @@ export async function sendFile(client: TelegramClient, entity: EntityLike,
         voiceNote: voiceNote,
         videoNote: videoNote,
         supportsStreaming: supportsStreaming,
-        workers:workers
+        workers: workers,
     });
-    if (media==undefined){
-        throw new Error(`Cannot use ${file} as file.`)
+    if (media == undefined) {
+        throw new Error(`Cannot use ${file} as file.`);
     }
     const markup = client.buildReplyMarkup(buttons);
     const request = new Api.messages.SendMedia({
-        peer:entity,
-        media:media,
-        replyToMsgId:replyTo,
-        message:caption,
-        entities:msgEntities,
-        replyMarkup:markup,
-        silent:silent,
-        scheduleDate:scheduleDate,
-        clearDraft:clearDraft,
+        peer: entity,
+        media: media,
+        replyToMsgId: replyTo,
+        message: caption,
+        entities: msgEntities,
+        replyMarkup: markup,
+        silent: silent,
+        scheduleDate: scheduleDate,
+        clearDraft: clearDraft,
     });
     // todo get message
     return client.invoke(request);
 }
 
 function fileToBuffer(file: File | CustomFile) {
-    if (typeof File !== 'undefined' && file instanceof File) {
+    if (typeof File !== "undefined" && file instanceof File) {
         return new Response(file).arrayBuffer();
     } else if (file instanceof CustomFile) {
         if (file.buffer != undefined) {

+ 184 - 133
gramjs/client/users.ts

@@ -1,10 +1,10 @@
-import {Api} from "../tl";
-import type {Entity, EntityLike} from "../define";
-import {getPeerId as peerUtils } from "../Utils";
-import {_entityType, _EntityType, sleep,isArrayLike} from "../Helpers";
-import {errors, utils} from "../";
-import type {TelegramClient} from "../";
-import bigInt from 'big-integer';
+import { Api } from "../tl";
+import type { Entity, EntityLike } from "../define";
+import { getPeerId as peerUtils } from "../Utils";
+import { _entityType, _EntityType, sleep, isArrayLike } from "../Helpers";
+import { errors, utils } from "../";
+import type { TelegramClient } from "../";
+import bigInt from "big-integer";
 // UserMethods {
 // region Invoking Telegram request
 
@@ -14,12 +14,16 @@ import bigInt from 'big-integer';
  * @param request
  * @returns {Promise}
  */
-export async function invoke<R extends Api.AnyRequest>(client: TelegramClient, request: R): Promise<R['__response']> {
-    if (request.classType !== 'request') {
-        throw new Error('You can only invoke MTProtoRequests')
+export async function invoke<R extends Api.AnyRequest>(
+    client: TelegramClient,
+    request: R
+): Promise<R["__response"]> {
+    if (request.classType !== "request") {
+        throw new Error("You can only invoke MTProtoRequests");
+    }
+    if (client._sender == undefined) {
+        throw new Error("Cannot send requests while disconnected");
     }
-    // This causes issues for now because not enough utils
-    // await request.resolve(this, utils)
     await request.resolve(client, utils);
     client._lastRequest = new Date().getTime();
     let attempt: number;
@@ -27,52 +31,76 @@ export async function invoke<R extends Api.AnyRequest>(client: TelegramClient, r
         try {
             const promise = client._sender.send(request);
             const result = await promise;
-            client.session.processEntities(result)
+            client.session.processEntities(result);
             client._entityCache.add(result);
-            return result
+            return result;
         } catch (e) {
-            if (e instanceof errors.ServerError || e.message === 'RPC_CALL_FAIL' ||
-                e.message === 'RPC_MCGET_FAIL') {
-                client._log.warn(`Telegram is having internal issues ${e.constructor.name}`);
-                await sleep(2000)
-            } else if (e instanceof errors.FloodWaitError || e instanceof errors.FloodTestPhoneWaitError) {
+            if (
+                e instanceof errors.ServerError ||
+                e.message === "RPC_CALL_FAIL" ||
+                e.message === "RPC_MCGET_FAIL"
+            ) {
+                client._log.warn(
+                    `Telegram is having internal issues ${e.constructor.name}`
+                );
+                await sleep(2000);
+            } else if (
+                e instanceof errors.FloodWaitError ||
+                e instanceof errors.FloodTestPhoneWaitError
+            ) {
                 if (e.seconds <= client.floodSleepThreshold) {
-                    client._log.info(`Sleeping for ${e.seconds}s on flood wait`);
-                    await sleep(e.seconds * 1000)
+                    client._log.info(
+                        `Sleeping for ${e.seconds}s on flood wait`
+                    );
+                    await sleep(e.seconds * 1000);
                 } else {
-                    throw e
+                    throw e;
                 }
-            } else if (e instanceof errors.PhoneMigrateError || e instanceof errors.NetworkMigrateError ||
-                e instanceof errors.UserMigrateError) {
+            } else if (
+                e instanceof errors.PhoneMigrateError ||
+                e instanceof errors.NetworkMigrateError ||
+                e instanceof errors.UserMigrateError
+            ) {
                 client._log.info(`Phone migrated to ${e.newDc}`);
-                const shouldRaise = e instanceof errors.PhoneMigrateError || e instanceof errors.NetworkMigrateError;
-                if (shouldRaise && await client.isUserAuthorized()) {
-                    throw e
+                const shouldRaise =
+                    e instanceof errors.PhoneMigrateError ||
+                    e instanceof errors.NetworkMigrateError;
+                if (shouldRaise && (await client.isUserAuthorized())) {
+                    throw e;
                 }
-                await client._switchDC(e.newDc)
+                await client._switchDC(e.newDc);
             } else {
-                throw e
+                throw e;
             }
         }
     }
-    throw new Error(`Request was unsuccessful ${attempt} time(s)`)
+    throw new Error(`Request was unsuccessful ${attempt} time(s)`);
 }
 
-export async function getMe(client: TelegramClient, inputPeer = false): Promise<Api.InputPeerUser | Api.User> {
+export async function getMe(
+    client: TelegramClient,
+    inputPeer = false
+): Promise<Api.InputPeerUser | Api.User> {
     if (inputPeer && client._selfInputPeer) {
         return client._selfInputPeer;
     }
     try {
-        const me = (await client.invoke(new Api.users
-            .GetUsers({id: [new Api.InputUserSelf()]})))[0] as Api.User;
+        const me = (
+            await client.invoke(
+                new Api.users.GetUsers({ id: [new Api.InputUserSelf()] })
+            )
+        )[0] as Api.User;
         client._bot = me.bot;
 
         if (!client._selfInputPeer) {
-            client._selfInputPeer = utils.getInputPeer(me, true) as Api.InputPeerUser;
+            client._selfInputPeer = utils.getInputPeer(
+                me,
+                true
+            ) as Api.InputPeerUser;
         }
         return inputPeer ? client._selfInputPeer : me;
     } catch (e) {
-        if (client._log.canSend('error')){
+        if (client._log.canSend("error")) {
             console.error(e);
         }
         throw new Error("Could not get me");
@@ -98,7 +126,10 @@ export async function isUserAuthorized(client: TelegramClient) {
     }
 }
 
-export async function getEntity(client: TelegramClient, entity: any): Promise<Entity> {
+export async function getEntity(
+    client: TelegramClient,
+    entity: any
+): Promise<Entity> {
     const single = !isArrayLike(entity);
 
     if (single) {
@@ -107,7 +138,7 @@ export async function getEntity(client: TelegramClient, entity: any): Promise<En
 
     const inputs = [];
     for (const x of entity) {
-        if (typeof x === 'string') {
+        if (typeof x === "string") {
             inputs.push(x);
         } else {
             inputs.push(await client.getInputEntity(x));
@@ -121,29 +152,32 @@ export async function getEntity(client: TelegramClient, entity: any): Promise<En
     for (const x of inputs) {
         try {
             lists.get(_entityType(x))!.push(x);
-        } catch (e) {
-
-        }
+        } catch (e) {}
     }
     let users = lists.get(_EntityType.USER)!;
     let chats = lists.get(_EntityType.CHAT)!;
     let channels = lists.get(_EntityType.CHANNEL)!;
 
     if (users.length) {
-        users = await client.invoke(new Api.users.GetUsers({
-            id: users
-        }));
+        users = await client.invoke(
+            new Api.users.GetUsers({
+                id: users,
+            })
+        );
     }
     if (chats.length) {
         const chatIds = chats.map((x) => x.chatId);
-        chats = (await client.invoke(new Api.messages.GetChats({id: chatIds}))).chats;
+        chats = (
+            await client.invoke(new Api.messages.GetChats({ id: chatIds }))
+        ).chats;
     }
     if (channels.length) {
-        channels = (await client.invoke(new Api.channels.GetChannels(({id: channels})))).chats;
+        channels = (
+            await client.invoke(new Api.channels.GetChannels({ id: channels }))
+        ).chats;
     }
     const idEntity = new Map<number, any>();
 
-
     for (const user of users) {
         idEntity.set(peerUtils(user), user);
     }
@@ -158,15 +192,15 @@ export async function getEntity(client: TelegramClient, entity: any): Promise<En
 
     const result = [];
     for (const x of inputs) {
-        if (typeof x === 'string') {
+        if (typeof x === "string") {
             result.push(await client._getEntityFromString(x));
         } else if (!(x instanceof Api.InputPeerSelf)) {
-            result.push(idEntity.get(peerUtils(x)))
+            result.push(idEntity.get(peerUtils(x)));
         } else {
             for (const [key, u] of idEntity.entries()) {
                 if (u instanceof Api.User && u.self) {
                     result.push(u);
-                    break
+                    break;
                 }
             }
         }
@@ -174,29 +208,33 @@ export async function getEntity(client: TelegramClient, entity: any): Promise<En
     return single ? result[0] : result;
 }
 
-export async function getInputEntity(client: TelegramClient, peer: EntityLike): Promise<Api.TypeInputPeer> {
+export async function getInputEntity(
+    client: TelegramClient,
+    peer: EntityLike
+): Promise<Api.TypeInputPeer> {
     // Short-circuit if the input parameter directly maps to an InputPeer
 
     try {
-        return utils.getInputPeer(peer)
+        return utils.getInputPeer(peer);
         // eslint-disable-next-line no-empty
-    } catch (e) {
-    }
+    } catch (e) {}
     // Next in priority is having a peer (or its ID) cached in-memory
     try {
         // 0x2d45687 == crc32(b'Peer')
-        if (typeof peer !== "string" && (typeof peer === 'number' || peer.SUBCLASS_OF_ID === 0x2d45687)) {
+        if (
+            typeof peer !== "string" &&
+            (typeof peer === "number" || peer.SUBCLASS_OF_ID === 0x2d45687)
+        ) {
             const res = client._entityCache.get(peer);
             if (res) {
                 return res;
             }
         }
         // eslint-disable-next-line no-empty
-    } catch (e) {
-    }
+    } catch (e) {}
     // Then come known strings that take precedence
-    if (typeof peer == 'string') {
-        if (['me', 'this', 'self'].includes(peer)) {
+    if (typeof peer == "string") {
+        if (["me", "this", "self"].includes(peer)) {
             return new Api.InputPeerSelf();
         }
     }
@@ -204,14 +242,13 @@ export async function getInputEntity(client: TelegramClient, peer: EntityLike):
     // No InputPeer, cached peer, or known string. Fetch from disk cache
     try {
         if (peer != undefined) {
-            return client.session.getInputEntity(peer)
+            return client.session.getInputEntity(peer);
         }
         // eslint-disable-next-line no-empty
-    } catch (e) {
-    }
+    } catch (e) {}
     // Only network left to try
-    if (typeof peer === 'string') {
-        return utils.getInputPeer(await client._getEntityFromString(peer))
+    if (typeof peer === "string") {
+        return utils.getInputPeer(await client._getEntityFromString(peer));
     }
     // If we're a bot and the user has messaged us privately users.getUsers
     // will work with accessHash = 0. Similar for channels.getChannels.
@@ -219,12 +256,16 @@ export async function getInputEntity(client: TelegramClient, peer: EntityLike):
     // regardless. These are the only two special-cased requests.
     peer = utils.getPeer(peer);
     if (peer instanceof Api.PeerUser) {
-        const users = await client.invoke(new Api.users.GetUsers({
-            id: [new Api.InputUser({
-                userId: peer.userId,
-                accessHash: bigInt.zero,
-            })],
-        }));
+        const users = await client.invoke(
+            new Api.users.GetUsers({
+                id: [
+                    new Api.InputUser({
+                        userId: peer.userId,
+                        accessHash: bigInt.zero,
+                    }),
+                ],
+            })
+        );
         if (users.length && !(users[0] instanceof Api.UserEmpty)) {
             // If the user passed a valid ID they expect to work for
             // channels but would be valid for users, we get UserEmpty.
@@ -233,106 +274,123 @@ export async function getInputEntity(client: TelegramClient, peer: EntityLike):
             // We *could* try to guess if it's a channel first, and if
             // it's not, work as a chat and try to validate it through
             // another request, but that becomes too much work.
-            return utils.getInputPeer(users[0])
+            return utils.getInputPeer(users[0]);
         }
     } else if (peer instanceof Api.PeerChat) {
         return new Api.InputPeerChat({
             chatId: peer.chatId,
-        })
+        });
     } else if (peer instanceof Api.PeerChannel) {
         try {
-            const channels = await client.invoke(new Api.channels.GetChannels({
-                id: [new Api.InputChannel({
-                    channelId: peer.channelId,
-                    accessHash: bigInt.zero,
-                })],
-            }));
-
-            return utils.getInputPeer(channels.chats[0])
-            // eslint-disable-next-line no-empty
+            const channels = await client.invoke(
+                new Api.channels.GetChannels({
+                    id: [
+                        new Api.InputChannel({
+                            channelId: peer.channelId,
+                            accessHash: bigInt.zero,
+                        }),
+                    ],
+                })
+            );
+
+            return utils.getInputPeer(channels.chats[0]);
         } catch (e) {
-            console.log(e)
+            console.error(e);
         }
     }
-    throw new Error(`Could not find the input entity for ${peer}.
+    throw new Error(
+        `Could not find the input entity for ${peer}.
          Please read https://` +
-        'docs.telethon.dev/en/latest/concepts/entities.html to' +
-        ' find out more details.',
-    )
+            "docs.telethon.dev/en/latest/concepts/entities.html to" +
+            " find out more details."
+    );
 }
 
-export async function _getEntityFromString(client: TelegramClient, string: string) {
+export async function _getEntityFromString(
+    client: TelegramClient,
+    string: string
+) {
     const phone = utils.parsePhone(string);
     if (phone) {
         try {
             const result = await client.invoke(
                 new Api.contacts.GetContacts({
-                    hash: 0
-                }));
+                    hash: 0,
+                })
+            );
             if (!(result instanceof Api.contacts.ContactsNotModified)) {
                 for (const user of result.users) {
                     if (!(user instanceof Api.User) || user.phone === phone) {
-                        return user
+                        return user;
                     }
                 }
             }
-
         } catch (e) {
-            if (e.message === 'BOT_METHOD_INVALID') {
-                throw new Error('Cannot get entity by phone number as a ' +
-                    'bot (try using integer IDs, not strings)')
+            if (e.message === "BOT_METHOD_INVALID") {
+                throw new Error(
+                    "Cannot get entity by phone number as a " +
+                        "bot (try using integer IDs, not strings)"
+                );
             }
-            throw e
+            throw e;
         }
-    } else if (['me', 'this'].includes(string.toLowerCase())) {
-        return client.getMe()
+    } else if (["me", "this"].includes(string.toLowerCase())) {
+        return client.getMe();
     } else {
-        const {username, isInvite} = utils.parseUsername(string);
+        const { username, isInvite } = utils.parseUsername(string);
         if (isInvite) {
-            const invite = await client.invoke(new Api.messages.CheckChatInvite({
-                'hash': username,
-            }));
+            const invite = await client.invoke(
+                new Api.messages.CheckChatInvite({
+                    hash: username,
+                })
+            );
             if (invite instanceof Api.ChatInvite) {
-                throw new Error('Cannot get entity from a channel (or group) ' +
-                    'that you are not part of. Join the group and retry',
-                )
+                throw new Error(
+                    "Cannot get entity from a channel (or group) " +
+                        "that you are not part of. Join the group and retry"
+                );
             } else if (invite instanceof Api.ChatInviteAlready) {
-                return invite.chat
+                return invite.chat;
             }
         } else if (username) {
             try {
                 const result = await client.invoke(
-                    new Api.contacts.ResolveUsername({username: username}));
+                    new Api.contacts.ResolveUsername({ username: username })
+                );
                 const pid = utils.getPeerId(result.peer, false);
                 if (result.peer instanceof Api.PeerUser) {
                     for (const x of result.users) {
                         if (x.id === pid) {
-                            return x
+                            return x;
                         }
                     }
                 } else {
                     for (const x of result.chats) {
                         if (x.id === pid) {
-                            return x
+                            return x;
                         }
                     }
                 }
             } catch (e) {
-                if (e.message === 'USERNAME_NOT_OCCUPIED') {
-                    throw new Error(`No user has "${username}" as username`)
+                if (e.message === "USERNAME_NOT_OCCUPIED") {
+                    throw new Error(`No user has "${username}" as username`);
                 }
-                throw e
+                throw e;
             }
         }
     }
-    throw new Error(`Cannot find any entity corresponding to "${string}"`)
+    throw new Error(`Cannot find any entity corresponding to "${string}"`);
 }
 
-export async function getPeerId(client: TelegramClient, peer: EntityLike, addMark = false) {
-    if (typeof peer == 'number') {
+export async function getPeerId(
+    client: TelegramClient,
+    peer: EntityLike,
+    addMark = false
+) {
+    if (typeof peer == "number") {
         return utils.getPeerId(peer, addMark);
     }
-    if (typeof peer == 'string') {
+    if (typeof peer == "string") {
         peer = await client.getInputEntity(peer);
     }
 
@@ -343,8 +401,6 @@ export async function getPeerId(client: TelegramClient, peer: EntityLike, addMar
         peer = await client.getMe(true);
     }
     return utils.getPeerId(peer, addMark);
-
-
 }
 
 export async function _getPeer(client: TelegramClient, peer: EntityLike) {
@@ -355,26 +411,25 @@ export async function _getPeer(client: TelegramClient, peer: EntityLike) {
     return new cls({
         userId: i,
         channelId: i,
-        chatId: i
+        chatId: i,
     });
 }
 
 export async function _getInputDialog(client: TelegramClient, dialog: any) {
     try {
-        if (dialog.SUBCLASS_OF_ID == 0xa21c9795) { // crc32(b'InputDialogPeer')
+        if (dialog.SUBCLASS_OF_ID == 0xa21c9795) {
+            // crc32(b'InputDialogPeer')
             dialog.peer = await client.getInputEntity(dialog.peer);
-            return dialog
-        } else if (dialog.SUBCLASS_OF_ID == 0xc91c90b6) { //crc32(b'InputPeer')
+            return dialog;
+        } else if (dialog.SUBCLASS_OF_ID == 0xc91c90b6) {
+            //crc32(b'InputPeer')
             return new Api.InputDialogPeer({
                 peer: dialog,
             });
         }
-
-    } catch (e) {
-
-    }
+    } catch (e) {}
     return new Api.InputDialogPeer({
-        peer: dialog
+        peer: dialog,
     });
 }
 
@@ -382,16 +437,12 @@ export async function _getInputNotify(client: TelegramClient, notify: any) {
     try {
         if (notify.SUBCLASS_OF_ID == 0x58981615) {
             if (notify instanceof Api.InputNotifyPeer) {
-                notify.peer = await client.getInputEntity(notify.peer)
+                notify.peer = await client.getInputEntity(notify.peer);
             }
             return notify;
         }
-    } catch (e) {
-
-    }
+    } catch (e) {}
     return new Api.InputNotifyPeer({
-        peer: await client.getInputEntity(notify)
-    })
-
+        peer: await client.getInputEntity(notify),
+    });
 }
-

+ 30 - 16
gramjs/crypto/AuthKey.ts

@@ -1,6 +1,11 @@
-import {sha1, toSignedLittleBuffer, readBufferFromBigInt, readBigIntFromBuffer} from '../Helpers';
-import {BinaryReader} from '../extensions';
-import {sleep} from '../Helpers';
+import {
+    sha1,
+    toSignedLittleBuffer,
+    readBufferFromBigInt,
+    readBigIntFromBuffer,
+} from "../Helpers";
+import { BinaryReader } from "../extensions";
+import { sleep } from "../Helpers";
 
 export class AuthKey {
     private _key?: Buffer;
@@ -10,44 +15,44 @@ export class AuthKey {
 
     constructor(value?: Buffer, hash?: Buffer) {
         if (!hash || !value) {
-            return
+            return;
         }
         this._key = value;
         this._hash = hash;
         const reader = new BinaryReader(hash);
         this.auxHash = reader.readLong(false);
         reader.read(4);
-        this.keyId = reader.readLong(false)
+        this.keyId = reader.readLong(false);
     }
 
     async setKey(value?: Buffer | AuthKey) {
         if (!value) {
             this._key = this.auxHash = this.keyId = this._hash = undefined;
-            return
+            return;
         }
         if (value instanceof AuthKey) {
             this._key = value._key;
             this.auxHash = value.auxHash;
             this.keyId = value.keyId;
             this._hash = value._hash;
-            return
+            return;
         }
         this._key = value;
         this._hash = await sha1(this._key);
         const reader = new BinaryReader(this._hash);
         this.auxHash = reader.readLong(false);
         reader.read(4);
-        this.keyId = reader.readLong(false)
+        this.keyId = reader.readLong(false);
     }
 
     async waitForKey() {
         while (!this.keyId) {
-            await sleep(20)
+            await sleep(20);
         }
     }
 
     getKey() {
-        return this._key
+        return this._key;
     }
 
     // TODO : This doesn't really fit here, it's only used in authentication
@@ -58,23 +63,32 @@ export class AuthKey {
      * @param number
      * @returns {bigInt.BigInteger}
      */
-    async calcNewNonceHash(newNonce: bigInt.BigInteger, number: number): Promise<bigInt.BigInteger> {
+    async calcNewNonceHash(
+        newNonce: bigInt.BigInteger,
+        number: number
+    ): Promise<bigInt.BigInteger> {
         if (this.auxHash) {
-
             const nonce = toSignedLittleBuffer(newNonce, 32);
             const n = Buffer.alloc(1);
             n.writeUInt8(number, 0);
-            const data = Buffer.concat([nonce,
-                Buffer.concat([n, readBufferFromBigInt(this.auxHash, 8, true)])]);
+            const data = Buffer.concat([
+                nonce,
+                Buffer.concat([n, readBufferFromBigInt(this.auxHash, 8, true)]),
+            ]);
 
             // Calculates the message key from the given data
             const shaData = (await sha1(data)).slice(4, 20);
-            return readBigIntFromBuffer(shaData, true, true)
+            return readBigIntFromBuffer(shaData, true, true);
         }
         throw new Error("Auth key not set");
     }
 
     equals(other: AuthKey) {
-        return other instanceof this.constructor && this._key && Buffer.isBuffer(other.getKey()) && other.getKey()?.equals(this._key);
+        return (
+            other instanceof this.constructor &&
+            this._key &&
+            Buffer.isBuffer(other.getKey()) &&
+            other.getKey()?.equals(this._key)
+        );
     }
 }

+ 4 - 4
gramjs/crypto/CTR.ts

@@ -1,17 +1,17 @@
-import * as crypto from './crypto';
+import * as crypto from "./crypto";
 
 export class CTR {
     private cipher: any;
 
     constructor(key: Buffer, iv: Buffer) {
         if (!Buffer.isBuffer(key) || !Buffer.isBuffer(iv) || iv.length !== 16) {
-            throw new Error('Key and iv need to be a buffer')
+            throw new Error("Key and iv need to be a buffer");
         }
 
-        this.cipher = crypto.createCipheriv('AES-256-CTR', key, iv)
+        this.cipher = crypto.createCipheriv("AES-256-CTR", key, iv);
     }
 
     encrypt(data: any) {
-        return Buffer.from(this.cipher.update(data))
+        return Buffer.from(this.cipher.update(data));
     }
 }

+ 17 - 20
gramjs/crypto/Factorizator.ts

@@ -1,5 +1,5 @@
 import bigInt from "big-integer";
-import {modExp} from "../Helpers";
+import { modExp } from "../Helpers";
 
 export class Factorizator {
     /**
@@ -8,13 +8,13 @@ export class Factorizator {
      * @param b {BigInteger}
      * @returns {BigInteger}
      */
-    static gcd(a:bigInt.BigInteger, b:bigInt.BigInteger) {
+    static gcd(a: bigInt.BigInteger, b: bigInt.BigInteger) {
         while (b.neq(bigInt.zero)) {
             const temp = b;
             b = a.remainder(b);
-            a = temp
+            a = temp;
         }
-        return a
+        return a;
     }
 
     /**
@@ -22,13 +22,13 @@ export class Factorizator {
      * @param pq {BigInteger}
      * @returns {{p: *, q: *}}
      */
-    static factorize(pq:bigInt.BigInteger) {
+    static factorize(pq: bigInt.BigInteger) {
         if (pq.remainder(2).equals(bigInt.zero)) {
-            return { p: bigInt(2), q: pq.divide(bigInt(2)) }
+            return { p: bigInt(2), q: pq.divide(bigInt(2)) };
         }
-        let y = bigInt.randBetween(bigInt(1),pq.minus(1));
-        const c = bigInt.randBetween(bigInt(1),pq.minus(1));
-        const m = bigInt.randBetween(bigInt(1),pq.minus(1));
+        let y = bigInt.randBetween(bigInt(1), pq.minus(1));
+        const c = bigInt.randBetween(bigInt(1), pq.minus(1));
+        const m = bigInt.randBetween(bigInt(1), pq.minus(1));
 
         let g = bigInt.one;
         let r = bigInt.one;
@@ -40,39 +40,36 @@ export class Factorizator {
         while (g.eq(bigInt.one)) {
             x = y;
             for (let i = 0; bigInt(i).lesser(r); i++) {
-                y = (modExp(y, bigInt(2), pq)).add(c).remainder(pq)
+                y = modExp(y, bigInt(2), pq).add(c).remainder(pq);
             }
             k = bigInt.zero;
 
             while (k.lesser(r) && g.eq(bigInt.one)) {
-
                 ys = y;
                 const condition = bigInt.min(m, r.minus(k));
                 for (let i = 0; bigInt(i).lesser(condition); i++) {
-                    y = (modExp(y, bigInt(2), pq)).add(c).remainder(pq);
-                    q = q.multiply(x.minus(y).abs()).remainder(pq)
+                    y = modExp(y, bigInt(2), pq).add(c).remainder(pq);
+                    q = q.multiply(x.minus(y).abs()).remainder(pq);
                 }
                 g = Factorizator.gcd(q, pq);
-                k = k.add(m)
+                k = k.add(m);
             }
 
-            r = r.multiply(2)
+            r = r.multiply(2);
         }
 
-
         if (g.eq(pq)) {
             while (true) {
-                ys = (modExp(ys, bigInt(2), pq)).add(c).remainder(pq);
+                ys = modExp(ys, bigInt(2), pq).add(c).remainder(pq);
                 g = Factorizator.gcd(x.minus(ys).abs(), pq);
 
                 if (g.greater(1)) {
-                    break
+                    break;
                 }
             }
         }
         const p = g;
         q = pq.divide(g);
-        return p < q ? { p: p, q: q } : { p: q, q: p }
+        return p < q ? { p: p, q: q } : { p: q, q: p };
     }
 }
-

+ 10 - 10
gramjs/crypto/IGE.ts

@@ -1,12 +1,12 @@
-const Helpers = require('../Helpers');
+const Helpers = require("../Helpers");
 
-const {IGE: aes_ige} = require('@cryptography/aes');
+const { IGE: aes_ige } = require("@cryptography/aes");
 
 class IGENEW {
     private ige: any;
 
     constructor(key: Buffer, iv: Buffer) {
-        this.ige = new aes_ige(key, iv)
+        this.ige = new aes_ige(key, iv);
     }
 
     /**
@@ -15,7 +15,7 @@ class IGENEW {
      * @returns {Buffer}
      */
     decryptIge(cipherText: Buffer) {
-        return Helpers.convertToLittle(this.ige.decrypt(cipherText))
+        return Helpers.convertToLittle(this.ige.decrypt(cipherText));
     }
 
     /**
@@ -26,14 +26,14 @@ class IGENEW {
     encryptIge(plainText: Buffer) {
         const padding = plainText.length % 16;
         if (padding) {
-            plainText = Buffer.concat([plainText, Helpers.generateRandomBytes(16 - padding)])
+            plainText = Buffer.concat([
+                plainText,
+                Helpers.generateRandomBytes(16 - padding),
+            ]);
         }
 
-        return Helpers.convertToLittle(this.ige.encrypt(plainText))
-
-
+        return Helpers.convertToLittle(this.ige.encrypt(plainText));
     }
-
 }
 
-export {IGENEW as IGE}
+export { IGENEW as IGE };

+ 60 - 22
gramjs/crypto/RSA.ts

@@ -1,28 +1,66 @@
-import {generateRandomBytes, modExp, readBigIntFromBuffer, readBufferFromBigInt, sha1} from "../Helpers";
+import {
+    generateRandomBytes,
+    modExp,
+    readBigIntFromBuffer,
+    readBufferFromBigInt,
+    sha1,
+} from "../Helpers";
 import bigInt from "big-integer";
 
-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,
-}];
+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,
+    },
+];
 
 const _serverKeys = new Map();
 
-PUBLIC_KEYS.forEach(({fingerprint, ...keyInfo}) => {
-    _serverKeys.set(readBigIntFromBuffer(Buffer.from(fingerprint.slice(-8)), true, true).toString(), keyInfo);
+PUBLIC_KEYS.forEach(({ fingerprint, ...keyInfo }) => {
+    _serverKeys.set(
+        readBigIntFromBuffer(
+            Buffer.from(fingerprint.slice(-8)),
+            true,
+            true
+        ).toString(),
+        keyInfo
+    );
 });
 
 /**
@@ -36,7 +74,7 @@ PUBLIC_KEYS.forEach(({fingerprint, ...keyInfo}) => {
 export async function encrypt(fingerprint: bigInt.BigInteger, data: Buffer) {
     const key = _serverKeys.get(fingerprint.toString());
     if (!key) {
-        return undefined
+        return undefined;
     }
 
     // len(sha1.digest) is always 20, so we're left with 255 - 20 - x padding
@@ -49,5 +87,5 @@ export async function encrypt(fingerprint: bigInt.BigInteger, data: Buffer) {
     const payload = readBigIntFromBuffer(toEncrypt, false);
     const encrypted = modExp(payload, bigInt(key.e), key.n);
     // rsa module uses transform.int2bytes(encrypted, keylength), easier:
-    return readBufferFromBigInt(encrypted, 256, false)
+    return readBufferFromBigInt(encrypted, 256, false);
 }

+ 16 - 12
gramjs/crypto/converters.ts

@@ -9,9 +9,9 @@ export function i2abLow(buf: Uint32Array): ArrayBuffer {
         const int = buf[j];
 
         uint8[i++] = int >>> 24;
-        uint8[i++] = (int >> 16) & 0xFF;
-        uint8[i++] = (int >> 8) & 0xFF;
-        uint8[i++] = int & 0xFF;
+        uint8[i++] = (int >> 16) & 0xff;
+        uint8[i++] = (int >> 8) & 0xff;
+        uint8[i++] = int & 0xff;
     }
 
     return uint8.buffer;
@@ -27,17 +27,18 @@ export function i2abBig(buf: Uint32Array): ArrayBuffer {
 /**
  * ArrayBuffer -> Uint32Array (low-endian os)
  */
-export function ab2iLow(ab: ArrayBuffer | SharedArrayBuffer | Uint8Array): Uint32Array {
+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]
-        );
+        buf[i / 4] =
+            (uint8[i] << 24) ^
+            (uint8[i + 1] << 16) ^
+            (uint8[i + 2] << 8) ^
+            uint8[i + 3];
     }
 
     return buf;
@@ -46,10 +47,13 @@ export function ab2iLow(ab: ArrayBuffer | SharedArrayBuffer | Uint8Array): Uint3
 /**
  * ArrayBuffer -> Uint32Array (big-endian os)
  */
-export function ab2iBig(ab: ArrayBuffer | SharedArrayBuffer | Uint8Array): Uint32Array {
+export function ab2iBig(
+    ab: ArrayBuffer | SharedArrayBuffer | Uint8Array
+): Uint32Array {
     return new Uint32Array(ab);
 }
 
-export const isBigEndian = new Uint8Array(new Uint32Array([0x01020304]))[0] === 0x01;
+export const isBigEndian =
+    new Uint8Array(new Uint32Array([0x01020304]))[0] === 0x01;
 export const i2ab = isBigEndian ? i2abBig : i2abLow;
 export const ab2i = isBigEndian ? ab2iBig : ab2iLow;

+ 55 - 39
gramjs/crypto/crypto.ts

@@ -1,22 +1,21 @@
-import {default as AES} from '@cryptography/aes';
-import {i2ab, ab2i} from './converters';
-import {getWords} from './words';
+import { default as AES } from "@cryptography/aes";
+import { i2ab, ab2i } from "./converters";
+import { getWords } from "./words";
 
 export class Counter {
     _counter: Buffer;
 
     constructor(initialValue: any) {
-        this._counter = Buffer.from(initialValue)
-
+        this._counter = Buffer.from(initialValue);
     }
 
     increment() {
         for (let i = 15; i >= 0; i--) {
             if (this._counter[i] === 255) {
-                this._counter[i] = 0
+                this._counter[i] = 0;
             } else {
                 this._counter[i]++;
-                break
+                break;
             }
         }
     }
@@ -29,9 +28,8 @@ export class CTR {
     private _aes: AES;
 
     constructor(key: Buffer, counter: any) {
-
         if (!(counter instanceof Counter)) {
-            counter = new Counter(counter)
+            counter = new Counter(counter);
         }
 
         this._counter = counter;
@@ -39,11 +37,11 @@ export class CTR {
         this._remainingCounter = undefined;
         this._remainingCounterIndex = 16;
 
-        this._aes = new AES(getWords(key))
+        this._aes = new AES(getWords(key));
     }
 
     update(plainText: any) {
-        return this.encrypt(plainText)
+        return this.encrypt(plainText);
     }
 
     encrypt(plainText: any) {
@@ -51,40 +49,43 @@ export class CTR {
 
         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._remainingCounter = Buffer.from(
+                    i2ab(this._aes.encrypt(ab2i(this._counter._counter)))
+                );
                 this._remainingCounterIndex = 0;
-                this._counter.increment()
+                this._counter.increment();
             }
             if (this._remainingCounter) {
-                encrypted[i] ^= this._remainingCounter[this._remainingCounterIndex++]
+                encrypted[i] ^=
+                    this._remainingCounter[this._remainingCounterIndex++];
             }
         }
 
-        return encrypted
+        return encrypted;
     }
 }
 
 // endregion
 export function createDecipheriv(algorithm: string, key: Buffer, iv: Buffer) {
-    if (algorithm.includes('ECB')) {
-        throw new Error('Not supported')
+    if (algorithm.includes("ECB")) {
+        throw new Error("Not supported");
     } else {
-        return new CTR(key, iv)
+        return new CTR(key, iv);
     }
 }
 
 export function createCipheriv(algorithm: string, key: Buffer, iv: Buffer) {
-    if (algorithm.includes('ECB')) {
-        throw new Error('Not supported')
+    if (algorithm.includes("ECB")) {
+        throw new Error("Not supported");
     } else {
-        return new CTR(key, iv)
+        return new CTR(key, iv);
     }
 }
 
 export function randomBytes(count: Buffer) {
     const bytes = new Uint8Array(count);
     crypto.getRandomValues(bytes);
-    return bytes
+    return bytes;
 }
 
 export class Hash {
@@ -92,37 +93,52 @@ export class Hash {
     private data?: Uint8Array;
 
     constructor(algorithm: string) {
-        this.algorithm = algorithm
-
+        this.algorithm = algorithm;
     }
 
     update(data: Buffer) {
         //We shouldn't be needing new Uint8Array but it doesn't
         //work without it
-        this.data = new Uint8Array(data)
+        this.data = new Uint8Array(data);
     }
 
     async digest() {
         if (this.data) {
-            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))
+            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)
+                );
             }
         }
-
     }
 }
 
-export async function pbkdf2Sync(password:any, salt:any, iterations:any) {
-    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))
+export async function pbkdf2Sync(password: any, salt: any, iterations: any) {
+    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
+        )
+    );
 }
 
-export function createHash(algorithm:string) {
-    return new Hash(algorithm)
+export function createHash(algorithm: string) {
+    return new Hash(algorithm);
 }

+ 13 - 13
gramjs/crypto/words.ts

@@ -4,10 +4,10 @@
 
 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)
+        (str.charCodeAt(pos) << 24) ^
+        (str.charCodeAt(pos + 1) << 16) ^
+        (str.charCodeAt(pos + 2) << 8) ^
+        str.charCodeAt(pos + 3)
     );
 }
 
@@ -19,8 +19,9 @@ export function getWords(key: string | Uint8Array | Uint32Array) {
         return key;
     }
 
-    if (typeof key === 'string') {
-        if (key.length % 4 !== 0) for (let i = key.length % 4; i <= 4; i++) key += '\0x00';
+    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);
@@ -32,18 +33,17 @@ export function getWords(key: string | Uint8Array | Uint32Array) {
         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]
-            );
+            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');
+    throw new Error("Unable to create 32-bit words");
 }
 
 export function xor(left: Uint32Array, right: Uint32Array, to = left) {

+ 31 - 19
gramjs/define.d.ts

@@ -1,15 +1,26 @@
-import type {Button} from "./tl/custom/button";
-import {Api} from "./tl";
-import type {CustomFile} from "./client/uploads";
+import type { Button } from "./tl/custom/button";
+import { Api } from "./tl";
+import type { CustomFile } from "./client/uploads";
 
 type ValueOf<T> = T[keyof T];
 type Phone = string;
 type Username = string;
 type PeerID = number;
 type Entity = Api.User | Api.Chat | Api.Channel;
-type FullEntity = Api.UserFull | Api.messages.ChatFull | Api.ChatFull | Api.ChannelFull;
-type PeerLike = Api.TypePeer | Api.TypeInputPeer | Entity | FullEntity
-type EntityLike = Phone | Username | PeerID | Api.TypePeer | Api.TypeInputPeer | Entity | FullEntity ;
+type FullEntity =
+    | Api.UserFull
+    | Api.messages.ChatFull
+    | Api.ChatFull
+    | Api.ChannelFull;
+type PeerLike = Api.TypePeer | Api.TypeInputPeer | Entity | FullEntity;
+type EntityLike =
+    | Phone
+    | Username
+    | PeerID
+    | Api.TypePeer
+    | Api.TypeInputPeer
+    | Entity
+    | FullEntity;
 
 type EntitiesLike = EntityLike[];
 type MessageIDLike = number | Api.Message | Api.TypeInputMessage;
@@ -20,21 +31,22 @@ type ExternalUrl = string;
 type BotFileID = string;
 
 type FileLike =
-    LocalPath |
-    ExternalUrl |
-    BotFileID |
-    Buffer |
-    Api.TypeMessageMedia |
-    Api.TypeInputFile |
-    Api.TypeInputFileLocation |
-    File |
-    CustomFile
+    | LocalPath
+    | ExternalUrl
+    | BotFileID
+    | Buffer
+    | Api.TypeMessageMedia
+    | Api.TypeInputFile
+    | Api.TypeInputFileLocation
+    | File
+    | CustomFile;
 
 type ProgressCallback = (total: number, downloaded: number) => void;
 type ButtonLike = Api.TypeKeyboardButton | Button;
 
-type MarkupLike = Api.TypeReplyMarkup |
-    ButtonLike |
-    ButtonLike[] |
-    ButtonLike[][];
+type MarkupLike =
+    | Api.TypeReplyMarkup
+    | ButtonLike
+    | ButtonLike[]
+    | ButtonLike[][];
 type DateLike = number;

+ 24 - 25
gramjs/entityCache.ts

@@ -1,8 +1,8 @@
 // Which updates have the following fields?
 
-import {getInputPeer, getPeerId, } from "./Utils";
-import {isArrayLike} from './Helpers'
-import {Api} from "./tl";
+import { getInputPeer, getPeerId } from "./Utils";
+import { isArrayLike } from "./Helpers";
+import { Api } from "./tl";
 
 export class EntityCache {
     private cacheMap: Map<number, any>;
@@ -12,19 +12,18 @@ export class EntityCache {
     }
 
     add(entities: any) {
-
         const temp = [];
         if (!isArrayLike(entities)) {
-            if (entities != undefined){
-                if (typeof entities == 'object'){
-                    if ('chats' in entities) {
-                        temp.push(...entities.chats)
+            if (entities != undefined) {
+                if (typeof entities == "object") {
+                    if ("chats" in entities) {
+                        temp.push(...entities.chats);
                     }
-                    if ('users' in entities) {
-                        temp.push(...entities.users)
+                    if ("users" in entities) {
+                        temp.push(...entities.users);
                     }
-                    if ('user' in entities) {
-                        temp.push(entities.user)
+                    if ("user" in entities) {
+                        temp.push(entities.user);
                     }
                 }
             }
@@ -40,14 +39,12 @@ export class EntityCache {
                 if (!this.cacheMap.has(pid)) {
                     this.cacheMap.set(pid, getInputPeer(entity));
                 }
-            } catch (e) {
-
-            }
+            } catch (e) {}
         }
     }
 
     get(item: any) {
-        if (!(typeof item === 'number') || item < 0) {
+        if (!(typeof item === "number") || item < 0) {
             let res;
             try {
                 res = this.cacheMap.get(getPeerId(item));
@@ -55,22 +52,24 @@ export class EntityCache {
                     return res;
                 }
             } catch (e) {
-                throw new Error('Invalid key will not have entity')
+                throw new Error("Invalid key will not have entity");
             }
         }
         for (const cls of [Api.PeerUser, Api.PeerChat, Api.PeerChannel]) {
             // TODO remove these "as"
-            const result = this.cacheMap.get(getPeerId(new cls({
-                userId: item as number,
-                chatId: item as number,
-                channelId: item as number
-            })));
+            const result = this.cacheMap.get(
+                getPeerId(
+                    new cls({
+                        userId: item as number,
+                        chatId: item as number,
+                        channelId: item as number,
+                    })
+                )
+            );
             if (result) {
                 return result;
             }
         }
-        throw new Error('No cached entity for the given key');
+        throw new Error("No cached entity for the given key");
     }
-
-
 }

+ 39 - 34
gramjs/errors/Common.ts

@@ -2,15 +2,14 @@
  * Errors not related to the Telegram API itself
  */
 
-
-import {Api} from "../tl";
+import { Api } from "../tl";
 
 /**
  * Occurs when a read operation was cancelled.
  */
 export class ReadCancelledError extends Error {
     constructor() {
-        super('The read operation was cancelled.')
+        super("The read operation was cancelled.");
     }
 }
 
@@ -26,11 +25,13 @@ export class TypeNotFoundError extends Error {
         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.length}`);
-        if (typeof alert !== 'undefined') {
-            alert(`Missing MTProto Entity: Please, make sure to add TL definition for ID ${invalidConstructorId}`);
+        if (typeof alert !== "undefined") {
+            alert(
+                `Missing MTProto Entity: Please, make sure to add TL definition for ID ${invalidConstructorId}`
+            );
         }
         this.invalidConstructorId = invalidConstructorId;
-        this.remaining = remaining
+        this.remaining = remaining;
     }
 }
 
@@ -43,7 +44,9 @@ export class InvalidChecksumError extends Error {
     private validChecksum: number;
 
     constructor(checksum: number, validChecksum: number) {
-        super(`Invalid checksum (${checksum} when ${validChecksum} was expected). This packet should be skipped.`);
+        super(
+            `Invalid checksum (${checksum} when ${validChecksum} was expected). This packet should be skipped.`
+        );
         this.checksum = checksum;
         this.validChecksum = validChecksum;
     }
@@ -61,9 +64,9 @@ export class InvalidBufferError extends Error {
         let code = undefined;
         if (payload.length === 4) {
             code = -payload.readInt32LE(0);
-            super(`Invalid response buffer (HTTP code ${code})`)
+            super(`Invalid response buffer (HTTP code ${code})`);
         } else {
-            super(`Invalid response buffer (too short ${payload})`)
+            super(`Invalid response buffer (too short ${payload})`);
         }
         this.code = code;
         this.payload = payload;
@@ -76,9 +79,9 @@ export class InvalidBufferError extends Error {
 export class SecurityError extends Error {
     constructor(...args: any[]) {
         if (!args.length) {
-            args = ['A security check failed.']
+            args = ["A security check failed."];
         }
-        super(...args)
+        super(...args);
     }
 }
 
@@ -88,7 +91,7 @@ export class SecurityError extends Error {
  */
 export class CdnFileTamperedError extends SecurityError {
     constructor() {
-        super('The CDN file has been altered and its download cancelled.')
+        super("The CDN file has been altered and its download cancelled.");
     }
 }
 
@@ -97,49 +100,51 @@ export class CdnFileTamperedError extends SecurityError {
  */
 export class BadMessageError extends Error {
     static ErrorMessages = {
-
         16:
-            'msg_id too low (most likely, client time is wrong it would be worthwhile to ' +
-            'synchronize it using msg_id notifications and re-send the original message ' +
-            'with the “correct” msg_id or wrap it in a container with a new msg_id if the ' +
-            'original message had waited too long on the client to be transmitted).',
+            "msg_id too low (most likely, client time is wrong it would be worthwhile to " +
+            "synchronize it using msg_id notifications and re-send the original message " +
+            "with the “correct” msg_id or wrap it in a container with a new msg_id if the " +
+            "original message had waited too long on the client to be transmitted).",
 
         17:
-            'msg_id too high (similar to the previous case, the client time has to be ' +
-            'synchronized, and the message re-sent with the correct msg_id).',
+            "msg_id too high (similar to the previous case, the client time has to be " +
+            "synchronized, and the message re-sent with the correct msg_id).",
 
         18:
-            'Incorrect two lower order msg_id bits (the server expects client message msg_id ' +
-            'to be divisible by 4).',
+            "Incorrect two lower order msg_id bits (the server expects client message msg_id " +
+            "to be divisible by 4).",
 
-        19: 'Container msg_id is the same as msg_id of a previously received message ' + '(this must never happen).',
+        19:
+            "Container msg_id is the same as msg_id of a previously received message " +
+            "(this must never happen).",
 
         20:
-            'Message too old, and it cannot be verified whether the server has received a ' +
-            'message with this msg_id or not.',
+            "Message too old, and it cannot be verified whether the server has received a " +
+            "message with this msg_id or not.",
 
         32:
-            'msg_seqno too low (the server has already received a message with a lower ' +
-            'msg_id but with either a higher or an equal and odd seqno).',
+            "msg_seqno too low (the server has already received a message with a lower " +
+            "msg_id but with either a higher or an equal and odd seqno).",
 
         33:
-            'msg_seqno too high (similarly, there is a message with a higher msg_id but with ' +
-            'either a lower or an equal and odd seqno).',
+            "msg_seqno too high (similarly, there is a message with a higher msg_id but with " +
+            "either a lower or an equal and odd seqno).",
 
-        34: 'An even msg_seqno expected (irrelevant message), but odd received.',
+        34: "An even msg_seqno expected (irrelevant message), but odd received.",
 
-        35: 'Odd msg_seqno expected (relevant message), but even received.',
+        35: "Odd msg_seqno expected (relevant message), but even received.",
 
         48:
-            'Incorrect server salt (in this case, the bad_server_salt response is received with ' +
-            'the correct salt, and the message is to be re-sent with it).',
+            "Incorrect server salt (in this case, the bad_server_salt response is received with " +
+            "the correct salt, and the message is to be re-sent with it).",
 
-        64: 'Invalid container.',
+        64: "Invalid container.",
     };
     private code: number;
 
     constructor(request: Api.AnyRequest, code: number) {
-        let errorMessage = (BadMessageError.ErrorMessages as any)[code] ||
+        let errorMessage =
+            (BadMessageError.ErrorMessages as any)[code] ||
             `Unknown error code (this should not happen): ${code}.`;
         errorMessage += `  Caused by ${request.className}`;
         super(errorMessage);

+ 18 - 18
gramjs/errors/RPCBaseErrors.ts

@@ -1,28 +1,28 @@
 /**
  * Base class for all Remote Procedure Call errors.
  */
-import {Api} from "../tl";
+import { Api } from "../tl";
 
 export class RPCError extends Error {
     protected code: number | undefined;
 
     constructor(message: string, request: Api.AnyRequest, code?: number) {
         super(
-            'RPCError {0}: {1}{2}'
-                .replace('{0}', code?.toString() || '')
-                .replace('{1}', message||'')
-                .replace('{2}', RPCError._fmtRequest(request)),
+            "RPCError {0}: {1}{2}"
+                .replace("{0}", code?.toString() || "")
+                .replace("{1}", message || "")
+                .replace("{2}", RPCError._fmtRequest(request))
         );
         this.code = code;
-        this.message = message
+        this.message = message;
     }
 
     static _fmtRequest(request: Api.AnyRequest) {
         // TODO fix this
         if (request) {
-            return ` (caused by ${request.className})`
+            return ` (caused by ${request.className})`;
         } else {
-            return ''
+            return "";
         }
     }
 }
@@ -31,10 +31,10 @@ export class RPCError extends Error {
  * The request must be repeated, but directed to a different data center.
  */
 export class InvalidDCError extends RPCError {
-    constructor(message:string,request:Api.AnyRequest, code?: number) {
+    constructor(message: string, request: Api.AnyRequest, code?: number) {
         super(message, request, code);
         this.code = code || 303;
-        this.message = message || 'ERROR_SEE_OTHER'
+        this.message = message || "ERROR_SEE_OTHER";
     }
 }
 
@@ -45,7 +45,7 @@ export class InvalidDCError extends RPCError {
  */
 export class BadRequestError extends RPCError {
     code = 400;
-    message = 'BAD_REQUEST'
+    message = "BAD_REQUEST";
 }
 
 /**
@@ -54,7 +54,7 @@ export class BadRequestError extends RPCError {
  */
 export class UnauthorizedError extends RPCError {
     code = 401;
-    message = 'UNAUTHORIZED'
+    message = "UNAUTHORIZED";
 }
 
 /**
@@ -63,7 +63,7 @@ export class UnauthorizedError extends RPCError {
  */
 export class ForbiddenError extends RPCError {
     code = 403;
-    message = 'FORBIDDEN'
+    message = "FORBIDDEN";
 }
 
 /**
@@ -71,7 +71,7 @@ export class ForbiddenError extends RPCError {
  */
 export class NotFoundError extends RPCError {
     code = 404;
-    message = 'NOT_FOUND'
+    message = "NOT_FOUND";
 }
 
 /**
@@ -80,7 +80,7 @@ export class NotFoundError extends RPCError {
  */
 export class AuthKeyError extends RPCError {
     code = 406;
-    message = 'AUTH_KEY'
+    message = "AUTH_KEY";
 }
 
 /**
@@ -91,7 +91,7 @@ export class AuthKeyError extends RPCError {
  */
 export class FloodError extends RPCError {
     code = 420;
-    message = 'FLOOD'
+    message = "FLOOD";
 }
 
 /**
@@ -101,7 +101,7 @@ export class FloodError extends RPCError {
  */
 export class ServerError extends RPCError {
     code = 500; // Also witnessed as -500
-    message = 'INTERNAL'
+    message = "INTERNAL";
 }
 
 /**
@@ -110,5 +110,5 @@ export class ServerError extends RPCError {
  */
 export class TimedOutError extends RPCError {
     code = 503; // Only witnessed as -503
-    message = 'Timeout'
+    message = "Timeout";
 }

+ 64 - 25
gramjs/errors/RPCErrorList.ts

@@ -1,26 +1,36 @@
-import {RPCError, InvalidDCError, FloodError} from './RPCBaseErrors';
-
+import { RPCError, InvalidDCError, FloodError } from "./RPCBaseErrors";
 
 export class UserMigrateError extends InvalidDCError {
     public newDc: number;
 
     constructor(args: any) {
         const newDc = Number(args.capture || 0);
-        super(`The user whose identity is being used to execute queries is associated with DC ${newDc}` + RPCError._fmtRequest(args.request), args.request);
-        this.message = `The user whose identity is being used to execute queries is associated with DC ${newDc}` + RPCError._fmtRequest(args.request);
-        this.newDc = newDc
+        super(
+            `The user whose identity is being used to execute queries is associated with DC ${newDc}` +
+                RPCError._fmtRequest(args.request),
+            args.request
+        );
+        this.message =
+            `The user whose identity is being used to execute queries is associated with DC ${newDc}` +
+            RPCError._fmtRequest(args.request);
+        this.newDc = newDc;
     }
 }
 
-
 export class PhoneMigrateError extends InvalidDCError {
     public newDc: number;
 
     constructor(args: any) {
         const newDc = Number(args.capture || 0);
-        super(`The phone number a user is trying to use for authorization is associated with DC ${newDc}` + RPCError._fmtRequest(args.request), args.request);
-        this.message = `The phone number a user is trying to use for authorization is associated with DC ${newDc}` + RPCError._fmtRequest(args.request);
-        this.newDc = newDc
+        super(
+            `The phone number a user is trying to use for authorization is associated with DC ${newDc}` +
+                RPCError._fmtRequest(args.request),
+            args.request
+        );
+        this.message =
+            `The phone number a user is trying to use for authorization is associated with DC ${newDc}` +
+            RPCError._fmtRequest(args.request);
+        this.newDc = newDc;
     }
 }
 
@@ -29,9 +39,15 @@ export class SlowModeWaitError extends FloodError {
 
     constructor(args: any) {
         const seconds = Number(args.capture || 0);
-        super(`A wait of ${seconds} seconds is required before sending another message in this chat` + RPCError._fmtRequest(args.request), args.request);
-        this.message = `A wait of ${seconds} seconds is required before sending another message in this chat` + RPCError._fmtRequest(args.request);
-        this.seconds = seconds
+        super(
+            `A wait of ${seconds} seconds is required before sending another message in this chat` +
+                RPCError._fmtRequest(args.request),
+            args.request
+        );
+        this.message =
+            `A wait of ${seconds} seconds is required before sending another message in this chat` +
+            RPCError._fmtRequest(args.request);
+        this.seconds = seconds;
     }
 }
 
@@ -40,9 +56,15 @@ export class FloodWaitError extends FloodError {
 
     constructor(args: any) {
         const seconds = Number(args.capture || 0);
-        super(`A wait of ${seconds} seconds is required` + RPCError._fmtRequest(args.request), args.request);
-        this.message = `A wait of ${seconds} seconds is required` + RPCError._fmtRequest(args.request);
-        this.seconds = seconds
+        super(
+            `A wait of ${seconds} seconds is required` +
+                RPCError._fmtRequest(args.request),
+            args.request
+        );
+        this.message =
+            `A wait of ${seconds} seconds is required` +
+            RPCError._fmtRequest(args.request);
+        this.seconds = seconds;
     }
 }
 
@@ -51,9 +73,15 @@ export class FloodTestPhoneWaitError extends FloodError {
 
     constructor(args: any) {
         const seconds = Number(args.capture || 0);
-        super(`A wait of ${seconds} seconds is required in the test servers` + RPCError._fmtRequest(args.request), args.request);
-        this.message = `A wait of ${seconds} seconds is required in the test servers` + RPCError._fmtRequest(args.request);
-        this.seconds = seconds
+        super(
+            `A wait of ${seconds} seconds is required in the test servers` +
+                RPCError._fmtRequest(args.request),
+            args.request
+        );
+        this.message =
+            `A wait of ${seconds} seconds is required in the test servers` +
+            RPCError._fmtRequest(args.request);
+        this.seconds = seconds;
     }
 }
 
@@ -62,9 +90,15 @@ export class FileMigrateError extends InvalidDCError {
 
     constructor(args: any) {
         const newDc = Number(args.capture || 0);
-        super(`The file to be accessed is currently stored in DC ${newDc}` + RPCError._fmtRequest(args.request), args.request);
-        this.message = `The file to be accessed is currently stored in DC ${newDc}` + RPCError._fmtRequest(args.request);
-        this.newDc = newDc
+        super(
+            `The file to be accessed is currently stored in DC ${newDc}` +
+                RPCError._fmtRequest(args.request),
+            args.request
+        );
+        this.message =
+            `The file to be accessed is currently stored in DC ${newDc}` +
+            RPCError._fmtRequest(args.request);
+        this.newDc = newDc;
     }
 }
 
@@ -73,9 +107,15 @@ export class NetworkMigrateError extends InvalidDCError {
 
     constructor(args: any) {
         const newDc = Number(args.capture || 0);
-        super(`The source IP address is associated with DC ${newDc}` + RPCError._fmtRequest(args.request), args.request);
-        this.message = `The source IP address is associated with DC ${newDc}` + RPCError._fmtRequest(args.request);
-        this.newDc = newDc
+        super(
+            `The source IP address is associated with DC ${newDc}` +
+                RPCError._fmtRequest(args.request),
+            args.request
+        );
+        this.message =
+            `The source IP address is associated with DC ${newDc}` +
+            RPCError._fmtRequest(args.request);
+        this.newDc = newDc;
     }
 }
 
@@ -87,5 +127,4 @@ export const rpcErrorRe = new Map<RegExp, any>([
     [/SLOWMODE_WAIT_(\d+)/, SlowModeWaitError],
     [/USER_MIGRATE_(\d+)/, UserMigrateError],
     [/NETWORK_MIGRATE_(\d+)/, NetworkMigrateError],
-
 ]);

+ 12 - 9
gramjs/errors/index.ts

@@ -4,23 +4,26 @@
  * @param request the request that caused this error
  * @constructor the RPCError as a Python exception that represents this error
  */
-import {Api} from "../tl";
+import { Api } from "../tl";
 
-import {RPCError} from './RPCBaseErrors';
-import {rpcErrorRe} from './RPCErrorList';
+import { RPCError } from "./RPCBaseErrors";
+import { rpcErrorRe } from "./RPCErrorList";
 
-export function RPCMessageToError(rpcError: Api.RpcError, request: Api.AnyRequest) {
+export function RPCMessageToError(
+    rpcError: Api.RpcError,
+    request: Api.AnyRequest
+) {
     for (const [msgRegex, Cls] of rpcErrorRe) {
         const m = rpcError.errorMessage.match(msgRegex);
         if (m) {
             const capture = m.length === 2 ? parseInt(m[1]) : null;
-            return new Cls({request: request, capture: capture})
+            return new Cls({ request: request, capture: capture });
         }
     }
 
-    return new RPCError(rpcError.errorMessage, request)
+    return new RPCError(rpcError.errorMessage, request);
 }
 
-export * from './Common';
-export * from './RPCBaseErrors';
-export * from './RPCErrorList';
+export * from "./Common";
+export * from "./RPCBaseErrors";
+export * from "./RPCErrorList";

+ 96 - 63
gramjs/events/NewMessage.ts

@@ -1,19 +1,19 @@
-import {_intoIdSet, EventBuilder, EventCommon} from "./common";
-import type {Entity, EntityLike} from "../define";
-import type {TelegramClient} from "../client/TelegramClient";
-import {Api} from "../tl";
-import {Message} from "../tl/patched";
-import type {Message as CustomMessage} from "../tl/custom/message";
+import { _intoIdSet, EventBuilder, EventCommon } from "./common";
+import type { Entity, EntityLike } from "../define";
+import type { TelegramClient } from "../client/TelegramClient";
+import { Api } from "../tl";
+import { Message } from "../tl/patched";
+import type { Message as CustomMessage } from "../tl/custom/message";
 
 interface NewMessageInterface {
-    chats?: EntityLike[],
-    func?: CallableFunction,
-    incoming?: boolean,
-    outgoing?: boolean,
-    fromUsers?: EntityLike[],
-    forwards?: boolean,
-    pattern?: RegExp,
-    blacklistChats?: boolean
+    chats?: EntityLike[];
+    func?: CallableFunction;
+    incoming?: boolean;
+    outgoing?: boolean;
+    fromUsers?: EntityLike[];
+    forwards?: boolean;
+    pattern?: RegExp;
+    blacklistChats?: boolean;
 }
 
 export class NewMessage extends EventBuilder {
@@ -26,7 +26,16 @@ export class NewMessage extends EventBuilder {
     pattern?: RegExp;
     private _noCheck: boolean;
 
-    constructor({chats, func, incoming, outgoing, fromUsers, forwards, pattern, blacklistChats = true}: NewMessageInterface) {
+    constructor({
+        chats,
+        func,
+        incoming,
+        outgoing,
+        fromUsers,
+        forwards,
+        pattern,
+        blacklistChats = true,
+    }: NewMessageInterface) {
         if (incoming && outgoing) {
             incoming = outgoing = undefined;
         } else if (incoming != undefined && outgoing == undefined) {
@@ -34,16 +43,23 @@ export class NewMessage extends EventBuilder {
         } else if (outgoing != undefined && incoming == undefined) {
             incoming = !outgoing;
         } else if (outgoing == false && incoming == false) {
-            throw new Error("Don't create an event handler if you don't want neither incoming nor outgoing!")
+            throw new Error(
+                "Don't create an event handler if you don't want neither incoming nor outgoing!"
+            );
         }
-        super({chats, blacklistChats, func});
+        super({ chats, blacklistChats, func });
         this.incoming = incoming;
         this.outgoing = outgoing;
         this.fromUsers = fromUsers;
         this.forwards = forwards;
         this.pattern = pattern;
-        this._noCheck = [incoming, outgoing, fromUsers, forwards, pattern].every(v => v == undefined);
-
+        this._noCheck = [
+            incoming,
+            outgoing,
+            fromUsers,
+            forwards,
+            pattern,
+        ].every((v) => v == undefined);
     }
 
     async _resolve(client: TelegramClient) {
@@ -52,48 +68,62 @@ export class NewMessage extends EventBuilder {
     }
 
     build(update: Api.TypeUpdate, others: any = null) {
-        if (update instanceof Api.UpdateNewMessage || update instanceof Api.UpdateNewChannelMessage) {
-            if (!(update.message instanceof Api.Message) && !(update.message instanceof Message)) {
+        if (
+            update instanceof Api.UpdateNewMessage ||
+            update instanceof Api.UpdateNewChannelMessage
+        ) {
+            if (
+                !(update.message instanceof Api.Message) &&
+                !(update.message instanceof Message)
+            ) {
                 return undefined;
             }
-            const event = new NewMessageEvent(update.message as Message, update);
+            const event = new NewMessageEvent(
+                update.message as Message,
+                update
+            );
             this.addAttributes(event);
             return event;
         } else if (update instanceof Api.UpdateShortMessage) {
-
-            return new NewMessageEvent(new Message({
-                out: update.out,
-                mentioned: update.mentioned,
-                mediaUnread: update.mediaUnread,
-                silent: update.silent,
-                id: update.id,
-                peerId: new Api.PeerUser({userId: update.userId}),
-                fromId: new Api.PeerUser({userId: update.userId}),
-                message: update.message,
-                date: update.date,
-                fwdFrom: update.fwdFrom,
-                viaBotId: update.viaBotId,
-                replyTo: update.replyTo,
-                entities: update.entities,
-                // ttlPeriod:update.ttlPeriod
-            }), update)
+            return new NewMessageEvent(
+                new Message({
+                    out: update.out,
+                    mentioned: update.mentioned,
+                    mediaUnread: update.mediaUnread,
+                    silent: update.silent,
+                    id: update.id,
+                    peerId: new Api.PeerUser({ userId: update.userId }),
+                    fromId: new Api.PeerUser({ userId: update.userId }),
+                    message: update.message,
+                    date: update.date,
+                    fwdFrom: update.fwdFrom,
+                    viaBotId: update.viaBotId,
+                    replyTo: update.replyTo,
+                    entities: update.entities,
+                    // ttlPeriod:update.ttlPeriod
+                }),
+                update
+            );
         } else if (update instanceof Api.UpdateShortChatMessage) {
-            return new NewMessageEvent(new Message({
-                out: update.out,
-                mentioned: update.mentioned,
-                mediaUnread: update.mediaUnread,
-                silent: update.silent,
-                id: update.id,
-                peerId: new Api.PeerChat({chatId: update.chatId}),
-                fromId: new Api.PeerUser({userId: update.fromId}),
-                message: update.message,
-                date: update.date,
-                fwdFrom: update.fwdFrom,
-                viaBotId: update.viaBotId,
-                replyTo: update.replyTo,
-                entities: update.entities,
-                // ttlPeriod:update.ttlPeriod
-            }), update)
+            return new NewMessageEvent(
+                new Message({
+                    out: update.out,
+                    mentioned: update.mentioned,
+                    mediaUnread: update.mediaUnread,
+                    silent: update.silent,
+                    id: update.id,
+                    peerId: new Api.PeerChat({ chatId: update.chatId }),
+                    fromId: new Api.PeerUser({ userId: update.fromId }),
+                    message: update.message,
+                    date: update.date,
+                    fwdFrom: update.fwdFrom,
+                    viaBotId: update.viaBotId,
+                    replyTo: update.replyTo,
+                    entities: update.entities,
+                    // ttlPeriod:update.ttlPeriod
+                }),
+                update
+            );
         }
     }
 
@@ -102,7 +132,7 @@ export class NewMessage extends EventBuilder {
             return event;
         }
         if (this.incoming && event.message.out) {
-            return
+            return;
         }
         if (this.outgoing && !event.message.out) {
             return;
@@ -115,18 +145,16 @@ export class NewMessage extends EventBuilder {
         if (this.pattern) {
             const match = event.message.message.match(this.pattern);
             if (!match) {
-                return
+                return;
             }
             event.message.patternMatch = match;
         }
         return super.filter(event);
     }
 
-
     addAttributes(update: any) {
         //update.patternMatch =
     }
-
 }
 
 export class NewMessageEvent extends EventCommon {
@@ -134,7 +162,6 @@ export class NewMessageEvent extends EventCommon {
     originalUpdate: Api.TypeUpdate & { _entities?: Map<number, Entity> };
 
     constructor(message: CustomMessage, originalUpdate: Api.TypeUpdate) {
-
         super({
             msgId: message.id,
             chatPeer: message.peerId,
@@ -149,10 +176,16 @@ export class NewMessageEvent extends EventCommon {
         const m = this.message;
         try {
             // todo make sure this never fails
-            m._finishInit(client, this.originalUpdate._entities || new Map(), undefined);
+            m._finishInit(
+                client,
+                this.originalUpdate._entities || new Map(),
+                undefined
+            );
         } catch (e) {
-            client._log.error("Got error while trying to finish init message with id " + m.id);
-            if (client._log.canSend('error')){
+            client._log.error(
+                "Got error while trying to finish init message with id " + m.id
+            );
+            if (client._log.canSend("error")) {
                 console.error(e);
             }
         }

+ 9 - 10
gramjs/events/Raw.ts

@@ -1,17 +1,17 @@
-import {EventBuilder, EventCommon} from "./common";
-import type {TelegramClient} from "../client/TelegramClient";
-import {Api} from "../tl";
+import { EventBuilder, EventCommon } from "./common";
+import type { TelegramClient } from "../client/TelegramClient";
+import { Api } from "../tl";
 
 interface RawInterface {
-    types?: Function[],
-    func?: CallableFunction
+    types?: Function[];
+    func?: CallableFunction;
 }
 
 export class Raw extends EventBuilder {
     private types?: Function[];
 
-    constructor({types = undefined, func = undefined}: RawInterface) {
-        super({func: func});
+    constructor({ types = undefined, func = undefined }: RawInterface) {
+        super({ func: func });
         this.types = types;
     }
 
@@ -20,17 +20,16 @@ export class Raw extends EventBuilder {
     }
 
     build(update: Api.TypeUpdate, others: any = null): Api.TypeUpdate {
-        return update
+        return update;
     }
 
     filter(event: EventCommon) {
-
         if (this.types) {
             let correct = false;
             for (const type of this.types) {
                 if (event instanceof type) {
                     correct = true;
-                    break
+                    break;
                 }
             }
             if (!correct) {

+ 55 - 39
gramjs/events/common.ts

@@ -1,19 +1,22 @@
-import {Api} from "../tl";
-import type {Entity, EntityLike} from "../define";
-import {ChatGetter} from "../tl/custom";
-import type {TelegramClient} from "../client/TelegramClient";
+import { Api } from "../tl";
+import type { Entity, EntityLike } from "../define";
+import { ChatGetter } from "../tl/custom";
+import type { TelegramClient } from "../client/TelegramClient";
 
 import bigInt from "big-integer";
-import {isArrayLike} from "../Helpers";
-import {utils} from "../";
-import {Message} from "../tl/patched";
-
-export async function _intoIdSet(client: TelegramClient, chats: EntityLike[] | EntityLike | undefined): Promise<number[] | undefined> {
+import { isArrayLike } from "../Helpers";
+import { utils } from "../";
+import { Message } from "../tl/patched";
+
+export async function _intoIdSet(
+    client: TelegramClient,
+    chats: EntityLike[] | EntityLike | undefined
+): Promise<number[] | undefined> {
     if (chats == undefined) {
         return undefined;
     }
     if (!isArrayLike(chats)) {
-        chats = [chats]
+        chats = [chats];
     }
     const result: Set<number> = new Set<number>();
     for (let chat of chats) {
@@ -21,18 +24,32 @@ export async function _intoIdSet(client: TelegramClient, chats: EntityLike[] | E
             if (chat < 0) {
                 result.add(chat);
             } else {
-                result.add(utils.getPeerId(new Api.PeerUser({
-                    userId: chat,
-                })));
-                result.add(utils.getPeerId(new Api.PeerChat({
-                    chatId: chat,
-                })));
-                result.add(utils.getPeerId(new Api.PeerChannel({
-                    channelId: chat,
-                })));
-
+                result.add(
+                    utils.getPeerId(
+                        new Api.PeerUser({
+                            userId: chat,
+                        })
+                    )
+                );
+                result.add(
+                    utils.getPeerId(
+                        new Api.PeerChat({
+                            chatId: chat,
+                        })
+                    )
+                );
+                result.add(
+                    utils.getPeerId(
+                        new Api.PeerChannel({
+                            channelId: chat,
+                        })
+                    )
+                );
             }
-        } else if (typeof chat == "object" && chat.SUBCLASS_OF_ID == 0x2d45687) {
+        } else if (
+            typeof chat == "object" &&
+            chat.SUBCLASS_OF_ID == 0x2d45687
+        ) {
             result.add(utils.getPeerId(chat));
         } else {
             chat = await client.getInputEntity(chat);
@@ -46,10 +63,9 @@ export async function _intoIdSet(client: TelegramClient, chats: EntityLike[] | E
 }
 
 interface DefaultEventInterface {
-    chats?: EntityLike[],
-    blacklistChats?: boolean,
-    func?: CallableFunction,
-
+    chats?: EntityLike[];
+    blacklistChats?: boolean;
+    func?: CallableFunction;
 }
 
 export class EventBuilder {
@@ -58,21 +74,20 @@ export class EventBuilder {
     resolved: boolean;
     func?: CallableFunction;
 
-    constructor({chats, blacklistChats = true, func}: DefaultEventInterface) {
+    constructor({ chats, blacklistChats = true, func }: DefaultEventInterface) {
         this.chats = chats;
         this.blacklistChats = blacklistChats;
         this.resolved = false;
-        this.func = func
+        this.func = func;
     }
 
     build(update: Api.TypeUpdate, others = null): any {
-        if (update)
-            return update;
+        if (update) return update;
     }
 
     async resolve(client: TelegramClient) {
         if (this.resolved) {
-            return
+            return;
         }
         await this._resolve(client);
         this.resolved = true;
@@ -84,7 +99,7 @@ export class EventBuilder {
 
     filter(event: any): undefined | EventBuilder {
         if (!this.resolved) {
-            return
+            return;
         }
         if (this.chats != undefined && event.chatId != undefined) {
             const inside = this.chats.includes(event.chatId);
@@ -92,7 +107,6 @@ export class EventBuilder {
                 // If this chat matches but it's a blacklist ignore.
                 // If it doesn't match but it's a whitelist ignore.
                 return;
-
             }
         }
         return event;
@@ -100,9 +114,9 @@ export class EventBuilder {
 }
 
 interface EventCommonInterface {
-    chatPeer?: EntityLike,
-    msgId?: number,
-    broadcast?: boolean,
+    chatPeer?: EntityLike;
+    msgId?: number;
+    broadcast?: boolean;
 }
 
 export class EventCommon extends ChatGetter {
@@ -110,8 +124,12 @@ export class EventCommon extends ChatGetter {
     _entities: Map<number, Entity>;
     _messageId?: number;
 
-    constructor({chatPeer = undefined, msgId = undefined, broadcast = undefined}: EventCommonInterface) {
-        super({chatPeer, broadcast});
+    constructor({
+        chatPeer = undefined,
+        msgId = undefined,
+        broadcast = undefined,
+    }: EventCommonInterface) {
+        super({ chatPeer, broadcast });
         this._entities = new Map();
         this._client = undefined;
         this._messageId = msgId;
@@ -124,6 +142,4 @@ export class EventCommon extends ChatGetter {
     get client() {
         return this._client;
     }
-
 }
-

+ 2 - 2
gramjs/events/index.ts

@@ -1,2 +1,2 @@
-export {Raw} from "./Raw";
-export {NewMessage, NewMessageEvent} from "./NewMessage";
+export { Raw } from "./Raw";
+export { NewMessage, NewMessageEvent } from "./NewMessage";

+ 16 - 17
gramjs/extensions/AsyncQueue.ts

@@ -1,37 +1,36 @@
 export class AsyncQueue {
     public _queue: any[];
     private canGet: Promise<unknown>;
-    private resolveGet: ((value?: any) => void);
+    private resolveGet: (value?: any) => void;
     private canPush: boolean | Promise<boolean>;
-    private resolvePush: ((value?: any) => void);
+    private resolvePush: (value?: any) => void;
 
     constructor() {
-        this._queue = []
-        this.canPush = true
-        this.resolvePush =value => {};
-        this.resolveGet =value => {};
-        this.canGet = new Promise(resolve => {
-            this.resolveGet = resolve
-        })
-
+        this._queue = [];
+        this.canPush = true;
+        this.resolvePush = (value) => {};
+        this.resolveGet = (value) => {};
+        this.canGet = new Promise((resolve) => {
+            this.resolveGet = resolve;
+        });
     }
 
     async push(value: any) {
         await this.canPush;
         this._queue.push(value);
         this.resolveGet(true);
-        this.canPush = new Promise(resolve => {
-            this.resolvePush = resolve
-        })
+        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
+        this.resolvePush(true);
+        this.canGet = new Promise((resolve) => {
+            this.resolveGet = resolve;
         });
-        return returned
+        return returned;
     }
 }

+ 43 - 42
gramjs/extensions/BinaryReader.ts

@@ -1,9 +1,8 @@
-import {TypeNotFoundError} from '../errors';
-import {coreObjects} from '../tl/core';
-
-import {tlobjects} from '../tl/AllTLObjects';
-import {readBigIntFromBuffer} from "../Helpers";
+import { TypeNotFoundError } from "../errors";
+import { coreObjects } from "../tl/core";
 
+import { tlobjects } from "../tl/AllTLObjects";
+import { readBigIntFromBuffer } from "../Helpers";
 
 export class BinaryReader {
     private stream: Buffer;
@@ -17,7 +16,7 @@ export class BinaryReader {
     constructor(data: Buffer) {
         this.stream = data;
         this._last = undefined;
-        this.offset = 0
+        this.offset = 0;
     }
 
     // region Reading
@@ -28,7 +27,7 @@ export class BinaryReader {
      * Reads a single byte value.
      */
     readByte() {
-        return this.read(1)[0]
+        return this.read(1)[0];
     }
 
     /**
@@ -38,12 +37,12 @@ export class BinaryReader {
     readInt(signed = true) {
         let res;
         if (signed) {
-            res = this.stream.readInt32LE(this.offset)
+            res = this.stream.readInt32LE(this.offset);
         } else {
-            res = this.stream.readUInt32LE(this.offset)
+            res = this.stream.readUInt32LE(this.offset);
         }
         this.offset += 4;
-        return res
+        return res;
     }
 
     /**
@@ -52,7 +51,7 @@ export class BinaryReader {
      * @returns {BigInteger}
      */
     readLong(signed = true) {
-        return this.readLargeInt(64, signed)
+        return this.readLargeInt(64, signed);
     }
 
     /**
@@ -60,7 +59,7 @@ export class BinaryReader {
      * @returns {number}
      */
     readFloat() {
-        return this.read(4).readFloatLE(0)
+        return this.read(4).readFloatLE(0);
     }
 
     /**
@@ -69,7 +68,7 @@ export class BinaryReader {
      */
     readDouble() {
         // was this a bug ? it should have been <d
-        return this.read(8).readDoubleLE(0)
+        return this.read(8).readDoubleLE(0);
     }
 
     /**
@@ -79,7 +78,7 @@ export class BinaryReader {
      */
     readLargeInt(bits: number, signed = true) {
         const buffer = this.read(Math.floor(bits / 8));
-        return readBigIntFromBuffer(buffer, true, signed)
+        return readBigIntFromBuffer(buffer, true, signed);
     }
 
     /**
@@ -88,17 +87,17 @@ export class BinaryReader {
      */
     read(length = -1) {
         if (length === -1) {
-            length = this.stream.length - this.offset
+            length = this.stream.length - this.offset;
         }
         const result = this.stream.slice(this.offset, this.offset + length);
         this.offset += length;
         if (result.length !== length) {
             throw Error(
-                `No more data left to read (need ${length}, got ${result.length}: ${result}); last read ${this._last}`,
-            )
+                `No more data left to read (need ${length}, got ${result.length}: ${result}); last read ${this._last}`
+            );
         }
         this._last = result;
-        return result
+        return result;
     }
 
     /**
@@ -106,7 +105,7 @@ export class BinaryReader {
      * @returns {Buffer}
      */
     getBuffer() {
-        return this.stream
+        return this.stream;
     }
 
     // endregion
@@ -122,20 +121,23 @@ export class BinaryReader {
         let padding;
         let length;
         if (firstByte === 254) {
-            length = this.readByte() | (this.readByte() << 8) | (this.readByte() << 16);
-            padding = length % 4
+            length =
+                this.readByte() |
+                (this.readByte() << 8) |
+                (this.readByte() << 16);
+            padding = length % 4;
         } else {
             length = firstByte;
-            padding = (length + 1) % 4
+            padding = (length + 1) % 4;
         }
         const data = this.read(length);
 
         if (padding > 0) {
             padding = 4 - padding;
-            this.read(padding)
+            this.read(padding);
         }
 
-        return data
+        return data;
     }
 
     /**
@@ -143,7 +145,7 @@ export class BinaryReader {
      * @returns {string}
      */
     tgReadString() {
-        return this.tgReadBytes().toString('utf-8')
+        return this.tgReadBytes().toString("utf-8");
     }
 
     /**
@@ -154,12 +156,12 @@ export class BinaryReader {
         const value = this.readInt(false);
         if (value === 0x997275b5) {
             // boolTrue
-            return true
+            return true;
         } else if (value === 0xbc799737) {
             // boolFalse
-            return false
+            return false;
         } else {
-            throw new Error(`Invalid boolean code ${value.toString(16)}`)
+            throw new Error(`Invalid boolean code ${value.toString(16)}`);
         }
     }
 
@@ -170,7 +172,7 @@ export class BinaryReader {
      */
     tgReadDate() {
         const value = this.readInt();
-        return new Date(value * 1000)
+        return new Date(value * 1000);
     }
 
     /**
@@ -188,18 +190,18 @@ export class BinaryReader {
             const value = constructorId;
             if (value === 0x997275b5) {
                 // boolTrue
-                return true
+                return true;
             } else if (value === 0xbc799737) {
                 // boolFalse
-                return false
+                return false;
             } else if (value === 0x1cb5c415) {
                 // Vector
                 const temp = [];
                 const length = this.readInt();
                 for (let i = 0; i < length; i++) {
-                    temp.push(this.tgReadObject())
+                    temp.push(this.tgReadObject());
                 }
-                return temp
+                return temp;
             }
 
             clazz = coreObjects.get(constructorId);
@@ -210,10 +212,10 @@ export class BinaryReader {
                 const pos = this.tellPosition();
                 const error = new TypeNotFoundError(constructorId, this.read());
                 this.setPosition(pos);
-                throw error
+                throw error;
             }
         }
-        return clazz.fromReader(this)
+        return clazz.fromReader(this);
     }
 
     /**
@@ -222,19 +224,18 @@ export class BinaryReader {
      */
     tgReadVector() {
         if (this.readInt(false) !== 0x1cb5c415) {
-            throw new Error('Invalid constructor code, vector was expected')
+            throw new Error("Invalid constructor code, vector was expected");
         }
         const count = this.readInt();
         const temp = [];
         for (let i = 0; i < count; i++) {
-            temp.push(this.tgReadObject())
+            temp.push(this.tgReadObject());
         }
-        return temp
+        return temp;
     }
 
     // endregion
 
-
     // region Position related
 
     /**
@@ -242,7 +243,7 @@ export class BinaryReader {
      * @returns {number}
      */
     tellPosition() {
-        return this.offset
+        return this.offset;
     }
 
     /**
@@ -250,7 +251,7 @@ export class BinaryReader {
      * @param position
      */
     setPosition(position: number) {
-        this.offset = position
+        this.offset = position;
     }
 
     /**
@@ -259,7 +260,7 @@ export class BinaryReader {
      * @param offset
      */
     seek(offset: number) {
-        this.offset += offset
+        this.offset += offset;
     }
 
     // endregion

+ 3 - 3
gramjs/extensions/BinaryWriter.ts

@@ -2,14 +2,14 @@ export class BinaryWriter {
     private _stream: Buffer;
 
     constructor(stream: Buffer) {
-        this._stream = stream
+        this._stream = stream;
     }
 
     write(buffer: Buffer) {
-        this._stream = Buffer.concat([this._stream, buffer])
+        this._stream = Buffer.concat([this._stream, buffer]);
     }
 
     getValue() {
-        return this._stream
+        return this._stream;
     }
 }

+ 46 - 32
gramjs/extensions/Logger.ts

@@ -1,38 +1,45 @@
-import {IS_NODE} from "../Helpers";
+import { IS_NODE } from "../Helpers";
 
 let _level: string | undefined = undefined;
 
 export class Logger {
-    static levels = ['error', 'warn', 'info', 'debug'];
+    static levels = ["error", "warn", "info", "debug"];
     private isBrowser: boolean;
-    private colors: { warn: string; debug: string; start: string; end: string; error: string; info: string };
+    private colors: {
+        warn: string;
+        debug: string;
+        start: string;
+        end: string;
+        error: string;
+        info: string;
+    };
     private messageFormat: string;
 
     constructor(level?: string) {
         if (!_level) {
-            _level = level || 'debug'
+            _level = level || "debug";
         }
         this.isBrowser = !IS_NODE;
         if (!this.isBrowser) {
             this.colors = {
-                start: '\x1b[2m',
-                warn: '\x1b[35m',
-                info: '\x1b[33m',
-                debug: '\x1b[36m',
-                error: '\x1b[31m',
-                end: '\x1b[0m',
-            }
+                start: "\x1b[2m",
+                warn: "\x1b[35m",
+                info: "\x1b[33m",
+                debug: "\x1b[36m",
+                error: "\x1b[31m",
+                end: "\x1b[0m",
+            };
         } else {
             this.colors = {
-                start: '%c',
-                warn: 'color : #ff00ff',
-                info: 'color : #ffff00',
-                debug: 'color : #00ffff',
-                error: 'color : #ff0000',
-                end: '',
-            }
+                start: "%c",
+                warn: "color : #ff00ff",
+                info: "color : #ffff00",
+                debug: "color : #00ffff",
+                error: "color : #ff0000",
+                end: "",
+            };
         }
-        this.messageFormat = '[%t] [%l] - [%m]'
+        this.messageFormat = "[%t] [%l] - [%m]";
     }
 
     /**
@@ -41,45 +48,48 @@ export class Logger {
      * @returns {boolean}
      */
     canSend(level: string) {
-        return _level ? (Logger.levels.indexOf(_level) >= Logger.levels.indexOf(level)) : false;
+        return _level
+            ? Logger.levels.indexOf(_level) >= Logger.levels.indexOf(level)
+            : false;
     }
 
     /**
      * @param message {string}
      */
     warn(message: string) {
-        this._log('warn', message, this.colors.warn)
+        this._log("warn", message, this.colors.warn);
     }
 
     /**
      * @param message {string}
      */
     info(message: string) {
-        this._log('info', message, this.colors.info)
+        this._log("info", message, this.colors.info);
     }
 
     /**
      * @param message {string}
      */
     debug(message: string) {
-        this._log('debug', message, this.colors.debug)
+        this._log("debug", message, this.colors.debug);
     }
 
     /**
      * @param message {string}
      */
     error(message: string) {
-        this._log('error', message, this.colors.error)
+        this._log("error", message, this.colors.error);
     }
 
     format(message: string, level: string) {
-        return this.messageFormat.replace('%t', new Date().toISOString())
-            .replace('%l', level.toUpperCase())
-            .replace('%m', message)
+        return this.messageFormat
+            .replace("%t", new Date().toISOString())
+            .replace("%l", level.toUpperCase())
+            .replace("%m", message);
     }
 
     static setLevel(level: string) {
-        _level = level
+        _level = level;
     }
 
     /**
@@ -89,16 +99,20 @@ export class Logger {
      */
     _log(level: string, message: string, color: string) {
         if (!_level) {
-            return
+            return;
         }
         if (this.canSend(level)) {
             if (!this.isBrowser) {
-                console.log(color + this.format(message, level) + this.colors.end)
+                console.log(
+                    color + this.format(message, level) + this.colors.end
+                );
             } else {
-                console.log(this.colors.start + this.format(message, level), color)
+                console.log(
+                    this.colors.start + this.format(message, level),
+                    color
+                );
             }
         } else {
-
         }
     }
 }

+ 45 - 31
gramjs/extensions/MessagePacker.ts

@@ -1,8 +1,8 @@
-import {MessageContainer} from '../tl/core';
-import {TLMessage} from '../tl/core';
-import {BinaryWriter} from './BinaryWriter';
-import type{MTProtoState} from "../network/MTProtoState";
-import type{RequestState} from "../network/RequestState";
+import { MessageContainer } from "../tl/core";
+import { TLMessage } from "../tl/core";
+import { BinaryWriter } from "./BinaryWriter";
+import type { MTProtoState } from "../network/MTProtoState";
+import type { RequestState } from "../network/RequestState";
 
 export class MessagePacker {
     private _state: any;
@@ -14,14 +14,14 @@ export class MessagePacker {
     constructor(state: MTProtoState, logger: any) {
         this._state = state;
         this._queue = [];
-        this._ready = new Promise((resolve => {
-            this.setReady = resolve
-        }));
-        this._log = logger
+        this._ready = new Promise((resolve) => {
+            this.setReady = resolve;
+        });
+        this._log = logger;
     }
 
     values() {
-        return this._queue
+        return this._queue;
     }
 
     append(state: RequestState) {
@@ -33,7 +33,7 @@ export class MessagePacker {
 
     extend(states: RequestState[]) {
         for (const state of states) {
-            this._queue.push(state)
+            this._queue.push(state);
         }
         if (this.setReady) {
             this.setReady(true);
@@ -41,16 +41,15 @@ export class MessagePacker {
     }
 
     async get() {
-
         if (!this._queue.length) {
-            this._ready = new Promise((resolve => {
-                this.setReady = resolve
-            }));
-            await this._ready
+            this._ready = new Promise((resolve) => {
+                this.setReady = resolve;
+            });
+            await this._ready;
         }
         if (!this._queue[this._queue.length - 1]) {
             this._queue = [];
-            return
+            return;
         }
         let data;
         let buffer = new BinaryWriter(Buffer.alloc(0));
@@ -58,33 +57,46 @@ export class MessagePacker {
         const batch = [];
         let size = 0;
 
-        while (this._queue.length && batch.length <= MessageContainer.MAXIMUM_LENGTH) {
+        while (
+            this._queue.length &&
+            batch.length <= MessageContainer.MAXIMUM_LENGTH
+        ) {
             const state = this._queue.shift();
             size += state.data.length + TLMessage.SIZE_OVERHEAD;
             if (size <= MessageContainer.MAXIMUM_SIZE) {
                 let afterId;
                 if (state.after) {
-                    afterId = state.after.msgId
+                    afterId = state.after.msgId;
                 }
                 state.msgId = await this._state.writeDataAsMessage(
-                    buffer, state.data, state.request.classType === 'request',
-                    afterId,
+                    buffer,
+                    state.data,
+                    state.request.classType === "request",
+                    afterId
+                );
+                this._log.debug(
+                    `Assigned msgId = ${state.msgId} to ${
+                        state.request.className ||
+                        state.request.constructor.name
+                    }`
                 );
-                this._log.debug(`Assigned msgId = ${state.msgId} to ${state.request.className || state.request.constructor.name}`);
                 batch.push(state);
-                continue
+                continue;
             }
             if (batch.length) {
                 this._queue.unshift(state);
-                break
+                break;
             }
-            this._log.warn(`Message payload for ${state.request.className || state.request.constructor.name} is too long ${state.data.length} and cannot be sent`);
-            state.promise.reject('Request Payload is too big');
+            this._log.warn(
+                `Message payload for ${
+                    state.request.className || state.request.constructor.name
+                } is too long ${state.data.length} and cannot be sent`
+            );
+            state.promise.reject("Request Payload is too big");
             size = 0;
-
         }
         if (!batch.length) {
-            return null
+            return null;
         }
         if (batch.length > 1) {
             const b = Buffer.alloc(8);
@@ -93,14 +105,16 @@ export class MessagePacker {
             data = Buffer.concat([b, buffer.getValue()]);
             buffer = new BinaryWriter(Buffer.alloc(0));
             const containerId = await this._state.writeDataAsMessage(
-                buffer, data, false,
+                buffer,
+                data,
+                false
             );
             for (const s of batch) {
-                s.containerId = containerId
+                s.containerId = containerId;
             }
         }
 
         data = buffer.getValue();
-        return {batch, data}
+        return { batch, data };
     }
 }

+ 29 - 32
gramjs/extensions/PromisedNetSockets.ts

@@ -1,9 +1,9 @@
-import {Socket} from 'net';
-import {Mutex} from 'async-mutex';
+import { Socket } from "net";
+import { Mutex } from "async-mutex";
 
 const mutex = new Mutex();
 
-const closeError = new Error('NetSocket was closed');
+const closeError = new Error("NetSocket was closed");
 
 export class PromisedNetSockets {
     private client?: Socket;
@@ -14,7 +14,7 @@ export class PromisedNetSockets {
 
     constructor() {
         this.client = undefined;
-        this.closed = true
+        this.closed = true;
         this.stream = Buffer.alloc(0);
     }
 
@@ -25,40 +25,40 @@ export class PromisedNetSockets {
             readData = Buffer.concat([readData, thisTime]);
             number = number - thisTime.length;
             if (!number) {
-                return readData
+                return readData;
             }
         }
     }
 
     async read(number: number) {
         if (this.closed) {
-            throw closeError
+            throw closeError;
         }
         await this.canRead;
         if (this.closed) {
-            throw closeError
+            throw closeError;
         }
         const toReturn = this.stream.slice(0, number);
         this.stream = this.stream.slice(number);
         if (this.stream.length === 0) {
-            this.canRead = new Promise(resolve => {
-                this.resolveRead = resolve
-            })
+            this.canRead = new Promise((resolve) => {
+                this.resolveRead = resolve;
+            });
         }
 
-        return toReturn
+        return toReturn;
     }
 
     async readAll() {
-        if (this.closed || !await this.canRead) {
-            throw closeError
+        if (this.closed || !(await this.canRead)) {
+            throw closeError;
         }
         const toReturn = this.stream;
         this.stream = Buffer.alloc(0);
-        this.canRead = new Promise(resolve => {
-            this.resolveRead = resolve
+        this.canRead = new Promise((resolve) => {
+            this.resolveRead = resolve;
         });
-        return toReturn
+        return toReturn;
     }
 
     /**
@@ -71,40 +71,38 @@ export class PromisedNetSockets {
         this.stream = Buffer.alloc(0);
 
         this.client = new Socket();
-        this.canRead = new Promise(resolve => {
-            this.resolveRead = resolve
+        this.canRead = new Promise((resolve) => {
+            this.resolveRead = resolve;
         });
         this.closed = false;
         return new Promise((resolve, reject) => {
             if (this.client) {
                 this.client.connect(port, ip, () => {
                     this.receive();
-                    resolve(this)
+                    resolve(this);
                 });
-                this.client.on('error', reject);
-                this.client.on('close', () => {
+                this.client.on("error", reject);
+                this.client.on("close", () => {
                     if (this.client && this.client.destroyed) {
                         if (this.resolveRead) {
                             this.resolveRead(false);
                         }
-                        this.closed = true
+                        this.closed = true;
                     }
-                })
+                });
             }
-
-        })
+        });
     }
 
     write(data: Buffer) {
         if (this.closed) {
-            throw closeError
+            throw closeError;
         }
         if (this.client) {
-            this.client.write(data)
+            this.client.write(data);
         }
     }
 
-
     async close() {
         if (this.client) {
             await this.client.destroy();
@@ -115,20 +113,19 @@ export class PromisedNetSockets {
 
     async receive() {
         if (this.client) {
-            this.client.on('data', async message => {
+            this.client.on("data", async (message) => {
                 const release = await mutex.acquire();
                 try {
                     let data;
                     //CONTEST BROWSER
                     this.stream = Buffer.concat([this.stream, message]);
                     if (this.resolveRead) {
-                        this.resolveRead(true)
+                        this.resolveRead(true);
                     }
                 } finally {
-                    release()
+                    release();
                 }
             });
         }
-
     }
 }

+ 40 - 43
gramjs/extensions/PromisedWebSockets.ts

@@ -1,9 +1,9 @@
-import {w3cwebsocket} from 'websocket';
-import {Mutex} from 'async-mutex';
+import { w3cwebsocket } from "websocket";
+import { Mutex } from "async-mutex";
 
 const mutex = new Mutex();
 
-const closeError = new Error('WebSocket was closed');
+const closeError = new Error("WebSocket was closed");
 
 export class PromisedWebSockets {
     private closed: boolean;
@@ -17,7 +17,7 @@ export class PromisedWebSockets {
         this.client = undefined;
         this.stream = Buffer.alloc(0);
 
-        this.closed = true
+        this.closed = true;
     }
 
     async readExactly(number: number) {
@@ -27,123 +27,120 @@ export class PromisedWebSockets {
             readData = Buffer.concat([readData, thisTime]);
             number = number - thisTime.length;
             if (!number) {
-                return readData
+                return readData;
             }
         }
     }
 
     async read(number: number) {
         if (this.closed) {
-            throw closeError
+            throw closeError;
         }
         await this.canRead;
         if (this.closed) {
-            throw closeError
+            throw closeError;
         }
         const toReturn = this.stream.slice(0, number);
         this.stream = this.stream.slice(number);
         if (this.stream.length === 0) {
-            this.canRead = new Promise(resolve => {
-                this.resolveRead = resolve
-            })
+            this.canRead = new Promise((resolve) => {
+                this.resolveRead = resolve;
+            });
         }
 
-        return toReturn
+        return toReturn;
     }
 
     async readAll() {
-        if (this.closed || !await this.canRead) {
-            throw closeError
+        if (this.closed || !(await this.canRead)) {
+            throw closeError;
         }
         const toReturn = this.stream;
         this.stream = Buffer.alloc(0);
-        this.canRead = new Promise(resolve => {
-            this.resolveRead = resolve
+        this.canRead = new Promise((resolve) => {
+            this.resolveRead = resolve;
         });
-        return toReturn
+        return toReturn;
     }
 
     getWebSocketLink(ip: string, port: number) {
         if (port === 443) {
-            return `wss://${ip}:${port}/apiws`
+            return `wss://${ip}:${port}/apiws`;
         } else {
-            return `ws://${ip}:${port}/apiws`
+            return `ws://${ip}:${port}/apiws`;
         }
     }
 
     async connect(port: number, ip: string) {
         this.stream = Buffer.alloc(0);
-        this.canRead = new Promise(resolve => {
-            this.resolveRead = resolve
+        this.canRead = new Promise((resolve) => {
+            this.resolveRead = resolve;
         });
         this.closed = false;
         this.website = this.getWebSocketLink(ip, port);
-        this.client = new w3cwebsocket(this.website, 'binary');
+        this.client = new w3cwebsocket(this.website, "binary");
         return new Promise((resolve, reject) => {
             if (this.client) {
-
-
                 this.client.onopen = () => {
                     this.receive();
-                    resolve(this)
+                    resolve(this);
                 };
                 this.client.onerror = (error: any) => {
-                    reject(error)
+                    reject(error);
                 };
                 this.client.onclose = () => {
                     if (this.resolveRead) {
                         this.resolveRead(false);
                     }
-                    this.closed = true
+                    this.closed = true;
                 };
                 //CONTEST
-                if (typeof window !== 'undefined') {
-                    window.addEventListener('offline', async () => {
+                if (typeof window !== "undefined") {
+                    window.addEventListener("offline", async () => {
                         await this.close();
                         if (this.resolveRead) {
-                            this.resolveRead(false)
+                            this.resolveRead(false);
                         }
-                    })
+                    });
                 }
             }
-        })
+        });
     }
 
     write(data: Buffer) {
         if (this.closed) {
-            throw closeError
+            throw closeError;
         }
-        if(this.client){
-            this.client.send(data)
+        if (this.client) {
+            this.client.send(data);
         }
     }
 
     async close() {
-        if(this.client) {
-
+        if (this.client) {
             await this.client.close();
         }
-        this.closed = true
+        this.closed = true;
     }
 
     async receive() {
         if (this.client) {
-
             this.client.onmessage = async (message: any) => {
                 const release = await mutex.acquire();
                 try {
                     let data;
                     //CONTEST BROWSER
-                    data = Buffer.from(await new Response(message.data).arrayBuffer());
+                    data = Buffer.from(
+                        await new Response(message.data).arrayBuffer()
+                    );
                     this.stream = Buffer.concat([this.stream, data]);
                     if (this.resolveRead) {
-                        this.resolveRead(true)
+                        this.resolveRead(true);
                     }
                 } finally {
-                    release()
+                    release();
                 }
-            }
+            };
         }
     }
 }
-

+ 71 - 66
gramjs/extensions/html.ts

@@ -1,7 +1,7 @@
-import {Parser} from "htmlparser2";
-import {Handler} from "htmlparser2/lib/Parser";
-import {Api} from "../tl";
-import {helpers} from "../index";
+import { Parser } from "htmlparser2";
+import { Handler } from "htmlparser2/lib/Parser";
+import { Api } from "../tl";
+import { helpers } from "../index";
 
 class HTMLToTelegramParser implements Handler {
     text: string;
@@ -11,16 +11,19 @@ class HTMLToTelegramParser implements Handler {
     private _openTagsMeta: (string | undefined)[];
 
     constructor() {
-        this.text = '';
+        this.text = "";
         this.entities = [];
         this._buildingEntities = new Map<string, Api.TypeMessageEntity>();
         this._openTags = [];
         this._openTagsMeta = [];
     }
 
-    onopentag(name: string, attributes: {
-        [s: string]: string;
-    }) {
+    onopentag(
+        name: string,
+        attributes: {
+            [s: string]: string;
+        }
+    ) {
         /*
          * This fires when a new tag is opened.
          *
@@ -42,10 +45,13 @@ class HTMLToTelegramParser implements Handler {
         } else if (name == "blockquote") {
             EntityType = Api.MessageEntityBlockquote;
         } else if (name == "code") {
-            const pre = this._buildingEntities.get('pre');
+            const pre = this._buildingEntities.get("pre");
             if (pre && pre instanceof Api.MessageEntityPre) {
                 try {
-                    pre.language = attributes.class.slice('language-'.length, attributes.class.length);
+                    pre.language = attributes.class.slice(
+                        "language-".length,
+                        attributes.class.length
+                    );
                 } catch (e) {
                     // no language block
                 }
@@ -64,7 +70,6 @@ class HTMLToTelegramParser implements Handler {
                 url = url.slice("mailto:".length, url.length);
                 EntityType = Api.MessageEntityEmail;
             } else {
-
                 EntityType = Api.MessageEntityTextUrl;
                 args["url"] = url;
                 url = undefined;
@@ -74,27 +79,29 @@ class HTMLToTelegramParser implements Handler {
         }
 
         if (EntityType && !this._buildingEntities.has(name)) {
-            this._buildingEntities.set(name, new EntityType({
-                offset: this.text.length,
-                length: 0,
-                ...args
-            }));
+            this._buildingEntities.set(
+                name,
+                new EntityType({
+                    offset: this.text.length,
+                    length: 0,
+                    ...args,
+                })
+            );
         }
-
     }
 
     ontext(text: string) {
         const previousTag = this._openTags.length > 0 ? this._openTags[0] : "";
         if (previousTag == "a") {
-            const url = this._openTagsMeta[0]
+            const url = this._openTagsMeta[0];
             if (url) {
-                text = url
+                text = url;
             }
         }
         for (let [tag, entity] of this._buildingEntities) {
             entity.length += text.length;
         }
-        this.text += text
+        this.text += text;
     }
 
     onclosetag(tagname: string) {
@@ -104,60 +111,55 @@ class HTMLToTelegramParser implements Handler {
         if (entity) {
             this._buildingEntities.delete(tagname);
             this.entities.push(entity);
-
         }
     }
 
-    onattribute(name: string, value: string, quote?: string | undefined | null): void {
-    }
+    onattribute(
+        name: string,
+        value: string,
+        quote?: string | undefined | null
+    ): void {}
 
-    oncdataend(): void {
-    }
+    oncdataend(): void {}
 
-    oncdatastart(): void {
-    }
+    oncdatastart(): void {}
 
-    oncomment(data: string): void {
-    }
+    oncomment(data: string): void {}
 
-    oncommentend(): void {
-    }
+    oncommentend(): void {}
 
-    onend(): void {
-    }
+    onend(): void {}
 
-    onerror(error: Error): void {
-    }
+    onerror(error: Error): void {}
 
-    onopentagname(name: string): void {
-    }
+    onopentagname(name: string): void {}
 
-    onparserinit(parser: Parser): void {
-    }
+    onparserinit(parser: Parser): void {}
 
-    onprocessinginstruction(name: string, data: string): void {
-    }
-
-    onreset(): void {
-    }
+    onprocessinginstruction(name: string, data: string): void {}
 
+    onreset(): void {}
 }
 
 export class HTMLParser {
     static parse(html: string): [string, Api.TypeMessageEntity[]] {
         if (!html) {
-            return [html, []]
+            return [html, []];
         }
         const handler = new HTMLToTelegramParser();
         const parser = new Parser(handler);
         parser.write(html);
         parser.end();
-        const text = helpers.stripText(handler.text,handler.entities);
+        const text = helpers.stripText(handler.text, handler.entities);
         return [text, handler.entities];
-
     }
 
-    static unparse(text: string, entities: Api.TypeMessageEntity[] | undefined, _offset: number = 0, _length?: number): string {
+    static unparse(
+        text: string,
+        entities: Api.TypeMessageEntity[] | undefined,
+        _offset: number = 0,
+        _length?: number
+    ): string {
         if (!text || !entities || !entities.length) {
             return text;
         }
@@ -175,46 +177,50 @@ export class HTMLParser {
             if (relativeOffset > lastOffset) {
                 html.push(text.slice(lastOffset, relativeOffset));
             } else if (relativeOffset < lastOffset) {
-                continue
+                continue;
             }
             let skipEntity = false;
             let length = entity.length;
-            let entityText = this.unparse(text.slice(relativeOffset, relativeOffset + length),
+            let entityText = this.unparse(
+                text.slice(relativeOffset, relativeOffset + length),
                 entities.slice(i + 1, entities.length),
-                entity.offset, length)
+                entity.offset,
+                length
+            );
             if (entity instanceof Api.MessageEntityBold) {
-                html.push(`<strong>${entityText}</strong>`)
+                html.push(`<strong>${entityText}</strong>`);
             } else if (entity instanceof Api.MessageEntityItalic) {
-                html.push(`<em>${entityText}</em>`)
+                html.push(`<em>${entityText}</em>`);
             } else if (entity instanceof Api.MessageEntityBold) {
-                html.push(`<strong>${entityText}</strong>`)
+                html.push(`<strong>${entityText}</strong>`);
             } else if (entity instanceof Api.MessageEntityCode) {
-                html.push(`<code>${entityText}</code>`)
+                html.push(`<code>${entityText}</code>`);
             } else if (entity instanceof Api.MessageEntityUnderline) {
-                html.push(`<u>${entityText}</u>`)
+                html.push(`<u>${entityText}</u>`);
             } else if (entity instanceof Api.MessageEntityStrike) {
-                html.push(`<del>${entityText}</del>`)
+                html.push(`<del>${entityText}</del>`);
             } else if (entity instanceof Api.MessageEntityBlockquote) {
-                html.push(`<blockquote>${entityText}</blockquote>`)
+                html.push(`<blockquote>${entityText}</blockquote>`);
             } else if (entity instanceof Api.MessageEntityPre) {
                 if (entity.language) {
                     html.push(`<pre>
 <code class="language-${entity.language}">
     ${entityText}
 </code>
-</pre>`)
+</pre>`);
                 } else {
-                    html.push(`<pre></pre><code>${entityText}</code><pre>`)
-
+                    html.push(`<pre></pre><code>${entityText}</code><pre>`);
                 }
             } else if (entity instanceof Api.MessageEntityEmail) {
-                html.push(`<a href="mailto:${entityText}">${entityText}</a>`)
+                html.push(`<a href="mailto:${entityText}">${entityText}</a>`);
             } else if (entity instanceof Api.MessageEntityUrl) {
-                html.push(`<a href="${entityText}">${entityText}</a>`)
+                html.push(`<a href="${entityText}">${entityText}</a>`);
             } else if (entity instanceof Api.MessageEntityTextUrl) {
-                html.push(`<a href="${entity.url}">${entityText}</a>`)
+                html.push(`<a href="${entity.url}">${entityText}</a>`);
             } else if (entity instanceof Api.MessageEntityMentionName) {
-                html.push(`<a href="tg://user?id=${entity.userId}">${entityText}</a>`)
+                html.push(
+                    `<a href="tg://user?id=${entity.userId}">${entityText}</a>`
+                );
             } else {
                 skipEntity = true;
             }
@@ -224,4 +230,3 @@ export class HTMLParser {
         return html.join("");
     }
 }
-

+ 7 - 7
gramjs/extensions/index.ts

@@ -1,7 +1,7 @@
-export { Logger } from './Logger'
-export { BinaryWriter } from './BinaryWriter'
-export { BinaryReader } from './BinaryReader'
-export { PromisedWebSockets } from './PromisedWebSockets'
-export { PromisedNetSockets } from './PromisedNetSockets'
-export { MessagePacker } from './MessagePacker'
-export { AsyncQueue } from './AsyncQueue'
+export { Logger } from "./Logger";
+export { BinaryWriter } from "./BinaryWriter";
+export { BinaryReader } from "./BinaryReader";
+export { PromisedWebSockets } from "./PromisedWebSockets";
+export { PromisedNetSockets } from "./PromisedNetSockets";
+export { MessagePacker } from "./MessagePacker";
+export { AsyncQueue } from "./AsyncQueue";

+ 12 - 13
gramjs/extensions/markdown.ts

@@ -1,9 +1,8 @@
-import {Api} from "../tl";
-import type {ValueOf} from "../define";
-import {DEFAULT_DELIMITERS, messageEntities} from "../client/messageParse";
+import { Api } from "../tl";
+import type { ValueOf } from "../define";
+import { DEFAULT_DELIMITERS, messageEntities } from "../client/messageParse";
 
 export class MarkdownParser {
-
     // TODO maybe there is a better way :shrug:
     static parse(message: string): [string, Api.TypeMessageEntity[]] {
         let i = 0;
@@ -22,7 +21,6 @@ export class MarkdownParser {
                     foundIndex = index;
                     foundDelim = key;
                 }
-
             }
 
             if (foundIndex === -1 || foundDelim == undefined) {
@@ -32,22 +30,25 @@ export class MarkdownParser {
                 tempEntities[foundDelim] = new DEFAULT_DELIMITERS[foundDelim]({
                     offset: foundIndex,
                     length: -1,
-                    language: ""
+                    language: "",
                 });
                 keys[foundDelim] = true;
             } else {
                 keys[foundDelim] = false;
-                tempEntities[foundDelim].length = foundIndex - tempEntities[foundDelim].offset;
-                entities.push(tempEntities[foundDelim])
+                tempEntities[foundDelim].length =
+                    foundIndex - tempEntities[foundDelim].offset;
+                entities.push(tempEntities[foundDelim]);
             }
             message = message.replace(foundDelim, "");
             i = foundIndex;
-
         }
         return [message, entities];
     }
 
-    static unparse(text: string, entities: Api.TypeMessageEntity[] | undefined) {
+    static unparse(
+        text: string,
+        entities: Api.TypeMessageEntity[] | undefined
+    ) {
         const delimiters = DEFAULT_DELIMITERS;
         if (!text || !entities) {
             return text;
@@ -55,11 +56,10 @@ export class MarkdownParser {
         let insertAt: [number, string][] = [];
 
         const tempDelimiters: Map<string, string> = new Map();
-        Object.keys(delimiters).forEach(key => {
+        Object.keys(delimiters).forEach((key) => {
             tempDelimiters.set(delimiters[key].className, key);
         });
         for (const entity of entities) {
-
             const s = entity.offset;
             const e = entity.offset + entity.length;
             const delimiter = tempDelimiters.get(entity.className);
@@ -77,5 +77,4 @@ export class MarkdownParser {
         }
         return text;
     }
-
 }

+ 10 - 10
gramjs/index.d.ts

@@ -1,10 +1,10 @@
-export  * 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';
+export * 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";

+ 10 - 16
gramjs/index.ts

@@ -1,18 +1,12 @@
-export {Api} from './tl';
-export {TelegramClient} from './client/TelegramClient';
-export {Connection} from './network';
-export {version} from './Version';
+export { Api } from "./tl";
+export { TelegramClient } from "./client/TelegramClient";
+export { Connection } from "./network";
+export { version } from "./Version";
 //export {events} from './events';
-import * as utils from './Utils';
-import * as errors from './errors';
-import * as sessions from './sessions';
-import * as extensions from './extensions';
-import * as helpers from './Helpers';
+import * as utils from "./Utils";
+import * as errors from "./errors";
+import * as sessions from "./sessions";
+import * as extensions from "./extensions";
+import * as helpers from "./Helpers";
 
-export {
-    utils,
-    errors,
-    sessions,
-    extensions,
-    helpers
-}
+export { utils, errors, sessions, extensions, helpers };

+ 85 - 53
gramjs/network/Authenticator.ts

@@ -4,36 +4,36 @@
  * @param log
  * @returns {Promise<{authKey: *, timeOffset: *}>}
  */
-import {Api} from "../tl";
-import {SecurityError} from "../errors";
-import {Factorizator} from "../crypto/Factorizator";
-import {IGE} from "../crypto/IGE";
-import {BinaryReader} from "../extensions";
-import {AuthKey} from "../crypto/AuthKey";
-import {helpers} from "../";
-import {encrypt} from "../crypto/RSA";
-import bigInt from 'big-integer';
-import type {MTProtoPlainSender} from "./MTProtoPlainSender";
+import { Api } from "../tl";
+import { SecurityError } from "../errors";
+import { Factorizator } from "../crypto/Factorizator";
+import { IGE } from "../crypto/IGE";
+import { BinaryReader } from "../extensions";
+import { AuthKey } from "../crypto/AuthKey";
+import { helpers } from "../";
+import { encrypt } from "../crypto/RSA";
+import bigInt from "big-integer";
+import type { MTProtoPlainSender } from "./MTProtoPlainSender";
 
 export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
     // Step 1 sending: PQ Request, endianness doesn't matter since it's random
     let bytes = helpers.generateRandomBytes(16);
 
     const nonce = helpers.readBigIntFromBuffer(bytes, false, true);
-    const resPQ = await sender.send(new Api.ReqPqMulti({nonce: nonce}));
-    log.debug('Starting authKey generation step 1');
+    const resPQ = await sender.send(new Api.ReqPqMulti({ nonce: nonce }));
+    log.debug("Starting authKey generation step 1");
 
     if (!(resPQ instanceof Api.ResPQ)) {
-        throw new Error(`Step 1 answer was ${resPQ}`)
+        throw new Error(`Step 1 answer was ${resPQ}`);
     }
     if (resPQ.nonce.neq(nonce)) {
-        throw new SecurityError('Step 1 invalid nonce from server')
+        throw new SecurityError("Step 1 invalid nonce from server");
     }
     const pq = helpers.readBigIntFromBuffer(resPQ.pq, false, true);
-    log.debug('Finished authKey generation step 1');
-    log.debug('Starting authKey generation step 2');
+    log.debug("Finished authKey generation step 1");
+    log.debug("Starting authKey generation step 2");
     // Step 2 sending: DH Exchange
-    let {p, q} = Factorizator.factorize(pq);
+    let { p, q } = Factorizator.factorize(pq);
 
     const pBuffer = helpers.getByteArray(p);
     const qBuffer = helpers.getByteArray(q);
@@ -56,11 +56,13 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
         cipherText = await encrypt(fingerprint, pqInnerData.getBytes());
         if (cipherText !== undefined) {
             targetFingerprint = fingerprint;
-            break
+            break;
         }
     }
     if (cipherText === undefined) {
-        throw new SecurityError('Step 2 could not find a valid key for fingerprints')
+        throw new SecurityError(
+            "Step 2 could not find a valid key for fingerprints"
+        );
     }
     const serverDhParams = await sender.send(
         new Api.ReqDHParams({
@@ -70,38 +72,48 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
             q: qBuffer,
             publicKeyFingerprint: targetFingerprint,
             encryptedData: cipherText,
-        }),
+        })
     );
 
-    if (!(serverDhParams instanceof Api.ServerDHParamsOk || serverDhParams instanceof Api.ServerDHParamsFail)) {
-        throw new Error(`Step 2.1 answer was ${serverDhParams}`)
+    if (
+        !(
+            serverDhParams instanceof Api.ServerDHParamsOk ||
+            serverDhParams instanceof Api.ServerDHParamsFail
+        )
+    ) {
+        throw new Error(`Step 2.1 answer was ${serverDhParams}`);
     }
     if (serverDhParams.nonce.neq(resPQ.nonce)) {
-        throw new SecurityError('Step 2 invalid nonce from server')
+        throw new SecurityError("Step 2 invalid nonce from server");
     }
 
     if (serverDhParams.serverNonce.neq(resPQ.serverNonce)) {
-        throw new SecurityError('Step 2 invalid server nonce from server')
+        throw new SecurityError("Step 2 invalid server nonce from server");
     }
 
     if (serverDhParams instanceof Api.ServerDHParamsFail) {
-        const sh = await helpers.sha1(helpers.toSignedLittleBuffer(newNonce, 32).slice(4, 20));
+        const sh = await helpers.sha1(
+            helpers.toSignedLittleBuffer(newNonce, 32).slice(4, 20)
+        );
         const nnh = helpers.readBigIntFromBuffer(sh, true, true);
         if (serverDhParams.newNonceHash.neq(nnh)) {
-            throw new SecurityError('Step 2 invalid DH fail nonce from server')
+            throw new SecurityError("Step 2 invalid DH fail nonce from server");
         }
     }
     if (!(serverDhParams instanceof Api.ServerDHParamsOk)) {
-        throw new Error(`Step 2.2 answer was ${serverDhParams}`)
+        throw new Error(`Step 2.2 answer was ${serverDhParams}`);
     }
-    log.debug('Finished authKey generation step 2');
-    log.debug('Starting authKey generation step 3');
+    log.debug("Finished authKey generation step 2");
+    log.debug("Starting authKey generation step 3");
 
     // Step 3 sending: Complete DH Exchange
-    const {key, iv} = await helpers.generateKeyDataFromNonce(resPQ.serverNonce, newNonce);
+    const { key, iv } = await helpers.generateKeyDataFromNonce(
+        resPQ.serverNonce,
+        newNonce
+    );
     if (serverDhParams.encryptedAnswer.length % 16 !== 0) {
         // See PR#453
-        throw new SecurityError('Step 3 AES block size mismatch')
+        throw new SecurityError("Step 3 AES block size mismatch");
     }
     const ige = new IGE(key, iv);
     const plainTextAnswer = ige.decryptIge(serverDhParams.encryptedAnswer);
@@ -109,19 +121,30 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
     reader.read(20); // hash sum
     const serverDhInner = reader.tgReadObject();
     if (!(serverDhInner instanceof Api.ServerDHInnerData)) {
-        throw new Error(`Step 3 answer was ${serverDhInner}`)
+        throw new Error(`Step 3 answer was ${serverDhInner}`);
     }
 
     if (serverDhInner.nonce.neq(resPQ.nonce)) {
-        throw new SecurityError('Step 3 Invalid nonce in encrypted answer')
+        throw new SecurityError("Step 3 Invalid nonce in encrypted answer");
     }
     if (serverDhInner.serverNonce.neq(resPQ.serverNonce)) {
-        throw new SecurityError('Step 3 Invalid server nonce in encrypted answer')
-    }
-    const dhPrime = helpers.readBigIntFromBuffer(serverDhInner.dhPrime, false, false);
+        throw new SecurityError(
+            "Step 3 Invalid server nonce in encrypted answer"
+        );
+    }
+    const dhPrime = helpers.readBigIntFromBuffer(
+        serverDhInner.dhPrime,
+        false,
+        false
+    );
     const ga = helpers.readBigIntFromBuffer(serverDhInner.gA, false, false);
-    const timeOffset = serverDhInner.serverTime - Math.floor(new Date().getTime() / 1000);
-    const b = helpers.readBigIntFromBuffer(helpers.generateRandomBytes(256), false, false);
+    const timeOffset =
+        serverDhInner.serverTime - Math.floor(new Date().getTime() / 1000);
+    const b = helpers.readBigIntFromBuffer(
+        helpers.generateRandomBytes(256),
+        false,
+        false
+    );
     const gb = helpers.modExp(bigInt(serverDhInner.g), b, dhPrime);
     const gab = helpers.modExp(ga, b, dhPrime);
 
@@ -133,7 +156,10 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
         gB: helpers.getByteArray(gb, false),
     }).getBytes();
 
-    const clientDdhInnerHashed = Buffer.concat([await helpers.sha1(clientDhInner), clientDhInner]);
+    const clientDdhInnerHashed = Buffer.concat([
+        await helpers.sha1(clientDhInner),
+        clientDhInner,
+    ]);
     // Encryption
 
     const clientDhEncrypted = ige.encryptIge(clientDdhInnerHashed);
@@ -142,20 +168,28 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
             nonce: resPQ.nonce,
             serverNonce: resPQ.serverNonce,
             encryptedData: clientDhEncrypted,
-        }),
+        })
     );
     const nonceTypes = [Api.DhGenOk, Api.DhGenRetry, Api.DhGenFail];
     // TS being weird again.
-    const nonceTypesString = ['DhGenOk', 'DhGenRetry', 'DhGenFail'];
-    if (!(dhGen instanceof nonceTypes[0] || dhGen instanceof nonceTypes[1] || dhGen instanceof nonceTypes[2])) {
-        throw new Error(`Step 3.1 answer was ${dhGen}`)
-    }
-    const {name} = dhGen.constructor;
+    const nonceTypesString = ["DhGenOk", "DhGenRetry", "DhGenFail"];
+    if (
+        !(
+            dhGen instanceof nonceTypes[0] ||
+            dhGen instanceof nonceTypes[1] ||
+            dhGen instanceof nonceTypes[2]
+        )
+    ) {
+        throw new Error(`Step 3.1 answer was ${dhGen}`);
+    }
+    const { name } = dhGen.constructor;
     if (dhGen.nonce.neq(resPQ.nonce)) {
-        throw new SecurityError(`Step 3 invalid ${name} nonce from server`)
+        throw new SecurityError(`Step 3 invalid ${name} nonce from server`);
     }
     if (dhGen.serverNonce.neq(resPQ.serverNonce)) {
-        throw new SecurityError(`Step 3 invalid ${name} server nonce from server`)
+        throw new SecurityError(
+            `Step 3 invalid ${name} server nonce from server`
+        );
     }
     const authKey = new AuthKey();
     await authKey.setKey(helpers.getByteArray(gab));
@@ -167,15 +201,13 @@ export async function doAuthentication(sender: MTProtoPlainSender, log: any) {
     const dhHash = dhGen[`newNonceHash${nonceNumber}`];
 
     if (dhHash.neq(newNonceHash)) {
-        throw new SecurityError('Step 3 invalid new nonce hash')
+        throw new SecurityError("Step 3 invalid new nonce hash");
     }
 
     if (!(dhGen instanceof Api.DhGenOk)) {
-        throw new Error(`Step 3.2 answer was ${dhGen}`)
+        throw new Error(`Step 3.2 answer was ${dhGen}`);
     }
-    log.debug('Finished authKey generation step 3');
+    log.debug("Finished authKey generation step 3");
 
-    return {authKey, timeOffset}
+    return { authKey, timeOffset };
 }
-
-

+ 28 - 26
gramjs/network/MTProtoPlainSender.ts

@@ -2,12 +2,12 @@
  *  This module contains the class used to communicate with Telegram's servers
  *  in plain text, when no authorization key has been created yet.
  */
-import {MTProtoState} from "./MTProtoState";
-import {Api} from "../tl";
-import {toSignedLittleBuffer} from "../Helpers";
-import {InvalidBufferError} from "../errors";
-import {BinaryReader} from "../extensions";
-import type {Connection} from "./connection";
+import { MTProtoState } from "./MTProtoState";
+import { Api } from "../tl";
+import { toSignedLittleBuffer } from "../Helpers";
+import { InvalidBufferError } from "../errors";
+import { BinaryReader } from "../extensions";
+import type { Connection } from "./connection";
 
 /**
  * MTProto Mobile Protocol plain sender (https://core.telegram.org/mtproto/description#unencrypted-messages)
@@ -23,8 +23,8 @@ export class MTProtoPlainSender {
      * @param loggers
      */
     constructor(connection: any, loggers: any) {
-        this._state = new MTProtoState(undefined, loggers)
-        this._connection = connection
+        this._state = new MTProtoState(undefined, loggers);
+        this._connection = connection;
     }
 
     /**
@@ -32,27 +32,30 @@ export class MTProtoPlainSender {
      * @param request
      */
     async send(request: Api.AnyRequest) {
-        let body = request.getBytes()
+        let body = request.getBytes();
 
-        let msgId = this._state._getNewMsgId()
-        const m = toSignedLittleBuffer(msgId, 8)
-        const b = Buffer.alloc(4)
-        b.writeInt32LE(body.length, 0)
+        let msgId = this._state._getNewMsgId();
+        const m = toSignedLittleBuffer(msgId, 8);
+        const b = Buffer.alloc(4);
+        b.writeInt32LE(body.length, 0);
 
-        const res = Buffer.concat([Buffer.concat([Buffer.alloc(8), m, b]), body])
-        await this._connection.send(res)
-        body = await this._connection.recv()
+        const res = Buffer.concat([
+            Buffer.concat([Buffer.alloc(8), m, b]),
+            body,
+        ]);
+        await this._connection.send(res);
+        body = await this._connection.recv();
         if (body.length < 8) {
-            throw new InvalidBufferError(body)
+            throw new InvalidBufferError(body);
         }
-        const reader = new BinaryReader(body)
-        const authKeyId = reader.readLong()
+        const reader = new BinaryReader(body);
+        const authKeyId = reader.readLong();
         if (authKeyId.neq(BigInt(0))) {
-            throw new Error('Bad authKeyId')
+            throw new Error("Bad authKeyId");
         }
-        msgId = reader.readLong()
+        msgId = reader.readLong();
         if (msgId.eq(BigInt(0))) {
-            throw new Error('Bad msgId')
+            throw new Error("Bad msgId");
         }
         /** ^ We should make sure that the read ``msg_id`` is greater
          * than our own ``msg_id``. However, under some circumstances
@@ -60,16 +63,15 @@ export class MTProtoPlainSender {
          * be the case, which would cause endless assertion errors.
          */
 
-        const length = reader.readInt()
+        const length = reader.readInt();
         if (length <= 0) {
-            throw new Error('Bad length')
+            throw new Error("Bad length");
         }
         /**
          * We could read length 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.
          */
-        return reader.tgReadObject()
+        return reader.tgReadObject();
     }
-
 }

+ 210 - 161
gramjs/network/MTProtoSender.ts

@@ -11,36 +11,41 @@
  * A new authorization key will be generated on connection if no other
  * key exists yet.
  */
-import {AuthKey} from "../crypto/AuthKey";
-import {MTProtoState} from "./MTProtoState";
-import {BinaryReader } from "../extensions";
-import  {MessagePacker } from "../extensions";
-import {GZIPPacked, MessageContainer, RPCResult, TLMessage} from "../tl/core";
-import {Api} from "../tl";
-import bigInt from 'big-integer'
-import {sleep} from "../Helpers";
-import {RequestState} from "./RequestState";
-import {doAuthentication} from "./Authenticator";
-import  {MTProtoPlainSender} from "./MTProtoPlainSender";
-import {BadMessageError, TypeNotFoundError,InvalidBufferError,SecurityError,RPCMessageToError} from "../errors";
-import {Connection, UpdateConnectionState} from "./";
+import { AuthKey } from "../crypto/AuthKey";
+import { MTProtoState } from "./MTProtoState";
+import { BinaryReader } from "../extensions";
+import { MessagePacker } from "../extensions";
+import { GZIPPacked, MessageContainer, RPCResult, TLMessage } from "../tl/core";
+import { Api } from "../tl";
+import bigInt from "big-integer";
+import { sleep } from "../Helpers";
+import { RequestState } from "./RequestState";
+import { doAuthentication } from "./Authenticator";
+import { MTProtoPlainSender } from "./MTProtoPlainSender";
+import {
+    BadMessageError,
+    TypeNotFoundError,
+    InvalidBufferError,
+    SecurityError,
+    RPCMessageToError,
+} from "../errors";
+import { Connection, UpdateConnectionState } from "./";
 
 interface DEFAULT_OPTIONS {
-    logger: any,
-    retries: number,
-    delay: number,
-    autoReconnect: boolean,
-    connectTimeout: any,
-    authKeyCallback: any,
-    updateCallback?: any,
-    autoReconnectCallback?: any,
-    isMainSender: boolean,
-    dcId: number,
-    senderCallback?: any,
+    logger: any;
+    retries: number;
+    delay: number;
+    autoReconnect: boolean;
+    connectTimeout: any;
+    authKeyCallback: any;
+    updateCallback?: any;
+    autoReconnectCallback?: any;
+    isMainSender: boolean;
+    dcId: number;
+    senderCallback?: any;
 }
 
 {
-
 }
 
 export class MTProtoSender {
@@ -86,7 +91,7 @@ export class MTProtoSender {
      * @param opts
      */
     constructor(authKey: undefined | AuthKey, opts: DEFAULT_OPTIONS) {
-        const args = {...MTProtoSender.DEFAULT_OPTIONS, ...opts};
+        const args = { ...MTProtoSender.DEFAULT_OPTIONS, ...opts };
         this._connection = undefined;
         this._log = args.logger;
         this._dcId = args.dcId;
@@ -152,21 +157,33 @@ export class MTProtoSender {
          */
 
         this._handlers = {
-            [RPCResult.CONSTRUCTOR_ID.toString()]: this._handleRPCResult.bind(this),
-            [MessageContainer.CONSTRUCTOR_ID.toString()]: this._handleContainer.bind(this),
-            [GZIPPacked.CONSTRUCTOR_ID.toString()]: this._handleGzipPacked.bind(this),
+            [RPCResult.CONSTRUCTOR_ID.toString()]:
+                this._handleRPCResult.bind(this),
+            [MessageContainer.CONSTRUCTOR_ID.toString()]:
+                this._handleContainer.bind(this),
+            [GZIPPacked.CONSTRUCTOR_ID.toString()]:
+                this._handleGzipPacked.bind(this),
             [Api.Pong.CONSTRUCTOR_ID.toString()]: this._handlePong.bind(this),
-            [Api.BadServerSalt.CONSTRUCTOR_ID.toString()]: this._handleBadServerSalt.bind(this),
-            [Api.BadMsgNotification.CONSTRUCTOR_ID.toString()]: this._handleBadNotification.bind(this),
-            [Api.MsgDetailedInfo.CONSTRUCTOR_ID.toString()]: this._handleDetailedInfo.bind(this),
-            [Api.MsgNewDetailedInfo.CONSTRUCTOR_ID.toString()]: this._handleNewDetailedInfo.bind(this),
-            [Api.NewSessionCreated.CONSTRUCTOR_ID.toString()]: this._handleNewSessionCreated.bind(this),
+            [Api.BadServerSalt.CONSTRUCTOR_ID.toString()]:
+                this._handleBadServerSalt.bind(this),
+            [Api.BadMsgNotification.CONSTRUCTOR_ID.toString()]:
+                this._handleBadNotification.bind(this),
+            [Api.MsgDetailedInfo.CONSTRUCTOR_ID.toString()]:
+                this._handleDetailedInfo.bind(this),
+            [Api.MsgNewDetailedInfo.CONSTRUCTOR_ID.toString()]:
+                this._handleNewDetailedInfo.bind(this),
+            [Api.NewSessionCreated.CONSTRUCTOR_ID.toString()]:
+                this._handleNewSessionCreated.bind(this),
             [Api.MsgsAck.CONSTRUCTOR_ID.toString()]: this._handleAck.bind(this),
-            [Api.FutureSalts.CONSTRUCTOR_ID.toString()]: this._handleFutureSalts.bind(this),
-            [Api.MsgsStateReq.CONSTRUCTOR_ID.toString()]: this._handleStateForgotten.bind(this),
-            [Api.MsgResendReq.CONSTRUCTOR_ID.toString()]: this._handleStateForgotten.bind(this),
-            [Api.MsgsAllInfo.CONSTRUCTOR_ID.toString()]: this._handleMsgAll.bind(this),
-        }
+            [Api.FutureSalts.CONSTRUCTOR_ID.toString()]:
+                this._handleFutureSalts.bind(this),
+            [Api.MsgsStateReq.CONSTRUCTOR_ID.toString()]:
+                this._handleStateForgotten.bind(this),
+            [Api.MsgResendReq.CONSTRUCTOR_ID.toString()]:
+                this._handleStateForgotten.bind(this),
+            [Api.MsgsAllInfo.CONSTRUCTOR_ID.toString()]:
+                this._handleMsgAll.bind(this),
+        };
     }
 
     set dcId(dcId: number) {
@@ -187,8 +204,8 @@ export class MTProtoSender {
      */
     async connect(connection: any, eventDispatch?: any) {
         if (this._userConnected) {
-            this._log.info('User is already connected!');
-            return false
+            this._log.info("User is already connected!");
+            return false;
         }
         this._connection = connection;
 
@@ -197,20 +214,22 @@ export class MTProtoSender {
         for (let attempt = 0; attempt < retries; attempt++) {
             try {
                 await this._connect();
-                break
+                break;
             } catch (e) {
                 if (attempt === 0 && eventDispatch) {
-                    eventDispatch({update: new UpdateConnectionState(-1)})
+                    eventDispatch({ update: new UpdateConnectionState(-1) });
                 }
-                this._log.error('WebSocket connection failed attempt : ' + (attempt + 1));
-                await sleep(this._delay)
+                this._log.error(
+                    "WebSocket connection failed attempt : " + (attempt + 1)
+                );
+                await sleep(this._delay);
             }
         }
-        return true
+        return true;
     }
 
     isConnected() {
-        return this._userConnected
+        return this._userConnected;
     }
 
     /**
@@ -218,8 +237,7 @@ export class MTProtoSender {
      * all pending requests, and closes the send and receive loops.
      */
     async disconnect() {
-
-        await this._disconnect()
+        await this._disconnect();
     }
 
     /**
@@ -249,7 +267,7 @@ export class MTProtoSender {
      */
     send(request: Api.AnyRequest): any {
         if (!this._userConnected) {
-            throw new Error('Cannot send requests while disconnected')
+            throw new Error("Cannot send requests while disconnected");
         }
         const state = new RequestState(request);
         this._sendQueue.append(state);
@@ -264,16 +282,17 @@ export class MTProtoSender {
      * @private
      */
     async _connect() {
-
-        this._log.info('Connecting to {0}...'.replace('{0}', this._connection!.toString()));
+        this._log.info(
+            "Connecting to {0}...".replace("{0}", this._connection!.toString())
+        );
         await this._connection!.connect();
-        this._log.debug('Connection success!');
+        this._log.debug("Connection success!");
         //process.exit(0)
         if (!this.authKey.getKey()) {
             const plain = new MTProtoPlainSender(this._connection, this._log);
-            this._log.debug('New auth_key attempt ...');
+            this._log.debug("New auth_key attempt ...");
             const res = await doAuthentication(plain, this._log);
-            this._log.debug('Generated new auth_key successfully');
+            this._log.debug("Generated new auth_key successfully");
             await this.authKey.setKey(res.authKey);
 
             this._state.timeOffset = res.timeOffset;
@@ -285,39 +304,48 @@ export class MTProtoSender {
              * switch to different data centers.
              */
             if (this._authKeyCallback) {
-                await this._authKeyCallback(this.authKey, this._dcId)
+                await this._authKeyCallback(this.authKey, this._dcId);
             }
         } else {
-            this._log.debug('Already have an auth key ...')
+            this._log.debug("Already have an auth key ...");
         }
         this._userConnected = true;
         this._reconnecting = false;
-        this._log.debug('Starting receive loop');
+        this._log.debug("Starting receive loop");
         this._recvLoopHandle = this._recvLoop();
 
-        this._log.debug('Starting send loop');
+        this._log.debug("Starting send loop");
         this._sendLoopHandle = this._sendLoop();
 
-
         // _disconnected only completes after manual disconnection
         // or errors after which the sender cannot continue such
         // as failing to reconnect or any unexpected error.
 
-        this._log.info('Connection to %s complete!'.replace('%s', this._connection!.toString()))
+        this._log.info(
+            "Connection to %s complete!".replace(
+                "%s",
+                this._connection!.toString()
+            )
+        );
     }
 
     async _disconnect(error = null) {
         if (this._connection === null) {
-            this._log.info('Not disconnecting (already have no connection)');
-            return
+            this._log.info("Not disconnecting (already have no connection)");
+            return;
         }
         if (this._updateCallback) {
-            this._updateCallback(-1)
+            this._updateCallback(-1);
         }
-        this._log.info('Disconnecting from %s...'.replace('%s', this._connection!.toString()));
+        this._log.info(
+            "Disconnecting from %s...".replace(
+                "%s",
+                this._connection!.toString()
+            )
+        );
         this._userConnected = false;
-        this._log.debug('Closing current connection...');
-        await this._connection!.disconnect()
+        this._log.debug("Closing current connection...");
+        await this._connection!.disconnect();
     }
 
     /**
@@ -332,53 +360,58 @@ export class MTProtoSender {
 
         while (this._userConnected && !this._reconnecting) {
             if (this._pendingAck.size) {
-
-                const ack = new RequestState(new Api.MsgsAck({msgIds: Array(...this._pendingAck)}));
+                const ack = new RequestState(
+                    new Api.MsgsAck({ msgIds: Array(...this._pendingAck) })
+                );
                 this._sendQueue.append(ack);
                 this._lastAcks.push(ack);
-                this._pendingAck.clear()
+                this._pendingAck.clear();
             }
-            this._log.debug('Waiting for messages to send...' + this._reconnecting);
+            this._log.debug(
+                "Waiting for messages to send..." + this._reconnecting
+            );
             // TODO Wait for the connection send queue to be empty?
             // This means that while it's not empty we can wait for
             // more messages to be added to the send queue.
             const res = await this._sendQueue.get();
             if (this._reconnecting) {
-                this._log.debug('Reconnecting. will stop loop');
-                return
+                this._log.debug("Reconnecting. will stop loop");
+                return;
             }
 
             if (!res) {
-                this._log.debug('Empty result. will stop loop');
-                continue
+                this._log.debug("Empty result. will stop loop");
+                continue;
             }
             let data = res.data;
             const batch = res.batch;
-            this._log.debug(`Encrypting ${batch.length} message(s) in ${data.length} bytes for sending`);
+            this._log.debug(
+                `Encrypting ${batch.length} message(s) in ${data.length} bytes for sending`
+            );
 
             data = await this._state.encryptMessageData(data);
 
             try {
-                await this._connection!.send(data)
+                await this._connection!.send(data);
             } catch (e) {
                 this._log.error(e);
-                this._log.info('Connection closed while sending data');
-                return
+                this._log.info("Connection closed while sending data");
+                return;
             }
             for (const state of batch) {
                 if (!Array.isArray(state)) {
-                    if (state.request.classType === 'request') {
-                        this._pendingState.set(state.msgId.toString(), state)
+                    if (state.request.classType === "request") {
+                        this._pendingState.set(state.msgId.toString(), state);
                     }
                 } else {
                     for (const s of state) {
-                        if (s.request.classType === 'request') {
-                            this._pendingState.set(s.msgId.toString(), s)
+                        if (s.request.classType === "request") {
+                            this._pendingState.set(s.msgId.toString(), s);
                         }
                     }
                 }
             }
-            this._log.debug('Encrypted messages put in a queue to be sent')
+            this._log.debug("Encrypted messages put in a queue to be sent");
         }
     }
 
@@ -388,35 +421,39 @@ export class MTProtoSender {
 
         while (this._userConnected && !this._reconnecting) {
             // this._log.debug('Receiving items from the network...');
-            this._log.debug('Receiving items from the network...');
+            this._log.debug("Receiving items from the network...");
             try {
-                body = await this._connection!.recv()
+                body = await this._connection!.recv();
             } catch (e) {
                 // this._log.info('Connection closed while receiving data');
-                this._log.warn('Connection closed while receiving data');
+                this._log.warn("Connection closed while receiving data");
                 this._startReconnect();
-                return
+                return;
             }
             try {
-                message = await this._state.decryptMessageData(body)
+                message = await this._state.decryptMessageData(body);
             } catch (e) {
                 if (e instanceof TypeNotFoundError) {
                     // Received object which we don't know how to deserialize
-                    this._log.info(`Type ${e.invalidConstructorId} not found, remaining data ${e.remaining}`);
-                    continue
+                    this._log.info(
+                        `Type ${e.invalidConstructorId} not found, remaining data ${e.remaining}`
+                    );
+                    continue;
                 } else if (e instanceof SecurityError) {
                     // A step while decoding had the incorrect data. This message
                     // should not be considered safe and it should be ignored.
-                    this._log.warn(`Security error while unpacking a received message: ${e}`);
-                    continue
+                    this._log.warn(
+                        `Security error while unpacking a received message: ${e}`
+                    );
+                    continue;
                 } else if (e instanceof InvalidBufferError) {
-                    this._log.info('Broken authorization key; resetting');
+                    this._log.info("Broken authorization key; resetting");
                     if (this._updateCallback && this._isMainSender) {
                         // 0 == broken
-                        this._updateCallback(0)
+                        this._updateCallback(0);
                     } else if (this._senderCallback && !this._isMainSender) {
                         // Deletes the current sender from the object
-                        this._senderCallback(this._dcId)
+                        this._senderCallback(this._dcId);
                     }
 
                     // We don't really need to do this if we're going to sign in again
@@ -427,20 +464,20 @@ export class MTProtoSender {
                     }*/
                     // We can disconnect at sign in
                     /* await this.disconnect()
-                    */
-                    return
+                     */
+                    return;
                 } else {
-                    this._log.error('Unhandled error while receiving data');
+                    this._log.error("Unhandled error while receiving data");
                     this._log.error(e);
                     this._startReconnect();
-                    return
+                    return;
                 }
             }
             try {
-                await this._processMessage(message)
+                await this._processMessage(message);
             } catch (e) {
-                this._log.error('Unhandled error while receiving data');
-                this._log.error(e)
+                this._log.error("Unhandled error while receiving data");
+                this._log.error(e);
             }
         }
     }
@@ -461,10 +498,10 @@ export class MTProtoSender {
         message.obj = await message.obj;
         let handler = this._handlers[message.obj.CONSTRUCTOR_ID.toString()];
         if (!handler) {
-            handler = this._handleUpdate.bind(this)
+            handler = this._handleUpdate.bind(this);
         }
 
-        await handler(message)
+        await handler(message);
     }
 
     /**
@@ -478,14 +515,14 @@ export class MTProtoSender {
         let state = this._pendingState.get(msgId.toString());
         if (state) {
             this._pendingState.delete(msgId.toString());
-            return [state]
+            return [state];
         }
 
         const toPop = [];
 
         for (const state of Object.values(this._pendingState)) {
             if (state.containerId && state.containerId.equals(msgId)) {
-                toPop.push(state.msgId)
+                toPop.push(state.msgId);
             }
         }
 
@@ -495,16 +532,16 @@ export class MTProtoSender {
                 temp.push(this._pendingState.get(x));
                 this._pendingState.delete(x);
             }
-            return temp
+            return temp;
         }
 
         for (const ack of this._lastAcks) {
             if (ack.msgId === msgId) {
-                return [ack]
+                return [ack];
             }
         }
 
-        return []
+        return [];
     }
 
     /**
@@ -519,9 +556,11 @@ export class MTProtoSender {
         const RPCResult = message.obj;
         const state = this._pendingState.get(RPCResult.reqMsgId.toString());
         if (state) {
-            this._pendingState.delete(RPCResult.reqMsgId.toString())
+            this._pendingState.delete(RPCResult.reqMsgId.toString());
         }
-        this._log.debug(`Handling RPC result for message ${RPCResult.reqMsgId}`);
+        this._log.debug(
+            `Handling RPC result for message ${RPCResult.reqMsgId}`
+        );
 
         if (!state) {
             // TODO We should not get responses to things we never sent
@@ -531,27 +570,31 @@ export class MTProtoSender {
             try {
                 const reader = new BinaryReader(RPCResult.body);
                 if (!(reader.tgReadObject() instanceof Api.upload.File)) {
-                    throw new Error('Not an upload.File')
+                    throw new Error("Not an upload.File");
                 }
             } catch (e) {
                 this._log.error(e);
                 if (e instanceof TypeNotFoundError) {
-                    this._log.info(`Received response without parent request: ${RPCResult.body}`);
-                    return
+                    this._log.info(
+                        `Received response without parent request: ${RPCResult.body}`
+                    );
+                    return;
                 } else {
-                    throw e
+                    throw e;
                 }
             }
             return;
         }
         if (RPCResult.error && state.msgId) {
             const error = RPCMessageToError(RPCResult.error, state.request);
-            this._sendQueue.append(new RequestState(new Api.MsgsAck({msgIds: [state.msgId]})));
-            state.reject(error)
+            this._sendQueue.append(
+                new RequestState(new Api.MsgsAck({ msgIds: [state.msgId] }))
+            );
+            state.reject(error);
         } else {
             const reader = new BinaryReader(RPCResult.body);
             const read = state.request.readResult(reader);
-            state.resolve(read)
+            state.resolve(read);
         }
     }
 
@@ -563,9 +606,9 @@ export class MTProtoSender {
      * @private
      */
     async _handleContainer(message: TLMessage) {
-        this._log.debug('Handling container');
+        this._log.debug("Handling container");
         for (const innerMessage of message.obj.messages) {
-            await this._processMessage(innerMessage)
+            await this._processMessage(innerMessage);
         }
     }
 
@@ -577,21 +620,23 @@ export class MTProtoSender {
      * @private
      */
     async _handleGzipPacked(message: TLMessage) {
-        this._log.debug('Handling gzipped data');
+        this._log.debug("Handling gzipped data");
         const reader = new BinaryReader(message.obj.data);
         message.obj = reader.tgReadObject();
-        await this._processMessage(message)
+        await this._processMessage(message);
     }
 
     async _handleUpdate(message: TLMessage) {
         if (message.obj.SUBCLASS_OF_ID !== 0x8af52aac) {
             // crc32(b'Updates')
-            this._log.warn(`Note: ${message.obj.className} is not an update, not dispatching it`);
-            return
+            this._log.warn(
+                `Note: ${message.obj.className} is not an update, not dispatching it`
+            );
+            return;
         }
-        this._log.debug('Handling update ' + message.obj.className);
+        this._log.debug("Handling update " + message.obj.className);
         if (this._updateCallback) {
-            this._updateCallback(message.obj)
+            this._updateCallback(message.obj);
         }
     }
 
@@ -611,7 +656,7 @@ export class MTProtoSender {
 
         // Todo Check result
         if (state) {
-            state.resolve(pong)
+            state.resolve(pong);
         }
     }
 
@@ -630,7 +675,7 @@ export class MTProtoSender {
         this._state.salt = badSalt.newServerSalt;
         const states = this._popStates(badSalt.badMsgId);
         this._sendQueue.extend(states);
-        this._log.debug(`${states.length} message(s) will be resent`)
+        this._log.debug(`${states.length} message(s) will be resent`);
     }
 
     /**
@@ -650,25 +695,28 @@ export class MTProtoSender {
             // Sent msg_id too low or too high (respectively).
             // Use the current msg_id to determine the right time offset.
             const to = this._state.updateTimeOffset(bigInt(message.msgId));
-            this._log.info(`System clock is wrong, set time offset to ${to}s`)
+            this._log.info(`System clock is wrong, set time offset to ${to}s`);
         } else if (badMsg.errorCode === 32) {
             // msg_seqno too low, so just pump it up by some "large" amount
             // TODO A better fix would be to start with a new fresh session ID
-            this._state._sequence += 64
+            this._state._sequence += 64;
         } else if (badMsg.errorCode === 33) {
             // msg_seqno too high never seems to happen but just in case
-            this._state._sequence -= 16
+            this._state._sequence -= 16;
         } else {
-
             for (const state of states) {
-                state.reject(new BadMessageError(state.request, badMsg.errorCode))
+                state.reject(
+                    new BadMessageError(state.request, badMsg.errorCode)
+                );
             }
 
-            return
+            return;
         }
         // Messages are to be re-sent once we've corrected the issue
         this._sendQueue.extend(states);
-        this._log.debug(`${states.length} messages will be resent due to bad msg`)
+        this._log.debug(
+            `${states.length} messages will be resent due to bad msg`
+        );
     }
 
     /**
@@ -683,7 +731,7 @@ export class MTProtoSender {
         // TODO https://goo.gl/VvpCC6
         const msgId = message.obj.answerMsgId;
         this._log.debug(`Handling detailed info for message ${msgId}`);
-        this._pendingAck.add(msgId)
+        this._pendingAck.add(msgId);
     }
 
     /**
@@ -698,7 +746,7 @@ export class MTProtoSender {
         // TODO https://goo.gl/VvpCC6
         const msgId = message.obj.answerMsgId;
         this._log.debug(`Handling new detailed info for message ${msgId}`);
-        this._pendingAck.add(msgId)
+        this._pendingAck.add(msgId);
     }
 
     /**
@@ -711,8 +759,8 @@ export class MTProtoSender {
      */
     async _handleNewSessionCreated(message: TLMessage) {
         // TODO https://goo.gl/LMyN7A
-        this._log.debug('Handling new session created');
-        this._state.salt = message.obj.serverSalt
+        this._log.debug("Handling new session created");
+        this._state.salt = message.obj.serverSalt;
     }
 
     /**
@@ -740,7 +788,7 @@ export class MTProtoSender {
             const state = this._pendingState.get(msgId);
             if (state && state.request instanceof Api.auth.LogOut) {
                 this._pendingState.delete(msgId);
-                state.resolve(true)
+                state.resolve(true);
             }
         }
     }
@@ -762,7 +810,7 @@ export class MTProtoSender {
 
         if (state) {
             this._pendingState.delete(message.msgId.toString());
-            state.resolve(message.obj)
+            state.resolve(message.obj);
         }
     }
 
@@ -775,11 +823,13 @@ export class MTProtoSender {
      */
     async _handleStateForgotten(message: TLMessage) {
         this._sendQueue.append(
-            new RequestState(new Api.MsgsStateInfo({
-                reqMsgId: message.msgId,
-                info: String.fromCharCode(1).repeat(message.obj.msgIds)
-            }))
-        )
+            new RequestState(
+                new Api.MsgsStateInfo({
+                    reqMsgId: message.msgId,
+                    info: String.fromCharCode(1).repeat(message.obj.msgIds),
+                })
+            )
+        );
     }
 
     /**
@@ -788,32 +838,30 @@ export class MTProtoSender {
      * @returns {Promise<void>}
      * @private
      */
-    async _handleMsgAll(message: TLMessage) {
-    }
+    async _handleMsgAll(message: TLMessage) {}
 
     async _startReconnect() {
         if (this._userConnected && !this._reconnecting) {
             this._reconnecting = true;
             // TODO Should we set this?
             // this._user_connected = false
-            this._log.info('Started reconnecting');
-            this._reconnect()
+            this._log.info("Started reconnecting");
+            this._reconnect();
         }
     }
 
     async _reconnect() {
-        this._log.debug('Closing current connection...');
+        this._log.debug("Closing current connection...");
         try {
-            await this.disconnect()
+            await this.disconnect();
         } catch (err) {
-            this._log.warn(err)
+            this._log.warn(err);
         }
         // @ts-ignore
         this._sendQueue.append(null);
         this._state.reset();
         const retries = this._retries;
 
-
         for (let attempt = 0; attempt < retries; attempt++) {
             try {
                 await this._connect();
@@ -821,18 +869,19 @@ export class MTProtoSender {
                 this._sendQueue.extend(Array.from(this._pendingState.values()));
                 this._pendingState = new Map<string, RequestState>();
                 if (this._autoReconnectCallback) {
-                    await this._autoReconnectCallback()
+                    await this._autoReconnectCallback();
                 }
                 if (this._updateCallback) {
-                    this._updateCallback(1)
+                    this._updateCallback(1);
                 }
 
-                break
+                break;
             } catch (e) {
-                this._log.error('WebSocket connection failed attempt : ' + (attempt + 1));
-                await sleep(this._delay)
+                this._log.error(
+                    "WebSocket connection failed attempt : " + (attempt + 1)
+                );
+                await sleep(this._delay);
             }
         }
     }
 }
-

+ 72 - 43
gramjs/network/MTProtoState.ts

@@ -1,13 +1,13 @@
-import bigInt from 'big-integer';
-import type {AuthKey} from "../crypto/AuthKey";
-import {helpers} from "../";
-import {Api} from '../tl';
-import {sha256, toSignedLittleBuffer} from "../Helpers";
-import {GZIPPacked, TLMessage} from "../tl/core";
-import {BinaryReader} from "../extensions";
-import type {BinaryWriter} from "../extensions";
-import {IGE} from "../crypto/IGE";
-import {InvalidBufferError, SecurityError} from "../errors";
+import bigInt from "big-integer";
+import type { AuthKey } from "../crypto/AuthKey";
+import { helpers } from "../";
+import { Api } from "../tl";
+import { sha256, toSignedLittleBuffer } from "../Helpers";
+import { GZIPPacked, TLMessage } from "../tl/core";
+import { BinaryReader } from "../extensions";
+import type { BinaryWriter } from "../extensions";
+import { IGE } from "../crypto/IGE";
+import { InvalidBufferError, SecurityError } from "../errors";
 
 export class MTProtoState {
     private authKey?: AuthKey;
@@ -50,7 +50,7 @@ export class MTProtoState {
         this.salt = bigInt.zero;
         this._sequence = 0;
         this.id = this._lastMsgId = bigInt.zero;
-        this.reset()
+        this.reset();
     }
 
     /**
@@ -60,7 +60,7 @@ export class MTProtoState {
         // Session IDs can be random on every connection
         this.id = helpers.generateRandomLong(true);
         this._sequence = 0;
-        this._lastMsgId = bigInt.zero
+        this._lastMsgId = bigInt.zero;
     }
 
     /**
@@ -69,7 +69,7 @@ export class MTProtoState {
      * @param message
      */
     updateMessageId(message: any) {
-        message.msgId = this._getNewMsgId()
+        message.msgId = this._getNewMsgId();
     }
 
     /**
@@ -85,9 +85,17 @@ export class MTProtoState {
             sha256(Buffer.concat([msgKey, authKey.slice(x, x + 36)])),
             sha256(Buffer.concat([authKey.slice(x + 40, x + 76), msgKey])),
         ]);
-        const key = Buffer.concat([sha256a.slice(0, 8), sha256b.slice(8, 24), sha256a.slice(24, 32)]);
-        const iv = Buffer.concat([sha256b.slice(0, 8), sha256a.slice(8, 24), sha256b.slice(24, 32)]);
-        return {key, iv}
+        const key = Buffer.concat([
+            sha256a.slice(0, 8),
+            sha256b.slice(8, 24),
+            sha256a.slice(24, 32),
+        ]);
+        const iv = Buffer.concat([
+            sha256b.slice(0, 8),
+            sha256a.slice(8, 24),
+            sha256b.slice(24, 32),
+        ]);
+        return { key, iv };
     }
 
     /**
@@ -98,17 +106,25 @@ export class MTProtoState {
      * @param contentRelated
      * @param afterId
      */
-    async writeDataAsMessage(buffer: BinaryWriter, data: Buffer, contentRelated: boolean, afterId: bigInt.BigInteger) {
+    async writeDataAsMessage(
+        buffer: BinaryWriter,
+        data: Buffer,
+        contentRelated: boolean,
+        afterId: bigInt.BigInteger
+    ) {
         const msgId = this._getNewMsgId();
         const seqNo = this._getSeqNo(contentRelated);
         let body;
         if (!afterId) {
-            body = await GZIPPacked.gzipIfSmaller(contentRelated, data)
+            body = await GZIPPacked.gzipIfSmaller(contentRelated, data);
         } else {
-            body = await GZIPPacked.gzipIfSmaller(contentRelated, new Api.InvokeAfterMsg({
-                msgId: afterId,
-                query: data
-            }).getBytes())
+            body = await GZIPPacked.gzipIfSmaller(
+                contentRelated,
+                new Api.InvokeAfterMsg({
+                    msgId: afterId,
+                    query: data,
+                }).getBytes()
+            );
         }
         const s = Buffer.alloc(4);
         s.writeInt32LE(seqNo, 0);
@@ -117,7 +133,7 @@ export class MTProtoState {
         const m = toSignedLittleBuffer(msgId, 8);
         buffer.write(Buffer.concat([m, s, b]));
         buffer.write(body);
-        return msgId
+        return msgId;
     }
 
     /**
@@ -142,17 +158,25 @@ export class MTProtoState {
         const s = toSignedLittleBuffer(this.salt, 8);
         const i = toSignedLittleBuffer(this.id, 8);
         data = Buffer.concat([Buffer.concat([s, i]), data]);
-        const padding = helpers.generateRandomBytes(helpers.mod(-(data.length + 12), 16) + 12);
+        const padding = helpers.generateRandomBytes(
+            helpers.mod(-(data.length + 12), 16) + 12
+        );
         // Being substr(what, offset, length); x = 0 for client
         // "msg_key_large = SHA256(substr(auth_key, 88+x, 32) + pt + padding)"
-        const msgKeyLarge = await sha256(Buffer.concat([authKey.slice(88, 88 + 32), data, padding]));
+        const msgKeyLarge = await sha256(
+            Buffer.concat([authKey.slice(88, 88 + 32), data, padding])
+        );
         // "msg_key = substr (msg_key_large, 8, 16)"
         const msgKey = msgKeyLarge.slice(8, 24);
 
-        const {iv, key} = await this._calcKey(authKey, msgKey, true);
+        const { iv, key } = await this._calcKey(authKey, msgKey, true);
 
         const keyId = helpers.readBufferFromBigInt(this.authKey.keyId, 8);
-        return Buffer.concat([keyId, msgKey, new IGE(key, iv).encryptIge(Buffer.concat([data, padding]))])
+        return Buffer.concat([
+            keyId,
+            msgKey,
+            new IGE(key, iv).encryptIge(Buffer.concat([data, padding])),
+        ]);
     }
 
     /**
@@ -165,30 +189,33 @@ export class MTProtoState {
         }
 
         if (body.length < 8) {
-            throw new InvalidBufferError(body)
+            throw new InvalidBufferError(body);
         }
 
         // TODO Check salt,sessionId, and sequenceNumber
         const keyId = helpers.readBigIntFromBuffer(body.slice(0, 8));
         if (!this.authKey.keyId || keyId.neq(this.authKey.keyId)) {
-            throw new SecurityError('Server replied with an invalid auth key');
+            throw new SecurityError("Server replied with an invalid auth key");
         }
         const authKey = this.authKey.getKey();
         if (!authKey) {
-            throw new SecurityError('Unset AuthKey');
-
+            throw new SecurityError("Unset AuthKey");
         }
         const msgKey = body.slice(8, 24);
-        const {iv, key} = await this._calcKey(authKey, msgKey, false);
+        const { iv, key } = await this._calcKey(authKey, msgKey, false);
         body = new IGE(key, iv).decryptIge(body.slice(24));
 
         // https://core.telegram.org/mtproto/security_guidelines
         // Sections "checking sha256 hash" and "message length"
 
-        const ourKey = await sha256(Buffer.concat([authKey.slice(96, 96 + 32), body]));
+        const ourKey = await sha256(
+            Buffer.concat([authKey.slice(96, 96 + 32), body])
+        );
 
         if (!msgKey.equals(ourKey.slice(8, 24))) {
-            throw new SecurityError('Received msg_key doesn\'t match with expected one')
+            throw new SecurityError(
+                "Received msg_key doesn't match with expected one"
+            );
         }
 
         const reader = new BinaryReader(body);
@@ -207,7 +234,7 @@ export class MTProtoState {
         // reader isn't used for anything else after this, it's unnecessary.
         const obj = reader.tgReadObject();
 
-        return new TLMessage(remoteMsgId, remoteSequence, obj)
+        return new TLMessage(remoteMsgId, remoteSequence, obj);
     }
 
     /**
@@ -218,12 +245,14 @@ export class MTProtoState {
     _getNewMsgId() {
         const now = new Date().getTime() / 1000 + this.timeOffset;
         const nanoseconds = Math.floor((now - Math.floor(now)) * 1e9);
-        let newMsgId = (bigInt(Math.floor(now)).shiftLeft(bigInt(32))).or(bigInt(nanoseconds).shiftLeft(bigInt(2)));
+        let newMsgId = bigInt(Math.floor(now))
+            .shiftLeft(bigInt(32))
+            .or(bigInt(nanoseconds).shiftLeft(bigInt(2)));
         if (this._lastMsgId.greaterOrEquals(newMsgId)) {
-            newMsgId = this._lastMsgId.add(bigInt(4))
+            newMsgId = this._lastMsgId.add(bigInt(4));
         }
         this._lastMsgId = newMsgId;
-        return newMsgId
+        return newMsgId;
     }
 
     /**
@@ -241,11 +270,11 @@ export class MTProtoState {
         if (this.timeOffset !== old) {
             this._lastMsgId = bigInt.zero;
             this._log.debug(
-                `Updated time offset (old offset ${old}, bad ${bad}, good ${correctMsgId}, new ${this.timeOffset})`,
-            )
+                `Updated time offset (old offset ${old}, bad ${bad}, good ${correctMsgId}, new ${this.timeOffset})`
+            );
         }
 
-        return this.timeOffset
+        return this.timeOffset;
     }
 
     /**
@@ -258,9 +287,9 @@ export class MTProtoState {
         if (contentRelated) {
             const result = this._sequence * 2 + 1;
             this._sequence += 1;
-            return result
+            return result;
         } else {
-            return this._sequence * 2
+            return this._sequence * 2;
         }
     }
 }

+ 2 - 4
gramjs/network/RequestState.ts

@@ -22,9 +22,7 @@ export class RequestState {
         this.result = undefined;
         this.promise = new Promise((resolve, reject) => {
             this.resolve = resolve;
-            this.reject = reject
-        })
+            this.reject = reject;
+        });
     }
 }
-
-

+ 42 - 41
gramjs/network/connection/Connection.ts

@@ -1,7 +1,7 @@
-import type{BinaryReader} from '../../extensions'
-import {PromisedNetSockets,PromisedWebSockets} from '../../extensions'
-import {AsyncQueue} from '../../extensions'
-import {IS_NODE} from '../../Helpers'
+import type { BinaryReader } from "../../extensions";
+import { PromisedNetSockets, PromisedWebSockets } from "../../extensions";
+import { AsyncQueue } from "../../extensions";
+import { IS_NODE } from "../../Helpers";
 
 /**
  * The `Connection` class is a wrapper around ``asyncio.open_connection``.
@@ -16,7 +16,7 @@ import {IS_NODE} from '../../Helpers'
  */
 class Connection {
     // @ts-ignore
-    PacketCodecClass : any; //"typeof AbridgedPacketCodec|typeof FullPacketCodec|typeof ObfuscatedConnection as "
+    PacketCodecClass: any; //"typeof AbridgedPacketCodec|typeof FullPacketCodec|typeof ObfuscatedConnection as "
     private _ip: string;
     private _port: number;
     private _dcId: number;
@@ -42,33 +42,34 @@ class Connection {
         this._obfuscation = undefined; // TcpObfuscated and MTProxy
         this._sendArray = new AsyncQueue();
         this._recvArray = new AsyncQueue();
-        this.socket = IS_NODE ? new PromisedNetSockets() : new PromisedWebSockets()
+        this.socket = IS_NODE
+            ? new PromisedNetSockets()
+            : new PromisedWebSockets();
 
         //this.socket = new PromisedWebSockets()
     }
 
     async _connect() {
-        this._log.debug('Connecting');
+        this._log.debug("Connecting");
         this._codec = new this.PacketCodecClass(this);
         await this.socket.connect(this._port, this._ip);
-        this._log.debug('Finished connecting');
+        this._log.debug("Finished connecting");
         // await this.socket.connect({host: this._ip, port: this._port});
-        await this._initConn()
+        await this._initConn();
     }
 
     async connect() {
         await this._connect();
         this._connected = true;
 
-        this._sendTask = this._sendLoop()
-        this._recvTask = this._recvLoop()
-
+        this._sendTask = this._sendLoop();
+        this._recvTask = this._recvLoop();
     }
 
     async disconnect() {
         this._connected = false;
         await this._recvArray.push(undefined);
-        await this.socket.close()
+        await this.socket.close();
     }
 
     async send(data: Buffer) {
@@ -76,9 +77,9 @@ class Connection {
             // this will stop the current loop
             // @ts-ignore
             await this._sendArray(undefined);
-            throw new Error('Not connected')
+            throw new Error("Not connected");
         }
-        await this._sendArray.push(data)
+        await this._sendArray.push(data);
     }
 
     async recv() {
@@ -86,10 +87,10 @@ class Connection {
             const result = await this._recvArray.pop();
             // undefined = sentinel value = keep trying
             if (result && result.length) {
-                return result
+                return result;
             }
         }
-        throw new Error('Not connected')
+        throw new Error("Not connected");
     }
 
     async _sendLoop() {
@@ -99,12 +100,12 @@ class Connection {
                 const data = await this._sendArray.pop();
                 if (!data) {
                     this._sendTask = undefined;
-                    return
+                    return;
                 }
-                await this._send(data)
+                await this._send(data);
             }
         } catch (e) {
-            this._log.info('The server closed the connection while sending')
+            this._log.info("The server closed the connection while sending");
         }
     }
 
@@ -114,36 +115,39 @@ class Connection {
             try {
                 data = await this._recv();
                 if (!data) {
-                    throw new Error('no data received')
+                    throw new Error("no data received");
                 }
             } catch (e) {
-                this._log.info('connection closed');
+                this._log.info("connection closed");
                 //await this._recvArray.push()
 
                 this.disconnect();
-                return
+                return;
             }
-            await this._recvArray.push(data)
+            await this._recvArray.push(data);
         }
     }
 
     async _initConn() {
         if (this._codec.tag) {
-            await this.socket.write(this._codec.tag)
+            await this.socket.write(this._codec.tag);
         }
     }
 
     async _send(data: Buffer) {
         const encodedPacket = this._codec.encodePacket(data);
-        this.socket.write(encodedPacket)
+        this.socket.write(encodedPacket);
     }
 
     async _recv() {
-        return await this._codec.readPacket(this.socket)
+        return await this._codec.readPacket(this.socket);
     }
 
     toString() {
-        return `${this._ip}:${this._port}/${this.constructor.name.replace('Connection', '')}`
+        return `${this._ip}:${this._port}/${this.constructor.name.replace(
+            "Connection",
+            ""
+        )}`;
     }
 }
 
@@ -152,16 +156,15 @@ class ObfuscatedConnection extends Connection {
 
     async _initConn() {
         this._obfuscation = new this.ObfuscatedIO(this);
-        this.socket.write(this._obfuscation.header)
+        this.socket.write(this._obfuscation.header);
     }
 
     async _send(data: Buffer) {
-        this._obfuscation.write(this._codec.encodePacket(data))
+        this._obfuscation.write(this._codec.encodePacket(data));
     }
 
-
     async _recv() {
-        return await this._codec.readPacket(this._obfuscation)
+        return await this._codec.readPacket(this._obfuscation);
     }
 }
 
@@ -169,23 +172,21 @@ class PacketCodec {
     private _conn: Buffer;
 
     constructor(connection: Buffer) {
-        this._conn = connection
+        this._conn = connection;
     }
 
     encodePacket(data: Buffer) {
-        throw new Error('Not Implemented')
+        throw new Error("Not Implemented");
 
         // Override
     }
 
-    async readPacket(reader: PromisedNetSockets | PromisedWebSockets) :Promise<Buffer>{
+    async readPacket(
+        reader: PromisedNetSockets | PromisedWebSockets
+    ): Promise<Buffer> {
         // override
-        throw new Error('Not Implemented')
+        throw new Error("Not Implemented");
     }
 }
 
-export {
-    Connection,
-    PacketCodec,
-    ObfuscatedConnection,
-};
+export { Connection, PacketCodec, ObfuscatedConnection };

+ 19 - 12
gramjs/network/connection/TCPAbridged.ts

@@ -1,12 +1,12 @@
-import {readBufferFromBigInt} from '../../Helpers';
-import {Connection, PacketCodec} from './Connection';
-import type {PromisedNetSockets, PromisedWebSockets} from "../../extensions";
+import { readBufferFromBigInt } from "../../Helpers";
+import { Connection, PacketCodec } from "./Connection";
+import type { PromisedNetSockets, PromisedWebSockets } from "../../extensions";
 
 import bigInt from "big-integer";
 
 export class AbridgedPacketCodec extends PacketCodec {
-    static tag = Buffer.from('ef', 'hex');
-    static obfuscateTag = Buffer.from('efefefef', 'hex');
+    static tag = Buffer.from("ef", "hex");
+    static obfuscateTag = Buffer.from("efefefef", "hex");
     private tag: Buffer;
     private obfuscateTag: Buffer;
 
@@ -22,19 +22,26 @@ export class AbridgedPacketCodec extends PacketCodec {
         if (length < 127) {
             const b = Buffer.alloc(1);
             b.writeUInt8(length, 0);
-            temp = b
+            temp = b;
         } else {
-            temp = Buffer.concat([Buffer.from('7f', 'hex'), readBufferFromBigInt(bigInt(length), 3)])
+            temp = Buffer.concat([
+                Buffer.from("7f", "hex"),
+                readBufferFromBigInt(bigInt(length), 3),
+            ]);
         }
-        return Buffer.concat([temp, data])
+        return Buffer.concat([temp, data]);
     }
 
-    async readPacket(reader: PromisedNetSockets | PromisedWebSockets): Promise<Buffer> {
+    async readPacket(
+        reader: PromisedNetSockets | PromisedWebSockets
+    ): Promise<Buffer> {
         const readData = await reader.read(1);
         let length = readData[0];
         if (length >= 127) {
-            length = Buffer.concat([await reader.read(3), Buffer.alloc(1)])
-                .readInt32LE(0)
+            length = Buffer.concat([
+                await reader.read(3),
+                Buffer.alloc(1),
+            ]).readInt32LE(0);
         }
 
         return reader.read(length << 2);
@@ -47,5 +54,5 @@ export class AbridgedPacketCodec extends PacketCodec {
  * 508 bytes (127 << 2, which is very common).
  */
 export class ConnectionTCPAbridged extends Connection {
-    PacketCodecClass = AbridgedPacketCodec
+    PacketCodecClass = AbridgedPacketCodec;
 }

+ 11 - 9
gramjs/network/connection/TCPFull.ts

@@ -1,14 +1,14 @@
-import {Connection, PacketCodec} from './Connection';
-import {crc32} from '../../Helpers';
-import {InvalidChecksumError} from '../../errors';
-import type {PromisedNetSockets, PromisedWebSockets} from "../../extensions";
+import { Connection, PacketCodec } from "./Connection";
+import { crc32 } from "../../Helpers";
+import { InvalidChecksumError } from "../../errors";
+import type { PromisedNetSockets, PromisedWebSockets } from "../../extensions";
 
 class FullPacketCodec extends PacketCodec {
     private _sendCounter: number;
 
     constructor(connection: any) {
         super(connection);
-        this._sendCounter = 0 // Telegram will ignore us otherwise
+        this._sendCounter = 0; // Telegram will ignore us otherwise
     }
 
     encodePacket(data: Buffer) {
@@ -22,7 +22,7 @@ class FullPacketCodec extends PacketCodec {
         const crc = Buffer.alloc(4);
         crc.writeUInt32LE(crc32(data), 0);
         this._sendCounter += 1;
-        return Buffer.concat([data, crc])
+        return Buffer.concat([data, crc]);
     }
 
     /**
@@ -30,7 +30,9 @@ class FullPacketCodec extends PacketCodec {
      * @param reader {PromisedWebSockets}
      * @returns {Promise<*>}
      */
-    async readPacket(reader: PromisedNetSockets | PromisedWebSockets): Promise<Buffer> {
+    async readPacket(
+        reader: PromisedNetSockets | PromisedWebSockets
+    ): Promise<Buffer> {
         const packetLenSeq = await reader.readExactly(8); // 4 and 4
         // process.exit(0);
         if (packetLenSeq === undefined) {
@@ -44,9 +46,9 @@ class FullPacketCodec extends PacketCodec {
 
         const validChecksum = crc32(Buffer.concat([packetLenSeq, body]));
         if (!(validChecksum === checksum)) {
-            throw new InvalidChecksumError(checksum, validChecksum)
+            throw new InvalidChecksumError(checksum, validChecksum);
         }
-        return body
+        return body;
     }
 }
 

+ 28 - 17
gramjs/network/connection/TCPObfuscated.ts

@@ -1,8 +1,8 @@
-import {generateRandomBytes} from '../../Helpers'
-import {ObfuscatedConnection} from './Connection'
-import {AbridgedPacketCodec} from './TCPAbridged'
-import {CTR} from '../../crypto/CTR';
-import {PromisedNetSockets, PromisedWebSockets} from "../../extensions";
+import { generateRandomBytes } from "../../Helpers";
+import { ObfuscatedConnection } from "./Connection";
+import { AbridgedPacketCodec } from "./TCPAbridged";
+import { CTR } from "../../crypto/CTR";
+import { PromisedNetSockets, PromisedWebSockets } from "../../extensions";
 
 class ObfuscatedIO {
     header?: Buffer = undefined;
@@ -16,28 +16,35 @@ class ObfuscatedIO {
         this.header = res.random;
 
         this._encrypt = res.encryptor;
-        this._decrypt = res.decryptor
+        this._decrypt = res.decryptor;
     }
 
     initHeader(packetCodec: any) {
         // Obfuscated messages secrets cannot start with any of these
-        const keywords = [Buffer.from('50567247', 'hex'), Buffer.from('474554', 'hex'),
-            Buffer.from('504f5354', 'hex'), Buffer.from('eeeeeeee', 'hex')];
+        const keywords = [
+            Buffer.from("50567247", "hex"),
+            Buffer.from("474554", "hex"),
+            Buffer.from("504f5354", "hex"),
+            Buffer.from("eeeeeeee", "hex"),
+        ];
         let random;
 
         // eslint-disable-next-line no-constant-condition
         while (true) {
             random = generateRandomBytes(64);
-            if (random[0] !== 0xef && !(random.slice(4, 8).equals(Buffer.alloc(4)))) {
+            if (
+                random[0] !== 0xef &&
+                !random.slice(4, 8).equals(Buffer.alloc(4))
+            ) {
                 let ok = true;
                 for (const key of keywords) {
                     if (key.equals(random.slice(0, 4))) {
                         ok = false;
-                        break
+                        break;
                     }
                 }
                 if (ok) {
-                    break
+                    break;
                 }
             }
         }
@@ -53,25 +60,29 @@ class ObfuscatedIO {
         const decryptor = new CTR(decryptKey, decryptIv);
 
         random = Buffer.concat([
-            Buffer.from(random.slice(0, 56)), packetCodec.obfuscateTag, Buffer.from(random.slice(60)),
+            Buffer.from(random.slice(0, 56)),
+            packetCodec.obfuscateTag,
+            Buffer.from(random.slice(60)),
         ]);
         random = Buffer.concat([
-            Buffer.from(random.slice(0, 56)), Buffer.from(encryptor.encrypt(random).slice(56, 64)), Buffer.from(random.slice(64)),
+            Buffer.from(random.slice(0, 56)),
+            Buffer.from(encryptor.encrypt(random).slice(56, 64)),
+            Buffer.from(random.slice(64)),
         ]);
-        return {random, encryptor, decryptor}
+        return { random, encryptor, decryptor };
     }
 
     async read(n: number) {
         const data = await this.connection.readExactly(n);
-        return this._decrypt.encrypt(data)
+        return this._decrypt.encrypt(data);
     }
 
     write(data: Buffer) {
-        this.connection.write(this._encrypt.encrypt(data))
+        this.connection.write(this._encrypt.encrypt(data));
     }
 }
 
 export class ConnectionTCPObfuscated extends ObfuscatedConnection {
     ObfuscatedIO = ObfuscatedIO;
-    PacketCodecClass = AbridgedPacketCodec
+    PacketCodecClass = AbridgedPacketCodec;
 }

+ 4 - 4
gramjs/network/connection/index.ts

@@ -1,4 +1,4 @@
-export {Connection} from './Connection';
-export {ConnectionTCPFull} from './TCPFull';
-export {ConnectionTCPAbridged} from './TCPAbridged';
-export {ConnectionTCPObfuscated} from './TCPObfuscated';
+export { Connection } from "./Connection";
+export { ConnectionTCPFull } from "./TCPFull";
+export { ConnectionTCPAbridged } from "./TCPAbridged";
+export { ConnectionTCPObfuscated } from "./TCPObfuscated";

+ 8 - 9
gramjs/network/index.ts

@@ -1,11 +1,11 @@
-export {MTProtoPlainSender} from './MTProtoPlainSender'
-export {doAuthentication} from './Authenticator'
-export {MTProtoSender} from './MTProtoSender'
+export { MTProtoPlainSender } from "./MTProtoPlainSender";
+export { doAuthentication } from "./Authenticator";
+export { MTProtoSender } from "./MTProtoSender";
 
 interface states {
-    disconnected: -1,
-    connected: 1,
-    broken: 0,
+    disconnected: -1;
+    connected: 1;
+    broken: 0;
 }
 
 export class UpdateConnectionState {
@@ -17,8 +17,7 @@ export class UpdateConnectionState {
     private state: number;
 
     constructor(state: number) {
-        this.state = state
-
+        this.state = state;
     }
 }
 
@@ -27,4 +26,4 @@ export {
     ConnectionTCPFull,
     ConnectionTCPAbridged,
     ConnectionTCPObfuscated,
-} from './connection'
+} from "./connection";

+ 18 - 17
gramjs/requestIter.ts

@@ -1,14 +1,13 @@
-import type {TelegramClient} from "./client/TelegramClient";
-import {sleep} from './Helpers';
-import {helpers} from "./";
+import type { TelegramClient } from "./client/TelegramClient";
+import { sleep } from "./Helpers";
+import { helpers } from "./";
 
 interface BaseRequestIterInterface {
-    reverse?: boolean,
-    waitTime?: number,
+    reverse?: boolean;
+    waitTime?: number;
 }
 
 export class RequestIter implements AsyncIterable<any> {
-
     public client: TelegramClient;
     public reverse: boolean | undefined;
     public waitTime: number | undefined;
@@ -20,9 +19,12 @@ export class RequestIter implements AsyncIterable<any> {
     private lastLoad: number;
     kwargs: {};
 
-    [key: string]: any;
-
-    constructor(client: TelegramClient, limit: number, params: BaseRequestIterInterface = {}, args = {}) {
+    constructor(
+        client: TelegramClient,
+        limit?: number,
+        params: BaseRequestIterInterface = {},
+        args = {}
+    ) {
         this.client = client;
         this.reverse = params.reverse;
         this.waitTime = params.waitTime;
@@ -32,10 +34,9 @@ export class RequestIter implements AsyncIterable<any> {
         this.kwargs = args;
         this.index = 0;
         this.total = undefined;
-        this.lastLoad = 0
+        this.lastLoad = 0;
     }
 
-
     async _init(kwargs: any): Promise<boolean | void> {
         // for overload
     }
@@ -47,7 +48,6 @@ export class RequestIter implements AsyncIterable<any> {
         this.left = this.limit;
         return {
             next: async () => {
-
                 if (this.buffer == undefined) {
                     this.buffer = [];
                     if (await this._init(this.kwargs)) {
@@ -62,7 +62,10 @@ export class RequestIter implements AsyncIterable<any> {
                 }
                 if (this.index == this.buffer.length) {
                     if (this.waitTime) {
-                        await sleep(this.waitTime - ((new Date().getTime() / 1000) - this.lastLoad));
+                        await sleep(
+                            this.waitTime -
+                                (new Date().getTime() / 1000 - this.lastLoad)
+                        );
                     }
                     this.lastLoad = new Date().getTime() / 1000;
                     this.index = 0;
@@ -93,8 +96,8 @@ export class RequestIter implements AsyncIterable<any> {
                     value: result,
                     done: false,
                 };
-            }
-        }
+            },
+        };
     }
 
     async collect() {
@@ -109,6 +112,4 @@ export class RequestIter implements AsyncIterable<any> {
     async _loadNextChunk(): Promise<boolean | undefined> {
         throw new Error("Not Implemented");
     }
-
 }
-

+ 9 - 15
gramjs/sessions/Abstract.ts

@@ -1,9 +1,8 @@
-import type {AuthKey} from "../crypto/AuthKey";
-import type {EntityLike} from "../define";
-import {Api} from "../tl";
+import type { AuthKey } from "../crypto/AuthKey";
+import type { EntityLike } from "../define";
+import { Api } from "../tl";
 
 export abstract class Session {
-
     /**
      * Creates a clone of this session file
      * @param toInstance {Session|null}
@@ -28,7 +27,7 @@ export abstract class Session {
     /**
      * Returns the currently-used data center ID.
      */
-    abstract get dcId(): number ;
+    abstract get dcId(): number;
 
     /**
      * Returns the server address where the library should connect to.
@@ -38,24 +37,24 @@ export abstract class Session {
     /**
      * Returns the port to which the library should connect to.
      */
-    abstract get port(): number | undefined ;
+    abstract get port(): number | undefined;
 
     /**
      * Returns an ``AuthKey`` instance associated with the saved
      * data center, or `None` if a new one should be generated.
      */
-    abstract get authKey(): AuthKey | undefined ;
+    abstract get authKey(): AuthKey | undefined;
 
     /**
      * Sets the ``AuthKey`` to be used for the saved data center.
      * @param value
      */
-    abstract set authKey(value: AuthKey | undefined) ;
+    abstract set authKey(value: AuthKey | undefined);
 
     /**
      * Called before using the session
      */
-    abstract load(): Promise<void>
+    abstract load(): Promise<void>;
 
     /**
      *  sets auth key for a dc
@@ -66,8 +65,7 @@ export abstract class Session {
      *  gets auth key for a dc
      */
 
-    abstract getAuthKey(dcId?: number): AuthKey | undefined ;
-
+    abstract getAuthKey(dcId?: number): AuthKey | undefined;
 
     /**
      * Turns the given key into an ``InputPeer`` (e.g. ``InputPeerUser``).
@@ -131,7 +129,6 @@ export abstract class Session {
 
     abstract close(): void;
 
-
     /**
      * called whenever important properties change. It should
      * make persist the relevant session information to disk.
@@ -145,13 +142,10 @@ export abstract class Session {
 
     abstract delete(): void;
 
-
     /**
      * Processes the input ``TLObject`` or ``list`` and saves
      * whatever information is relevant (e.g., ID or access hash).
      * @param tlo
      */
     abstract processEntities(tlo: any): void;
-
-
 }

+ 55 - 53
gramjs/sessions/CacheApiSession.js

@@ -1,126 +1,128 @@
-const MemorySession = require('./Memory')
-const AuthKey = require('../crypto/AuthKey')
-const utils = require('../Utils')
+const MemorySession = require("./Memory");
+const AuthKey = require("../crypto/AuthKey");
+const utils = require("../Utils");
 
-const CACHE_NAME = 'GramJs'
-const STORAGE_KEY_BASE = 'GramJs-session-'
+const CACHE_NAME = "GramJs";
+const STORAGE_KEY_BASE = "GramJs-session-";
 
 class CacheApi extends MemorySession {
     constructor(sessionId) {
-        super()
-        this._storageKey = sessionId
-        this._authKeys = {}
+        super();
+        this._storageKey = sessionId;
+        this._authKeys = {};
     }
 
     async load() {
         if (!this._storageKey) {
-            return
+            return;
         }
 
         try {
-            const json = await fetchFromCache(this._storageKey)
-            const { mainDcId, keys, hashes } = JSON.parse(json)
-            const { ipAddress, port } = await utils.getDC(mainDcId)
+            const json = await fetchFromCache(this._storageKey);
+            const { mainDcId, keys, hashes } = JSON.parse(json);
+            const { ipAddress, port } = await utils.getDC(mainDcId);
 
-            this.setDC(mainDcId, ipAddress, port, true)
+            this.setDC(mainDcId, ipAddress, port, true);
 
-            Object.keys(keys).forEach(dcId => {
-                if (keys[dcId] && hashes[dcId]){
+            Object.keys(keys).forEach((dcId) => {
+                if (keys[dcId] && hashes[dcId]) {
                     this._authKeys[dcId] = new AuthKey(
                         Buffer.from(keys[dcId].data),
-                        Buffer.from(hashes[dcId].data),
-                    )
+                        Buffer.from(hashes[dcId].data)
+                    );
                 }
-            })
+            });
         } catch (err) {
-            throw new Error(`Failed to retrieve or parse JSON from Cache for key ${this._storageKey}`)
+            throw new Error(
+                `Failed to retrieve or parse JSON from Cache for key ${this._storageKey}`
+            );
         }
     }
 
     setDC(dcId, serverAddress, port, skipUpdateStorage = false) {
-        this._dcId = dcId
-        this._serverAddress = serverAddress
-        this._port = port
+        this._dcId = dcId;
+        this._serverAddress = serverAddress;
+        this._port = port;
 
-        delete this._authKeys[dcId]
+        delete this._authKeys[dcId];
 
         if (!skipUpdateStorage) {
-            void this._updateStorage()
+            void this._updateStorage();
         }
     }
 
     async save() {
         if (!this._storageKey) {
-            this._storageKey = generateStorageKey()
+            this._storageKey = generateStorageKey();
         }
 
-        await this._updateStorage()
+        await this._updateStorage();
 
-        return this._storageKey
+        return this._storageKey;
     }
 
     get authKey() {
-        throw new Error('Not supported')
+        throw new Error("Not supported");
     }
 
     set authKey(value) {
-        throw new Error('Not supported')
+        throw new Error("Not supported");
     }
 
     getAuthKey(dcId = this._dcId) {
-        return this._authKeys[dcId]
+        return this._authKeys[dcId];
     }
 
     setAuthKey(authKey, dcId = this._dcId) {
-        this._authKeys[dcId] = authKey
+        this._authKeys[dcId] = authKey;
 
-        void this._updateStorage()
+        void this._updateStorage();
     }
 
     async _updateStorage() {
         if (!this._storageKey) {
-            return
+            return;
         }
 
         const sessionData = {
             mainDcId: this._dcId,
             keys: {},
             hashes: {},
-        }
+        };
 
-        Object.keys(this._authKeys).map(dcId => {
-            const authKey = this._authKeys[dcId]
-            sessionData.keys[dcId] = authKey._key
-            sessionData.hashes[dcId] = authKey._hash
-        })
+        Object.keys(this._authKeys).map((dcId) => {
+            const authKey = this._authKeys[dcId];
+            sessionData.keys[dcId] = authKey._key;
+            sessionData.hashes[dcId] = authKey._hash;
+        });
 
-        await saveToCache(this._storageKey, JSON.stringify(sessionData))
+        await saveToCache(this._storageKey, JSON.stringify(sessionData));
     }
 
     async delete() {
-        const request = new Request(this._storageKey)
-        const cache = await self.caches.open(CACHE_NAME)
-        await cache.delete(request)
+        const request = new Request(this._storageKey);
+        const cache = await self.caches.open(CACHE_NAME);
+        await cache.delete(request);
     }
 }
 
 function generateStorageKey() {
     // Creating two sessions at the same moment is not expected nor supported.
-    return `${STORAGE_KEY_BASE}${Date.now()}`
+    return `${STORAGE_KEY_BASE}${Date.now()}`;
 }
 
 async function fetchFromCache(key) {
-    const request = new Request(key)
-    const cache = await self.caches.open(CACHE_NAME)
-    const cached = await cache.match(request)
-    return cached ? cached.text() : null
+    const request = new Request(key);
+    const cache = await self.caches.open(CACHE_NAME);
+    const cached = await cache.match(request);
+    return cached ? cached.text() : null;
 }
 
 async function saveToCache(key, data) {
-    const request = new Request(key)
-    const response = new Response(data)
-    const cache = await self.caches.open(CACHE_NAME)
-    return cache.put(request, response)
+    const request = new Request(key);
+    const response = new Response(data);
+    const cache = await self.caches.open(CACHE_NAME);
+    return cache.put(request, response);
 }
 
-module.exports = CacheApi
+module.exports = CacheApi;

+ 99 - 83
gramjs/sessions/Memory.ts

@@ -1,12 +1,12 @@
-import {Session} from './Abstract';
-import type {AuthKey} from "../crypto/AuthKey";
-import {Api} from "../tl";
+import { Session } from "./Abstract";
+import type { AuthKey } from "../crypto/AuthKey";
+import { Api } from "../tl";
 import bigInt from "big-integer";
 
-import {getDisplayName, getInputPeer, getPeerId} from "../Utils";
-import {isArrayLike} from "../Helpers";
-import {utils} from "../";
-import type {EntityLike} from "../define";
+import { getDisplayName, getInputPeer, getPeerId } from "../Utils";
+import { isArrayLike } from "../Helpers";
+import { utils } from "../";
+import type { EntityLike } from "../define";
 
 export class MemorySession extends Session {
     protected _serverAddress?: string;
@@ -26,13 +26,13 @@ export class MemorySession extends Session {
         this._takeoutId = undefined;
 
         this._entities = new Set();
-        this._updateStates = {}
+        this._updateStates = {};
     }
 
     setDC(dcId: number, serverAddress: string, port: number) {
         this._dcId = dcId | 0;
         this._serverAddress = serverAddress;
-        this._port = port
+        this._port = port;
     }
 
     get dcId() {
@@ -40,11 +40,11 @@ export class MemorySession extends Session {
     }
 
     get serverAddress() {
-        return this._serverAddress
+        return this._serverAddress;
     }
 
     get port() {
-        return this._port
+        return this._port;
     }
 
     get authKey() {
@@ -56,79 +56,83 @@ export class MemorySession extends Session {
     }
 
     get takeoutId() {
-        return this._takeoutId
+        return this._takeoutId;
     }
 
     set takeoutId(value) {
-        this._takeoutId = value
+        this._takeoutId = value;
     }
 
     getAuthKey(dcId?: number) {
         if (dcId && dcId !== this.dcId) {
             // Not supported.
-            return undefined
+            return undefined;
         }
 
-        return this.authKey
+        return this.authKey;
     }
 
     setAuthKey(authKey?: AuthKey, dcId?: number) {
         if (dcId && dcId !== this.dcId) {
             // Not supported.
-            return undefined
+            return undefined;
         }
 
-        this.authKey = authKey
+        this.authKey = authKey;
     }
 
-    close() {
-    }
+    close() {}
 
-    save() {
-    }
+    save() {}
 
-    async load() {
+    async load() {}
 
-    }
-
-    delete() {
-    }
+    delete() {}
 
-    _entityValuesToRow(id: number, hash: bigInt.BigInteger, username: string, phone: string, name: string) {
+    _entityValuesToRow(
+        id: number,
+        hash: bigInt.BigInteger,
+        username: string,
+        phone: string,
+        name: string
+    ) {
         // While this is a simple implementation it might be overrode by,
         // other classes so they don't need to implement the plural form
         // of the method. Don't remove.
-        return [id, hash, username, phone, name]
+        return [id, hash, username, phone, name];
     }
 
     _entityToRow(e: any) {
         if (!(e.classType === "constructor")) {
-            return
+            return;
         }
         let p;
         let markedId;
         try {
             p = getInputPeer(e, false);
-            markedId = getPeerId(p)
+            markedId = getPeerId(p);
         } catch (e) {
-            return
+            return;
         }
         let pHash;
-        if (p instanceof Api.InputPeerUser || p instanceof Api.InputPeerChannel) {
-            pHash = p.accessHash
+        if (
+            p instanceof Api.InputPeerUser ||
+            p instanceof Api.InputPeerChannel
+        ) {
+            pHash = p.accessHash;
         } else if (p instanceof Api.InputPeerChat) {
             pHash = bigInt.zero;
         } else {
-            return
+            return;
         }
 
         let username = e.username;
         if (username) {
-            username = username.toLowerCase()
+            username = username.toLowerCase();
         }
         const phone = e.phone;
         const name = getDisplayName(e);
-        return this._entityValuesToRow(markedId, pHash, username, phone, name)
+        return this._entityValuesToRow(markedId, pHash, username, phone, name);
     }
 
     _entitiesToRows(tlo: any) {
@@ -138,14 +142,14 @@ export class MemorySession extends Session {
             entities = tlo;
         } else {
             if (typeof tlo === "object") {
-                if ('user' in tlo) {
-                    entities.push(tlo.user)
+                if ("user" in tlo) {
+                    entities.push(tlo.user);
                 }
-                if ('chats' in tlo && isArrayLike(tlo.chats)) {
-                    entities = entities.concat(tlo.chats)
+                if ("chats" in tlo && isArrayLike(tlo.chats)) {
+                    entities = entities.concat(tlo.chats);
                 }
-                if ('users' in tlo && isArrayLike(tlo.users)) {
-                    entities = entities.concat(tlo.users)
+                if ("users" in tlo && isArrayLike(tlo.users)) {
+                    entities = entities.concat(tlo.users);
                 }
             }
         }
@@ -153,58 +157,64 @@ export class MemorySession extends Session {
         for (const e of entities) {
             const row = this._entityToRow(e);
             if (row) {
-                rows.push(row)
+                rows.push(row);
             }
         }
-        return rows
+        return rows;
     }
 
     processEntities(tlo: any) {
         const entitiesSet = this._entitiesToRows(tlo);
         for (const e of entitiesSet) {
-            this._entities.add(e)
+            this._entities.add(e);
         }
     }
 
     getEntityRowsByPhone(phone: string) {
-        for (const e of this._entities) { // id, hash, username, phone, name
+        for (const e of this._entities) {
+            // id, hash, username, phone, name
             if (e[3] === phone) {
-                return [e[0], e[1]]
+                return [e[0], e[1]];
             }
         }
     }
 
     getEntityRowsByUsername(username: string) {
-        for (const e of this._entities) { // id, hash, username, phone, name
+        for (const e of this._entities) {
+            // id, hash, username, phone, name
             if (e[2] === username) {
-                return [e[0], e[1]]
+                return [e[0], e[1]];
             }
         }
     }
 
     getEntityRowsByName(name: string) {
-        for (const e of this._entities) { // id, hash, username, phone, name
+        for (const e of this._entities) {
+            // id, hash, username, phone, name
             if (e[4] === name) {
-                return [e[0], e[1]]
+                return [e[0], e[1]];
             }
         }
     }
 
     getEntityRowsById(id: number, exact = true) {
         if (exact) {
-            for (const e of this._entities) { // id, hash, username, phone, name
+            for (const e of this._entities) {
+                // id, hash, username, phone, name
                 if (e[0] === id) {
-                    return [e[0], e[1]]
+                    return [e[0], e[1]];
                 }
             }
         } else {
-            const ids = [utils.getPeerId(new Api.PeerUser({userId: id})),
-                utils.getPeerId(new Api.PeerChat({chatId: id})),
-                utils.getPeerId(new Api.PeerChannel({channelId: id})),
+            const ids = [
+                utils.getPeerId(new Api.PeerUser({ userId: id })),
+                utils.getPeerId(new Api.PeerChat({ chatId: id })),
+                utils.getPeerId(new Api.PeerChannel({ channelId: id })),
             ];
-            for (const e of this._entities) { // id, hash, username, phone, name
+            for (const e of this._entities) {
+                // id, hash, username, phone, name
                 if (ids.includes(e[0])) {
-                    return [e[0], e[1]]
+                    return [e[0], e[1]];
                 }
             }
         }
@@ -212,46 +222,49 @@ export class MemorySession extends Session {
 
     getInputEntity(key: EntityLike): Api.TypeInputPeer {
         let exact;
-        if (typeof key === 'object' && key.SUBCLASS_OF_ID) {
-            if ([0xc91c90b6, 0xe669bf46, 0x40f202fd].includes(key.SUBCLASS_OF_ID)) {
+        if (typeof key === "object" && key.SUBCLASS_OF_ID) {
+            if (
+                [0xc91c90b6, 0xe669bf46, 0x40f202fd].includes(
+                    key.SUBCLASS_OF_ID
+                )
+            ) {
                 // hex(crc32(b'InputPeer', b'InputUser' and b'InputChannel'))
                 // We already have an Input version, so nothing else required
-                return key
+                return key;
             }
             // Try to early return if this key can be casted as input peer
-            return utils.getInputPeer(key)
+            return utils.getInputPeer(key);
         } else {
             // Not a TLObject or can't be cast into InputPeer
-            if (typeof key === 'object' && key.classType === 'constructor') {
+            if (typeof key === "object" && key.classType === "constructor") {
                 key = utils.getPeerId(key);
-                exact = true
+                exact = true;
             } else {
-                exact = !(typeof key == 'number') || key < 0
+                exact = !(typeof key == "number") || key < 0;
             }
         }
 
-
         let result = undefined;
-        if (typeof key === 'string') {
+        if (typeof key === "string") {
             const phone = utils.parsePhone(key);
             if (phone) {
-                result = this.getEntityRowsByPhone(phone)
+                result = this.getEntityRowsByPhone(phone);
             } else {
-                const {username, isInvite} = utils.parseUsername(key);
+                const { username, isInvite } = utils.parseUsername(key);
                 if (username && !isInvite) {
-                    result = this.getEntityRowsByUsername(username)
+                    result = this.getEntityRowsByUsername(username);
                 } else {
                     const tup = utils.resolveInviteLink(key)[1];
                     if (tup) {
-                        result = this.getEntityRowsById(tup, false)
+                        result = this.getEntityRowsById(tup, false);
                     }
                 }
             }
-        } else if (typeof key === 'number') {
-            result = this.getEntityRowsById(key, exact)
+        } else if (typeof key === "number") {
+            result = this.getEntityRowsById(key, exact);
         }
-        if (!result && typeof key === 'string') {
-            result = this.getEntityRowsByName(key)
+        if (!result && typeof key === "string") {
+            result = this.getEntityRowsByName(key);
         }
 
         if (result) {
@@ -262,18 +275,21 @@ export class MemorySession extends Session {
             const kind = resolved[1];
             // removes the mark and returns type of entity
             if (kind === Api.PeerUser) {
-                return new Api.InputPeerUser({userId: entityId, accessHash: entityHash})
+                return new Api.InputPeerUser({
+                    userId: entityId,
+                    accessHash: entityHash,
+                });
             } else if (kind === Api.PeerChat) {
-                return new Api.InputPeerChat({chatId: entityId})
+                return new Api.InputPeerChat({ chatId: entityId });
             } else if (kind === Api.PeerChannel) {
-                return new Api.InputPeerChannel({channelId: entityId, accessHash: entityHash})
+                return new Api.InputPeerChannel({
+                    channelId: entityId,
+                    accessHash: entityHash,
+                });
             }
         } else {
-            throw new Error('Could not find input entity with key ' + key)
+            throw new Error("Could not find input entity with key " + key);
         }
-        throw new Error('Could not find input entity with key ' + key)
-
+        throw new Error("Could not find input entity with key " + key);
     }
-
 }
-

+ 7 - 8
gramjs/sessions/StoreSession.ts

@@ -1,7 +1,7 @@
-import {MemorySession} from "./Memory";
+import { MemorySession } from "./Memory";
 import store from "store2";
-import {AuthKey} from "../crypto/AuthKey";
-import {LocalStorage} from 'node-localstorage';
+import { AuthKey } from "../crypto/AuthKey";
+import { LocalStorage } from "node-localstorage";
 
 export class StoreSession extends MemorySession {
     private readonly sessionName: string;
@@ -9,10 +9,8 @@ export class StoreSession extends MemorySession {
 
     constructor(sessionName: string) {
         super();
-        this.store = store.area('fs', new LocalStorage("./" + sessionName));
+        this.store = store.area("fs", new LocalStorage("./" + sessionName));
         this.sessionName = sessionName + ":";
-
-
     }
 
     async load() {
@@ -28,14 +26,15 @@ export class StoreSession extends MemorySession {
         const dcId = this.store.get(this.sessionName + "dcId");
         if (dcId) {
             this._dcId = dcId;
-
         }
 
         const port = this.store.get(this.sessionName + "port");
         if (port) {
             this._port = port;
         }
-        const serverAddress = this.store.get(this.sessionName + "serverAddress");
+        const serverAddress = this.store.get(
+            this.sessionName + "serverAddress"
+        );
         if (serverAddress) {
             this._serverAddress = serverAddress;
         }

+ 33 - 30
gramjs/sessions/StringSession.ts

@@ -1,9 +1,8 @@
-import {MemorySession} from "./Memory";
-import {BinaryReader} from "../extensions";
-import {AuthKey} from "../crypto/AuthKey";
-
-const CURRENT_VERSION = '1';
+import { MemorySession } from "./Memory";
+import { BinaryReader } from "../extensions";
+import { AuthKey } from "../crypto/AuthKey";
 
+const CURRENT_VERSION = "1";
 
 export class StringSession extends MemorySession {
     private _key?: Buffer;
@@ -27,31 +26,32 @@ export class StringSession extends MemorySession {
         super();
         if (session) {
             if (session[0] !== CURRENT_VERSION) {
-                throw new Error('Not a valid string')
+                throw new Error("Not a valid string");
             }
             session = session.slice(1);
             const r = StringSession.decode(session);
 
             const reader = new BinaryReader(r);
-            this._dcId = reader.read(1)
-                .readUInt8(0);
+            this._dcId = reader.read(1).readUInt8(0);
             if (session.length == 352) {
                 // Telethon session
                 const ip_v4 = reader.read(4);
                 // TODO looks ugly smh
-                this._serverAddress = ip_v4[0].toString() + '.' + ip_v4[1].toString() + '.' + ip_v4[2].toString() + '.' + ip_v4[3].toString();
-
+                this._serverAddress =
+                    ip_v4[0].toString() +
+                    "." +
+                    ip_v4[1].toString() +
+                    "." +
+                    ip_v4[2].toString() +
+                    "." +
+                    ip_v4[3].toString();
             } else {
-                const serverAddressLen = reader.read(2)
-                    .readInt16BE(0);
+                const serverAddressLen = reader.read(2).readInt16BE(0);
                 this._serverAddress = reader.read(serverAddressLen).toString();
-
             }
 
-
-            this._port = reader.read(2)
-                .readInt16BE(0);
-            this._key = reader.read(-1)
+            this._port = reader.read(2).readInt16BE(0);
+            this._key = reader.read(-1);
         }
     }
 
@@ -60,7 +60,7 @@ export class StringSession extends MemorySession {
      * @returns {string}
      */
     static encode(x: Buffer) {
-        return x.toString('base64')
+        return x.toString("base64");
     }
 
     /**
@@ -68,7 +68,7 @@ export class StringSession extends MemorySession {
      * @returns {Buffer}
      */
     static decode(x: string) {
-        return Buffer.from(x, 'base64')
+        return Buffer.from(x, "base64");
     }
 
     async load() {
@@ -80,12 +80,12 @@ export class StringSession extends MemorySession {
 
     save() {
         if (!this.authKey || !this.serverAddress || !this.port) {
-            return ''
+            return "";
         }
         // TS is weird
         const key = this.authKey.getKey();
         if (!key) {
-            return '';
+            return "";
         }
         const dcBuffer = Buffer.from([this.dcId]);
         const addressBuffer = Buffer.from(this.serverAddress);
@@ -94,14 +94,17 @@ export class StringSession extends MemorySession {
         const portBuffer = Buffer.alloc(2);
         portBuffer.writeInt16BE(this.port, 0);
 
-        return CURRENT_VERSION + StringSession.encode(Buffer.concat([
-            dcBuffer,
-            addressLengthBuffer,
-            addressBuffer,
-            portBuffer,
-            key,
-        ]))
+        return (
+            CURRENT_VERSION +
+            StringSession.encode(
+                Buffer.concat([
+                    dcBuffer,
+                    addressLengthBuffer,
+                    addressBuffer,
+                    portBuffer,
+                    key,
+                ])
+            )
+        );
     }
-
 }
-

+ 3 - 4
gramjs/sessions/index.ts

@@ -1,6 +1,5 @@
-export {MemorySession} from './Memory';
-export {StringSession} from './StringSession';
-export {StoreSession} from './StoreSession';
+export { MemorySession } from "./Memory";
+export { StringSession } from "./StringSession";
+export { StoreSession } from "./StoreSession";
 // @ts-ignore
 //export {CacheApiSession} from './CacheApiSession';
-

+ 5 - 6
gramjs/tl/AllTLObjects.ts

@@ -1,16 +1,15 @@
-import {Api} from './'
+import { Api } from "./";
 
 export const LAYER = 128;
 const tlobjects: any = {};
 
-
 for (const tl of Object.values(Api)) {
-    if ('CONSTRUCTOR_ID' in tl) {
-        tlobjects[tl.CONSTRUCTOR_ID] = tl
+    if ("CONSTRUCTOR_ID" in tl) {
+        tlobjects[tl.CONSTRUCTOR_ID] = tl;
     } else {
         for (const sub of Object.values(tl)) {
-            tlobjects[sub.CONSTRUCTOR_ID] = sub
+            tlobjects[sub.CONSTRUCTOR_ID] = sub;
         }
     }
 }
-export {tlobjects};
+export { tlobjects };

+ 12 - 10
gramjs/tl/MTProtoRequest.ts

@@ -22,32 +22,34 @@ export class MTProtoRequest {
 
         this.constructorId = 0;
         this.confirmed = false;
-        this.responded = false
+        this.responded = false;
     }
 
     // these should not be overrode
     onSendSuccess() {
         this.sendTime = new Date().getTime();
-        this.sent = true
+        this.sent = true;
     }
 
     onConfirm() {
-        this.confirmReceived = true
+        this.confirmReceived = true;
     }
 
     needResend() {
-        return this.dirty || (this.confirmed && !this.confirmReceived && new Date().getTime() - this.sendTime > 3000)
+        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)
+        throw Error("Not overload " + this.constructor.name);
     }
 
-    onResponse(buffer: Buffer) {
-    }
+    onResponse(buffer: Buffer) {}
 
-    onException(exception: Error) {
-    }
+    onException(exception: Error) {}
 }
-

Fichier diff supprimé car celui-ci est trop grand
+ 151 - 155
gramjs/tl/api.d.ts


+ 345 - 283
gramjs/tl/api.js

@@ -1,357 +1,376 @@
-const bigInt = require('big-integer')
-
-
-const { generateRandomBytes, readBigIntFromBuffer, isArrayLike } = require('../Helpers')
+const bigInt = require("big-integer");
 
+const {
+    generateRandomBytes,
+    readBigIntFromBuffer,
+    isArrayLike,
+} = require("../Helpers");
 
 function generateRandomBigInt() {
-    return readBigIntFromBuffer(generateRandomBytes(8), false)
+    return readBigIntFromBuffer(generateRandomBytes(8), false);
 }
 
 const {
     parseTl,
     serializeBytes,
     serializeDate,
-} = require('./generationHelpers')
-const { IS_NODE, toSignedLittleBuffer } = require('../Helpers')
-let tlContent, schemeContent
+} = require("./generationHelpers");
+const { IS_NODE, toSignedLittleBuffer } = require("../Helpers");
+let tlContent, schemeContent;
 if (IS_NODE) {
-    const fs = require('fs')
+    const fs = require("fs");
 
-    tlContent = fs.readFileSync(__dirname + '/static/api.tl', 'utf-8')
-    schemeContent = fs.readFileSync(__dirname + '/static/schema.tl', 'utf-8')
+    tlContent = fs.readFileSync(__dirname + "/static/api.tl", "utf-8");
+    schemeContent = fs.readFileSync(__dirname + "/static/schema.tl", "utf-8");
 } else {
-    tlContent = require('./static/api.tl').default
-    schemeContent = require('./static/schema.tl').default
+    tlContent = require("./static/api.tl").default;
+    schemeContent = require("./static/schema.tl").default;
 }
-const NAMED_AUTO_CASTS = new Set([
-    'chatId,int',
-])
-const NAMED_BLACKLIST = new Set([
-    'discardEncryption',
-])
+const NAMED_AUTO_CASTS = new Set(["chatId,int"]);
+const NAMED_BLACKLIST = new Set(["discardEncryption"]);
 const AUTO_CASTS = new Set([
-    'InputPeer',
-    'InputChannel',
-    'InputUser',
-    'InputDialogPeer',
-    'InputNotifyPeer',
-    'InputMedia',
-    'InputPhoto',
-    'InputMessage',
-    'InputDocument',
-    'InputChatPhoto',
-])
+    "InputPeer",
+    "InputChannel",
+    "InputUser",
+    "InputDialogPeer",
+    "InputNotifyPeer",
+    "InputMedia",
+    "InputPhoto",
+    "InputMessage",
+    "InputDocument",
+    "InputChatPhoto",
+]);
 
 class CastError extends Error {
     constructor(objectName, expected, actual, ...params) {
         // Pass remaining arguments (including vendor specific ones) to parent constructor
-        const message = 'Found wrong type for ' + objectName + '. expected ' + expected + ' but received ' + actual
-        super(message, ...params)
+        const message =
+            "Found wrong type for " +
+            objectName +
+            ". expected " +
+            expected +
+            " but received " +
+            actual;
+        super(message, ...params);
 
         // Maintains proper stack trace for where our error was thrown (only available on V8)
         if (Error.captureStackTrace) {
-            Error.captureStackTrace(this, CastError)
+            Error.captureStackTrace(this, CastError);
         }
 
-        this.name = 'CastError'
+        this.name = "CastError";
         // Custom debugging information
     }
 }
 
+const CACHING_SUPPORTED =
+    typeof self !== "undefined" && self.localStorage !== undefined;
 
-const CACHING_SUPPORTED = typeof self !== 'undefined' && self.localStorage !== undefined
-
-const CACHE_KEY = 'GramJs:apiCache'
+const CACHE_KEY = "GramJs:apiCache";
 
 function buildApiFromTlSchema() {
-    let definitions
-    const fromCache = CACHING_SUPPORTED && loadFromCache()
+    let definitions;
+    const fromCache = CACHING_SUPPORTED && loadFromCache();
 
     if (fromCache) {
-        definitions = fromCache
+        definitions = fromCache;
     } else {
-        definitions = loadFromTlSchemas()
+        definitions = loadFromTlSchemas();
 
         if (CACHING_SUPPORTED) {
-            localStorage.setItem(CACHE_KEY, JSON.stringify(definitions))
+            localStorage.setItem(CACHE_KEY, JSON.stringify(definitions));
         }
     }
-    return createClasses('all', definitions)
-
+    return createClasses("all", definitions);
 }
 
 function loadFromCache() {
-    const jsonCache = localStorage.getItem(CACHE_KEY)
-    return jsonCache && JSON.parse(jsonCache)
+    const jsonCache = localStorage.getItem(CACHE_KEY);
+    return jsonCache && JSON.parse(jsonCache);
 }
 
 function loadFromTlSchemas() {
-    const [constructorParamsApi, functionParamsApi] = extractParams(tlContent)
-    const [constructorParamsSchema, functionParamsSchema] = extractParams(schemeContent)
-    const constructors = [].concat(constructorParamsApi, constructorParamsSchema)
-    const requests = [].concat(functionParamsApi, functionParamsSchema)
-    return [].concat(constructors, requests)
+    const [constructorParamsApi, functionParamsApi] = extractParams(tlContent);
+    const [constructorParamsSchema, functionParamsSchema] =
+        extractParams(schemeContent);
+    const constructors = [].concat(
+        constructorParamsApi,
+        constructorParamsSchema
+    );
+    const requests = [].concat(functionParamsApi, functionParamsSchema);
+    return [].concat(constructors, requests);
 }
 
-
 function extractParams(fileContent) {
-    const f = parseTl(fileContent, 109)
-    const constructors = []
-    const functions = []
+    const f = parseTl(fileContent, 109);
+    const constructors = [];
+    const functions = [];
     for (const d of f) {
-        d.isFunction ? functions.push(d) : constructors.push(d)
+        d.isFunction ? functions.push(d) : constructors.push(d);
     }
-    return [constructors, functions]
+    return [constructors, functions];
 }
 
 function argToBytes(x, type) {
     switch (type) {
-    case 'int':
-        const i = Buffer.alloc(4)
-        i.writeInt32LE(x, 0)
-        return i
-    case 'long':
-        return toSignedLittleBuffer(x, 8)
-    case 'int128':
-        return toSignedLittleBuffer(x, 16)
-    case 'int256':
-        return toSignedLittleBuffer(x, 32)
-    case 'double':
-        const d = Buffer.alloc(8)
-        d.writeDoubleLE(x, 0)
-        return d
-    case 'string':
-        return serializeBytes(x)
-    case 'Bool':
-        return x ? Buffer.from('b5757299', 'hex') : Buffer.from('379779bc', 'hex')
-    case 'true':
-        return Buffer.alloc(0)
-    case 'bytes':
-        return serializeBytes(x)
-    case 'date':
-        return serializeDate(x)
-    default:
-        return x.getBytes()
+        case "int":
+            const i = Buffer.alloc(4);
+            i.writeInt32LE(x, 0);
+            return i;
+        case "long":
+            return toSignedLittleBuffer(x, 8);
+        case "int128":
+            return toSignedLittleBuffer(x, 16);
+        case "int256":
+            return toSignedLittleBuffer(x, 32);
+        case "double":
+            const d = Buffer.alloc(8);
+            d.writeDoubleLE(x, 0);
+            return d;
+        case "string":
+            return serializeBytes(x);
+        case "Bool":
+            return x
+                ? Buffer.from("b5757299", "hex")
+                : Buffer.from("379779bc", "hex");
+        case "true":
+            return Buffer.alloc(0);
+        case "bytes":
+            return serializeBytes(x);
+        case "date":
+            return serializeDate(x);
+        default:
+            return x.getBytes();
     }
 }
 
 async function getInputFromResolve(utils, client, peer, peerType) {
     switch (peerType) {
-    case 'InputPeer':
-        return utils.getInputPeer(await client.getInputEntity(peer))
-    case 'InputChannel':
-        return utils.getInputChannel(await client.getInputEntity(peer))
-    case 'InputUser':
-        return utils.getInputUser(await client.getInputEntity(peer))
-    case 'InputDialogPeer':
-        return await client._getInputDialog(peer)
-    case 'InputNotifyPeer':
-        return await client._getInputNotify(peer)
-    case 'InputMedia':
-        return utils.getInputMedia(peer)
-    case 'InputPhoto':
-        return utils.getInputPhoto(peer)
-    case 'InputMessage':
-        return utils.getInputMessage(peer)
-    case 'InputDocument':
-        return utils.getInputDocument(peer)
-    case 'InputChatPhoto':
-        return utils.getInputChatPhoto(peer)
-    case 'chatId,int' :
-        return await client.getPeerId(peer, false)
-    default:
-        throw new Error('unsupported peer type : ' + peerType)
+        case "InputPeer":
+            return utils.getInputPeer(await client.getInputEntity(peer));
+        case "InputChannel":
+            return utils.getInputChannel(await client.getInputEntity(peer));
+        case "InputUser":
+            return utils.getInputUser(await client.getInputEntity(peer));
+        case "InputDialogPeer":
+            return await client._getInputDialog(peer);
+        case "InputNotifyPeer":
+            return await client._getInputNotify(peer);
+        case "InputMedia":
+            return utils.getInputMedia(peer);
+        case "InputPhoto":
+            return utils.getInputPhoto(peer);
+        case "InputMessage":
+            return utils.getInputMessage(peer);
+        case "InputDocument":
+            return utils.getInputDocument(peer);
+        case "InputChatPhoto":
+            return utils.getInputChatPhoto(peer);
+        case "chatId,int":
+            return await client.getPeerId(peer, false);
+        default:
+            throw new Error("unsupported peer type : " + peerType);
     }
 }
 
 function getArgFromReader(reader, arg) {
     if (arg.isVector) {
         if (arg.useVectorId) {
-            reader.readInt()
+            reader.readInt();
         }
-        const temp = []
-        const len = reader.readInt()
-        arg.isVector = false
+        const temp = [];
+        const len = reader.readInt();
+        arg.isVector = false;
         for (let i = 0; i < len; i++) {
-            temp.push(getArgFromReader(reader, arg))
+            temp.push(getArgFromReader(reader, arg));
         }
-        arg.isVector = true
-        return temp
+        arg.isVector = true;
+        return temp;
     } else if (arg.flagIndicator) {
-        return reader.readInt()
+        return reader.readInt();
     } else {
         switch (arg.type) {
-        case 'int':
-            return reader.readInt()
-        case 'long':
-            return reader.readLong()
-        case 'int128':
-            return reader.readLargeInt(128)
-        case 'int256':
-            return reader.readLargeInt(256)
-        case 'double':
-            return reader.readDouble()
-        case 'string':
-            return reader.tgReadString()
-        case 'Bool':
-            return reader.tgReadBool()
-        case 'true':
-            return true
-        case 'bytes':
-            return reader.tgReadBytes()
-        case 'date':
-            return reader.tgReadDate()
-        default:
-            if (!arg.skipConstructorId) {
-                return reader.tgReadObject()
-            } else {
-                return api.constructors[arg.type].fromReader(reader)
-            }
+            case "int":
+                return reader.readInt();
+            case "long":
+                return reader.readLong();
+            case "int128":
+                return reader.readLargeInt(128);
+            case "int256":
+                return reader.readLargeInt(256);
+            case "double":
+                return reader.readDouble();
+            case "string":
+                return reader.tgReadString();
+            case "Bool":
+                return reader.tgReadBool();
+            case "true":
+                return true;
+            case "bytes":
+                return reader.tgReadBytes();
+            case "date":
+                return reader.tgReadDate();
+            default:
+                if (!arg.skipConstructorId) {
+                    return reader.tgReadObject();
+                } else {
+                    return api.constructors[arg.type].fromReader(reader);
+                }
         }
     }
 }
 
 function compareType(value, type) {
-    let correct = true
+    let correct = true;
     switch (type) {
-    case 'number':
-    case 'string':
-    case 'boolean':
-        correct = typeof value === type
-        break
-    case 'bigInt':
-        correct = bigInt.isInstance(value)
-        break
-    case 'true':
-        correct = value
-    case 'buffer':
-        correct = Buffer.isBuffer(value)
-        break
-    case 'date':
-        correct = (value && Object.prototype.toString.call(value) === '[object Date]' && !isNaN(value)) || typeof value === 'number'
-        break
-    default:
-        throw new Error('Unknown type.' + type)
+        case "number":
+        case "string":
+        case "boolean":
+            correct = typeof value === type;
+            break;
+        case "bigInt":
+            correct = bigInt.isInstance(value);
+            break;
+        case "true":
+            correct = value;
+        case "buffer":
+            correct = Buffer.isBuffer(value);
+            break;
+        case "date":
+            correct =
+                (value &&
+                    Object.prototype.toString.call(value) === "[object Date]" &&
+                    !isNaN(value)) ||
+                typeof value === "number";
+            break;
+        default:
+            throw new Error("Unknown type." + type);
     }
-    return correct
+    return correct;
 }
 
-
 function createClasses(classesType, params) {
-    const classes = {}
+    const classes = {};
     for (const classParams of params) {
-        const { name, constructorId, subclassOfId, argsConfig, namespace, isFunction, result } = classParams
-        const fullName = [namespace, name].join('.').replace(/^\./, '')
+        const {
+            name,
+            constructorId,
+            subclassOfId,
+            argsConfig,
+            namespace,
+            isFunction,
+            result,
+        } = classParams;
+        const fullName = [namespace, name].join(".").replace(/^\./, "");
 
         class VirtualClass {
-            static CONSTRUCTOR_ID = constructorId
-            static SUBCLASS_OF_ID = subclassOfId
-            static className = fullName
-            static classType = isFunction ? 'request' : 'constructor'
+            static CONSTRUCTOR_ID = constructorId;
+            static SUBCLASS_OF_ID = subclassOfId;
+            static className = fullName;
+            static classType = isFunction ? "request" : "constructor";
 
-            CONSTRUCTOR_ID = constructorId
-            SUBCLASS_OF_ID = subclassOfId
-            className = fullName
-            classType = isFunction ? 'request' : 'constructor'
+            CONSTRUCTOR_ID = constructorId;
+            SUBCLASS_OF_ID = subclassOfId;
+            className = fullName;
+            classType = isFunction ? "request" : "constructor";
 
             constructor(args) {
-                args = args || {}
+                args = args || {};
                 for (const argName in argsConfig) {
-                    if (argName === 'randomId' && !args[argName]) {
-                        this[argName] = generateRandomBigInt()
+                    if (argName === "randomId" && !args[argName]) {
+                        this[argName] = generateRandomBigInt();
                     } else {
-                        this[argName] = args[argName]
+                        this[argName] = args[argName];
                     }
                 }
             }
 
             static fromReader(reader) {
-
-                const args = {}
+                const args = {};
 
                 for (const argName in argsConfig) {
                     if (argsConfig.hasOwnProperty(argName)) {
-                        const arg = argsConfig[argName]
+                        const arg = argsConfig[argName];
                         if (arg.isFlag) {
-                            if (arg.type === 'true') {
-                                args[argName] = Boolean(args['flags'] & 1 << arg.flagIndex)
-                                continue
+                            if (arg.type === "true") {
+                                args[argName] = Boolean(
+                                    args["flags"] & (1 << arg.flagIndex)
+                                );
+                                continue;
                             }
-                            if (args['flags'] & 1 << arg.flagIndex) {
-                                args[argName] = getArgFromReader(reader, arg)
+                            if (args["flags"] & (1 << arg.flagIndex)) {
+                                args[argName] = getArgFromReader(reader, arg);
                             } else {
-                                args[argName] = null
+                                args[argName] = null;
                             }
                         } else {
                             if (arg.flagIndicator) {
-                                arg.name = 'flags'
+                                arg.name = "flags";
                             }
-                            args[argName] = getArgFromReader(reader, arg)
+                            args[argName] = getArgFromReader(reader, arg);
                         }
                     }
                 }
-                return new this(args)
+                return new this(args);
             }
 
             validate() {
                 for (const arg in argsConfig) {
                     if (argsConfig.hasOwnProperty(arg)) {
-                        const currentValue = this[arg]
-                        this.assertType(arg, argsConfig[arg], currentValue)
-
-
+                        const currentValue = this[arg];
+                        this.assertType(arg, argsConfig[arg], currentValue);
                     }
                 }
             }
 
             assertType(objectName, object, value) {
-                let expected
-                if (object['isVector']) {
+                let expected;
+                if (object["isVector"]) {
                     if (!isArrayLike(value)) {
-                        throw new CastError(objectName, 'array', value)
+                        throw new CastError(objectName, "array", value);
                     }
                     for (const o of value) {
-                        this.assertType(objectName, { ...object, isVector: false }, o)
+                        this.assertType(
+                            objectName,
+                            { ...object, isVector: false },
+                            o
+                        );
                     }
                 } else {
-
-
-                    switch (object['type']) {
-                    case 'int':
-                        expected = 'number'
-                        break
-                    case 'long':
-                    case 'int128':
-                    case 'int256':
-                    case 'double':
-                        expected = 'bigInt'
-                        break
-                    case 'string':
-                        expected = 'string'
-                        break
-                    case 'Bool':
-                        expected = 'boolean'
-                        break
-                    case 'true':
-                        expected = 'true'
-                        break
-                    case 'bytes':
-                        expected = 'buffer'
-                        break
-                    case 'date':
-                        expected = 'date'
-                        break
-                    default:
-                        expected = 'object'
+                    switch (object["type"]) {
+                        case "int":
+                            expected = "number";
+                            break;
+                        case "long":
+                        case "int128":
+                        case "int256":
+                        case "double":
+                            expected = "bigInt";
+                            break;
+                        case "string":
+                            expected = "string";
+                            break;
+                        case "Bool":
+                            expected = "boolean";
+                            break;
+                        case "true":
+                            expected = "true";
+                            break;
+                        case "bytes":
+                            expected = "buffer";
+                            break;
+                        case "date":
+                            expected = "date";
+                            break;
+                        default:
+                            expected = "object";
                     }
-                    if (expected === 'object') {
+                    if (expected === "object") {
                         // will be validated in get byte();
-
                     } else {
-                        const isCorrectType = compareType(value, expected)
+                        const isCorrectType = compareType(value, expected);
                         if (isCorrectType !== true) {
-                            throw new CastError(objectName, expected, value)
+                            throw new CastError(objectName, expected, value);
                         }
                     }
                 }
@@ -360,109 +379,153 @@ function createClasses(classesType, params) {
             getBytes() {
                 // this.validate()
 
-                const idForBytes = this.CONSTRUCTOR_ID
-                const c = Buffer.alloc(4)
-                c.writeUInt32LE(idForBytes, 0)
-                const buffers = [c]
+                const idForBytes = this.CONSTRUCTOR_ID;
+                const c = Buffer.alloc(4);
+                c.writeUInt32LE(idForBytes, 0);
+                const buffers = [c];
                 for (const arg in argsConfig) {
                     if (argsConfig.hasOwnProperty(arg)) {
                         if (argsConfig[arg].isFlag) {
-                            if (this[arg] === false || this[arg] === null || this[arg] === undefined || argsConfig[arg].type === 'true') {
-                                continue
+                            if (
+                                this[arg] === false ||
+                                this[arg] === null ||
+                                this[arg] === undefined ||
+                                argsConfig[arg].type === "true"
+                            ) {
+                                continue;
                             }
                         }
                         if (argsConfig[arg].isVector) {
                             if (argsConfig[arg].useVectorId) {
-                                buffers.push(Buffer.from('15c4b51c', 'hex'))
+                                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))))
+                            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) {
-                            if (!Object.values(argsConfig)
-                                .some(f => f.isFlag)) {
-                                buffers.push(Buffer.alloc(4))
+                            if (
+                                !Object.values(argsConfig).some((f) => f.isFlag)
+                            ) {
+                                buffers.push(Buffer.alloc(4));
                             } else {
-                                let flagCalculate = 0
+                                let flagCalculate = 0;
                                 for (const f in argsConfig) {
                                     if (argsConfig[f].isFlag) {
-                                        if (this[f] === false || this[f] === undefined || this[f] === null) {
-                                            flagCalculate |= 0
+                                        if (
+                                            this[f] === false ||
+                                            this[f] === undefined ||
+                                            this[f] === null
+                                        ) {
+                                            flagCalculate |= 0;
                                         } else {
-                                            flagCalculate |= 1 << argsConfig[f].flagIndex
+                                            flagCalculate |=
+                                                1 << argsConfig[f].flagIndex;
                                         }
                                     }
                                 }
-                                const f = Buffer.alloc(4)
-                                f.writeUInt32LE(flagCalculate, 0)
-                                buffers.push(f)
+                                const f = Buffer.alloc(4);
+                                f.writeUInt32LE(flagCalculate, 0);
+                                buffers.push(f);
                             }
                         } else {
-                            buffers.push(argToBytes(this[arg], argsConfig[arg].type))
-
-                            if (this[arg] && typeof this[arg].getBytes === 'function') {
-                                let boxed = (argsConfig[arg].type.charAt(argsConfig[arg].type.indexOf('.') + 1))
-                                boxed = boxed === boxed.toUpperCase()
+                            buffers.push(
+                                argToBytes(this[arg], argsConfig[arg].type)
+                            );
+
+                            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()
+                                    buffers.shift();
                                 }
                             }
                         }
                     }
-
                 }
-                return Buffer.concat(buffers)
+                return Buffer.concat(buffers);
             }
 
             readResult(reader) {
                 if (!isFunction) {
-                    throw new Error('`readResult()` called for non-request instance')
+                    throw new Error(
+                        "`readResult()` called for non-request instance"
+                    );
                 }
 
-                const m = result.match(/Vector<(int|long)>/)
+                const m = result.match(/Vector<(int|long)>/);
                 if (m) {
-                    reader.readInt()
-                    const temp = []
-                    const len = reader.readInt()
-                    if (m[1] === 'int') {
+                    reader.readInt();
+                    const temp = [];
+                    const len = reader.readInt();
+                    if (m[1] === "int") {
                         for (let i = 0; i < len; i++) {
-                            temp.push(reader.readInt())
+                            temp.push(reader.readInt());
                         }
                     } else {
                         for (let i = 0; i < len; i++) {
-                            temp.push(reader.readLong())
+                            temp.push(reader.readLong());
                         }
                     }
-                    return temp
+                    return temp;
                 } else {
-                    return reader.tgReadObject()
+                    return reader.tgReadObject();
                 }
             }
 
             async resolve(client, utils) {
                 if (!isFunction) {
-                    throw new Error('`resolve()` called for non-request instance')
+                    throw new Error(
+                        "`resolve()` called for non-request instance"
+                    );
                 }
                 for (const arg in argsConfig) {
                     if (argsConfig.hasOwnProperty(arg)) {
                         if (!AUTO_CASTS.has(argsConfig[arg].type)) {
-                            if (!NAMED_AUTO_CASTS.has(`${argsConfig[arg].name},${argsConfig[arg].type}`)) {
-                                continue
+                            if (
+                                !NAMED_AUTO_CASTS.has(
+                                    `${argsConfig[arg].name},${argsConfig[arg].type}`
+                                )
+                            ) {
+                                continue;
                             }
                         }
                         if (argsConfig[arg].isFlag) {
                             if (!this[arg]) {
-                                continue
+                                continue;
                             }
                         }
                         if (argsConfig[arg].isVector) {
-                            const temp = []
+                            const temp = [];
                             for (const x of this[arg]) {
-                                temp.push(await getInputFromResolve(utils, client, x, argsConfig[arg].type))
+                                temp.push(
+                                    await getInputFromResolve(
+                                        utils,
+                                        client,
+                                        x,
+                                        argsConfig[arg].type
+                                    )
+                                );
                             }
-                            this[arg] = temp
+                            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
+                            );
                         }
                     }
                 }
@@ -471,18 +534,17 @@ function createClasses(classesType, params) {
 
         if (namespace) {
             if (!classes[namespace]) {
-                classes[namespace] = {}
+                classes[namespace] = {};
             }
-            classes[namespace][name] = VirtualClass
-
+            classes[namespace][name] = VirtualClass;
         } else {
-            classes[name] = VirtualClass
+            classes[name] = VirtualClass;
         }
     }
 
-    return classes
+    return classes;
 }
 
-const api = buildApiFromTlSchema()
+const api = buildApiFromTlSchema();
 
-module.exports = { Api: api }
+module.exports = { Api: api };

+ 14 - 14
gramjs/tl/core/GZIPPacked.ts

@@ -1,10 +1,10 @@
-import {serializeBytes} from '../';
-import {inflate} from 'pako';
-import type {BinaryReader} from "../../extensions";
+import { serializeBytes } from "../";
+import { inflate } from "pako";
+import type { BinaryReader } from "../../extensions";
 
 export class GZIPPacked {
     static CONSTRUCTOR_ID = 0x3072cfa1;
-    static classType = 'constructor';
+    static classType = "constructor";
     data: Buffer;
     private CONSTRUCTOR_ID: number;
     private classType: string;
@@ -12,27 +12,27 @@ export class GZIPPacked {
     constructor(data: Buffer) {
         this.data = data;
         this.CONSTRUCTOR_ID = 0x3072cfa1;
-        this.classType = 'constructor'
+        this.classType = "constructor";
     }
 
     static async gzipIfSmaller(contentRelated: boolean, data: Buffer) {
         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
+                return gzipped;
             }
         }
-        return data
+        return data;
     }
 
     static gzip(input: Buffer) {
-        return Buffer.from(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: Buffer) {
-        return Buffer.from(inflate(input))
+        return Buffer.from(inflate(input));
     }
 
     async toBytes() {
@@ -41,19 +41,19 @@ export class GZIPPacked {
         return Buffer.concat([
             g,
             serializeBytes(await GZIPPacked.gzip(this.data)),
-        ])
+        ]);
     }
 
     static async read(reader: BinaryReader) {
         const constructor = reader.readInt(false);
         if (constructor !== GZIPPacked.CONSTRUCTOR_ID) {
-            throw new Error('not equal')
+            throw new Error("not equal");
         }
-        return await GZIPPacked.gzip(reader.tgReadBytes())
+        return await GZIPPacked.gzip(reader.tgReadBytes());
     }
 
     static async fromReader(reader: BinaryReader) {
         const data = reader.tgReadBytes();
-        return new GZIPPacked(await GZIPPacked.ungzip(data))
+        return new GZIPPacked(await GZIPPacked.ungzip(data));
     }
 }

+ 6 - 8
gramjs/tl/core/MessageContainer.ts

@@ -1,9 +1,9 @@
-import {TLMessage} from './TLMessage';
-import type {BinaryReader} from "../../extensions";
+import { TLMessage } from "./TLMessage";
+import type { BinaryReader } from "../../extensions";
 
 export class MessageContainer {
     static CONSTRUCTOR_ID = 0x73f1f8dc;
-    static classType = 'constructor';
+    static classType = "constructor";
     // Maximum size in bytes for the inner payload of the container.
     // Telegram will close the connection if the payload is bigger.
     // The overhead of the container itself is subtracted.
@@ -23,10 +23,9 @@ export class MessageContainer {
     private classType: string;
 
     constructor(messages: any[]) {
-
         this.CONSTRUCTOR_ID = 0x73f1f8dc;
         this.messages = messages;
-        this.classType = 'constructor'
+        this.classType = "constructor";
     }
 
     static async fromReader(reader: BinaryReader) {
@@ -40,9 +39,8 @@ export class MessageContainer {
             const obj = reader.tgReadObject();
             reader.setPosition(before + length);
             const tlMessage = new TLMessage(msgId, seqNo, obj);
-            messages.push(tlMessage)
+            messages.push(tlMessage);
         }
-        return new MessageContainer(messages)
+        return new MessageContainer(messages);
     }
 }
-

+ 20 - 11
gramjs/tl/core/RPCResult.ts

@@ -1,39 +1,48 @@
-import {Api} from '../api';
-import type {BinaryReader} from "../../extensions";
-import {GZIPPacked} from "./";
-
-
+import { Api } from "../api";
+import type { BinaryReader } from "../../extensions";
+import { GZIPPacked } from "./";
 
 export class RPCResult {
     static CONSTRUCTOR_ID = 0xf35c6d01;
-    static classType = 'constructor';
+    static classType = "constructor";
     private CONSTRUCTOR_ID: number;
     private reqMsgId: bigInt.BigInteger;
     private body?: Buffer;
     private error?: Api.RpcError;
     private classType: string;
 
-    constructor(reqMsgId: bigInt.BigInteger, body?: Buffer, error?: Api.RpcError) {
+    constructor(
+        reqMsgId: bigInt.BigInteger,
+        body?: Buffer,
+        error?: Api.RpcError
+    ) {
         this.CONSTRUCTOR_ID = 0xf35c6d01;
         this.reqMsgId = reqMsgId;
         this.body = body;
         this.error = error;
-        this.classType = 'constructor'
+        this.classType = "constructor";
     }
 
     static async fromReader(reader: BinaryReader) {
         const msgId = reader.readLong();
         const innerCode = reader.readInt(false);
         if (innerCode === Api.RpcError.CONSTRUCTOR_ID) {
-            return new RPCResult(msgId, undefined, Api.RpcError.fromReader(reader) as Api.RpcError)
+            return new RPCResult(
+                msgId,
+                undefined,
+                Api.RpcError.fromReader(reader) as Api.RpcError
+            );
         }
         if (innerCode === GZIPPacked.CONSTRUCTOR_ID) {
-            return new RPCResult(msgId, (await GZIPPacked.fromReader(reader)).data)
+            return new RPCResult(
+                msgId,
+                (await GZIPPacked.fromReader(reader)).data
+            );
         }
         reader.seek(-4);
         // This reader.read() will read more than necessary, but it's okay.
         // We could make use of MessageContainer's length here, but since
         // it's not necessary we don't need to care about it.
-        return new RPCResult(msgId, reader.read(), undefined)
+        return new RPCResult(msgId, reader.read(), undefined);
     }
 }

+ 4 - 5
gramjs/tl/core/TLMessage.ts

@@ -1,17 +1,16 @@
-import bigInt from "big-integer"
+import bigInt from "big-integer";
 export class TLMessage {
     static SIZE_OVERHEAD = 12;
-    static classType = 'constructor';
+    static classType = "constructor";
     msgId: bigInt.BigInteger;
     private classType: string;
     private seqNo: number;
     obj: any;
 
-    constructor(msgId: bigInt.BigInteger, seqNo:number, obj:any) {
+    constructor(msgId: bigInt.BigInteger, seqNo: number, obj: any) {
         this.msgId = msgId;
         this.seqNo = seqNo;
         this.obj = obj;
-        this.classType = 'constructor'
+        this.classType = "constructor";
     }
 }
-

+ 5 - 5
gramjs/tl/core/index.ts

@@ -1,11 +1,11 @@
-import {TLMessage} from './TLMessage';
-import {RPCResult} from './RPCResult';
-import {MessageContainer} from './MessageContainer';
-import {GZIPPacked} from './GZIPPacked';
+import { TLMessage } from "./TLMessage";
+import { RPCResult } from "./RPCResult";
+import { MessageContainer } from "./MessageContainer";
+import { GZIPPacked } from "./GZIPPacked";
 
 export const coreObjects = new Map<number, Function>([
     [RPCResult.CONSTRUCTOR_ID, RPCResult],
     [GZIPPacked.CONSTRUCTOR_ID, GZIPPacked],
     [MessageContainer.CONSTRUCTOR_ID, MessageContainer],
 ]);
-export {RPCResult, TLMessage, MessageContainer, GZIPPacked};
+export { RPCResult, TLMessage, MessageContainer, GZIPPacked };

+ 79 - 23
gramjs/tl/custom/button.ts

@@ -1,14 +1,19 @@
-import type {ButtonLike, EntityLike} from "../../define";
-import {Api} from "../api";
-import {utils} from "../../";
+import type { ButtonLike, EntityLike } from "../../define";
+import { Api } from "../api";
+import { utils } from "../../";
 
-export class Button  {
+export class Button {
     public button: ButtonLike;
     public resize: boolean | undefined;
     public selective: boolean | undefined;
     public singleUse: boolean | undefined;
 
-    constructor(button: Api.TypeKeyboardButton, resize?: boolean, singleUse?: boolean, selective?: boolean) {
+    constructor(
+        button: Api.TypeKeyboardButton,
+        resize?: boolean,
+        singleUse?: boolean,
+        selective?: boolean
+    ) {
         this.button = button;
         this.resize = resize;
         this.singleUse = singleUse;
@@ -16,9 +21,12 @@ export class Button  {
     }
 
     static _isInline(button: ButtonLike) {
-        return button instanceof Api.KeyboardButtonCallback ||
+        return (
+            button instanceof Api.KeyboardButtonCallback ||
             button instanceof Api.KeyboardButtonSwitchInline ||
-            button instanceof Api.KeyboardButtonUrl || button instanceof Api.KeyboardButtonUrlAuth
+            button instanceof Api.KeyboardButtonUrl ||
+            button instanceof Api.KeyboardButtonUrlAuth
+        );
     }
 
     static inline(text: string, data?: Buffer) {
@@ -30,13 +38,15 @@ export class Button  {
         }
         return new Api.KeyboardButtonCallback({
             text: text,
-            data: data
-        })
+            data: data,
+        });
     }
 
-    static switchInline(text: string, query = '', samePeer = false) {
+    static switchInline(text: string, query = "", samePeer = false) {
         return new Api.KeyboardButtonSwitchInline({
-            text, query, samePeer
+            text,
+            query,
+            samePeer,
         });
     }
 
@@ -44,33 +54,79 @@ export class Button  {
         return new Api.KeyboardButtonUrl({
             text: text,
             url: url || text,
-        })
+        });
     }
 
-    static auth(text: string, url?: string, bot?: EntityLike, writeAccess?: boolean, fwdText?: string) {
+    static auth(
+        text: string,
+        url?: string,
+        bot?: EntityLike,
+        writeAccess?: boolean,
+        fwdText?: string
+    ) {
         return new Api.InputKeyboardButtonUrlAuth({
             text,
             url: url || text,
             bot: utils.getInputPeer(bot || new Api.InputUserSelf()),
             requestWriteAccess: writeAccess,
-            fwdText: fwdText
-        })
+            fwdText: fwdText,
+        });
     }
 
-    static text(text: string, resize?: boolean, singleUse?: boolean, selective?: boolean) {
-        return new this(new Api.KeyboardButton({text}), resize, singleUse, selective);
+    static text(
+        text: string,
+        resize?: boolean,
+        singleUse?: boolean,
+        selective?: boolean
+    ) {
+        return new this(
+            new Api.KeyboardButton({ text }),
+            resize,
+            singleUse,
+            selective
+        );
     }
 
-    static requestLocation(text: string, resize?: boolean, singleUse?: boolean, selective?: boolean) {
-        return new this(new Api.KeyboardButtonRequestGeoLocation({text}), resize, singleUse, selective);
+    static requestLocation(
+        text: string,
+        resize?: boolean,
+        singleUse?: boolean,
+        selective?: boolean
+    ) {
+        return new this(
+            new Api.KeyboardButtonRequestGeoLocation({ text }),
+            resize,
+            singleUse,
+            selective
+        );
     }
 
-    static requestPhone(text: string, resize?: boolean, singleUse?: boolean, selective?: boolean) {
-        return new this(new Api.KeyboardButtonRequestPhone({text}), resize, singleUse, selective);
+    static requestPhone(
+        text: string,
+        resize?: boolean,
+        singleUse?: boolean,
+        selective?: boolean
+    ) {
+        return new this(
+            new Api.KeyboardButtonRequestPhone({ text }),
+            resize,
+            singleUse,
+            selective
+        );
     }
 
-    static requestPoll(text: string, resize?: boolean, singleUse?: boolean, selective?: boolean) {
-        return new this(new Api.KeyboardButtonRequestPoll({text}), resize, singleUse, selective);
+    static requestPoll(
+        text: string,
+        resize?: boolean,
+        singleUse?: boolean,
+        selective?: boolean
+    ) {
+        return new this(
+            new Api.KeyboardButtonRequestPoll({ text }),
+            resize,
+            singleUse,
+            selective
+        );
     }
 
     static clear() {

+ 33 - 23
gramjs/tl/custom/chatGetter.ts

@@ -1,7 +1,7 @@
-import type {Entity, EntityLike} from "../../define";
-import type {TelegramClient} from "../../client/TelegramClient";
-import {utils} from "../../";
-import {Api} from "../api";
+import type { Entity, EntityLike } from "../../define";
+import type { TelegramClient } from "../../client/TelegramClient";
+import { utils } from "../../";
+import { Api } from "../api";
 
 export interface ChatGetterConstructorParams {
     chatPeer?: EntityLike;
@@ -11,31 +11,41 @@ export interface ChatGetterConstructorParams {
 }
 
 export class ChatGetter {
-     _chatPeer?: EntityLike;
-     _inputChat?: EntityLike;
-     _chat?: Entity;
-     _broadcast?: boolean;
+    _chatPeer?: EntityLike;
+    _inputChat?: EntityLike;
+    _chat?: Entity;
+    _broadcast?: boolean;
     public _client?: TelegramClient;
 
-    constructor({chatPeer, inputChat, chat, broadcast}: ChatGetterConstructorParams) {
-        ChatGetter.initClass(this, {chatPeer, inputChat, chat, broadcast});
+    constructor({
+        chatPeer,
+        inputChat,
+        chat,
+        broadcast,
+    }: ChatGetterConstructorParams) {
+        ChatGetter.initClass(this, { chatPeer, inputChat, chat, broadcast });
     }
 
-    static initClass(c: any, {chatPeer, inputChat, chat, broadcast}: ChatGetterConstructorParams) {
+    static initClass(
+        c: any,
+        { chatPeer, inputChat, chat, broadcast }: ChatGetterConstructorParams
+    ) {
         c._chatPeer = chatPeer;
         c._inputChat = inputChat;
         c._chat = chat;
         c._broadcast = broadcast;
-        c._client = undefined
+        c._client = undefined;
     }
 
-
     get chat() {
         return this._chat;
     }
 
     async getChat() {
-        if (!this._chat || 'min' in this._chat && await this.getInputChat()) {
+        if (
+            !this._chat ||
+            ("min" in this._chat && (await this.getInputChat()))
+        ) {
             try {
                 if (this._inputChat) {
                     this._chat = await this._client?.getEntity(this._inputChat);
@@ -49,16 +59,15 @@ export class ChatGetter {
 
     get inputChat() {
         if (!this._inputChat && this._chatPeer && this._client) {
-            try{
+            try {
                 this._inputChat = this._client._entityCache.get(this._chatPeer);
-            }catch (e) {
-
-            }
+            } catch (e) {}
         }
         return this._inputChat;
     }
 
-    async getInputChat() {/*
+    async getInputChat() {
+        /*
         if (!this._inputChat && this.chatId && this._client) {
             try {
                 const target = this.chatId;
@@ -82,11 +91,13 @@ export class ChatGetter {
     }
 
     get isPrivate() {
-        return this._chatPeer ? this._chatPeer instanceof Api.PeerUser : undefined;
+        return this._chatPeer
+            ? this._chatPeer instanceof Api.PeerUser
+            : undefined;
     }
 
     get isGroup() {
-        if (!this._broadcast && this.chat && 'broadcast' in this.chat) {
+        if (!this._broadcast && this.chat && "broadcast" in this.chat) {
             this._broadcast = Boolean(this.chat.broadcast);
         }
         if (this._chatPeer instanceof Api.PeerChannel) {
@@ -103,6 +114,5 @@ export class ChatGetter {
         return this._chatPeer instanceof Api.PeerChannel;
     }
 
-    async _refetchChat() {
-    }
+    async _refetchChat() {}
 }

+ 40 - 30
gramjs/tl/custom/dialog.ts

@@ -1,56 +1,66 @@
-import type {TelegramClient} from "../../client/TelegramClient";
-import {Api} from "../api";
-import type {Entity} from "../../define";
-import {getDisplayName, getInputPeer, getPeerId} from "../../Utils";
-import {Draft} from "./draft";
+import type { TelegramClient } from "../../client/TelegramClient";
+import { Api } from "../api";
+import type { Entity } from "../../define";
+import { getDisplayName, getInputPeer, getPeerId } from "../../Utils";
+import { Draft } from "./draft";
+import { Message } from "./message";
 
 export class Dialog {
-    private _client: TelegramClient;
-    private dialog: Api.Dialog;
-    private pinned: boolean;
-    private folderId: Api.int | undefined;
-    private archived: boolean;
-    private message: Api.Message;
-    private date: Api.int;
-    private entity: Entity | undefined;
-    private inputEntity: Api.TypeInputPeer;
-    private id?: number;
-    private name?: string;
-    private title?: string;
-    private unreadCount: Api.int;
-    private unreadMentionsCount: Api.int;
-    private draft: Draft;
-    private isUser: boolean;
-    private isGroup: boolean;
-    private isChannel: boolean;
+    _client: TelegramClient;
+    dialog: Api.Dialog;
+    pinned: boolean;
+    folderId?: number;
+    archived: boolean;
+    message?: Api.Message | Message;
+    date: number;
+    entity?: Entity;
+    inputEntity: Api.TypeInputPeer;
+    id?: number;
+    name?: string;
+    title?: string;
+    unreadCount: number;
+    unreadMentionsCount: number;
+    draft: Draft;
+    isUser: boolean;
+    isGroup: boolean;
+    isChannel: boolean;
 
-    constructor(client: TelegramClient, dialog: Api.Dialog, entities: Map<number, Entity>, message: Api.Message) {
+    constructor(
+        client: TelegramClient,
+        dialog: Api.Dialog,
+        entities: Map<number, Entity>,
+        message?: Api.Message | Message
+    ) {
         this._client = client;
         this.dialog = dialog;
-        this.pinned = !!(dialog.pinned);
+        this.pinned = !!dialog.pinned;
         this.folderId = dialog.folderId;
         this.archived = dialog.folderId != undefined;
         this.message = message;
-        this.date = this.message.date;
+        this.date = this.message?.date;
 
         this.entity = entities.get(getPeerId(dialog.peer));
         this.inputEntity = getInputPeer(this.entity);
         if (this.entity) {
-            this.id = getPeerId(this.entity);  // ^ May be InputPeerSelf();
+            this.id = getPeerId(this.entity); // ^ May be InputPeerSelf();
             this.name = this.title = getDisplayName(this.entity);
-
         }
 
         this.unreadCount = dialog.unreadCount;
         this.unreadMentionsCount = dialog.unreadMentionsCount;
-        if (!this.entity){
+        if (!this.entity) {
             throw new Error("Entity not found for dialog");
         }
         this.draft = new Draft(client, this.entity, this.dialog.draft);
 
         this.isUser = this.entity instanceof Api.User;
-        this.isGroup = !!((this.entity instanceof Api.Chat && this.entity instanceof Api.ChatForbidden) || (this.entity instanceof Api.Channel && this.entity.megagroup));
+        this.isGroup = !!(
+            (this.entity instanceof Api.Chat &&
+                this.entity instanceof Api.ChatForbidden) ||
+            (this.entity instanceof Api.Channel && this.entity.megagroup)
+        );
         this.isChannel = this.entity instanceof Api.Channel;
     }
+
     // TODO implement rest
 }

+ 15 - 9
gramjs/tl/custom/draft.ts

@@ -1,9 +1,8 @@
-import type {Entity} from "../../define";
-import type {TelegramClient} from "../../client/TelegramClient";
-import {getInputPeer, getPeer} from "../../Utils";
-import {Api} from "../api";
-import {MarkdownParser} from "../../extensions/markdown";
-
+import type { Entity } from "../../define";
+import type { TelegramClient } from "../../client/TelegramClient";
+import { getInputPeer, getPeer } from "../../Utils";
+import { Api } from "../api";
+import { MarkdownParser } from "../../extensions/markdown";
 
 export class Draft {
     private _client: TelegramClient;
@@ -16,20 +15,27 @@ export class Draft {
     private linkPreview?: boolean;
     private replyToMsgId?: Api.int;
 
-    constructor(client: TelegramClient, entity: Entity, draft: Api.TypeDraftMessage | undefined) {
+    constructor(
+        client: TelegramClient,
+        entity: Entity,
+        draft: Api.TypeDraftMessage | undefined
+    ) {
         this._client = client;
         this._peer = getPeer(entity);
         this._entity = entity;
         this._inputEntity = entity ? getInputPeer(entity) : undefined;
         if (!draft || !(draft instanceof Api.DraftMessage)) {
             draft = new Api.DraftMessage({
-                message: '',
+                message: "",
                 date: -1,
             });
         }
         if (!(draft instanceof Api.DraftMessageEmpty)) {
             this.linkPreview = !draft.noWebpage;
-            this._text = client.parseMode.unparse(draft.message,draft.entities);
+            this._text = client.parseMode.unparse(
+                draft.message,
+                draft.entities
+            );
             this._rawText = draft.message;
             this.date = draft.date;
             this.replyToMsgId = draft.replyToMsgId;

+ 21 - 16
gramjs/tl/custom/file.ts

@@ -1,6 +1,6 @@
-import type {FileLike} from "../../define";
-import {Api} from "../api";
-import {_photoSizeByteCount} from "../../Utils";
+import type { FileLike } from "../../define";
+import { Api } from "../api";
+import { _photoSizeByteCount } from "../../Utils";
 
 export class File {
     private readonly media: FileLike;
@@ -14,46 +14,52 @@ export class File {
     }
 
     get name() {
-        return this._fromAttr(Api.DocumentAttributeFilename, 'fileName');
+        return this._fromAttr(Api.DocumentAttributeFilename, "fileName");
     }
 
     get mimeType() {
         if (this.media instanceof Api.Photo) {
-            return 'image/jpeg';
+            return "image/jpeg";
         } else if (this.media instanceof Api.Document) {
             return this.media.mimeType;
         }
     }
 
     get width() {
-        return this._fromAttr([
-            Api.DocumentAttributeImageSize, Api.DocumentAttributeVideo], 'w');
+        return this._fromAttr(
+            [Api.DocumentAttributeImageSize, Api.DocumentAttributeVideo],
+            "w"
+        );
     }
 
     get height() {
-        return this._fromAttr([
-            Api.DocumentAttributeImageSize, Api.DocumentAttributeVideo], 'h');
+        return this._fromAttr(
+            [Api.DocumentAttributeImageSize, Api.DocumentAttributeVideo],
+            "h"
+        );
     }
 
     get duration() {
-        return this._fromAttr([
-            Api.DocumentAttributeAudio, Api.DocumentAttributeVideo], 'duration');
+        return this._fromAttr(
+            [Api.DocumentAttributeAudio, Api.DocumentAttributeVideo],
+            "duration"
+        );
     }
 
     get title() {
-        return this._fromAttr(Api.DocumentAttributeAudio, 'title');
+        return this._fromAttr(Api.DocumentAttributeAudio, "title");
     }
 
     get performer() {
-        return this._fromAttr(Api.DocumentAttributeAudio, 'performer');
+        return this._fromAttr(Api.DocumentAttributeAudio, "performer");
     }
 
     get emoji() {
-        return this._fromAttr(Api.DocumentAttributeSticker, 'alt');
+        return this._fromAttr(Api.DocumentAttributeSticker, "alt");
     }
 
     get stickerSet() {
-        return this._fromAttr(Api.DocumentAttributeSticker, 'stickerset');
+        return this._fromAttr(Api.DocumentAttributeSticker, "stickerset");
     }
 
     get size() {
@@ -64,7 +70,6 @@ export class File {
         }
     }
 
-
     _fromAttr(cls: any, field: string) {
         if (this.media instanceof Api.Document) {
             for (const attr of this.media.attributes) {

+ 24 - 19
gramjs/tl/custom/forward.ts

@@ -1,16 +1,19 @@
-import {ChatGetter} from "./chatGetter";
-import {SenderGetter} from "./senderGetter";
-import {Api} from "../api";
-import type {TelegramClient} from "../../client/TelegramClient";
-import type {Entity} from "../../define";
-import {_EntityType, _entityType} from "../../Helpers";
-import {_getEntityPair, getPeerId} from "../../Utils";
-
+import { ChatGetter } from "./chatGetter";
+import { SenderGetter } from "./senderGetter";
+import { Api } from "../api";
+import type { TelegramClient } from "../../client/TelegramClient";
+import type { Entity } from "../../define";
+import { _EntityType, _entityType } from "../../Helpers";
+import { _getEntityPair, getPeerId } from "../../Utils";
 
 export class Forward {
     private originalFwd: Api.MessageFwdHeader;
 
-    constructor(client: TelegramClient, original: Api.MessageFwdHeader, entities: Map<number, Entity>) {
+    constructor(
+        client: TelegramClient,
+        original: Api.MessageFwdHeader,
+        entities: Map<number, Entity>
+    ) {
         // Copy all objects here. probably need a better way tho. PRs are welcome
         Object.assign(this, original);
         this.originalFwd = original;
@@ -25,16 +28,23 @@ export class Forward {
             const ty = _entityType(original.fromId);
             if (ty === _EntityType.USER) {
                 senderId = getPeerId(original.fromId);
-                [sender, inputSender] = _getEntityPair(senderId, entities, client._entityCache);
+                [sender, inputSender] = _getEntityPair(
+                    senderId,
+                    entities,
+                    client._entityCache
+                );
             } else if (ty === _EntityType.CHANNEL || ty === _EntityType.CHAT) {
                 peer = original.fromId;
-                [chat, inputChat] = _getEntityPair(getPeerId(peer), entities, client._entityCache);
+                [chat, inputChat] = _getEntityPair(
+                    getPeerId(peer),
+                    entities,
+                    client._entityCache
+                );
             }
         }
         ChatGetter.initClass(this, {
             chatPeer: peer,
-            inputChat: inputChat
-
+            inputChat: inputChat,
         });
         SenderGetter.initClass(this, {
             senderId: senderId,
@@ -42,12 +52,7 @@ export class Forward {
             inputSender: inputSender,
         });
         this._client = client;
-
-
     }
-
-}
-
-export interface Forward extends ChatGetter, SenderGetter {
 }
 
+export interface Forward extends ChatGetter, SenderGetter {}

+ 1 - 1
gramjs/tl/custom/index.ts

@@ -1 +1 @@
-export {ChatGetter} from "./chatGetter";
+export { ChatGetter } from "./chatGetter";

+ 31 - 18
gramjs/tl/custom/inlineResult.ts

@@ -1,26 +1,31 @@
-import type {TelegramClient} from "../../client/TelegramClient";
-import type {EntityLike, MessageIDLike} from "../../define";
-import {Api} from "../api";
-import {utils} from "../../";
+import type { TelegramClient } from "../../client/TelegramClient";
+import type { EntityLike, MessageIDLike } from "../../define";
+import { Api } from "../api";
+import { utils } from "../../";
 
 export class InlineResult {
-    ARTICLE = 'article';
-    PHOTO = 'photo';
-    GIF = 'gif';
-    VIDEO = 'video';
-    VIDEO_GIF = 'mpeg4_gif';
-    AUDIO = 'audio';
-    DOCUMENT = 'document';
-    LOCATION = 'location';
-    VENUE = 'venue';
-    CONTACT = 'contact';
-    GAME = 'game';
+    ARTICLE = "article";
+    PHOTO = "photo";
+    GIF = "gif";
+    VIDEO = "video";
+    VIDEO_GIF = "mpeg4_gif";
+    AUDIO = "audio";
+    DOCUMENT = "document";
+    LOCATION = "location";
+    VENUE = "venue";
+    CONTACT = "contact";
+    GAME = "game";
     private _entity: EntityLike | undefined;
     private _queryId: Api.long | undefined;
     private result: Api.TypeBotInlineResult;
     private _client: TelegramClient;
 
-    constructor(client: TelegramClient, original: Api.TypeBotInlineResult, queryId?: Api.long, entity?: EntityLike) {
+    constructor(
+        client: TelegramClient,
+        original: Api.TypeBotInlineResult,
+        queryId?: Api.long,
+        entity?: EntityLike
+    ) {
         this._client = client;
         this.result = original;
         this._queryId = queryId;
@@ -61,13 +66,21 @@ export class InlineResult {
         }
     }
 
-    async click(entity?: EntityLike, replyTo?: MessageIDLike, silent: boolean = false, clearDraft: boolean = false, hideVia: boolean = false) {
+    async click(
+        entity?: EntityLike,
+        replyTo?: MessageIDLike,
+        silent: boolean = false,
+        clearDraft: boolean = false,
+        hideVia: boolean = false
+    ) {
         if (entity) {
             entity = await this._client.getInputEntity(entity);
         } else if (this._entity) {
             entity = this._entity;
         } else {
-            throw new Error("You must provide the entity where the result should be sent to");
+            throw new Error(
+                "You must provide the entity where the result should be sent to"
+            );
         }
         const replyId = replyTo ? utils.getMessageId(replyTo) : undefined;
         const request = new Api.messages.SendInlineBotResult({

+ 11 - 9
gramjs/tl/custom/inlineResults.ts

@@ -1,7 +1,6 @@
-import type {TelegramClient} from "../../client/TelegramClient";
-import type {EntityLike} from "../../define";
-import {Api} from "../api";
-
+import type { TelegramClient } from "../../client/TelegramClient";
+import type { EntityLike } from "../../define";
+import { Api } from "../api";
 
 export class InlineResults<InlineResult> extends Array<InlineResult> {
     private result: Api.messages.TypeBotResults;
@@ -13,19 +12,22 @@ export class InlineResults<InlineResult> extends Array<InlineResult> {
     private nextOffset: string | undefined;
     private switchPm: Api.TypeInlineBotSwitchPM | undefined;
 
-    constructor(client: TelegramClient, original: Api.messages.TypeBotResults, entity?: EntityLike) {
+    constructor(
+        client: TelegramClient,
+        original: Api.messages.TypeBotResults,
+        entity?: EntityLike
+    ) {
         super();
         this.result = original;
         this.queryId = original.queryId;
         this.cacheTime = original.cacheTime;
-        this._validUntil = ((new Date()).getTime() / 1000) + this.cacheTime;
+        this._validUntil = new Date().getTime() / 1000 + this.cacheTime;
         this.users = <Api.TypeUser[]>original.users;
         this.gallery = Boolean(original.gallery);
         this.nextOffset = original.nextOffset;
         this.switchPm = original.switchPm;
     }
-    resultsValid(){
-        return ((new Date()).getTime() / 1000) < this._validUntil;
+    resultsValid() {
+        return new Date().getTime() / 1000 < this._validUntil;
     }
 }
-

+ 123 - 73
gramjs/tl/custom/message.ts

@@ -1,12 +1,12 @@
-import {SenderGetter} from "./senderGetter";
-import type {Entity, EntityLike} from "../../define";
-import {Api} from "../api";
-import type {TelegramClient} from "../../client/TelegramClient";
-import {ChatGetter} from "./chatGetter";
+import { SenderGetter } from "./senderGetter";
+import type { Entity, EntityLike } from "../../define";
+import { Api } from "../api";
+import type { TelegramClient } from "../../client/TelegramClient";
+import { ChatGetter } from "./chatGetter";
 import * as utils from "../../Utils";
-import {Forward} from "./forward";
-import type {File} from "./file";
-import {Mixin} from "ts-mixer";
+import { Forward } from "./forward";
+import type { File } from "./file";
+import { Mixin } from "ts-mixer";
 
 interface MessageBaseInterface {
     id: any;
@@ -40,7 +40,6 @@ interface MessageBaseInterface {
     _entities?: Map<number, Entity>;
 }
 
-
 export class Message extends Mixin(SenderGetter, ChatGetter) {
     out?: boolean;
     mentioned?: boolean;
@@ -83,32 +82,46 @@ export class Message extends Mixin(SenderGetter, ChatGetter) {
     _inputSender: any;
     _forward?: Forward;
     _sender: any;
-    _entities: Map<number, Entity>
+    _entities: Map<number, Entity>;
     patternMatch?: RegExpMatchArray;
 
-    constructor(
-        {
-            id,
-            peerId = undefined, date = undefined,
-
-            out = undefined, mentioned = undefined, mediaUnread = undefined, silent = undefined,
-            post = undefined, fromId = undefined, replyTo = undefined,
-
-            message = undefined,
-
-
-            fwdFrom = undefined, viaBotId = undefined, media = undefined, replyMarkup = undefined,
-            entities = undefined, views = undefined, editDate = undefined, postAuthor = undefined,
-            groupedId = undefined, fromScheduled = undefined, legacy = undefined,
-            editHide = undefined, pinned = undefined, restrictionReason = undefined, forwards = undefined, replies = undefined,
-
-
-            action = undefined,
-
-
-            _entities = new Map<number, Entity>(),
-        }: MessageBaseInterface) {
-        if (!id) throw new Error('id is a required attribute for Message');
+    constructor({
+        id,
+        peerId = undefined,
+        date = undefined,
+
+        out = undefined,
+        mentioned = undefined,
+        mediaUnread = undefined,
+        silent = undefined,
+        post = undefined,
+        fromId = undefined,
+        replyTo = undefined,
+
+        message = undefined,
+
+        fwdFrom = undefined,
+        viaBotId = undefined,
+        media = undefined,
+        replyMarkup = undefined,
+        entities = undefined,
+        views = undefined,
+        editDate = undefined,
+        postAuthor = undefined,
+        groupedId = undefined,
+        fromScheduled = undefined,
+        legacy = undefined,
+        editHide = undefined,
+        pinned = undefined,
+        restrictionReason = undefined,
+        forwards = undefined,
+        replies = undefined,
+
+        action = undefined,
+
+        _entities = new Map<number, Entity>(),
+    }: MessageBaseInterface) {
+        if (!id) throw new Error("id is a required attribute for Message");
         let senderId = undefined;
         if (fromId) {
             senderId = utils.getPeerId(fromId);
@@ -161,68 +174,104 @@ export class Message extends Mixin(SenderGetter, ChatGetter) {
         this._actionEntities = undefined;
 
         // Note: these calls would reset the client
-        ChatGetter.initClass(this, {chatPeer: peerId, broadcast: post});
-        SenderGetter.initClass(this, {senderId: senderId});
+        ChatGetter.initClass(this, { chatPeer: peerId, broadcast: post });
+        SenderGetter.initClass(this, { senderId: senderId });
 
-        this._forward = undefined
+        this._forward = undefined;
     }
 
-
-    _finishInit(client: TelegramClient, entities: Map<number, Entity>, inputChat?: EntityLike) {
+    _finishInit(
+        client: TelegramClient,
+        entities: Map<number, Entity>,
+        inputChat?: EntityLike
+    ) {
         this._client = client;
         const cache = client._entityCache;
         if (this.senderId) {
-            [this._sender, this._inputSender] = utils._getEntityPair(this.senderId, entities, cache);
+            [this._sender, this._inputSender] = utils._getEntityPair(
+                this.senderId,
+                entities,
+                cache
+            );
         }
         if (this.chatId) {
-            [this._chat, this._inputChat] = utils._getEntityPair(this.chatId, entities, cache);
-
+            [this._chat, this._inputChat] = utils._getEntityPair(
+                this.chatId,
+                entities,
+                cache
+            );
         }
 
-        if (inputChat) { // This has priority
+        if (inputChat) {
+            // This has priority
             this._inputChat = inputChat;
         }
 
         if (this.viaBotId) {
-            [this._viaBot, this._viaInputBot] = utils._getEntityPair(this.viaBotId, entities, cache)
+            [this._viaBot, this._viaInputBot] = utils._getEntityPair(
+                this.viaBotId,
+                entities,
+                cache
+            );
         }
 
         if (this.fwdFrom) {
-            this._forward = new Forward(this._client, this.fwdFrom, this.entities)
+            // todo fix this
+            //  this._forward = new Forward(this._client, this.fwdFrom, this.entities)
         }
 
         if (this.action) {
-            if ((this.action instanceof Api.MessageActionChatAddUser) ||
-                (this.action instanceof Api.MessageActionChatCreate)) {
-                this._actionEntities = this.action.users.map((i) => entities.get(i))
+            if (
+                this.action instanceof Api.MessageActionChatAddUser ||
+                this.action instanceof Api.MessageActionChatCreate
+            ) {
+                this._actionEntities = this.action.users.map((i) =>
+                    entities.get(i)
+                );
             } else if (this.action instanceof Api.MessageActionChatDeleteUser) {
-                this._actionEntities = [entities.get(this.action.userId)]
-            } else if (this.action instanceof Api.MessageActionChatJoinedByLink) {
-                this._actionEntities = [entities.get(utils.getPeerId(
-                    new Api.PeerChannel({channelId: this.action.inviterId}),
-                ))]
-            } else if (this.action instanceof Api.MessageActionChannelMigrateFrom) {
-                this._actionEntities = [entities.get(utils.getPeerId(
-                    new Api.PeerChat({chatId: this.action.chatId})),
-                )]
+                this._actionEntities = [entities.get(this.action.userId)];
+            } else if (
+                this.action instanceof Api.MessageActionChatJoinedByLink
+            ) {
+                this._actionEntities = [
+                    entities.get(
+                        utils.getPeerId(
+                            new Api.PeerChannel({
+                                channelId: this.action.inviterId,
+                            })
+                        )
+                    ),
+                ];
+            } else if (
+                this.action instanceof Api.MessageActionChannelMigrateFrom
+            ) {
+                this._actionEntities = [
+                    entities.get(
+                        utils.getPeerId(
+                            new Api.PeerChat({ chatId: this.action.chatId })
+                        )
+                    ),
+                ];
             }
         }
-
     }
 
     get client() {
-        return this._client
+        return this._client;
     }
 
     get text() {
         if (this._text === undefined && this._client) {
             if (!this._client.parseMode) {
-                this._text = this.message
+                this._text = this.message;
             } else {
-                this._text = this._client.parseMode.unparse(this.message, this.entities)
+                this._text = this._client.parseMode.unparse(
+                    this.message,
+                    this.entities
+                );
             }
         }
-        return this._text || '';
+        return this._text || "";
     }
 
     set text(value: string) {
@@ -231,12 +280,12 @@ export class Message extends Mixin(SenderGetter, ChatGetter) {
             [this.message, this.entities] = this._client.parseMode.parse(value);
         } else {
             this.message = value;
-            this.entities = []
+            this.entities = [];
         }
     }
 
     get rawText() {
-        return this.message
+        return this.message;
     }
 
     /**
@@ -245,7 +294,7 @@ export class Message extends Mixin(SenderGetter, ChatGetter) {
     set rawText(value: string) {
         this.message = value;
         this.entities = [];
-        this._text = ''
+        this._text = "";
     }
 
     get isReply(): boolean {
@@ -253,11 +302,11 @@ export class Message extends Mixin(SenderGetter, ChatGetter) {
     }
 
     get forward() {
-        return this._forward
+        return this._forward;
     }
 
     async _refetchSender() {
-        await this._reloadMessage()
+        await this._reloadMessage();
     }
 
     /**
@@ -270,14 +319,16 @@ export class Message extends Mixin(SenderGetter, ChatGetter) {
         let msg: Message | undefined = undefined;
         try {
             const chat = this.isChannel ? await this.getInputChat() : undefined;
-            let temp = await this._client.getMessages(chat, {ids: this.id});
+            let temp = await this._client.getMessages(chat, { ids: this.id });
             if (temp) {
                 msg = temp[0];
             }
-
         } catch (e) {
-            this._client._log.error("Got error while trying to finish init message with id " + this.id);
-            if (this._client._log.canSend('error')) {
+            this._client._log.error(
+                "Got error while trying to finish init message with id " +
+                    this.id
+            );
+            if (this._client._log.canSend("error")) {
                 console.error(e);
             }
         }
@@ -290,7 +341,7 @@ export class Message extends Mixin(SenderGetter, ChatGetter) {
         this._viaBot = msg._viaBot;
         this._viaInputBot = msg._viaInputBot;
         this._forward = msg._forward;
-        this._actionEntities = msg._actionEntities
+        this._actionEntities = msg._actionEntities;
     }
 
     /*
@@ -689,5 +740,4 @@ export class Message extends Mixin(SenderGetter, ChatGetter) {
             }*/
 }
 
-export interface Message extends ChatGetter, SenderGetter {
-}
+export interface Message extends ChatGetter, SenderGetter {}

+ 35 - 21
gramjs/tl/custom/messageButton.ts

@@ -1,7 +1,7 @@
-import type {TelegramClient} from "../../client/TelegramClient";
-import type {ButtonLike, EntityLike, MessageIDLike} from "../../define";
-import {Api} from "../api";
-import {Button} from "./button";
+import type { TelegramClient } from "../../client/TelegramClient";
+import type { ButtonLike, EntityLike, MessageIDLike } from "../../define";
+import { Api } from "../api";
+import { Button } from "./button";
 
 export class MessageButton {
     private readonly _client: TelegramClient;
@@ -10,7 +10,13 @@ export class MessageButton {
     private readonly _bot: EntityLike;
     private readonly _msgId: MessageIDLike;
 
-    constructor(client: TelegramClient, original: ButtonLike, chat: EntityLike, bot: EntityLike, msgId: MessageIDLike) {
+    constructor(
+        client: TelegramClient,
+        original: ButtonLike,
+        chat: EntityLike,
+        bot: EntityLike,
+        msgId: MessageIDLike
+    ) {
         this.button = original;
         this._bot = bot;
         this._chat = chat;
@@ -23,7 +29,7 @@ export class MessageButton {
     }
 
     get text() {
-        return !(this.button instanceof Button) ? this.button.text : '';
+        return !(this.button instanceof Button) ? this.button.text : "";
     }
 
     get data() {
@@ -44,7 +50,7 @@ export class MessageButton {
         }
     }
 
-    async click({sharePhone = false, shareGeo = [0, 0]}) {
+    async click({ sharePhone = false, shareGeo = [0, 0] }) {
         if (this.button instanceof Api.KeyboardButton) {
             return this._client.sendMessage(this._chat, {
                 message: this.button.text,
@@ -65,11 +71,13 @@ export class MessageButton {
                 throw e;
             }
         } else if (this.button instanceof Api.KeyboardButtonSwitchInline) {
-            return this._client.invoke(new Api.messages.StartBot({
-                bot: this._bot,
-                peer: this._chat,
-                startParam: this.button.query
-            }))
+            return this._client.invoke(
+                new Api.messages.StartBot({
+                    bot: this._bot,
+                    peer: this._chat,
+                    startParam: this.button.query,
+                })
+            );
         } else if (this.button instanceof Api.KeyboardButtonUrl) {
             return this.button.url;
         } else if (this.button instanceof Api.KeyboardButtonGame) {
@@ -88,25 +96,32 @@ export class MessageButton {
             }
         } else if (this.button instanceof Api.KeyboardButtonRequestPhone) {
             if (!sharePhone) {
-                throw new Error('cannot click on phone buttons unless sharePhone=true');
+                throw new Error(
+                    "cannot click on phone buttons unless sharePhone=true"
+                );
             }
 
-            const me = await this._client.getMe() as Api.User;
+            const me = (await this._client.getMe()) as Api.User;
             const phoneMedia = new Api.InputMediaContact({
-                phoneNumber: me.phone || '',
-                firstName: me.firstName || '',
-                lastName: me.lastName || '',
-                vcard: '',
+                phoneNumber: me.phone || "",
+                firstName: me.firstName || "",
+                lastName: me.lastName || "",
+                vcard: "",
             });
             throw new Error("Not supported for now");
             // TODO
             //return this._client.sendFile(this._chat, phoneMedia);
         } else if (this.button instanceof Api.InputWebFileGeoPointLocation) {
             if (!shareGeo) {
-                throw new Error("cannot click on geo buttons unless shareGeo=[longitude, latitude]");
+                throw new Error(
+                    "cannot click on geo buttons unless shareGeo=[longitude, latitude]"
+                );
             }
             let geoMedia = new Api.InputMediaGeoPoint({
-                geoPoint: new Api.InputGeoPoint({lat: shareGeo[0], long: shareGeo[1]}),
+                geoPoint: new Api.InputGeoPoint({
+                    lat: shareGeo[0],
+                    long: shareGeo[1],
+                }),
             });
             throw new Error("Not supported for now");
             // TODO
@@ -114,5 +129,4 @@ export class MessageButton {
             //return this._client.sendFile(this._chat, geoMedia);
         }
     }
-
 }

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff