From 806f208295c24df56aa63fda804c54a00b52c1b9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 12 Oct 2020 16:03:50 +1030 Subject: [PATCH] pyln: handle (ignore) notifications, and add notify_msg to send them. We also sanity check that response id matches our request. Signed-off-by: Rusty Russell Changelog-Added: pyln: pyln.client handles and can send progress notifications. --- contrib/pyln-client/pyln/client/lightning.py | 15 ++++++++++++--- contrib/pyln-client/pyln/client/plugin.py | 20 ++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index 7028c412e..4a7ee33f0 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -316,17 +316,26 @@ class UnixDomainSocketRpc(object): # FIXME: we open a new socket for every readobj call... sock = UnixSocket(self.socket_path) + this_id = self.next_id self._writeobj(sock, { "jsonrpc": "2.0", "method": method, "params": payload, - "id": self.next_id, + "id": this_id, }) self.next_id += 1 - resp, _ = self._readobj(sock) - sock.close() + buf = b'' + while True: + resp, buf = self._readobj(sock, buf) + # FIXME: We should offer a callback for notifications. + if 'method' not in resp or 'id' in resp: + break self.logger.debug("Received response for %s call: %r", method, resp) + if 'id' in resp and resp['id'] != this_id: + raise ValueError("Malformed response, id is not {}: {}.".format(this_id, resp)) + sock.close() + if not isinstance(resp, dict): raise ValueError("Malformed response, response is not a dictionary %s." % resp) elif "error" in resp: diff --git a/contrib/pyln-client/pyln/client/plugin.py b/contrib/pyln-client/pyln/client/plugin.py index af9ab6758..4be219f4e 100644 --- a/contrib/pyln-client/pyln/client/plugin.py +++ b/contrib/pyln-client/pyln/client/plugin.py @@ -608,6 +608,26 @@ class Plugin(object): for line in message.split('\n'): self.notify('log', {'level': level, 'message': line}) + def notify_message(self, request: Request, message: str, + level: str = 'info') -> None: + """Send a notification message to sender of this request""" + self.notify("message", {"id": request.id, + "level": level, + "message": message}) + + def notify_progress(self, request: Request, + progress: int, progress_total: int, + stage: Optional[int] = None, + stage_total: Optional[int] = None) -> None: + """Send a progerss message to sender of this request: if more than one stage, set stage and stage_total""" + d: Dict[str, Any] = {"id": request.id, + "num": progress, + "total": progress_total} + if stage_total is not None: + d['stage'] = {"num": stage, "total": stage_total} + + self.notify("progress", d) + def _parse_request(self, jsrequest: Dict[str, JSONType]) -> Request: i = jsrequest.get('id', None) if not isinstance(i, int) and i is not None: