diff --git a/example-config.yaml b/example-config.yaml
index fce1ac91..1aa2a508 100644
--- a/example-config.yaml
+++ b/example-config.yaml
@@ -63,9 +63,6 @@ bridge:
native_replies: True
# If native replies are disabled, should the custom replies contain a link to the message being
# replied to?
- #
- # Warning: Using this on a client with native replies will not look good: the message will have
- # a native quote AND a non-native quote.
link_in_reply: False
# 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.
diff --git a/mautrix_appservice/intent_api.py b/mautrix_appservice/intent_api.py
index 06f16494..0fd002e0 100644
--- a/mautrix_appservice/intent_api.py
+++ b/mautrix_appservice/intent_api.py
@@ -344,24 +344,25 @@ async def mark_read(self, room_id, event_id):
return await self.client.request("POST", f"/rooms/{room_id}/receipt/m.read/{event_id}",
content={})
- def send_notice(self, room_id, text, html=None):
- return self.send_text(room_id, text, html, "m.notice")
+ def send_notice(self, room_id, text, html=None, relates_to=None):
+ return self.send_text(room_id, text, html, "m.notice", relates_to)
- def send_emote(self, room_id, text, html=None):
- return self.send_text(room_id, text, html, "m.emote")
+ def send_emote(self, room_id, text, html=None, relates_to=None):
+ return self.send_text(room_id, text, html, "m.emote", relates_to)
- def send_image(self, room_id, url, info=None, text=None):
- return self.send_file(room_id, url, info or {}, text, "m.image")
+ def send_image(self, room_id, url, info=None, text=None, relates_to=None):
+ return self.send_file(room_id, url, info or {}, text, "m.image", relates_to)
- def send_file(self, room_id, url, info=None, text=None, file_type="m.file"):
+ def send_file(self, room_id, url, info=None, text=None, file_type="m.file", relates_to=None):
return self.send_message(room_id, {
"msgtype": file_type,
"url": url,
"body": text or "Uploaded file",
"info": info or {},
+ "m.relates_to": relates_to or None,
})
- def send_text(self, room_id, text, html=None, msgtype="m.text"):
+ def send_text(self, room_id, text, html=None, msgtype="m.text", relates_to=None):
if html:
if not text:
text = html
@@ -370,11 +371,13 @@ def send_text(self, room_id, text, html=None, msgtype="m.text"):
"msgtype": msgtype,
"format": "org.matrix.custom.html",
"formatted_body": html or text,
+ "m.relates_to": relates_to or None,
})
else:
return self.send_message(room_id, {
"body": text,
"msgtype": msgtype,
+ "m.relates_to": relates_to or None,
})
def send_message(self, room_id, body):
diff --git a/mautrix_telegram/formatter.py b/mautrix_telegram/formatter.py
index 21236b4f..9590b5ec 100644
--- a/mautrix_telegram/formatter.py
+++ b/mautrix_telegram/formatter.py
@@ -47,19 +47,12 @@
# region Matrix to Telegram
-class MessageEntityReply(MessageEntityUnknown):
- def __init__(self, offset=0, length=0, msg_id=0):
- super().__init__(offset, length)
- self.msg_id = msg_id
-
class MatrixParser(HTMLParser):
mention_regex = re.compile("https://matrix.to/#/(@.+)")
- reply_regex = re.compile(r"https://matrix.to/#/(!.+?)/(\$.+)")
- def __init__(self, tg_space=None):
+ def __init__(self):
super().__init__()
- self._tg_space = tg_space
self.text = ""
self.entities = []
self._building_entities = {}
@@ -67,7 +60,6 @@ def __init__(self, tg_space=None):
self._open_tags = deque()
self._open_tags_meta = deque()
self._previous_ended_line = True
- self._building_reply = False
def handle_starttag(self, tag, attrs):
self._open_tags.appendleft(tag)
@@ -97,7 +89,6 @@ def handle_starttag(self, tag, attrs):
except KeyError:
return
mention = self.mention_regex.search(url)
- reply = self.reply_regex.search(url)
if mention:
mxid = mention.group(1)
user = p.Puppet.get_by_mxid(mxid, create=False)
@@ -111,20 +102,6 @@ def handle_starttag(self, tag, attrs):
else:
entity_type = MessageEntityMentionName
args["user_id"] = user.tgid
- elif reply and self._tg_space and (len(self.entities) == 0
- and len(self._building_entities) == 0):
- room_id = reply.group(1)
- message_id = reply.group(2)
- message = DBMessage.query.filter(DBMessage.mxid == message_id,
- DBMessage.mx_room == room_id,
- DBMessage.tg_space == self._tg_space
- ).one_or_none()
- if not message:
- return
- entity_type = MessageEntityReply
- args["msg_id"] = message.tgid
- self._building_reply = True
- url = None
elif url.startswith("mailto:"):
url = url[len("mailto:"):]
entity_type = MessageEntityEmail
@@ -152,8 +129,6 @@ def _list_depth(self):
def handle_data(self, text):
text = unescape(text)
- if self._building_reply:
- return
previous_tag = self._open_tags[0] if len(self._open_tags) > 0 else ""
list_format_offset = 0
if previous_tag == "a":
@@ -193,8 +168,6 @@ def handle_endtag(self, tag):
self._open_tags_meta.popleft()
except IndexError:
pass
- if tag == "a":
- self._building_reply = False
if (tag == "ul" or tag == "ol") and self.text.endswith("\n"):
self.text = self.text[:-1]
entity = self._building_entities.pop(tag, None)
@@ -202,22 +175,54 @@ def handle_endtag(self, tag):
self.entities.append(entity)
-def matrix_to_telegram(html, tg_space=None):
+def matrix_to_telegram(html):
try:
- parser = MatrixParser(tg_space)
+ parser = MatrixParser()
parser.feed(html)
return parser.text, parser.entities
except Exception:
log.exception("Failed to convert Matrix format:\nhtml=%s", html)
+def matrix_reply_to_telegram(content, tg_space, room_id=None):
+ try:
+ reply = content["m.relates_to"]["m.in_reply_to"]
+ room_id = room_id or reply["room_id"]
+ event_id = reply["event_id"]
+ message = DBMessage.query.filter(DBMessage.mxid == event_id and
+ DBMessage.tg_space == tg_space and
+ DBMessage.mx_room == room_id).one_or_none()
+ if message:
+ return message.tgid
+ except KeyError:
+ pass
+ return None
+
+
# endregion
# region Telegram to Matrix
+def telegram_reply_to_matrix(evt, source):
+ if evt.reply_to_msg_id:
+ space = (evt.to_id.channel_id
+ if isinstance(evt, Message) and isinstance(evt.to_id, PeerChannel)
+ else source.tgid)
+ msg = DBMessage.query.get((evt.reply_to_msg_id, space))
+ if msg:
+ return {
+ "m.in_reply_to": {
+ "event_id": msg.mxid,
+ "room_id": msg.mx_room,
+ }
+ }
+ return {}
+
+
async def telegram_event_to_matrix(evt, source, native_replies=False, message_link_in_reply=False,
main_intent=None, reply_text="Reply"):
text = evt.message
html = telegram_to_matrix(evt.message, evt.entities) if evt.entities else None
+ relates_to = {}
if evt.fwd_from:
if not html:
@@ -248,7 +253,10 @@ async def telegram_event_to_matrix(evt, source, native_replies=False, message_li
msg = DBMessage.query.get((evt.reply_to_msg_id, space))
if msg:
if native_replies:
- quote = f"Quote
"
+ relates_to["m.in_reply_to"] = {
+ "event_id": msg.mxid,
+ "room_id": msg.mx_room,
+ }
if reply_text == "Edit":
html = "Edit: " + (html or escape(text))
else:
@@ -268,10 +276,10 @@ async def telegram_event_to_matrix(evt, source, native_replies=False, message_li
quote = f"{reply_to_msg} to {reply_to_user}
{body}" except (ValueError, KeyError, MatrixRequestError): quote = "{reply_text} to unknown user (Failed to fetch message):