diff --git a/main.py b/main.py index dd61919..4b68777 100644 --- a/main.py +++ b/main.py @@ -28,7 +28,7 @@ from builtins import str # pylint: disable=unused-import from typing import (List, Set, Dict, Tuple, Text, Optional, AnyStr, Deque, Any) -from matrix import colors +from matrix.colors import Formatted from matrix.utf import utf8_decode from matrix.http import HttpResponse from matrix.api import MatrixMessage, MessageType, matrix_login @@ -407,13 +407,7 @@ def room_input_cb(server_name, buffer, input_data): if room.encrypted: return W.WEECHAT_RC_OK - formatted_data = colors.parse_input_line(input_data) - - extra_data = { - "author": server.user, - "message": colors.formatted_to_weechat(W, formatted_data), - "room_id": room_id - } + formatted_data = Formatted.from_input_line(input_data) message = MatrixMessage(server, OPTIONS, MessageType.SEND, room_id=room_id, formatted_message=formatted_data) diff --git a/matrix/api.py b/matrix/api.py index 9f45949..f160eef 100644 --- a/matrix/api.py +++ b/matrix/api.py @@ -29,7 +29,6 @@ except ImportError: from matrix.globals import OPTIONS from matrix.http import RequestType, HttpRequest -from matrix.colors import formatted_to_plain, formatted_to_html, is_formatted MATRIX_API_PATH = "/_matrix/client/r0" # type: str @@ -259,11 +258,10 @@ class MatrixMessage: assert self.room_id assert self.formatted_message - data = {"content": formatted_to_plain(self.formatted_message)} + data = {"content": self.formatted_message.to_plain()} - if is_formatted(self.formatted_message): - data["formatted_content"] = formatted_to_html( - self.formatted_message) + if self.formatted_message.is_formatted: + data["formatted_content"] = self.formatted_message.to_html() self.request = server.client.room_send_message( self.room_id, diff --git a/matrix/colors.py b/matrix/colors.py index 056467b..25c7e44 100644 --- a/matrix/colors.py +++ b/matrix/colors.py @@ -21,6 +21,7 @@ from __future__ import unicode_literals # pylint: disable=redefined-builtin from builtins import str from collections import namedtuple +from matrix.globals import W import webcolors @@ -36,6 +37,238 @@ FormattedString = namedtuple( ) +class Formatted(): + def __init__(self, substrings): + # type: List[FormattedString] -> None + self.substrings = substrings + + + def is_formatted(self): + # type: (Formatted) -> bool + for string in self.substrings: + if string.attributes != DEFAULT_ATRIBUTES: + return True + return False + + # TODO reverse video + @classmethod + def from_input_line(cls, line): + # type: (str) -> Formatted + """Parses the weechat input line and produces formatted strings that + can be later converted to HTML or to a string for weechat's print + functions + """ + text = "" # type: str + substrings = [] # type: List[FormattedString] + attributes = DEFAULT_ATRIBUTES.copy() + + i = 0 + while i < len(line): + # Bold + if line[i] == "\x02": + if text: + substrings.append(FormattedString(text, attributes.copy())) + text = "" + attributes["bold"] = not attributes["bold"] + i = i + 1 + + # Color + elif line[i] == "\x03": + if text: + substrings.append(FormattedString(text, attributes.copy())) + text = "" + i = i + 1 + + # check if it's a valid color, add it to the attributes + if line[i].isdigit(): + color_string = line[i] + i = i + 1 + + if line[i].isdigit(): + if color_string == "0": + color_string = line[i] + else: + color_string = color_string + line[i] + i = i + 1 + + attributes["fgcolor"] = color_line_to_weechat(color_string) + else: + attributes["fgcolor"] = None + + # check if we have a background color + if line[i] == "," and line[i + 1].isdigit(): + color_string = line[i + 1] + i = i + 2 + + if line[i].isdigit(): + if color_string == "0": + color_string = line[i] + else: + color_string = color_string + line[i] + i = i + 1 + + attributes["bgcolor"] = color_line_to_weechat(color_string) + else: + attributes["bgcolor"] = None + # Reset + elif line[i] == "\x0F": + if text: + substrings.append(FormattedString(text, attributes.copy())) + text = "" + # Reset all the attributes + attributes = DEFAULT_ATRIBUTES.copy() + i = i + 1 + # Italic + elif line[i] == "\x1D": + if text: + substrings.append(FormattedString(text, attributes.copy())) + text = "" + attributes["italic"] = not attributes["italic"] + i = i + 1 + + # Underline + elif line[i] == "\x1F": + if text: + substrings.append(FormattedString(text, attributes.copy())) + text = "" + attributes["underline"] = not attributes["underline"] + i = i + 1 + + # Normal text + else: + text = text + line[i] + i = i + 1 + + substrings.append(FormattedString(text, attributes)) + return cls(substrings) + + @classmethod + def from_html(cls, html): + # type: (str) -> Formatted + parser = MatrixHtmlParser() + parser.feed(html) + return cls(parser.get_substrings()) + + def to_html(self): + # TODO BG COLOR + def add_attribute(string, name, value): + if name == "bold" and value: + return "{bold_on}{text}{bold_off}".format( + bold_on="", + text=string, + bold_off="") + elif name == "italic" and value: + return "{italic_on}{text}{italic_off}".format( + italic_on="", + text=string, + italic_off="") + elif name == "underline" and value: + return "{underline_on}{text}{underline_off}".format( + underline_on="", + text=string, + underline_off="") + elif name == "strikethrough" and value: + return "{strike_on}{text}{strike_off}".format( + strike_on="", + text=string, + strike_off="") + elif name == "quote" and value: + return "{quote_on}{text}{quote_off}".format( + quote_on="
", + text=string, + quote_off="
") + elif name == "fgcolor" and value: + return "{color_on}{text}{color_off}".format( + color_on="".format( + color=color_weechat_to_html(value) + ), + text=string, + color_off="") + + return string + + def format_string(formatted_string): + text = formatted_string.text + attributes = formatted_string.attributes + + for key, value in attributes.items(): + text = add_attribute(text, key, value) + return text + + html_string = map(format_string, self.substrings) + return "".join(html_string) + + # TODO do we want at least some formatting using unicode + # (strikethrough, quotes)? + def to_plain(self): + # type: (List[FormattedString]) -> str + def strip_atribute(string, _, __): + return string + + def format_string(formatted_string): + text = formatted_string.text + attributes = formatted_string.attributes + + for key, value in attributes.items(): + text = strip_atribute(text, key, value) + return text + + plain_string = map(format_string, self.substrings) + return "".join(plain_string) + + def to_weechat(self): + # TODO BG COLOR + def add_attribute(string, name, value): + if name == "bold" and value: + return "{bold_on}{text}{bold_off}".format( + bold_on=W.color("bold"), + text=string, + bold_off=W.color("-bold")) + + elif name == "italic" and value: + return "{italic_on}{text}{italic_off}".format( + italic_on=W.color("italic"), + text=string, + italic_off=W.color("-italic")) + + elif name == "underline" and value: + return "{underline_on}{text}{underline_off}".format( + underline_on=W.color("underline"), + text=string, + underline_off=W.color("-underline")) + + elif name == "strikethrough" and value: + return string_strikethrough(string) + + elif name == "quote" and value: + return "“{text}”".format(text=string) + + elif name == "fgcolor" and value: + return "{color_on}{text}{color_off}".format( + color_on=W.color(value), + text=string, + color_off=W.color("resetcolor")) + + elif name == "bgcolor" and value: + return "{color_on}{text}{color_off}".format( + color_on=W.color("," + value), + text=string, + color_off=W.color("resetcolor")) + + return string + + def format_string(formatted_string): + text = formatted_string.text + attributes = formatted_string.attributes + + for key, value in attributes.items(): + text = add_attribute(text, key, value) + return text + + weechat_strings = map(format_string, self.substrings) + return "".join(weechat_strings) + + # TODO this should be a typed dict. DEFAULT_ATRIBUTES = { "bold": False, @@ -631,236 +864,6 @@ def color_weechat_to_html(color): return hex_color -# TODO reverse video -def parse_input_line(line): - # type: (str) -> List[FormattedString] - """Parses the weechat input line and produces formatted strings that can be - later converted to HTML or to a string for weechat's print functions - """ - text = "" # type: str - substrings = [] # type: List[FormattedString] - attributes = DEFAULT_ATRIBUTES.copy() - - i = 0 - while i < len(line): - # Bold - if line[i] == "\x02": - if text: - substrings.append(FormattedString(text, attributes.copy())) - text = "" - attributes["bold"] = not attributes["bold"] - i = i + 1 - - # Color - elif line[i] == "\x03": - if text: - substrings.append(FormattedString(text, attributes.copy())) - text = "" - i = i + 1 - - # check if it's a valid color, add it to the attributes - if line[i].isdigit(): - color_string = line[i] - i = i + 1 - - if line[i].isdigit(): - if color_string == "0": - color_string = line[i] - else: - color_string = color_string + line[i] - i = i + 1 - - attributes["fgcolor"] = color_line_to_weechat(color_string) - else: - attributes["fgcolor"] = None - - # check if we have a background color - if line[i] == "," and line[i+1].isdigit(): - color_string = line[i+1] - i = i + 2 - - if line[i].isdigit(): - if color_string == "0": - color_string = line[i] - else: - color_string = color_string + line[i] - i = i + 1 - - attributes["bgcolor"] = color_line_to_weechat(color_string) - else: - attributes["bgcolor"] = None - # Reset - elif line[i] == "\x0F": - if text: - substrings.append(FormattedString(text, attributes.copy())) - text = "" - # Reset all the attributes - attributes = DEFAULT_ATRIBUTES.copy() - i = i + 1 - # Italic - elif line[i] == "\x1D": - if text: - substrings.append(FormattedString(text, attributes.copy())) - text = "" - attributes["italic"] = not attributes["italic"] - i = i + 1 - - # Underline - elif line[i] == "\x1F": - if text: - substrings.append(FormattedString(text, attributes.copy())) - text = "" - attributes["underline"] = not attributes["underline"] - i = i + 1 - - # Normal text - else: - text = text + line[i] - i = i + 1 - - substrings.append(FormattedString(text, attributes)) - return substrings - - -def is_formatted(strings): - # type: (List[FormattedString]) -> bool - for string in strings: - if string.attributes != DEFAULT_ATRIBUTES: - return True - return False - - -def formatted_to_html(strings): - # type: (List[FormattedString]) -> str - # TODO BG COLOR - def add_attribute(string, name, value): - if name == "bold" and value: - return "{bold_on}{text}{bold_off}".format( - bold_on="", - text=string, - bold_off="") - elif name == "italic" and value: - return "{italic_on}{text}{italic_off}".format( - italic_on="", - text=string, - italic_off="") - elif name == "underline" and value: - return "{underline_on}{text}{underline_off}".format( - underline_on="", - text=string, - underline_off="") - elif name == "strikethrough" and value: - return "{strike_on}{text}{strike_off}".format( - strike_on="", - text=string, - strike_off="") - elif name == "quote" and value: - return "{quote_on}{text}{quote_off}".format( - quote_on="
", - text=string, - quote_off="
") - elif name == "fgcolor" and value: - return "{color_on}{text}{color_off}".format( - color_on="".format( - color=color_weechat_to_html(value) - ), - text=string, - color_off="") - - return string - - def format_string(formatted_string): - text = formatted_string.text - attributes = formatted_string.attributes - - for key, value in attributes.items(): - text = add_attribute(text, key, value) - return text - - html_string = map(format_string, strings) - return "".join(html_string) - - -# TODO do we want at least some formatting using unicode -# (strikethrough, quotes)? -def formatted_to_plain(strings): - # type: (List[FormattedString]) -> str - def strip_atribute(string, _, __): - return string - - def format_string(formatted_string): - text = formatted_string.text - attributes = formatted_string.attributes - - for key, value in attributes.items(): - text = strip_atribute(text, key, value) - return text - - plain_string = map(format_string, strings) - return "".join(plain_string) - - -def html_to_formatted(html): - # type: (str) -> FormattedString - parser = MatrixHtmlParser() - parser.feed(html) - return parser.get_substrings() - - def string_strikethrough(string): # type (str) -> str return "".join(["{}\u0336".format(c) for c in string]) - - -def formatted_to_weechat(W, strings): - # type: (weechat, List[FormattedString]) -> str - # TODO BG COLOR - def add_attribute(string, name, value): - if name == "bold" and value: - return "{bold_on}{text}{bold_off}".format( - bold_on=W.color("bold"), - text=string, - bold_off=W.color("-bold")) - - elif name == "italic" and value: - return "{italic_on}{text}{italic_off}".format( - italic_on=W.color("italic"), - text=string, - italic_off=W.color("-italic")) - - elif name == "underline" and value: - return "{underline_on}{text}{underline_off}".format( - underline_on=W.color("underline"), - text=string, - underline_off=W.color("-underline")) - - elif name == "strikethrough" and value: - return string_strikethrough(string) - - elif name == "quote" and value: - return "“{text}”".format(text=string) - - elif name == "fgcolor" and value: - return "{color_on}{text}{color_off}".format( - color_on=W.color(value), - text=string, - color_off=W.color("resetcolor")) - - elif name == "bgcolor" and value: - return "{color_on}{text}{color_off}".format( - color_on=W.color("," + value), - text=string, - color_off=W.color("resetcolor")) - - return string - - def format_string(formatted_string): - text = formatted_string.text - attributes = formatted_string.attributes - - for key, value in attributes.items(): - text = add_attribute(text, key, value) - return text - - weechat_strings = map(format_string, strings) - return "".join(weechat_strings) diff --git a/matrix/messages.py b/matrix/messages.py index a0654bc..076925d 100644 --- a/matrix/messages.py +++ b/matrix/messages.py @@ -24,7 +24,7 @@ import datetime from operator import itemgetter -import matrix.colors as colors +from matrix.colors import Formatted from matrix.globals import W, OPTIONS @@ -190,9 +190,9 @@ def matrix_handle_room_text_message(server, room_id, event, old=False): if 'format' in event['content'] and 'formatted_body' in event['content']: if event['content']['format'] == "org.matrix.custom.html": - formatted_data = colors.html_to_formatted( + formatted_data = Formatted.from_html( event['content']['formatted_body']) - msg = colors.formatted_to_weechat(W, formatted_data) + msg = formatted_data.to_weechat() if event['sender'] in room.users: user = room.users[event['sender']] @@ -708,10 +708,7 @@ def matrix_handle_message( elif message_type is MessageType.SEND: room_id = message.room_id author = server.user - weechat_message = colors.formatted_to_weechat( - W, - message.formatted_message - ) + weechat_message = message.formatted_message.to_weechat() date = int(time.time()) # TODO the event_id can be missing if sending has failed for