Forráskód Böngészése

Remove semicolons
Add helper functions for DC
Add flood waits handling

painor 5 éve
szülő
commit
0dd66c4303
51 módosított fájl, 2220 hozzáadás és 2292 törlés
  1. 6 0
      .eslintignore
  2. 3 1
      .eslintrc.json
  3. 3 3
      .gitignore
  4. 20 21
      examples/main.js
  5. 160 59
      gramjs/client/TelegramClient.js
  6. 31 31
      gramjs/crypto/AES.js
  7. 22 22
      gramjs/crypto/AuthKey.js
  8. 30 30
      gramjs/crypto/Factorizator.js
  9. 31 31
      gramjs/crypto/RSA.js
  10. 21 21
      gramjs/errors/Common.js
  11. 5 5
      gramjs/errors/RPCBaseErrors.js
  12. 11 5
      gramjs/errors/index.js
  13. 10 13
      gramjs/extensions/AsyncQueue.js
  14. 74 74
      gramjs/extensions/BinaryReader.js
  15. 4 4
      gramjs/extensions/BinaryWriter.js
  16. 43 43
      gramjs/extensions/MessagePacker.js
  17. 3 3
      gramjs/index.js
  18. 86 86
      gramjs/network/Authenticator.js
  19. 27 27
      gramjs/network/MTProtoPlainSender.js
  20. 205 204
      gramjs/network/MTProtoSender.js
  21. 75 75
      gramjs/network/MTProtoState.js
  22. 10 10
      gramjs/network/RequestState.js
  23. 48 51
      gramjs/network/connection/Connection.js
  24. 24 24
      gramjs/network/connection/TCPFull.js
  25. 46 50
      gramjs/sessions/Session.js
  26. 15 15
      gramjs/tl/MTProtoRequest.js
  27. 16 16
      gramjs/tl/core/GZIPPacked.js
  28. 17 17
      gramjs/tl/core/MessageContainer.js
  29. 15 15
      gramjs/tl/core/RPCResult.js
  30. 6 6
      gramjs/tl/core/TLMessage.js
  31. 7 7
      gramjs/tl/core/index.js
  32. 4 4
      gramjs/tl/index.js
  33. 22 22
      gramjs/tl/tlobject.js
  34. 84 185
      gramjs/utils/Helpers.js
  35. 92 92
      gramjs_generator/docswriter.js
  36. 269 269
      gramjs_generator/generators/docs.js
  37. 32 32
      gramjs_generator/generators/errors.js
  38. 4 4
      gramjs_generator/generators/index.js
  39. 311 311
      gramjs_generator/generators/tlobject.js
  40. 29 30
      gramjs_generator/parsers/errors.js
  41. 4 4
      gramjs_generator/parsers/index.js
  42. 21 21
      gramjs_generator/parsers/methods.js
  43. 4 4
      gramjs_generator/parsers/tlobject/index.js
  44. 58 60
      gramjs_generator/parsers/tlobject/parser.js
  45. 56 56
      gramjs_generator/parsers/tlobject/tlarg.js
  46. 61 60
      gramjs_generator/parsers/tlobject/tlobject.js
  47. 19 19
      gramjs_generator/sourcebuilder.js
  48. 5 5
      gramjs_generator/utils.js
  49. 70 70
      index.js
  50. 0 69
      package-lock.json
  51. 1 6
      package.json

+ 6 - 0
.eslintignore

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

+ 3 - 1
.eslintrc.json

@@ -4,8 +4,9 @@
         "es6": true,
         "node": true
     },
-    "extends": ["google"],
+    "extends": ["google","eslint:recommended"],
     "globals": {
+        "BigInt": true,
         "Atomics": "readonly",
         "SharedArrayBuffer": "readonly"
     },
@@ -14,6 +15,7 @@
         "ecmaVersion": 2018
     },
     "rules": {
+        "semi": [1,"never"],
         "indent": ["error", 4],
         "max-len": [
             "error",

+ 3 - 3
.gitignore

@@ -6,8 +6,8 @@
 /gramjs/tl/functions/
 /gramjs/tl/types/
 /gramjs/tl/patched/
-/gramjs/tl/alltlobjects.js
-/gramjs/errors/rpcerrorlist.js
+/gramjs/tl/AllTLObjects.js
+/gramjs/errors/RPCErrorList.js
 
 # User session
 *.session
@@ -19,4 +19,4 @@ example.js
 # dotenv
 .env
 
-settings
+settings

+ 20 - 21
examples/main.js

@@ -1,39 +1,38 @@
-const {TelegramClient} = require('../gramjs');
-const log4js = require('log4js');
-const {InputPeerChat} = require('../gramjs/tl/types');
-const {SendMessageRequest} = require('../gramjs/tl/functions/messages');
-const {ResolveUsernameRequest} = require('../gramjs/tl/functions/contacts');
-const logger = log4js.getLogger('gramjs');
+const { TelegramClient } = require('../gramjs')
+const log4js = require('log4js')
+const { InputPeerChat } = require('../gramjs/tl/types')
+const { SendMessageRequest } = require('../gramjs/tl/functions/messages')
+const logger = log4js.getLogger('gramjs')
 
-logger.level = 'debug';
+logger.level = 'debug'
 
 const inputPeer = new InputPeerChat({
     chatId: 400319287,
     accessHash: 4770003194588524965n,
-});
+})
 const message = new SendMessageRequest({
     peer: inputPeer,
     message: 'hi',
     randomId: 5,
-});
+})
 console.log(message.bytes.toString('hex'));
 
 (async () => {
-    console.log('Loading interactive example...');
-    const sessionName = 'anon';
-    const apiId = 17349;
-    const apiHash = '344583e45741c457fe1862106095a5eb';
-    const client = new TelegramClient(sessionName, apiId, apiHash);
-    await client.connect();
+    console.log('Loading interactive example...')
+    const sessionName = 'anon'
+    const apiId = 17349
+    const apiHash = '344583e45741c457fe1862106095a5eb'
+    const client = new TelegramClient(sessionName, apiId, apiHash)
+    await client.connect()
 
 
-    client._authorized = true;
+    client._authorized = true
 
     const message = new SendMessageRequest({
         peer: inputPeer,
         message: 'hi from GramJS',
-    });
-    const r = await client.invoke(message);
-    console.log(r);
-    console.log('You should now be connected.');
-})();
+    })
+    const r = await client.invoke(message)
+    console.log(r)
+    console.log('You should now be connected.')
+})()

+ 160 - 59
gramjs/client/TelegramClient.js

@@ -1,18 +1,21 @@
-const log4js = require("log4js");
-const Session = require("../sessions/Session");
-const os = require('os');
-const {GetConfigRequest} = require("../tl/functions/help");
-const {LAYER} = require("../tl/alltlobjects");
-const {functions} = require("../tl");
-const MTProtoSender = require("../network/MTProtoSender");
-const {ConnectionTCPFull} = require("../network/connection/TCPFull");
-DEFAULT_DC_ID = 4;
-DEFAULT_IPV4_IP = '149.154.167.51';
-DEFAULT_IPV6_IP = '[2001:67c:4e8:f002::a]';
-DEFAULT_PORT = 443;
+const log4js = require('log4js')
+const Helpers = require('../utils/Helpers')
+const errors = require('../errors/rpcerrorlist')
+const { addKey } = require('../crypto/RSA')
+const { TLRequest } = require('../tl/tlobject')
+const Session = require('../sessions/Session')
+const os = require('os')
+const { GetConfigRequest } = require('../tl/functions/help')
+const { LAYER } = require('../tl/AllTLObjects')
+const { functions } = require('../tl')
+const MTProtoSender = require('../network/MTProtoSender')
+const { ConnectionTCPFull } = require('../network/connection/TCPFull')
+const DEFAULT_DC_ID = 4
+const DEFAULT_IPV4_IP = '149.154.167.51'
+const DEFAULT_IPV6_IP = '[2001:67c:4e8:f002::a]'
+const DEFAULT_PORT = 443
 
 class TelegramClient {
-
     static DEFAULT_OPTIONS = {
         connection: ConnectionTCPFull,
         useIPV6: false,
@@ -29,74 +32,78 @@ class TelegramClient {
         appVersion: null,
         langCode: 'en',
         systemLangCode: 'en',
-        baseLogger: null
-    };
+        baseLogger: null,
+    }
 
     constructor(sessionName, apiId, apiHash, opts = TelegramClient.DEFAULT_OPTIONS) {
         if (apiId === undefined || apiHash === undefined) {
-            throw Error("Your API ID or Hash are invalid. Please read \"Requirements\" on README.md");
+            throw Error('Your API ID or Hash are invalid. Please read "Requirements" on README.md')
         }
-        const args = {...TelegramClient.DEFAULT_OPTIONS, ...opts};
-        this.apiId = apiId;
-        this.apiHash = apiHash;
-        this._useIPV6 = args.useIPV6;
+        const args = { ...TelegramClient.DEFAULT_OPTIONS, ...opts }
+        this.apiId = apiId
+        this.apiHash = apiHash
+        this._useIPV6 = args.useIPV6
 
         if (typeof args.baseLogger == 'string') {
-            this._log = log4js.getLogger(args.baseLogger);
+            this._log = log4js.getLogger(args.baseLogger)
         } else {
-            this._log = args.baseLogger;
+            this._log = args.baseLogger
         }
-        const session = Session.tryLoadOrCreateNew(sessionName);
+        const session = Session.tryLoadOrCreateNew(sessionName)
 
-        if (!session.serverAddress || (session.serverAddress.includes(":") !== this._useIPV6)) {
+        if (!session.serverAddress || (session.serverAddress.includes(':') !== this._useIPV6)) {
             session.setDc(DEFAULT_DC_ID, this._useIPV6 ? DEFAULT_IPV6_IP : DEFAULT_IPV4_IP, DEFAULT_PORT)
         }
-        this.FloodSleepLimit = args.FloodSleepLimit;
+        this.floodSleepLimit = args.floodSleepLimit
 
-        this.session = session;
-        //this._entityCache = EntityCache();
-        this.apiId = parseInt(apiId);
-        this.apiHash = apiHash;
+        this.session = session
+        // this._entityCache = EntityCache();
+        this.apiId = parseInt(apiId)
+        this.apiHash = apiHash
 
-        this._requestRetries = args.requestRetries;
-        this._connectionRetries = args.connectionRetries;
-        this._retryDelay = args.retryDelay || 0;
+        this._requestRetries = args.requestRetries
+        this._connectionRetries = args.connectionRetries
+        this._retryDelay = args.retryDelay || 0
         if (args.proxy) {
-            this._log.warn("proxies are not supported");
+            this._log.warn('proxies are not supported')
         }
-        this._proxy = args.proxy;
-        this._timeout = args.timeout;
-        this._autoReconnect = args.autoReconnect;
+        this._proxy = args.proxy
+        this._timeout = args.timeout
+        this._autoReconnect = args.autoReconnect
 
-        this._connection = args.connection;
-        //TODO add proxy support
+        this._connection = args.connection
+        // TODO add proxy support
 
+        this._floodWaitedRequests = {}
 
         this._initWith = (x) => {
             return new functions.InvokeWithLayerRequest({
                 layer: LAYER,
                 query: new functions.InitConnectionRequest({
                     apiId: this.apiId,
-                    deviceModel: args.deviceModel | os.type() | "Unkown",
+                    deviceModel: args.deviceModel | os.type() | 'Unkown',
                     systemVersion: args.systemVersion | os.release() | '1.0',
-                    appVersion: args.appVersion | this.__version__,
+                    appVersion: args.appVersion | '1.0',
                     langCode: args.langCode,
-                    langPack: "", //this should be left empty.
+                    langPack: '', // this should be left empty.
                     systemLangCode: args.systemVersion,
                     query: x,
                     proxy: null, // no proxies yet.
-                })
+                }),
             })
-        };
-        //These will be set later
+        }
+        // These will be set later
+        this._config = null
+
         this._sender = new MTProtoSender(this.session.authKey, {
             logger: this._log,
-        });
-        this.phoneCodeHashes = Array();
-
+        })
+        this.phoneCodeHashes = []
     }
 
 
+    // region Connecting
+
     /**
      * Connects to the Telegram servers, executing authentication if required.
      * Note that authenticating to the Telegram servers is not the same as authenticating
@@ -104,16 +111,16 @@ class TelegramClient {
      * @returns {Promise<void>}
      */
     async connect() {
-        const connection = new this._connection(this.session.serverAddress, this.session.port, this.session.dcId, this._log);
+        const connection = new this._connection(this.session.serverAddress
+            , this.session.port, this.session.dcId, this._log)
         if (!await this._sender.connect(connection)) {
-            return;
+            return
         }
-        this.session.authKey = this._sender.authKey;
-        await this.session.save();
+        this.session.authKey = this._sender.authKey
+        await this.session.save()
         await this._sender.send(this._initWith(
-            new GetConfigRequest()
-        ));
-
+            new GetConfigRequest(),
+        ))
     }
 
 
@@ -123,10 +130,55 @@ class TelegramClient {
      */
     async disconnect() {
         if (this._sender) {
-            await this._sender.disconnect();
+            await this._sender.disconnect()
         }
     }
 
+
+    async _switchDC(newDc) {
+        this._log.info(`Reconnecting to new data center ${newDc}`)
+        const DC = this._getDC(newDc)
+
+        this.session.setDC(DC.id, DC.ipAddress, DC.port)
+        // authKey's are associated with a server, which has now changed
+        // so it's not valid anymore. Set to None to force recreating it.
+        this._sender.authKey.key = null
+        this.session.authKey = null
+        await this.session.save()
+        await this.disconnect()
+        return await this.connect()
+    }
+
+    async _authKeyCallback(authKey) {
+        this.session.auth_key = authKey
+        await this.session.save()
+    }
+
+    // endregion
+
+    // region Working with different connections/Data Centers
+
+    async _getDC(dcId, cdn = false) {
+        if (!this._config) {
+            this._config = await this.invoke(new functions.help.GetConfigRequest())
+        }
+
+        if (cdn && !this._cdnConfig) {
+            this._cdnConfig = await this.invoke(new functions.help.GetCdnConfigRequest())
+            for (const pk of this._cdnConfig.publicKeys) {
+                addKey(pk.publicKey)
+            }
+        }
+        for (const DC of this._config.dcOptions) {
+            if (DC.id === dcId && DC.ipv6 === this._useIPV6 && DC.cdn === cdn) {
+                return DC
+            }
+        }
+    }
+
+    // endregion
+
+    // region Invoking Telegram request
     /**
      * Invokes a MTProtoRequest (sends and receives it) and returns its result
      * @param request
@@ -134,12 +186,61 @@ class TelegramClient {
      */
     async invoke(request) {
         if (!(request instanceof TLRequest)) {
-            throw new Error("You can only invoke MTProtoRequests");
+            throw new Error('You can only invoke MTProtoRequests')
+        }
+        if (request.CONSTRUCTOR_ID in this._floodWaitedRequests) {
+            const due = this._floodWaitedRequests[request.CONSTRUCTOR_ID]
+            const diff = Math.round(due - new Date().getTime() / 1000)
+            if (diff <= 3) {
+                delete this._floodWaitedRequests[request.CONSTRUCTOR_ID]
+            } else if (diff <= this.floodSleepLimit) {
+                this._log.info(`Sleeping early for ${diff}s on flood wait`)
+                await Helpers.sleep(diff)
+                delete this._floodWaitedRequests[request.CONSTRUCTOR_ID]
+            } else {
+                throw new errors.FloodWaitError({
+                    request: request,
+                    capture: diff,
+                })
+            }
+        }
+        this._last_request = new Date().getTime()
+        let attempt = 0
+        for (attempt = 0; attempt < this._requestRetries; attempt++) {
+            try {
+                const promise = this._sender.send(request)
+                const result = await promise
+                this.session.processEntities(result)
+                this._entityCache.add(result)
+                return result
+            } catch (e) {
+                if (e instanceof errors.ServerError || e instanceof errors.RpcCallFailError ||
+                    e instanceof errors.RpcMcgetFailError) {
+                    this._log.warn(`Telegram is having internal issues ${e.constructor.name}`)
+                    await Helpers.sleep(2)
+                } else if (e instanceof errors.FloodWaitError || e instanceof errors.FloodTestPhoneWaitError) {
+                    this._floodWaitedRequests = new Date().getTime() / 1000 + e.seconds
+                    if (e.seconds <= this.floodSleepLimit) {
+                        this._log.info(`Sleeping for ${e.seconds}s on flood wait`)
+                        await Helpers.sleep(e.seconds)
+                    } else {
+                        throw e
+                    }
+                } else if (e instanceof errors.PhoneMigrateError || e instanceof errors.NetworkMigrateError ||
+                    e instanceof errors.UserMigrateError) {
+                    this._log.info(`Phone migrated to ${e.newDc}`)
+                    const shouldRaise = e instanceof errors.PhoneMigrateError || e instanceof errors.NetworkMigrateError
+                    if (shouldRaise && await this.isUserAuthorized()) {
+                        throw e
+                    }
+                    await this._switchDC(e.newDc)
+                }
+            }
         }
-        return this._sender.send(request);
+        throw new Error(`Request was unsuccessful ${attempt} time(s)`)
     }
 
-
+    // endregion
 }
 
-module.exports = TelegramClient;
+module.exports = TelegramClient

+ 31 - 31
gramjs/crypto/AES.js

@@ -1,5 +1,5 @@
-const aesjs = require('aes-js');
-const Helpers = require('../utils/Helpers');
+const aesjs = require('aes-js')
+const Helpers = require('../utils/Helpers')
 
 class AES {
     /**
@@ -10,31 +10,31 @@ class AES {
      * @returns {Buffer}
      */
     static decryptIge(cipherText, key, iv) {
-        let iv1 = iv.slice(0, Math.floor(iv.length / 2));
-        let iv2 = iv.slice(Math.floor(iv.length / 2));
-        let plainText = new Array(cipherText.length).fill(0);
-        const aes = new aesjs.AES(key);
-        const blocksCount = Math.floor(plainText.length / 16);
-        const cipherTextBlock = new Array(16).fill(0);
+        let iv1 = iv.slice(0, Math.floor(iv.length / 2))
+        let iv2 = iv.slice(Math.floor(iv.length / 2))
+        let plainText = new Array(cipherText.length).fill(0)
+        const aes = new aesjs.AES(key)
+        const blocksCount = Math.floor(plainText.length / 16)
+        const cipherTextBlock = new Array(16).fill(0)
 
         for (let blockIndex = 0; blockIndex < blocksCount; blockIndex++) {
             for (let i = 0; i < 16; i++) {
-                cipherTextBlock[i] = cipherText[blockIndex * 16 + i] ^ iv2[i];
+                cipherTextBlock[i] = cipherText[blockIndex * 16 + i] ^ iv2[i]
             }
-            const plainTextBlock = aes.decrypt(cipherTextBlock);
+            const plainTextBlock = aes.decrypt(cipherTextBlock)
             for (let i = 0; i < 16; i++) {
-                plainTextBlock[i] ^= iv1[i];
+                plainTextBlock[i] ^= iv1[i]
             }
 
-            iv1 = cipherText.slice(blockIndex * 16, blockIndex * 16 + 16);
-            iv2 = plainTextBlock.slice(0, 16);
+            iv1 = cipherText.slice(blockIndex * 16, blockIndex * 16 + 16)
+            iv2 = plainTextBlock.slice(0, 16)
             plainText = new Uint8Array([
                 ...plainText.slice(0, blockIndex * 16),
                 ...plainTextBlock.slice(0, 16),
                 ...plainText.slice(blockIndex * 16 + 16),
-            ]);
+            ])
         }
-        return Buffer.from(plainText);
+        return Buffer.from(plainText)
     }
 
     /**
@@ -45,36 +45,36 @@ class AES {
      * @returns {Buffer}
      */
     static encryptIge(plainText, key, iv) {
-        const padding = plainText.length % 16;
+        const padding = plainText.length % 16
         if (padding) {
-            plainText = Buffer.concat([plainText, Helpers.generateRandomBytes(16 - padding)]);
+            plainText = Buffer.concat([plainText, Helpers.generateRandomBytes(16 - padding)])
         }
 
-        let iv1 = iv.slice(0, Math.floor(iv.length / 2));
-        let iv2 = iv.slice(Math.floor(iv.length / 2));
+        let iv1 = iv.slice(0, Math.floor(iv.length / 2))
+        let iv2 = iv.slice(Math.floor(iv.length / 2))
 
-        const aes = new aesjs.AES(key);
-        let cipherText = Buffer.alloc(0);
-        const blockCount = Math.floor(plainText.length / 16);
+        const aes = new aesjs.AES(key)
+        let cipherText = Buffer.alloc(0)
+        const blockCount = Math.floor(plainText.length / 16)
 
         for (let blockIndex = 0; blockIndex < blockCount; blockIndex++) {
-            const plainTextBlock = Buffer.from(plainText.slice(blockIndex * 16, blockIndex * 16 + 16));
+            const plainTextBlock = Buffer.from(plainText.slice(blockIndex * 16, blockIndex * 16 + 16))
 
             for (let i = 0; i < 16; i++) {
-                plainTextBlock[i] ^= iv1[i];
+                plainTextBlock[i] ^= iv1[i]
             }
-            const cipherTextBlock = Buffer.from(aes.encrypt(plainTextBlock));
+            const cipherTextBlock = Buffer.from(aes.encrypt(plainTextBlock))
 
             for (let i = 0; i < 16; i++) {
-                cipherTextBlock[i] ^= iv2[i];
+                cipherTextBlock[i] ^= iv2[i]
             }
 
-            iv1 = cipherTextBlock;
-            iv2 = plainText.slice(blockIndex * 16, blockIndex * 16 + 16);
-            cipherText = Buffer.concat([cipherText, cipherTextBlock]);
+            iv1 = cipherTextBlock
+            iv2 = plainText.slice(blockIndex * 16, blockIndex * 16 + 16)
+            cipherText = Buffer.concat([cipherText, cipherTextBlock])
         }
-        return cipherText;
+        return cipherText
     }
 }
 
-module.exports = AES;
+module.exports = AES

+ 22 - 22
gramjs/crypto/AuthKey.js

@@ -1,32 +1,32 @@
-const Helpers = require('../utils/Helpers');
-const BinaryReader = require('../extensions/BinaryReader');
-const struct = require('python-struct');
+const Helpers = require('../utils/Helpers')
+const BinaryReader = require('../extensions/BinaryReader')
+const struct = require('python-struct')
 
 class AuthKey {
     constructor(data) {
-        this.key = data;
+        this.key = data
     }
 
     set key(value) {
         if (!value) {
-            this._key = this.auxHash = this.keyId = null;
-            return;
+            this._key = this.auxHash = this.keyId = null
+            return
         }
         if (value instanceof AuthKey) {
-            this._key = value._key;
-            this.auxHash = value.auxHash;
-            this.keyId = value.keyId;
-            return;
+            this._key = value._key
+            this.auxHash = value.auxHash
+            this.keyId = value.keyId
+            return
         }
-        this._key = value;
-        const reader = new BinaryReader(Helpers.sha1(this._key));
-        this.auxHash = reader.readLong(false);
-        reader.read(4);
-        this.keyId = reader.readLong(false);
+        this._key = value
+        const reader = new BinaryReader(Helpers.sha1(this._key))
+        this.auxHash = reader.readLong(false)
+        reader.read(4)
+        this.keyId = reader.readLong(false)
     }
 
     get key() {
-        return this._key;
+        return this._key
     }
 
     // TODO : This doesn't really fit here, it's only used in authentication
@@ -38,17 +38,17 @@ class AuthKey {
      * @returns {bigint}
      */
     calcNewNonceHash(newNonce, number) {
-        newNonce = Helpers.readBufferFromBigInt(newNonce, 32, true, true);
-        const data = Buffer.concat([newNonce, struct.pack('<BQ', number.toString(), this.auxHash.toString())]);
+        newNonce = Helpers.readBufferFromBigInt(newNonce, 32, true, true)
+        const data = Buffer.concat([newNonce, struct.pack('<BQ', number.toString(), this.auxHash.toString())])
 
         // Calculates the message key from the given data
-        const shaData = Helpers.sha1(data).slice(4, 20);
-        return Helpers.readBigIntFromBuffer(shaData, true, true);
+        const shaData = Helpers.sha1(data).slice(4, 20)
+        return Helpers.readBigIntFromBuffer(shaData, true, true)
     }
 
     equals(other) {
-        return other instanceof this.constructor && other.key === this._key;
+        return other instanceof this.constructor && other.key === this._key
     }
 }
 
-module.exports = AuthKey;
+module.exports = AuthKey

+ 30 - 30
gramjs/crypto/Factorizator.js

@@ -1,4 +1,4 @@
-const Helpers = require('../utils/Helpers');
+const Helpers = require('../utils/Helpers')
 
 class Factorizator {
     /**
@@ -7,51 +7,51 @@ class Factorizator {
      * @return {BigInt}
      */
     static findSmallMultiplierLopatin(what) {
-        let g = 0n;
+        let g = 0n
         for (let i = 0n; i < 3n; i++) {
-            const q = 30n || (Helpers.getRandomInt(0, 127) & 15) + 17;
-            let x = 40n || Helpers.getRandomInt(0, 1000000000) + 1;
+            const q = 30n || (Helpers.getRandomInt(0, 127) & 15) + 17
+            let x = 40n || Helpers.getRandomInt(0, 1000000000) + 1
 
-            let y = x;
-            const lim = 1n << (i + 18n);
+            let y = x
+            const lim = 1n << (i + 18n)
             for (let j = 1n; j < lim; j++) {
-                let a = x;
-                let b = x;
+                let a = x
+                let b = x
 
-                let c = q;
+                let c = q
                 while (b !== 0n) {
                     if (BigInt(b & 1n) !== 0n) {
-                        c += a;
+                        c += a
                         if (c >= what) {
-                            c -= what;
+                            c -= what
                         }
                     }
-                    a += a;
+                    a += a
                     if (a >= what) {
-                        a -= what;
+                        a -= what
                     }
-                    b >>= 1n;
+                    b >>= 1n
                 }
 
-                x = c;
-                const z = BigInt(x < y ? y - x : x - y);
-                g = this.gcd(z, what);
+                x = c
+                const z = BigInt(x < y ? y - x : x - y)
+                g = this.gcd(z, what)
 
                 if (g !== 1n) {
-                    break;
+                    break
                 }
 
                 if ((j & (j - 1n)) === 0n) {
-                    y = x;
+                    y = x
                 }
             }
             if (g > 1) {
-                break;
+                break
             }
         }
-        const p = what / g;
+        const p = what / g
 
-        return p < g ? p : g;
+        return p < g ? p : g
     }
 
     /**
@@ -63,18 +63,18 @@ class Factorizator {
     static gcd(a, b) {
         while (a !== 0n && b !== 0n) {
             while ((b & 1n) === 0n) {
-                b >>= 1n;
+                b >>= 1n
             }
             while ((a & 1n) === 0n) {
-                a >>= 1n;
+                a >>= 1n
             }
             if (a > b) {
-                a -= b;
+                a -= b
             } else {
-                b -= a;
+                b -= a
             }
         }
-        return b === 0n ? a : b;
+        return b === 0n ? a : b
     }
 
     /**
@@ -83,9 +83,9 @@ class Factorizator {
      * @returns {{p: BigInt, q: BigInt}}
      */
     static factorize(pq) {
-        const divisor = this.findSmallMultiplierLopatin(pq);
-        return { p: divisor, q: pq / divisor };
+        const divisor = this.findSmallMultiplierLopatin(pq)
+        return { p: divisor, q: pq / divisor }
     }
 }
 
-module.exports = Factorizator;
+module.exports = Factorizator

+ 31 - 31
gramjs/crypto/RSA.js

@@ -1,7 +1,7 @@
-const NodeRSA = require('node-rsa');
-const { TLObject } = require('../tl/tlobject');
-const Helpers = require('../utils/Helpers');
-const _serverKeys = {};
+const NodeRSA = require('node-rsa')
+const { TLObject } = require('../tl/tlobject')
+const Helpers = require('../utils/Helpers')
+const _serverKeys = {}
 
 /**
  * Gets the arbitrary-length byte array corresponding to the given integer
@@ -10,46 +10,46 @@ const _serverKeys = {};
  * @returns {Buffer}
  */
 function getByteArray(integer, signed = false) {
-    const { length: bits } = integer.toString(2);
-    const byteLength = Math.floor((bits + 8 - 1) / 8);
-    return Helpers.readBufferFromBigInt(BigInt(integer), byteLength, false, signed);
+    const { length: bits } = integer.toString(2)
+    const byteLength = Math.floor((bits + 8 - 1) / 8)
+    return Helpers.readBufferFromBigInt(BigInt(integer), byteLength, false, signed)
 }
 
 function _computeFingerprint(key) {
-    const buf = Helpers.readBigIntFromBuffer(key.keyPair.n.toBuffer(), false);
-    const nArray = getByteArray(buf);
+    const buf = Helpers.readBigIntFromBuffer(key.keyPair.n.toBuffer(), false)
+    const nArray = getByteArray(buf)
 
-    const n = TLObject.serializeBytes(nArray);
-    const e = TLObject.serializeBytes(getByteArray(key.keyPair.e));
+    const n = TLObject.serializeBytes(nArray)
+    const e = TLObject.serializeBytes(getByteArray(key.keyPair.e))
     // Telegram uses the last 8 bytes as the fingerprint
-    const sh = Helpers.sha1(Buffer.concat([n, e]));
-    return Helpers.readBigIntFromBuffer(sh.slice(-8), true, true);
+    const sh = Helpers.sha1(Buffer.concat([n, e]))
+    return Helpers.readBigIntFromBuffer(sh.slice(-8), true, true)
 }
 
 function addKey(pub) {
-    const key = new NodeRSA(pub);
-    _serverKeys[_computeFingerprint(key)] = key;
+    const key = new NodeRSA(pub)
+    _serverKeys[_computeFingerprint(key)] = key
 }
 
 function encrypt(fingerprint, data) {
-    const key = _serverKeys[fingerprint];
+    const key = _serverKeys[fingerprint]
     if (!key) {
-        return undefined;
+        return undefined
     }
-    const buf = Helpers.readBigIntFromBuffer(key.keyPair.n.toBuffer(), false);
-    let rand = Helpers.generateRandomBytes(235 - data.length);
+    const buf = Helpers.readBigIntFromBuffer(key.keyPair.n.toBuffer(), false)
+    let rand = Helpers.generateRandomBytes(235 - data.length)
     rand = Buffer.from(
         '66a6f809e0dfd71d9dbbc2d6b5fe5fc0be9f5b2b0f2f85688843eea6b2c6d51329750f020c8de27a0a911b07d2a46600493d1abb7caf24' +
-            '01ccd815d7de7c5ea830cdf6cce8bff12f77db589f233bce436b644c3415f16d073335fdadfe313c603485b3274e8fcd148fd1a5e18bd2' +
-            '4b3e983df94d58b61c150333ab8d614101e7a904dc38af3a3b29e73d62',
-        'hex'
-    );
+        '01ccd815d7de7c5ea830cdf6cce8bff12f77db589f233bce436b644c3415f16d073335fdadfe313c603485b3274e8fcd148fd1a5e18bd2' +
+        '4b3e983df94d58b61c150333ab8d614101e7a904dc38af3a3b29e73d62',
+        'hex',
+    )
 
-    const toEncrypt = Buffer.concat([Helpers.sha1(data), data, rand]);
-    const payload = Helpers.readBigIntFromBuffer(toEncrypt, false);
-    const encrypted = Helpers.modExp(payload, BigInt(key.keyPair.e), buf);
-    const block = Helpers.readBufferFromBigInt(encrypted, 256, false);
-    return block;
+    const toEncrypt = Buffer.concat([Helpers.sha1(data), data, rand])
+    const payload = Helpers.readBigIntFromBuffer(toEncrypt, false)
+    const encrypted = Helpers.modExp(payload, BigInt(key.keyPair.e), buf)
+    const block = Helpers.readBufferFromBigInt(encrypted, 256, false)
+    return block
 }
 
 const publicKeys = [
@@ -88,9 +88,9 @@ qAqBdmI1iBGdQv/OQCBcbXIWCGDY2AsiqLhlGQfPOI7/vvKc188rTriocgUtoTUc
 WV9IMQmZmyZh/N7sMbGWQpt4NMchGpPGeJ2e5gHBjDnlIf2p1yZOYeUYrdbwcS0t
 UiggS4UeE8TzIuXFQxw7fzEIlmhIaq3FnwIDAQAB
 -----END RSA PUBLIC KEY-----`,
-];
+]
 for (const pub of publicKeys) {
-    addKey(pub);
+    addKey(pub)
 }
 
-module.exports = { encrypt };
+module.exports = { encrypt, addKey }

+ 21 - 21
gramjs/errors/Common.js

@@ -2,14 +2,14 @@
  * Errors not related to the Telegram API itself
  */
 
-const struct = require('python-struct');
+const struct = require('python-struct')
 
 /**
  * Occurs when a read operation was cancelled.
  */
 class ReadCancelledError extends Error {
     constructor() {
-        super('The read operation was cancelled.');
+        super('The read operation was cancelled.')
     }
 }
 
@@ -21,9 +21,9 @@ class TypeNotFoundError extends Error {
     constructor(invalidConstructorId, remaining) {
         super(`Could not find a matching Constructor ID for the TLObject that was supposed to be
         read with ID ${invalidConstructorId}. Most likely, a TLObject was trying to be read when
-         it should not be read. Remaining bytes: ${remaining}`);
-        this.invalidConstructorId = invalidConstructorId;
-        this.remaining = remaining;
+         it should not be read. Remaining bytes: ${remaining}`)
+        this.invalidConstructorId = invalidConstructorId
+        this.remaining = remaining
     }
 }
 
@@ -33,9 +33,9 @@ class TypeNotFoundError extends Error {
  */
 class InvalidChecksumError extends Error {
     constructor(checksum, validChecksum) {
-        super(`Invalid checksum (${checksum} when ${validChecksum} was expected). This packet should be skipped.`);
-        this.checksum = checksum;
-        this.validChecksum = validChecksum;
+        super(`Invalid checksum (${checksum} when ${validChecksum} was expected). This packet should be skipped.`)
+        this.checksum = checksum
+        this.validChecksum = validChecksum
     }
 }
 
@@ -45,15 +45,15 @@ class InvalidChecksumError extends Error {
  */
 class InvalidBufferError extends Error {
     constructor(payload) {
-        const [code] = -struct.unpack('<i', payload);
+        const [code] = -struct.unpack('<i', payload)
         if (payload.length === 4) {
-            super(`Invalid response buffer (HTTP code ${code})`);
+            super(`Invalid response buffer (HTTP code ${code})`)
         } else {
-            super(`Invalid response buffer (too short ${payload})`);
-            this.code = null;
+            super(`Invalid response buffer (too short ${payload})`)
+            this.code = null
         }
-        this.code = code;
-        this.payload = payload;
+        this.code = code
+        this.payload = payload
     }
 }
 
@@ -63,9 +63,9 @@ class InvalidBufferError extends Error {
 class SecurityError extends Error {
     constructor(...args) {
         if (!args.length) {
-            args = ['A security check failed.'];
+            args = ['A security check failed.']
         }
-        super(...args);
+        super(...args)
     }
 }
 
@@ -75,7 +75,7 @@ class SecurityError extends Error {
  */
 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.')
     }
 }
 
@@ -84,7 +84,7 @@ class CdnFileTamperedError extends SecurityError {
  */
 class AlreadyInConversationError extends Error {
     constructor() {
-        super('Cannot open exclusive conversation in a chat that already has one open conversation');
+        super('Cannot open exclusive conversation in a chat that already has one open conversation')
     }
 }
 
@@ -133,8 +133,8 @@ class BadMessageError extends Error {
     };
 
     constructor(code) {
-        super(BadMessageError.ErrorMessages[code] || `Unknown error code (this should not happen): ${code}.`);
-        this.code = code;
+        super(BadMessageError.ErrorMessages[code] || `Unknown error code (this should not happen): ${code}.`)
+        this.code = code
     }
 }
 
@@ -149,4 +149,4 @@ module.exports = {
     CdnFileTamperedError,
     AlreadyInConversationError,
     BadMessageError,
-};
+}

+ 5 - 5
gramjs/errors/RPCBaseErrors.js

@@ -8,14 +8,14 @@ class RPCError extends Error {
                 .replace('{0}', code)
                 .replace('{1}', message)
                 .replace('{2}', RPCError._fmtRequest(request))
-        );
-        this.code = code;
-        this.message = message;
+        )
+        this.code = code
+        this.message = message
     }
 
     static _fmtRequest(request) {
         // TODO fix this
-        return ` (caused by ${request})`;
+        return ` (caused by ${request})`
     }
 }
 
@@ -113,4 +113,4 @@ module.exports = {
     FloodError,
     ServerError,
     TimedOutError,
-};
+}

+ 11 - 5
gramjs/errors/index.js

@@ -4,19 +4,25 @@
  * @param request the request that caused this error
  * @constructor the RPCError as a Python exception that represents this error
  */
-const { rpcErrorsObject } = require('./rpcerrorlist');
+const { rpcErrorsObject } = require('./rpcerrorlist')
 
 function RPCMessageToError(rpcError, request) {
     // Try to get the error by direct look-up, otherwise regex
-    const cls = rpcErrorsObject[rpcError.errorMessage];
+    const cls = rpcErrorsObject[rpcError.errorMessage]
     if (cls) {
         // eslint-disable-next-line new-cap
-        return new cls(request);
+        return new cls(request)
     } else {
-        return rpcError.errorMessage;
+        return rpcError.errorMessage
     }
 }
 
 module.exports = {
     RPCMessageToError,
-};
+}
+module.exports = {
+    ...module.exports,
+    ...require('./Common'),
+    ...require('./RPCBaseErrors'),
+    ...require('./rpcerrorlist'),
+}

+ 10 - 13
gramjs/extensions/AsyncQueue.js

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

+ 74 - 74
gramjs/extensions/BinaryReader.js

@@ -1,8 +1,8 @@
-const { unpack } = require('python-struct');
-const { TypeNotFoundError } = require('../errors/Common');
-const { coreObjects } = require('../tl/core');
-const { tlobjects } = require('../tl/alltlobjects');
-const Helpers = require('../utils/Helpers');
+const { unpack } = require('python-struct')
+const { TypeNotFoundError } = require('../errors/Common')
+const { coreObjects } = require('../tl/core')
+const { tlobjects } = require('../tl/AllTLObjects')
+const Helpers = require('../utils/Helpers')
 
 class BinaryReader {
     /**
@@ -10,9 +10,9 @@ class BinaryReader {
      * @param data {Buffer}
      */
     constructor(data) {
-        this.stream = data;
-        this._last = null;
-        this.offset = 0;
+        this.stream = data
+        this._last = null
+        this.offset = 0
     }
 
     // region Reading
@@ -23,7 +23,7 @@ class BinaryReader {
      * Reads a single byte value.
      */
     readByte() {
-        return this.read(1)[0];
+        return this.read(1)[0]
     }
 
     /**
@@ -31,14 +31,14 @@ class BinaryReader {
      * @param signed {Boolean}
      */
     readInt(signed = true) {
-        let res;
+        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;
+        this.offset += 4
+        return res
     }
 
     /**
@@ -47,15 +47,15 @@ class BinaryReader {
      * @returns {bigint}
      */
     readLong(signed = true) {
-        let res;
+        let res
         if (signed) {
-            res = this.stream.readBigInt64LE(this.offset);
+            res = this.stream.readBigInt64LE(this.offset)
         } else {
-            res = this.stream.readBigUInt64LE(this.offset);
+            res = this.stream.readBigUInt64LE(this.offset)
         }
-        this.offset += 8;
+        this.offset += 8
 
-        return res;
+        return res
     }
 
     /**
@@ -63,7 +63,7 @@ class BinaryReader {
      * @returns {number}
      */
     readFloat() {
-        return unpack('<f', this.read(4))[0];
+        return unpack('<f', this.read(4))[0]
     }
 
     /**
@@ -71,7 +71,7 @@ class BinaryReader {
      * @returns {BigInt}
      */
     readDouble() {
-        return unpack('<f', this.read(8))[0];
+        return unpack('<f', this.read(8))[0]
     }
 
     /**
@@ -80,8 +80,8 @@ class BinaryReader {
      * @param signed {Boolean}
      */
     readLargeInt(bits, signed = true) {
-        const buffer = this.read(Math.floor(bits / 8));
-        return Helpers.readBigIntFromBuffer(buffer, true, signed);
+        const buffer = this.read(Math.floor(bits / 8))
+        return Helpers.readBigIntFromBuffer(buffer, true, signed)
     }
 
     /**
@@ -90,17 +90,17 @@ 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;
+        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}`
-            );
+            )
         }
-        this._last = result;
-        return result;
+        this._last = result
+        return result
     }
 
     /**
@@ -108,7 +108,7 @@ class BinaryReader {
      * @returns {Buffer}
      */
     getBuffer() {
-        return this.stream;
+        return this.stream
     }
 
     // endregion
@@ -120,24 +120,24 @@ class BinaryReader {
      * @returns {Buffer}
      */
     tgReadBytes() {
-        const firstByte = this.readByte();
-        let padding;
-        let length;
+        const firstByte = this.readByte()
+        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;
+            length = firstByte
+            padding = (length + 1) % 4
         }
-        const data = this.read(length);
+        const data = this.read(length)
 
         if (padding > 0) {
-            padding = 4 - padding;
-            this.read(padding);
+            padding = 4 - padding
+            this.read(padding)
         }
 
-        return data;
+        return data
     }
 
     /**
@@ -145,7 +145,7 @@ class BinaryReader {
      * @returns {string}
      */
     tgReadString() {
-        return this.tgReadBytes().toString('utf-8');
+        return this.tgReadBytes().toString('utf-8')
     }
 
     /**
@@ -153,15 +153,15 @@ class BinaryReader {
      * @returns {boolean}
      */
     tgReadBool() {
-        const value = this.readInt(false);
+        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')}`)
         }
     }
 
@@ -171,50 +171,50 @@ class BinaryReader {
      * @returns {Date}
      */
     tgReadDate() {
-        const value = this.readInt();
-        return new Date(value * 1000);
+        const value = this.readInt()
+        return new Date(value * 1000)
     }
 
     /**
      * Reads a Telegram object.
      */
     tgReadObject() {
-        const constructorId = this.readInt(false);
-        let clazz = tlobjects[constructorId];
+        const constructorId = this.readInt(false)
+        let clazz = tlobjects[constructorId]
         if (clazz === undefined) {
             /**
              * The class was None, but there's still a
              * chance of it being a manually parsed value like bool!
              */
-            const value = constructorId;
+            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();
+                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[constructorId];
+            clazz = coreObjects[constructorId]
 
             if (clazz === undefined) {
                 // If there was still no luck, give up
-                this.seek(-4); // Go back
-                const pos = this.tellPosition();
-                const error = new TypeNotFoundError(constructorId, this.read());
-                this.setPosition(pos);
-                throw error;
+                this.seek(-4) // Go back
+                const pos = this.tellPosition()
+                const error = new TypeNotFoundError(constructorId, this.read())
+                this.setPosition(pos)
+                throw error
             }
         }
-        return clazz.fromReader(this);
+        return clazz.fromReader(this)
     }
 
     /**
@@ -223,14 +223,14 @@ 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 = [];
+        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
@@ -239,7 +239,7 @@ class BinaryReader {
      * Closes the reader.
      */
     close() {
-        this.stream = null;
+        this.stream = null
     }
 
     // region Position related
@@ -249,7 +249,7 @@ class BinaryReader {
      * @returns {number}
      */
     tellPosition() {
-        return this.offset;
+        return this.offset
     }
 
     /**
@@ -257,7 +257,7 @@ class BinaryReader {
      * @param position
      */
     setPosition(position) {
-        this.offset = position;
+        this.offset = position
     }
 
     /**
@@ -266,10 +266,10 @@ class BinaryReader {
      * @param offset
      */
     seek(offset) {
-        this.offset += offset;
+        this.offset += offset
     }
 
     // endregion
 }
 
-module.exports = BinaryReader;
+module.exports = BinaryReader

+ 4 - 4
gramjs/extensions/BinaryWriter.js

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

+ 43 - 43
gramjs/extensions/MessagePacker.js

@@ -1,89 +1,89 @@
-const MessageContainer = require("../tl/core/MessageContainer");
-const TLMessage = require("../tl/core/TLMessage");
-const {TLRequest} = require("../tl/tlobject");
-const BinaryWriter = require("../extensions/BinaryWriter");
-
+const MessageContainer = require('../tl/core/MessageContainer')
+const TLMessage = require('../tl/core/TLMessage')
+const { TLRequest } = require('../tl/tlobject')
+const BinaryWriter = require('../extensions/BinaryWriter')
+const struct = require('python-struct')
 
 class MessagePacker {
     constructor(state, logger) {
-        this._state = state;
-        this._queue = [];
-        this._ready = new Promise((resolve => {
-            this.setReady = resolve;
-        }));
-        this._log = logger;
+        this._state = state
+        this._queue = []
+        this._ready = new Promise(((resolve) => {
+            this.setReady = resolve
+        }))
+        this._log = logger
     }
 
     append(state) {
-        this._queue.push(state);
-        this.setReady(true);
+        this._queue.push(state)
+        this.setReady(true)
     }
 
     extend(states) {
         for (const state of states) {
-            this._queue.push(state);
+            this._queue.push(state)
         }
-        this.setReady(true);
+        this.setReady(true)
     }
 
     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
         }
-        let data;
-        const buffer = new BinaryWriter(Buffer.alloc(0));
+        let data
+        const buffer = new BinaryWriter(Buffer.alloc(0))
 
-        const batch = [];
-        let size = 0;
+        const batch = []
+        let size = 0
 
         while (this._queue.length && batch.length <= MessageContainer.MAXIMUM_LENGTH) {
-            const state = this._queue.shift();
-            size += state.data.length + TLMessage.SIZE_OVERHEAD;
+            const state = this._queue.shift()
+            size += state.data.length + TLMessage.SIZE_OVERHEAD
             if (size <= MessageContainer.MAXIMUM_SIZE) {
-                let afterId;
+                let afterId
                 if (state.after) {
-                    afterId = state.after.msgId;
+                    afterId = state.after.msgId
                 }
                 state.msgId = await this._state.writeDataAsMessage(
                     buffer, state.data, state.request instanceof TLRequest,
                     afterId
-                );
+                )
 
-                this._log.debug(`Assigned msgId = ${state.msgId} to ${state.request.constructor.name}`);
-                batch.push(state);
-                continue;
+                this._log.debug(`Assigned msgId = ${state.msgId} to ${state.request.constructor.name}`)
+                batch.push(state)
+                continue
             }
             if (batch.length) {
-                this._queue.unshift(state);
-                break;
+                this._queue.unshift(state)
+                break
             }
-            this._log.warn(`Message payload for ${state.request.constructor.name} is too long ${state.data.length} and cannot be sent`);
-            state.promise.reject("Request Payload is too big");
-            size = 0;
+            this._log.warn(`Message payload for ${state.request.constructor.name} is too long ${state.data.length} and cannot be sent`)
+            state.promise.reject('Request Payload is too big')
+            size = 0
             continue
         }
         if (!batch.length) {
-            return null;
+            return null
         }
         if (batch.length > 1) {
             data = Buffer.concat([struct.pack(
                 '<Ii', MessageContainer.CONSTRUCTOR_ID, batch.length
-            ), buffer.getValue()]);
+            ), buffer.getValue()])
 
             const containerId = await this._state.writeDataAsMessage(
                 buffer, data, false
-            );
+            )
             for (const s of batch) {
-                s.containerId = containerId;
+                s.containerId = containerId
             }
         }
 
-        data = buffer.getValue();
-        return {batch, data}
+        data = buffer.getValue()
+        return { batch, data }
     }
 }
 
-module.exports = MessagePacker;
+module.exports = MessagePacker

+ 3 - 3
gramjs/index.js

@@ -1,4 +1,4 @@
-const TelegramClient = require("./client/TelegramClient");
+const TelegramClient = require('./client/TelegramClient')
 module.exports = {
-    TelegramClient
-};
+    TelegramClient,
+}

+ 86 - 86
gramjs/network/Authenticator.js

@@ -1,22 +1,22 @@
-const AES = require('../crypto/AES');
-const AuthKey = require('../crypto/AuthKey');
-const Factorizator = require('../crypto/Factorizator');
-const RSA = require('../crypto/RSA');
-const Helpers = require('../utils/Helpers');
-const { ServerDHParamsFail } = require('../tl/types');
-const { ServerDHParamsOk } = require('../tl/types');
-const { ReqDHParamsRequest } = require('../tl/functions');
-const { SecurityError } = require('../errors/Common');
-const { PQInnerData } = require('../tl/types');
-const BinaryReader = require('../extensions/BinaryReader');
-const { ClientDHInnerData } = require('../tl/types');
-const { DhGenFail } = require('../tl/types');
-const { DhGenRetry } = require('../tl/types');
-const { DhGenOk } = require('../tl/types');
-const { SetClientDHParamsRequest } = require('../tl/functions');
-const { ServerDHInnerData } = require('../tl/types');
-const { ResPQ } = require('../tl/types');
-const { ReqPqMultiRequest } = require('../tl/functions');
+const AES = require('../crypto/AES')
+const AuthKey = require('../crypto/AuthKey')
+const Factorizator = require('../crypto/Factorizator')
+const RSA = require('../crypto/RSA')
+const Helpers = require('../utils/Helpers')
+const { ServerDHParamsFail } = require('../tl/types')
+const { ServerDHParamsOk } = require('../tl/types')
+const { ReqDHParamsRequest } = require('../tl/functions')
+const { SecurityError } = require('../errors/Common')
+const { PQInnerData } = require('../tl/types')
+const BinaryReader = require('../extensions/BinaryReader')
+const { ClientDHInnerData } = require('../tl/types')
+const { DhGenFail } = require('../tl/types')
+const { DhGenRetry } = require('../tl/types')
+const { DhGenOk } = require('../tl/types')
+const { SetClientDHParamsRequest } = require('../tl/functions')
+const { ServerDHInnerData } = require('../tl/types')
+const { ResPQ } = require('../tl/types')
+const { ReqPqMultiRequest } = require('../tl/functions')
 
 /**
  * Executes the authentication process with the Telegram servers.
@@ -26,30 +26,30 @@ const { ReqPqMultiRequest } = require('../tl/functions');
  */
 async function doAuthentication(sender, log) {
     // Step 1 sending: PQ Request, endianness doesn't matter since it's random
-    let bytes = Helpers.generateRandomBytes(16);
+    let bytes = Helpers.generateRandomBytes(16)
 
-    const nonce = Helpers.readBigIntFromBuffer(bytes, false, true);
+    const nonce = Helpers.readBigIntFromBuffer(bytes, false, true)
 
-    const resPQ = await sender.send(new ReqPqMultiRequest({ nonce: nonce }));
-    log.debug('Starting authKey generation step 1');
+    const resPQ = await sender.send(new ReqPqMultiRequest({ nonce: nonce }))
+    log.debug('Starting authKey generation step 1')
 
     if (!(resPQ instanceof ResPQ)) {
-        throw new Error(`Step 1 answer was ${resPQ}`);
+        throw new Error(`Step 1 answer was ${resPQ}`)
     }
     if (resPQ.nonce !== 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');
+    const pq = Helpers.readBigIntFromBuffer(resPQ.pq, false, true)
+    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);
-    p = getByteArray(p);
+    let { p, q } = Factorizator.factorize(pq)
+    p = getByteArray(p)
 
-    q = getByteArray(q);
+    q = getByteArray(q)
 
-    bytes = Helpers.generateRandomBytes(32);
-    const newNonce = Helpers.readBigIntFromBuffer(bytes, true, true);
+    bytes = Helpers.generateRandomBytes(32)
+    const newNonce = Helpers.readBigIntFromBuffer(bytes, true, true)
 
     const pqInnerData = new PQInnerData({
         pq: getByteArray(pq), // unsigned
@@ -58,20 +58,20 @@ async function doAuthentication(sender, log) {
         nonce: resPQ.nonce,
         serverNonce: resPQ.serverNonce,
         newNonce: newNonce,
-    });
+    })
 
     // sha_digest + data + random_bytes
-    let cipherText = null;
-    let targetFingerprint = null;
+    let cipherText = null
+    let targetFingerprint = null
     for (const fingerprint of resPQ.serverPublicKeyFingerprints) {
-        cipherText = RSA.encrypt(fingerprint.toString(), pqInnerData.bytes);
+        cipherText = RSA.encrypt(fingerprint.toString(), pqInnerData.bytes)
         if (cipherText !== null && cipherText !== undefined) {
-            targetFingerprint = fingerprint;
-            break;
+            targetFingerprint = fingerprint
+            break
         }
     }
     if (cipherText === null || 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(
@@ -83,58 +83,58 @@ async function doAuthentication(sender, log) {
             publicKeyFingerprint: targetFingerprint,
             encryptedData: cipherText,
         })
-    );
+    )
     if (!(serverDhParams instanceof ServerDHParamsOk || serverDhParams instanceof ServerDHParamsFail)) {
-        throw new Error(`Step 2.1 answer was ${serverDhParams}`);
+        throw new Error(`Step 2.1 answer was ${serverDhParams}`)
     }
     if (serverDhParams.nonce !== resPQ.nonce) {
-        throw new SecurityError('Step 2 invalid nonce from server');
+        throw new SecurityError('Step 2 invalid nonce from server')
     }
 
     if (serverDhParams.serverNonce !== 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 ServerDHParamsFail) {
-        const sh = Helpers.sha1(Helpers.readBufferFromBigInt(newNonce, 32, true, true).slice(4, 20));
-        const nnh = Helpers.readBigIntFromBuffer(sh, true, true);
+        const sh = Helpers.sha1(Helpers.readBufferFromBigInt(newNonce, 32, true, true).slice(4, 20))
+        const nnh = Helpers.readBigIntFromBuffer(sh, true, true)
         if (serverDhParams.newNonceHash !== 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 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 } = Helpers.generateKeyDataFromNonce(resPQ.serverNonce, newNonce);
+    const { key, iv } = 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 plainTextAnswer = AES.decryptIge(serverDhParams.encryptedAnswer, key, iv);
-    const reader = new BinaryReader(plainTextAnswer);
-    reader.read(20); // hash sum
-    const serverDhInner = reader.tgReadObject();
+    const plainTextAnswer = AES.decryptIge(serverDhParams.encryptedAnswer, key, iv)
+    const reader = new BinaryReader(plainTextAnswer)
+    reader.read(20) // hash sum
+    const serverDhInner = reader.tgReadObject()
     if (!(serverDhInner instanceof ServerDHInnerData)) {
-        throw new Error(`Step 3 answer was ${serverDhInner}`);
+        throw new Error(`Step 3 answer was ${serverDhInner}`)
     }
 
     if (serverDhInner.nonce !== 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 !== resPQ.serverNonce) {
-        throw new SecurityError('Step 3 Invalid server nonce in encrypted answer');
+        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 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 gb = Helpers.modExp(BigInt(serverDhInner.g), b, dhPrime);
-    const gab = Helpers.modExp(ga, b, dhPrime);
+    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)
 
     // Prepare client DH Inner Data
     const { bytes: clientDhInner } = new ClientDHInnerData({
@@ -142,45 +142,45 @@ async function doAuthentication(sender, log) {
         serverNonce: resPQ.serverNonce,
         retryId: 0, // TODO Actual retry ID
         gB: getByteArray(gb, false),
-    });
+    })
 
-    const clientDdhInnerHashed = Buffer.concat([Helpers.sha1(clientDhInner), clientDhInner]);
+    const clientDdhInnerHashed = Buffer.concat([Helpers.sha1(clientDhInner), clientDhInner])
     // Encryption
-    const clientDhEncrypted = AES.encryptIge(clientDdhInnerHashed, key, iv);
+    const clientDhEncrypted = AES.encryptIge(clientDdhInnerHashed, key, iv)
     const dhGen = await sender.send(
         new SetClientDHParamsRequest({
             nonce: resPQ.nonce,
             serverNonce: resPQ.serverNonce,
             encryptedData: clientDhEncrypted,
         })
-    );
-    const nonceTypes = [DhGenOk, DhGenRetry, DhGenFail];
+    )
+    const nonceTypes = [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}`);
+        throw new Error(`Step 3.1 answer was ${dhGen}`)
     }
-    const { name } = dhGen.constructor;
+    const { name } = dhGen.constructor
     if (dhGen.nonce !== 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 !== 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(getByteArray(gab));
-    const nonceNumber = 1 + nonceTypes.indexOf(dhGen.constructor);
+    const authKey = new AuthKey(getByteArray(gab))
+    const nonceNumber = 1 + nonceTypes.indexOf(dhGen.constructor)
 
-    const newNonceHash = authKey.calcNewNonceHash(newNonce, nonceNumber);
-    const dhHash = dhGen[`newNonceHash${nonceNumber}`];
+    const newNonceHash = authKey.calcNewNonceHash(newNonce, nonceNumber)
+    const dhHash = dhGen[`newNonceHash${nonceNumber}`]
 
     if (dhHash !== newNonceHash) {
-        throw new SecurityError('Step 3 invalid new nonce hash');
+        throw new SecurityError('Step 3 invalid new nonce hash')
     }
 
     if (!(dhGen instanceof 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 }
 }
 
 /**
@@ -190,9 +190,9 @@ async function doAuthentication(sender, log) {
  * @returns {Buffer}
  */
 function getByteArray(integer, signed = false) {
-    const bits = integer.toString(2).length;
-    const byteLength = Math.floor((bits + 8 - 1) / 8);
-    return Helpers.readBufferFromBigInt(BigInt(integer), byteLength, false, signed);
+    const bits = integer.toString(2).length
+    const byteLength = Math.floor((bits + 8 - 1) / 8)
+    return Helpers.readBufferFromBigInt(BigInt(integer), byteLength, false, signed)
 }
 
-module.exports = doAuthentication;
+module.exports = doAuthentication

+ 27 - 27
gramjs/network/MTProtoPlainSender.js

@@ -2,11 +2,11 @@
  *  This module contains the class used to communicate with Telegram's servers
  *  in plain text, when no authorization key has been created yet.
  */
-const Helpers = require('../utils/Helpers');
-const MTProtoState = require('./MTProtoState');
-const struct = require('python-struct');
-const BinaryReader = require('../extensions/BinaryReader');
-const { InvalidBufferError } = require('../errors/Common');
+const Helpers = require('../utils/Helpers')
+const MTProtoState = require('./MTProtoState')
+const struct = require('python-struct')
+const BinaryReader = require('../extensions/BinaryReader')
+const { InvalidBufferError } = require('../errors/Common')
 
 /**
  * MTProto Mobile Protocol plain sender (https://core.telegram.org/mtproto/description#unencrypted-messages)
@@ -19,8 +19,8 @@ class MTProtoPlainSender {
      * @param loggers
      */
     constructor(connection, loggers) {
-        this._state = new MTProtoState(connection, loggers);
-        this._connection = connection;
+        this._state = new MTProtoState(connection, loggers)
+        this._connection = connection
     }
 
     /**
@@ -28,23 +28,23 @@ class MTProtoPlainSender {
      * @param request
      */
     async send(request) {
-        let body = request.bytes;
-        let msgId = this._state._getNewMsgId();
-        const res = Buffer.concat([struct.pack('<qqi', [0, msgId.toString(), body.length]), body]);
+        let body = request.bytes
+        let msgId = this._state._getNewMsgId()
+        const res = Buffer.concat([struct.pack('<qqi', [0, msgId.toString(), body.length]), body])
 
-        await this._connection.send(res);
-        body = await this._connection.recv();
+        await this._connection.send(res)
+        body = await this._connection.recv()
         if (body.length < 9) {
-            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 !== 0n) {
-            throw new Error('Bad authKeyId');
+            throw new Error('Bad authKeyId')
         }
-        msgId = reader.readLong();
+        msgId = reader.readLong()
         if (msgId === 0n) {
-            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
@@ -52,16 +52,16 @@ 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()
     }
 
     /**
@@ -70,18 +70,18 @@ class MTProtoPlainSender {
      */
     getNewMsgId() {
         // See https://core.telegram.org/mtproto/description#message-identifier-msg-id
-        const msTime = Date.now();
+        const msTime = Date.now()
         let newMsgId =
             (BigInt(Math.floor(msTime / 1000)) << BigInt(32)) | // "must approximately equal unixtime*2^32"
             (BigInt(msTime % 1000) << BigInt(32)) | // "approximate moment in time the message was created"
-            (BigInt(Helpers.getRandomInt(0, 524288)) << BigInt(2)); // "message identifiers are divisible by 4"
+            (BigInt(Helpers.getRandomInt(0, 524288)) << BigInt(2)) // "message identifiers are divisible by 4"
         // Ensure that we always return a message ID which is higher than the previous one
         if (this._lastMsgId >= newMsgId) {
-            newMsgId = this._lastMsgId + 4n;
+            newMsgId = this._lastMsgId + 4n
         }
-        this._lastMsgId = newMsgId;
-        return BigInt(newMsgId);
+        this._lastMsgId = newMsgId
+        return BigInt(newMsgId)
     }
 }
 
-module.exports = MTProtoPlainSender;
+module.exports = MTProtoPlainSender

+ 205 - 204
gramjs/network/MTProtoSender.js

@@ -1,18 +1,17 @@
-const MtProtoPlainSender = require('./MTProtoPlainSender');
-const MTProtoState = require('./MTProtoState');
-const Helpers = require('../utils/Helpers');
-const { MsgsAck } = require('../tl/types');
-const AuthKey = require('../crypto/AuthKey');
-const doAuthentication = require('./Authenticator');
-const RPCResult = require('../tl/core/RPCResult');
-const MessageContainer = require('../tl/core/MessageContainer');
-const GZIPPacked = require('../tl/core/GZIPPacked');
-const RequestState = require('./RequestState');
-const format = require('string-format');
-
-const MessagePacker = require('../extensions/MessagePacker');
-const Pong = require('../tl/core/GZIPPacked');
-const BinaryReader = require('../extensions/BinaryReader');
+const MtProtoPlainSender = require('./MTProtoPlainSender')
+const MTProtoState = require('./MTProtoState')
+const Helpers = require('../utils/Helpers')
+const AuthKey = require('../crypto/AuthKey')
+const doAuthentication = require('./Authenticator')
+const RPCResult = require('../tl/core/RPCResult')
+const MessageContainer = require('../tl/core/MessageContainer')
+const GZIPPacked = require('../tl/core/GZIPPacked')
+const RequestState = require('./RequestState')
+const format = require('string-format')
+const { MsgsAck, File, MsgsStateInfo } = require('../tl/types')
+const MessagePacker = require('../extensions/MessagePacker')
+const Pong = require('../tl/core/GZIPPacked')
+const BinaryReader = require('../extensions/BinaryReader')
 const {
     BadServerSalt,
     BadMsgNotification,
@@ -23,17 +22,17 @@ const {
     MsgsStateReq,
     MsgResendReq,
     MsgsAllInfo,
-} = require('../tl/types');
-const { SecurityError } = require('../errors/Common');
-const { InvalidBufferError } = require('../errors/Common');
-const { LogOutRequest } = require('../tl/functions/auth');
-const log4js = require('log4js');
-const { RPCMessageToError } = require('../errors');
-const { TypeNotFoundError } = require('../errors/Common');
-const logger = log4js.getLogger('gramjs');
+} = require('../tl/types')
+const { SecurityError } = require('../errors/Common')
+const { InvalidBufferError } = require('../errors/Common')
+const { LogOutRequest } = require('../tl/functions/auth')
+const log4js = require('log4js')
+const { RPCMessageToError } = require('../errors')
+const { TypeNotFoundError } = require('../errors/Common')
+const logger = log4js.getLogger('gramjs')
 
 // const { tlobjects } = require("../gramjs/tl/alltlobjects");
-format.extend(String.prototype, {});
+format.extend(String.prototype, {})
 
 /**
  * MTProto Mobile Protocol sender
@@ -64,17 +63,17 @@ class MTProtoSender {
             authKeyCallback: null,
             updateCallback: null,
             autoReconnectCallback: null,
-        }
+        },
     ) {
-        this._connection = null;
-        this._log = opt.logger;
-        this._retries = opt.retries;
-        this._delay = opt.delay;
-        this._autoReconnect = opt.autoReconnect;
-        this._connectTimeout = opt.connectTimeout;
-        this._authKeyCallback = opt.authKeyCallback;
-        this._updateCallback = opt.updateCallback;
-        this._autoReconnectCallback = opt.autoReconnectCallback;
+        this._connection = null
+        this._log = opt.logger
+        this._retries = opt.retries
+        this._delay = opt.delay
+        this._autoReconnect = opt.autoReconnect
+        this._connectTimeout = opt.connectTimeout
+        this._authKeyCallback = opt.authKeyCallback
+        this._updateCallback = opt.updateCallback
+        this._autoReconnectCallback = opt.autoReconnectCallback
 
         /**
          * Whether the user has explicitly connected or disconnected.
@@ -84,44 +83,44 @@ class MTProtoSender {
          * be cleared but on explicit user disconnection all the
          * pending futures should be cancelled.
          */
-        this._user_connected = false;
-        this._reconnecting = false;
-        this._disconnected = true;
+        this._user_connected = false
+        this._reconnecting = false
+        this._disconnected = true
 
         /**
          * We need to join the loops upon disconnection
          */
-        this._send_loop_handle = null;
-        this._recv_loop_handle = null;
+        this._send_loop_handle = null
+        this._recv_loop_handle = null
 
         /**
          * Preserving the references of the AuthKey and state is important
          */
-        this.authKey = authKey || new AuthKey(null);
-        this._state = new MTProtoState(this.authKey, this._log);
+        this.authKey = authKey || new AuthKey(null)
+        this._state = new MTProtoState(this.authKey, this._log)
 
         /**
          * Outgoing messages are put in a queue and sent in a batch.
          * Note that here we're also storing their ``_RequestState``.
          */
-        this._send_queue = new MessagePacker(this._state, this._log);
+        this._send_queue = new MessagePacker(this._state, this._log)
 
         /**
          * Sent states are remembered until a response is received.
          */
-        this._pending_state = {};
+        this._pending_state = {}
 
         /**
          * Responses must be acknowledged, and we can also batch these.
          */
-        this._pending_ack = new Set();
+        this._pending_ack = new Set()
 
         /**
          * Similar to pending_messages but only for the last acknowledges.
          * These can't go in pending_messages because no acknowledge for them
          * is received, but we may still need to resend their state on bad salts.
          */
-        this._last_acks = [];
+        this._last_acks = []
 
         /**
          * Jump table from response ID to method that handles it
@@ -142,7 +141,7 @@ class MTProtoSender {
             [MsgsStateReq.CONSTRUCTOR_ID]: this._handleStateForgotten.bind(this),
             [MsgResendReq.CONSTRUCTOR_ID]: this._handleStateForgotten.bind(this),
             [MsgsAllInfo.CONSTRUCTOR_ID]: this._handleMsgAll.bind(this),
-        };
+        }
     }
 
     // Public API
@@ -154,16 +153,16 @@ class MTProtoSender {
      */
     async connect(connection) {
         if (this._user_connected) {
-            this._log.info('User is already connected!');
-            return false;
+            this._log.info('User is already connected!')
+            return false
         }
-        this._connection = connection;
-        await this._connect();
-        return true;
+        this._connection = connection
+        await this._connect()
+        return true
     }
 
     isConnected() {
-        return this._user_connected;
+        return this._user_connected
     }
 
     /**
@@ -171,7 +170,7 @@ class MTProtoSender {
      * all pending requests, and closes the send and receive loops.
      */
     async disconnect() {
-        await this._disconnect();
+        await this._disconnect()
     }
 
     /**
@@ -201,15 +200,15 @@ class MTProtoSender {
      */
     send(request) {
         if (!this._user_connected) {
-            throw new Error('Cannot send requests while disconnected');
+            throw new Error('Cannot send requests while disconnected')
         }
 
         if (!Helpers.isArrayLike(request)) {
-            const state = new RequestState(request);
-            this._send_queue.append(state);
-            return state.promise;
+            const state = new RequestState(request)
+            this._send_queue.append(state)
+            return state.promise
         } else {
-            throw new Error('not supported');
+            throw new Error('not supported')
         }
     }
 
@@ -221,16 +220,16 @@ class MTProtoSender {
      * @private
      */
     async _connect() {
-        this._log.info('Connecting to {0}...'.replace('{0}', this._connection));
-        await this._connection.connect();
-        this._log.debug('Connection success!');
+        this._log.info('Connecting to {0}...'.replace('{0}', this._connection))
+        await this._connection.connect()
+        this._log.debug('Connection success!')
         if (!this.authKey._key) {
-            const plain = new MtProtoPlainSender(this._connection, this._loggers);
-            this._log.debug('New auth_key attempt ...');
-            const res = await doAuthentication(plain, this._log);
-            this._log.debug('Generated new auth_key successfully');
-            this.authKey.key = res.authKey;
-            this._state.time_offset = res.timeOffset;
+            const plain = new MtProtoPlainSender(this._connection, this._loggers)
+            this._log.debug('New auth_key attempt ...')
+            const res = await doAuthentication(plain, this._log)
+            this._log.debug('Generated new auth_key successfully')
+            this.authKey.key = res.authKey
+            this._state.time_offset = res.timeOffset
 
             /**
              * This is *EXTREMELY* important since we don't control
@@ -239,35 +238,35 @@ class MTProtoSender {
              * switch to different data centers.
              */
             if (this._authKeyCallback) {
-                this._authKeyCallback(this.authKey);
+                this._authKeyCallback(this.authKey)
             }
         } else {
-            this._log.debug('Already have an auth key ...');
+            this._log.debug('Already have an auth key ...')
         }
-        this._user_connected = true;
+        this._user_connected = true
 
-        this._log.debug('Starting send loop');
-        this._send_loop_handle = this._sendLoop();
+        this._log.debug('Starting send loop')
+        this._send_loop_handle = this._sendLoop()
 
-        this._log.debug('Starting receive loop');
-        this._recv_loop_handle = this._recvLoop();
+        this._log.debug('Starting receive loop')
+        this._recv_loop_handle = this._recvLoop()
 
         // _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!', this._connection.toString());
+        this._log.info('Connection to %s complete!', 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
         }
-        this._log.info('Disconnecting from %s...', this._connection);
-        this._user_connected = false;
-        this._log.debug('Closing current connection...');
-        await this._connection.disconnect();
+        this._log.info('Disconnecting from %s...', this._connection)
+        this._user_connected = false
+        this._log.debug('Closing current connection...')
+        await this._connection.disconnect()
     }
 
     /**
@@ -280,85 +279,85 @@ class MTProtoSender {
     async _sendLoop() {
         while (this._user_connected && !this._reconnecting) {
             if (this._pending_ack.size) {
-                const ack = new RequestState(new MsgsAck({ msgIds: Array(...this._pending_ack) }));
-                this._send_queue.append(ack);
-                this._last_acks.push(ack);
-                this._pending_ack.clear();
+                const ack = new RequestState(new MsgsAck({ msgIds: Array(...this._pending_ack) }))
+                this._send_queue.append(ack)
+                this._last_acks.push(ack)
+                this._pending_ack.clear()
             }
             // this._log.debug('Waiting for messages to send...');
-            this._log.debug('Waiting for messages to send...');
+            this._log.debug('Waiting for messages to send...')
             // 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._send_queue.get();
+            const res = await this._send_queue.get()
             if (!res) {
-                continue;
+                continue
             }
-            let data = res.data;
-            const batch = res.batch;
-            this._log.debug(`Encrypting ${batch.length} message(s) in ${data.length} bytes for sending`);
+            let data = res.data
+            const batch = res.batch
+            this._log.debug(`Encrypting ${batch.length} message(s) in ${data.length} bytes for sending`)
 
-            data = this._state.encryptMessageData(data);
+            data = this._state.encryptMessageData(data)
 
             try {
-                await this._connection.send(data);
+                await this._connection.send(data)
             } catch (e) {
-                console.log(e);
-                this._log.info('Connection closed while sending data');
-                return;
+                console.log(e)
+                this._log.info('Connection closed while sending data')
+                return
             }
             for (const state of batch) {
-                this._pending_state[state.msgId] = state;
+                this._pending_state[state.msgId] = state
             }
-            this._log.debug('Encrypted messages put in a queue to be sent');
+            this._log.debug('Encrypted messages put in a queue to be sent')
         }
     }
 
     async _recvLoop() {
-        let body;
-        let message;
+        let body
+        let message
 
         while (this._user_connected && !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.debug('Connection closed while receiving data');
-                return;
+                this._log.debug('Connection closed while receiving data')
+                return
             }
             try {
-                message = await this._state.decryptMessageData(body);
+                message = await this._state.decryptMessageData(body)
             } catch (e) {
-                console.log(e);
+                console.log(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.authKey.key = null;
+                    this._log.info('Broken authorization key; resetting')
+                    this.authKey.key = null
                     if (this._authKeyCallback) {
-                        this._authKeyCallback(null);
+                        this._authKeyCallback(null)
                     }
-                    return;
+                    return
                 } else {
-                    this._log.error('Unhandled error while receiving data');
-                    return;
+                    this._log.error('Unhandled error while receiving data')
+                    return
                 }
             }
             try {
-                await this._processMessage(message);
+                await this._processMessage(message)
             } catch (e) {
-                console.log(e);
-                this._log.error('Unhandled error while receiving data');
+                console.log(e)
+                this._log.error('Unhandled error while receiving data')
             }
         }
     }
@@ -374,14 +373,15 @@ class MTProtoSender {
      * @private
      */
     async _processMessage(message) {
-        this._pending_ack.add(message.msgId);
-        message.obj = await message.obj;
-        let handler = this._handlers[message.obj.CONSTRUCTOR_ID];
+        this._pending_ack.add(message.msgId)
+        // eslint-disable-next-line require-atomic-updates
+        message.obj = await message.obj
+        let handler = this._handlers[message.obj.CONSTRUCTOR_ID]
         if (!handler) {
-            handler = this._handleUpdate;
+            handler = this._handleUpdate
         }
 
-        await handler(message);
+        await handler(message)
     }
 
     /**
@@ -392,36 +392,36 @@ class MTProtoSender {
      * @private
      */
     _popStates(msgId) {
-        let state = this._pending_state[msgId];
+        let state = this._pending_state[msgId]
         if (state) {
-            delete this._pending_state[msgId];
-            return [state];
+            delete this._pending_state[msgId]
+            return [state]
         }
 
-        const toPop = [];
+        const toPop = []
 
         for (state in this._pending_state) {
             if (state.containerId === msgId) {
-                toPop.push(state.msgId);
+                toPop.push(state.msgId)
             }
         }
 
         if (toPop.length) {
-            const temp = [];
+            const temp = []
             for (const x of toPop) {
-                temp.push(this._pending_state[x]);
-                delete this._pending_state[x];
+                temp.push(this._pending_state[x])
+                delete this._pending_state[x]
             }
-            return temp;
+            return temp
         }
 
         for (const ack of this._last_acks) {
             if (ack.msgId === msgId) {
-                return [ack];
+                return [ack]
             }
         }
 
-        return [];
+        return []
     }
 
     /**
@@ -433,12 +433,12 @@ class MTProtoSender {
      * @private
      */
     async _handleRPCResult(message) {
-        const RPCResult = message.obj;
-        const state = this._pending_state[RPCResult.reqMsgId];
+        const RPCResult = message.obj
+        const state = this._pending_state[RPCResult.reqMsgId]
         if (state) {
-            delete this._pending_state[RPCResult.reqMsgId];
+            delete this._pending_state[RPCResult.reqMsgId]
         }
-        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
@@ -446,31 +446,31 @@ class MTProtoSender {
             // See #658, #759 and #958. They seem to happen in a container
             // which contain the real response right after.
             try {
-                const reader = new BinaryReader(RPCResult.body);
+                const reader = new BinaryReader(RPCResult.body)
                 if (!(reader.tgReadObject() instanceof File)) {
-                    throw new TypeNotFoundError('Not an upload.File');
+                    throw new TypeNotFoundError('Not an upload.File')
                 }
             } catch (e) {
-                console.log(e);
+                console.log(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
                 }
             }
         }
         if (RPCResult.error) {
             // eslint-disable-next-line new-cap
-            const error = RPCMessageToError(RPCResult.error, state.request);
-            console.log('error happen', error);
+            const error = RPCMessageToError(RPCResult.error, state.request)
+            console.log('error happen', error)
 
-            this._send_queue.append(new RequestState(new MsgsAck({ msgIds: [state.msgId] })));
-            state.reject(error);
+            this._send_queue.append(new RequestState(new MsgsAck({ msgIds: [state.msgId] })))
+            state.reject(error)
         } else {
-            const reader = new BinaryReader(RPCResult.body);
-            const read = await state.request.readResult(reader);
-            state.resolve(read);
+            const reader = new BinaryReader(RPCResult.body)
+            const read = await state.request.readResult(reader)
+            state.resolve(read)
         }
     }
 
@@ -482,9 +482,9 @@ class MTProtoSender {
      * @private
      */
     async _handleContainer(message) {
-        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)
         }
     }
 
@@ -496,22 +496,22 @@ class MTProtoSender {
      * @private
      */
     async _handleGzipPacked(message) {
-        this._log.debug('Handling gzipped data');
-        const reader = new BinaryReader(message.obj.data);
-        message.obj = reader.tgReadObject();
-        await this._processMessage(message);
+        this._log.debug('Handling gzipped data')
+        const reader = new BinaryReader(message.obj.data)
+        message.obj = reader.tgReadObject()
+        await this._processMessage(message)
     }
 
     async _handleUpdate(message) {
         if (message.obj.SUBCLASS_OF_ID !== 0x8af52aac) {
             // crc32(b'Updates')
             // TODO fix this. currently getting an error about this not being defined.
-            logger.warn(`Note: ${message.obj.constructor.name} is not an update, not dispatching it`);
-            return;
+            logger.warn(`Note: ${message.obj.constructor.name} is not an update, not dispatching it`)
+            return
         }
-        this._log.debug('Handling update %s', message.obj.constructor.name);
+        this._log.debug('Handling update %s', message.obj.constructor.name)
         if (this._updateCallback) {
-            this._updateCallback(message.obj);
+            this._updateCallback(message.obj)
         }
     }
 
@@ -524,14 +524,14 @@ class MTProtoSender {
      * @private
      */
     async _handlePong(message) {
-        const pong = message.obj;
-        this._log.debug(`Handling pong for message ${pong.msgId}`);
-        const state = this._pending_state[pong.msgId];
-        delete this._pending_state[pong.msgId];
+        const pong = message.obj
+        this._log.debug(`Handling pong for message ${pong.msgId}`)
+        const state = this._pending_state[pong.msgId]
+        delete this._pending_state[pong.msgId]
 
         // Todo Check result
         if (state) {
-            state.resolve(pong);
+            state.resolve(pong)
         }
     }
 
@@ -545,12 +545,12 @@ class MTProtoSender {
      * @private
      */
     async _handleBadServerSalt(message) {
-        const badSalt = message.obj;
-        this._log.debug(`Handling bad salt for message ${badSalt.badMsgId}`);
-        this._state.salt = badSalt.newServerSalt;
-        const states = this._popStates(badSalt.badMsgId);
-        this._send_queue.extend(states);
-        this._log.debug(`${states.length} message(s) will be resent`);
+        const badSalt = message.obj
+        this._log.debug(`Handling bad salt for message ${badSalt.badMsgId}`)
+        this._state.salt = badSalt.newServerSalt
+        const states = this._popStates(badSalt.badMsgId)
+        this._send_queue.extend(states)
+        this._log.debug(`${states.length} message(s) will be resent`)
     }
 
     /**
@@ -563,21 +563,21 @@ class MTProtoSender {
      * @private
      */
     async _handleBadNotification(message) {
-        const badMsg = message.obj;
-        const states = this._popStates(badMsg.badMsgId);
-        this._log.debug(`Handling bad msg ${badMsg}`);
+        const badMsg = message.obj
+        const states = this._popStates(badMsg.badMsgId)
+        this._log.debug(`Handling bad msg ${badMsg}`)
         if ([16, 17].contains(badMsg.errorCode)) {
             // 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(message.msgId);
-            this._log.info(`System clock is wrong, set time offset to ${to}s`);
+            const to = this._state.updateTimeOffset(message.msgId)
+            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) {
             // TODO set errors;
@@ -585,11 +585,11 @@ class MTProtoSender {
             BadMessageError(state.request, bad_msg.error_code))*/
             // }
 
-            return;
+            return
         }
         // Messages are to be re-sent once we've corrected the issue
-        this._send_queue.extend(states);
-        this._log.debug(`${states.length} messages will be resent due to bad msg`);
+        this._send_queue.extend(states)
+        this._log.debug(`${states.length} messages will be resent due to bad msg`)
     }
 
     /**
@@ -602,9 +602,9 @@ class MTProtoSender {
      */
     async _handleDetailedInfo(message) {
         // TODO https://goo.gl/VvpCC6
-        const msgId = message.obj.answerMsgId;
-        this._log.debug(`Handling detailed info for message ${msgId}`);
-        this._pending_ack.add(msgId);
+        const msgId = message.obj.answerMsgId
+        this._log.debug(`Handling detailed info for message ${msgId}`)
+        this._pending_ack.add(msgId)
     }
 
     /**
@@ -617,9 +617,9 @@ class MTProtoSender {
      */
     async _handleNewDetailedInfo(message) {
         // TODO https://goo.gl/VvpCC6
-        const msgId = message.obj.answerMsgId;
-        this._log.debug(`Handling new detailed info for message ${msgId}`);
-        this._pending_ack.add(msgId);
+        const msgId = message.obj.answerMsgId
+        this._log.debug(`Handling new detailed info for message ${msgId}`)
+        this._pending_ack.add(msgId)
     }
 
     /**
@@ -632,8 +632,8 @@ class MTProtoSender {
      */
     async _handleNewSessionCreated(message) {
         // 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
     }
 
     /**
@@ -655,13 +655,13 @@ class MTProtoSender {
      * @private
      */
     async _handleAck(message) {
-        const ack = message.obj;
-        this._log.debug(`Handling acknowledge for ${ack.msgIds}`);
+        const ack = message.obj
+        this._log.debug(`Handling acknowledge for ${ack.msgIds}`)
         for (const msgId of ack.msgIds) {
-            const state = this._pending_state[msgId];
+            const state = this._pending_state[msgId]
             if (state && state.request instanceof LogOutRequest) {
-                delete this._pending_state[msgId];
-                state.resolve(true);
+                delete this._pending_state[msgId]
+                state.resolve(true)
             }
         }
     }
@@ -678,12 +678,12 @@ class MTProtoSender {
     async _handleFutureSalts(message) {
         // TODO save these salts and automatically adjust to the
         // correct one whenever the salt in use expires.
-        this._log.debug(`Handling future salts for message ${message.msgId}`);
-        const state = this._pending_state[message.msgId];
+        this._log.debug(`Handling future salts for message ${message.msgId}`)
+        const state = this._pending_state[message.msgId]
 
         if (state) {
-            delete this._pending_state[message];
-            state.resolve(message.obj);
+            delete this._pending_state[message]
+            state.resolve(message.obj)
         }
     }
 
@@ -696,8 +696,8 @@ class MTProtoSender {
      */
     async _handleStateForgotten(message) {
         this._send_queue.append(
-            new RequestState(new MsgsStateInfo(message.msgId, String.fromCharCode(1).repeat(message.obj.msgIds)))
-        );
+            new RequestState(new MsgsStateInfo(message.msgId, String.fromCharCode(1).repeat(message.obj.msgIds))),
+        )
     }
 
     /**
@@ -706,7 +706,8 @@ class MTProtoSender {
      * @returns {Promise<void>}
      * @private
      */
-    async _handleMsgAll(message) {}
+    async _handleMsgAll(message) {
+    }
 }
 
-module.exports = MTProtoSender;
+module.exports = MTProtoSender

+ 75 - 75
gramjs/network/MTProtoState.js

@@ -1,11 +1,11 @@
-const struct = require('python-struct');
-const Helpers = require('../utils/Helpers');
-const AES = require('../crypto/AES');
-const BinaryReader = require('../extensions/BinaryReader');
-const GZIPPacked = require('../tl/core/GZIPPacked');
-const { TLMessage } = require('../tl/core');
-const { SecurityError } = require('../errors/Common');
-const { InvalidBufferError } = require('../errors/Common');
+const struct = require('python-struct')
+const Helpers = require('../utils/Helpers')
+const AES = require('../crypto/AES')
+const BinaryReader = require('../extensions/BinaryReader')
+const GZIPPacked = require('../tl/core/GZIPPacked')
+const { TLMessage } = require('../tl/core')
+const { SecurityError, InvalidBufferError } = require('../errors/Common')
+const { InvokeAfterMsgRequest } = require('../tl/functions')
 
 class MTProtoState {
     /**
@@ -34,13 +34,13 @@ class MTProtoState {
      * @param loggers
      */
     constructor(authKey, loggers) {
-        this.authKey = authKey;
-        this._log = loggers;
-        this.timeOffset = 0;
-        this.salt = 0;
+        this.authKey = authKey
+        this._log = loggers
+        this.timeOffset = 0
+        this.salt = 0
 
-        this.id = this._sequence = this._lastMsgId = null;
-        this.reset();
+        this.id = this._sequence = this._lastMsgId = null
+        this.reset()
     }
 
     /**
@@ -48,9 +48,9 @@ class MTProtoState {
      */
     reset() {
         // Session IDs can be random on every connection
-        this.id = Helpers.generateRandomLong(true);
-        this._sequence = 0;
-        this._lastMsgId = 0;
+        this.id = Helpers.generateRandomLong(true)
+        this._sequence = 0
+        this._lastMsgId = 0
     }
 
     /**
@@ -59,7 +59,7 @@ class MTProtoState {
      * @param message
      */
     updateMessageId(message) {
-        message.msgId = this._getNewMsgId();
+        message.msgId = this._getNewMsgId()
     }
 
     /**
@@ -70,13 +70,13 @@ class MTProtoState {
      * @returns {{iv: Buffer, key: Buffer}}
      */
     _calcKey(authKey, msgKey, client) {
-        const x = client === true ? 0 : 8;
-        const sha256a = Helpers.sha256(Buffer.concat([msgKey, authKey.slice(x, x + 36)]));
-        const sha256b = Helpers.sha256(Buffer.concat([authKey.slice(x + 40, x + 76), msgKey]));
+        const x = client === true ? 0 : 8
+        const sha256a = Helpers.sha256(Buffer.concat([msgKey, authKey.slice(x, x + 36)]))
+        const sha256b = Helpers.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 }
     }
 
     /**
@@ -88,17 +88,17 @@ class MTProtoState {
      * @param afterId
      */
     async writeDataAsMessage(buffer, data, contentRelated, afterId) {
-        const msgId = this._getNewMsgId();
-        const seqNo = this._getSeqNo(contentRelated);
-        let body;
+        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 InvokeAfterMsgRequest(afterId, data).toBuffer());
+            body = await GZIPPacked.gzipIfSmaller(contentRelated, new InvokeAfterMsgRequest(afterId, data).toBuffer())
         }
-        buffer.write(struct.pack('<qii', msgId.toString(), seqNo, body.length));
-        buffer.write(body);
-        return msgId;
+        buffer.write(struct.pack('<qii', msgId.toString(), seqNo, body.length))
+        buffer.write(body)
+        return msgId
     }
 
     /**
@@ -107,18 +107,18 @@ class MTProtoState {
      * @param data
      */
     encryptMessageData(data) {
-        data = Buffer.concat([struct.pack('<qq', this.salt.toString(), this.id.toString()), data]);
-        const padding = Helpers.generateRandomBytes(Helpers.mod(-(data.length + 12), 16) + 12);
+        data = Buffer.concat([struct.pack('<qq', this.salt.toString(), this.id.toString()), data])
+        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 = Helpers.sha256(Buffer.concat([this.authKey.key.slice(88, 88 + 32), data, padding]));
+        const msgKeyLarge = Helpers.sha256(Buffer.concat([this.authKey.key.slice(88, 88 + 32), data, padding]))
         // "msg_key = substr (msg_key_large, 8, 16)"
-        const msgKey = msgKeyLarge.slice(8, 24);
+        const msgKey = msgKeyLarge.slice(8, 24)
 
-        const { iv, key } = this._calcKey(this.authKey.key, msgKey, true);
+        const { iv, key } = this._calcKey(this.authKey.key, msgKey, true)
 
-        const keyId = Helpers.readBufferFromBigInt(this.authKey.keyId, 8);
-        return Buffer.concat([keyId, msgKey, AES.encryptIge(Buffer.concat([data, padding]), key, iv)]);
+        const keyId = Helpers.readBufferFromBigInt(this.authKey.keyId, 8)
+        return Buffer.concat([keyId, msgKey, AES.encryptIge(Buffer.concat([data, padding]), key, iv)])
     }
 
     /**
@@ -127,46 +127,46 @@ class MTProtoState {
      */
     async decryptMessageData(body) {
         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));
+        const keyId = Helpers.readBigIntFromBuffer(body.slice(0, 8))
 
         if (keyId !== this.authKey.keyId) {
-            throw new SecurityError('Server replied with an invalid auth key');
+            throw new SecurityError('Server replied with an invalid auth key')
         }
 
-        const msgKey = body.slice(8, 24);
-        const { iv, key } = this._calcKey(this.authKey.key, msgKey, false);
-        body = AES.decryptIge(body.slice(24), key, iv);
+        const msgKey = body.slice(8, 24)
+        const { iv, key } = this._calcKey(this.authKey.key, msgKey, false)
+        body = AES.decryptIge(body.slice(24), key, iv)
 
         // https://core.telegram.org/mtproto/security_guidelines
         // Sections "checking sha256 hash" and "message length"
 
-        const ourKey = Helpers.sha256(Buffer.concat([this.authKey.key.slice(96, 96 + 32), body]));
+        const ourKey = Helpers.sha256(Buffer.concat([this.authKey.key.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);
-        reader.readLong(); // removeSalt
-        const serverId = reader.readLong();
+        const reader = new BinaryReader(body)
+        reader.readLong() // removeSalt
+        const serverId = reader.readLong()
         if (serverId !== this.id) {
             // throw new SecurityError('Server replied with a wrong session ID');
         }
 
-        const remoteMsgId = reader.readLong();
-        const remoteSequence = reader.readInt();
-        reader.readInt(); // msgLen for the inner object, padding ignored
+        const remoteMsgId = reader.readLong()
+        const remoteSequence = reader.readInt()
+        reader.readInt() // msgLen for the inner object, padding ignored
 
         // We could read msg_len bytes and use those in a new reader to read
         // the next TLObject without including the padding, but since the
         // reader isn't used for anything else after this, it's unnecessary.
-        const obj = await reader.tgReadObject();
+        const obj = await reader.tgReadObject()
 
-        return new TLMessage(remoteMsgId, remoteSequence, obj);
+        return new TLMessage(remoteMsgId, remoteSequence, obj)
     }
 
     /**
@@ -175,15 +175,15 @@ class MTProtoState {
      * @private
      */
     _getNewMsgId() {
-        const now = new Date().getTime() / 1000 + this.timeOffset;
-        const nanoseconds = Math.floor((now - Math.floor(now)) * 1e9);
-        let newMsgId = (BigInt(Math.floor(now)) << 32n) | (BigInt(nanoseconds) << 2n);
+        const now = new Date().getTime() / 1000 + this.timeOffset
+        const nanoseconds = Math.floor((now - Math.floor(now)) * 1e9)
+        let newMsgId = (BigInt(Math.floor(now)) << 32n) | (BigInt(nanoseconds) << 2n)
         if (this._lastMsgId >= newMsgId) {
-            newMsgId = this._lastMsgId + 4n;
+            newMsgId = this._lastMsgId + 4n
         }
-        this._lastMsgId = newMsgId;
+        this._lastMsgId = newMsgId
 
-        return newMsgId;
+        return newMsgId
     }
 
     /**
@@ -192,20 +192,20 @@ class MTProtoState {
      * @param correctMsgId
      */
     updateTimeOffset(correctMsgId) {
-        const bad = this._getNewMsgId();
-        const old = this.timeOffset;
-        const now = Math.floor(new Date().getTime() / 1000);
-        const correct = correctMsgId >> 32n;
-        this.timeOffset = correct - now;
+        const bad = this._getNewMsgId()
+        const old = this.timeOffset
+        const now = Math.floor(new Date().getTime() / 1000)
+        const correct = correctMsgId >> 32n
+        this.timeOffset = correct - now
 
         if (this.timeOffset !== old) {
-            this._lastMsgId = 0;
+            this._lastMsgId = 0
             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
     }
 
     /**
@@ -216,13 +216,13 @@ class MTProtoState {
      */
     _getSeqNo(contentRelated) {
         if (contentRelated) {
-            const result = this._sequence * 2 + 1;
-            this._sequence += 1;
-            return result;
+            const result = this._sequence * 2 + 1
+            this._sequence += 1
+            return result
         } else {
-            return this._sequence * 2;
+            return this._sequence * 2
         }
     }
 }
 
-module.exports = MTProtoState;
+module.exports = MTProtoState

+ 10 - 10
gramjs/network/RequestState.js

@@ -1,16 +1,16 @@
 class RequestState {
     constructor(request, after = null) {
-        this.containerId = null;
-        this.msgId = null;
-        this.request = request;
-        this.data = request.bytes;
-        this.after = after;
-        this.result = null;
+        this.containerId = null
+        this.msgId = null
+        this.request = request
+        this.data = request.bytes
+        this.after = after
+        this.result = null
         this.promise = new Promise((resolve, reject) => {
-            this.resolve = resolve;
-            this.reject = reject;
-        });
+            this.resolve = resolve
+            this.reject = reject
+        })
     }
 }
 
-module.exports = RequestState;
+module.exports = RequestState

+ 48 - 51
gramjs/network/connection/Connection.js

@@ -1,7 +1,6 @@
-const { PromiseSocket } = require('promise-socket');
-const { Socket } = require('net');
-const Helpers = require('../../utils/Helpers');
-const AsyncQueue = require("../../extensions/AsyncQueue");
+const { PromiseSocket } = require('promise-socket')
+const { Socket } = require('net')
+const AsyncQueue = require('../../extensions/AsyncQueue')
 /**
  * The `Connection` class is a wrapper around ``asyncio.open_connection``.
  *
@@ -17,126 +16,124 @@ class Connection {
     PacketCodecClass = null;
 
     constructor(ip, port, dcId, loggers) {
-        this._ip = ip;
-        this._port = port;
-        this._dcId = dcId;
-        this._log = loggers;
-        this._reader = null;
-        this._writer = null;
-        this._connected = false;
-        this._sendTask = null;
-        this._recvTask = null;
-        this._codec = null;
-        this._obfuscation = null; // TcpObfuscated and MTProxy
-        this._sendArray = new AsyncQueue();
-        this._recvArray = new AsyncQueue();
-        this.socket = new PromiseSocket(new Socket());
+        this._ip = ip
+        this._port = port
+        this._dcId = dcId
+        this._log = loggers
+        this._reader = null
+        this._writer = null
+        this._connected = false
+        this._sendTask = null
+        this._recvTask = null
+        this._codec = null
+        this._obfuscation = null // TcpObfuscated and MTProxy
+        this._sendArray = new AsyncQueue()
+        this._recvArray = new AsyncQueue()
+        this.socket = new PromiseSocket(new Socket())
     }
 
     async _connect() {
-        await this.socket.connect(this._port, this._ip);
+        await this.socket.connect(this._port, this._ip)
 
         // await this.socket.connect({host: this._ip, port: this._port});
-        this._codec = new this.PacketCodecClass(this);
-        this._initConn();
+        this._codec = new this.PacketCodecClass(this)
+        this._initConn()
     }
 
     async connect() {
-        await this._connect();
-        this._connected = true;
-        this._sendTask = this._sendLoop();
-        this._recvTask = this._recvLoop();
+        await this._connect()
+        this._connected = true
+        this._sendTask = this._sendLoop()
+        this._recvTask = this._recvLoop()
     }
 
     async disconnect() {
-        this._connected = false;
-        await this.socket.end();
+        this._connected = false
+        await this.socket.end()
     }
 
     async send(data) {
         if (!this._connected) {
-            throw new Error('Not connected');
+            throw new Error('Not connected')
         }
-        await this._sendArray.push(data);
+        await this._sendArray.push(data)
     }
 
     async recv() {
         while (this._connected) {
-            const result = await this._recvArray.pop();
+            const result = await this._recvArray.pop()
 
             // null = sentinel value = keep trying
             if (result) {
-                return result;
+                return result
             }
         }
-        throw new Error('Not connected');
+        throw new Error('Not connected')
     }
 
     async _sendLoop() {
         // TODO handle errors
         try {
             while (this._connected) {
-
-                await this._send(await this._sendArray.pop());
+                await this._send(await this._sendArray.pop())
             }
         } catch (e) {
-            console.log(e);
-            this._log.info('The server closed the connection while sending');
+            console.log(e)
+            this._log.info('The server closed the connection while sending')
         }
     }
 
     async _recvLoop() {
-        let data;
+        let data
         while (this._connected) {
             try {
-                data = await this._recv();
+                data = await this._recv()
             } catch (e) {
-                console.log(e);
-                this._log.info('The server closed the connection');
+                console.log(e)
+                this._log.info('The server closed the connection')
             }
-            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) {
-        const encodedPacket = this._codec.encodePacket(data);
-        await this.socket.write(encodedPacket);
+        const encodedPacket = this._codec.encodePacket(data)
+        await 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', '')}`
     }
 }
 
 class PacketCodec {
     constructor(connection) {
-        this._conn = connection;
+        this._conn = connection
     }
 
     encodePacket(data) {
-        throw new Error('Not Implemented');
+        throw new Error('Not Implemented')
 
         // Override
     }
 
     async readPacket(reader) {
         // override
-        throw new Error('Not Implemented');
+        throw new Error('Not Implemented')
     }
 }
 
 module.exports = {
     Connection,
     PacketCodec,
-};
+}

+ 24 - 24
gramjs/network/connection/TCPFull.js

@@ -1,22 +1,22 @@
-const { Connection, PacketCodec } = require('./Connection');
-const struct = require('python-struct');
-const { crc32 } = require('crc');
-const { InvalidChecksumError } = require('../../errors/Common');
+const { Connection, PacketCodec } = require('./Connection')
+const struct = require('python-struct')
+const { crc32 } = require('crc')
+const { InvalidChecksumError } = require('../../errors/Common')
 
 class FullPacketCodec extends PacketCodec {
     constructor(connection) {
-        super(connection);
-        this._sendCounter = 0; // Telegram will ignore us otherwise
+        super(connection)
+        this._sendCounter = 0 // Telegram will ignore us otherwise
     }
 
     encodePacket(data) {
         // https://core.telegram.org/mtproto#tcp-transport
         // total length, sequence number, packet and checksum (CRC32)
-        const length = data.length + 12;
-        data = Buffer.concat([struct.pack('<ii', length, this._sendCounter), data]);
-        const crc = struct.pack('<I', crc32(data));
-        this._sendCounter += 1;
-        return Buffer.concat([data, crc]);
+        const length = data.length + 12
+        data = Buffer.concat([struct.pack('<ii', length, this._sendCounter), data])
+        const crc = struct.pack('<I', crc32(data))
+        this._sendCounter += 1
+        return Buffer.concat([data, crc])
     }
 
     /**
@@ -25,26 +25,26 @@ class FullPacketCodec extends PacketCodec {
      * @returns {Promise<*>}
      */
     async readPacket(reader) {
-        const packetLenSeq = await reader.read(8); // 4 and 4
+        const packetLenSeq = await reader.read(8) // 4 and 4
         // process.exit(0);
 
         if (packetLenSeq === undefined) {
-            console.log('connection closed. exiting');
-            process.exit(0);
-            throw new Error('closed connection');
+            console.log('connection closed. exiting')
+            process.exit(0)
+            throw new Error('closed connection')
         }
 
-        const res = struct.unpack('<ii', packetLenSeq);
-        const [packetLen] = res;
-        let body = await reader.read(packetLen - 8);
-        const [checksum] = struct.unpack('<I', body.slice(-4));
-        body = body.slice(0, -4);
+        const res = struct.unpack('<ii', packetLenSeq)
+        const [packetLen] = res
+        let body = await reader.read(packetLen - 8)
+        const [checksum] = struct.unpack('<I', body.slice(-4))
+        body = body.slice(0, -4)
 
-        const validChecksum = crc32(Buffer.concat([packetLenSeq, body]));
+        const validChecksum = crc32(Buffer.concat([packetLenSeq, body]))
         if (!(validChecksum === checksum)) {
-            throw new InvalidChecksumError(checksum, validChecksum);
+            throw new InvalidChecksumError(checksum, validChecksum)
         }
-        return body;
+        return body
     }
 }
 
@@ -55,4 +55,4 @@ class ConnectionTCPFull extends Connection {
 module.exports = {
     FullPacketCodec,
     ConnectionTCPFull,
-};
+}

+ 46 - 50
gramjs/sessions/Session.js

@@ -1,34 +1,30 @@
-const Helpers = require('../utils/Helpers');
-const fs = require('fs').promises;
-const { existsSync, readFileSync } = require('fs');
-const AuthKey = require('../crypto/AuthKey');
+const Helpers = require('../utils/Helpers')
+const fs = require('fs').promises
+const { existsSync, readFileSync } = require('fs')
+const AuthKey = require('../crypto/AuthKey')
 
-const DEFAULT_DC_ID = 4;
-const DEFAULT_IPV4_IP = '149.154.167.92';
-const DEFAULT_IPV6_IP = '[2001:67c:4e8:f002::a]';
-const DEFAULT_PORT = 443;
 
 BigInt.toJSON = function() {
-    return { fool: this.fool.toString('hex') };
-};
+    return { fool: this.fool.toString('hex') }
+}
 BigInt.parseJson = function() {
-    return { fool: BigInt('0x' + this.fool) };
-};
+    return { fool: BigInt('0x' + this.fool) }
+}
 
 class Session {
     constructor(sessionUserId) {
-        this.sessionUserId = sessionUserId;
-        this.serverAddress = DEFAULT_IPV4_IP;
-        this.port = DEFAULT_PORT;
+        this.sessionUserId = sessionUserId
+        this.serverAddress = 0
+        this.port = 0
         // this.serverAddress = "localhost";
         // this.port = 21;
-        this.authKey = undefined;
-        this.id = Helpers.generateRandomLong(false);
-        this.sequence = 0;
-        this.salt = 0n; // Unsigned long
-        this.timeOffset = 0n;
-        this.lastMessageId = 0n;
-        this.user = undefined;
+        this.authKey = undefined
+        this.id = Helpers.generateRandomLong(false)
+        this.sequence = 0
+        this.salt = 0n // Unsigned long
+        this.timeOffset = 0n
+        this.lastMessageId = 0n
+        this.user = undefined
     }
 
     /**
@@ -38,62 +34,62 @@ class Session {
         if (this.sessionUserId) {
             const str = JSON.stringify(this, function(key, value) {
                 if (typeof value === 'bigint') {
-                    return value.toString() + 'n';
+                    return value.toString() + 'n'
                 } else {
-                    return value;
+                    return value
                 }
-            });
+            })
 
-            await fs.writeFile(`${this.sessionUserId}.session`, str);
+            await fs.writeFile(`${this.sessionUserId}.session`, str)
         }
     }
 
     static tryLoadOrCreateNew(sessionUserId) {
         if (sessionUserId === undefined) {
-            return new Session();
+            return new Session()
         }
-        const filepath = `${sessionUserId}.session`;
+        const filepath = `${sessionUserId}.session`
         if (existsSync(filepath)) {
             const ob = JSON.parse(readFileSync(filepath, 'utf-8'), function(key, value) {
                 if (typeof value == 'string' && value.match(/(\d+)n/)) {
-                    return BigInt(value.slice(0, -1));
+                    return BigInt(value.slice(0, -1))
                 } else {
-                    return value;
+                    return value
                 }
-            });
+            })
 
-            const authKey = new AuthKey(Buffer.from(ob.authKey._key.data));
-            const session = new Session(ob.sessionUserId);
-            session.serverAddress = ob.serverAddress;
-            session.port = ob.port;
+            const authKey = new AuthKey(Buffer.from(ob.authKey._key.data))
+            const session = new Session(ob.sessionUserId)
+            session.serverAddress = ob.serverAddress
+            session.port = ob.port
             // this.serverAddress = "localhost";
             // this.port = 21;
-            session.authKey = authKey;
-            session.id = ob.id;
-            session.sequence = ob.sequence;
-            session.salt = ob.salt; // Unsigned long
-            session.timeOffset = ob.timeOffset;
-            session.lastMessageId = ob.lastMessageId;
-            session.user = ob.user;
-            return session;
+            session.authKey = authKey
+            session.id = ob.id
+            session.sequence = ob.sequence
+            session.salt = ob.salt // Unsigned long
+            session.timeOffset = ob.timeOffset
+            session.lastMessageId = ob.lastMessageId
+            session.user = ob.user
+            return session
         } else {
-            return new Session(sessionUserId);
+            return new Session(sessionUserId)
         }
     }
 
     getNewMsgId() {
-        const msTime = new Date().getTime();
+        const msTime = new Date().getTime()
         let newMessageId =
             (BigInt(BigInt(Math.floor(msTime / 1000)) + this.timeOffset) << 32n) |
             (BigInt(msTime % 1000) << 22n) |
-            (BigInt(Helpers.getRandomInt(0, 524288)) << 2n); // 2^19
+            (BigInt(Helpers.getRandomInt(0, 524288)) << 2n) // 2^19
 
         if (this.lastMessageId >= newMessageId) {
-            newMessageId = this.lastMessageId + 4n;
+            newMessageId = this.lastMessageId + 4n
         }
-        this.lastMessageId = newMessageId;
-        return newMessageId;
+        this.lastMessageId = newMessageId
+        return newMessageId
     }
 }
 
-module.exports = Session;
+module.exports = Session

+ 15 - 15
gramjs/tl/MTProtoRequest.js

@@ -1,37 +1,37 @@
 class MTProtoRequest {
     constructor() {
-        this.sent = false;
-        this.msgId = 0; // long
-        this.sequence = 0;
+        this.sent = false
+        this.msgId = 0 // long
+        this.sequence = 0
 
-        this.dirty = false;
-        this.sendTime = 0;
-        this.confirmReceived = false;
+        this.dirty = false
+        this.sendTime = 0
+        this.confirmReceived = false
 
         // These should be overrode
 
-        this.constructorId = 0;
-        this.confirmed = false;
-        this.responded = false;
+        this.constructorId = 0
+        this.confirmed = false
+        this.responded = false
     }
 
     // these should not be overrode
     onSendSuccess() {
-        this.sendTime = new Date().getTime();
-        this.sent = true;
+        this.sendTime = new Date().getTime()
+        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) {}
@@ -39,4 +39,4 @@ class MTProtoRequest {
     onException(exception) {}
 }
 
-module.exports = MTProtoRequest;
+module.exports = MTProtoRequest

+ 16 - 16
gramjs/tl/core/GZIPPacked.js

@@ -1,45 +1,45 @@
-const { TLObject } = require('../tlobject');
-const struct = require('python-struct');
-const { ungzip } = require('node-gzip');
-const { gzip } = require('node-gzip');
+const { TLObject } = require('../tlobject')
+const struct = require('python-struct')
+const { ungzip } = require('node-gzip')
+const { gzip } = require('node-gzip')
 
 class GZIPPacked extends TLObject {
     static CONSTRUCTOR_ID = 0x3072cfa1;
 
     constructor(data) {
-        super();
-        this.data = data;
-        this.CONSTRUCTOR_ID = 0x3072cfa1;
+        super()
+        this.data = data
+        this.CONSTRUCTOR_ID = 0x3072cfa1
     }
 
     static async gzipIfSmaller(contentRelated, data) {
         if (contentRelated && data.length > 512) {
-            const gzipped = await new GZIPPacked(data).toBytes();
+            const gzipped = await new GZIPPacked(data).toBytes()
             if (gzipped.length < data.length) {
-                return gzipped;
+                return gzipped
             }
         }
-        return data;
+        return data
     }
 
     async toBytes() {
         return Buffer.concat([
             struct.pack('<I', GZIPPacked.CONSTRUCTOR_ID),
             TLObject.serializeBytes(await gzip(this.data)),
-        ]);
+        ])
     }
 
     static async read(reader) {
-        const constructor = reader.readInt(false);
+        const constructor = reader.readInt(false)
         if (constructor !== GZIPPacked.CONSTRUCTOR_ID) {
-            throw new Error('not equal');
+            throw new Error('not equal')
         }
-        return await gzip(reader.tgReadBytes());
+        return await gzip(reader.tgReadBytes())
     }
 
     static async fromReader(reader) {
-        return new GZIPPacked(await ungzip(reader.tgReadBytes()));
+        return new GZIPPacked(await ungzip(reader.tgReadBytes()))
     }
 }
 
-module.exports = GZIPPacked;
+module.exports = GZIPPacked

+ 17 - 17
gramjs/tl/core/MessageContainer.js

@@ -1,5 +1,5 @@
-const { TLObject } = require('../tlobject');
-const TLMessage = require('./TLMessage');
+const { TLObject } = require('../tlobject')
+const TLMessage = require('./TLMessage')
 
 class MessageContainer extends TLObject {
     static CONSTRUCTOR_ID = 0x73f1f8dc;
@@ -20,26 +20,26 @@ class MessageContainer extends TLObject {
     static MAXIMUM_LENGTH = 100;
 
     constructor(messages) {
-        super();
-        this.CONSTRUCTOR_ID = 0x73f1f8dc;
-        this.messages = messages;
+        super()
+        this.CONSTRUCTOR_ID = 0x73f1f8dc
+        this.messages = messages
     }
 
     static async fromReader(reader) {
-        const messages = [];
-        const length = reader.readInt();
+        const messages = []
+        const length = reader.readInt()
         for (let x = 0; x < length; x++) {
-            const msgId = reader.readLong();
-            const seqNo = reader.readInt();
-            const length = reader.readInt();
-            const before = reader.tellPosition();
-            const obj = reader.tgReadObject();
-            reader.setPosition(before + length);
-            const tlMessage = new TLMessage(msgId, seqNo, obj);
-            messages.push(tlMessage);
+            const msgId = reader.readLong()
+            const seqNo = reader.readInt()
+            const length = reader.readInt()
+            const before = reader.tellPosition()
+            const obj = reader.tgReadObject()
+            reader.setPosition(before + length)
+            const tlMessage = new TLMessage(msgId, seqNo, obj)
+            messages.push(tlMessage)
         }
-        return new MessageContainer(messages);
+        return new MessageContainer(messages)
     }
 }
 
-module.exports = MessageContainer;
+module.exports = MessageContainer

+ 15 - 15
gramjs/tl/core/RPCResult.js

@@ -1,33 +1,33 @@
-const { TLObject } = require('../tlobject');
-const { RpcError } = require('../types');
-const GZIPPacked = require('./GZIPPacked');
+const { TLObject } = require('../tlobject')
+const { RpcError } = require('../types')
+const GZIPPacked = require('./GZIPPacked')
 
 class RPCResult extends TLObject {
     static CONSTRUCTOR_ID = 0xf35c6d01;
 
     constructor(reqMsgId, body, error) {
-        super();
-        this.CONSTRUCTOR_ID = 0xf35c6d01;
-        this.reqMsgId = reqMsgId;
-        this.body = body;
-        this.error = error;
+        super()
+        this.CONSTRUCTOR_ID = 0xf35c6d01
+        this.reqMsgId = reqMsgId
+        this.body = body
+        this.error = error
     }
 
     static async fromReader(reader) {
-        const msgId = reader.readLong();
-        const innerCode = reader.readInt(false);
+        const msgId = reader.readLong()
+        const innerCode = reader.readInt(false)
         if (innerCode === RpcError.CONSTRUCTOR_ID) {
-            return new RPCResult(msgId, null, RpcError.fromReader(reader));
+            return new RPCResult(msgId, null, RpcError.fromReader(reader))
         }
         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);
+        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(), null);
+        return new RPCResult(msgId, reader.read(), null)
     }
 }
 
-module.exports = RPCResult;
+module.exports = RPCResult

+ 6 - 6
gramjs/tl/core/TLMessage.js

@@ -1,14 +1,14 @@
-const { TLObject } = require('../tlobject');
+const { TLObject } = require('../tlobject')
 
 class TLMessage extends TLObject {
     static SIZE_OVERHEAD = 12;
 
     constructor(msgId, seqNo, obj) {
-        super();
-        this.msgId = msgId;
-        this.seqNo = seqNo;
-        this.obj = obj;
+        super()
+        this.msgId = msgId
+        this.seqNo = seqNo
+        this.obj = obj
     }
 }
 
-module.exports = TLMessage;
+module.exports = TLMessage

+ 7 - 7
gramjs/tl/core/index.js

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

+ 4 - 4
gramjs/tl/index.js

@@ -1,9 +1,9 @@
-const types = require('./types');
-const functions = require('./functions');
-const patched = null;
+const types = require('./types')
+const functions = require('./functions')
+const patched = null
 
 module.exports = {
     types,
     functions,
     patched,
-};
+}

+ 22 - 22
gramjs/tl/tlobject.js

@@ -1,4 +1,4 @@
-const struct = require('python-struct');
+const struct = require('python-struct')
 
 class TLObject {
     CONSTRUCTOR_ID = null;
@@ -15,47 +15,47 @@ class TLObject {
     static serializeBytes(data) {
         if (!(data instanceof Buffer)) {
             if (typeof data == 'string') {
-                data = Buffer.from(data);
+                data = Buffer.from(data)
             } else {
-                throw Error(`Bytes or str expected, not ${data.constructor.name}`);
+                throw Error(`Bytes or str expected, not ${data.constructor.name}`)
             }
         }
-        const r = [];
-        let padding;
+        const r = []
+        let padding
         if (data.length < 254) {
-            padding = (data.length + 1) % 4;
+            padding = (data.length + 1) % 4
             if (padding !== 0) {
-                padding = 4 - padding;
+                padding = 4 - padding
             }
-            r.push(Buffer.from([data.length]));
-            r.push(data);
+            r.push(Buffer.from([data.length]))
+            r.push(data)
         } else {
-            padding = data.length % 4;
+            padding = data.length % 4
             if (padding !== 0) {
-                padding = 4 - padding;
+                padding = 4 - padding
             }
-            r.push(Buffer.from([254, data.length % 256, (data.length >> 8) % 256, (data.length >> 16) % 256]));
-            r.push(data);
+            r.push(Buffer.from([254, data.length % 256, (data.length >> 8) % 256, (data.length >> 16) % 256]))
+            r.push(data)
         }
-        r.push(Buffer.alloc(padding).fill(0));
-        return Buffer.concat(r);
+        r.push(Buffer.alloc(padding).fill(0))
+        return Buffer.concat(r)
     }
 
     static serializeDate(dt) {
         if (!dt) {
-            return Buffer.alloc(4).fill(0);
+            return Buffer.alloc(4).fill(0)
         }
         if (dt instanceof Date) {
-            dt = Math.floor((Date.now() - dt.getTime()) / 1000);
+            dt = Math.floor((Date.now() - dt.getTime()) / 1000)
         }
         if (typeof dt == 'number') {
-            return struct.pack('<i', dt);
+            return struct.pack('<i', dt)
         }
-        throw Error(`Cannot interpret "${dt}" as a date`);
+        throw Error(`Cannot interpret "${dt}" as a date`)
     }
 
     fromReader(reader) {
-        throw Error('not implemented');
+        throw Error('not implemented')
     }
 }
 
@@ -69,7 +69,7 @@ class TLRequest extends TLObject {
      * @returns {boolean}
      */
     readResult(reader) {
-        return reader.tgReadObject();
+        return reader.tgReadObject()
     }
 
     async resolve(self, client, utils) {}
@@ -78,4 +78,4 @@ class TLRequest extends TLObject {
 module.exports = {
     TLObject,
     TLRequest,
-};
+}

+ 84 - 185
gramjs/utils/Helpers.js

@@ -1,57 +1,56 @@
-const crypto = require('crypto');
-const fs = require('fs').promises;
+const crypto = require('crypto')
+const fs = require('fs').promises
 
 class Helpers {
     static readBigIntFromBuffer(buffer, little = true, signed = false) {
-        let randBuffer = Buffer.from(buffer);
-        const bytesNumber = randBuffer.length;
+        let randBuffer = Buffer.from(buffer)
+        const bytesNumber = randBuffer.length
         if (little) {
-            randBuffer = randBuffer.reverse();
+            randBuffer = randBuffer.reverse()
         }
-        let bigInt = BigInt('0x' + randBuffer.toString('hex'));
+        let bigInt = BigInt('0x' + randBuffer.toString('hex'))
         if (signed && Math.floor(bigInt.toString('2').length / 8) >= bytesNumber) {
-            bigInt -= 2n ** BigInt(bytesNumber * 8);
+            bigInt -= 2n ** BigInt(bytesNumber * 8)
         }
-        return bigInt;
+        return bigInt
     }
 
     static readBufferFromBigInt(bigInt, bytesNumber, little = true, signed = false) {
-        const bitLength = bigInt.toString('2').length;
+        const bitLength = bigInt.toString('2').length
 
-        const bytes = Math.ceil(bitLength / 8);
+        const bytes = Math.ceil(bitLength / 8)
         if (bytesNumber < bytes) {
-            throw new Error('OverflowError: int too big to convert');
-        } else if (bytesNumber > bytes) {
+            throw new Error('OverflowError: int too big to convert')
         }
         if (!signed && bigInt < 0) {
-            throw new Error('Cannot convert to unsigned');
+            throw new Error('Cannot convert to unsigned')
         }
-        let below = false;
+        let below = false
         if (bigInt < 0) {
-            below = true;
-            bigInt = -bigInt;
+            below = true
+            bigInt = -bigInt
         }
 
-        const hex = bigInt.toString('16').padStart(bytesNumber * 2, '0');
-        let l = Buffer.from(hex, 'hex');
+        const hex = bigInt.toString('16').padStart(bytesNumber * 2, '0')
+        let l = Buffer.from(hex, 'hex')
         if (little) {
-            l = l.reverse();
+            l = l.reverse()
         }
 
         if (signed && below) {
             if (little) {
-                l[0] = 256 - l[0];
+                l[0] = 256 - l[0]
                 for (let i = 1; i < l.length; i++) {
-                    l[i] = 255 - l[i];
+                    l[i] = 255 - l[i]
                 }
             } else {
-                l[l.length - 1] = 256 - l[l.length - 1];
+                l[l.length - 1] = 256 - l[l.length - 1]
                 for (let i = 0; i < l.length - 1; i++) {
-                    l[i] = 255 - l[i];
+                    l[i] = 255 - l[i]
                 }
             }
         }
-        return l;
+        return l
     }
 
     /**
@@ -59,7 +58,7 @@ class Helpers {
      * @returns {BigInt}
      */
     static generateRandomLong(signed = true) {
-        return this.readBigIntFromBuffer(Helpers.generateRandomBytes(8), true, signed);
+        return this.readBigIntFromBuffer(Helpers.generateRandomBytes(8), true, signed)
     }
 
     /**
@@ -69,7 +68,7 @@ class Helpers {
      * @returns {number}
      */
     static mod(n, m) {
-        return ((n % m) + m) % m;
+        return ((n % m) + m) % m
     }
 
     /**
@@ -78,7 +77,7 @@ class Helpers {
      * @returns {Buffer}
      */
     static generateRandomBytes(count) {
-        return crypto.randomBytes(count);
+        return crypto.randomBytes(count)
     }
 
     /**
@@ -87,28 +86,28 @@ class Helpers {
      * @returns {Promise<void>}
      */
     static async loadSettings(path = 'api/settings') {
-        const settings = {};
-        let left;
-        let right;
-        let valuePair;
+        const settings = {}
+        let left
+        let right
+        let valuePair
 
-        const data = await fs.readFile(path, 'utf-8');
+        const data = await fs.readFile(path, 'utf-8')
 
         for (const line of data.toString().split('\n')) {
-            valuePair = line.split('=');
+            valuePair = line.split('=')
             if (valuePair.length !== 2) {
-                break;
+                break
             }
-            left = valuePair[0].replace(/ \r?\n|\r/g, '');
-            right = valuePair[1].replace(/ \r?\n|\r/g, '');
+            left = valuePair[0].replace(/ \r?\n|\r/g, '')
+            right = valuePair[1].replace(/ \r?\n|\r/g, '')
             if (!isNaN(right)) {
-                settings[left] = Number.parseInt(right);
+                settings[left] = Number.parseInt(right)
             } else {
-                settings[left] = right;
+                settings[left] = right
             }
         }
 
-        return settings;
+        return settings
     }
 
     /**
@@ -120,16 +119,16 @@ class Helpers {
      */
 
     static calcKey(sharedKey, msgKey, client) {
-        const x = client === true ? 0 : 8;
-        const sha1a = Helpers.sha1(Buffer.concat([msgKey, sharedKey.slice(x, x + 32)]));
+        const x = client === true ? 0 : 8
+        const sha1a = Helpers.sha1(Buffer.concat([msgKey, sharedKey.slice(x, x + 32)]))
         const sha1b = Helpers.sha1(
-            Buffer.concat([sharedKey.slice(x + 32, x + 48), msgKey, sharedKey.slice(x + 48, x + 64)])
-        );
-        const sha1c = Helpers.sha1(Buffer.concat([sharedKey.slice(x + 64, x + 96), msgKey]));
-        const sha1d = Helpers.sha1(Buffer.concat([msgKey, sharedKey.slice(x + 96, x + 128)]));
-        const key = Buffer.concat([sha1a.slice(0, 8), sha1b.slice(8, 20), sha1c.slice(4, 16)]);
-        const iv = Buffer.concat([sha1a.slice(8, 20), sha1b.slice(0, 8), sha1c.slice(16, 20), sha1d.slice(0, 8)]);
-        return { key, iv };
+            Buffer.concat([sharedKey.slice(x + 32, x + 48), msgKey, sharedKey.slice(x + 48, x + 64)]),
+        )
+        const sha1c = Helpers.sha1(Buffer.concat([sharedKey.slice(x + 64, x + 96), msgKey]))
+        const sha1d = Helpers.sha1(Buffer.concat([msgKey, sharedKey.slice(x + 96, x + 128)]))
+        const key = Buffer.concat([sha1a.slice(0, 8), sha1b.slice(8, 20), sha1c.slice(4, 16)])
+        const iv = Buffer.concat([sha1a.slice(8, 20), sha1b.slice(0, 8), sha1c.slice(16, 20), sha1d.slice(0, 8)])
+        return { key, iv }
     }
 
     /**
@@ -138,7 +137,7 @@ class Helpers {
      * @returns {Buffer}
      */
     static calcMsgKey(data) {
-        return Helpers.sha1(data).slice(4, 20);
+        return Helpers.sha1(data).slice(4, 20)
     }
 
     /**
@@ -148,14 +147,14 @@ class Helpers {
      * @returns {{key: Buffer, iv: Buffer}}
      */
     static generateKeyDataFromNonce(serverNonce, newNonce) {
-        serverNonce = Helpers.readBufferFromBigInt(serverNonce, 16, true, true);
-        newNonce = Helpers.readBufferFromBigInt(newNonce, 32, true, true);
-        const hash1 = Helpers.sha1(Buffer.concat([newNonce, serverNonce]));
-        const hash2 = Helpers.sha1(Buffer.concat([serverNonce, newNonce]));
-        const hash3 = Helpers.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)]);
-        return { key: keyBuffer, iv: ivBuffer };
+        serverNonce = Helpers.readBufferFromBigInt(serverNonce, 16, true, true)
+        newNonce = Helpers.readBufferFromBigInt(newNonce, 32, true, true)
+        const hash1 = Helpers.sha1(Buffer.concat([newNonce, serverNonce]))
+        const hash2 = Helpers.sha1(Buffer.concat([serverNonce, newNonce]))
+        const hash3 = Helpers.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)])
+        return { key: keyBuffer, iv: ivBuffer }
     }
 
     /**
@@ -164,9 +163,9 @@ class Helpers {
      * @returns {Buffer}
      */
     static sha1(data) {
-        const shaSum = crypto.createHash('sha1');
-        shaSum.update(data);
-        return shaSum.digest();
+        const shaSum = crypto.createHash('sha1')
+        shaSum.update(data)
+        return shaSum.digest()
     }
 
     /**
@@ -175,109 +174,9 @@ class Helpers {
      * @returns {Buffer}
      */
     static sha256(data) {
-        const shaSum = crypto.createHash('sha256');
-        shaSum.update(data);
-        return shaSum.digest();
-    }
-
-    /**
-     * Reads a Telegram-encoded string
-     * @param buffer {Buffer}
-     * @param offset {number}
-     * @returns {{string: string, offset: number}}
-     */
-    static tgReadString(buffer, offset) {
-        const res = Helpers.tgReadByte(buffer, offset);
-        offset = res.offset;
-        const string = res.data.toString('utf8');
-        return { string, offset };
-    }
-
-    /**
-     *
-     * @param reader {Buffer}
-     * @param offset {number}
-     */
-    static tgReadObject(reader, offset) {
-        const constructorId = reader.readUInt32LE(offset);
-        offset += 4;
-        const clazz = tlobjects[constructorId];
-        if (clazz === undefined) {
-            /**
-             * The class was None, but there's still a
-             *  chance of it being a manually parsed value like bool!
-             */
-            if (constructorId === 0x997275b5) {
-                return true;
-            } else if (constructorId === 0xbc799737) {
-                return false;
-            }
-            throw Error('type not found ' + constructorId);
-        }
-        return undefined;
-    }
-
-    /**
-     *
-     * @param buffer {Buffer}
-     * @param offset {Number}
-     * @returns {{data: Buffer, offset: Number}}
-     */
-    static tgReadByte(buffer, offset) {
-        const firstByte = buffer[offset];
-        offset += 1;
-        let padding;
-        let length;
-        if (firstByte === 254) {
-            length = buffer.readInt8(offset) | (buffer.readInt8(offset + 1) << 8) | (buffer.readInt8(offset + 2) << 16);
-            offset += 3;
-            padding = length % 4;
-        } else {
-            length = firstByte;
-            padding = (length + 1) % 4;
-        }
-
-        const data = buffer.slice(offset, offset + length);
-
-        offset += length;
-
-        if (padding > 0) {
-            padding = 4 - padding;
-            offset += padding;
-        }
-
-        return { data: data, offset: offset };
-    }
-
-    static tgWriteString(string) {
-        return Helpers.tgWriteBytes(Buffer.from(string, 'utf8'));
-    }
-
-    static tgWriteBytes(data) {
-        let buffer;
-        let padding;
-
-        if (data.length < 254) {
-            padding = (data.length + 1) % 4;
-            if (padding !== 0) {
-                padding = 4 - padding;
-            }
-            buffer = Buffer.concat([Buffer.from([data.length]), data]);
-        } else {
-            padding = data.length % 4;
-            if (padding !== 0) {
-                padding = 4 - padding;
-            }
-            buffer = Buffer.concat([
-                Buffer.from([254]),
-                Buffer.from([data.length % 256]),
-                Buffer.from([(data.length >> 8) % 256]),
-                Buffer.from([(data.length >> 16) % 256]),
-                data,
-            ]);
-        }
-
-        return Buffer.concat([buffer, Buffer.alloc(padding).fill(0)]);
+        const shaSum = crypto.createHash('sha256')
+        shaSum.update(data)
+        return shaSum.digest()
     }
 
     /**
@@ -288,20 +187,20 @@ class Helpers {
      * @returns {bigint}
      */
     static modExp(a, b, n) {
-        a = a % n;
-        let result = 1n;
-        let x = a;
+        a = a % n
+        let result = 1n
+        let x = a
         while (b > 0n) {
-            const leastSignificantBit = b % 2n;
-            b = b / 2n;
+            const leastSignificantBit = b % 2n
+            b = b / 2n
             if (leastSignificantBit === 1n) {
-                result = result * x;
-                result = result % n;
+                result = result * x
+                result = result % n
             }
-            x = x * x;
-            x = x % n;
+            x = x * x
+            x = x % n
         }
-        return result;
+        return result
     }
 
     /**
@@ -311,9 +210,9 @@ class Helpers {
      * @returns {number}
      */
     static getRandomInt(min, max) {
-        min = Math.ceil(min);
-        max = Math.floor(max);
-        return Math.floor(Math.random() * (max - min + 1)) + min;
+        min = Math.ceil(min)
+        max = Math.floor(max)
+        return Math.floor(Math.random() * (max - min + 1)) + min
     }
 
     /**
@@ -321,7 +220,7 @@ class Helpers {
      * @param ms time in milliseconds
      * @returns {Promise}
      */
-    static sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
+    static sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
 
     /**
      * Checks if the obj is an array
@@ -329,18 +228,18 @@ class Helpers {
      * @returns {boolean}
      */
     static isArrayLike(obj) {
-        if (!obj) return false;
-        const l = obj.length;
-        if (typeof l != 'number' || l < 0) return false;
-        if (Math.floor(l) !== l) return false;
+        if (!obj) return false
+        const l = obj.length
+        if (typeof l != 'number' || l < 0) return false
+        if (Math.floor(l) !== l) return false
         // fast check
-        if (l > 0 && !(l - 1 in obj)) return false;
+        if (l > 0 && !(l - 1 in obj)) return false
         // more complete check (optional)
         for (let i = 0; i < l; ++i) {
-            if (!(i in obj)) return false;
+            if (!(i in obj)) return false
         }
-        return true;
+        return true
     }
 }
 
-module.exports = Helpers;
+module.exports = Helpers

+ 92 - 92
gramjs_generator/docswriter.js

@@ -1,6 +1,6 @@
-const fs = require('fs');
-const path = require('path');
-const util = require('util');
+const fs = require('fs')
+const path = require('path')
+const util = require('util')
 
 class DocsWriter {
     /**
@@ -10,23 +10,23 @@ class DocsWriter {
      * creating the parent directories when used if required.
      */
     constructor(filename, typeToPath) {
-        this.filename = filename;
-        this._parent = path.join(this.filename, '..');
-        this.handle = null;
-        this.title = '';
+        this.filename = filename
+        this._parent = path.join(this.filename, '..')
+        this.handle = null
+        this.title = ''
 
         // Should be set before calling adding items to the menu
-        this.menuSeparatorTag = null;
+        this.menuSeparatorTag = null
 
         // Utility functions
-        this.typeToPath = (t) => this._rel(typeToPath(t));
+        this.typeToPath = (t) => this._rel(typeToPath(t))
 
         // Control signals
-        this.menuBegan = false;
-        this.tableColumns = 0;
-        this.tableColumnsLeft = null;
-        this.writeCopyScript = false;
-        this._script = '';
+        this.menuBegan = false
+        this.tableColumns = 0
+        this.tableColumnsLeft = null
+        this.writeCopyScript = false
+        this._script = ''
     }
 
     /**
@@ -36,7 +36,7 @@ class DocsWriter {
     _rel(path_) {
         return path
             .relative(this._parent, path_)
-            .replace(new RegExp(`\\${path.sep}`, 'g'), '/');
+            .replace(new RegExp(`\\${path.sep}`, 'g'), '/')
     }
 
     /**
@@ -45,7 +45,7 @@ class DocsWriter {
      */
     // High level writing
     writeHead(title, cssPath, defaultCss) {
-        this.title = title;
+        this.title = title
         this.write(
             `<!DOCTYPE html>
 <html>
@@ -66,7 +66,7 @@ class DocsWriter {
 </head>
 <body>
 <div id="main_div">`
-        );
+        )
     }
 
     /**
@@ -75,9 +75,9 @@ class DocsWriter {
      */
     setMenuSeparator(img) {
         if (img) {
-            this.menuSeparatorTag = `<img src="${this._rel(img)}" alt="/" />`;
+            this.menuSeparatorTag = `<img src="${this._rel(img)}" alt="/" />`
         } else {
-            this.menuSeparatorTag = null;
+            this.menuSeparatorTag = null
         }
     }
 
@@ -87,28 +87,28 @@ class DocsWriter {
     addMenu(name, link) {
         if (this.menuBegan) {
             if (this.menuSeparatorTag) {
-                this.write(this.menuSeparatorTag);
+                this.write(this.menuSeparatorTag)
             }
         } else {
             // First time, create the menu tag
-            this.write('<ul class="horizontal">');
-            this.menuBegan = true;
+            this.write('<ul class="horizontal">')
+            this.menuBegan = true
         }
 
-        this.write('<li>');
+        this.write('<li>')
 
         if (link) {
-            this.write(`<a href="${this._rel(link)}">`);
+            this.write(`<a href="${this._rel(link)}">`)
         }
 
         // Write the real menu entry text
-        this.write(name);
+        this.write(name)
 
         if (link) {
-            this.write('</a>');
+            this.write('</a>')
         }
 
-        this.write('</li>');
+        this.write('</li>')
     }
 
     /**
@@ -116,10 +116,10 @@ class DocsWriter {
      */
     endMenu() {
         if (!this.menuBegan) {
-            throw new Error('No menu had been started in the first place.');
+            throw new Error('No menu had been started in the first place.')
         }
 
-        this.write('</ul>');
+        this.write('</ul>')
     }
 
     /**
@@ -127,12 +127,12 @@ class DocsWriter {
      * with an optional depth level
      */
     writeTitle(title, level, id) {
-        level = level || 1;
+        level = level || 1
 
         if (id) {
-            this.write(`<h${level} id="${id}">${title}</h${level}>`);
+            this.write(`<h${level} id="${id}">${title}</h${level}>`)
         } else {
-            this.write(`<h${level}>${title}</h${level}>`);
+            this.write(`<h${level}>${title}</h${level}>`)
         }
     }
 
@@ -143,105 +143,105 @@ class DocsWriter {
     writeCode(tlobject) {
         this.write(
             `<pre>---${tlobject.isFunction ? 'functions' : 'types'}---\n`
-        );
+        )
 
         // Write the function or type and its ID
         if (tlobject.namespace) {
-            this.write(tlobject.namespace);
-            this.write('.');
+            this.write(tlobject.namespace)
+            this.write('.')
         }
 
         this.write(
             `${tlobject.name}#${tlobject.id.toString(16).padStart(8, '0')}`
-        );
+        )
 
         // Write all the arguments (or do nothing if there's none)
         for (const arg of tlobject.args) {
-            this.write(' ');
-            const addLink = !arg.genericDefinition && !arg.isGeneric;
+            this.write(' ')
+            const addLink = !arg.genericDefinition && !arg.isGeneric
 
             // "Opening" modifiers
             if (arg.genericDefinition) {
-                this.write('{');
+                this.write('{')
             }
 
             // Argument name
-            this.write(arg.name);
-            this.write(':');
+            this.write(arg.name)
+            this.write(':')
 
             // "Opening" modifiers
             if (arg.isFlag) {
-                this.write(`flags.${arg.flagIndex}?`);
+                this.write(`flags.${arg.flagIndex}?`)
             }
 
             if (arg.isGeneric) {
-                this.write('!');
+                this.write('!')
             }
 
             if (arg.isVector) {
                 this.write(
                     `<a href="${this.typeToPath('vector')}">Vector</a>&lt;`
-                );
+                )
             }
 
             // Argument type
             if (arg.type) {
                 if (addLink) {
-                    this.write(`<a href="${this.typeToPath(arg.type)}">`);
+                    this.write(`<a href="${this.typeToPath(arg.type)}">`)
                 }
 
-                this.write(arg.type);
+                this.write(arg.type)
 
                 if (addLink) {
-                    this.write('</a>');
+                    this.write('</a>')
                 }
             } else {
-                this.write('#');
+                this.write('#')
             }
 
             // "Closing" modifiers
             if (arg.isVector) {
-                this.write('&gt;');
+                this.write('&gt;')
             }
 
             if (arg.genericDefinition) {
-                this.write('}');
+                this.write('}')
             }
         }
 
         // Now write the resulting type (result from a function/type)
-        this.write(' = ');
+        this.write(' = ')
         const [genericName] = tlobject.args
             .filter((arg) => arg.genericDefinition)
-            .map((arg) => arg.name);
+            .map((arg) => arg.name)
 
         if (tlobject.result === genericName) {
             // Generic results cannot have any link
-            this.write(tlobject.result);
+            this.write(tlobject.result)
         } else {
             if (/^vector</i.test(tlobject.result)) {
                 // Notice that we don't simply make up the "Vector" part,
                 // because some requests (as of now, only FutureSalts),
                 // use a lower type name for it (see #81)
-                let [vector, inner] = tlobject.result.split('<');
-                inner = inner.replace(/>+$/, '');
+                let [vector, inner] = tlobject.result.split('<')
+                inner = inner.replace(/>+$/, '')
 
                 this.write(
                     `<a href="${this.typeToPath(vector)}">${vector}</a>&lt;`
-                );
+                )
                 this.write(
                     `<a href="${this.typeToPath(inner)}">${inner}</a>&gt;`
-                );
+                )
             } else {
                 this.write(
                     `<a href="${this.typeToPath(tlobject.result)}">${
                         tlobject.result
                     }</a>`
-                );
+                )
             }
         }
 
-        this.write('</pre>');
+        this.write('</pre>')
     }
 
     /**
@@ -249,9 +249,9 @@ class DocsWriter {
      * create the right amount of columns when adding items to the rows
      */
     beginTable(columnCount) {
-        this.tableColumns = columnCount;
-        this.tableColumnsLeft = 0;
-        this.write('<table>');
+        this.tableColumns = columnCount
+        this.tableColumnsLeft = 0
+        this.write('<table>')
     }
 
     /**
@@ -261,58 +261,58 @@ class DocsWriter {
     addRow(text, link, bold, align) {
         if (!this.tableColumnsLeft) {
             // Starting a new row
-            this.write('<tr>');
-            this.tableColumnsLeft = this.tableColumns;
+            this.write('<tr>')
+            this.tableColumnsLeft = this.tableColumns
         }
 
-        this.write('<td');
+        this.write('<td')
 
         if (align) {
-            this.write(` style="text-align: ${align}"`);
+            this.write(` style="text-align: ${align}"`)
         }
 
-        this.write('>');
+        this.write('>')
 
         if (bold) {
-            this.write('<b>');
+            this.write('<b>')
         }
 
         if (link) {
-            this.write(`<a href="${this._rel(link)}">`);
+            this.write(`<a href="${this._rel(link)}">`)
         }
 
         // Finally write the real table data, the given text
-        this.write(text);
+        this.write(text)
 
         if (link) {
-            this.write('</a>');
+            this.write('</a>')
         }
 
         if (bold) {
-            this.write('</b>');
+            this.write('</b>')
         }
 
-        this.write('</td>');
+        this.write('</td>')
 
-        this.tableColumnsLeft -= 1;
+        this.tableColumnsLeft -= 1
         if (!this.tableColumnsLeft) {
-            this.write('</tr>');
+            this.write('</tr>')
         }
     }
 
     endTable() {
         if (this.tableColumnsLeft) {
-            this.write('</tr>');
+            this.write('</tr>')
         }
 
-        this.write('</table>');
+        this.write('</table>')
     }
 
     /**
      * Writes a paragraph of text
      */
     writeText(text) {
-        this.write(`<p>${text}</p>`);
+        this.write(`<p>${text}</p>`)
     }
 
     /**
@@ -320,20 +320,20 @@ class DocsWriter {
      * to copy 'textToCopy' to clipboard when it's clicked.
      */
     writeCopyButton(text, textToCopy) {
-        this.writeCopyScript = true;
+        this.writeCopyScript = true
         this.write(
             `<button onclick="cp('${textToCopy.replace(
                 /'/g,
                 '\\\''
             )}');">${text}</button>`
-        );
+        )
     }
 
     addScript(src, path) {
         if (path) {
-            this._script += `<script src="${this._rel(path)}"></script>`;
+            this._script += `<script src="${this._rel(path)}"></script>`
         } else if (src) {
-            this._script += `<script>${src}</script>`;
+            this._script += `<script>${src}</script>`
         }
     }
 
@@ -352,10 +352,10 @@ class DocsWriter {
                     'try{document.execCommand("copy")}' +
                     'catch(e){}}' +
                     '</script>'
-            );
+            )
         }
 
-        this.write(`</div>${this._script}</body></html>`);
+        this.write(`</div>${this._script}</body></html>`)
     }
 
     /**
@@ -364,27 +364,27 @@ class DocsWriter {
     // "Low" level writing
     write(s, ...args) {
         if (args.length) {
-            fs.appendFileSync(this.handle, util.format(s, ...args));
+            fs.appendFileSync(this.handle, util.format(s, ...args))
         } else {
-            fs.appendFileSync(this.handle, s);
+            fs.appendFileSync(this.handle, s)
         }
     }
 
     open() {
         // Sanity check
-        const parent = path.join(this.filename, '..');
-        fs.mkdirSync(parent, { recursive: true });
+        const parent = path.join(this.filename, '..')
+        fs.mkdirSync(parent, { recursive: true })
 
-        this.handle = fs.openSync(this.filename, 'w');
+        this.handle = fs.openSync(this.filename, 'w')
 
-        return this;
+        return this
     }
 
     close() {
-        fs.closeSync(this.handle);
+        fs.closeSync(this.handle)
     }
 }
 
 module.exports = {
     DocsWriter,
-};
+}

+ 269 - 269
gramjs_generator/generators/docs.js

@@ -1,9 +1,9 @@
-const fs = require('fs');
-const path = require('path');
-const format = require('string-format');
-const { DocsWriter } = require('../docswriter');
-const { TLObject, Usability } = require('../parsers');
-const { snakeToCamelCase } = require('../utils');
+const fs = require('fs')
+const path = require('path')
+const format = require('string-format')
+const { DocsWriter } = require('../docswriter')
+const { TLObject, Usability } = require('../parsers')
+const { snakeToCamelCase } = require('../utils')
 
 const CORE_TYPES = new Set([
     'int',
@@ -17,69 +17,69 @@ const CORE_TYPES = new Set([
     'true',
     'bytes',
     'date',
-]);
+])
 
-const mkdir = (path) => fs.mkdirSync(path, { recursive: true });
+const mkdir = (path) => fs.mkdirSync(path, { recursive: true })
 
 const titleCase = (text) =>
     text
         .toLowerCase()
         .split(/(\W)/)
         .map((word) => `${word.slice(0, 1).toUpperCase()}${word.slice(1)}`)
-        .join('');
+        .join('')
 
 /**
  * ``ClassName -> class_name.html``.
  */
 const getFileName = (tlobject) => {
-    const name = tlobject instanceof TLObject ? tlobject.name : tlobject;
+    const name = tlobject instanceof TLObject ? tlobject.name : tlobject
     // Courtesy of http://stackoverflow.com/a/1176023/4759433
-    const s1 = name.replace(/(.)([A-Z][a-z]+)/, '$1_$2');
-    const result = s1.replace(/([a-z0-9])([A-Z])/, '$1_$2').toLowerCase();
-    return `${result}.html`;
-};
+    const s1 = name.replace(/(.)([A-Z][a-z]+)/, '$1_$2')
+    const result = s1.replace(/([a-z0-9])([A-Z])/, '$1_$2').toLowerCase()
+    return `${result}.html`
+}
 
 /**
  * ``TLObject -> const { ... } = require(...);``.
  */
 const getImportCode = (tlobject) => {
-    const kind = tlobject.isFunction ? 'functions' : 'types';
-    const ns = tlobject.namespace ? `/${tlobject.namespace}` : '';
-    return `const { ${tlobject.className} } = require('gramjs/tl/${kind}${ns}');`;
-};
+    const kind = tlobject.isFunction ? 'functions' : 'types'
+    const ns = tlobject.namespace ? `/${tlobject.namespace}` : ''
+    return `const { ${tlobject.className} } = require('gramjs/tl/${kind}${ns}');`
+}
 
 /**
  * Returns the path for the given TLObject.
  */
 const getPathFor = (tlobject) => {
-    let outDir = tlobject.isFunction ? 'methods' : 'constructors';
+    let outDir = tlobject.isFunction ? 'methods' : 'constructors'
 
     if (tlobject.namespace) {
-        outDir += `/${tlobject.namespace}`;
+        outDir += `/${tlobject.namespace}`
     }
 
-    return `${outDir}/${getFileName(tlobject)}`;
-};
+    return `${outDir}/${getFileName(tlobject)}`
+}
 
 /**
  * Similar to `getPathFor` but for only type names.
  */
 const getPathForType = (type) => {
     if (CORE_TYPES.has(type.toLowerCase())) {
-        return `index.html#${type.toLowerCase()}`;
+        return `index.html#${type.toLowerCase()}`
     } else if (type.includes('.')) {
-        const [namespace, name] = type.split('.');
-        return `types/${namespace}/${getFileName(name)}`;
+        const [namespace, name] = type.split('.')
+        return `types/${namespace}/${getFileName(name)}`
     } else {
-        return `types/${getFileName(type)}`;
+        return `types/${getFileName(type)}`
     }
-};
+}
 
 /**
  * Finds the <title> for the given HTML file, or (Unknown).
  */
 const findTitle = (htmlFile) => {
-    const f = fs.readFileSync(htmlFile, { encoding: 'utf-8' });
+    const f = fs.readFileSync(htmlFile, { encoding: 'utf-8' })
 
     for (const line of f.split('\n')) {
         if (line.includes('<title>')) {
@@ -87,152 +87,152 @@ const findTitle = (htmlFile) => {
             return line.slice(
                 line.indexOf('<title>') + 7,
                 line.indexOf('</title>')
-            );
+            )
         }
     }
 
-    return '(Unknown)';
-};
+    return '(Unknown)'
+}
 
 /**
  * Builds the menu used for the current ``DocumentWriter``.
  */
 const buildMenu = (docs) => {
-    const paths = [];
-    let current = docs.filename;
+    const paths = []
+    let current = docs.filename
 
     while (current !== '.') {
-        current = path.join(current, '..');
-        paths.push(current);
+        current = path.join(current, '..')
+        paths.push(current)
     }
 
     for (const path_ of paths.reverse()) {
-        const name = path.parse(path_).name;
+        const name = path.parse(path_).name
 
         docs.addMenu(
             name === '.' ? 'API' : titleCase(name),
             `${path_}/index.html`
-        );
+        )
     }
 
     if (path.parse(docs.filename).name !== 'index') {
-        docs.addMenu(docs.title, docs.filename);
+        docs.addMenu(docs.title, docs.filename)
     }
 
-    docs.endMenu();
-};
+    docs.endMenu()
+}
 
 /**
  * Generates the index file for the specified folder
  */
 const generateIndex = (folder, paths, botsIndex, botsIndexPaths) => {
-    botsIndexPaths = botsIndexPaths || [];
+    botsIndexPaths = botsIndexPaths || []
 
     // Determine the namespaces listed here (as sub folders)
     // and the files (.html files) that we should link to
-    const namespaces = [];
-    const files = [];
-    const INDEX = 'index.html';
-    const BOT_INDEX = 'botindex.html';
+    const namespaces = []
+    const files = []
+    const INDEX = 'index.html'
+    const BOT_INDEX = 'botindex.html'
 
     for (const item of botsIndexPaths.length ? botsIndexPaths : fs.readdirSync(folder)) {
-        const fullPath = botsIndexPaths.length ? item : `${folder}/${item}`;
+        const fullPath = botsIndexPaths.length ? item : `${folder}/${item}`
 
         if (fs.statSync(fullPath).isDirectory()) {
-            namespaces.push(fullPath);
+            namespaces.push(fullPath)
         } else if (![INDEX, BOT_INDEX].includes(item)) {
-            files.push(fullPath);
+            files.push(fullPath)
         }
     }
 
     // Now that everything is setup, write the index.html file
-    const filename = `${folder}/${botsIndex ? BOT_INDEX : INDEX}`;
-    const docs = new DocsWriter(filename, getPathForType).open();
+    const filename = `${folder}/${botsIndex ? BOT_INDEX : INDEX}`
+    const docs = new DocsWriter(filename, getPathForType).open()
 
     // Title should be the current folder name
     docs.writeHead(
         titleCase(folder.replace(new RegExp(`\\${path.sep}`, 'g'), '/')),
         paths.css,
         paths.defaultCss
-    );
+    )
 
-    docs.setMenuSeparator(paths.arrow);
-    buildMenu(docs);
+    docs.setMenuSeparator(paths.arrow)
+    buildMenu(docs)
     docs.writeTitle(
         titleCase(
             path
                 .join(filename, '..')
                 .replace(new RegExp(`\\${path.sep}`, 'g'), '/')
         )
-    );
+    )
 
     if (botsIndex) {
         docs.writeText(
             `These are the methods that you may be able to use as a bot. Click <a href="${INDEX}">here</a> to view them all.`
-        );
+        )
     } else {
         docs.writeText(
             `Click <a href="${BOT_INDEX}">here</a> to view the methods that you can use as a bot.`
-        );
+        )
     }
 
     if (namespaces.length) {
-        docs.writeTitle('Namespaces', 3);
-        docs.beginTable(4);
-        namespaces.sort();
+        docs.writeTitle('Namespaces', 3)
+        docs.beginTable(4)
+        namespaces.sort()
 
         for (const namespace of namespaces) {
             // For every namespace, also write the index of it
-            const namespacePaths = [];
+            const namespacePaths = []
 
             if (botsIndex) {
                 for (const item of botsIndexPaths) {
                     if (path.relative(item, '..') === namespace) {
-                        namespacePaths.push(item);
+                        namespacePaths.push(item)
                     }
                 }
             }
 
-            generateIndex(namespace, paths, botsIndex, namespacePaths);
+            generateIndex(namespace, paths, botsIndex, namespacePaths)
 
             docs.addRow(
                 titleCase(path.parse(namespace).name),
                 `${namespace}/${botsIndex ? BOT_INDEX : INDEX}`
-            );
+            )
         }
 
-        docs.endTable();
+        docs.endTable()
     }
 
-    docs.writeTitle('Available items');
-    docs.beginTable(2);
+    docs.writeTitle('Available items')
+    docs.beginTable(2)
 
     files
         .sort((x, y) => x.localeCompare(y))
         .forEach((file) => {
-            docs.addRow(findTitle(file), file);
-        });
+            docs.addRow(findTitle(file), file)
+        })
 
-    docs.endTable();
-    docs.endBody();
-    docs.close();
-};
+    docs.endTable()
+    docs.endBody()
+    docs.close()
+}
 
 /**
  * Generates a proper description for the given argument.
  */
 const getDescription = (arg) => {
-    const desc = [];
-    let otherwise = false;
+    const desc = []
+    let otherwise = false
 
     if (arg.canBeInferred) {
-        desc.push('If left unspecified, it will be inferred automatically.');
-        otherwise = true;
+        desc.push('If left unspecified, it will be inferred automatically.')
+        otherwise = true
     } else if (arg.isFlag) {
         desc.push(
             'This argument defaults to <code>null</code> and can be omitted.'
-        );
-        otherwise = true;
+        )
+        otherwise = true
     }
 
     if (
@@ -246,26 +246,26 @@ const getDescription = (arg) => {
     ) {
         desc.push(
             'Anything entity-like will work if the library can find its <code>Input</code> version (e.g., usernames, <code>Peer</code>, <code>User</code> or <code>Channel</code> objects, etc.).'
-        );
+        )
     }
 
     if (arg.isVector) {
         if (arg.isGeneric) {
-            desc.push('A list of other Requests must be supplied.');
+            desc.push('A list of other Requests must be supplied.')
         } else {
-            desc.push('A list must be supplied.');
+            desc.push('A list must be supplied.')
         }
     } else if (arg.isGeneric) {
-        desc.push('A different Request must be supplied for this argument.');
+        desc.push('A different Request must be supplied for this argument.')
     } else {
-        otherwise = false; // Always reset to false if no other text is added
+        otherwise = false // Always reset to false if no other text is added
     }
 
     if (otherwise) {
-        desc.splice(1, 0, 'Otherwise,');
+        desc.splice(1, 0, 'Otherwise,')
         desc[desc.length - 1] =
             desc[desc.length - 1].slice(0, -1).toLowerCase() +
-            desc[desc.length - 1].slice(1);
+            desc[desc.length - 1].slice(1)
     }
 
     return desc
@@ -273,14 +273,14 @@ const getDescription = (arg) => {
         .replace(
             /list/g,
             '<span class="tooltip" title="Any iterable that supports .length will work too">list</span>'
-        );
-};
+        )
+}
 
 /**
  * Copies the src file into dst applying the replacements dict
  */
 const copyReplace = (src, dst, replacements) => {
-    const infile = fs.readFileSync(src, { encoding: 'utf-8' });
+    const infile = fs.readFileSync(src, { encoding: 'utf-8' })
 
     fs.writeFileSync(
         dst,
@@ -293,8 +293,8 @@ const copyReplace = (src, dst, replacements) => {
         //     ),
         //     m => replacements[m].toString()
         // )
-    );
-};
+    )
+}
 
 /**
  * Generates the documentation HTML files from from ``scheme.tl``
@@ -315,197 +315,197 @@ const writeHtmlPages = (tlobjects, methods, layer, inputRes) => {
         'indexMethods': 'methods/index.html',
         'indexConstructors': 'constructors/index.html',
         'defaultCss': 'light',
-    };
+    }
 
-    const typeToConstructors = {};
-    const typeToFunctions = {};
+    const typeToConstructors = {}
+    const typeToFunctions = {}
 
     for (const tlobject of tlobjects) {
-        const d = tlobject.isFunction ? typeToFunctions : typeToConstructors;
+        const d = tlobject.isFunction ? typeToFunctions : typeToConstructors
 
         if (!d[tlobject.innermostResult]) {
-            d[tlobject.innermostResult] = [];
+            d[tlobject.innermostResult] = []
         }
 
-        d[tlobject.innermostResult].push(tlobject);
+        d[tlobject.innermostResult].push(tlobject)
     }
 
     for (const [t, cs] of Object.entries(typeToConstructors)) {
-        typeToConstructors[t] = cs.sort((x, y) => x.name.localeCompare(y.name));
+        typeToConstructors[t] = cs.sort((x, y) => x.name.localeCompare(y.name))
     }
 
-    methods = methods.reduce((x, m) => ({ ...x, [m.name]: m }), {});
-    const botDocsPath = [];
+    methods = methods.reduce((x, m) => ({ ...x, [m.name]: m }), {})
+    const botDocsPath = []
 
     for (const tlobject of tlobjects) {
-        const filename = getPathFor(tlobject);
-        const docs = new DocsWriter(filename, getPathForType).open();
-        docs.writeHead(tlobject.className, paths.css, paths.defaultCss);
+        const filename = getPathFor(tlobject)
+        const docs = new DocsWriter(filename, getPathForType).open()
+        docs.writeHead(tlobject.className, paths.css, paths.defaultCss)
 
         // Create the menu (path to the current TLObject)
-        docs.setMenuSeparator(paths.arrow);
-        buildMenu(docs);
+        docs.setMenuSeparator(paths.arrow)
+        buildMenu(docs)
 
         // Create the page title
-        docs.writeTitle(tlobject.className);
+        docs.writeTitle(tlobject.className)
 
         if (tlobject.isFunction) {
-            let start;
+            let start
 
             if (tlobject.usability === Usability.USER) {
-                start = '<strong>Only users</strong> can';
+                start = '<strong>Only users</strong> can'
             } else if (tlobject.usability === Usability.BOT) {
-                botDocsPath.push(filename);
-                start = '<strong>Only bots</strong> can';
+                botDocsPath.push(filename)
+                start = '<strong>Only bots</strong> can'
             } else if (tlobject.usability === Usability.BOTH) {
-                botDocsPath.push(filename);
-                start = '<strong>Both users and bots</strong> can';
+                botDocsPath.push(filename)
+                start = '<strong>Both users and bots</strong> can'
             } else {
-                botDocsPath.push(filename);
-                start = 'Both users and bots <strong>may</strong> be able to';
+                botDocsPath.push(filename)
+                start = 'Both users and bots <strong>may</strong> be able to'
             }
 
             docs.writeText(
                 `${start} use this method. <a href="#examples">See code examples.</a>`
-            );
+            )
         }
 
         // Write the code definition for this TLObject
-        docs.writeCode(tlobject);
+        docs.writeCode(tlobject)
         docs.writeCopyButton(
             'Copy import to clipboard',
             getImportCode(tlobject)
-        );
+        )
 
         // Write the return type (or constructors belonging to the same type)
-        docs.writeTitle(tlobject.isFunction ? 'Returns' : 'Belongs to', 3);
+        docs.writeTitle(tlobject.isFunction ? 'Returns' : 'Belongs to', 3)
 
         let [genericArg] = tlobject.args
             .filter((arg) => arg.genericDefinition)
-            .map((arg) => arg.name);
+            .map((arg) => arg.name)
 
         if (tlobject.result === genericArg) {
             //  We assume it's a function returning a generic type
             [genericArg] = tlobject.args
                 .filter((arg) => arg.isGeneric)
-                .map((arg) => arg.name);
+                .map((arg) => arg.name)
 
             docs.writeText(
                 `This function returns the result of whatever the result from invoking the request passed through <i>${genericArg}</i> is.`
-            );
+            )
         } else {
-            let inner = tlobject.result;
+            let inner = tlobject.result
 
             if (/^vector</i.test(tlobject.result)) {
-                docs.writeText('A list of the following type is returned.');
-                inner = tlobject.innermostResult;
+                docs.writeText('A list of the following type is returned.')
+                inner = tlobject.innermostResult
             }
 
-            docs.beginTable(1);
-            docs.addRow(inner, getPathForType(inner));
-            docs.endTable();
+            docs.beginTable(1)
+            docs.addRow(inner, getPathForType(inner))
+            docs.endTable()
 
-            const cs = typeToConstructors[inner] || [];
+            const cs = typeToConstructors[inner] || []
             if (!cs.length) {
-                docs.writeText('This type has no instances available.');
+                docs.writeText('This type has no instances available.')
             } else if (cs.length === 1) {
-                docs.writeText('This type can only be an instance of:');
+                docs.writeText('This type can only be an instance of:')
             } else {
-                docs.writeText('This type can be an instance of either:');
+                docs.writeText('This type can be an instance of either:')
             }
 
-            docs.beginTable(2);
+            docs.beginTable(2)
 
             for (const constructor of cs) {
-                const link = getPathFor(constructor);
-                docs.addRow(constructor.className, link);
+                const link = getPathFor(constructor)
+                docs.addRow(constructor.className, link)
             }
 
-            docs.endTable();
+            docs.endTable()
         }
 
         // Return (or similar types) written. Now parameters/members
-        docs.writeTitle(tlobject.isFunction ? 'Parameters' : 'Members', 3);
+        docs.writeTitle(tlobject.isFunction ? 'Parameters' : 'Members', 3)
 
         // Sort the arguments in the same way they're sorted
         // on the generated code (flags go last)
         const args = tlobject
             .sortedArgs()
-            .filter((a) => !a.flagIndicator && !a.genericDefinition);
+            .filter((a) => !a.flagIndicator && !a.genericDefinition)
 
         if (args.length) {
             // Writing parameters
-            docs.beginTable(3);
+            docs.beginTable(3)
 
             for (const arg of args) {
                 // Name row
-                docs.addRow(arg.name, null, true);
+                docs.addRow(arg.name, null, true)
 
                 // Type row
-                const friendlyType = arg.type === 'true' ? 'flag' : arg.type;
+                const friendlyType = arg.type === 'true' ? 'flag' : arg.type
                 if (arg.isGeneric) {
-                    docs.addRow(`!${friendlyType}`, null, null, 'center');
+                    docs.addRow(`!${friendlyType}`, null, null, 'center')
                 } else {
                     docs.addRow(
                         friendlyType,
                         getPathForType(arg.type),
                         null,
                         'center'
-                    );
+                    )
                 }
 
                 // Add a description for this argument
-                docs.addRow(getDescription(arg));
+                docs.addRow(getDescription(arg))
             }
 
-            docs.endTable();
+            docs.endTable()
         } else {
             if (tlobject.isFunction) {
-                docs.writeText('This request takes no input parameters.');
+                docs.writeText('This request takes no input parameters.')
             } else {
-                docs.writeText('This type has no members.');
+                docs.writeText('This type has no members.')
             }
         }
 
         if (tlobject.isFunction) {
-            docs.writeTitle('Known RPC errors');
-            const methodInfo = methods[tlobject.fullname];
-            const errors = methodInfo && methodInfo.errors;
+            docs.writeTitle('Known RPC errors')
+            const methodInfo = methods[tlobject.fullname]
+            const errors = methodInfo && methodInfo.errors
 
             if (!errors || !errors.length) {
                 docs.writeText(
                     'This request can\'t cause any RPC error as far as we know.'
-                );
+                )
             } else {
                 docs.writeText(
                     `This request can cause ${errors.length} known error${
                         errors.length === 1 ? '' : 's'
                     }:`
-                );
+                )
 
-                docs.beginTable(2);
+                docs.beginTable(2)
 
                 for (const error of errors) {
-                    docs.addRow(`<code>${error.name}</code>`);
-                    docs.addRow(`${error.description}.`);
+                    docs.addRow(`<code>${error.name}</code>`)
+                    docs.addRow(`${error.description}.`)
                 }
 
-                docs.endTable();
+                docs.endTable()
                 docs.writeText(
                     'You can import these from <code>gramjs/errors</code>.'
-                );
+                )
             }
 
-            docs.writeTitle('Example', null, 'examples');
+            docs.writeTitle('Example', null, 'examples')
             if (tlobject.friendly) {
-                const [ns, friendly] = tlobject.friendly;
+                const [ns, friendly] = tlobject.friendly
                 docs.writeText(
                     `Please refer to the documentation of <a href="https://docs.telethon.dev/en/latest/modules/client.html#telethon.client.${ns}.${friendly}"><code>client.${friendly}()</code></a> to learn about the parameters and see several code examples on how to use it.`
-                );
+                )
                 docs.writeText(
                     'The method above is the recommended way to do it. If you need more control over the parameters or want to learn how it is implemented, open the details by clicking on the "Details" text.'
-                );
-                docs.write('<details>');
+                )
+                docs.write('<details>')
             }
 
             docs.write(`<pre><strong>const</strong> { TelegramClient } <strong>=</strong> require('gramjs');
@@ -515,165 +515,165 @@ const writeHtmlPages = (tlobjects, methods, layer, inputRes) => {
     <strong>const</strong> client <strong>=</strong> <strong>new</strong> TelegramClient(name, apiId, apiHash);
     await client.start();
 
-    <strong>const</strong> result <strong>= await</strong> client.invoke(`);
-            tlobject.asExample(docs, 1);
-            docs.write(');\n');
+    <strong>const</strong> result <strong>= await</strong> client.invoke(`)
+            tlobject.asExample(docs, 1)
+            docs.write(');\n')
 
             if (tlobject.result.startsWith('Vector')) {
                 docs.write(
                     `<strong>for</strong> x <strong>in</strong> result:
         print(x`
-                );
+                )
             } else {
-                docs.write('    console.log(result');
+                docs.write('    console.log(result')
                 if (
                     tlobject.result !== 'Bool' &&
                     !tlobject.result.startsWith('Vector')
                 ) {
-                    docs.write('.stringify()');
+                    docs.write('.stringify()')
                 }
             }
 
-            docs.write(');\n})();</pre>');
+            docs.write(');\n})();</pre>')
             if (tlobject.friendly) {
-                docs.write('</details>');
+                docs.write('</details>')
             }
 
-            const depth = '../'.repeat(tlobject.namespace ? 2 : 1);
-            docs.addScript(`prependPath = "${depth}";`);
-            docs.addScript(null, paths['search.js']);
-            docs.endBody();
+            const depth = '../'.repeat(tlobject.namespace ? 2 : 1)
+            docs.addScript(`prependPath = "${depth}";`)
+            docs.addScript(null, paths['search.js'])
+            docs.endBody()
         }
 
-        docs.close();
+        docs.close()
     }
 
     // Find all the available types (which are not the same as the constructors)
     // Each type has a list of constructors associated to it, hence is a map
     for (const [t, cs] of Object.entries(typeToConstructors)) {
-        const filename = getPathForType(t);
-        const outDir = path.join(filename, '..');
+        const filename = getPathForType(t)
+        const outDir = path.join(filename, '..')
 
         if (outDir) {
-            mkdir(outDir);
+            mkdir(outDir)
         }
 
         // Since we don't have access to the full TLObject, split the type
-        let name = t;
+        let name = t
 
         if (t.includes('.')) {
-            [, name] = t.split('.');
+            [, name] = t.split('.')
         }
 
-        const docs = new DocsWriter(filename, getPathForType).open();
+        const docs = new DocsWriter(filename, getPathForType).open()
 
-        docs.writeHead(snakeToCamelCase(name), paths.css, paths.defaultCss);
-        docs.setMenuSeparator(paths.arrow);
-        buildMenu(docs);
+        docs.writeHead(snakeToCamelCase(name), paths.css, paths.defaultCss)
+        docs.setMenuSeparator(paths.arrow)
+        buildMenu(docs)
 
         // Main file title
-        docs.writeTitle(snakeToCamelCase(name));
+        docs.writeTitle(snakeToCamelCase(name))
 
         // List available constructors for this type
-        docs.writeTitle('Available constructors', 3);
+        docs.writeTitle('Available constructors', 3)
         if (!cs.length) {
-            docs.writeText('This type has no constructors available.');
+            docs.writeText('This type has no constructors available.')
         } else if (cs.length === 1) {
-            docs.writeText('This type has one constructor available.');
+            docs.writeText('This type has one constructor available.')
         } else {
             docs.writeText(
                 `This type has ${cs.length} constructors available.`
-            );
+            )
         }
 
-        docs.beginTable(2);
+        docs.beginTable(2)
 
         for (const constructor of cs) {
             // Constructor full name
-            const link = getPathFor(constructor);
-            docs.addRow(constructor.className, link);
+            const link = getPathFor(constructor)
+            docs.addRow(constructor.className, link)
         }
 
-        docs.endTable();
+        docs.endTable()
 
         // List all the methods which return this type
-        docs.writeTitle('Methods returning this type', 3);
-        const functions = typeToFunctions[t] || [];
+        docs.writeTitle('Methods returning this type', 3)
+        const functions = typeToFunctions[t] || []
 
         if (!functions.length) {
-            docs.writeText('No method returns this type.');
+            docs.writeText('No method returns this type.')
         } else if (functions.length === 1) {
-            docs.writeText('Only the following method returns this type.');
+            docs.writeText('Only the following method returns this type.')
         } else {
             docs.writeText(
                 `The following ${functions.length} methods return this type as a result.`
-            );
+            )
         }
 
-        docs.beginTable(2);
+        docs.beginTable(2)
 
         for (const func of functions) {
-            const link = getPathFor(func);
-            docs.addRow(func.className, link);
+            const link = getPathFor(func)
+            docs.addRow(func.className, link)
         }
 
-        docs.endTable();
+        docs.endTable()
 
         // List all the methods which take this type as input
-        docs.writeTitle('Methods accepting this type as input', 3);
+        docs.writeTitle('Methods accepting this type as input', 3)
         const otherMethods = tlobjects
             .filter((u) => u.isFunction && u.args.some((a) => a.type === t))
-            .sort((x, y) => x.name.localeCompare(y.name));
+            .sort((x, y) => x.name.localeCompare(y.name))
 
         if (!otherMethods.length) {
             docs.writeText(
                 'No methods accept this type as an input parameter.'
-            );
+            )
         } else if (otherMethods.length === 1) {
-            docs.writeText('Only this method has a parameter with this type.');
+            docs.writeText('Only this method has a parameter with this type.')
         } else {
             docs.writeText(
                 `The following ${otherMethods.length} methods accept this type as an input parameter.`
-            );
+            )
         }
 
-        docs.beginTable(2);
+        docs.beginTable(2)
 
         for (const ot of otherMethods) {
-            const link = getPathFor(ot);
-            docs.addRow(ot.className, link);
+            const link = getPathFor(ot)
+            docs.addRow(ot.className, link)
         }
 
-        docs.endTable();
+        docs.endTable()
 
         // List every other type which has this type as a member
-        docs.writeTitle('Other types containing this type', 3);
+        docs.writeTitle('Other types containing this type', 3)
         const otherTypes = tlobjects
             .filter((u) => !u.isFunction && u.args.some((a) => a.type === t))
-            .sort((x, y) => x.name.localeCompare(y.name));
+            .sort((x, y) => x.name.localeCompare(y.name))
 
         if (!otherTypes.length) {
-            docs.writeText('No other types have a member of this type.');
+            docs.writeText('No other types have a member of this type.')
         } else if (otherTypes.length === 1) {
             docs.writeText(
                 'You can find this type as a member of this other type.'
-            );
+            )
         } else {
             docs.writeText(
                 `You can find this type as a member of any of the following ${otherTypes.length} types.`
-            );
+            )
         }
 
-        docs.beginTable(2);
+        docs.beginTable(2)
 
         for (const ot of otherTypes) {
-            const link = getPathFor(ot);
-            docs.addRow(ot.className, link);
+            const link = getPathFor(ot)
+            docs.addRow(ot.className, link)
         }
 
-        docs.endTable();
-        docs.endBody();
-        docs.close();
+        docs.endTable()
+        docs.endBody()
+        docs.close()
     }
 
     // After everything's been written, generate an index.html per folder.
@@ -681,45 +681,45 @@ const writeHtmlPages = (tlobjects, methods, layer, inputRes) => {
     // information that we have available, simply a file listing all the others
     // accessible by clicking on their title
     for (const folder of ['types', 'methods', 'constructors']) {
-        generateIndex(folder, paths);
+        generateIndex(folder, paths)
     }
 
-    generateIndex('methods', paths, true, botDocsPath);
+    generateIndex('methods', paths, true, botDocsPath)
 
     // Write the final core index, the main index for the rest of files
-    const types = new Set();
-    const methods_ = [];
-    const cs = [];
+    const types = new Set()
+    const methods_ = []
+    const cs = []
 
     for (const tlobject of tlobjects) {
         if (tlobject.isFunction) {
-            methods_.push(tlobject);
+            methods_.push(tlobject)
         } else {
-            cs.push(tlobject);
+            cs.push(tlobject)
         }
 
         if (!CORE_TYPES.has(tlobject.result.toLowerCase())) {
             if (/^vector</i.test(tlobject.result)) {
-                types.add(tlobject.innermostResult);
+                types.add(tlobject.innermostResult)
             } else {
-                types.add(tlobject.result);
+                types.add(tlobject.result)
             }
         }
     }
 
-    fs.copyFileSync(`${inputRes}/404.html`, paths['404']);
+    fs.copyFileSync(`${inputRes}/404.html`, paths['404'])
     copyReplace(`${inputRes}/core.html`, paths.indexAll, {
         typeCount: [...types].length,
         methodCount: methods_.length,
         constructorCount: tlobjects.length - methods_.length,
         layer,
-    });
+    })
 
     let fmt = (xs) => {
-        const zs = []; // create an object to hold those which have duplicated keys
+        const zs = [] // create an object to hold those which have duplicated keys
 
         for (const x of xs) {
-            zs[x.className] = x.className in zs;
+            zs[x.className] = x.className in zs
         }
 
         return xs
@@ -728,11 +728,11 @@ const writeHtmlPages = (tlobjects, methods, layer, inputRes) => {
                     `"${x.namespace}.${x.className}"` :
                     `"${x.className}"`
             )
-            .join(', ');
-    };
+            .join(', ')
+    }
 
-    const requestNames = fmt(methods_);
-    const constructorNames = fmt(cs);
+    const requestNames = fmt(methods_)
+    const constructorNames = fmt(cs)
 
     fmt = (xs, formatter) => {
         return xs
@@ -743,16 +743,16 @@ const writeHtmlPages = (tlobjects, methods, layer, inputRes) => {
                         '/'
                     )}"`
             )
-            .join(', ');
-    };
+            .join(', ')
+    }
 
-    const typeNames = fmt([...types], (x) => x);
+    const typeNames = fmt([...types], (x) => x)
 
-    const requestUrls = fmt(methods_, getPathFor);
-    const typeUrls = fmt([...types], getPathForType);
-    const constructorUrls = fmt(cs, getPathFor);
+    const requestUrls = fmt(methods_, getPathFor)
+    const typeUrls = fmt([...types], getPathForType)
+    const constructorUrls = fmt(cs, getPathFor)
 
-    mkdir(path.join(paths['search.js'], '..'));
+    mkdir(path.join(paths['search.js'], '..'))
     copyReplace(`${inputRes}/js/search.js`, paths['search.js'], {
         requestNames,
         typeNames,
@@ -760,68 +760,68 @@ const writeHtmlPages = (tlobjects, methods, layer, inputRes) => {
         requestUrls,
         typeUrls,
         constructorUrls,
-    });
-};
+    })
+}
 
 const copyResources = (resDir) => {
     for (const [dirname, files] of [
         ['css', ['docs.light.css', 'docs.dark.css']],
         ['img', ['arrow.svg']],
     ]) {
-        mkdir(dirname);
+        mkdir(dirname)
 
         for (const file of files) {
             fs.copyFileSync(
                 `${resDir}/${dirname}/${file}`,
                 `${dirname}/${file}`
-            );
+            )
         }
     }
-};
+}
 
 /**
  * Pre-create the required directory structure.
  */
 const createStructure = (tlobjects) => {
-    const typeNs = new Set();
-    const methodsNs = new Set();
+    const typeNs = new Set()
+    const methodsNs = new Set()
 
     for (const obj of tlobjects) {
         if (obj.namespace) {
             if (obj.isFunction) {
-                methodsNs.add(obj.namespace);
+                methodsNs.add(obj.namespace)
             } else {
-                typeNs.add(obj.namespace);
+                typeNs.add(obj.namespace)
             }
         }
     }
 
-    const outputDir = '.';
-    const typeDir = `${outputDir}/types`;
-    mkdir(typeDir);
+    const outputDir = '.'
+    const typeDir = `${outputDir}/types`
+    mkdir(typeDir)
 
-    const consDir = `${outputDir}/constructors`;
-    mkdir(consDir);
+    const consDir = `${outputDir}/constructors`
+    mkdir(consDir)
 
     for (const ns of typeNs) {
-        mkdir(`${typeDir}/${ns}`);
-        mkdir(`${consDir}/${ns}`);
+        mkdir(`${typeDir}/${ns}`)
+        mkdir(`${consDir}/${ns}`)
     }
 
-    const methDir = `${outputDir}/methods`;
-    mkdir(methDir);
+    const methDir = `${outputDir}/methods`
+    mkdir(methDir)
 
     for (const ns of typeNs) {
-        mkdir(`${methDir}/${ns}`);
+        mkdir(`${methDir}/${ns}`)
     }
-};
+}
 
 const generateDocs = (tlobjects, methodsInfo, layer, inputRes) => {
-    createStructure(tlobjects);
-    writeHtmlPages(tlobjects, methodsInfo, layer, inputRes);
-    copyResources(inputRes);
-};
+    createStructure(tlobjects)
+    writeHtmlPages(tlobjects, methodsInfo, layer, inputRes)
+    copyResources(inputRes)
+}
 
 module.exports = {
     generateDocs,
-};
+}

