diff --git a/matrix/buffer.py b/matrix/buffer.py index db4b36a..3944d8b 100644 --- a/matrix/buffer.py +++ b/matrix/buffer.py @@ -57,6 +57,7 @@ from .colors import Formatted from .config import RedactType from .globals import SCRIPT_NAME, SERVERS, W, TYPING_NOTICE_TIMEOUT from .utf import utf8_decode +from .message_renderer import Render from .utils import ( server_ts_to_weechat, shorten_sender, @@ -1163,24 +1164,7 @@ class RoomBuffer(object): return censor = self.find_nick(event.sender) - - reason = ( - "" - if not event.reason - else ', reason: "{reason}"'.format(reason=event.reason) - ) - - redaction_msg = ( - "{del_color}<{log_color}Message redacted by: " - "{censor}{log_color}{reason}{del_color}>" - "{ncolor}" - ).format( - del_color=W.color("chat_delimiters"), - ncolor=W.color("reset"), - log_color=W.color("logger.color.backlog_line"), - censor=censor, - reason=reason, - ) + redaction_msg = Render.redacted(censor, event.reason) line = lines[0] message = line.message @@ -1209,33 +1193,6 @@ class RoomBuffer(object): line.message = new_message line.tags = tags - def _handle_redacted_message(self, event): - nick = self.find_nick(event.sender) - date = server_ts_to_weechat(event.server_timestamp) - tags = self.get_event_tags(event) - tags.append(SCRIPT_NAME + "_redacted") - - reason = ( - ', reason: "{reason}"'.format(reason=event.reason) - if event.reason - else "" - ) - - censor = self.find_nick(event.redacter) - - data = ( - "{del_color}<{log_color}Message redacted by: " - "{censor}{log_color}{reason}{del_color}>{ncolor}" - ).format( - del_color=W.color("chat_delimiters"), - ncolor=W.color("reset"), - log_color=W.color("logger.color.backlog_line"), - censor=censor, - reason=reason, - ) - - self.weechat_buffer.message(nick, data, date, tags) - def _handle_topic(self, event, is_state): nick = self.find_nick(event.sender) @@ -1359,12 +1316,8 @@ class RoomBuffer(object): elif isinstance(event, RoomMessageText): nick = self.find_nick(event.sender) - formatted = None - if event.formatted_body: - formatted = Formatted.from_html(event.formatted_body) - - data = formatted.to_weechat() if formatted else event.body + data = Render.message(event.body, event.formatted_body) extra_prefix = (self.warning_prefix if event.decrypted and not event.verified else "") @@ -1388,11 +1341,7 @@ class RoomBuffer(object): elif isinstance(event, RoomMessageMedia): nick = self.find_nick(event.sender) date = server_ts_to_weechat(event.server_timestamp) - http_url = Api.mxc_to_http(event.url, self.homeserver.geturl()) - url = http_url if http_url else event.url - - description = "/{}".format(event.body) if event.body else "" - data = "{url}{desc}".format(url=url, desc=description) + data = Render.media(event.url, event.body, self.homeserver.geturl()) extra_prefix = (self.warning_prefix if event.decrypted and not event.verified else "") @@ -1404,21 +1353,14 @@ class RoomBuffer(object): elif isinstance(event, RoomEncryptedMedia): nick = self.find_nick(event.sender) date = server_ts_to_weechat(event.server_timestamp) - http_url = Api.encrypted_mxc_to_plumb( + data = Render.encrypted_media( event.url, + event.body, event.key["k"], event.hashes["sha256"], event.iv, self.homeserver.geturl() ) - url = http_url if http_url else event.url - - description = "{}".format(event.body) if event.body else "file" - data = ("{del_color}<{ncolor}{desc}{del_color}>{ncolor} " - "{del_color}[{ncolor}{url}{del_color}]{ncolor}").format( - del_color=W.color("chat_delimiters"), - ncolor=W.color("reset"), - desc=description, url=url) extra_prefix = (self.warning_prefix if event.decrypted and not event.verified else "") @@ -1430,7 +1372,10 @@ class RoomBuffer(object): elif isinstance(event, RoomMessageUnknown): nick = self.find_nick(event.sender) date = server_ts_to_weechat(event.server_timestamp) - data = ("Unknown message of type {t}").format(t=event.type) + data = Render.unknown( + event.type, + event.event_dict.get("content", None) + ) extra_prefix = (self.warning_prefix if event.decrypted and not event.verified else "") @@ -1442,7 +1387,15 @@ class RoomBuffer(object): self._redact_line(event) elif isinstance(event, RedactedEvent): - self._handle_redacted_message(event) + nick = self.find_nick(event.sender) + date = server_ts_to_weechat(event.server_timestamp) + tags = self.get_event_tags(event) + tags.append(SCRIPT_NAME + "_redacted") + + censor = self.find_nick(event.redacter) + data = Render.redacted(censor, event.reason) + + self.weechat_buffer.message(nick, data, date, tags) elif isinstance(event, RoomEncryptionEvent): message = ( @@ -1459,12 +1412,8 @@ class RoomBuffer(object): nick = self.find_nick(event.sender) date = server_ts_to_weechat(event.server_timestamp) - data = ("{del_color}<{log_color}Unable to decrypt: " - "The sender's device has not sent us " - "the keys for this message{del_color}>{ncolor}").format( - del_color=W.color("chat_delimiters"), - log_color=W.color("logger.color.backlog_line"), - ncolor=W.color("reset")) + data = Render.megolm() + session_id_tag = SCRIPT_NAME + "_sessionid_" + event.session_id self.weechat_buffer.message( nick, @@ -1625,24 +1574,9 @@ class RoomBuffer(object): "no_log", "no_highlight", ] - reason = ( - ', reason: "{reason}"'.format(reason=event.reason) - if event.reason - else "" - ) censor = self.find_nick(event.redacter) - - data = ( - "{del_color}<{log_color}Message redacted by: " - "{censor}{log_color}{reason}{del_color}>{ncolor}" - ).format( - del_color=W.color("chat_delimiters"), - ncolor=W.color("reset"), - log_color=W.color("logger.color.backlog_line"), - censor=censor, - reason=reason, - ) + data = Render.redacted(censor, event.reason) tags += self.get_event_tags(event) nick = self.find_nick(event.sender) diff --git a/matrix/message_renderer.py b/matrix/message_renderer.py new file mode 100644 index 0000000..e4f547f --- /dev/null +++ b/matrix/message_renderer.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- + +# Copyright © 2018, 2019 Damir Jelić +# +# Permission to use, copy, modify, and/or distribute this software for +# any purpose with or without fee is hereby granted, provided that the +# above copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER +# RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF +# CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +"""Module for rendering matrix messages in Weechat.""" + +from nio import Api +from .globals import W +from .colors import Formatted + + +class Render(object): + """Class collecting methods for rendering matrix messages in Weechat.""" + + @staticmethod + def _media(url, description): + return ("{del_color}<{ncolor}{desc}{del_color}>{ncolor} " + "{del_color}[{ncolor}{url}{del_color}]{ncolor}").format( + del_color=W.color("chat_delimiters"), + ncolor=W.color("reset"), + desc=description, url=url) + + @staticmethod + def media(mxc, body, homeserver=None): + """Render a mxc media URI.""" + url = Api.mxc_to_http(mxc, homeserver) + description = "{}".format(body) if body else "file" + return Render._media(url, description) + + @staticmethod + def encrypted_media(mxc, body, key, hash, iv, homeserver=None): + """Render a mxc media URI of an encrypted file.""" + http_url = Api.encrypted_mxc_to_plumb( + mxc, + key, + hash, + iv, + homeserver + ) + url = http_url if http_url else mxc + description = "{}".format(body) if body else "file" + return Render._media(url, description) + + @staticmethod + def message(body, formatted_body): + """Render a room message.""" + if formatted_body: + formatted = Formatted.from_html(formatted_body) + return formatted.to_weechat() + + return body + + @staticmethod + def redacted(censor, reason=None): + """Render a redacted event message.""" + reason = ( + ', reason: "{reason}"'.format(reason=reason) + if reason + else "" + ) + + data = ( + "{del_color}<{log_color}Message redacted by: " + "{censor}{log_color}{reason}{del_color}>{ncolor}" + ).format( + del_color=W.color("chat_delimiters"), + ncolor=W.color("reset"), + log_color=W.color("logger.color.backlog_line"), + censor=censor, + reason=reason, + ) + + return data + + @staticmethod + def room_encryption(nick): + """Render a room encryption event.""" + return "{nick} has enabled encryption in this room".format(nick=nick) + + @staticmethod + def unknown(message_type, content=None): + """Render a message of an unknown type.""" + content = ( + ': "{content}"'.format(content=content) + if content + else "" + ) + return "Unknown message of type {t}{c}".format( + t=message_type, + c=content + ) + + @staticmethod + def megolm(): + """Render an undecrypted megolm event.""" + return ("{del_color}<{log_color}Unable to decrypt: " + "The sender's device has not sent us " + "the keys for this message{del_color}>{ncolor}").format( + del_color=W.color("chat_delimiters"), + log_color=W.color("logger.color.backlog_line"), + ncolor=W.color("reset")) diff --git a/matrix/uploads.py b/matrix/uploads.py index a88a91b..82712e0 100644 --- a/matrix/uploads.py +++ b/matrix/uploads.py @@ -32,6 +32,7 @@ except ImportError: from .globals import SCRIPT_NAME, SERVERS, W, UPLOADS from .utf import utf8_decode +from .message_renderer import Render from matrix import globals as G from nio import Api @@ -206,25 +207,15 @@ class Upload(object): assert self.content_uri if self.encrypt: - http_url = Api.encrypted_mxc_to_plumb( - self.content_uri, + return Render.encrypted_media( + self.conetent_uri, + self.file_name, self.file_keys["key"]["k"], self.file_keys["hashes"]["sha256"], - self.file_keys["iv"] + self.file_keys["iv"], ) - url = http_url if http_url else self.content_uri - description = "{}".format(self.file_name) - return ("{del_color}<{ncolor}{desc}{del_color}>{ncolor} " - "{del_color}[{ncolor}{url}{del_color}]{ncolor}").format( - del_color=W.color("chat_delimiters"), - ncolor=W.color("reset"), - desc=description, url=url) - - http_url = Api.mxc_to_http(self.content_uri) - description = ("/{}".format(self.file_name) if self.file_name - else "") - return "{url}{desc}".format(url=http_url, desc=description) + return Render.media(self.content_uri, self.file_name) @attr.s