Rework of the color handling, add a Formatted class.

This commit is contained in:
poljar (Damir Jelić) 2018-02-10 15:34:40 +01:00
parent 4c9069a8b1
commit 6b87994a94
4 changed files with 242 additions and 250 deletions

10
main.py
View file

@ -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)

View file

@ -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,

View file

@ -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="<strong>",
text=string,
bold_off="</strong>")
elif name == "italic" and value:
return "{italic_on}{text}{italic_off}".format(
italic_on="<em>",
text=string,
italic_off="</em>")
elif name == "underline" and value:
return "{underline_on}{text}{underline_off}".format(
underline_on="<u>",
text=string,
underline_off="</u>")
elif name == "strikethrough" and value:
return "{strike_on}{text}{strike_off}".format(
strike_on="<del>",
text=string,
strike_off="</del>")
elif name == "quote" and value:
return "{quote_on}{text}{quote_off}".format(
quote_on="<blockquote>",
text=string,
quote_off="</blockquote>")
elif name == "fgcolor" and value:
return "{color_on}{text}{color_off}".format(
color_on="<font color={color}>".format(
color=color_weechat_to_html(value)
),
text=string,
color_off="</font>")
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="<strong>",
text=string,
bold_off="</strong>")
elif name == "italic" and value:
return "{italic_on}{text}{italic_off}".format(
italic_on="<em>",
text=string,
italic_off="</em>")
elif name == "underline" and value:
return "{underline_on}{text}{underline_off}".format(
underline_on="<u>",
text=string,
underline_off="</u>")
elif name == "strikethrough" and value:
return "{strike_on}{text}{strike_off}".format(
strike_on="<del>",
text=string,
strike_off="</del>")
elif name == "quote" and value:
return "{quote_on}{text}{quote_off}".format(
quote_on="<blockquote>",
text=string,
quote_off="</blockquote>")
elif name == "fgcolor" and value:
return "{color_on}{text}{color_off}".format(
color_on="<font color={color}>".format(
color=color_weechat_to_html(value)
),
text=string,
color_off="</font>")
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)

View file

@ -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