فهرست منبع

Add relaybot functionality for bridge user

a000a 4 سال پیش
والد
کامیت
52312e46ed
5فایلهای تغییر یافته به همراه94 افزوده شده و 0 حذف شده
  1. 19 0
      mautrix_signal/config.py
  2. 18 0
      mautrix_signal/example-config.yaml
  3. 3 0
      mautrix_signal/matrix.py
  4. 53 0
      mautrix_signal/portal.py
  5. 1 0
      mautrix_signal/user.py

+ 19 - 0
mautrix_signal/config.py

@@ -99,6 +99,10 @@ class Config(BaseBridgeConfig):
 
         copy_dict("bridge.permissions")
 
+        copy("bridge.relaybot.enable")
+        copy("bridge.relaybot.users")
+        copy_dict("bridge.relaybot.message_formats")
+
     def _get_permissions(self, key: str) -> Permissions:
         level = self["bridge.permissions"].get(key, "")
         admin = level == "admin"
@@ -115,3 +119,18 @@ class Config(BaseBridgeConfig):
             return self._get_permissions(homeserver)
 
         return self._get_permissions("*")
+
+    def get_relay_users(self, mxid: UserID) -> Permissions:
+        relay_users = self["bridge.relaybot.users"]
+        if not isinstance(relay_users, list):
+            return True
+        if len(relay_users) == 0:
+            return True
+        if mxid in relay_users:
+            return True
+
+        _, homeserver = Client.parse_user_id(mxid)
+        if homeserver in relay_users: 
+            return True
+
+        return False

+ 18 - 0
mautrix_signal/example-config.yaml

@@ -197,6 +197,24 @@ bridge:
         "example.com": "user"
         "@admin:example.com": "admin"
 
+    relaybot:
+        enable: false
+        # The formats to use when sending messages to Signal via the relaybot.
+        # (markdown/html is not supported by signal yet.)
+        message_formats:
+            m.text: '*{{ .Sender.Displayname }}*: {{ .Message }}'
+            m.notice: '*{{ .Sender.Displayname }}*: {{ .Message }}'
+            m.emote: '*{{ .Sender.Displayname }}* {{ .Message }}'
+            m.file: '*{{ .Sender.Displayname }}* sent a file'
+            m.image: '*{{ .Sender.Displayname }}* sent an image'
+            m.audio: '*{{ .Sender.Displayname }}* sent an audio file'
+            m.video: '*{{ .Sender.Displayname }}* sent a video'
+            m.location: '*{{ .Sender.Displayname }}* sent a location'
+        # Users that may function as relaybot, means messages in signal are sent by these users.
+        # If empty all users may function as relaybot. Only the last reaction can be handled by signal.
+        users:
+            - '@signal_relaybot:example.com'
+
 
 # Python logging configuration.
 #

+ 3 - 0
mautrix_signal/matrix.py

@@ -162,3 +162,6 @@ class MatrixHandler(BaseMatrixHandler):
             await portal.handle_matrix_name(user, evt.content.name)
         elif evt.type == EventType.ROOM_AVATAR:
             await portal.handle_matrix_avatar(user, evt.content.url)
+
+    async def allow_bridging_message(self, user: 'BaseUser', portal: 'BasePortal') -> bool:
+        return self.config['bridge.relaybot.enable'] or await user.is_logged_in()

+ 53 - 0
mautrix_signal/portal.py

@@ -25,6 +25,10 @@ import os.path
 import time
 import os
 
+# for relaybot
+from html import escape as escape_html
+from string import Template
+
 from mausignald.types import (Address, MessageData, Reaction, Quote, Group, Contact, Profile,
                               Attachment, GroupID, GroupV2ID, GroupV2, Mention, Sticker,
                               GroupAccessControl, AccessControlMode, GroupMemberRole)
@@ -33,6 +37,7 @@ from mautrix.appservice import AppService, IntentAPI
 from mautrix.bridge import BasePortal, async_getter_lock
 from mautrix.types import (EventID, MessageEventContent, RoomID, EventType, MessageType,
                            MessageEvent, EncryptedEvent, ContentURI, MediaMessageEventContent,
+                           UserID, TextMessageEventContent, Format, #for relaybot
                            ImageInfo, VideoInfo, FileInfo, AudioInfo, PowerLevelStateEventContent)
 from mautrix.errors import MatrixError, MForbidden, IntentError
 
@@ -187,12 +192,44 @@ class Portal(DBPortal, BasePortal):
             data = await self.main_intent.download_media(message.url)
         return self._write_outgoing_file(data)
 
+    async def get_displayname(self, user: 'u.User') -> str:
+        return await self.main_intent.get_room_displayname(self.mxid, user.mxid) or user.mxid
+
+    async def _apply_msg_format(self, sender: 'u.User', content: MessageEventContent
+                                ) -> None:
+        if not isinstance(content, TextMessageEventContent) or content.format != Format.HTML:
+            content.format = Format.HTML
+            content.formatted_body = escape_html(content.body).replace("\n", "<br/>")
+ 
+        tpl = (self.config[f"relaybot.message_formats.[{content.msgtype.value}]"]
+               or "*$sender_displayname*: $message")
+        displayname = await self.get_displayname(sender)
+        tpl_args = dict(sender_mxid=sender.mxid,
+                        sender_username=sender.mxid, #TODO
+                        sender_displayname=escape_html(displayname),
+                        message=content.formatted_body,
+                        body=content.body, formatted_body=content.formatted_body)             
+        content.formatted_body = Template(tpl).safe_substitute(tpl_args)
+        content.body = Template(tpl).safe_substitute(tpl_args)
+
     async def handle_matrix_message(self, sender: 'u.User', message: MessageEventContent,
                                     event_id: EventID) -> None:
         if ((message.get(self.bridge.real_user_content_key, False)
              and await p.Puppet.get_by_custom_mxid(sender.mxid))):
             self.log.debug(f"Ignoring puppet-sent message by confirmed puppet user {sender.mxid}")
             return
+        if not await sender.is_logged_in() and self.config['bridge.relaybot.enable']:
+            self.log.trace(f"Message sent by non signal-user {sender.mxid}")
+            async for user in u.User.all_logged_in():
+                await self._apply_msg_format(sender, message)
+                if await user.is_in_portal(self) and user.is_relaybot:
+                    await self._handle_matrix_message(user, message, event_id, True)
+                    return
+        else:
+            await self._handle_matrix_message(sender, message, event_id)
+
+    async def _handle_matrix_message(self, sender: 'u.User', message: MessageEventContent,
+            event_id: EventID, relay_sender = None) -> None:
         request_id = int(time.time() * 1000)
         self._msgts_dedup.appendleft((sender.address, request_id))
 
@@ -213,6 +250,9 @@ class Portal(DBPortal, BasePortal):
             attachment = self._make_attachment(message, attachment_path)
             attachments = [attachment]
             text = None
+            if relay_sender:
+                self.log.trace(f"Format text for relay")
+                text = message.body
             self.log.trace("Formed outgoing attachment %s", attachment)
         else:
             self.log.debug(f"Unknown msgtype {message.msgtype} in Matrix message {event_id}")
@@ -243,6 +283,19 @@ class Portal(DBPortal, BasePortal):
             self.log.debug(f"Ignoring reaction to unknown event {reacting_to}")
             return
 
+        if not await sender.is_logged_in() and self.config['bridge.relaybot.enable']:
+            self.log.trace(f"Reaction by non signal-user {sender.mxid}")
+            react_permitted = False
+            async for user in u.User.all_logged_in():
+                if await user.is_in_portal(self) and user.is_relaybot:
+                    self.log.trace(f"Set new sender to {user.mxid}")
+                    react_permitted = True
+                    sender=user
+                    break
+            if not react_permitted:
+                self.log.debug(f"Reaction not permitted.")
+                return
+
         existing = await DBReaction.get_by_signal_id(self.chat_id, self.receiver, message.sender,
                                                      message.timestamp, sender.address)
         if existing and existing.emoji == emoji:

+ 1 - 0
mautrix_signal/user.py

@@ -64,6 +64,7 @@ class User(DBUser, BaseUser):
         self._connected = False
         perms = self.config.get_permissions(mxid)
         self.is_whitelisted, self.is_admin, self.permission_level = perms
+        self.is_relaybot = self.config.get_relay_users(mxid)
 
     @classmethod
     def init_cls(cls, bridge: 'SignalBridge') -> None: