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 # pylint: disable=unused-import
from typing import (List, Set, Dict, Tuple, Text, Optional, AnyStr, Deque, Any) 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.utf import utf8_decode
from matrix.http import HttpResponse from matrix.http import HttpResponse
from matrix.api import MatrixMessage, MessageType, matrix_login from matrix.api import MatrixMessage, MessageType, matrix_login
@ -407,13 +407,7 @@ def room_input_cb(server_name, buffer, input_data):
if room.encrypted: if room.encrypted:
return W.WEECHAT_RC_OK return W.WEECHAT_RC_OK
formatted_data = colors.parse_input_line(input_data) formatted_data = Formatted.from_input_line(input_data)
extra_data = {
"author": server.user,
"message": colors.formatted_to_weechat(W, formatted_data),
"room_id": room_id
}
message = MatrixMessage(server, OPTIONS, MessageType.SEND, room_id=room_id, message = MatrixMessage(server, OPTIONS, MessageType.SEND, room_id=room_id,
formatted_message=formatted_data) formatted_message=formatted_data)

View file

@ -29,7 +29,6 @@ except ImportError:
from matrix.globals import OPTIONS from matrix.globals import OPTIONS
from matrix.http import RequestType, HttpRequest 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 MATRIX_API_PATH = "/_matrix/client/r0" # type: str
@ -259,11 +258,10 @@ class MatrixMessage:
assert self.room_id assert self.room_id
assert self.formatted_message 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): if self.formatted_message.is_formatted:
data["formatted_content"] = formatted_to_html( data["formatted_content"] = self.formatted_message.to_html()
self.formatted_message)
self.request = server.client.room_send_message( self.request = server.client.room_send_message(
self.room_id, self.room_id,

View file

@ -21,6 +21,7 @@ from __future__ import unicode_literals
# pylint: disable=redefined-builtin # pylint: disable=redefined-builtin
from builtins import str from builtins import str
from collections import namedtuple from collections import namedtuple
from matrix.globals import W
import webcolors 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. # TODO this should be a typed dict.
DEFAULT_ATRIBUTES = { DEFAULT_ATRIBUTES = {
"bold": False, "bold": False,
@ -631,236 +864,6 @@ def color_weechat_to_html(color):
return hex_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): def string_strikethrough(string):
# type (str) -> str # type (str) -> str
return "".join(["{}\u0336".format(c) for c in string]) 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 from operator import itemgetter
import matrix.colors as colors from matrix.colors import Formatted
from matrix.globals import W, OPTIONS 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 'format' in event['content'] and 'formatted_body' in event['content']:
if event['content']['format'] == "org.matrix.custom.html": if event['content']['format'] == "org.matrix.custom.html":
formatted_data = colors.html_to_formatted( formatted_data = Formatted.from_html(
event['content']['formatted_body']) event['content']['formatted_body'])
msg = colors.formatted_to_weechat(W, formatted_data) msg = formatted_data.to_weechat()
if event['sender'] in room.users: if event['sender'] in room.users:
user = room.users[event['sender']] user = room.users[event['sender']]
@ -708,10 +708,7 @@ def matrix_handle_message(
elif message_type is MessageType.SEND: elif message_type is MessageType.SEND:
room_id = message.room_id room_id = message.room_id
author = server.user author = server.user
weechat_message = colors.formatted_to_weechat( weechat_message = message.formatted_message.to_weechat()
W,
message.formatted_message
)
date = int(time.time()) date = int(time.time())
# TODO the event_id can be missing if sending has failed for # TODO the event_id can be missing if sending has failed for