+ 32 - 32
gramjs_generator/generators/errors.js

@@ -1,86 +1,86 @@
 const generateErrors = (errors, f) => {
     // Exact/regex match to create {CODE: ErrorClassName}
-    const exactMatch = [];
-    const regexMatch = [];
+    const exactMatch = []
+    const regexMatch = []
 
     // Find out what subclasses to import and which to create
-    const importBase = new Set();
-    const createBase = {};
+    const importBase = new Set()
+    const createBase = {}
 
     for (const error of errors) {
         if (error.subclassExists) {
-            importBase.add(error.subclass);
+            importBase.add(error.subclass)
         } else {
-            createBase[error.subclass] = error.intCode;
+            createBase[error.subclass] = error.intCode
         }
 
         if (error.hasCaptures) {
-            regexMatch.push(error);
+            regexMatch.push(error)
         } else {
-            exactMatch.push(error);
+            exactMatch.push(error)
         }
     }
 
     // Imports and new subclass creation
-    f.write(`const { RPCError, ${[...importBase.values()].join(', ')} } = require('./rpcbaseerrors');`);
+    f.write(`const { RPCError, ${[...importBase.values()].join(', ')} } = require('./rpcbaseerrors');`)
 
-    f.write('\nconst format = require(\'string-format\');');
+    f.write('\nconst format = require(\'string-format\');')
 
     for (const [cls, intCode] of Object.entries(createBase)) {
-        f.write(`\n\nclass ${cls} extends RPCError {\n    constructor() {\n        this.code = ${intCode};\n    }\n}`);
+        f.write(`\n\nclass ${cls} extends RPCError {\n    constructor() {\n        this.code = ${intCode};\n    }\n}`)
     }
 
     // Error classes generation
     for (const error of errors) {
-        f.write(`\n\nclass ${error.name} extends ${error.subclass} {\n    constructor(args) {\n        `);
+        f.write(`\n\nclass ${error.name} extends ${error.subclass} {\n    constructor(args) {\n        `)
 
         if (error.hasCaptures) {
-            f.write(`const ${error.captureName} = Number(args.capture || 0);\n        `);
+            f.write(`const ${error.captureName} = Number(args.capture || 0);\n        `)
         }
 
-        const capture = error.description.replace(/'/g, '\\\'');
+        const capture = error.description.replace(/'/g, '\\\'')
 
         if (error.hasCaptures) {
-            f.write(`super(format('${capture}', {${error.captureName}})`);
+            f.write(`super(format('${capture}', {${error.captureName}})`)
         } else {
-            f.write(`super('${capture}'`);
+            f.write(`super('${capture}'`)
         }
 
-        f.write(' + RPCError._fmtRequest(args.request));\n');
+        f.write(' + RPCError._fmtRequest(args.request));\n')
 
         if (error.hasCaptures) {
-            f.write(`        this.${error.captureName} = ${error.captureName};\n`);
+            f.write(`        this.${error.captureName} = ${error.captureName};\n`)
         }
 
-        f.write('    }\n}\n');
+        f.write('    }\n}\n')
     }
 
-    f.write('\n\nconst rpcErrorsObject = {\n');
+    f.write('\n\nconst rpcErrorsObject = {\n')
 
     for (const error of exactMatch) {
-        f.write(`    ${error.pattern}: ${error.name},\n`);
+        f.write(`    ${error.pattern}: ${error.name},\n`)
     }
 
-    f.write('};\n\nconst rpcErrorRe = [\n');
+    f.write('};\n\nconst rpcErrorRe = [\n')
 
     for (const error of regexMatch) {
-        f.write(`    [/${error.pattern}/, ${error.name}],\n`);
+        f.write(`    [/${error.pattern}/, ${error.name}],\n`)
     }
 
-    f.write('];');
-    f.write('module.exports = {');
+    f.write('];')
+    f.write('module.exports = {')
     for (const error of regexMatch) {
-        f.write(`     ${error.name},\n`);
+        f.write(`     ${error.name},\n`)
     }
     for (const error of exactMatch) {
-        f.write(`     ${error.name},\n`);
+        f.write(`     ${error.name},\n`)
     }
-    f.write('     rpcErrorsObject,\n');
-    f.write('     rpcErrorRe,\n');
+    f.write('     rpcErrorsObject,\n')
+    f.write('     rpcErrorRe,\n')
 
-    f.write('}');
-};
+    f.write('}')
+}
 
 module.exports = {
     generateErrors,
-};
+}

+ 4 - 4
gramjs_generator/generators/index.js

@@ -1,10 +1,10 @@
-const { generateErrors } = require('./errors');
-const { generateTLObjects, cleanTLObjects } = require('./tlobject');
-const { generateDocs } = require('./docs');
+const { generateErrors } = require('./errors')
+const { generateTLObjects, cleanTLObjects } = require('./tlobject')
+const { generateDocs } = require('./docs')
 
 module.exports = {
     generateErrors,
     generateTLObjects,
     cleanTLObjects,
     generateDocs,
-};
+}

+ 311 - 311
gramjs_generator/generators/tlobject.js

@@ -1,10 +1,10 @@
-const fs = require('fs');
-const util = require('util');
-const { crc32 } = require('crc');
-const SourceBuilder = require('../sourcebuilder');
-const { snakeToCamelCase, variableSnakeToCamelCase } = require('../utils');
+const fs = require('fs')
+const util = require('util')
+const { crc32 } = require('crc')
+const SourceBuilder = require('../sourcebuilder')
+const { snakeToCamelCase, variableSnakeToCamelCase } = require('../utils')
 
-const AUTO_GEN_NOTICE = '/*! File generated by TLObjects\' generator. All changes will be ERASED !*/';
+const AUTO_GEN_NOTICE = '/*! File generated by TLObjects\' generator. All changes will be ERASED !*/'
 
 const AUTO_CASTS = {
     InputPeer: 'utils.get_input_peer(await client.get_input_entity(%s))',
@@ -17,11 +17,11 @@ const AUTO_CASTS = {
     InputMessage: 'utils.get_input_message(%s)',
     InputDocument: 'utils.get_input_document(%s)',
     InputChatPhoto: 'utils.get_input_chat_photo(%s)',
-};
+}
 
 const NAMED_AUTO_CASTS = {
     'chat_id,int': 'await client.get_peer_id(%s, add_mark=False)',
-};
+}
 
 // Secret chats have a chat_id which may be negative.
 // With the named auto-cast above, we would break it.
@@ -31,9 +31,9 @@ const NAMED_AUTO_CASTS = {
 // NOTE: This works because the auto-cast is not recursive.
 //       There are plenty of types that would break if we
 //       did recurse into them to resolve them.
-const NAMED_BLACKLIST = new Set(['messages.discardEncryption']);
+const NAMED_BLACKLIST = new Set(['messages.discardEncryption'])
 
-const BASE_TYPES = ['string', 'bytes', 'int', 'long', 'int128', 'int256', 'double', 'Bool', 'true'];
+// const BASE_TYPES = ['string', 'bytes', 'int', 'long', 'int128', 'int256', 'double', 'Bool', 'true'];
 
 // Patched types {fullname: custom.ns.Name}
 
@@ -44,23 +44,23 @@ const BASE_TYPES = ['string', 'bytes', 'int', 'long', 'int128', 'int256', 'doubl
     message: 'message.Message',
     messageService: 'message.Message',
 };*/
-const PATCHED_TYPES = {};
+const PATCHED_TYPES = {}
 
 const writeModules = (outDir, depth, kind, namespaceTlobjects, typeConstructors) => {
     // namespace_tlobjects: {'namespace', [TLObject]}
-    fs.mkdirSync(outDir, { recursive: true });
+    fs.mkdirSync(outDir, { recursive: true })
 
     for (const [ns, tlobjects] of Object.entries(namespaceTlobjects)) {
-        const file = `${outDir}/${ns === 'null' ? 'index' : ns}.js`;
-        const stream = fs.createWriteStream(file);
-        const builder = new SourceBuilder(stream);
-        const dotDepth = '.'.repeat(depth || 1);
+        const file = `${outDir}/${ns === 'null' ? 'index' : ns}.js`
+        const stream = fs.createWriteStream(file)
+        const builder = new SourceBuilder(stream)
+        const dotDepth = '.'.repeat(depth || 1)
 
-        builder.writeln(AUTO_GEN_NOTICE);
-        builder.writeln(`const { TLObject } = require('${dotDepth}/tlobject');`);
+        builder.writeln(AUTO_GEN_NOTICE)
+        builder.writeln(`const { TLObject } = require('${dotDepth}/tlobject');`)
 
         if (kind !== 'TLObject') {
-            builder.writeln(`const { ${kind} } = require('${dotDepth}/tlobject');`);
+            builder.writeln(`const { ${kind} } = require('${dotDepth}/tlobject');`)
         }
 
         // Add the relative imports to the namespaces,
@@ -68,17 +68,17 @@ const writeModules = (outDir, depth, kind, namespaceTlobjects, typeConstructors)
         if (!ns) {
             const imports = Object.keys(namespaceTlobjects)
                 .filter(Boolean)
-                .join(`, `);
+                .join(`, `)
 
-            builder.writeln(`const { ${imports} } = require('.');`);
+            builder.writeln(`const { ${imports} } = require('.');`)
         }
 
         // Import struct for the .__bytes__(self) serialization
-        builder.writeln('const struct = require(\'python-struct\');');
-        builder.writeln(`const Helpers = require('../../utils/Helpers');`);
+        builder.writeln('const struct = require(\'python-struct\');')
+        builder.writeln(`const Helpers = require('../../utils/Helpers');`)
 
-        const typeNames = new Set();
-        const typeDefs = [];
+        const typeNames = new Set()
+        const typeDefs = []
         /*
         // Find all the types in this file and generate type definitions
         // based on the types. The type definitions are written to the
@@ -114,38 +114,38 @@ const writeModules = (outDir, depth, kind, namespaceTlobjects, typeConstructors)
             }
         }*/
 
-        const imports = {};
-        const primitives = new Set(['int', 'long', 'int128', 'int256', 'double', 'string', 'bytes', 'Bool', 'true']);
+        const imports = {}
+        const primitives = new Set(['int', 'long', 'int128', 'int256', 'double', 'string', 'bytes', 'Bool', 'true'])
 
         // Find all the types in other files that are used in this file
         // and generate the information required to import those types.
         for (const t of tlobjects) {
             for (const arg of t.args) {
-                let name = arg.type;
+                let name = arg.type
 
                 if (!name || primitives.has(name)) {
-                    continue;
+                    continue
                 }
 
-                let importSpace = `${dotDepth}/tl/types`;
+                let importSpace = `${dotDepth}/tl/types`
 
                 if (name.includes('.')) {
-                    const [namespace] = name.split('.');
-                    name = name.split('.');
-                    importSpace += `/${namespace}`;
+                    const [namespace] = name.split('.')
+                    name = name.split('.')
+                    importSpace += `/${namespace}`
                 }
 
                 if (!typeNames.has(name)) {
-                    typeNames.add(name);
+                    typeNames.add(name)
 
                     if (name === 'date') {
-                        imports.datetime = ['datetime'];
-                        continue;
+                        imports.datetime = ['datetime']
+                        continue
                     } else if (!(importSpace in imports)) {
-                        imports[importSpace] = new Set();
+                        imports[importSpace] = new Set()
                     }
 
-                    imports[importSpace].add(`Type${name}`);
+                    imports[importSpace].add(`Type${name}`)
                 }
             }
         }
@@ -169,34 +169,34 @@ const writeModules = (outDir, depth, kind, namespaceTlobjects, typeConstructors)
         // Generate the class for every TLObject
         for (const t of tlobjects) {
             if (t.fullname in PATCHED_TYPES) {
-                builder.writeln(`const ${t.className} = null; // Patched`);
+                builder.writeln(`const ${t.className} = null; // Patched`)
             } else {
-                writeSourceCode(t, kind, builder, typeConstructors);
-                builder.currentIndent = 0;
+                writeSourceCode(t, kind, builder, typeConstructors)
+                builder.currentIndent = 0
             }
         }
 
         // Write the type definitions generated earlier.
-        builder.writeln();
+        builder.writeln()
 
         for (const line of typeDefs) {
-            builder.writeln(line);
+            builder.writeln(line)
         }
-        writeModuleExports(tlobjects, builder);
+        writeModuleExports(tlobjects, builder)
         if (file.indexOf('index.js') > 0) {
             for (const [ns] of Object.entries(namespaceTlobjects)) {
                 if (ns !== 'null') {
-                    builder.writeln('let %s = require(\'./%s\');', ns, ns);
+                    builder.writeln('let %s = require(\'./%s\');', ns, ns)
                 }
             }
             for (const [ns] of Object.entries(namespaceTlobjects)) {
                 if (ns !== 'null') {
-                    builder.writeln('module.exports.%s = %s;', ns, ns);
+                    builder.writeln('module.exports.%s = %s;', ns, ns)
                 }
             }
         }
     }
-};
+}
 
 const writeReadResult = (tlobject, builder) => {
     // Only requests can have a different response that's not their
@@ -205,7 +205,7 @@ const writeReadResult = (tlobject, builder) => {
     // The default behaviour is reading a TLObject too, so no need
     // to override it unless necessary.
     if (!tlobject.isFunction) {
-        return;
+        return
     }
 
     // https://core.telegram.org/mtproto/serialize#boxed-and-bare-types
@@ -215,23 +215,23 @@ const writeReadResult = (tlobject, builder) => {
     // Currently only un-boxed responses are Vector<int>/Vector<long>.
     // If this weren't the case, we should check upper case after
     // max(index('<'), index('.')) (and if it is, it's boxed, so return).
-    const m = tlobject.result.match(/Vector<(int|long)>/);
+    const m = tlobject.result.match(/Vector<(int|long)>/)
     if (!m) {
-        return;
+        return
     }
 
     // builder.endBlock();
-    builder.writeln('readResult(reader){');
-    builder.writeln('reader.readInt();  // Vector ID');
-    builder.writeln('let temp = [];');
-    builder.writeln('let len = reader.readInt(); //fix this');
-    builder.writeln('for (let i=0;i<len;i++){');
-    const read = m[1][0].toUpperCase() + m[1].slice(1);
-    builder.writeln('temp.push(reader.read%s())', read);
-    builder.endBlock();
-    builder.writeln('return temp');
-    builder.endBlock();
-};
+    builder.writeln('readResult(reader){')
+    builder.writeln('reader.readInt();  // Vector ID')
+    builder.writeln('let temp = [];')
+    builder.writeln('let len = reader.readInt(); //fix this')
+    builder.writeln('for (let i=0;i<len;i++){')
+    const read = m[1][0].toUpperCase() + m[1].slice(1)
+    builder.writeln('temp.push(reader.read%s())', read)
+    builder.endBlock()
+    builder.writeln('return temp')
+    builder.endBlock()
+}
 
 /**
  * Writes the source code corresponding to the given TLObject
@@ -242,69 +242,69 @@ const writeReadResult = (tlobject, builder) => {
  * importing and documentation strings.
  */
 const writeSourceCode = (tlobject, kind, builder, typeConstructors) => {
-    writeClassConstructor(tlobject, kind, typeConstructors, builder);
-    writeResolve(tlobject, builder);
+    writeClassConstructor(tlobject, kind, typeConstructors, builder)
+    writeResolve(tlobject, builder)
     // writeToJson(tlobject, builder);
-    writeToBytes(tlobject, builder);
-    builder.currentIndent--;
-    writeFromReader(tlobject, builder);
-    writeReadResult(tlobject, builder);
-    builder.currentIndent--;
-    builder.writeln('}');
-};
+    writeToBytes(tlobject, builder)
+    builder.currentIndent--
+    writeFromReader(tlobject, builder)
+    writeReadResult(tlobject, builder)
+    builder.currentIndent--
+    builder.writeln('}')
+}
 
 const writeClassConstructor = (tlobject, kind, typeConstructors, builder) => {
-    builder.writeln();
-    builder.writeln();
-    builder.writeln(`class ${tlobject.className} extends ${kind} {`);
+    builder.writeln()
+    builder.writeln()
+    builder.writeln(`class ${tlobject.className} extends ${kind} {`)
 
     // Write the __init__ function if it has any argument
     if (!tlobject.realArgs.length) {
-        return;
+        return
     }
 
     // Note : this is needed to be able to access them
     // with or without an instance
-    builder.writeln(`static CONSTRUCTOR_ID = 0x${tlobject.id.toString(16).padStart(8, '0')};`);
-    builder.writeln(`static SUBCLASS_OF_ID = 0x${crc32(tlobject.result).toString(16)};`);
-    builder.writeln();
+    builder.writeln(`static CONSTRUCTOR_ID = 0x${tlobject.id.toString(16).padStart(8, '0')};`)
+    builder.writeln(`static SUBCLASS_OF_ID = 0x${crc32(tlobject.result).toString(16)};`)
+    builder.writeln()
 
-    builder.writeln('/**');
+    builder.writeln('/**')
 
     if (tlobject.isFunction) {
-        builder.write(`:returns ${tlobject.result}: `);
+        builder.write(`:returns ${tlobject.result}: `)
     } else {
-        builder.write(`Constructor for ${tlobject.result}: `);
+        builder.write(`Constructor for ${tlobject.result}: `)
     }
 
-    const constructors = typeConstructors[tlobject.result];
+    const constructors = typeConstructors[tlobject.result]
 
     if (!constructors) {
-        builder.writeln('This type has no constructors.');
+        builder.writeln('This type has no constructors.')
     } else if (constructors.length === 1) {
-        builder.writeln(`Instance of ${constructors[0].className}`);
+        builder.writeln(`Instance of ${constructors[0].className}`)
     } else {
-        builder.writeln(`Instance of either ${constructors.map((c) => c.className).join(', ')}`);
+        builder.writeln(`Instance of either ${constructors.map((c) => c.className).join(', ')}`)
     }
 
-    builder.writeln('*/');
-    builder.writeln(`constructor(args) {`);
-    builder.writeln(`super();`);
+    builder.writeln('*/')
+    builder.writeln(`constructor(args) {`)
+    builder.writeln(`super();`)
 
     // Class-level variable to store its Telegram's constructor ID
-    builder.writeln(`this.CONSTRUCTOR_ID = 0x${tlobject.id.toString(16).padStart(8, '0')};`);
-    builder.writeln(`this.SUBCLASS_OF_ID = 0x${crc32(tlobject.result).toString(16)};`);
+    builder.writeln(`this.CONSTRUCTOR_ID = 0x${tlobject.id.toString(16).padStart(8, '0')};`)
+    builder.writeln(`this.SUBCLASS_OF_ID = 0x${crc32(tlobject.result).toString(16)};`)
 
-    builder.writeln();
+    builder.writeln()
 
     // Set the arguments
     for (const arg of tlobject.realArgs) {
         if (!arg.canBeInferred) {
             builder.writeln(
-                `this.${variableSnakeToCamelCase(arg.name)}: ${a.typeHint()} = args.${variableSnakeToCamelCase(
-                    arg.name
-                )}${a.isFlag || a.canBeInferred ? ' || null' : ''};`
-            );
+                `this.${variableSnakeToCamelCase(arg.name)} = args.${variableSnakeToCamelCase(
+                    arg.name,
+                )}${arg.isFlag || arg.canBeInferred ? ' || null' : ''};`,
+            )
 
             // Currently the only argument that can be
             // inferred are those called 'random_id'
@@ -312,26 +312,26 @@ const writeClassConstructor = (tlobject, kind, typeConstructors, builder) => {
             // Endianness doesn't really matter, and 'big' is shorter
             let code = `Helpers.readBigIntFromBuffer(Helpers.generateRandomBytes(${
                 arg.type === 'long' ? 8 : 4
-            }),false,true)`;
+            }),false,true)`
 
             if (arg.isVector) {
                 // Currently for the case of "messages.forwardMessages"
                 // Ensure we can infer the length from id:Vector<>
                 if (!tlobject.realArgs.find((a) => a.name === 'id').isVector) {
-                    throw new Error(`Cannot infer list of random ids for ${tlobject}`);
+                    throw new Error(`Cannot infer list of random ids for ${tlobject}`)
                 }
 
-                code = `new Array(id.length).fill().map(_ => ${code})`;
+                code = `new Array(id.length).fill().map(_ => ${code})`
             }
 
-            builder.writeln(`this.randomId = args.randomId !== undefined ? args.randomId : ${code};`);
+            builder.writeln(`this.randomId = args.randomId !== undefined ? args.randomId : ${code};`)
         } else {
-            throw new Error(`Cannot infer a value for ${arg}`);
+            throw new Error(`Cannot infer a value for ${arg}`)
         }
     }
 
-    builder.endBlock();
-};
+    builder.endBlock()
+}
 
 const writeResolve = (tlobject, builder) => {
     if (
@@ -339,45 +339,45 @@ const writeResolve = (tlobject, builder) => {
         tlobject.realArgs.some(
             (arg) =>
                 arg.type in AUTO_CASTS ||
-                (`${arg.name},${arg.type}` in NAMED_AUTO_CASTS && !NAMED_BLACKLIST.has(tlobject.fullname))
+                (`${arg.name},${arg.type}` in NAMED_AUTO_CASTS && !NAMED_BLACKLIST.has(tlobject.fullname)),
         )
     ) {
-        builder.writeln('async resolve(client, utils) {');
+        builder.writeln('async resolve(client, utils) {')
 
         for (const arg of tlobject.realArgs) {
-            let ac = AUTO_CASTS[arg.type];
+            let ac = AUTO_CASTS[arg.type]
 
             if (!ac) {
-                ac = NAMED_AUTO_CASTS[`${arg.name},${arg.type}`];
+                ac = NAMED_AUTO_CASTS[`${arg.name},${arg.type}`]
 
                 if (!ac) {
-                    continue;
+                    continue
                 }
             }
 
             if (arg.isFlag) {
-                builder.writeln(`if (this.${arg.name}) {`);
+                builder.writeln(`if (this.${arg.name}) {`)
             }
 
             if (arg.isVector) {
-                builder.write(`const _tmp = [];`);
-                builder.writeln(`for (const _x of this.${arg.name}) {`);
-                builder.writeln(`_tmp.push(%s);`, util.format(ac, '_x'));
-                builder.endBlock();
-                builder.writeln(`this.${arg.name} = _tmp;`);
+                builder.write(`const _tmp = [];`)
+                builder.writeln(`for (const _x of this.${arg.name}) {`)
+                builder.writeln(`_tmp.push(%s);`, util.format(ac, '_x'))
+                builder.endBlock()
+                builder.writeln(`this.${arg.name} = _tmp;`)
             } else {
-                builder.writeln(`this.${arg.name} = %s`, util.format(ac, `this.${arg.name}`));
+                builder.writeln(`this.${arg.name} = %s`, util.format(ac, `this.${arg.name}`))
             }
 
             if (arg.isFlag) {
-                builder.currentIndent--;
-                builder.writeln('}');
+                builder.currentIndent--
+                builder.writeln('}')
             }
         }
 
-        builder.endBlock();
+        builder.endBlock()
     }
-};
+}
 /**
  const writeToJson = (tlobject, builder) => {
     builder.writeln('toJson() {');
@@ -424,79 +424,79 @@ const writeResolve = (tlobject, builder) => {
 };
  */
 const writeToBytes = (tlobject, builder) => {
-    builder.writeln('get bytes() {');
+    builder.writeln('get bytes() {')
 
     // Some objects require more than one flag parameter to be set
     // at the same time. In this case, add an assertion.
-    const repeatedArgs = {};
+    const repeatedArgs = {}
     for (const arg of tlobject.args) {
         if (arg.isFlag) {
             if (!repeatedArgs[arg.flagIndex]) {
-                repeatedArgs[arg.flagIndex] = [];
+                repeatedArgs[arg.flagIndex] = []
             }
-            repeatedArgs[arg.flagIndex].push(arg);
+            repeatedArgs[arg.flagIndex].push(arg)
         }
     }
 
     for (const ra of Object.values(repeatedArgs)) {
         if (ra.length > 1) {
-            const cnd1 = [];
-            const cnd2 = [];
-            const names = [];
+            const cnd1 = []
+            const cnd2 = []
+            const names = []
 
             for (const a of ra) {
-                cnd1.push(`this.${a.name} || this.${a.name}!==null`);
-                cnd2.push(`this.${a.name}===null || this.${a.name}===false`);
-                names.push(a.name);
+                cnd1.push(`this.${a.name} || this.${a.name}!==null`)
+                cnd2.push(`this.${a.name}===null || this.${a.name}===false`)
+                names.push(a.name)
             }
             builder.writeln(
                 'if (!((%s) && (%s)))\n\t throw new Error(\'%s paramaters must all be false-y or all true\')',
                 cnd1.join(' && '),
                 cnd2.join(' && '),
-                names.join(', ')
-            );
+                names.join(', '),
+            )
         }
     }
-    builder.writeln('return Buffer.concat([');
-    builder.currentIndent++;
-    const b = Buffer.alloc(4);
-    b.writeUInt32LE(tlobject.id, 0);
+    builder.writeln('return Buffer.concat([')
+    builder.currentIndent++
+    const b = Buffer.alloc(4)
+    b.writeUInt32LE(tlobject.id, 0)
     // First constructor code, we already know its bytes
-    builder.writeln('Buffer.from("%s","hex"),', b.toString('hex'));
+    builder.writeln('Buffer.from("%s","hex"),', b.toString('hex'))
     for (const arg of tlobject.args) {
         if (writeArgToBytes(builder, arg, tlobject.args)) {
-            builder.writeln(',');
+            builder.writeln(',')
         }
     }
-    builder.writeln('])');
-    builder.endBlock();
-};
+    builder.writeln('])')
+    builder.endBlock()
+}
 
 // writeFromReader
 const writeFromReader = (tlobject, builder) => {
-    builder.writeln('static fromReader(reader) {');
+    builder.writeln('static fromReader(reader) {')
     for (const arg of tlobject.args) {
         if (arg.name !== 'flag') {
             if (arg.name !== 'x') {
-                builder.writeln('let %s', '_' + arg.name + ';');
+                builder.writeln('let %s', '_' + arg.name + ';')
             }
         }
     }
 
     // TODO fix this really
-    builder.writeln('let _x;');
-    builder.writeln('let len;');
+    builder.writeln('let _x;')
+    builder.writeln('let len;')
 
     for (const arg of tlobject.args) {
-        writeArgReadCode(builder, arg, tlobject.args, '_' + arg.name);
+        writeArgReadCode(builder, arg, tlobject.args, '_' + arg.name)
     }
-    const temp = [];
+    const temp = []
     for (const a of tlobject.realArgs) {
-        temp.push(`${variableSnakeToCamelCase(a.name)}:_${a.name}`);
+        temp.push(`${variableSnakeToCamelCase(a.name)}:_${a.name}`)
     }
-    builder.writeln('return new this({%s})', temp.join(',\n\t'));
-    builder.endBlock();
-};
+    builder.writeln('return new this({%s})', temp.join(',\n\t'))
+    builder.endBlock()
+}
 // writeReadResult
 
 /**
@@ -511,21 +511,21 @@ const writeFromReader = (tlobject, builder) => {
  */
 const writeArgToBytes = (builder, arg, args, name = null) => {
     if (arg.genericDefinition) {
-        return; // Do nothing, this only specifies a later type
+        return // Do nothing, this only specifies a later type
     }
 
     if (name === null) {
-        name = `this.${arg.name}`;
+        name = `this.${arg.name}`
     }
 
-    name = variableSnakeToCamelCase(name);
+    name = variableSnakeToCamelCase(name)
     // The argument may be a flag, only write if it's not None AND
     // if it's not a True type.
     // True types are not actually sent, but instead only used to
     // determine the flags.
     if (arg.isFlag) {
         if (arg.type === 'true') {
-            return; // Exit, since true type is never written
+            return // Exit, since true type is never written
         } else if (arg.isVector) {
             // Vector flags are special since they consist of 3 values,
             // so we need an extra join here. Note that empty vector flags
@@ -534,99 +534,99 @@ const writeArgToBytes = (builder, arg, args, name = null) => {
                 '(%s === undefined || %s === false || %s ===null) ? Buffer.alloc(0) :Buffer.concat([',
                 name,
                 name,
-                name
-            );
+                name,
+            )
         } else {
-            builder.write('(%s === undefined || %s === false || %s ===null) ? Buffer.alloc(0) : [', name, name, name);
+            builder.write('(%s === undefined || %s === false || %s ===null) ? Buffer.alloc(0) : [', name, name, name)
         }
     }
 
     if (arg.isVector) {
         if (arg.useVectorId) {
-            builder.write('Buffer.from(\'15c4b51c\', \'hex\'),');
+            builder.write('Buffer.from(\'15c4b51c\', \'hex\'),')
         }
 
-        builder.write('struct.pack(\'<i\', %s.length),', name);
+        builder.write('struct.pack(\'<i\', %s.length),', name)
 
         // Cannot unpack the values for the outer tuple through *[(
         // since that's a Python >3.5 feature, so add another join.
-        builder.write('Buffer.concat(%s.map(x => ', name);
+        builder.write('Buffer.concat(%s.map(x => ', name)
         // Temporary disable .is_vector, not to enter this if again
         // Also disable .is_flag since it's not needed per element
-        const oldFlag = arg.isFlag;
-        arg.isVector = arg.isFlag = false;
-        writeArgToBytes(builder, arg, args, 'x');
-        arg.isVector = true;
-        arg.isFlag = oldFlag;
+        const oldFlag = arg.isFlag
+        arg.isVector = arg.isFlag = false
+        writeArgToBytes(builder, arg, args, 'x')
+        arg.isVector = true
+        arg.isFlag = oldFlag
 
-        builder.write('))');
+        builder.write('))')
     } else if (arg.flagIndicator) {
         // Calculate the flags with those items which are not None
         if (!args.some((f) => f.isFlag)) {
             // There's a flag indicator, but no flag arguments so it's 0
-            builder.write('Buffer.alloc(4)');
+            builder.write('Buffer.alloc(4)')
         } else {
-            builder.write('struct.pack(\'<I\', ');
+            builder.write('struct.pack(\'<I\', ')
             builder.write(
                 args
                     .filter((flag) => flag.isFlag)
                     .map(
                         (flag) =>
                             `(this.${variableSnakeToCamelCase(
-                                flag.name
+                                flag.name,
                             )} === undefined || this.${variableSnakeToCamelCase(
-                                flag.name
+                                flag.name,
                             )} === false || this.${variableSnakeToCamelCase(flag.name)} === null) ? 0 : ${1 <<
-                                flag.flagIndex}`
+                            flag.flagIndex}`,
                     )
-                    .join(' | ')
-            );
-            builder.write(')');
+                    .join(' | '),
+            )
+            builder.write(')')
         }
     } else if (arg.type === 'int') {
-        builder.write('struct.pack(\'<i\', %s)', name);
+        builder.write('struct.pack(\'<i\', %s)', name)
     } else if (arg.type === 'long') {
-        builder.write('Helpers.readBufferFromBigInt(%s,8,true,true)', name);
+        builder.write('Helpers.readBufferFromBigInt(%s,8,true,true)', name)
     } else if (arg.type === 'int128') {
-        builder.write('Helpers.readBufferFromBigInt(%s,16,true,true)', name);
+        builder.write('Helpers.readBufferFromBigInt(%s,16,true,true)', name)
     } else if (arg.type === 'int256') {
-        builder.write('Helpers.readBufferFromBigInt(%s,32,true,true)', name);
+        builder.write('Helpers.readBufferFromBigInt(%s,32,true,true)', name)
     } else if (arg.type === 'double') {
-        builder.write('struct.pack(\'<d\', %s.toString())', name);
+        builder.write('struct.pack(\'<d\', %s.toString())', name)
     } else if (arg.type === 'string') {
-        builder.write('TLObject.serializeBytes(%s)', name);
+        builder.write('TLObject.serializeBytes(%s)', name)
     } else if (arg.type === 'Bool') {
-        builder.write('%s ? 0xb5757299 : 0x379779bc', name);
+        builder.write('%s ? 0xb5757299 : 0x379779bc', name)
     } else if (arg.type === 'true') {
         // These are actually NOT written! Only used for flags
     } else if (arg.type === 'bytes') {
-        builder.write('TLObject.serializeBytes(%s)', name);
+        builder.write('TLObject.serializeBytes(%s)', name)
     } else if (arg.type === 'date') {
-        builder.write('TLObject.serializeDatetime(%s)', name);
+        builder.write('TLObject.serializeDatetime(%s)', name)
     } else {
         // Else it may be a custom type
-        builder.write('%s.bytes', name);
+        builder.write('%s.bytes', name)
 
         // If the type is not boxed (i.e. starts with lowercase) we should
         // not serialize the constructor ID (so remove its first 4 bytes).
-        let boxed = arg.type.charAt(arg.type.indexOf('.') + 1);
-        boxed = boxed === boxed.toUpperCase();
+        let boxed = arg.type.charAt(arg.type.indexOf('.') + 1)
+        boxed = boxed === boxed.toUpperCase()
 
         if (!boxed) {
-            builder.write('.slice(4)');
+            builder.write('.slice(4)')
         }
     }
 
     if (arg.isFlag) {
-        builder.write(']');
+        builder.write(']')
 
         if (arg.isVector) {
-            builder.write(')');
+            builder.write(')')
         }
     }
 
