diff --git a/matrix/buffer.py b/matrix/buffer.py index a796c44..b6a5e52 100644 --- a/matrix/buffer.py +++ b/matrix/buffer.py @@ -20,6 +20,7 @@ from __future__ import unicode_literals import time from builtins import super from functools import partial +from collections import deque from typing import Dict, List, NamedTuple, Optional, Set from uuid import UUID @@ -816,6 +817,7 @@ class RoomBuffer(object): self.sent_messages_queue = dict() # type: Dict[UUID, OwnMessage] self.printed_before_ack_queue = list() # type: List[UUID] + self.undecrypted_events = deque(maxlen=5000) buffer_name = "{}.{}".format(server_name, room.room_id) @@ -1300,6 +1302,8 @@ class RoomBuffer(object): self.get_event_tags(event) + [session_id_tag] ) + self.undecrypted_events.append(event) + else: W.prnt( "", "Unhandled event of type {}.".format(type(event).__name__) @@ -1384,6 +1388,43 @@ class RoomBuffer(object): new_tags.append(SCRIPT_NAME + "_id_" + new_message.event_id) line.tags = new_tags + def replace_undecrypted_line(self, event): + """Find a undecrypted message in the buffer and replace it with the now + decrypted event.""" + # TODO different messages need different formatting + # To implement this, refactor out the different formatting code + # snippets to a Formatter class and reuse them here. + if not isinstance(event, RoomMessageText): + return + + def predicate(event_id, line): + event_tag = SCRIPT_NAME + "_id_{}".format(event_id) + if event_tag in line.tags: + return True + return False + + lines = self.weechat_buffer.find_lines( + partial(predicate, event.event_id) + ) + + if not lines: + return + + formatted = None + if event.formatted_body: + formatted = Formatted.from_html(event.formatted_body) + + data = formatted.to_weechat() if formatted else event.body + # TODO this isn't right if the data has multiple lines, that is + # everything is printed on a signle line and newlines are shown as a + # space. + # Weechat should support deleting lines and printing new ones at an + # arbitrary position. + # To implement this without weechat support either only handle single + # line messages or edit the first line in place, print new ones at the + # bottom and sort the buffer lines. + lines[0].message = data + def old_redacted(self, event): tags = [ SCRIPT_NAME + "_message", diff --git a/matrix/server.py b/matrix/server.py index d4e2d22..2cdd3ee 100644 --- a/matrix/server.py +++ b/matrix/server.py @@ -957,6 +957,28 @@ class MatrixServer(object): "matrix_load_users_cb", self.name) self.lazy_load_hook = hook + def decrypt_printed_messages(self, key_event): + """Decrypt already printed messages and send them to the buffer""" + try: + room_buffer = self.find_room_from_id(key_event.room_id) + except KeyError: + return + + decrypted_events = [] + + for undecrypted_event in room_buffer.undecrypted_events: + if undecrypted_event.session_id != key_event.session_id: + continue + + event = self.client.decrypt_event(undecrypted_event) + if event: + decrypted_events.append((undecrypted_event, event)) + + for event_pair in decrypted_events: + undecrypted_event, event = event_pair + room_buffer.undecrypted_events.remove(undecrypted_event) + room_buffer.replace_undecrypted_line(event) + def _handle_sync(self, response): # we got the same batch again, nothing to do if self.next_batch == response.next_batch: @@ -979,6 +1001,10 @@ class MatrixServer(object): } W.hook_hsignal_send("matrix_room_key_received", message) + # TODO try to decrypt some cached undecrypted messages with the + # new key + # self.decrypt_printed_messages(event) + # Full sync response handle everything. if isinstance(response, SyncResponse): if self.client.should_upload_keys: