From 235cd02ee4eac6008cf4eeb4fd2000cbfaaf93a3 Mon Sep 17 00:00:00 2001 From: Nico Alt <nicoalt@posteo.org> Date: Sat, 1 Jun 2019 13:35:46 +0200 Subject: [PATCH] Watch for new messages using websockets Inspired by https://code.briarproject.org/grote/briar-cli-python-demo/blob/8ced7b9f37e6ee59dac6042c9bcf21c54c648e9e/briar-cli.py. --- app.briar.gtk.json | 11 +++++-- src/briar/api/models/private_chat.py | 6 ++++ src/briar/api/models/socket_listener.py | 41 +++++++++++++++++++++++++ src/briar/gtk/containers/chat.py | 17 +++++++--- 4 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 src/briar/api/models/socket_listener.py diff --git a/app.briar.gtk.json b/app.briar.gtk.json index 93954c4..e7c2cf6 100644 --- a/app.briar.gtk.json +++ b/app.briar.gtk.json @@ -45,10 +45,10 @@ ] }, { - "name": "python3-requests2", + "name": "python-dependencies", "buildsystem": "simple", "build-commands": [ - "pip3 install --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} requests2" + "pip3 install --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} requests2 websockets" ], "sources": [ { @@ -75,7 +75,12 @@ "type": "file", "url": "https://files.pythonhosted.org/packages/09/0f/9dacaa075f8e46d463248c28dbb9770445d2e04cca5c4aa23d431c9ba861/requests2-2.16.0.tar.gz", "sha256": "f7685b2d620a172053cced288dddf12d4b3441a382cc382821c0f36c02488516" - } + }, + { + "type": "file", + "url": "https://files.pythonhosted.org/packages/ba/60/59844a5cef2428cb752bd4f446b72095b1edee404a58c27e87cd12a141e2/websockets-7.0.tar.gz", + "sha256": "08e3c3e0535befa4f0c4443824496c03ecc25062debbcf895874f8a0b4c97c9f" + } ] }, { diff --git a/src/briar/api/models/private_chat.py b/src/briar/api/models/private_chat.py index 07bc9dd..4332c49 100644 --- a/src/briar/api/models/private_chat.py +++ b/src/briar/api/models/private_chat.py @@ -3,6 +3,7 @@ # License-Filename: LICENSE.md from briar.api.models.model import Model +from briar.api.models.socket_listener import SocketListener from requests import get as _get from urllib.parse import urljoin @@ -15,3 +16,8 @@ class PrivateChat(Model): url = urljoin(self._constants.get_base_url(), 'messages/' + contact_id) r = _get(url, headers=headers) return r.json() + + def watch_messages(self, contact_id, callback): + socket_listener = SocketListener(self._api) + socket_listener.watch(callback, "ConversationMessageReceivedEvent", + contact_id=contact_id) diff --git a/src/briar/api/models/socket_listener.py b/src/briar/api/models/socket_listener.py new file mode 100644 index 0000000..93646f4 --- /dev/null +++ b/src/briar/api/models/socket_listener.py @@ -0,0 +1,41 @@ +# Copyright (c) 2019 Nico Alt +# SPDX-License-Identifier: AGPL-3.0-only +# License-Filename: LICENSE.md + +from briar.api.models.model import Model + +import asyncio +import json +from threading import Thread +import websockets + + +class SocketListener(Model): + + def watch(self, callback, event, contact_id="0"): + websocket_thread = Thread(target=self._start_watch_loop, + args=(callback, event, contact_id), + daemon=True) + websocket_thread.start() + + def _start_watch_loop(self, callback, event, contact_id="0"): + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + loop.create_task(self._start_websocket(callback, event, contact_id)) + loop.run_forever() + loop.close() + + async def _start_websocket(self, callback, event, contact_id="0"): + async with websockets.connect('ws://localhost:7000/v1/ws') as websocket: + await websocket.send(self._api.auth_token) + await self._watch_messages(websocket, callback) + + async def _watch_messages(self, websocket, callback): + while not websocket.closed and not asyncio.get_event_loop().is_closed(): + message = await websocket.recv() + m = json.loads(message) + if m['name'] == 'ConversationMessageReceivedEvent': + callback(m['data']) + if not asyncio.get_event_loop().is_closed(): + asyncio.get_event_loop().create_task( + self._watch_messages(websocket, callback)) diff --git a/src/briar/gtk/containers/chat.py b/src/briar/gtk/containers/chat.py index 21d8d12..803bd82 100644 --- a/src/briar/gtk/containers/chat.py +++ b/src/briar/gtk/containers/chat.py @@ -8,7 +8,7 @@ from briar.gtk.define import App import gi gi.require_version('Gtk', '3.0') -from gi.repository import Gtk +from gi.repository import GLib, Gtk class ChatContainer(Container): @@ -27,8 +27,15 @@ class ChatContainer(Container): def _load_content(self, contact_id): private_chat = PrivateChat(self._api) messages_list = private_chat.get(contact_id) - messages_list_box = self.builder.get_object("contacts_list") + self._messages_list_box = self.builder.get_object("contacts_list") for message in messages_list: - message_label = Gtk.Label(message["text"]) - message_label.show() - messages_list_box.add(message_label) + self._add_message(message) + private_chat.watch_messages(contact_id, self._add_message_async) + + def _add_message(self, message): + message_label = Gtk.Label(message["text"]) + message_label.show() + self._messages_list_box.add(message_label) + + def _add_message_async(self, message): + GLib.idle_add(self._add_message, message) -- GitLab