-    return true;
-};
+    return true
+}
 
 /**
  * Writes the read code for the given argument, setting the
@@ -641,267 +641,267 @@ const writeArgToBytes = (builder, arg, args, name = null) => {
  */
 const writeArgReadCode = (builder, arg, args, name) => {
     if (arg.genericDefinition) {
-        return; // Do nothing, this only specifies a later type
+        return // Do nothing, this only specifies a later type
     }
     // The argument may be a flag, only write that flag was given!
-    let wasFlag = false;
+    let wasFlag = false
     if (arg.isFlag) {
         // Treat 'true' flags as a special case, since they're true if
         // they're set, and nothing else needs to actually be read.
         if (arg.type === 'true') {
-            builder.writeln('%s = Boolean(flags & %s);', name, 1 << arg.flagIndex);
-            return;
+            builder.writeln('%s = Boolean(flags & %s);', name, 1 << arg.flagIndex)
+            return
         }
 
-        wasFlag = true;
-        builder.writeln('if (flags & %s) {', 1 << arg.flagIndex);
+        wasFlag = true
+        builder.writeln('if (flags & %s) {', 1 << arg.flagIndex)
         // Temporary disable .is_flag not to enter this if
         // again when calling the method recursively
-        arg.isFlag = false;
+        arg.isFlag = false
     }
 
     if (arg.isVector) {
         if (arg.useVectorId) {
             // We have to read the vector's constructor ID
-            builder.writeln('reader.readInt();');
+            builder.writeln('reader.readInt();')
         }
 
-        builder.writeln('%s = [];', name);
-        builder.writeln('len = reader.readInt();');
-        builder.writeln('for (let i=0;i<len;i++){');
+        builder.writeln('%s = [];', name)
+        builder.writeln('len = reader.readInt();')
+        builder.writeln('for (let i=0;i<len;i++){')
 
         // Temporary disable .is_vector, not to enter this if again
-        arg.isVector = false;
-        writeArgReadCode(builder, arg, args, '_x');
-        builder.writeln('%s.push(_x);', name);
-        arg.isVector = true;
+        arg.isVector = false
+        writeArgReadCode(builder, arg, args, '_x')
+        builder.writeln('%s.push(_x);', name)
+        arg.isVector = true
     } else if (arg.flagIndicator) {
         // Read the flags, which will indicate what items we should read next
-        builder.writeln('let flags = reader.readInt();');
-        builder.writeln();
+        builder.writeln('let flags = reader.readInt();')
+        builder.writeln()
     } else if (arg.type === 'int') {
-        builder.writeln('%s = reader.readInt();', name);
+        builder.writeln('%s = reader.readInt();', name)
     } else if (arg.type === 'long') {
-        builder.writeln('%s = reader.readLong();', name);
+        builder.writeln('%s = reader.readLong();', name)
     } else if (arg.type === 'int128') {
-        builder.writeln('%s = reader.readLargeInt(128);', name);
+        builder.writeln('%s = reader.readLargeInt(128);', name)
     } else if (arg.type === 'int256') {
-        builder.writeln('%s = reader.readLargeInt(256);', name);
+        builder.writeln('%s = reader.readLargeInt(256);', name)
     } else if (arg.type === 'double') {
-        builder.writeln('%s = reader.readDouble();', name);
+        builder.writeln('%s = reader.readDouble();', name)
     } else if (arg.type === 'string') {
-        builder.writeln('%s = reader.tgReadString();', name);
+        builder.writeln('%s = reader.tgReadString();', name)
     } else if (arg.type === 'Bool') {
-        builder.writeln('%s = reader.tgReadBool();', name);
+        builder.writeln('%s = reader.tgReadBool();', name)
     } else if (arg.type === 'true') {
-        builder.writeln('%s = true;', name);
+        builder.writeln('%s = true;', name)
     } else if (arg.type === 'bytes') {
-        builder.writeln('%s = reader.tgReadBytes();', name);
+        builder.writeln('%s = reader.tgReadBytes();', name)
     } else if (arg.type === 'date') {
-        builder.writeln('%s = reader.tgReadDate();', name);
+        builder.writeln('%s = reader.tgReadDate();', name)
     } else {
         // Else it may be a custom type
         if (!arg.skipConstructorId) {
-            builder.writeln('%s = reader.tgReadObject();', name);
+            builder.writeln('%s = reader.tgReadObject();', name)
         } else {
             // Import the correct type inline to avoid cyclic imports.
             // There may be better solutions so that we can just access
             // all the types before the files have been parsed, but I
             // don't know of any.
-            const sepIndex = arg.type.indexOf('.');
-            let ns;
-            let t;
+            const sepIndex = arg.type.indexOf('.')
+            let ns
+            let t
 
             if (sepIndex === -1) {
-                ns = '.';
-                t = arg.type;
+                ns = '.'
+                t = arg.type
             } else {
-                ns = '.' + arg.type.slice(0, sepIndex);
-                t = arg.type.slice(sepIndex + 1);
+                ns = '.' + arg.type.slice(0, sepIndex)
+                t = arg.type.slice(sepIndex + 1)
             }
 
-            const className = snakeToCamelCase(t);
+            const className = snakeToCamelCase(t)
 
             // There would be no need to import the type if we're in the
             // file with the same namespace, but since it does no harm
             // and we don't have information about such thing in the
             // method we just ignore that case.
-            builder.writeln('let %s = require("%s");', className, ns);
-            builder.writeln('%s = %s.fromReader(reader);', name, className);
+            builder.writeln('let %s = require("%s");', className, ns)
+            builder.writeln('%s = %s.fromReader(reader);', name, className)
         }
     }
 
     // End vector and flag blocks if required (if we opened them before)
     if (arg.isVector) {
-        builder.writeln('}');
+        builder.writeln('}')
     }
 
     if (wasFlag) {
-        builder.endBlock();
-        builder.writeln('else {');
-        builder.writeln('%s = null', name);
-        builder.endBlock();
+        builder.endBlock()
+        builder.writeln('else {')
+        builder.writeln('%s = null', name)
+        builder.endBlock()
         // Restore .isFlag;
-        arg.isFlag = true;
+        arg.isFlag = true
     }
-};
+}
 
 const writePatched = (outDir, namespaceTlobjects) => {
-    fs.mkdirSync(outDir, { recursive: true });
+    fs.mkdirSync(outDir, { recursive: true })
 
     for (const [ns, tlobjects] of Object.entries(namespaceTlobjects)) {
-        const file = `${outDir}/${ns === 'null' ? 'index' : ns}.js`;
-        const stream = fs.createWriteStream(file);
-        const builder = new SourceBuilder(stream);
+        const file = `${outDir}/${ns === 'null' ? 'index' : ns}.js`
+        const stream = fs.createWriteStream(file)
+        const builder = new SourceBuilder(stream)
 
-        builder.writeln(AUTO_GEN_NOTICE);
-        builder.writeln('const struct = require(\'python-struct\');');
-        builder.writeln(`const { TLObject, types, custom } = require('..');`);
-        builder.writeln(`const Helpers = require('../../utils/Helpers');`);
+        builder.writeln(AUTO_GEN_NOTICE)
+        builder.writeln('const struct = require(\'python-struct\');')
+        builder.writeln(`const { TLObject, types, custom } = require('..');`)
+        builder.writeln(`const Helpers = require('../../utils/Helpers');`)
 
-        builder.writeln();
+        builder.writeln()
 
         for (const t of tlobjects) {
-            builder.writeln('class %s extends custom.%s {', t.className, PATCHED_TYPES[t.fullname]);
-            builder.writeln(`static CONSTRUCTOR_ID = 0x${t.id.toString(16)}`);
-            builder.writeln(`static SUBCLASS_OF_ID = 0x${crc32(t.result).toString('16')}`);
-            builder.writeln();
-            builder.writeln('constructor() {');
-            builder.writeln('super();');
-            builder.writeln(`this.CONSTRUCTOR_ID = 0x${t.id.toString(16)}`);
-            builder.writeln(`this.SUBCLASS_OF_ID = 0x${crc32(t.result).toString('16')}`);
+            builder.writeln('class %s extends custom.%s {', t.className, PATCHED_TYPES[t.fullname])
+            builder.writeln(`static CONSTRUCTOR_ID = 0x${t.id.toString(16)}`)
+            builder.writeln(`static SUBCLASS_OF_ID = 0x${crc32(t.result).toString('16')}`)
+            builder.writeln()
+            builder.writeln('constructor() {')
+            builder.writeln('super();')
+            builder.writeln(`this.CONSTRUCTOR_ID = 0x${t.id.toString(16)}`)
+            builder.writeln(`this.SUBCLASS_OF_ID = 0x${crc32(t.result).toString('16')}`)
 
-            builder.endBlock();
+            builder.endBlock()
 
             // writeToJson(t, builder);
-            writeToBytes(t, builder);
-            writeFromReader(t, builder);
-
-            builder.writeln();
-            builder.endBlock();
-            builder.currentIndent = 0;
-            builder.writeln('types.%s%s = %s', t.namespace ? `${t.namespace}.` : '', t.className, t.className);
-            builder.writeln();
+            writeToBytes(t, builder)
+            writeFromReader(t, builder)
+
+            builder.writeln()
+            builder.endBlock()
+            builder.currentIndent = 0
+            builder.writeln('types.%s%s = %s', t.namespace ? `${t.namespace}.` : '', t.className, t.className)
+            builder.writeln()
         }
     }
-};
+}
 
 const writeAllTLObjects = (tlobjects, layer, builder) => {
-    builder.writeln(AUTO_GEN_NOTICE);
-    builder.writeln();
+    builder.writeln(AUTO_GEN_NOTICE)
+    builder.writeln()
 
-    builder.writeln('const { types, functions, patched } = require(\'.\');');
-    builder.writeln();
+    builder.writeln('const { types, functions, patched } = require(\'.\');')
+    builder.writeln()
 
     // Create a constant variable to indicate which layer this is
-    builder.writeln(`const LAYER = %s;`, layer);
-    builder.writeln();
+    builder.writeln(`const LAYER = %s;`, layer)
+    builder.writeln()
 
     // Then create the dictionary containing constructor_id: class
-    builder.writeln('const tlobjects = {');
+    builder.writeln('const tlobjects = {')
 
     // Fill the dictionary (0x1a2b3c4f: tl.full.type.path.Class)
     for (const tlobject of tlobjects) {
-        builder.write('0x0%s: ', tlobject.id.toString(16).padStart(8, '0'));
+        builder.write('0x0%s: ', tlobject.id.toString(16).padStart(8, '0'))
 
         if (tlobject.fullname in PATCHED_TYPES) {
-            builder.write('patched');
+            builder.write('patched')
         } else {
-            builder.write(tlobject.isFunction ? 'functions' : 'types');
+            builder.write(tlobject.isFunction ? 'functions' : 'types')
         }
 
         if (tlobject.namespace) {
-            builder.write('.%s', tlobject.namespace);
+            builder.write('.%s', tlobject.namespace)
         }
 
-        builder.writeln('.%s,', tlobject.className);
+        builder.writeln('.%s,', tlobject.className)
     }
 
-    builder.endBlock(true);
-    builder.writeln('');
-    builder.writeln('module.exports = {');
-    builder.writeln('LAYER,');
-    builder.writeln('tlobjects');
-    builder.endBlock(true);
-};
+    builder.endBlock(true)
+    builder.writeln('')
+    builder.writeln('module.exports = {')
+    builder.writeln('LAYER,')
+    builder.writeln('tlobjects')
+    builder.endBlock(true)
+}
 
 const generateTLObjects = (tlobjects, layer, importDepth, outputDir) => {
     // Group everything by {namespace :[tlobjects]} to generate index.js
-    const namespaceFunctions = {};
-    const namespaceTypes = {};
-    const namespacePatched = {};
+    const namespaceFunctions = {}
+    const namespaceTypes = {}
+    const namespacePatched = {}
 
     // Group {type: [constructors]} to generate the documentation
-    const typeConstructors = {};
+    const typeConstructors = {}
 
     for (const tlobject of tlobjects) {
         if (tlobject.isFunction) {
             if (!namespaceFunctions[tlobject.namespace]) {
-                namespaceFunctions[tlobject.namespace] = [];
+                namespaceFunctions[tlobject.namespace] = []
             }
 
-            namespaceFunctions[tlobject.namespace].push(tlobject);
+            namespaceFunctions[tlobject.namespace].push(tlobject)
         } else {
             if (!namespaceTypes[tlobject.namespace]) {
-                namespaceTypes[tlobject.namespace] = [];
+                namespaceTypes[tlobject.namespace] = []
             }
 
             if (!typeConstructors[tlobject.result]) {
-                typeConstructors[tlobject.result] = [];
+                typeConstructors[tlobject.result] = []
             }
 
-            namespaceTypes[tlobject.namespace].push(tlobject);
-            typeConstructors[tlobject.result].push(tlobject);
+            namespaceTypes[tlobject.namespace].push(tlobject)
+            typeConstructors[tlobject.result].push(tlobject)
 
             if (tlobject.fullname in PATCHED_TYPES) {
                 if (!namespacePatched[tlobject.namespace]) {
-                    namespacePatched[tlobject.namespace] = [];
+                    namespacePatched[tlobject.namespace] = []
                 }
 
-                namespacePatched[tlobject.namespace].push(tlobject);
+                namespacePatched[tlobject.namespace].push(tlobject)
             }
         }
     }
 
-    writeModules(`${outputDir}/functions`, importDepth, 'TLRequest', namespaceFunctions, typeConstructors);
-    writeModules(`${outputDir}/types`, importDepth, 'TLObject', namespaceTypes, typeConstructors);
-    writePatched(`${outputDir}/patched`, namespacePatched);
+    writeModules(`${outputDir}/functions`, importDepth, 'TLRequest', namespaceFunctions, typeConstructors)
+    writeModules(`${outputDir}/types`, importDepth, 'TLObject', namespaceTypes, typeConstructors)
+    writePatched(`${outputDir}/patched`, namespacePatched)
 
-    const filename = `${outputDir}/alltlobjects.js`;
-    const stream = fs.createWriteStream(filename);
-    const builder = new SourceBuilder(stream);
+    const filename = `${outputDir}/AllTLObjects.js`
+    const stream = fs.createWriteStream(filename)
+    const builder = new SourceBuilder(stream)
 
-    writeAllTLObjects(tlobjects, layer, builder);
-};
+    writeAllTLObjects(tlobjects, layer, builder)
+}
 
 const cleanTLObjects = (outputDir) => {
     for (let d of ['functions', 'types', 'patched']) {
-        d = `${outputDir}/d`;
+        d = `${outputDir}/d`
 
         if (fs.statSync(d).isDirectory()) {
-            fs.rmdirSync(d);
+            fs.rmdirSync(d)
         }
     }
 
-    const tl = `${outputDir}/alltlobjects.js`;
+    const tl = `${outputDir}/AllTLObjects.js`
 
     if (fs.statSync(tl).isFile()) {
-        fs.unlinkSync(tl);
+        fs.unlinkSync(tl)
     }
-};
+}
 
 const writeModuleExports = (tlobjects, builder) => {
-    builder.writeln('module.exports = {');
+    builder.writeln('module.exports = {')
 
     for (const t of tlobjects) {
-        builder.writeln(`${t.className},`);
+        builder.writeln(`${t.className},`)
     }
 
-    builder.currentIndent--;
-    builder.writeln('};');
-};
+    builder.currentIndent--
+    builder.writeln('};')
+}
 
 module.exports = {
     generateTLObjects,
     cleanTLObjects,
-};
+}

+ 29 - 30
gramjs_generator/parsers/errors.js

@@ -1,6 +1,6 @@
-const fs = require('fs');
-const csvParse = require('csv-parse/lib/sync');
-const { snakeToCamelCase } = require('../utils');
+const fs = require('fs')
+const csvParse = require('csv-parse/lib/sync')
+const { snakeToCamelCase, variableSnakeToCamelCase } = require('../utils')
 
 const KNOWN_BASE_CLASSES = {
     303: 'InvalidDCError',
@@ -12,7 +12,7 @@ const KNOWN_BASE_CLASSES = {
     420: 'FloodError',
     500: 'ServerError',
     503: 'TimedOutError',
-};
+}
 
 /**
  * Gets the corresponding class name for the given error code,
@@ -20,7 +20,7 @@ const KNOWN_BASE_CLASSES = {
  */
 const getClassName = (errorCode) => {
     if (typeof errorCode === 'number') {
-        return KNOWN_BASE_CLASSES[Math.abs(errorCode)] || 'RPCError' + errorCode.toString().replace('-', 'Neg');
+        return KNOWN_BASE_CLASSES[Math.abs(errorCode)] || 'RPCError' + errorCode.toString().replace('-', 'Neg')
     }
 
     return snakeToCamelCase(
@@ -28,31 +28,30 @@ const getClassName = (errorCode) => {
             .replace('FIRSTNAME', 'FIRST_NAME')
             .replace('SLOWMODE', 'SLOW_MODE')
             .toLowerCase(),
-        'Error'
-    );
-};
+        'Error',
+    )
+}
 
 class TelegramError {
     constructor(codes, name, description) {
         // TODO Some errors have the same name but different integer codes
         // Should these be split into different files or doesn't really matter?
         // Telegram isn't exactly consistent with returned errors anyway.
-        [this.intCode] = codes;
-        this.stringCode = name;
-        this.subclass = getClassName(codes[0]);
-        this.subclassExists = Math.abs(codes[0]) in KNOWN_BASE_CLASSES;
-        this.description = description;
-
-        this.hasCaptures = name.includes('_X');
+        [this.intCode] = codes
+        this.stringCode = name
+        this.subclass = getClassName(codes[0])
+        this.subclassExists = Math.abs(codes[0]) in KNOWN_BASE_CLASSES
+        this.description = description
+        this.hasCaptures = name.includes('_X')
 
         if (this.hasCaptures) {
-            this.name = getClassName(name.replace('_X', ''));
-            this.pattern = name.replace('_X', '_(\\d+)');
-            [this.captureName] = description.match(/{(\w+)}/);
+            this.name = variableSnakeToCamelCase(getClassName(name.replace('_X', '')))
+            this.pattern = variableSnakeToCamelCase(name.replace('_X', '_(\\d+)'))
+            this.captureName = variableSnakeToCamelCase(description.match(/{(\w+)}/)[1])
         } else {
-            this.name = getClassName(name);
-            this.pattern = name;
-            this.captureName = null;
+            this.name = variableSnakeToCamelCase(getClassName(name))
+            this.pattern = variableSnakeToCamelCase(name)
+            this.captureName = null
         }
     }
 }
@@ -62,32 +61,32 @@ class TelegramError {
  * and yields `Error` instances as a result.
  */
 const parseErrors = function* (csvFile) {
-    const f = csvParse(fs.readFileSync(csvFile, { encoding: 'utf-8' })).slice(1);
+    const f = csvParse(fs.readFileSync(csvFile, { encoding: 'utf-8' })).slice(1)
 
     for (let line = 0; line < f.length; line++) {
         if (f[line].length !== 3) {
-            throw new Error(`Columns count mismatch, unquoted comma in desc? (line ${line + 2})`);
+            throw new Error(`Columns count mismatch, unquoted comma in desc? (line ${line + 2})`)
         }
 
-        let [name, codes, description] = f[line];
+        let [name, codes, description] = f[line]
 
         codes =
             codes === '' ?
                 [400] :
                 codes.split(' ').map((x) => {
                     if (isNaN(x)) {
-                        throw new Error(`Not all codes are integers (line ${line + 2})`);
+                        throw new Error(`Not all codes are integers (line ${line + 2})`)
                     }
 
-                    return Number(x);
-                });
+                    return Number(x)
+                })
 
-        yield new TelegramError(codes, name, description);
+        yield new TelegramError(codes, name, description)
     }
-};
+}
 
 module.exports = {
     KNOWN_BASE_CLASSES,
     TelegramError,
     parseErrors,
-};
+}

+ 4 - 4
gramjs_generator/parsers/index.js

@@ -1,6 +1,6 @@
-const { TelegramError, parseErrors } = require('./errors');
-const { MethodInfo, Usability, parseMethods } = require('./methods');
-const { TLObject, parseTl, findLayer } = require('./tlobject');
+const { TelegramError, parseErrors } = require('./errors')
+const { MethodInfo, Usability, parseMethods } = require('./methods')
+const { TLObject, parseTl, findLayer } = require('./tlobject')
 
 module.exports = {
     TelegramError,
@@ -11,4 +11,4 @@ module.exports = {
     TLObject,
     parseTl,
     findLayer,
-};
+}

+ 21 - 21
gramjs_generator/parsers/methods.js

@@ -1,23 +1,23 @@
-const fs = require('fs');
-const csvParse = require('csv-parse/lib/sync');
+const fs = require('fs')
+const csvParse = require('csv-parse/lib/sync')
 
 const Usability = {
     UNKNOWN: 0,
     USER: 1,
     BOT: 2,
     BOTH: 4,
-};
+}
 
 class MethodInfo {
     constructor(name, usability, errors, friendly) {
-        this.name = name;
-        this.errors = errors;
-        this.friendly = friendly;
+        this.name = name
+        this.errors = errors
+        this.friendly = friendly
 
         if (usability.toUpperCase() in Usability) {
-            this.usability = Usability[usability.toUpperCase()];
+            this.usability = Usability[usability.toUpperCase()]
         } else {
-            throw new Error(`Usability must be either user, bot, both or unknown, not ${usability}`);
+            throw new Error(`Usability must be either user, bot, both or unknown, not ${usability}`)
         }
     }
 }
@@ -27,39 +27,39 @@ class MethodInfo {
  * and yields `MethodInfo` instances as a result.
  */
 const parseMethods = function* (csvFile, friendlyCsvFile, errorsDict) {
-    const rawToFriendly = {};
-    const f1 = csvParse(fs.readFileSync(friendlyCsvFile, { encoding: 'utf-8' }));
+    const rawToFriendly = {}
+    const f1 = csvParse(fs.readFileSync(friendlyCsvFile, { encoding: 'utf-8' }))
 
     for (const [ns, friendly, rawList] of f1.slice(1)) {
         for (const raw of rawList.split(' ')) {
-            rawToFriendly[raw] = [ns, friendly];
+            rawToFriendly[raw] = [ns, friendly]
         }
     }
 
-    const f2 = csvParse(fs.readFileSync(csvFile, { encoding: 'utf-8' })).slice(1);
+    const f2 = csvParse(fs.readFileSync(csvFile, { encoding: 'utf-8' })).slice(1)
 
     for (let line = 0; line < f2.length; line++) {
-        let [method, usability, errors] = f2[line];
+        let [method, usability, errors] = f2[line]
 
         errors = errors
             .split(' ')
             .filter(Boolean)
             .map((x) => {
                 if (x && !(x in errorsDict)) {
-                    throw new Error(`Method ${method} references unknown errors ${errors}`);
+                    throw new Error(`Method ${method} references unknown errors ${errors}`)
                 }
 
-                return errorsDict[x];
-            });
+                return errorsDict[x]
+            })
 
-        const friendly = rawToFriendly[method];
-        delete rawToFriendly[method];
-        yield new MethodInfo(method, usability, errors, friendly);
+        const friendly = rawToFriendly[method]
+        delete rawToFriendly[method]
+        yield new MethodInfo(method, usability, errors, friendly)
     }
-};
+}
 
 module.exports = {
     Usability,
     MethodInfo,
     parseMethods,
-};
+}

+ 4 - 4
gramjs_generator/parsers/tlobject/index.js

@@ -1,10 +1,10 @@
-const { TLArg } = require('./tlarg');
-const { TLObject } = require('./tlobject');
-const { parseTl, findLayer } = require('./parser');
+const { TLArg } = require('./tlarg')
+const { TLObject } = require('./tlobject')
+const { parseTl, findLayer } = require('./parser')
 
 module.exports = {
     TLArg,
     TLObject,
     parseTl,
     findLayer,
-};
+}

+ 58 - 60
gramjs_generator/parsers/tlobject/parser.js

@@ -1,7 +1,7 @@
-const fs = require('fs');
-const { TLArg } = require('./tlarg');
-const { TLObject } = require('./tlobject');
-const { Usability } = require('../methods');
+const fs = require('fs')
+const { TLArg } = require('./tlarg')
+const { TLObject } = require('./tlobject')
+const { Usability } = require('../methods')
 
 const CORE_TYPES = new Set([
     0xbc799737, // boolFalse#bc799737 = Bool;
@@ -9,7 +9,7 @@ const CORE_TYPES = new Set([
     0x3fedd339, // true#3fedd339 = True;
     0xc4b9f9bb, // error#c4b9f9bb code:int text:string = Error;
     0x56730bcc, // null#56730bcc = Null;
-]);
+])
 
 // Telegram Desktop (C++) doesn't care about string/bytes, and the .tl files
 // don't either. However in Python we *do*, and we want to deal with bytes
@@ -29,48 +29,48 @@ const AUTH_KEY_TYPES = new Set([
     0xd712e4be, // req_DH_params
     0xf5045f1f, // set_client_DH_params
     0x3072cfa1, // gzip_packed
-]);
+])
 
 const findall = (regex, str, matches) => {
     if (!matches) {
-        matches = [];
+        matches = []
     }
 
     if (!regex.flags.includes(`g`)) {
-        regex = new RegExp(regex.source, `g`);
+        regex = new RegExp(regex.source, `g`)
     }
 
-    const res = regex.exec(str);
+    const res = regex.exec(str)
 
     if (res) {
-        matches.push(res.slice(1));
-        findall(regex, str, matches);
+        matches.push(res.slice(1))
+        findall(regex, str, matches)
     }
 
-    return matches;
-};
+    return matches
+}
 
 const fromLine = (line, isFunction, methodInfo, layer) => {
-    const match = line.match(/([\w.]+)(?:#([0-9a-fA-F]+))?(?:\s{?\w+:[\w\d<>#.?!]+}?)*\s=\s([\w\d<>#.?]+);$/);
+    const match = line.match(/([\w.]+)(?:#([0-9a-fA-F]+))?(?:\s{?\w+:[\w\d<>#.?!]+}?)*\s=\s([\w\d<>#.?]+);$/)
 
     if (!match) {
         // Probably "vector#1cb5c415 {t:Type} # [ t ] = Vector t;"
-        throw new Error(`Cannot parse TLObject ${line}`);
+        throw new Error(`Cannot parse TLObject ${line}`)
     }
 
-    const argsMatch = findall(/({)?(\w+):([\w\d<>#.?!]+)}?/, line);
-    const [, name] = match;
-    methodInfo = methodInfo[name];
+    const argsMatch = findall(/({)?(\w+):([\w\d<>#.?!]+)}?/, line)
+    const [, name] = match
+    methodInfo = methodInfo[name]
 
-    let usability;
-    let friendly;
+    let usability
+    let friendly
 
     if (methodInfo) {
-        usability = methodInfo.usability;
-        friendly = methodInfo.friendly;
+        usability = methodInfo.usability
+        friendly = methodInfo.friendly
     } else {
-        usability = Usability.UNKNOWN;
-        friendly = null;
+        usability = Usability.UNKNOWN
+        friendly = null
     }
 
     return new TLObject(
@@ -81,9 +81,9 @@ const fromLine = (line, isFunction, methodInfo, layer) => {
         isFunction,
         usability,
         friendly,
-        layer
-    );
-};
+        layer,
+    )
+}
 
 /**
  * This method yields TLObjects from a given .tl file.
@@ -92,56 +92,56 @@ const fromLine = (line, isFunction, methodInfo, layer) => {
  * because references to other objects may appear later in the file.
  */
 const parseTl = function* (filePath, layer, methods, ignoreIds = CORE_TYPES) {
-    const methodInfo = (methods || []).reduce((o, m) => ({ ...o, [m.name]: m }), {});
-    const objAll = [];
-    const objByName = {};
-    const objByType = {};
+    const methodInfo = (methods || []).reduce((o, m) => ({ ...o, [m.name]: m }), {})
+    const objAll = []
+    const objByName = {}
+    const objByType = {}
 
-    const file = fs.readFileSync(filePath, { encoding: 'utf-8' });
+    const file = fs.readFileSync(filePath, { encoding: 'utf-8' })
 
-    let isFunction = false;
+    let isFunction = false
 
     for (let line of file.split('\n')) {
-        const commentIndex = line.indexOf('//');
+        const commentIndex = line.indexOf('//')
 
         if (commentIndex !== -1) {
-            line = line.slice(0, commentIndex);
+            line = line.slice(0, commentIndex)
         }
 
-        line = line.trim();
+        line = line.trim()
 
         if (!line) {
-            continue;
+            continue
         }
 
-        const match = line.match(/---(\w+)---/);
+        const match = line.match(/---(\w+)---/)
 
         if (match) {
-            const [, followingTypes] = match;
-            isFunction = followingTypes === 'functions';
-            continue;
+            const [, followingTypes] = match
+            isFunction = followingTypes === 'functions'
+            continue
         }
 
         try {
-            const result = fromLine(line, isFunction, methodInfo, layer);
+            const result = fromLine(line, isFunction, methodInfo, layer)
 
             if (ignoreIds.has(result.id)) {
-                continue;
+                continue
             }
 
-            objAll.push(result);
+            objAll.push(result)
 
             if (!result.isFunction) {
                 if (!objByType[result.result]) {
-                    objByType[result.result] = [];
+                    objByType[result.result] = []
                 }
 
-                objByName[result.fullname] = result;
-                objByType[result.result].push(result);
+                objByName[result.fullname] = result
+                objByType[result.result].push(result)
             }
         } catch (e) {
             if (!e.toString().includes('vector#1cb5c415')) {
-                throw e;
+                throw e
             }
         }
     }
@@ -152,39 +152,37 @@ const parseTl = function* (filePath, layer, methods, ignoreIds = CORE_TYPES) {
         if (AUTH_KEY_TYPES.has(obj.id)) {
             for (const arg of obj.args) {
                 if (arg.type === 'string') {
-                    arg.type = 'bytes';
+                    arg.type = 'bytes'
                 }
             }
         }
 
         for (const arg of obj.args) {
-            arg.cls = objByType[arg.type] || (arg.type in objByName ? [objByName[arg.type]] : []);
+            arg.cls = objByType[arg.type] || (arg.type in objByName ? [objByName[arg.type]] : [])
         }
     }
 
     for (const obj of objAll) {
-        yield obj;
+        yield obj
     }
-};
+}
 
 /**
  * Finds the layer used on the specified scheme.tl file.
  */
 const findLayer = (filePath) => {
-    const layerRegex = /^\/\/\s*LAYER\s*(\d+)$/;
-
-    const file = fs.readFileSync(filePath, { encoding: 'utf-8' });
+    const layerRegex = /^\/\/\s*LAYER\s*(\d+)/
 
+    const file = fs.readFileSync(filePath, { encoding: 'utf-8' })
     for (const line of file.split('\n')) {
-        const match = line.match(layerRegex);
-
+        const match = line.match(layerRegex)
         if (match) {
-            return Number(match[1]);
+            return Number(match[1])
         }
     }
-};
+}
 
 module.exports = {
     parseTl,
     findLayer,
-};
+}

+ 56 - 56
gramjs_generator/parsers/tlobject/tlarg.js

@@ -2,13 +2,13 @@ const fmtStrings = (...objects) => {
     for (const object of objects) {
         for (const [k, v] of Object.entries(object)) {
             if (['null', 'true', 'false'].includes(v)) {
-                object[k] = `<strong>${v}</strong>`;
+                object[k] = `<strong>${v}</strong>`
             } else {
-                object[k] = v.replace(/((['"]).*\2)/, (_, g) => `<em>${g}</em>`);
+                object[k] = v.replace(/((['"]).*\2)/, (_, g) => `<em>${g}</em>`)
             }
         }
     }
-};
+}
 
 const KNOWN_NAMED_EXAMPLES = {
     'message,string': '\'Hello there!\'',
@@ -33,7 +33,7 @@ const KNOWN_NAMED_EXAMPLES = {
     'lang_code,string': '\'en\'',
     'chat_id,int': '478614198',
     'client_id,long': 'random.randrange(-2**63, 2**63)',
-};
+}
 
 const KNOWN_TYPED_EXAMPLES = {
     int128: 'int.from_bytes(crypto.randomBytes(16), \'big\')',
@@ -48,9 +48,9 @@ const KNOWN_TYPED_EXAMPLES = {
     InputChatPhoto: 'client.upload_file(\'/path/to/photo.jpg\')',
     InputFile: 'client.upload_file(\'/path/to/file.jpg\')',
     InputPeer: '\'username\'',
-};
+}
 
-fmtStrings(KNOWN_NAMED_EXAMPLES, KNOWN_TYPED_EXAMPLES);
+fmtStrings(KNOWN_NAMED_EXAMPLES, KNOWN_TYPED_EXAMPLES)
 
 const SYNONYMS = {
     InputUser: 'InputPeer',
@@ -58,7 +58,7 @@ const SYNONYMS = {
     InputDialogPeer: 'InputPeer',
     InputNotifyPeer: 'InputPeer',
     InputMessage: 'int',
-};
+}
 
 // These are flags that are cleaner to leave off
 const OMITTED_EXAMPLES = [
@@ -79,7 +79,7 @@ const OMITTED_EXAMPLES = [
     'admins',
     'edit',
     'delete',
-];
+]
 
 /**
  * Initializes a new .tl argument
@@ -90,57 +90,57 @@ const OMITTED_EXAMPLES = [
  */
 class TLArg {
     constructor(name, argType, genericDefinition) {
-        this.name = name === 'self' ? 'is_self' : name;
+        this.name = name === 'self' ? 'is_self' : name
 
         // Default values
-        this.isVector = false;
-        this.isFlag = false;
-        this.skipConstructorId = false;
-        this.flagIndex = -1;
-        this.cls = null;
+        this.isVector = false
+        this.isFlag = false
+        this.skipConstructorId = false
+        this.flagIndex = -1
+        this.cls = null
 
         // Special case: some types can be inferred, which makes it
         // less annoying to type. Currently the only type that can
         // be inferred is if the name is 'random_id', to which a
         // random ID will be assigned if left as None (the default)
-        this.canBeInferred = name === 'random_id';
+        this.canBeInferred = name === 'random_id'
 
         // The type can be an indicator that other arguments will be flags
         if (argType === '#') {
-            this.flagIndicator = true;
-            this.type = null;
-            this.isGeneric = false;
+            this.flagIndicator = true
+            this.type = null
+            this.isGeneric = false
         } else {
-            this.flagIndicator = false;
-            this.isGeneric = argType.startsWith('!');
+            this.flagIndicator = false
+            this.isGeneric = argType.startsWith('!')
             // Strip the exclamation mark always to have only the name
-            this.type = argType.replace(/^!+/, '');
+            this.type = argType.replace(/^!+/, '')
 
             // The type may be a flag (flags.IDX?REAL_TYPE)
             // Note that 'flags' is NOT the flags name; this
             // is determined by a previous argument
             // However, we assume that the argument will always be called 'flags'
-            const flagMatch = this.type.match(/flags.(\d+)\?([\w<>.]+)/);
+            const flagMatch = this.type.match(/flags.(\d+)\?([\w<>.]+)/)
 
             if (flagMatch) {
-                this.isFlag = true;
+                this.isFlag = true
                 this.flagIndex = Number(flagMatch[1]);
                 // Update the type to match the exact type, not the "flagged" one
-                [, , this.type] = flagMatch;
+                [, , this.type] = flagMatch
             }
 
             // Then check if the type is a Vector<REAL_TYPE>
-            const vectorMatch = this.type.match(/[Vv]ector<([\w\d.]+)>/);
+            const vectorMatch = this.type.match(/[Vv]ector<([\w\d.]+)>/)
 
             if (vectorMatch) {
-                this.isVector = true;
+                this.isVector = true
 
                 // If the type's first letter is not uppercase, then
                 // it is a constructor and we use (read/write) its ID.
                 this.useVectorId = this.type.charAt(0) === 'V';
 
                 // Update the type to match the one inside the vector
-                [, this.type] = vectorMatch;
+                [, this.type] = vectorMatch
             }
 
             // See use_vector_id. An example of such case is ipPort in
@@ -150,10 +150,10 @@ class TLArg {
                     this.type
                         .split('.')
                         .pop()
-                        .charAt(0)
+                        .charAt(0),
                 )
             ) {
-                this.skipConstructorId = true;
+                this.skipConstructorId = true
             }
 
             // The name may contain "date" in it, if this is the case and
@@ -169,14 +169,14 @@ class TLArg {
             // }
         }
 
-        this.genericDefinition = genericDefinition;
+        this.genericDefinition = genericDefinition
     }
 
     typeHint() {
-        let cls = this.type;
+        let cls = this.type
 
         if (cls.includes('.')) {
-            [, cls] = cls.split('.');
+            [, cls] = cls.split('.')
         }
 
         let result = {
@@ -190,49 +190,49 @@ class TLArg {
             bytes: 'bytes',
             Bool: 'bool',
             true: 'bool',
-        };
+        }
 
-        result = result[cls] || `'Type${cls}'`;
+        result = result[cls] || `'Type${cls}'`
 
         if (this.isVector) {
-            result = `List[${result}]`;
+            result = `List[${result}]`
         }
 
         if (this.isFlag && cls !== 'date') {
-            result = `Optional[${result}]`;
+            result = `Optional[${result}]`
         }
 
-        return result;
+        return result
     }
 
     realType() {
         // Find the real type representation by updating it as required
-        let realType = this.flagIndicator ? '#' : this.type;
+        let realType = this.flagIndicator ? '#' : this.type
 
         if (this.isVector) {
             if (this.useVectorId) {
-                realType = `Vector<${realType}>`;
+                realType = `Vector<${realType}>`
             } else {
-                realType = `vector<${realType}>`;
+                realType = `vector<${realType}>`
             }
         }
 
         if (this.isGeneric) {
-            realType = `!${realType}`;
+            realType = `!${realType}`
         }
 
         if (this.isFlag) {
-            realType = `flags.${this.flagIndex}?${realType}`;
+            realType = `flags.${this.flagIndex}?${realType}`
         }
 
-        return realType;
+        return realType
     }
 
     toString() {
         if (this.genericDefinition) {
-            return `{${this.name}:${this.realType()}}`;
+            return `{${this.name}:${this.realType()}}`
         } else {
-            return `${this.name}:${this.realType()}`;
+            return `${this.name}:${this.realType()}`
         }
     }
 
@@ -240,23 +240,23 @@ class TLArg {
         return {
             name: this.name.replace('is_self', 'self'),
             type: this.realType().replace(/\bdate$/, 'int'),
-        };
+        }
     }
 
     asExample(f, indent) {
         if (this.isGeneric) {
-            f.write('other_request');
-            return;
+            f.write('other_request')
+            return
         }
 
         const known =
             KNOWN_NAMED_EXAMPLES[`${this.name},${this.type}`] ||
             KNOWN_TYPED_EXAMPLES[this.type] ||
-            KNOWN_TYPED_EXAMPLES[SYNONYMS[this.type]];
+            KNOWN_TYPED_EXAMPLES[SYNONYMS[this.type]]
 
         if (known) {
-            f.write(known);
-            return;
+            f.write(known)
+            return
         }
 
         // assert self.omit_example() or self.cls, 'TODO handle ' + str(self)
@@ -264,17 +264,17 @@ class TLArg {
         // Pick an interesting example if any
         for (const cls of this.cls) {
             if (cls.isGoodExample()) {
-                cls.asExample(f, indent || 0);
-                return;
+                cls.asExample(f, indent || 0)
+                return
             }
         }
 
         // If no example is good, just pick the first
-        this.cls[0].asExample(f, indent || 0);
+        this.cls[0].asExample(f, indent || 0)
     }
 
     omitExample() {
-        return this.isFlag || (this.canBeInferred && OMITTED_EXAMPLES.includes(this.name));
+        return this.isFlag || (this.canBeInferred && OMITTED_EXAMPLES.includes(this.name))
     }
 }
 
@@ -284,4 +284,4 @@ module.exports = {
     SYNONYMS,
     OMITTED_EXAMPLES,
     TLArg,
-};
+}

+ 61 - 60
gramjs_generator/parsers/tlobject/tlobject.js

@@ -1,6 +1,6 @@
-const { crc32 } = require('crc');
-const struct = require('python-struct');
-const { snakeToCamelCase } = require('../../utils');
+const { crc32 } = require('crc')
+const struct = require('python-struct')
+const { snakeToCamelCase } = require('../../utils')
 
 // https://github.com/telegramdesktop/tdesktop/blob/4bf66cb6e93f3965b40084771b595e93d0b11bcd/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py#L57-L62
 const WHITELISTED_MISMATCHING_IDS = {
@@ -11,7 +11,7 @@ const WHITELISTED_MISMATCHING_IDS = {
         'accessPointRule',
         'help.configSimple',
     ]),
-};
+}
 
 /**
  * Initializes a new TLObject, given its properties.
@@ -29,51 +29,51 @@ const WHITELISTED_MISMATCHING_IDS = {
 class TLObject {
     constructor(fullname, objectId, args, result, isFunction, usability, friendly, layer) {
         // The name can or not have a namespace
-        this.fullname = fullname;
+        this.fullname = fullname
 
         if (fullname.includes('.')) {
-            [this.namespace, this.name] = fullname.split(/\.(.+)/);
+            [this.namespace, this.name] = fullname.split(/\.(.+)/)
             // console.log(fullname.split(/\.(.+)/));
             // const [namespace, ...name] = fullname.split('.');
             // this.namespace = namespace;
             // this.name = name.join('.');
         } else {
-            this.namespace = null;
-            this.name = fullname;
+            this.namespace = null
+            this.name = fullname
         }
 
-        this.args = args;
-        this.result = result;
-        this.isFunction = isFunction;
-        this.usability = usability;
-        this.friendly = friendly;
-        this.id = null;
+        this.args = args
+        this.result = result
+        this.isFunction = isFunction
+        this.usability = usability
+        this.friendly = friendly
+        this.id = null
 
         if (!objectId) {
-            this.id = this.inferId();
+            this.id = this.inferId()
         } else {
-            this.id = parseInt(objectId, 16);
+            this.id = parseInt(objectId, 16)
 
             const whitelist = new Set([
                 ...WHITELISTED_MISMATCHING_IDS[0],
                 ...(WHITELISTED_MISMATCHING_IDS[layer] || []),
-            ]);
+            ])
 
             if (!whitelist.has(this.fullname)) {
                 if (this.id !== this.inferId()) {
-                    throw new Error(`Invalid inferred ID for ${this.repr()}`);
+                    throw new Error(`Invalid inferred ID for ${this.repr()}`)
                 }
             }
         }
 
-        this.className = snakeToCamelCase(this.name, this.isFunction ? 'Request' : '');
+        this.className = snakeToCamelCase(this.name, this.isFunction ? 'Request' : '')
 
-        this.realArgs = this.sortedArgs().filter((a) => !(a.flagIndicator || a.genericDefinition));
+        this.realArgs = this.sortedArgs().filter((a) => !(a.flagIndicator || a.genericDefinition))
     }
 
     get innermostResult() {
-        const index = this.result.indexOf('<');
-        return index === -1 ? this.result : this.result.slice(index + 1, -1);
+        const index = this.result.indexOf('<')
+        return index === -1 ? this.result : this.result.slice(index + 1, -1)
     }
 
     /**
@@ -82,26 +82,26 @@ class TLObject {
      * can be inferred will go last so they can default =None)
      */
     sortedArgs() {
-        return this.args.sort((x) => x.isFlag || x.canBeInferred);
+        return this.args.sort((x) => x.isFlag || x.canBeInferred)
     }
 
     repr(ignoreId) {
-        let hexId;
-        let args;
+        let hexId
+        let args
 
         if (this.id === null || ignoreId) {
-            hexId = '';
+            hexId = ''
         } else {
-            hexId = `#${this.id.toString(16).padStart(8, '0')}`;
+            hexId = `#${this.id.toString(16).padStart(8, '0')}`
         }
 
         if (this.args.length) {
-            args = ` ${this.args.map((arg) => arg.toString()).join(' ')}`;
+            args = ` ${this.args.map((arg) => arg.toString()).join(' ')}`
         } else {
-            args = '';
+            args = ''
         }
 
-        return `${this.fullname}${hexId}${args} = ${this.result}`;
+        return `${this.fullname}${hexId}${args} = ${this.result}`
     }
 
     inferId() {
@@ -109,14 +109,15 @@ class TLObject {
             .replace(/(:|\?)bytes /g, '$1string ')
             .replace(/</g, ' ')
             .replace(/>|{|}/g, '')
-            .replace(/ \w+:flags\.\d+\?true/g, '');
+            .replace(/ \w+:flags\.\d+\?true/g, '')
 
         if (this.fullname === 'inputMediaInvoice') {
+            // eslint-disable-next-line no-empty
             if (this.fullname === 'inputMediaInvoice') {
             }
         }
 
-        return crc32(Buffer.from(representation, 'utf8'));
+        return crc32(Buffer.from(representation, 'utf8'))
     }
 
     toJson() {
@@ -125,66 +126,66 @@ class TLObject {
             [this.isFunction ? 'method' : 'predicate']: this.fullname,
             param: this.args.filter((x) => !x.genericDefinition).map((x) => x.toJson()),
             type: this.result,
-        };
+        }
     }
 
     isGoodExample() {
-        return !this.className.endsWith('Empty');
+        return !this.className.endsWith('Empty')
     }
 
     asExample(f, indent) {
-        f.write('<strong>new</strong> ');
-        f.write(this.isFunction ? 'functions' : 'types');
+        f.write('<strong>new</strong> ')
+        f.write(this.isFunction ? 'functions' : 'types')
 
         if (this.namespace) {
-            f.write('.');
-            f.write(this.namespace);
+            f.write('.')
+            f.write(this.namespace)
         }
 
-        f.write('.');
-        f.write(this.className);
-        f.write('(');
+        f.write('.')
+        f.write(this.className)
+        f.write('(')
 
-        const args = this.realArgs.filter((arg) => !arg.omitExample());
+        const args = this.realArgs.filter((arg) => !arg.omitExample())
 
         if (!args.length) {
-            f.write(')');
-            return;
+            f.write(')')
+            return
         }
 
-        f.write('\n');
-        indent++;
-        let remaining = args.length;
+        f.write('\n')
+        indent++
+        let remaining = args.length
 
         for (const arg of args) {
-            remaining--;
-            f.write('    '.repeat(indent));
-            f.write(arg.name);
-            f.write('=');
+            remaining--
+            f.write('    '.repeat(indent))
+            f.write(arg.name)
+            f.write('=')
 
             if (arg.isVector) {
-                f.write('[');
+                f.write('[')
             }
 
-            arg.asExample(f, indent || 0);
+            arg.asExample(f, indent || 0)
 
             if (arg.isVector) {
-                f.write(']');
+                f.write(']')
             }
 
             if (remaining) {
-                f.write(',');
+                f.write(',')
             }
 
-            f.write('\n');
+            f.write('\n')
         }
 
-        indent--;
-        f.write('    '.repeat(indent));
-        f.write(')');
+        indent--
+        f.write('    '.repeat(indent))
+        f.write(')')
     }
 }
 
 module.exports = {
     TLObject,
-};
+}

+ 19 - 19
gramjs_generator/sourcebuilder.js

@@ -1,17 +1,17 @@
-const util = require('util');
+const util = require('util')
 
 /**
  * This class should be used to build .py source files
  */
 class SourceBuilder {
     constructor(stream, indentSize) {
-        this.currentIndent = 0;
-        this.onNewLine = false;
-        this.indentSize = indentSize || 4;
-        this.stream = stream;
+        this.currentIndent = 0
+        this.onNewLine = false
+        this.indentSize = indentSize || 4
+        this.stream = stream
 
         // Was a new line added automatically before? If so, avoid it
-        this.autoAddedLine = false;
+        this.autoAddedLine = false
     }
 
     /**
@@ -19,7 +19,7 @@ class SourceBuilder {
      * by the current indentation level
      */
     indent() {
-        this.write(' '.repeat(Math.abs(this.currentIndent * this.indentSize)));
+        this.write(' '.repeat(Math.abs(this.currentIndent * this.indentSize)))
     }
 
     /**
@@ -28,18 +28,18 @@ class SourceBuilder {
      */
     write(string, ...args) {
         if (this.onNewLine) {
-            this.onNewLine = false; // We're not on a new line anymore
+            this.onNewLine = false // We're not on a new line anymore
 
             // If the string was not empty, indent; Else probably a new line
             if (string.trim()) {
-                this.indent();
+                this.indent()
             }
         }
 
         if (args.length) {
-            this.stream.write(util.format(string, ...args));
+            this.stream.write(util.format(string, ...args))
         } else {
-            this.stream.write(string);
+            this.stream.write(string)
         }
     }
 
@@ -48,30 +48,30 @@ class SourceBuilder {
      * applying indentation if required
      */
     writeln(string, ...args) {
-        this.write(`${string || ''}\n`, ...args);
-        this.onNewLine = true;
+        this.write(`${string || ''}\n`, ...args)
+        this.onNewLine = true
 
         // If we're writing a block, increment indent for the next time
         if (string && string.endsWith('{')) {
-            this.currentIndent++;
+            this.currentIndent++
         }
 
         // Clear state after the user adds a new line
-        this.autoAddedLine = false;
+        this.autoAddedLine = false
     }
 
     /**
      * Ends an indentation block, leaving an empty line afterwards
      */
     endBlock(semiColon = false) {
-        this.currentIndent--;
+        this.currentIndent--
 
         // If we did not add a new line automatically yet, now it's the time!
         if (!this.autoAddedLine) {
-            this.writeln('}%s', semiColon ? ';' : '');
-            this.autoAddedLine = true;
+            this.writeln('}%s', semiColon ? ';' : '')
+            this.autoAddedLine = true
         }
     }
 }
 
-module.exports = SourceBuilder;
+module.exports = SourceBuilder

+ 5 - 5
gramjs_generator/utils.js

@@ -1,15 +1,15 @@
 const snakeToCamelCase = (name, suffix) => {
-    const result = name.replace(/(?:^|_)([a-z])/g, (_, g) => g.toUpperCase());
-    return result.replace(/_/g, '') + (suffix || '');
-};
+    const result = name.replace(/(?:^|_)([a-z])/g, (_, g) => g.toUpperCase())
+    return result.replace(/_/g, '') + (suffix || '')
+}
 const variableSnakeToCamelCase = (str) => str.replace(
     /([-_][a-z])/g,
     (group) => group.toUpperCase()
         .replace('-', '')
         .replace('_', '')
-);
+)
 
 module.exports = {
     snakeToCamelCase,
     variableSnakeToCamelCase,
-};
+}

+ 70 - 70
index.js

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

+ 0 - 69
package-lock.json

@@ -212,33 +212,6 @@
       "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
       "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
     },
-    "bigint-buffer": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/bigint-buffer/-/bigint-buffer-1.1.2.tgz",
-      "integrity": "sha512-gof9NgbodUgCXD2aUHfUKk5KUDk6/zPlwE+YYpRM2t7zS0K/6WipJFWfziIY+EavxFDuGUnuuQQzzvNcckPKNA==",
-      "dev": true,
-      "requires": {
-        "bindings": "^1.3.0"
-      }
-    },
-    "biguintle": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/biguintle/-/biguintle-1.0.3.tgz",
-      "integrity": "sha512-NRNO+dcFQyS2N0fZ0Lt2QWxhGUsQiREsooSUlt5sP7aMb4LSHk07iYnpgMxhCYZHoZ++rPgxDVAVdGfeytT8TQ==",
-      "dev": true,
-      "requires": {
-        "nanoassert": "^1.1.0"
-      }
-    },
-    "bindings": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
-      "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
-      "dev": true,
-      "requires": {
-        "file-uri-to-path": "1.0.0"
-      }
-    },
     "brace-expansion": {
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -630,12 +603,6 @@
         "flat-cache": "^2.0.1"
       }
     },
-    "file-uri-to-path": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
-      "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
-      "dev": true
-    },
     "find-up": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
@@ -818,11 +785,6 @@
         "through": "^2.3.6"
       }
     },
-    "ip": {
-      "version": "1.1.5",
-      "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
-      "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
-    },
     "is-arrayish": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
@@ -917,12 +879,6 @@
         "graceful-fs": "^4.1.6"
       }
     },
-    "leemon": {
-      "version": "6.2.0",
-      "resolved": "https://registry.npmjs.org/leemon/-/leemon-6.2.0.tgz",
-      "integrity": "sha512-a5ieuGSGEb5ezCL6UNds5//cVFaKpeexVK0VDCE8/eOF0r0/9Og94LQ33U2Px5dUcHVCDPWQY8gXLgDlDJnyyg==",
-      "dev": true
-    },
     "levn": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
@@ -1003,12 +959,6 @@
       "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
       "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s="
     },
-    "nanoassert": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-1.1.0.tgz",
-      "integrity": "sha1-TzFS4JVA/eKMdvRLGbvNHVpCR40=",
-      "dev": true
-    },
     "natural-compare": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -1242,11 +1192,6 @@
         "long": "^4.0.0"
       }
     },
-    "random-bigint": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/random-bigint/-/random-bigint-0.0.1.tgz",
-      "integrity": "sha512-X+NTsf5Hzl/tRNLiNTD3N1LRU0eKdIE0+plNlV1CmXLTlnAxj6HipcTnOhWvFRoSytCz6l1f4KYFf/iH8NNSLw=="
-    },
     "read-pkg": {
       "version": "5.2.0",
       "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
@@ -1380,20 +1325,6 @@
         "is-fullwidth-code-point": "^2.0.0"
       }
     },
-    "smart-buffer": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.2.tgz",
-      "integrity": "sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw=="
-    },
-    "socks": {
-      "version": "2.3.2",
-      "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.2.tgz",
-      "integrity": "sha512-pCpjxQgOByDHLlNqlnh/mNSAxIUkyBBuwwhTcV+enZGbDaClPvHdvm6uvOwZfFJkam7cGhBNbb4JxiP8UZkRvQ==",
-      "requires": {
-        "ip": "^1.1.5",
-        "smart-buffer": "4.0.2"
-      }
-    },
     "source-map": {
       "version": "0.5.7",
       "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",

+ 1 - 6
package.json

@@ -26,16 +26,11 @@
     "node-rsa": "^1.0.6",
     "promise-socket": "^6.0.2",
     "python-struct": "^1.1.1",
-    "random-bigint": "0.0.1",
-    "socks": "^2.3.2",
     "string-format": "^2.0.0"
   },
   "devDependencies": {
     "babel-eslint": "^10.0.3",
-    "bigint-buffer": "^1.1.2",
-    "biguintle": "^1.0.3",
     "eslint": "^6.5.1",
-    "eslint-config-google": "^0.14.0",
-    "leemon": "^6.2.0"
+    "eslint-config-google": "^0.14.0"
   }
 }