From e7efc33a54169486e4003fb3fc6255eb1e18b302 Mon Sep 17 00:00:00 2001
From: Tulir Asokan <tulir@maunium.net>
Date: Sat, 3 Aug 2019 16:21:46 +0300
Subject: [PATCH] Add Matrix -> Messenger reply and mention bridging

---
 ROADMAP.md                                |  4 +--
 mautrix_facebook/formatter/from_matrix.py | 41 ++++++++++++++++++++---
 mautrix_facebook/portal.py                |  4 ++-
 3 files changed, 41 insertions(+), 8 deletions(-)

diff --git a/ROADMAP.md b/ROADMAP.md
index 9570fb4..2026b28 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -11,8 +11,8 @@
       * [x] Images
       * [ ] Locations
     * [x] Formatting
-    * [ ] Replies
-    * [ ] Mentions
+    * [x] Replies
+    * [x] Mentions
   * [x] Message redactions
   * [x] Message reactions
   * [x] Presence
diff --git a/mautrix_facebook/formatter/from_matrix.py b/mautrix_facebook/formatter/from_matrix.py
index 3784ea1..2e3d487 100644
--- a/mautrix_facebook/formatter/from_matrix.py
+++ b/mautrix_facebook/formatter/from_matrix.py
@@ -13,12 +13,17 @@
 #
 # You should have received a copy of the GNU Affero General Public License
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
+from typing import Optional
 from enum import Enum
+import re
 
-from fbchat.models import Message
+from fbchat.models import Message, Mention
 
-from mautrix.types import TextMessageEventContent, Format
-from mautrix.util.formatter import MatrixParser as BaseMatrixParser
+from mautrix.types import TextMessageEventContent, Format, UserID, RoomID, RelationType
+from mautrix.util.formatter import MatrixParser as BaseMatrixParser, FormattedString
+
+from .. import puppet as pu, user as u
+from ..db import Message as DBMessage
 
 
 class EntityType(Enum):
@@ -48,13 +53,39 @@ class EntityType(Enum):
         return text
 
 
+MENTION_REGEX = re.compile(r"@([0-9]{15})\u2063(.+)\u2063")
+
+
 class MatrixParser(BaseMatrixParser):
     e = EntityType
 
+    @classmethod
+    def user_pill_to_fstring(cls, msg: FormattedString, user_id: UserID
+                             ) -> Optional[FormattedString]:
+        user = u.User.get_by_mxid(user_id, create=False)
+        if user and user.fbid:
+            return FormattedString(f"@{user.fbid}\u2063{msg.text}\u2063")
+        puppet = pu.Puppet.get_by_mxid(user_id, create=False)
+        if puppet:
+            return FormattedString(f"@{puppet.fbid}\u2063{puppet.name or msg.text}\u2063")
+        return msg
+
 
-def matrix_to_facebook(content: TextMessageEventContent) -> Message:
+def matrix_to_facebook(content: TextMessageEventContent, room_id: RoomID) -> Message:
+    mentions = []
+    reply_to_id = None
+    if content.relates_to.rel_type == RelationType.REFERENCE:
+        message = DBMessage.get_by_mxid(content.relates_to.event_id, room_id)
+        if message:
+            content.trim_reply_fallback()
+            reply_to_id = message.fbid
     if content.format == Format.HTML and content.formatted_body:
         text = MatrixParser.parse(content.formatted_body).text
+        for mention in MENTION_REGEX.finditer(text):
+            fbid, name = mention.groups()
+            start, end = mention.start(), mention.end()
+            text = f"{text[:start]}{name}{text[end:]}"
+            mentions.append(Mention(thread_id=fbid, offset=start, length=len(name)))
     else:
         text = content.body
-    return Message(text=text)
+    return Message(text=text, mentions=mentions, reply_to_id=reply_to_id)
diff --git a/mautrix_facebook/portal.py b/mautrix_facebook/portal.py
index 16d7732..e058b76 100644
--- a/mautrix_facebook/portal.py
+++ b/mautrix_facebook/portal.py
@@ -362,7 +362,7 @@ class Portal:
             self._last_bridged_mxid = event_id
 
     async def _handle_matrix_text(self, sender: 'u.User', message: TextMessageEventContent) -> str:
-        return await sender.send(matrix_to_facebook(message), self.fbid, self.fb_type)
+        return await sender.send(matrix_to_facebook(message, self.mxid), self.fbid, self.fb_type)
 
     async def _handle_matrix_image(self, sender: 'u.User',
                                    message: MediaMessageEventContent) -> str:
@@ -496,6 +496,8 @@ class Portal:
             if message:
                 evt = await self.main_intent.get_event(message.mx_room, message.mxid)
                 if evt:
+                    if isinstance(evt.content, TextMessageEventContent):
+                        evt.content.trim_reply_fallback()
                     content.set_reply(evt)
 
     def _get_facebook_reply(self, reply: str) -> Optional[RelatesTo]:
-- 
GitLab