コード例 #1
0
ファイル: portal.py プロジェクト: hummans/mautrix-instagram
    async def _add_instagram_reply(self, content: MessageEventContent,
                                   reply_to: Optional[ThreadItem]) -> None:
        if not reply_to:
            return

        message = await DBMessage.get_by_item_id(reply_to.item_id, self.receiver)
        if not message:
            return

        content.set_reply(message.mxid)
        if not isinstance(content, TextMessageEventContent):
            return

        try:
            evt = await self.main_intent.get_event(message.mx_room, message.mxid)
        except (MNotFound, MForbidden):
            evt = None
        if not evt:
            return

        if evt.type == EventType.ROOM_ENCRYPTED:
            try:
                evt = await self.matrix.e2ee.decrypt(evt, wait_session_timeout=0)
            except SessionNotFound:
                return

        if isinstance(evt.content, TextMessageEventContent):
            evt.content.trim_reply_fallback()

        content.set_reply(evt)
コード例 #2
0
ファイル: matrix.py プロジェクト: t2bot/mautrix-python
    async def handle_message(self, room_id: RoomID, user_id: UserID, message: MessageEventContent,
                             event_id: EventID) -> None:
        sender = await self.bridge.get_user(user_id)
        if not sender or not await self.allow_message(sender):
            self.log.debug(f"Ignoring message {event_id} from {user_id} to {room_id}:"
                           " User is not whitelisted.")
            return
        self.log.debug(f"Received Matrix event {event_id} from {sender.mxid} in {room_id}")
        self.log.trace("Event %s content: %s", event_id, message)

        if isinstance(message, TextMessageEventContent):
            message.trim_reply_fallback()

        is_command, text = self.is_command(message)
        portal = await self.bridge.get_portal(room_id)
        if not is_command and portal and await self.allow_bridging_message(sender, portal):
            await portal.handle_matrix_message(sender, message, event_id)
            return

        if message.msgtype != MessageType.TEXT or not await self.allow_command(sender):
            return

        is_management = await self.is_management(room_id)

        if is_command or is_management:
            try:
                command, arguments = text.split(" ", 1)
                args = arguments.split(" ")
            except ValueError:
                # Not enough values to unpack, i.e. no arguments
                command = text
                args = []
            await self.commands.handle(room_id, event_id, sender, command, args, message,
                                       is_management, is_portal=portal is not None)
コード例 #3
0
 async def apply_relay_message_format(
     self, sender: br.BaseUser, content: MessageEventContent
 ) -> None:
     if self.relay_formatted_body and content.get("format", None) != Format.HTML:
         content["format"] = Format.HTML
         content["formatted_body"] = html.escape(content.body).replace("\n", "<br/>")
     tpl = self.bridge.config["bridge.relay.message_formats"].get(
         content.msgtype.value, "$sender_displayname: $message"
     )
     displayname = await self.get_displayname(sender)
     username, _ = self.az.intent.parse_user_id(sender.mxid)
     tpl_args = {
         "sender_mxid": sender.mxid,
         "sender_username": username,
         "sender_displayname": html.escape(displayname),
         "formatted_body": content["formatted_body"],
         "body": content.body,
         "message": content.body,
     }
     content.body = Template(tpl).safe_substitute(tpl_args)
     if self.relay_formatted_body and "formatted_body" in content:
         tpl_args["message"] = content["formatted_body"]
         content["formatted_body"] = Template(tpl).safe_substitute(tpl_args)
     if self.relay_emote_to_text and content.msgtype == MessageType.EMOTE:
         content.msgtype = MessageType.TEXT
コード例 #4
0
 async def _matrix_document_edit(self, client: 'MautrixTelegramClient',
                                 content: MessageEventContent, space: TelegramID,
                                 caption: str, media: Any, event_id: EventID) -> bool:
     if content.get_edit():
         orig_msg = DBMessage.get_by_mxid(content.get_edit(), self.mxid, space)
         if orig_msg:
             response = await client.edit_message(self.peer, orig_msg.tgid,
                                                  caption, file=media)
             self._add_telegram_message_to_db(event_id, space, -1, response)
             return True
     return False
コード例 #5
0
ファイル: portal.py プロジェクト: witchent/mautrix-signal
    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
        request_id = int(time.time() * 1000)
        self._msgts_dedup.appendleft((sender.uuid, request_id))

        quote = None
        if message.get_reply_to():
            reply = await DBMessage.get_by_mxid(message.get_reply_to(),
                                                self.mxid)
            # TODO include actual text? either store in db or fetch event from homeserver
            quote = Quote(id=reply.timestamp,
                          author=Address(uuid=reply.sender),
                          text="")

        text = message.body
        attachments: Optional[List[Attachment]] = None
        attachment_path: Optional[str] = None
        if message.msgtype == MessageType.EMOTE:
            text = f"/me {text}"
        elif message.msgtype.is_media:
            attachment_path = await self._download_matrix_media(message)
            attachment = self._make_attachment(message, attachment_path)
            attachments = [attachment]
            text = None
            self.log.trace("Formed outgoing attachment %s", attachment)
        await self.signal.send(username=sender.username,
                               recipient=self.recipient,
                               body=text,
                               quote=quote,
                               attachments=attachments,
                               timestamp=request_id)
        msg = DBMessage(mxid=event_id,
                        mx_room=self.mxid,
                        sender=sender.uuid,
                        timestamp=request_id,
                        signal_chat_id=self.chat_id,
                        signal_receiver=self.receiver)
        await msg.insert()
        await self._send_delivery_receipt(event_id)
        self.log.debug(f"Handled Matrix message {event_id} -> {request_id}")
        if attachment_path and self.config["signal.remove_file_after_handling"]:
            try:
                os.remove(attachment_path)
            except FileNotFoundError:
                pass
コード例 #6
0
ファイル: __init__.py プロジェクト: tulir/mautrix-telegram
async def matrix_reply_to_telegram(
        content: MessageEventContent,
        tg_space: TelegramID,
        room_id: RoomID | None = None) -> TelegramID | None:
    event_id = content.get_reply_to()
    if not event_id:
        return
    content.trim_reply_fallback()

    message = await DBMessage.get_by_mxid(event_id, room_id, tg_space)
    if message:
        return message.tgid
    return None
コード例 #7
0
def matrix_reply_to_telegram(
        content: MessageEventContent,
        tg_space: TelegramID,
        room_id: Optional[RoomID] = None) -> Optional[TelegramID]:
    event_id = content.get_reply_to()
    if not event_id:
        return
    content.trim_reply_fallback()

    message = DBMessage.get_by_mxid(event_id, room_id, tg_space)
    if message:
        return message.tgid
    return None
コード例 #8
0
    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.get_config(f"message_formats.[{content.msgtype.value}]")
               or "<b>$sender_displayname</b>: $message")
        displayname = await self.get_displayname(sender)
        tpl_args = dict(sender_mxid=sender.mxid,
                        sender_username=sender.mxid_localpart,
                        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)
コード例 #9
0
ファイル: portal.py プロジェクト: hooger/mautrix-hangouts
 async def handle_matrix_message(self, sender: 'u.User',
                                 message: MessageEventContent,
                                 event_id: EventID) -> None:
     puppet = p.Puppet.get_by_custom_mxid(sender.mxid)
     if puppet and message.get("net.maunium.hangouts.puppet", False):
         self.log.debug(
             f"Ignoring puppet-sent message by confirmed puppet user {sender.mxid}"
         )
         return
     # TODO this probably isn't nice for bridging images, it really only needs to lock the
     #      actual message send call and dedup queue append.
     async with self.require_send_lock(sender.gid):
         if message.msgtype == MessageType.TEXT or message.msgtype == MessageType.NOTICE:
             gid = await self._handle_matrix_text(sender, message)
         elif message.msgtype == MessageType.EMOTE:
             gid = await self._handle_matrix_emote(sender, message)
         elif message.msgtype == MessageType.IMAGE:
             gid = await self._handle_matrix_image(sender, message)
         # elif message.msgtype == MessageType.LOCATION:
         #     gid = await self._handle_matrix_location(sender, message)
         else:
             self.log.warning(f"Unsupported msgtype in {message}")
             return
         if not gid:
             return
         self._dedup.appendleft(gid)
         DBMessage(mxid=event_id,
                   mx_room=self.mxid,
                   gid=gid,
                   receiver=self.receiver,
                   index=0).insert()
         self._last_bridged_mxid = event_id
     await self._send_delivery_receipt(event_id)
