diff --git a/example-config.yaml b/example-config.yaml
index f2a96e96..59ffaa3b 100644
--- a/example-config.yaml
+++ b/example-config.yaml
@@ -74,6 +74,8 @@ bridge:
# Show message editing as a reply to the original message.
# If this is false, message edits are not shown at all, as Matrix does not support editing yet.
edits_as_replies: false
+ # Highlight changed/added parts in edits. Requires lxml.
+ highlight_edits: false
# Whether or not Matrix bot messages (type m.notice) should be bridged.
bridge_notices: false
# The maximum number of simultaneous Telegram deletions to handle.
diff --git a/mautrix_telegram/formatter/from_telegram.py b/mautrix_telegram/formatter/from_telegram.py
index 7d4be35e..576f51be 100644
--- a/mautrix_telegram/formatter/from_telegram.py
+++ b/mautrix_telegram/formatter/from_telegram.py
@@ -15,7 +15,12 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
from html import escape
+try:
+ from lxml.html.diff import htmldiff
+except ImportError:
+ htmldiff = None
import logging
+import re
from telethon_aio.tl.types import *
from mautrix_appservice import MatrixRequestError
@@ -26,6 +31,7 @@ from .util import (add_surrogates, remove_surrogates, trim_reply_fallback_html,
trim_reply_fallback_text, unicode_to_html)
log = logging.getLogger("mau.fmt.tg")
+should_highlight_edits = False
def telegram_reply_to_matrix(evt, source):
@@ -68,6 +74,21 @@ async def _add_forward_header(source, text, html, fwd_from_id):
return text, html
+def highlight_edits(new_html, old_html):
+ # Don't include `Edit:` text in diff.
+ if old_html.startswith("Edit: "):
+ old_html = old_html[len("Edit: "):]
+
+ # Generate diff with lxml
+ new_html = htmldiff(old_html, new_html)
+
+ # Replace with since Riot doesn't allow
+ new_html = new_html.replace("", "").replace("", "")
+ # Remove s since we just want to hide deletions.
+ new_html = re.sub(".+?", "", new_html)
+ return new_html
+
+
async def _add_reply_header(source, text, html, evt, relates_to, main_intent, is_edit):
space = (evt.to_id.channel_id
if isinstance(evt, Message) and isinstance(evt.to_id, PeerChannel)
@@ -81,9 +102,6 @@ async def _add_reply_header(source, text, html, evt, relates_to, main_intent, is
"event_id": msg.mxid,
"room_id": msg.mx_room,
}
- if is_edit:
- html = f"Edit: {html or escape(text)}"
- text = f"Edit: {text}"
try:
event = await main_intent.get_event(msg.mx_room, msg.mxid)
@@ -99,12 +117,19 @@ async def _add_reply_header(source, text, html, evt, relates_to, main_intent, is
puppet = pu.Puppet.get_by_mxid(r_sender, create=False)
r_displayname = puppet.displayname if puppet else r_sender
r_sender_link = f"{r_displayname}"
+
+ if is_edit and should_highlight_edits:
+ html = highlight_edits(html or escape(text), r_html_body)
except (ValueError, KeyError, MatrixRequestError):
r_sender_link = "unknown user"
# r_sender = "unknown user"
r_text_body = "Failed to fetch message"
r_html_body = "Failed to fetch message"
+ if is_edit:
+ html = f"Edit: {html or escape(text)}"
+ text = f"Edit: {text}"
+
r_keyword = "In reply to" if not is_edit else "Edit to"
r_msg_link = f"{r_keyword}"
html = (f"{r_msg_link} {r_sender_link} {r_html_body}
"
@@ -257,4 +282,5 @@ def _parse_url(html, entity_text, url):
def init_tg(context):
- pass
+ global should_highlight_edits
+ should_highlight_edits = htmldiff and context.config["bridge.highlight_edits"]
diff --git a/requirements/optional.txt b/requirements/optional.txt
new file mode 100644
index 00000000..ab90481d
--- /dev/null
+++ b/requirements/optional.txt
@@ -0,0 +1 @@
+lxml
diff --git a/setup.py b/setup.py
index dbc732ed..eabecf3d 100644
--- a/setup.py
+++ b/setup.py
@@ -31,6 +31,9 @@ setuptools.setup(
dependency_links=[
"https://github.com/tulir/telethon-asyncio/tarball/9b389cfb4b6d3876e9661c23507f17e96897e4b0#egg=telethon-aio-git-0.18.0+1"
],
+ extras_require={
+ "highlight_edits": ["lxml>=4.1.1,<5"],
+ },
classifiers=[
"Development Status :: 4 Beta",