Rework of the color handling, add a Formatted class.
This commit is contained in:
parent
4c9069a8b1
commit
6b87994a94
4 changed files with 242 additions and 250 deletions
10
main.py
10
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)
|
||||
|
|
|
@ -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,
|
||||
|
|
463
matrix/colors.py
463
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="<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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue