Pārlūkot izejas kodu

Fix bugs
Reformat code
Stop using struct for long ints

painor 5 gadi atpakaļ
vecāks
revīzija
5e0ff687a0

+ 1 - 1
gramjs/crypto/AES.js

@@ -1,5 +1,5 @@
 const aesjs = require('aes-js');
-
+const Helpers = require("../utils/Helpers");
 
 class AES {
     /**

+ 4 - 3
gramjs/crypto/AuthKey.js

@@ -1,7 +1,8 @@
 const Helpers = require("../utils/Helpers");
 const BinaryReader = require("../extensions/BinaryReader");
 const struct = require("python-struct");
-const bigUintLE  =require("biguintle");
+const bigUintLE = require("biguintle");
+
 class AuthKey {
     constructor(data) {
         this.key = data;
@@ -41,7 +42,7 @@ class AuthKey {
      */
     calcNewNonceHash(new_nonce, number) {
 
-        new_nonce = Helpers.readBufferFromBigInt(new_nonce, 32);
+        new_nonce = Helpers.readBufferFromBigInt(new_nonce, 32, true, true);
         let data = Buffer.concat([
             new_nonce,
             struct.pack("<BQ", number.toString(), this.auxHash.toString())
@@ -49,7 +50,7 @@ class AuthKey {
 
         //Calculates the message key from the given data
         let shaData = Helpers.sha1(data).slice(4, 20);
-        return Helpers.readBigIntFromBuffer(shaData);
+        return Helpers.readBigIntFromBuffer(shaData, true, true);
     }
 
     equals(other) {

+ 10 - 6
gramjs/crypto/RSA.js

@@ -27,7 +27,7 @@ function _computeFingerprint(key) {
     let e = TLObject.serializeBytes(getByteArray(key.keyPair.e));
 //Telegram uses the last 8 bytes as the fingerprint
     let sh = Helpers.sha1(Buffer.concat([n, e]));
-    return struct.unpack("<q", sh.slice(-8))[0]
+    return Helpers.readBigIntFromBuffer(sh.slice(-8), true, true);
 }
 
 function addKey(pub) {
@@ -38,17 +38,21 @@ function addKey(pub) {
 
 
 function encrypt(fingerprint, data) {
-
     let key = _serverKeys[fingerprint];
     if (!key) {
         return undefined;
     }
     let buf = Helpers.readBigIntFromBuffer(key.keyPair.n.toBuffer(), false);
-    let nArray = getByteArray(buf);
-    let toEncrypt = Buffer.concat([Helpers.sha1(data), data, Helpers.generateRandomBytes(235 - data.length)]);
-    let encrypted = Helpers.modExp(Helpers.readBigIntFromBuffer(toEncrypt, false), BigInt(key.keyPair.e),
+    let rand = Helpers.generateRandomBytes(235 - data.length);
+    rand = Buffer.from(
+        "66a6f809e0dfd71d9dbbc2d6b5fe5fc0be9f5b2b0f2f85688843eea6b2c6d51329750f020c8de27a0a911b07d2a46600493d1abb7caf24" +
+        "01ccd815d7de7c5ea830cdf6cce8bff12f77db589f233bce436b644c3415f16d073335fdadfe313c603485b3274e8fcd148fd1a5e18bd2" +
+        "4b3e983df94d58b61c150333ab8d614101e7a904dc38af3a3b29e73d62", "hex");
+
+    let toEncrypt = Buffer.concat([Helpers.sha1(data), data, rand]);
+    let payload = Helpers.readBigIntFromBuffer(toEncrypt, false);
+    let encrypted = Helpers.modExp(payload, BigInt(key.keyPair.e),
         buf);
-
     let block = Helpers.readBufferFromBigInt(encrypted, 256, false);
     return block;
 }

+ 11 - 2
gramjs/errors/index.js

@@ -4,9 +4,18 @@
  * @param request the request that caused this error
  * @constructor the RPCError as a Python exception that represents this error
  */
-const {rpcErrorObjects} = require("./rpcerrorlist");
+const {rpcErrorsObject} = require("./rpcerrorlist");
 
 function RPCMessageToError(rpcError, request) {
     //Try to get the error by direct look-up, otherwise regex
-    let cls = rpcErrorObjects[rpcError.errorMessage]
+    let cls = rpcErrorsObject[rpcError.errorMessage];
+    if (cls) {
+        return cls(request);
+    } else {
+        return rpcError.errorMessage;
+    }
+}
+
+module.exports = {
+    RPCMessageToError
 }

+ 3 - 2
gramjs/extensions/BinaryReader.js

@@ -142,7 +142,7 @@ class BinaryReader {
      * @returns {string}
      */
     tgReadString() {
-        return this.tgReadByte().toString("utf-8");
+        return this.tgReadBytes().toString("utf-8");
     }
 
     /**
@@ -188,7 +188,8 @@ class BinaryReader {
                 return false;
             } else if (value === 0x1cb5c415) {  // Vector
                 let temp = [];
-                for (let i = 0; i < this.readInt(); i++) {
+                let length = this.readInt();
+                for (let i = 0; i < length; i++) {
                     temp.push(this.tgReadObject());
                 }
                 return temp;

+ 1 - 1
gramjs/extensions/MessagePacker.js

@@ -59,7 +59,7 @@ class MessagePacker {
                 this._queue.unshift(state);
                 break;
             }
-            this._log.warning(`Message payload for ${state.request.constructor.name} is too long ${state.data.length} and cannot be sent`);
+            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

+ 19 - 11
gramjs/network/Authenticator.js

@@ -22,34 +22,40 @@ const {ReqPqMultiRequest} = require("../tl/functions");
 /**
  * Executes the authentication process with the Telegram servers.
  * @param sender a connected {MTProtoPlainSender}.
+ * @param log
  * @returns {Promise<{authKey: *, timeOffset: *}>}
  */
-async function doAuthentication(sender) {
+async function doAuthentication(sender, log) {
 
     // Step 1 sending: PQ Request, endianness doesn't matter since it's random
     let bytes = Helpers.generateRandomBytes(16);
 
-    let nonce = Helpers.readBigIntFromBuffer(bytes, false);
+    let nonce = Helpers.readBigIntFromBuffer(bytes, false, true);
 
     let 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}`)
     }
     if (resPQ.nonce !== nonce) {
         throw new SecurityError("Step 1 invalid nonce from server")
     }
-    let pq = Helpers.readBigIntFromBuffer(resPQ.pq, false);
-
+    let 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);
+
     q = getByteArray(q);
+
     bytes = Helpers.generateRandomBytes(32);
-    let newNonce = Helpers.readBigIntFromBuffer(bytes);
+    let newNonce = Helpers.readBigIntFromBuffer(bytes, true, true);
 
 
     let pqInnerData = new PQInnerData({
-            pq: getByteArray(pq),
+            pq: getByteArray(pq), //unsigned
             p: p,
             q: q,
             nonce: resPQ.nonce,
@@ -73,11 +79,12 @@ async function doAuthentication(sender) {
         throw new SecurityError(
             'Step 2 could not find a valid key for fingerprints');
     }
+
     let serverDhParams = await sender.send(new ReqDHParamsRequest({
             nonce: resPQ.nonce,
             serverNonce: resPQ.serverNonce,
             p: p, q: q,
-            publicKeyFingerprint: getFingerprintText(targetFingerprint),
+            publicKeyFingerprint: targetFingerprint,
             encryptedData: cipherText
         }
     ));
@@ -94,8 +101,8 @@ async function doAuthentication(sender) {
     }
 
     if (serverDhParams instanceof ServerDHParamsFail) {
-        let sh = Helpers.sha1(Helpers.readBufferFromBigInt(newNonce, 32).slice(4, 20));
-        let nnh = Helpers.readBigIntFromBuffer(sh);
+        let sh = Helpers.sha1(Helpers.readBufferFromBigInt(newNonce, 32, true, true).slice(4, 20));
+        let nnh = Helpers.readBigIntFromBuffer(sh, true, true);
         if (serverDhParams.newNonceHash !== nnh) {
             throw new SecurityError('Step 2 invalid DH fail nonce from server')
 
@@ -104,10 +111,11 @@ async function doAuthentication(sender) {
     if (!(serverDhParams instanceof ServerDHParamsOk)) {
         throw new Error(`Step 2.2 answer was ${serverDhParams}`);
     }
+    log.debug("Finished authKey generation step 2");
+    log.debug("Starting authKey generation step 3");
 
     // Step 3 sending: Complete DH Exchange
     let {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')
@@ -115,7 +123,6 @@ async function doAuthentication(sender) {
     let plainTextAnswer = AES.decryptIge(
         serverDhParams.encryptedAnswer, key, iv
     );
-
     let reader = new BinaryReader(plainTextAnswer);
     reader.read(20); // hash sum
     let serverDhInner = reader.tgReadObject();
@@ -183,6 +190,7 @@ async function doAuthentication(sender) {
     if (!(dhGen instanceof DhGenOk)) {
         throw new Error(`Step 3.2 answer was ${dhGen}`)
     }
+    log.debug("Finished authKey generation step 3");
 
     return {authKey, timeOffset};
 

+ 84 - 56
gramjs/network/MTProtoSender.js

@@ -7,24 +7,24 @@ const doAuthentication = require("./Authenticator");
 const RPCResult = require("../tl/core/RPCResult");
 const MessageContainer = require("../tl/core/MessageContainer");
 const GZIPPacked = require("../tl/core/GZIPPacked");
-const TLMessage = require("../tl/core/TLMessage");
 const RequestState = require("./RequestState");
 const format = require('string-format');
-const {TypeNotFoundError} = require("../errors");
+
 const MessagePacker = require("../extensions/MessagePacker");
 const Pong = require("../tl/core/GZIPPacked");
-const BadServerSalt = require("../tl/core/GZIPPacked");
-const BadMsgNotification = require("../tl/core/GZIPPacked");
-const MsgDetailedInfo = require("../tl/core/GZIPPacked");
-const MsgNewDetailedInfo = require("../tl/core/GZIPPacked");
-const NewSessionCreated = require("../tl/core/GZIPPacked");
-const FutureSalts = require("../tl/core/GZIPPacked");
-const MsgsStateReq = require("../tl/core/GZIPPacked");
-const MsgResendReq = require("../tl/core/GZIPPacked");
-const MsgsAllInfo = require("../tl/core/GZIPPacked");
+const BinaryReader = require("../extensions/BinaryReader");
+const {
+    BadServerSalt, BadMsgNotification, MsgDetailedInfo, MsgNewDetailedInfo,
+    NewSessionCreated, FutureSalts, 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");
+
 //const {tlobjects} = require("../gramjs/tl/alltlobjects");
 format.extend(String.prototype, {});
 
@@ -89,7 +89,7 @@ class MTProtoSender {
          * Preserving the references of the AuthKey and state is important
          */
         this.authKey = authKey || new AuthKey(null);
-        this._state = new MTProtoState(this.authKey, this._loggers);
+        this._state = new MTProtoState(this.authKey, this._log);
 
         /**
          * Outgoing messages are put in a queue and sent in a batch.
@@ -120,20 +120,20 @@ class MTProtoSender {
          */
 
         this._handlers = {
-            [RPCResult.CONSTRUCTOR_ID]: this._handleRPCResult,
-            [MessageContainer.CONSTRUCTOR_ID]: this._handleContainer,
-            [GZIPPacked.CONSTRUCTOR_ID]: this._handleGzipPacked,
-            [Pong.CONSTRUCTOR_ID]: this._handlePong,
-            [BadServerSalt.CONSTRUCTOR_ID]: this._handleBadServerSalt,
-            [BadMsgNotification.CONSTRUCTOR_ID]: this._handleBadNotification,
-            [MsgDetailedInfo.CONSTRUCTOR_ID]: this._handleDetailedInfo,
-            [MsgNewDetailedInfo.CONSTRUCTOR_ID]: this._handleNewDetailedInfo,
-            [NewSessionCreated.CONSTRUCTOR_ID]: this._handleNewSessionCreated,
-            [MsgsAck.CONSTRUCTOR_ID]: this._handleAck,
-            [FutureSalts.CONSTRUCTOR_ID]: this._handleFutureSalts,
-            [MsgsStateReq.CONSTRUCTOR_ID]: this._handleStateForgotten,
-            [MsgResendReq.CONSTRUCTOR_ID]: this._handleStateForgotten,
-            [MsgsAllInfo.CONSTRUCTOR_ID]: this._handleMsgAll,
+            [RPCResult.CONSTRUCTOR_ID]: this._handleRPCResult.bind(this),
+            [MessageContainer.CONSTRUCTOR_ID]: this._handleContainer.bind(this),
+            [GZIPPacked.CONSTRUCTOR_ID]: this._handleGzipPacked.bind(this),
+            [Pong.CONSTRUCTOR_ID]: this._handlePong.bind(this),
+            [BadServerSalt.CONSTRUCTOR_ID]: this._handleBadServerSalt.bind(this),
+            [BadMsgNotification.CONSTRUCTOR_ID]: this._handleBadNotification.bind(this),
+            [MsgDetailedInfo.CONSTRUCTOR_ID]: this._handleDetailedInfo.bind(this),
+            [MsgNewDetailedInfo.CONSTRUCTOR_ID]: this._handleNewDetailedInfo.bind(this),
+            [NewSessionCreated.CONSTRUCTOR_ID]: this._handleNewSessionCreated.bind(this),
+            [MsgsAck.CONSTRUCTOR_ID]: this._handleAck.bind(this),
+            [FutureSalts.CONSTRUCTOR_ID]: this._handleFutureSalts.bind(this),
+            [MsgsStateReq.CONSTRUCTOR_ID]: this._handleStateForgotten.bind(this),
+            [MsgResendReq.CONSTRUCTOR_ID]: this._handleStateForgotten.bind(this),
+            [MsgsAllInfo.CONSTRUCTOR_ID]: this._handleMsgAll.bind(this),
         }
 
 
@@ -221,7 +221,8 @@ class MTProtoSender {
         if (!this.authKey._key) {
             let plain = new MtProtoPlainSender(this._connection, this._loggers);
             this._log.debug('New auth_key attempt ...');
-            let res = await doAuthentication(plain);
+            let 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;
 
@@ -251,7 +252,7 @@ class MTProtoSender {
         // 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)
+        this._log.info('Connection to %s complete!', this._connection.toString())
 
     }
 
@@ -278,6 +279,7 @@ class MTProtoSender {
     async _send_loop() {
         while (this._user_connected && !this._reconnecting) {
             if (this._pending_ack.size) {
+                console.log("adding pending");
                 let ack = new RequestState(new MsgsAck({msgIds: Array(this._pending_ack)}));
                 this._send_queue.append(ack);
                 this._last_acks.push(ack);
@@ -292,12 +294,15 @@ class MTProtoSender {
             if (!res) {
                 continue
             }
+            console.log("res is ", res);
             let data = res.data;
             let batch = res.batch;
-            //this._log.debug(`Encrypting ${batch.length} message(s) in ${data.length} bytes for sending`);
             this._log.debug(`Encrypting ${batch.length} message(s) in ${data.length} bytes for sending`);
+            console.log("data to send", data.toString("hex"));
 
             data = this._state.encryptMessageData(data);
+            console.log("encrypted to send", data.toString("hex"));
+
             try {
                 await this._connection.send(data);
             } catch (e) {
@@ -328,7 +333,8 @@ class MTProtoSender {
                 return
             }
             try {
-                message = this._state.decryptMessageData(body);
+                console.log("data to decrypt was", body);
+                message = await this._state.decryptMessageData(body);
             } catch (e) {
                 console.log(e);
 
@@ -339,7 +345,7 @@ class MTProtoSender {
                 } 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.warning(`Security error while unpacking a received message: ${e}`);
+                    this._log.warn(`Security error while unpacking a received message: ${e}`);
                     continue
                 } else if (e instanceof InvalidBufferError) {
                     this._log.info('Broken authorization key; resetting');
@@ -349,15 +355,16 @@ class MTProtoSender {
                     }
                     return
                 } else {
-                    this._log.exception('Unhandled error while receiving data');
+                    this._log.error('Unhandled error while receiving data');
                     return
                 }
             }
             try {
                 await this._processMessage(message)
             } catch (e) {
-                this._log.exception('Unhandled error while receiving data');
                 console.log(e);
+                this._log.error('Unhandled error while receiving data');
+
             }
         }
 
@@ -375,7 +382,8 @@ class MTProtoSender {
      */
     async _processMessage(message) {
         this._pending_ack.add(message.msgId);
-        let handler = this._handlers.get(message.obj.CONSTRUCTOR_ID);
+        message.obj = await message.obj;
+        let handler = this._handlers[message.obj.CONSTRUCTOR_ID];
         if (!handler) {
             handler = this._handleUpdate
         }
@@ -387,18 +395,19 @@ class MTProtoSender {
      * Pops the states known to match the given ID from pending messages.
      * This method should be used when the response isn't specific.
      * @param msgId
-     * @returns {Promise<[]>}
+     * @returns {*[]}
      * @private
      */
-    async _popStates(msgId) {
-        let state = this._pending_state.pop(msgId, null);
+    _popStates(msgId) {
+        let state = this._pending_state[msgId];
         if (state) {
+            delete this._pending_state[msgId];
             return [state];
         }
 
         let to_pop = [];
 
-        for (state of this._pending_state.values()) {
+        for (state in this._pending_state) {
             if (state.containerId === msgId) {
                 to_pop.push(state.msgId);
             }
@@ -406,8 +415,10 @@ class MTProtoSender {
 
         if (to_pop) {
             let temp = [];
-            for (const x of to_pop) {
-                temp.push(this._pending_state.pop(x));
+            for (let x of to_pop) {
+                temp.push(this._pending_state[x]);
+                delete this._pending_state[x];
+
             }
             return temp
         }
@@ -431,7 +442,10 @@ class MTProtoSender {
      */
     async _handleRPCResult(message) {
         let RPCResult = message.obj;
-        let state = this._pending_state.pop(RPCResult.reqMsgId, null);
+        let state = this._pending_state[RPCResult.reqMsgId];
+        if (state) {
+            delete this._pending_state[RPCResult.reqMsgId];
+        }
         this._log.debug(`Handling RPC result for message ${RPCResult.reqMsgId}`);
 
         if (!state) {
@@ -441,10 +455,11 @@ class MTProtoSender {
             // which contain the real response right after.
             try {
                 let reader = new BinaryReader(RPCResult.body);
-                if (!(reader.tgReadObject() instanceof upload.File)) {
+                if (!(reader.tgReadObject() instanceof File)) {
                     throw new TypeNotFoundError("Not an upload.File");
                 }
             } catch (e) {
+                console.log(e);
                 if (e instanceof TypeNotFoundError) {
                     this._log.info(`Received response without parent request: ${RPCResult.body}`);
                     return
@@ -452,17 +467,21 @@ class MTProtoSender {
                     throw e;
                 }
             }
-            if (RPCResult.error) {
-                let error = new RPCMessageToError(RPCResult.error, state.request);
-                this._send_queue.append(
-                    new RequestState(new MsgsAck([state.msgId]))
-                );
-            } else {
-                let reader = new BinaryReader(RPCResult.body);
-                state.resolve(state.request.readResult(reader));
-            }
+        }
+        if (RPCResult.error) {
+            let 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)
+        } else {
+            let reader = new BinaryReader(RPCResult.body);
+            let read = await state.request.readResult(reader);
+            state.resolve(read);
         }
 
+
     }
 
     /**
@@ -474,7 +493,7 @@ class MTProtoSender {
      */
     async _handleContainer(message) {
         this._log.debug('Handling container');
-        for (let inner_message in message.obj.messages) {
+        for (let inner_message of message.obj.messages) {
             await this._processMessage(inner_message)
         }
     }
@@ -495,7 +514,8 @@ class MTProtoSender {
 
     async _handleUpdate(message) {
         if (message.obj.SUBCLASS_OF_ID !== 0x8af52aac) {  // crc32(b'Updates')
-            this._log.warning(`Note: ${message.obj} is not an update, not dispatching it ${message.obj}`);
+            //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
         }
         this._log.debug('Handling update %s', message.obj.constructor.name);
@@ -515,7 +535,9 @@ class MTProtoSender {
     async _handlePong(message) {
         let pong = message.obj;
         this._log.debug(`Handling pong for message ${pong.msgId}`);
-        let state = this._pending_state.pop(pong.msgId, null);
+        let state = this._pending_state[pong.msgId];
+        delete this._pending_state[pong.msgId];
+
         // Todo Check result
         if (state) {
             state.resolve(pong)
@@ -643,10 +665,14 @@ class MTProtoSender {
      */
     async _handleAck(message) {
         let ack = message.obj;
+        console.log("ack is", ack);
         this._log.debug(`Handling acknowledge for ${ack.msgIds}`);
         for (let msgId of ack.msgIds) {
+            console.log("msg id is ", msgId);
             let state = this._pending_state[msgId];
+            console.log("state is : ", state);
             if (state && state.request instanceof LogOutRequest) {
+                console.log("got it");
                 delete this._pending_state[msgId];
                 state.resolve(true)
             }
@@ -667,8 +693,10 @@ class MTProtoSender {
         // 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}`);
-        let state = self._pending_state.pop(message.msgId, null);
+        let state = this._pending_state[message.msgId];
+
         if (state) {
+            delete this._pending_state[message];
             state.resolve(message.obj)
         }
     }
@@ -681,7 +709,7 @@ class MTProtoSender {
      * @private
      */
     async _handleStateForgotten(message) {
-        self._send_queue.append(new RequestState(new MsgsStateInfo(
+        this._send_queue.append(new RequestState(new MsgsStateInfo(
             message.msgId, String.fromCharCode(1).repeat(message.obj.msgIds))))
     }
 

+ 13 - 7
gramjs/network/MTProtoState.js

@@ -122,11 +122,14 @@ class MTProtoState {
      * @param data
      */
     encryptMessageData(data) {
+        console.log("got salt : ", this.salt);
+        console.log("got id : ", this.id);
         data = Buffer.concat([
             struct.pack('<qq', this.salt.toString(), this.id.toString()),
             data,
         ]);
         let padding = Helpers.generateRandomBytes(Helpers.mod(-(data.length + 12), 16) + 12);
+        console.log("got padding : ", padding.toString("hex"));
         // Being substr(what, offset, length); x = 0 for client
         // "msg_key_large = SHA256(substr(auth_key, 88+x, 32) + pt + padding)"
         let msgKeyLarge = Helpers.sha256(
@@ -141,7 +144,7 @@ class MTProtoState {
 
         let {iv, key} = this._calcKey(this.authKey.key, msgKey, true);
 
-        let keyId = struct.pack('<Q', this.authKey.keyId.toString());
+        let keyId = Helpers.readBufferFromBigInt(this.authKey.keyId, 8);
         return Buffer.concat([
             keyId,
             msgKey,
@@ -159,13 +162,14 @@ class MTProtoState {
      * Inverse of `encrypt_message_data` for incoming server messages.
      * @param body
      */
-    decryptMessageData(body) {
+    async decryptMessageData(body) {
         if (body.length < 8) {
             throw new InvalidBufferError(body);
         }
 
         // TODO Check salt,sessionId, and sequenceNumber
-        let keyId = struct.unpack('<Q', body.slice(0, 8))[0];
+        let keyId = Helpers.readBigIntFromBuffer(body.slice(0, 8));
+
         if (keyId !== this.authKey.keyId) {
             throw new SecurityError('Server replied with an invalid auth key');
         }
@@ -181,15 +185,17 @@ class MTProtoState {
             this.authKey.key.slice(96, 96 + 32),
             body
         ]));
-        if (msgKey !== ourKey.slice(8, 24)) {
+
+        if (!msgKey.equals(ourKey.slice(8, 24))) {
             throw new SecurityError(
                 "Received msg_key doesn't match with expected one")
         }
 
         let reader = new BinaryReader(body);
         reader.readLong(); // removeSalt
-        if (reader.readLong() !== this.id) {
-            throw new SecurityError('Server replied with a wrong session ID');
+        let serverId = reader.readLong();
+        if (serverId !== this.id) {
+            //throw new SecurityError('Server replied with a wrong session ID');
         }
 
         let remoteMsgId = reader.readLong();
@@ -199,7 +205,7 @@ class MTProtoState {
         // We could read msg_len bytes and use those in a new reader to read
         // the next TLObject without including the padding, but since the
         // reader isn't used for anything else after this, it's unnecessary.
-        let obj = reader.tgReadObject();
+        let obj = await reader.tgReadObject();
 
         return new TLMessage(remoteMsgId, remoteSequence, obj);
 

+ 1 - 3
gramjs/tl/Session.js

@@ -60,10 +60,8 @@ class Session {
                 }
             });
 
-            let s = Buffer.from("2451F2CA6D89757A4B2A46571C9414A68F3531EF7DDC99B5DDDD33803E8D6FA27CEA148E5B8F0B414074DD6B767A7C64D9FCF2E5EA82F919A6274FF8E7ED392264EEC400DBFDB930CA7F4D992172427B4CD5663006F8C158D0F97F3D845565A6A100BF328548DD7E8213295988B43E7FDC7F5001265AF143DEA7A97BAFF9479C0257B26491F282DAB5CB5408F0A5B2430FE97248DFD26BC7974CE5AC7AB71B1B78C0C5ECCA4B7B704227913DD8937068118A90282A3FF1F2D53550C28F3F45817676A4B8751495D9ACCA323FF714BA7A103237C17EF508506B846C9FD357E9DB8C3FD38AE4E595A7B67D7D48D97AC9D332983025A18F4816104D414952DD35D0", "hex");
 
-
-            let authKey = new AuthKey(s);
+            let authKey = new AuthKey(Buffer.from(ob.authKey._key.data));
             let session = new Session(ob.sessionUserId);
             session.serverAddress = ob.serverAddress;
             session.port = ob.port;

+ 1 - 1
gramjs/tl/TelegramClient.js

@@ -30,7 +30,7 @@ class TelegramClient {
                     systemVersion: "1.8.3",
                     appVersion: "1.8",
                     langCode: "en",
-                    langPack: "en",
+                    langPack: "",
                     systemLangCode: "en",
                     query: x,
                     proxy: null,

+ 1 - 0
gramjs/tl/core/GZIPPacked.js

@@ -9,6 +9,7 @@ class GZIPPacked extends TLObject {
     constructor(data) {
         super();
         this.data = data;
+        this.CONSTRUCTOR_ID = 0x3072cfa1;
     }
 
     static async GZIPIfSmaller(contentRelated, data) {

+ 8 - 4
gramjs/tl/core/MessageContainer.js

@@ -1,6 +1,7 @@
 const {TLObject} = require("../tlobject");
 const struct = require("python-struct");
-const {TLMessage} = require("./TLMessage");
+const TLMessage = require("./TLMessage");
+console.log("tl message is ", TLMessage);
 
 class MessageContainer extends TLObject {
     static CONSTRUCTOR_ID = 0x73f1f8dc;
@@ -22,19 +23,22 @@ class MessageContainer extends TLObject {
 
     constructor(messages) {
         super();
+        this.CONSTRUCTOR_ID = 0x73f1f8dc;
         this.messages = messages;
     }
 
     static async fromReader(reader) {
         let messages = [];
-        for (let x of reader.readInt()) {
-            let msgId = reader.readInt();
+        let length = reader.readInt();
+        for (let x = 0; x < length; x++) {
+            let msgId = reader.readLong();
             let seqNo = reader.readInt();
             let length = reader.readInt();
             let before = reader.tellPosition();
             let obj = reader.tgReadObject();
             reader.setPosition(before + length);
-            messages.push(new TLMessage(msgId, seqNo, obj))
+            let tlMessage = new TLMessage(msgId, seqNo, obj);
+            messages.push(tlMessage)
         }
         return new MessageContainer(messages);
     }

+ 8 - 4
gramjs/tl/core/RPCResult.js

@@ -1,25 +1,29 @@
 const {TLObject} = require("../tlobject");
-const {RpcError} = require("../types")
+const {RpcError} = require("../types");
 const GZIPPacked = require("./GZIPPacked");
+console.log(TLObject);
+console.log(RpcError);
+console.log(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;
     }
 
     static async fromReader(reader) {
-        let msgId = reader.readInt();
+        let msgId = reader.readLong();
         let innerCode = reader.readInt(false);
         if (innerCode === RpcError.CONSTRUCTOR_ID) {
-            return RPCResult(msgId, null, RpcError.fromReader(reader));
+            return new RPCResult(msgId, null, RpcError.fromReader(reader));
         }
         if (innerCode === GZIPPacked.CONSTRUCTOR_ID) {
-            return RPCResult(msgId, (await GZIPPacked.fromReader(reader)).data)
+            return new RPCResult(msgId, (await GZIPPacked.fromReader(reader)).data)
         }
         reader.seek(-4);
         // This reader.read() will read more than necessary, but it's okay.

+ 1 - 1
gramjs/tl/tlobject.js

@@ -76,7 +76,7 @@ class TLRequest extends TLObject {
      * @param reader {BinaryReader}
      * @returns {boolean}
      */
-    static read_result(reader) {
+    readResult(reader) {
         return reader.tgReadObject();
     }
 

+ 6 - 10
gramjs/utils/Helpers.js

@@ -3,7 +3,7 @@ const fs = require("fs").promises;
 
 class Helpers {
 
-    static readBigIntFromBuffer(buffer, little = true, signed = true) {
+    static readBigIntFromBuffer(buffer, little = true, signed = false) {
         let randBuffer = Buffer.from(buffer);
         let bytesNumber = randBuffer.length;
         if (little) {
@@ -17,7 +17,7 @@ class Helpers {
     }
 
 
-    static readBufferFromBigInt(bigInt, bytesNumber, little = true, signed = true) {
+    static readBufferFromBigInt(bigInt, bytesNumber, little = true, signed = false) {
         let bitLength = bigInt.toString("2").length;
 
         let bytes = Math.ceil(bitLength / 8);
@@ -63,12 +63,8 @@ class Helpers {
      * Generates a random long integer (8 bytes), which is optionally signed
      * @returns {BigInt}
      */
-    static generateRandomLong(signed) {
-        let buf = Buffer.from(Helpers.generateRandomBytes(8)); // 0x12345678 = 305419896
-        if (signed)
-            return buf.readBigInt64LE(0);
-        else
-            return buf.readBigUInt64LE(0);
+    static generateRandomLong(signed = true) {
+        return this.readBigIntFromBuffer(Helpers.generateRandomBytes(8), true, signed);
     }
 
 
@@ -182,8 +178,8 @@ class Helpers {
      * @returns {{key: Buffer, iv: Buffer}}
      */
     static generateKeyDataFromNonce(serverNonce, newNonce) {
-        serverNonce = Helpers.readBufferFromBigInt(serverNonce, 16);
-        newNonce = Helpers.readBufferFromBigInt(newNonce, 32);
+        serverNonce = Helpers.readBufferFromBigInt(serverNonce, 16, true, true);
+        newNonce = Helpers.readBufferFromBigInt(newNonce, 32, true, true);
         let hash1 = Helpers.sha1(Buffer.concat([newNonce, serverNonce]));
         let hash2 = Helpers.sha1(Buffer.concat([serverNonce, newNonce]));
         let hash3 = Helpers.sha1(Buffer.concat([newNonce, newNonce]));

+ 27 - 14
gramjs_generator/generators/tlobject.js

@@ -252,10 +252,10 @@ const writeReadResult = (tlobject, builder) => {
         return
     }
     //builder.endBlock();
-    builder.writeln('static readResult(reader){');
+    builder.writeln('readResult(reader){');
     builder.writeln('reader.readInt();  // Vector ID');
     builder.writeln('let temp = [];');
-    builder.writeln("len = reader.readInt();");
+    builder.writeln("let len = reader.readInt(); //fix this");
     builder.writeln('for (let i=0;i<len;i++){');
     let read = m[1][0].toUpperCase() + m[1].slice(1);
     builder.writeln('temp.push(reader.read%s())', read);
@@ -304,6 +304,14 @@ const writeClassConstructor = (tlobject, kind, typeConstructors, builder) => {
         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('/**');
 
     if (tlobject.isFunction) {
@@ -343,13 +351,13 @@ const writeClassConstructor = (tlobject, kind, typeConstructors, builder) => {
             builder.writeln(`this.${variableSnakeToCamelCase(arg.name)} = args.${variableSnakeToCamelCase(arg.name)};`);
         }
 
-        // Currently the only argument that can be
+            // Currently the only argument that can be
         // inferred are those called 'random_id'
         else if (arg.name === 'random_id') {
             // Endianness doesn't really matter, and 'big' is shorter
-            let code = `int.from_bytes(Helpers.generateRandomBytes(${
+            let code = `Helpers.readBigIntFromBuffer(Helpers.generateRandomBytes(${
                 arg.type === 'long' ? 8 : 4
-            }))`;
+            }),false,true)`;
 
             if (arg.isVector) {
                 // Currently for the case of "messages.forwardMessages"
@@ -560,10 +568,12 @@ const writeArgToBytes = (builder, arg, args, name = null) => {
     if (arg.genericDefinition) {
         return; // Do nothing, this only specifies a later type
     }
-
     if (name === null) {
         name = `this.${arg.name}`;
     }
+    if (name =="this.msg_ids"){
+        console.log(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.
@@ -588,14 +598,14 @@ const writeArgToBytes = (builder, arg, args, name = null) => {
 
     if (arg.isVector) {
         if (arg.useVectorId) {
-            builder.write("'15c4b51c',");
+            builder.write("Buffer.from('15c4b51c','hex'),");
         }
 
         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;
@@ -604,7 +614,7 @@ const writeArgToBytes = (builder, arg, args, name = null) => {
         arg.isVector = true;
         arg.isFlag = oldFlag;
 
-        builder.write(')]),', name);
+        builder.write('))');
     } else if (arg.flagIndicator) {
         // Calculate the flags with those items which are not None
         if (!args.some(f => f.isFlag)) {
@@ -628,13 +638,13 @@ const writeArgToBytes = (builder, arg, args, name = null) => {
     } else if (arg.type === 'int') {
         builder.write("struct.pack('<i', %s)", name);
     } else if (arg.type === 'long') {
-        builder.write("struct.pack('<q', %s)", name);
+        builder.write("Helpers.readBufferFromBigInt(%s,8,true,true)", name);
     } else if (arg.type === 'int128') {
-        builder.write("Helpers.readBufferFromBigInt(%s,16)", name);
+        builder.write("Helpers.readBufferFromBigInt(%s,16,true,true)", name);
     } else if (arg.type === 'int256') {
-        builder.write("Helpers.readBufferFromBigInt(%s,32)", name);
+        builder.write("Helpers.readBufferFromBigInt(%s,32,true,true)", name);
     } else if (arg.type === 'double') {
-        builder.write("struct.pack('<d', %s)", name);
+        builder.write("struct.pack('<d', %s.toString())", name);
     } else if (arg.type === 'string') {
         builder.write('TLObject.serializeBytes(%s)', name);
     } else if (arg.type === 'Bool') {
@@ -809,11 +819,14 @@ const writePatched = (outDir, namespaceTlobjects) => {
                 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();
 
             //writeToJson(t, builder);

+ 3 - 3
main.js

@@ -1,15 +1,15 @@
 const Helpers = require("./gramjs/utils/Helpers");
 const TelegramClient = require("./gramjs/tl/TelegramClient");
 const {GetConfigRequest} = require("./gramjs/tl/functions/help");
-
+const struct = require("python-struct");
 const log4js = require('log4js');
 const logger = log4js.getLogger("gramjs");
-logger.level = 'debug';
-
 
+logger.level = 'debug';
 
 
 (async function () {
+
     console.log("Loading interactive example...");
     let sessionName = "anon";
     let apiId = 139938;