From ba4d7bdb4821c70c507af1b96061defc069391e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Fornarino?= <jeremy@fornarino.fr> Date: Mon, 10 Feb 2020 21:03:45 +0100 Subject: [PATCH 01/10] add getChatHistory command --- backend/whatsapp_web_backend.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/whatsapp_web_backend.py b/backend/whatsapp_web_backend.py index b75711d..6b5e1f2 100755 --- a/backend/whatsapp_web_backend.py +++ b/backend/whatsapp_web_backend.py @@ -91,6 +91,8 @@ def handleMessage(self): currWhatsAppInstance.getLoginInfo(callback); elif cmd == "backend-getConnectionInfo": currWhatsAppInstance.getConnectionInfo(callback); + elif cmd == "backend-getChatHistory": + currWhatsAppInstance.get_chat_history(str(obj["jid"])); elif cmd == "backend-disconnectWhatsApp": currWhatsAppInstance.disconnect(); self.sendJSON({ "type": "resource_disconnected", "resource": "whatsapp", "resource_instance_id": obj["whatsapp_instance_id"] }, tag); From b77690ac1bcbd47a14cb235d8764ea2554ffebc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Fornarino?= <jeremy@fornarino.fr> Date: Mon, 10 Feb 2020 21:04:33 +0100 Subject: [PATCH 02/10] Add get_chat_history method --- backend/whatsapp.py | 51 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/backend/whatsapp.py b/backend/whatsapp.py index 0a95299..95b9cfc 100755 --- a/backend/whatsapp.py +++ b/backend/whatsapp.py @@ -4,6 +4,7 @@ import sys; sys.dont_write_bytecode = True; +import binascii; import os; import signal; import base64; @@ -24,8 +25,12 @@ import websocket; import curve25519; import pyqrcode; + +from Crypto import Random; from utilities import *; from whatsapp_binary_reader import whatsappReadBinary; +from whatsapp_binary_writer import whatsappWriteBinary; +from whatsapp_defines import WAMetrics; reload(sys); sys.setdefaultencoding("utf-8"); @@ -136,7 +141,7 @@ def onMessage(self, ws, message): messageSplit = message.split(",", 1); messageTag = messageSplit[0]; messageContent = messageSplit[1]; - + if messageTag in self.messageQueue: # when the server responds to a client's message pend = self.messageQueue[messageTag]; if pend["desc"] == "_status": @@ -165,7 +170,7 @@ def onMessage(self, ws, message): hmacValidation = HmacSha256(self.loginInfo["key"]["macKey"], messageContent[32:]); if hmacValidation != messageContent[:32]: raise ValueError("Hmac mismatch"); - + decryptedMessage = AESDecrypt(self.loginInfo["key"]["encKey"], messageContent[32:]); try: processedData = whatsappReadBinary(decryptedMessage, True); @@ -185,7 +190,7 @@ def onMessage(self, ws, message): self.connInfo["serverToken"] = jsonObj[1]["serverToken"]; self.connInfo["browserToken"] = jsonObj[1]["browserToken"]; self.connInfo["me"] = jsonObj[1]["wid"]; - + self.connInfo["secret"] = base64.b64decode(jsonObj[1]["secret"]); self.connInfo["sharedSecret"] = self.loginInfo["privateKey"].get_shared_key(curve25519.Public(self.connInfo["secret"][:32]), lambda a: a); sse = self.connInfo["sharedSecretExpanded"] = HKDF(self.connInfo["sharedSecret"], 80); @@ -197,7 +202,7 @@ def onMessage(self, ws, message): keysDecrypted = AESDecrypt(sse[:32], keysEncrypted); self.loginInfo["key"]["encKey"] = keysDecrypted[:32]; self.loginInfo["key"]["macKey"] = keysDecrypted[32:64]; - + # eprint("private key : ", base64.b64encode(self.loginInfo["privateKey"].serialize())); # eprint("secret : ", base64.b64encode(self.connInfo["secret"])); # eprint("shared secret : ", base64.b64encode(self.connInfo["sharedSecret"])); @@ -224,7 +229,7 @@ def connect(self): on_open = lambda ws: self.onOpen(ws), on_close = lambda ws: self.onClose(ws), header = { "Origin: https://web.whatsapp.com" }); - + self.websocketThread = Thread(target = self.activeWs.run_forever); self.websocketThread.daemon = True; self.websocketThread.start(); @@ -235,7 +240,7 @@ def generateQRCode(self, callback=None): self.messageQueue[messageTag] = { "desc": "_login", "callback": callback }; message = messageTag + ',["admin","init",[0,3,2390],["Chromium at ' + datetime.datetime.now().isoformat() + '","Chromium"],"' + self.loginInfo["clientId"] + '",true]'; self.activeWs.send(message); - + def restoreSession(self, callback=None): messageTag = str(getTimestamp()) message = messageTag + ',["admin","init",[0,3,2390],["Chromium at ' + datetime.now().isoformat() + '","Chromium"],"' + self.loginInfo["clientId"] + '",true]' @@ -247,13 +252,39 @@ def restoreSession(self, callback=None): "serverToken"] + '", "' + self.loginInfo["clientId"] + '", "takeover"]' self.activeWs.send(message) - + def getLoginInfo(self, callback): callback["func"]({ "type": "login_info", "data": self.loginInfo }, callback); - + def getConnectionInfo(self, callback): callback["func"]({ "type": "connection_info", "data": self.connInfo }, callback); - + + def get_chat_history(self, jid, count=10000): + """ + + :param jid: + :param count: + :return: + """ + return self.__send_request( + ["query", {"type": "message", "jid": jid, "count": str(count)}, None], + WAMetrics.QUERY_MESSAGES + ); + + def __send_request(self, msgData, metrics): + messageId = "3EB0"+binascii.hexlify(Random.get_random_bytes(8)).upper() + + encryptedMessage = WhatsAppEncrypt( + self.loginInfo["key"]["encKey"], + self.loginInfo["key"]["macKey"], + whatsappWriteBinary(msgData) + ) + + payload = bytearray(messageId) + bytearray(",") + bytearray( + to_bytes(metrics, 1) + ) + bytearray([0x80]) + encryptedMessage + self.activeWs.send(payload, websocket.ABNF.OPCODE_BINARY) + def sendTextMessage(self, number, text): messageId = "3EB0"+binascii.hexlify(Random.get_random_bytes(8)).upper() messageTag = str(getTimestamp()) @@ -264,7 +295,7 @@ def sendTextMessage(self, number, text): self.messageSentCount = self.messageSentCount + 1 self.messageQueue[messageId] = {"desc": "__sending"} self.activeWs.send(payload, websocket.ABNF.OPCODE_BINARY) - + def status(self, callback=None): if self.activeWs is not None: messageTag = str(getTimestamp()) From ff2e8d04429ae1247d7f2c1eaf46fe6913013d91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Fornarino?= <jeremy@fornarino.fr> Date: Tue, 11 Feb 2020 00:37:28 +0100 Subject: [PATCH 03/10] backend-getChatHistory listener WIP --- index.js | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index f6bbcba..9ed2bdf 100644 --- a/index.js +++ b/index.js @@ -131,6 +131,34 @@ wss.on("connection", function(clientWebsocketRaw, req) { clientCallRequest.respond({ type: "error", reason: reason }); }) }).run(); + clientWebsocket.waitForMessage({ + condition: obj => { + return obj.from == "client" && obj.type == "call" && obj.command == "backend-getChatHistory" + && "data" in obj && typeof(obj.data) === "object" && "jid" in obj.data + }, + keepWhenHit: true + }).then( + clientCallRequest => { + if(!backendWebsocket.isOpen) { + clientCallRequest.respond({ type: "error", reason: "No backend connected." }); + return; + } + new BootstrapStep({ + websocket: backendWebsocket, + request: { + type: "call", + callArgs: { + command: "backend-getChatHistory", + jid: clientCallRequest.data.jid, + whatsapp_instance_id: backendWebsocket.activeWhatsAppInstanceId, + }, + // TODO Add success Condition + successCondition: obj => true + } + }).run() + //TODO + // THEN & CATCH + }).run(); //TODO: @@ -139,6 +167,7 @@ wss.on("connection", function(clientWebsocketRaw, req) { // - add buttons for that to client // - look for handlers in "decoder.py" and add them to output information // - when decoding fails, write packet to file for further investigation later + // - List contacts and add buttons for each to get messages @@ -169,7 +198,7 @@ wss.on("connection", function(clientWebsocketRaw, req) { switch(obj.command) { case "api-connectBackend": {*/ - + //backendWebsocket = new WebSocketClient("ws://localhost:2020", true); //backendWebsocket.onClose From 0595df1f2670fb967d16655d95c1de2a925d3106 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Fornarino?= <jeremy@fornarino.fr> Date: Tue, 11 Feb 2020 17:13:28 +0100 Subject: [PATCH 04/10] Fix condition for backend-getChatHistory --- index.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/index.js b/index.js index 9ed2bdf..cb9ce86 100644 --- a/index.js +++ b/index.js @@ -132,10 +132,7 @@ wss.on("connection", function(clientWebsocketRaw, req) { }) }).run(); clientWebsocket.waitForMessage({ - condition: obj => { - return obj.from == "client" && obj.type == "call" && obj.command == "backend-getChatHistory" - && "data" in obj && typeof(obj.data) === "object" && "jid" in obj.data - }, + condition: obj => obj.from === "client" && obj.type === "call" && obj.command === "backend-getChatHistory" && "jid" in obj, keepWhenHit: true }).then( clientCallRequest => { From 65071adc11d062e0ee0e7ba360e4f15866eb78bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Fornarino?= <jeremy@fornarino.fr> Date: Tue, 11 Feb 2020 17:14:19 +0100 Subject: [PATCH 05/10] Replace console "hello" by Download form --- client/index.html | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/client/index.html b/client/index.html index 81bc7bf..9959d5e 100644 --- a/client/index.html +++ b/client/index.html @@ -3,9 +3,9 @@ <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> - + <title>WhatsApp Web</title> - + <!-- stylesheets --> <link rel="stylesheet" href="lib/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="lib/font-awesome/css/font-awesome.min.css"> @@ -62,7 +62,16 @@ <h1>WhatsApp Web</h1> <button id="console-arrow-button" class="btn"><i class="fa fa-angle-left" aria-hidden="true"></i></button> </div> <div id="console"> - Hello + <span>Download chat history (.json)</span> + <form class="form-inline" id="formDlChat"> + <div class="form-group"> + <label for="chatDlRemoteJID">Chat JID</label> + <input type="text" class="form-control" id="chatDlRemoteJID" placeholder="Enter chat JID"> + </div> + <button type="submit" class="btn btn-default" value="download"> + <i class="fa fa-download" aria-hidden="true"></i> + </button> + </form> </div> </body> </html> From c643defda565e92638e582a74808be0fd457bbd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Fornarino?= <jeremy@fornarino.fr> Date: Tue, 11 Feb 2020 20:34:07 +0100 Subject: [PATCH 06/10] Create _chathistory request. ==> Externalize message decryption in function. Externalize callback check. --- backend/whatsapp.py | 62 +++++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/backend/whatsapp.py b/backend/whatsapp.py index 95b9cfc..ae9e97a 100755 --- a/backend/whatsapp.py +++ b/backend/whatsapp.py @@ -36,6 +36,10 @@ sys.setdefaultencoding("utf-8"); +def is_callback_valid_in(pend): + return "callback" in pend and pend["callback"] is not None and\ + "func" in pend["callback"] and pend["callback"]["func"] is not None and\ + "tag" in pend["callback"] and pend["callback"]["tag"] is not None def HmacSha256(key, sign): return hmac.new(key, sign, hashlib.sha256).digest(); @@ -137,6 +141,7 @@ def onClose(self, ws): eprint("WhatsApp backend Websocket closed."); def onMessage(self, ws, message): + try: messageSplit = message.split(",", 1); messageTag = messageSplit[0]; @@ -160,26 +165,24 @@ def onMessage(self, ws, message): svgBuffer = io.BytesIO(); # from https://github.com/mnooner256/pyqrcode/issues/39#issuecomment-207621532 pyqrcode.create(qrCodeContents, error='L').svg(svgBuffer, scale=6, background="rgba(0,0,0,0.0)", module_color="#122E31", quiet_zone=0); - if "callback" in pend and pend["callback"] is not None and "func" in pend["callback"] and pend["callback"]["func"] is not None and "tag" in pend["callback"] and pend["callback"]["tag"] is not None: - pend["callback"]["func"]({ "type": "generated_qr_code", "image": "data:image/svg+xml;base64," + base64.b64encode(svgBuffer.getvalue()), "content": qrCodeContents }, pend["callback"]); + if is_callback_valid_in(pend): + pend["callback"]["func"]({"type": "generated_qr_code", "image": "data:image/svg+xml;base64," + base64.b64encode(svgBuffer.getvalue()), "content": qrCodeContents }, pend["callback"]); + elif pend["desc"] == "_chatHistory": + if messageContent != "": + messageDecrypted = self.decrypt_message_content(messageContent); + if is_callback_valid_in(pend): + pend["callback"]["func"]({ + "type": "chat_history", + "jid": pend["callback"]["args"]["jid"], + "content": messageDecrypted + }, pend["callback"]); else: try: jsonObj = json.loads(messageContent); # try reading as json - except ValueError, e: + except ValueError: if messageContent != "": - hmacValidation = HmacSha256(self.loginInfo["key"]["macKey"], messageContent[32:]); - if hmacValidation != messageContent[:32]: - raise ValueError("Hmac mismatch"); - - decryptedMessage = AESDecrypt(self.loginInfo["key"]["encKey"], messageContent[32:]); - try: - processedData = whatsappReadBinary(decryptedMessage, True); - messageType = "binary"; - except: - processedData = { "traceback": traceback.format_exc().splitlines() }; - messageType = "error"; - finally: - self.onMessageCallback["func"](processedData, self.onMessageCallback, { "message_type": messageType }); + processedData, messageType = self.decrypt_message_content(messageContent) + self.onMessageCallback["func"](processedData, self.onMessageCallback, {"message_type": messageType}); else: self.onMessageCallback["func"](jsonObj, self.onMessageCallback, { "message_type": "json" }); if isinstance(jsonObj, list) and len(jsonObj) > 0: # check if the result is an array @@ -220,7 +223,19 @@ def onMessage(self, ws, message): except: eprint(traceback.format_exc()); - + def decrypt_message_content(self, messageContent): + hmacValidation = HmacSha256(self.loginInfo["key"]["macKey"], messageContent[32:]); + if hmacValidation != messageContent[:32]: + raise ValueError("Hmac mismatch"); + decryptedMessage = AESDecrypt(self.loginInfo["key"]["encKey"], messageContent[32:]); + try: + processedData = whatsappReadBinary(decryptedMessage, True); + messageType = "binary"; + except: + processedData = {"traceback": traceback.format_exc().splitlines()}; + messageType = "error"; + finally: + return processedData, messageType def connect(self): self.activeWs = websocket.WebSocketApp("wss://web.whatsapp.com/ws", @@ -259,28 +274,31 @@ def getLoginInfo(self, callback): def getConnectionInfo(self, callback): callback["func"]({ "type": "connection_info", "data": self.connInfo }, callback); - def get_chat_history(self, jid, count=10000): + def get_chat_history(self, callback, jid, count=10000): """ + :param callback: :param jid: :param count: :return: """ - return self.__send_request( + + messageTag = "3EB0"+binascii.hexlify(Random.get_random_bytes(8)).upper() + self.messageQueue[messageTag] = { "desc": "_chatHistory", "callback": callback}; + messageTag = self.__send_request(messageTag, ["query", {"type": "message", "jid": jid, "count": str(count)}, None], WAMetrics.QUERY_MESSAGES ); - def __send_request(self, msgData, metrics): - messageId = "3EB0"+binascii.hexlify(Random.get_random_bytes(8)).upper() + def __send_request(self, messageTag, msgData, metrics): encryptedMessage = WhatsAppEncrypt( self.loginInfo["key"]["encKey"], self.loginInfo["key"]["macKey"], whatsappWriteBinary(msgData) ) - payload = bytearray(messageId) + bytearray(",") + bytearray( + payload = bytearray(messageTag) + bytearray(",") + bytearray( to_bytes(metrics, 1) ) + bytearray([0x80]) + encryptedMessage self.activeWs.send(payload, websocket.ABNF.OPCODE_BINARY) From 9be24ad64146815f8b9f12406ff11ab808d01212 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Fornarino?= <jeremy@fornarino.fr> Date: Tue, 11 Feb 2020 20:34:46 +0100 Subject: [PATCH 07/10] add backend-getChatHistory request listener --- backend/whatsapp_web_backend.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/backend/whatsapp_web_backend.py b/backend/whatsapp_web_backend.py index 6b5e1f2..4fcc590 100755 --- a/backend/whatsapp_web_backend.py +++ b/backend/whatsapp_web_backend.py @@ -3,6 +3,8 @@ from __future__ import print_function; import sys; + + sys.dont_write_bytecode = True; import os; @@ -75,10 +77,14 @@ def handleMessage(self): self.clientInstances[clientInstanceId] = WhatsAppWebClient(onOpenCallback, onMessageCallback, onCloseCallback); else: currWhatsAppInstance = self.clientInstances[obj["whatsapp_instance_id"]]; + + def callback_function(obj_, cbSelf_): + self.sendJSON(mergeDicts(obj_, getAttr(cbSelf_, "args")), getAttr(cbSelf_, "tag")) + callback = { - "func": lambda obj, cbSelf: self.sendJSON(mergeDicts(obj, getAttr(cbSelf, "args")), getAttr(cbSelf, "tag")), + "func": callback_function, "tag": tag, - "args": { "resource_instance_id": obj["whatsapp_instance_id"] } + "args": {"resource_instance_id": obj["whatsapp_instance_id"]} }; if currWhatsAppInstance.activeWs is None: self.sendError("No WhatsApp server connected to backend."); @@ -92,7 +98,11 @@ def handleMessage(self): elif cmd == "backend-getConnectionInfo": currWhatsAppInstance.getConnectionInfo(callback); elif cmd == "backend-getChatHistory": - currWhatsAppInstance.get_chat_history(str(obj["jid"])); + currWhatsAppInstance.get_chat_history({ + "func": callback_function, + "tag": tag, + "args": {"resource_instance_id": obj["whatsapp_instance_id"], "jid": obj["jid"]} + }, str(obj["jid"])); elif cmd == "backend-disconnectWhatsApp": currWhatsAppInstance.disconnect(); self.sendJSON({ "type": "resource_disconnected", "resource": "whatsapp", "resource_instance_id": obj["whatsapp_instance_id"] }, tag); From 02f02d2b0b1e83306a80082ecc770a1bf83c6630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Fornarino?= <jeremy@fornarino.fr> Date: Tue, 11 Feb 2020 20:35:12 +0100 Subject: [PATCH 08/10] Hide download chat history form by default --- client/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/index.html b/client/index.html index 9959d5e..c895c34 100644 --- a/client/index.html +++ b/client/index.html @@ -62,8 +62,8 @@ <h1>WhatsApp Web</h1> <button id="console-arrow-button" class="btn"><i class="fa fa-angle-left" aria-hidden="true"></i></button> </div> <div id="console"> - <span>Download chat history (.json)</span> - <form class="form-inline" id="formDlChat"> + <form class="form-inline hidden" id="formDlChat"> + <span>Download chat history (.json)</span> <div class="form-group"> <label for="chatDlRemoteJID">Chat JID</label> <input type="text" class="form-control" id="chatDlRemoteJID" placeholder="Enter chat JID"> From 69dba6ce6f135bb25789beafe563634d42915144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Fornarino?= <jeremy@fornarino.fr> Date: Tue, 11 Feb 2020 20:36:00 +0100 Subject: [PATCH 09/10] Form to download history chat added --- client/js/main.js | 56 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/client/js/main.js b/client/js/main.js index cf2eb45..879bed5 100644 --- a/client/js/main.js +++ b/client/js/main.js @@ -6,8 +6,44 @@ function sleep(ms) { setTimeout(() => resolve(), ms); }); } +function request_chat_history(jid) { + new BootstrapStep({ + websocket: apiWebsocket, + request: { + type: "call", + callArgs: { command: "backend-getChatHistory", jid: jid }, + successCondition: obj => "jid" in obj && "type" in obj && + obj.jid === jid && obj.type === "chat_history" && "messages" in obj && Array.isArray(obj.messages), + successActor: (websocket, {messages, jid})=> { + download("messages-" + jid + ".json", JSON.stringify(messages)); + } + } + }).run().catch(() => currentRequestJID = null); +} +/** + * https://stackoverflow.com/questions/3665115/how-to-create-a-file-in-memory-for-user-to-download-but-not-through-server + * @param filename + * @param text + */ +function download(filename, text) { + var element = document.createElement('a'); + element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); + element.setAttribute('download', filename); + + element.style.display = 'none'; + document.body.appendChild(element); + + element.click(); + + document.body.removeChild(element); +} $(document).ready(function() { + $("#formDlChat").submit((event) => { + event.preventDefault(); + request_chat_history($("#chatDlRemoteJID").val()); + }); + $("#console-arrow-button").click(() => { if(consoleShown) { $("#console-arrow").removeClass("extended").find("i.fa").removeClass("fa-angle-right").addClass("fa-angle-left"); @@ -19,10 +55,10 @@ $(document).ready(function() { } consoleShown = !consoleShown; }); - + const responseTimeout = 10000; let bootstrapState = 0; - + let apiInfo = { @@ -40,12 +76,16 @@ $(document).ready(function() { let allWhatsAppMessages = []; let bootstrapInfo = { + deactivated: false, activateButton: (text, buttonEnabled) => { let container = $("#bootstrap-container").removeClass("hidden").children("#bootstrap-container-content"); container.children("img").detach(); container.children("button").removeClass("hidden").html(text).attr("disabled", !buttonEnabled); $("#main-container").addClass("hidden"); + $("#formDlChat").addClass("hidden"); + this.deactivated = false; + allWhatsAppMessages = []; $("#messages-list-table-body").empty(); }, @@ -56,9 +96,14 @@ $(document).ready(function() { $("#main-container").addClass("hidden"); }, deactivate: () => { - $("#bootstrap-container").addClass("hidden"); + if (this.deactivated) return; + this.deactivated = true; + $("#bootstrap-container").addClass("hidden") + + $("#formDlChat").removeClass("hidden"); $("#main-container").removeClass("hidden"); $("#button-disconnect").html("Disconnect").attr("disabled", false); + }, steps: [ new BootstrapStep({ @@ -152,6 +197,7 @@ $(document).ready(function() { keepWhenHit: true }).then(whatsAppMessage => { bootstrapInfo.deactivate(); + /*<tr> <th scope="row">1</th> <td>Do., 21.12.2017, 22:59:09.123</td> @@ -191,7 +237,7 @@ $(document).ready(function() { tree = jsonTree.create(jsonData, dialog.find(".bootbox-body").empty()[0]); }); }); - + let tableRow = $("<tr></tr>").attr("data-message-index", allWhatsAppMessages.length); tableRow.append($("<th></th>").attr("scope", "row").html(allWhatsAppMessages.length+1)); tableRow.append($("<td></td>").html(moment.unix(d.timestamp/1000.0).format("ddd, DD.MM.YYYY, HH:mm:ss.SSS"))); @@ -229,7 +275,7 @@ $(document).ready(function() { $("#button-disconnect").click(function() { if(!apiWebsocket.backendConnectedToWhatsApp) return; - + $(this).attr("disabled", true).html("Disconnecting..."); new BootstrapStep({ websocket: apiWebsocket, From e04ebcb2b72e38605579f955a2cea5278fdc046d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Fornarino?= <jeremy@fornarino.fr> Date: Tue, 11 Feb 2020 20:36:38 +0100 Subject: [PATCH 10/10] Listen and respond for chat history requests --- index.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index cb9ce86..1141fdd 100644 --- a/index.js +++ b/index.js @@ -140,21 +140,27 @@ wss.on("connection", function(clientWebsocketRaw, req) { clientCallRequest.respond({ type: "error", reason: "No backend connected." }); return; } + let jid = clientCallRequest.data.jid; new BootstrapStep({ websocket: backendWebsocket, request: { type: "call", callArgs: { + jid, command: "backend-getChatHistory", - jid: clientCallRequest.data.jid, whatsapp_instance_id: backendWebsocket.activeWhatsAppInstanceId, }, - // TODO Add success Condition - successCondition: obj => true + successCondition: obj => "from" in obj && "jid" in obj && "type" in obj && + obj.from === "backend" && obj.jid === jid && obj.type === "chat_history", } - }).run() - //TODO - // THEN & CATCH + }).run().then( + backendResponse => clientCallRequest.respond( + { + type: "chat_history", + jid: backendResponse.data.jid, + messages: backendResponse.data.content[0][2] + } + )); }).run();