コード例 #10
0
 async def _pre_process_matrix_message(
         self, sender: 'u.User', use_relaybot: bool,
         content: MessageEventContent) -> None:
     if content.msgtype == MessageType.EMOTE:
         await self._apply_msg_format(sender, content)
         content.msgtype = MessageType.TEXT
     elif use_relaybot:
         await self._apply_msg_format(sender, content)
コード例 #11
0
    async def handle_matrix_message(self, sender: 'u.User', content: MessageEventContent,
                                    event_id: EventID) -> None:
        if not content.body or not content.msgtype:
            self.log.debug(f"Ignoring message {event_id} in {self.mxid} without body or msgtype")
            return

        puppet = p.Puppet.get_by_custom_mxid(sender.mxid)
        if puppet and content.get("net.maunium.telegram.puppet", False):
            self.log.debug("Ignoring puppet-sent message by confirmed puppet user %s", sender.mxid)
            return

        logged_in = not await sender.needs_relaybot(self)
        client = sender.client if logged_in else self.bot.client
        sender_id = sender.tgid if logged_in else self.bot.tgid
        space = (self.tgid if self.peer_type == "channel"  # Channels have their own ID space
                 else (sender.tgid if logged_in else self.bot.tgid))
        reply_to = formatter.matrix_reply_to_telegram(content, space, room_id=self.mxid)

        media = (MessageType.STICKER, MessageType.IMAGE, MessageType.FILE, MessageType.AUDIO,
                 MessageType.VIDEO)

        if content.msgtype == MessageType.NOTICE:
            bridge_notices = self.get_config("bridge_notices.default")
            excepted = sender.mxid in self.get_config("bridge_notices.exceptions")
            if not bridge_notices and not excepted:
                return

        if content.msgtype in (MessageType.TEXT, MessageType.EMOTE, MessageType.NOTICE):
            await self._pre_process_matrix_message(sender, not logged_in, content)
            await self._handle_matrix_text(sender_id, event_id, space, client, content, reply_to)
        elif content.msgtype == MessageType.LOCATION:
            await self._pre_process_matrix_message(sender, not logged_in, content)
            await self._handle_matrix_location(sender_id, event_id, space, client, content,
                                               reply_to)
        elif content.msgtype in media:
            content["net.maunium.telegram.internal.filename"] = content.body
            try:
                caption_content: MessageEventContent = sender.command_status["caption"]
                reply_to = reply_to or formatter.matrix_reply_to_telegram(caption_content, space,
                                                                          room_id=self.mxid)
                sender.command_status = None
            except (KeyError, TypeError):
                caption_content = None if logged_in else TextMessageEventContent(body=content.body)
            if caption_content:
                caption_content.msgtype = content.msgtype
                await self._pre_process_matrix_message(sender, not logged_in, caption_content)
            await self._handle_matrix_file(sender_id, event_id, space, client, content, reply_to,
                                           caption_content)
        else:
            self.log.debug(f"Unhandled Matrix event: {content}")
