From bc20fecc82c43305f09f74ba272cccc89b40c32d Mon Sep 17 00:00:00 2001 From: tecnovert Date: Sun, 8 Jun 2025 11:48:25 +0200 Subject: [PATCH] net: Update response format for SimpleX Chat v6.3.4 --- basicswap/basicswap.py | 19 ++++--- basicswap/network/simplex.py | 38 +++++++++++--- tests/basicswap/extended/test_simplex.py | 63 ++++++++++++++---------- 3 files changed, 79 insertions(+), 41 deletions(-) diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py index c870622..12be57a 100644 --- a/basicswap/basicswap.py +++ b/basicswap/basicswap.py @@ -151,11 +151,13 @@ from .explorers import ( ExplorerChainz, ) from .network.simplex import ( + closeSimplexChat, encryptMsg, + getJoinedSimplexLink, + getResponseData, initialiseSimplexNetwork, readSimplexMsgs, sendSimplexMsg, - closeSimplexChat, ) from .network.util import ( getMsgPubkey, @@ -1838,7 +1840,7 @@ class BasicSwap(BaseApp, UIApp): self.log.debug(f"Finding name for Simplex chat, ID: {pccConnId}") cmd_id = net_i.send_command("/chats") response = net_i.wait_for_command_response(cmd_id) - for chat in response["resp"]["chats"]: + for chat in getResponseData(response, "chats"): if ( "chatInfo" not in chat or "type" not in chat["chatInfo"] @@ -4000,8 +4002,8 @@ class BasicSwap(BaseApp, UIApp): cmd_id = net_i.send_command("/connect") response = net_i.wait_for_command_response(cmd_id) - connReqInvitation = response["resp"]["connReqInvitation"] - pccConnId = response["resp"]["connection"]["pccConnId"] + connReqInvitation = getJoinedSimplexLink(response) + pccConnId = getResponseData(response, "connection")["pccConnId"] req_data["bsx_address"] = addr_from req_data["connection_req"] = connReqInvitation @@ -10153,7 +10155,7 @@ class BasicSwap(BaseApp, UIApp): connReqInvitation = req_data["connection_req"] cmd_id = net_i.send_command(f"/connect {connReqInvitation}") response = net_i.wait_for_command_response(cmd_id) - pccConnId = response["resp"]["connection"]["pccConnId"] + pccConnId = getResponseData(response, "connection")["pccConnId"] now: int = self.getTime() message_route = DirectMessageRoute( @@ -10211,8 +10213,9 @@ class BasicSwap(BaseApp, UIApp): self.saveBidInSession(bid_id, bid, cursor) def processContactConnected(self, event_data) -> None: - connId = event_data["resp"]["contact"]["activeConn"]["connId"] - localDisplayName = event_data["resp"]["contact"]["localDisplayName"] + contact_data = getResponseData(event_data, "contact") + connId = contact_data["activeConn"]["connId"] + localDisplayName = contact_data["localDisplayName"] self.log.debug( f"Processing Contact Connected event, ID: {connId}, contact name: {localDisplayName}." ) @@ -10274,7 +10277,7 @@ class BasicSwap(BaseApp, UIApp): def processContactDisconnected(self, event_data) -> None: net_i = self.getActiveNetworkInterface(2) - connId = event_data["resp"]["contact"]["activeConn"]["connId"] + connId = getResponseData(event_data, "contact")["activeConn"]["connId"] self.log.info(f"Direct message route disconnected, connId: {connId}") closeSimplexChat(self, net_i, connId) diff --git a/basicswap/network/simplex.py b/basicswap/network/simplex.py index ca69d91..3841ca8 100644 --- a/basicswap/network/simplex.py +++ b/basicswap/network/simplex.py @@ -276,7 +276,7 @@ def sendSimplexMsg( to = "#bsx " sent_id = ws_thread.send_command(to + encode_base64(smsg_msg)) response = waitForResponse(ws_thread, sent_id, self.delay_event) - if response["resp"]["type"] != "newChatItems": + if getResponseData(response, "type") != "newChatItems": json_str = json.dumps(response, indent=4) self.log.debug(f"Response {json_str}") raise ValueError("Send failed") @@ -408,9 +408,9 @@ def readSimplexMsgs(self, network): data = json.loads(message) # self.log.debug(f"Message: {json.dumps(data, indent=4)}") try: - msg_type: str = data["resp"]["type"] + msg_type: str = getResponseData(data, "type") if msg_type in ("chatItemsStatusesUpdated", "newChatItems"): - for chat_item in data["resp"]["chatItems"]: + for chat_item in getResponseData(data, "chatItems"): decrypted_msg = parseSimplexMsg(self, chat_item) if decrypted_msg is None: continue @@ -431,6 +431,30 @@ def readSimplexMsgs(self, network): self.delay_event.wait(0.05) +def getResponseData(data, tag=None): + if "Right" in data["resp"]: + if tag: + return data["resp"]["Right"][tag] + return data["resp"]["Right"] + if tag: + return data["resp"][tag] + return data["resp"] + + +def getNewSimplexLink(data): + response_data = getResponseData(data) + if "connLinkContact" in response_data: + return response_data["connLinkContact"]["connFullLink"] + return response_data["connReqContact"] + + +def getJoinedSimplexLink(data): + response_data = getResponseData(data) + if "connLinkInvitation" in response_data: + return response_data["connLinkInvitation"]["connFullLink"] + return response_data["connReqInvitation"] + + def initialiseSimplexNetwork(self, network_config) -> None: self.log.debug("initialiseSimplexNetwork") @@ -445,10 +469,10 @@ def initialiseSimplexNetwork(self, network_config) -> None: sent_id = ws_thread.send_command("/groups") response = waitForResponse(ws_thread, sent_id, self.delay_event) - if len(response["resp"]["groups"]) < 1: + if len(getResponseData(response, "groups")) < 1: sent_id = ws_thread.send_command("/c " + network_config["group_link"]) response = waitForResponse(ws_thread, sent_id, self.delay_event) - assert "groupLinkId" in response["resp"]["connection"] + assert "groupLinkId" in getResponseData(response, "connection") network = { "type": "simplex", @@ -463,7 +487,7 @@ def closeSimplexChat(self, net_i, connId) -> bool: cmd_id = net_i.send_command("/chats") response = net_i.wait_for_command_response(cmd_id, num_tries=500) remote_name = None - for chat in response["resp"]["chats"]: + for chat in getResponseData(response, "chats"): if ( "chatInfo" not in chat or "type" not in chat["chatInfo"] @@ -487,7 +511,7 @@ def closeSimplexChat(self, net_i, connId) -> bool: cmd_id = net_i.send_command(f"/delete @{remote_name}") cmd_response = net_i.wait_for_command_response(cmd_id) - if cmd_response["resp"]["type"] != "contactDeleted": + if getResponseData(cmd_response, "type") != "contactDeleted": self.log.warning(f"Failed to delete simplex chat, ID: {connId}") self.log.debug( "cmd_response: {}".format(json.dumps(cmd_response, indent=4)) diff --git a/tests/basicswap/extended/test_simplex.py b/tests/basicswap/extended/test_simplex.py index 5f3c07a..c43bddb 100644 --- a/tests/basicswap/extended/test_simplex.py +++ b/tests/basicswap/extended/test_simplex.py @@ -42,9 +42,12 @@ from basicswap.basicswap import ( from basicswap.chainparams import Coins from basicswap.network.simplex import ( - WebSocketThread, + getJoinedSimplexLink, + getNewSimplexLink, + getResponseData, waitForConnected, waitForResponse, + WebSocketThread, ) from basicswap.network.simplex_chat import startSimplexClient from tests.basicswap.common import ( @@ -71,10 +74,13 @@ if not len(logger.handlers): def parse_message(msg_data): - if msg_data["resp"]["type"] not in ("chatItemsStatusesUpdated", "newChatItems"): + if getResponseData(msg_data, "type") not in ( + "chatItemsStatusesUpdated", + "newChatItems", + ): return None - for chat_item in msg_data["resp"]["chatItems"]: + for chat_item in getResponseData(msg_data, "chatItems"): chat_type: str = chat_item["chatInfo"]["type"] if chat_type == "group": chat_name = chat_item["chatInfo"]["groupInfo"]["localDisplayName"] @@ -155,7 +161,7 @@ class TestSimplex(unittest.TestCase): waitForConnected(ws_thread, test_delay_event) sent_id = ws_thread.send_command("/group bsx") response = waitForResponse(ws_thread, sent_id, test_delay_event) - assert response["resp"]["type"] == "groupCreated" + assert getResponseData(response, "type") == "groupCreated" ws_thread.send_command("/set voice #bsx off") ws_thread.send_command("/set files #bsx off") @@ -165,36 +171,35 @@ class TestSimplex(unittest.TestCase): ws_thread.send_command("/set disappear #bsx on week") sent_id = ws_thread.send_command("/create link #bsx") - connReqContact = None connReqMsgData = waitForResponse(ws_thread, sent_id, test_delay_event) - connReqContact = connReqMsgData["resp"]["connReqContact"] + connReqContact = getNewSimplexLink(connReqMsgData) group_link = "https://simplex.chat" + connReqContact[8:] logger.info(f"group_link: {group_link}") sent_id = ws_thread2.send_command("/c " + group_link) response = waitForResponse(ws_thread2, sent_id, test_delay_event) - assert "groupLinkId" in response["resp"]["connection"] + assert "groupLinkId" in getResponseData(response, "connection") sent_id = ws_thread2.send_command("/groups") response = waitForResponse(ws_thread2, sent_id, test_delay_event) - assert len(response["resp"]["groups"]) == 1 + assert len(getResponseData(response, "groups")) == 1 sent_id = ws_thread2.send_command("/connect") response = waitForResponse(ws_thread2, sent_id, test_delay_event) with open(os.path.join(client2_dir, "chat_inv.txt"), "w") as fp: fp.write(json.dumps(response, indent=4)) - connReqInvitation = response["resp"]["connReqInvitation"] + connReqInvitation = getJoinedSimplexLink(response) logger.info(f"direct_link: {connReqInvitation}") - pccConnId_2_sent = response["resp"]["connection"]["pccConnId"] + pccConnId_2_sent = getResponseData(response, "connection")["pccConnId"] print(f"pccConnId_2_sent: {pccConnId_2_sent}") sent_id = ws_thread.send_command(f"/connect {connReqInvitation}") response = waitForResponse(ws_thread, sent_id, test_delay_event) with open(os.path.join(client1_dir, "chat_inv_accept.txt"), "w") as fp: fp.write(json.dumps(response, indent=4)) - pccConnId_1_accepted = response["resp"]["connection"]["pccConnId"] + pccConnId_1_accepted = getResponseData(response, "connection")["pccConnId"] print(f"pccConnId_1_accepted: {pccConnId_1_accepted}") sent_id = ws_thread.send_command("/chats") @@ -203,7 +208,7 @@ class TestSimplex(unittest.TestCase): fp.write(json.dumps(response, indent=4)) direct_local_name_1 = None - for chat in response["resp"]["chats"]: + for chat in getResponseData(response, "chats"): print(f"chat: {chat}") if ( chat["chatInfo"]["contact"]["activeConn"]["connId"] @@ -221,7 +226,7 @@ class TestSimplex(unittest.TestCase): fp.write(json.dumps(response, indent=4)) direct_local_name_2 = None - for chat in response["resp"]["chats"]: + for chat in getResponseData(response, "chats"): print(f"chat: {chat}") if ( chat["chatInfo"]["contact"]["activeConn"]["connId"] @@ -238,10 +243,10 @@ class TestSimplex(unittest.TestCase): sent_id = ws_thread.send_command("#bsx test msg 1") response = waitForResponse(ws_thread, sent_id, test_delay_event) - assert response["resp"]["type"] == "newChatItems" + assert getResponseData(response, "type") == "newChatItems" sent_id = ws_thread.send_command("@user_1 test msg 2") response = waitForResponse(ws_thread, sent_id, test_delay_event) - assert response["resp"]["type"] == "newChatItems" + assert getResponseData(response, "type") == "newChatItems" msg_counter1: int = 0 msg_counter2: int = 0 @@ -258,7 +263,7 @@ class TestSimplex(unittest.TestCase): msg_counter1 += 1 data = json.loads(message) try: - msg_type = data["resp"]["type"] + msg_type = getResponseData(data, "type") except Exception as e: print(f"msg_type error: {e}") msg_type = "None" @@ -287,7 +292,7 @@ class TestSimplex(unittest.TestCase): msg_counter2 += 1 data = json.loads(message) try: - msg_type = data["resp"]["type"] + msg_type = getResponseData(data, "type") except Exception as e: print(f"msg_type error: {e}") msg_type = "None" @@ -320,18 +325,24 @@ class TestSimplex(unittest.TestCase): assert len(found_connected[0]) == 1 node1_connect = list(found_connected[0].values())[0] assert ( - node1_connect["resp"]["contact"]["activeConn"]["connId"] + getResponseData(node1_connect, "contact")["activeConn"]["connId"] == pccConnId_1_accepted ) - assert node1_connect["resp"]["contact"]["localDisplayName"] == "user_2" + assert ( + getResponseData(node1_connect, "contact")["localDisplayName"] + == "user_2" + ) assert len(found_connected[1]) == 1 node2_connect = list(found_connected[1].values())[0] assert ( - node2_connect["resp"]["contact"]["activeConn"]["connId"] + getResponseData(node2_connect, "contact")["activeConn"]["connId"] == pccConnId_2_sent ) - assert node2_connect["resp"]["contact"]["localDisplayName"] == "user_2" + assert ( + getResponseData(node2_connect, "contact")["localDisplayName"] + == "user_2" + ) node1_msg1 = [m for m in found[0].values() if m["text"] == "test msg 1"] assert len(node1_msg1) == 1 @@ -361,18 +372,18 @@ class TestSimplex(unittest.TestCase): sent_id = ws_thread.send_command("/delete @user_1") response = waitForResponse(ws_thread, sent_id, test_delay_event) - assert response["resp"]["type"] == "contactDeleted" + assert getResponseData(response, "type") == "contactDeleted" sent_id = ws_thread2.send_command("/delete @user_1") response = waitForResponse(ws_thread2, sent_id, test_delay_event) - assert response["resp"]["type"] == "contactDeleted" + assert getResponseData(response, "type") == "contactDeleted" sent_id = ws_thread2.send_command("/chats") response = waitForResponse(ws_thread2, sent_id, test_delay_event) with open(os.path.join(client2_dir, "chats_after_delete.txt"), "w") as fp: fp.write(json.dumps(response, indent=4)) - assert len(response["resp"]["chats"]) == 4 + assert len(getResponseData(response, "chats")) == 4 finally: for t in threads: @@ -417,7 +428,7 @@ class Test(BaseTest): waitForConnected(ws_thread, test_delay_event) sent_id = ws_thread.send_command("/group bsx") response = waitForResponse(ws_thread, sent_id, test_delay_event) - assert response["resp"]["type"] == "groupCreated" + assert getResponseData(response, "type") == "groupCreated" ws_thread.send_command("/set voice #bsx off") ws_thread.send_command("/set files #bsx off") @@ -429,7 +440,7 @@ class Test(BaseTest): connReqContact = None connReqMsgData = waitForResponse(ws_thread, sent_id, test_delay_event) - connReqContact = connReqMsgData["resp"]["connReqContact"] + connReqContact = getNewSimplexLink(connReqMsgData) cls.group_link = "https://simplex.chat" + connReqContact[8:] logger.info(f"BSX group_link: {cls.group_link}")