diff --git a/example-config.yaml b/example-config.yaml index e481784a..0addff2f 100644 --- a/example-config.yaml +++ b/example-config.yaml @@ -40,10 +40,11 @@ bridge: displayname_template: "{displayname} (Telegram)" # Set the preferred order of user identifiers which to use in the Matrix puppet display name. - # In the (hopefully unlikely) scenario that none of the given keys are found, the numeric user ID is used. + # In the (hopefully unlikely) scenario that none of the given keys are found, the numeric user + # ID is used. # - # If the bridge is working properly, a phone number or an username should always be known, but the other one can - # very well be empty. + # If the bridge is working properly, a phone number or an username should always be known, but + # the other one can very well be empty. # # Valid keys: # "full name" (First and/or last name) @@ -57,6 +58,9 @@ bridge: - username - phone number + # Whether or not to use native Matrix replies. At the time of writing, only riot-web supports + # replies and the format of them is subject to change. + native_replies: False # The prefix for commands. Only required in non-management rooms. command_prefix: "!tg" @@ -67,7 +71,8 @@ bridge: - "internal-hs.example.com" - "@user:public.example.com" - # Admins can do things like delete portal rooms. + # Admins can do things like delete portal rooms. Here you must specify the exact MXID, domains + # are not accepted. admins: - "@admin:internal-hs.example.com" diff --git a/mautrix_appservice/intent_api.py b/mautrix_appservice/intent_api.py index f4d3fde5..b9c61121 100644 --- a/mautrix_appservice/intent_api.py +++ b/mautrix_appservice/intent_api.py @@ -275,6 +275,10 @@ class IntentAPI: events.remove(event_id) self.set_pinned_messages(room_id, events) + def get_event(self, room_id, event_id): + self.ensure_joined(room_id) + return self.client._send("GET", f"/rooms/{room_id}/event/{event_id}") + def set_typing(self, room_id, is_typing=True, timeout=5000): self.ensure_joined(room_id) return self.client.set_typing(room_id, is_typing, timeout) diff --git a/mautrix_telegram/formatter.py b/mautrix_telegram/formatter.py index 030f72aa..d2f74b9c 100644 --- a/mautrix_telegram/formatter.py +++ b/mautrix_telegram/formatter.py @@ -20,6 +20,8 @@ from collections import deque import re import logging +from matrix_client.errors import MatrixRequestError + from telethon.tl.types import * from . import user as u, puppet as p @@ -194,7 +196,7 @@ def matrix_to_telegram(html, tg_space=None): # endregion # region Telegram to Matrix -def telegram_event_to_matrix(evt, source): +def telegram_event_to_matrix(evt, source, native_replies=False, main_intent=None): text = evt.message html = telegram_to_matrix(evt.message, evt.entities) if evt.entities else None @@ -225,7 +227,17 @@ def telegram_event_to_matrix(evt, source): PeerChannel) else source.tgid msg = DBMessage.query.get((evt.reply_to_msg_id, space)) if msg: - quote = f"Quote
" + if native_replies: + quote = f"Quote
" + else: + try: + event = main_intent.get_event(msg.mx_room, msg.mxid) + content = event["content"] + body = content["formatted_body"] if "formatted_body" in content else content["body"] + reply_to = f"event['sender']" + quote = f"Reply to {reply_to}
{body}
" + except (ValueError, KeyError, MatrixRequestError): + quote = "Reply to someone (failed to fetch message)
" if html: html = quote + html else: diff --git a/mautrix_telegram/portal.py b/mautrix_telegram/portal.py index 008ea49a..1907cfa2 100644 --- a/mautrix_telegram/portal.py +++ b/mautrix_telegram/portal.py @@ -122,7 +122,7 @@ class Portal: if len(self._dedup) > 20: del self._dedup_mxid[self._dedup.popleft()] - return False + return None def get_input_entity(self, user): return user.client.get_input_entity(self.peer) @@ -416,10 +416,11 @@ class Portal: else: self.log.debug("Unhandled Matrix event: %s", message) return - self.is_duplicate(response, event_id) + tg_space = self.tgid if self.peer_type == "channel" else sender.tgid + self.is_duplicate(response, (event_id, tg_space)) self.db.add(DBMessage( tgid=response.id, - tg_space=self.tgid if self.peer_type == "channel" else sender.tgid, + tg_space=tg_space, mx_room=self.mxid, mxid=event_id)) self.db.commit() @@ -679,7 +680,9 @@ class Portal: def handle_telegram_text(self, source, sender, evt): self.log.debug(f"Sending {evt.message} to {self.mxid} by {sender.id}") - text, html = formatter.telegram_event_to_matrix(evt, source) + text, html = formatter.telegram_event_to_matrix(evt, source, + config["bridge.native_replies"], + self.main_intent) sender.intent.set_typing(self.mxid, is_typing=False) return sender.intent.send_text(self.mxid, text, html=html) @@ -690,10 +693,12 @@ class Portal: tg_space = self.tgid if self.peer_type == "channel" else source.tgid temporary_identifier = f"${random.randint(1000000000000,9999999999999)}TGBRIDGETEMP" - mxid = self.is_duplicate(evt, temporary_identifier) - if mxid: - self.db.add(DBMessage(tgid=evt.id, mx_room=self.mxid, mxid=mxid, tg_space=tg_space)) - self.db.commit() + duplicate_found = self.is_duplicate(evt, (temporary_identifier, tg_space)) + if duplicate_found: + mxid, other_tg_space = duplicate_found + if tg_space != other_tg_space: + self.db.add(DBMessage(tgid=evt.id, mx_room=self.mxid, mxid=mxid, tg_space=tg_space)) + self.db.commit() return if evt.message: @@ -715,8 +720,7 @@ class Portal: mxid = response["event_id"] DBMessage.query \ .filter(DBMessage.mx_room == self.mxid, - DBMessage.mxid == temporary_identifier, - DBMessage.tg_space == tg_space) \ + DBMessage.mxid == temporary_identifier) \ .update({"mxid": mxid}) self.db.add(DBMessage(tgid=evt.id, mx_room=self.mxid, mxid=mxid, tg_space=tg_space)) self.db.commit()