コード例 #12
0
ファイル: portal.py プロジェクト: kubesail/mautrix-twitter
 async def handle_matrix_message(self, sender: 'u.User',
                                 message: MessageEventContent,
                                 event_id: EventID) -> None:
     if not sender.client:
         self.log.debug(
             f"Ignoring message {event_id} as user is not connected")
         return
     elif ((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
     request_id = str(sender.client.new_request_id())
     self._reqid_dedup.add(request_id)
     text = message.body
     media_id = None
     if message.msgtype == MessageType.EMOTE:
         text = f"/me {text}"
     elif message.msgtype.is_media:
         if message.file and decrypt_attachment:
             data = await self.main_intent.download_media(message.file.url)
             data = decrypt_attachment(data, message.file.key.key,
                                       message.file.hashes.get("sha256"),
                                       message.file.iv)
         else:
             data = await self.main_intent.download_media(message.url)
         mime_type = message.info.mimetype or magic.from_buffer(data,
                                                                mime=True)
         # TODO this will throw errors if the mime type is not supported
         #      those errors should be sent back to the client
         upload_resp = await sender.client.upload(data, mime_type=mime_type)
         media_id = upload_resp.media_id
         text = ""
     resp = await sender.client.conversation(self.twid
                                             ).send(text,
                                                    media_id=media_id,
                                                    request_id=request_id)
     resp_msg_id = int(resp.entries[0].message.id)
     self._msgid_dedup.appendleft(resp_msg_id)
     msg = DBMessage(mxid=event_id,
                     mx_room=self.mxid,
                     twid=resp_msg_id,
                     receiver=self.receiver)
     await msg.insert()
     self._reqid_dedup.remove(request_id)
     await self._send_delivery_receipt(event_id)
     self.log.debug(f"Handled Matrix message {event_id} -> {resp_msg_id}")
コード例 #13
0
ファイル: portal.py プロジェクト: hummans/mautrix-instagram
 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
     elif not sender.is_connected:
         await self._send_bridge_error("You're not connected to Instagram", confirmed=True)
         return
     else:
         self.log.debug(f"Handling Matrix message {event_id} from {sender.mxid}/{sender.igpk}")
     request_id = str(uuid4())
     self._reqid_dedup.add(request_id)
     if message.msgtype in (MessageType.EMOTE, MessageType.TEXT):
         text = message.body
         if message.msgtype == MessageType.EMOTE:
             text = f"/me {text}"
         self.log.trace(f"Sending Matrix text from {event_id} with request ID {request_id}")
         resp = await sender.mqtt.send_text(self.thread_id, text=text,
                                            client_context=request_id)
     elif message.msgtype.is_media:
         if message.file and decrypt_attachment:
             data = await self.main_intent.download_media(message.file.url)
             data = decrypt_attachment(data, message.file.key.key,
                                       message.file.hashes.get("sha256"), message.file.iv)
         else:
             data = await self.main_intent.download_media(message.url)
         mime_type = message.info.mimetype or magic.from_buffer(data, mime=True)
         if mime_type != "image/jpeg" and mime_type.startswith("image/"):
             with BytesIO(data) as inp:
                 img = Image.open(inp)
                 with BytesIO() as out:
                     img.convert("RGB").save(out, format="JPEG", quality=80)
                     data = out.getvalue()
             mime_type = "image/jpeg"
         if mime_type == "image/jpeg":
             self.log.trace(f"Uploading photo from {event_id}")
             upload_resp = await sender.client.upload_jpeg_photo(data)
             self.log.trace(f"Broadcasting uploaded photo with request ID {request_id}")
             # TODO is it possible to do this with MQTT?
             resp = await sender.client.broadcast(self.thread_id,
                                                  ThreadItemType.CONFIGURE_PHOTO,
                                                  client_context=request_id,
                                                  upload_id=upload_resp.upload_id,
                                                  allow_full_aspect_ratio="1")
         else:
             await self._send_bridge_error("Non-image files are currently not supported",
                                           confirmed=True)
             return
     else:
         self.log.debug(f"Unhandled Matrix message {event_id}: "
                        f"unknown msgtype {message.msgtype}")
         return
     self.log.trace(f"Got response to message send {request_id}: {resp}")
     if resp.status != "ok":
         self.log.warning(f"Failed to handle {event_id}: {resp}")
         await self._send_bridge_error(resp.payload.message)
     else:
         self._msgid_dedup.appendleft(resp.payload.item_id)
         await DBMessage(mxid=event_id, mx_room=self.mxid, item_id=resp.payload.item_id,
                         receiver=self.receiver, sender=sender.igpk).insert()
         self._reqid_dedup.remove(request_id)
         await self._send_delivery_receipt(event_id)
         self.log.debug(f"Handled Matrix message {event_id} -> {resp.payload.item_id}")
コード例 #14
0
ファイル: matrix.py プロジェクト: tulir/mautrix-python
    async def handle_message(
        self, room_id: RoomID, user_id: UserID, message: MessageEventContent, event_id: EventID
    ) -> None:
        async def bail(error_text: str, step=MessageSendCheckpointStep.REMOTE) -> None:
            self.log.debug(error_text)
            await MessageSendCheckpoint(
                event_id=event_id,
                room_id=room_id,
                step=step,
                timestamp=int(time.time() * 1000),
                status=MessageSendCheckpointStatus.PERM_FAILURE,
                reported_by=MessageSendCheckpointReportedBy.BRIDGE,
                event_type=EventType.ROOM_MESSAGE,
                message_type=message.msgtype,
                info=error_text,
            ).send(
                self.bridge.config["homeserver.message_send_checkpoint_endpoint"],
                self.az.as_token,
                self.log,
            )

        sender = await self.bridge.get_user(user_id)
        if not sender or not await self.allow_message(sender):
            await bail(
                f"Ignoring message {event_id} from {user_id} to {room_id}:"
                " user is not whitelisted."
            )
            return
        self.log.debug(f"Received Matrix event {event_id} from {sender.mxid} in {room_id}")
        self.log.trace("Event %s content: %s", event_id, message)

        if isinstance(message, TextMessageEventContent):
            message.trim_reply_fallback()

        is_command, text = self.is_command(message)
        portal = await self.bridge.get_portal(room_id)
        if not is_command and portal:
            if await self.allow_bridging_message(sender, portal):
                await portal.handle_matrix_message(sender, message, event_id)
            else:
                await bail(
                    f"Ignoring event {event_id} from {sender.mxid}:"
                    " not allowed to send to portal"
                )
            return

        if message.msgtype != MessageType.TEXT:
            await bail(f"Ignoring event {event_id}: not a portal room and not a m.text message")
            return
        elif not await self.allow_command(sender):
            await bail(
                f"Ignoring command {event_id} from {sender.mxid}: not allowed to perform command",
                step=MessageSendCheckpointStep.COMMAND,
            )
            return

        has_two_members, bridge_bot_in_room = await self._is_direct_chat(room_id)
        is_management = has_two_members and bridge_bot_in_room

        if is_command or is_management:
            try:
                command, arguments = text.split(" ", 1)
                args = arguments.split(" ")
            except ValueError:
                # Not enough values to unpack, i.e. no arguments
                command = text
                args = []

            try:
                await self.commands.handle(
                    room_id,
                    event_id,
                    sender,
                    command,
                    args,
                    message,
                    portal,
                    is_management,
                    bridge_bot_in_room,
                )
            except Exception as e:
                await bail(repr(e), step=MessageSendCheckpointStep.COMMAND)
            else:
                await MessageSendCheckpoint(
                    event_id=event_id,
                    room_id=room_id,
                    step=MessageSendCheckpointStep.COMMAND,
                    timestamp=int(time.time() * 1000),
                    status=MessageSendCheckpointStatus.SUCCESS,
                    reported_by=MessageSendCheckpointReportedBy.BRIDGE,
                    event_type=EventType.ROOM_MESSAGE,
                    message_type=message.msgtype,
                ).send(
                    self.bridge.config["homeserver.message_send_checkpoint_endpoint"],
                    self.az.as_token,
                    self.log,
                )
        else:
            await bail(
                f"Ignoring event {event_id} from {sender.mxid}:"
                " not a command and not a portal room"
            )