Ejemplo n.º 1
0
def facebook_to_matrix(message: Message) -> TextMessageEventContent:
    content = TextMessageEventContent(msgtype=MessageType.TEXT,
                                      body=message.text)
    text = message.text
    for m in reversed(message.mentions):
        original = text[m.offset:m.offset + m.length]
        if len(original) > 0 and original[0] == "@":
            original = original[1:]
        text = f"{text[:m.offset]}@{m.thread_id}\u2063{original}\u2063{text[m.offset + m.length:]}"
    html = escape(text)
    output = []
    codeblock = False
    blockquote = False
    line: str
    lines = html.split("\n")
    for i, line in enumerate(lines):
        blockquote, line = _handle_blockquote(output, blockquote, line)
        codeblock, line, post_args = _handle_codeblock_pre(
            output, codeblock, line)
        output.append(_convert_formatting(line))
        if i != len(lines) - 1:
            output.append("<br/>")
        _handle_codeblock_post(output, *post_args)
    html = "".join(output)

    html = MENTION_REGEX.sub(_mention_replacer, html)
    if html != escape(content.body).replace("\n", "<br/>"):
        content.format = Format.HTML
        content.formatted_body = html
    return content
Ejemplo n.º 2
0
 async def send_bridge_notice(
     self,
     text: str,
     edit: EventID | None = None,
     state_event: BridgeStateEvent | None = None,
     important: bool = False,
     error_code: str | None = None,
     error_message: str | None = None,
 ) -> EventID | None:
     if state_event:
         await self.push_bridge_state(
             state_event, error=error_code, message=error_message if error_code else text
         )
     if self.config["bridge.disable_bridge_notices"]:
         return None
     if not important and not self.config["bridge.unimportant_bridge_notices"]:
         self.log.debug("Not sending unimportant bridge notice: %s", text)
         return None
     event_id = None
     try:
         self.log.debug("Sending bridge notice: %s", text)
         content = TextMessageEventContent(
             body=text, msgtype=(MessageType.TEXT if important else MessageType.NOTICE)
         )
         if edit:
             content.set_edit(edit)
         # This is locked to prevent notices going out in the wrong order
         async with self._notice_send_lock:
             event_id = await self.az.intent.send_message(await self.get_notice_room(), content)
     except Exception:
         self.log.warning("Failed to send bridge notice", exc_info=True)
     return edit or event_id
Ejemplo n.º 3
0
async def signal_to_matrix(message: MessageData) -> TextMessageEventContent:
    content = TextMessageEventContent(msgtype=MessageType.TEXT,
                                      body=message.body)
    surrogated_text = add_surrogate(message.body)
    if message.mentions:
        text_chunks = []
        html_chunks = []
        last_offset = 0
        for mention in message.mentions:
            before = surrogated_text[last_offset:mention.start]
            last_offset = mention.start + mention.length

            text_chunks.append(before)
            html_chunks.append(escape(before))
            puppet = await pu.Puppet.get_by_address(Address(uuid=mention.uuid))
            name = add_surrogate(puppet.name or puppet.mxid)
            text_chunks.append(name)
            html_chunks.append(
                f'<a href="https://matrix.to/#/{puppet.mxid}">{name}</a>')
        end = surrogated_text[last_offset:]
        text_chunks.append(end)
        html_chunks.append(escape(end))
        content.body = del_surrogate("".join(text_chunks))
        content.format = Format.HTML
        content.formatted_body = del_surrogate("".join(html_chunks))
    return content
Ejemplo n.º 4
0
async def matrix_to_signal(
        content: TextMessageEventContent) -> Tuple[str, List[Mention]]:
    if content.msgtype == MessageType.EMOTE:
        content.body = f"/me {content.body}"
        if content.formatted_body:
            content.formatted_body = f"/me {content.formatted_body}"
    mentions = []
    if content.format == Format.HTML and content.formatted_body:
        parsed = MatrixParser.parse(add_surrogate(content.formatted_body))
        text = del_surrogate(parsed.text)
        for mention in parsed.entities:
            mxid = mention.extra_info["user_id"]
            user = await u.User.get_by_mxid(mxid, create=False)
            if user and user.uuid:
                uuid = user.uuid
            else:
                puppet = await pu.Puppet.get_by_mxid(mxid, create=False)
                if puppet:
                    uuid = puppet.uuid
                else:
                    continue
            mentions.append(
                Mention(uuid=uuid, start=mention.offset,
                        length=mention.length))
    else:
        text = content.body
    return text, mentions
Ejemplo n.º 5
0
async def matrix_to_facebook(content: TextMessageEventContent, room_id: RoomID) -> SendParams:
    mentions = []
    reply_to = None
    if content.relates_to.rel_type == RelationType.REPLY:
        message = await DBMessage.get_by_mxid(content.relates_to.event_id, room_id)
        if message:
            content.trim_reply_fallback()
            reply_to = message.fbid
    if content.format == Format.HTML and content.formatted_body:
        parsed = MatrixParser.parse(content.formatted_body)
        text = parsed.text
        mentions = []
        for mention in parsed.entities:
            mxid = mention.extra_info["user_id"]
            user = await u.User.get_by_mxid(mxid, create=False)
            if user and user.fbid:
                fbid = user.fbid
            else:
                puppet = await pu.Puppet.get_by_mxid(mxid, create=False)
                if puppet:
                    fbid = puppet.fbid
                else:
                    continue
            mentions.append(Mention(user_id=str(fbid), offset=mention.offset,
                                    length=mention.length))
    else:
        text = content.body
    return SendParams(text=text, mentions=mentions, reply_to=reply_to)
Ejemplo n.º 6
0
Archivo: echo.py Proyecto: maubot/echo
 async def ping_handler(self, evt: MessageEvent, message: str = "") -> None:
     diff = int(time() * 1000) - evt.timestamp
     pretty_diff = self.prettify_diff(diff)
     text_message = f'"{message[:20]}" took' if message else "took"
     html_message = f'"{escape(message[:20])}" took' if message else "took"
     content = TextMessageEventContent(
         msgtype=MessageType.NOTICE,
         format=Format.HTML,
         body=
         f"{evt.sender}: Pong! (ping {text_message} {pretty_diff} to arrive)",
         formatted_body=
         f"<a href='https://matrix.to/#/{evt.sender}'>{evt.sender}</a>: Pong! "
         f"(<a href='https://matrix.to/#/{evt.room_id}/{evt.event_id}'>ping</a> {html_message} "
         f"{pretty_diff} to arrive)",
         relates_to=RelatesTo(
             rel_type=RelationType("xyz.maubot.pong"),
             event_id=evt.event_id,
         ))
     pong_from = evt.sender.split(":", 1)[1]
     content.relates_to["from"] = pong_from
     content.relates_to["ms"] = diff
     content["pong"] = {
         "ms": diff,
         "from": pong_from,
         "ping": evt.event_id,
     }
     await evt.respond(content)
Ejemplo n.º 7
0
 async def _send_reminder(self, reminder: ReminderInfo) -> None:
     if len(reminder.users) == 0:
         self.log.debug(
             f"Cancelling reminder {reminder}, no users left to remind")
         return
     wait = (reminder.date - datetime.now(tz=pytz.UTC)).total_seconds()
     if wait > 0:
         self.log.debug(f"Waiting {wait} seconds to send {reminder}")
         await asyncio.sleep(wait)
     else:
         self.log.debug(f"Sending {reminder} immediately")
     users = " ".join(reminder.users)
     users_html = " ".join(
         f"<a href='https://matrix.to/#/{user_id}'>{user_id}</a>"
         for user_id in reminder.users)
     content = TextMessageEventContent(
         msgtype=MessageType.TEXT,
         body=f"{users}: {reminder.message}",
         format=Format.HTML,
         formatted_body=f"{users_html}: {escape(reminder.message)}")
     content["xyz.maubot.reminder"] = {
         "id": reminder.id,
         "message": reminder.message,
         "targets": list(reminder.users),
         "reply_to": reminder.reply_to,
     }
     if reminder.reply_to:
         content.set_reply(await
                           self.client.get_event(reminder.room_id,
                                                 reminder.reply_to))
     await self.client.send_message(reminder.room_id, content)
Ejemplo n.º 8
0
 async def _handle_matrix_text(self, sender_id: TelegramID,
                               event_id: EventID, space: TelegramID,
                               client: 'MautrixTelegramClient',
                               content: TextMessageEventContent,
                               reply_to: TelegramID) -> None:
     if content.formatted_body and content.format == Format.HTML:
         message, entities = formatter.matrix_to_telegram(
             content.formatted_body)
     else:
         message, entities = formatter.matrix_text_to_telegram(content.body)
     async with self.send_lock(sender_id):
         lp = self.get_config("telegram_link_preview")
         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,
                     message,
                     formatting_entities=entities,
                     link_preview=lp)
                 self._add_telegram_message_to_db(event_id, space, -1,
                                                  response)
                 return
         response = await client.send_message(self.peer,
                                              message,
                                              reply_to=reply_to,
                                              formatting_entities=entities,
                                              link_preview=lp)
         self._add_telegram_message_to_db(event_id, space, 0, response)
     await self._send_delivery_receipt(event_id)
Ejemplo n.º 9
0
    def send_text(self,
                  room_id: RoomID,
                  text: str,
                  html: Optional[str] = None,
                  msgtype: MessageType = MessageType.TEXT,
                  relates_to: Optional[RelatesTo] = None,
                  **kwargs) -> Awaitable[EventID]:
        """
        Send a text message to a room.

        Args:
            room_id: The ID of the room to send the message to.
            text: The text to send. If set to None, the given HTML is used instead.
            html: The HTML to send.
            msgtype: The message type to send.
                Defaults to :attr:`MessageType.TEXT` (normal text message)
            relates_to: Message relation metadata used for things like replies.
            **kwargs: Optional parameters to pass to the :meth:`HTTPAPI.request` method.

        Returns:
            The ID of the event that was sent.
        """
        if html:
            if not text:
                text = html
            content = TextMessageEventContent(msgtype=msgtype,
                                              body=text,
                                              format=Format.HTML,
                                              formatted_body=html)
        else:
            content = TextMessageEventContent(msgtype=msgtype, body=text)
        if relates_to:
            content.relates_to = relates_to
        return self.send_message(room_id, content, **kwargs)
Ejemplo n.º 10
0
def facebook_to_matrix(message: fbchat.MessageData) -> TextMessageEventContent:
    text = message.text or ""
    content = TextMessageEventContent(msgtype=MessageType.TEXT, body=text)
    for m in reversed(message.mentions):
        original = text[m.offset:m.offset + m.length]
        if len(original) > 0 and original[0] == "@":
            original = original[1:]
        text = f"{text[:m.offset]}@{m.thread_id}\u2063{original}\u2063{text[m.offset + m.length:]}"
    html = escape(text)
    output = []
    if html:
        codeblock = False
        blockquote = False
        line: str
        lines = html.split("\n")
        for i, line in enumerate(lines):
            blockquote, line = _handle_blockquote(output, blockquote, line)
            codeblock, line, post_args = _handle_codeblock_pre(output, codeblock, line)
            output.append(_convert_formatting(line))
            if i != len(lines) - 1:
                output.append("<br/>")
            _handle_codeblock_post(output, *post_args)
    for attachment in message.attachments:
        if ((isinstance(attachment, fbchat.ShareAttachment)
             and attachment.original_url.rstrip("/") not in text)):
            output.append(f"<br/><a href='{attachment.original_url}'>{attachment.title}</a>")
            content.body += f"\n{attachment.title}: {attachment.original_url}"
    html = "".join(output)

    html = MENTION_REGEX.sub(_mention_replacer, html)
    if html != escape(content.body).replace("\n", "<br/>"):
        content.format = Format.HTML
        content.formatted_body = html
    return content
Ejemplo n.º 11
0
 async def send_simple_html(self, room_id: str, html: str):
     ''' Publish a simple HTML message in the selected room. '''
     from mautrix.types import Format
     content = TextMessageEventContent(msgtype=MessageType.TEXT)
     content.format = Format.HTML
     content.formatted_body = html
     await self._client.send_message(room_id=room_id, content=content)
Ejemplo n.º 12
0
 async def _handle_matrix_text(self, sender_id: TelegramID,
                               event_id: EventID, space: TelegramID,
                               client: 'MautrixTelegramClient',
                               content: TextMessageEventContent,
                               reply_to: TelegramID) -> None:
     async with self.send_lock(sender_id):
         lp = self.get_config("telegram_link_preview")
         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,
                     content,
                     parse_mode=self._matrix_event_to_entities,
                     link_preview=lp)
                 self._add_telegram_message_to_db(event_id, space, -1,
                                                  response)
                 return
         response = await client.send_message(
             self.peer,
             content,
             reply_to=reply_to,
             parse_mode=self._matrix_event_to_entities,
             link_preview=lp)
         self._add_telegram_message_to_db(event_id, space, 0, response)
     await self._send_delivery_receipt(event_id)
Ejemplo n.º 13
0
 async def _handle_encrypted_wait(self, evt: EncryptedEvent,
                                  err: SessionNotFound, wait: int) -> None:
     self.log.warning(
         f"Didn't find session {err.session_id}, waiting even longer")
     msg = (
         "\u26a0 Your message was not bridged: the bridge hasn't received the decryption "
         f"keys. The bridge will retry for {wait} seconds. If this error keeps happening, "
         "try restarting your client.")
     event_id = await self.az.intent.send_notice(evt.room_id, msg)
     got_keys = await self.e2ee.crypto.wait_for_session(evt.room_id,
                                                        err.sender_key,
                                                        err.session_id,
                                                        timeout=wait)
     if got_keys:
         try:
             decrypted = await self.e2ee.decrypt(evt,
                                                 wait_session_timeout=0)
         except DecryptionError as e:
             msg = f"\u26a0 Your message was not bridged: {e}"
         else:
             await self.az.intent.redact(evt.room_id, event_id)
             await self.int_handle_event(decrypted)
             return
     else:
         msg = (
             "\u26a0 Your message was not bridged: the bridge hasn't received the decryption "
             "keys. If this error keeps happening, try restarting your client."
         )
     content = TextMessageEventContent(msgtype=MessageType.NOTICE, body=msg)
     content.set_edit(event_id)
     await self.az.intent.send_message(evt.room_id, content)
Ejemplo n.º 14
0
async def _add_forward_header(source: 'AbstractUser', content: TextMessageEventContent,
                              fwd_from: MessageFwdHeader) -> None:
    if not content.formatted_body or content.format != Format.HTML:
        content.format = Format.HTML
        content.formatted_body = escape(content.body)
    fwd_from_html, fwd_from_text = None, None
    if isinstance(fwd_from.from_id, PeerUser):
        user = u.User.get_by_tgid(TelegramID(fwd_from.from_id.user_id))
        if user:
            fwd_from_text = user.displayname or user.mxid
            fwd_from_html = (f"<a href='https://matrix.to/#/{user.mxid}'>"
                             f"{escape(fwd_from_text)}</a>")

        if not fwd_from_text:
            puppet = pu.Puppet.get(TelegramID(fwd_from.from_id.user_id), create=False)
            if puppet and puppet.displayname:
                fwd_from_text = puppet.displayname or puppet.mxid
                fwd_from_html = (f"<a href='https://matrix.to/#/{puppet.mxid}'>"
                                 f"{escape(fwd_from_text)}</a>")

        if not fwd_from_text:
            try:
                user = await source.client.get_entity(fwd_from.from_id)
                if user:
                    fwd_from_text, _ = pu.Puppet.get_displayname(user, False)
                    fwd_from_html = f"<b>{escape(fwd_from_text)}</b>"
            except (ValueError, RPCError):
                fwd_from_text = fwd_from_html = "unknown user"
    elif isinstance(fwd_from.from_id, (PeerChannel, PeerChat)):
        from_id = (fwd_from.from_id.chat_id if isinstance(fwd_from.from_id, PeerChat)
                   else fwd_from.from_id.channel_id)
        portal = po.Portal.get_by_tgid(TelegramID(from_id))
        if portal and portal.title:
            fwd_from_text = portal.title
            if portal.alias:
                fwd_from_html = (f"<a href='https://matrix.to/#/{portal.alias}'>"
                                 f"{escape(fwd_from_text)}</a>")
            else:
                fwd_from_html = f"channel <b>{escape(fwd_from_text)}</b>"
        else:
            try:
                channel = await source.client.get_entity(fwd_from.from_id)
                if channel:
                    fwd_from_text = f"channel {channel.title}"
                    fwd_from_html = f"channel <b>{escape(channel.title)}</b>"
            except (ValueError, RPCError):
                fwd_from_text = fwd_from_html = "unknown channel"
    elif fwd_from.from_name:
        fwd_from_text = fwd_from.from_name
        fwd_from_html = f"<b>{escape(fwd_from.from_name)}</b>"
    else:
        fwd_from_text = "unknown source"
        fwd_from_html = f"unknown source"

    content.body = "\n".join([f"> {line}" for line in content.body.split("\n")])
    content.body = f"Forwarded from {fwd_from_text}:\n{content.body}"
    content.formatted_body = (
        f"Forwarded message from {fwd_from_html}<br/>"
        f"<tg-forward><blockquote>{content.formatted_body}</blockquote></tg-forward>")
Ejemplo n.º 15
0
 async def _expire_telegram_photo(self, intent: IntentAPI, event_id: EventID, ttl: int) -> None:
     try:
         content = TextMessageEventContent(msgtype=MessageType.NOTICE, body="Photo has expired")
         content.set_edit(event_id)
         await asyncio.sleep(ttl)
         await self._send_message(intent, content)
     except Exception:
         self.log.warning("Failed to expire Telegram photo %s", event_id, exc_info=True)
Ejemplo n.º 16
0
Archivo: rsvc.py Proyecto: maubot/rsvc
 async def _edit(self, room_id: RoomID, event_id: EventID,
                 text: str) -> None:
     content = TextMessageEventContent(msgtype=MessageType.NOTICE,
                                       body=text,
                                       format=Format.HTML,
                                       formatted_body=markdown.render(text))
     content.set_edit(event_id)
     await self.client.send_message(room_id, content)
Ejemplo n.º 17
0
async def login_qr(evt: CommandEvent) -> EventID:
    login_as = evt.sender
    if len(evt.args) > 0 and evt.sender.is_admin:
        login_as = await u.User.get_by_mxid(UserID(evt.args[0]))
    if not qrcode or not QRLogin:
        return await evt.reply("This bridge instance does not support logging in with a QR code.")
    if await login_as.is_logged_in():
        return await evt.reply(f"You are already logged in as {login_as.human_tg_id}.")

    await login_as.ensure_started(even_if_no_session=True)
    qr_login = QRLogin(login_as.client, ignored_ids=[])
    qr_event_id: EventID | None = None

    async def upload_qr() -> None:
        nonlocal qr_event_id
        buffer = io.BytesIO()
        image = qrcode.make(qr_login.url)
        size = image.pixel_size
        image.save(buffer, "PNG")
        qr = buffer.getvalue()
        mxc = await evt.az.intent.upload_media(qr, "image/png", "login-qr.png", len(qr))
        content = MediaMessageEventContent(
            body=qr_login.url,
            url=mxc,
            msgtype=MessageType.IMAGE,
            info=ImageInfo(mimetype="image/png", size=len(qr), width=size, height=size),
        )
        if qr_event_id:
            content.set_edit(qr_event_id)
            await evt.az.intent.send_message(evt.room_id, content)
        else:
            content.set_reply(evt.event_id)
            qr_event_id = await evt.az.intent.send_message(evt.room_id, content)

    retries = 4
    while retries > 0:
        await qr_login.recreate()
        await upload_qr()
        try:
            user = await qr_login.wait()
            break
        except asyncio.TimeoutError:
            retries -= 1
        except SessionPasswordNeededError:
            evt.sender.command_status = {
                "next": enter_password,
                "login_as": login_as if login_as != evt.sender else None,
                "action": "Login (password entry)",
            }
            return await evt.reply(
                "Your account has two-factor authentication. Please send your password here."
            )
    else:
        timeout = TextMessageEventContent(body="Login timed out", msgtype=MessageType.TEXT)
        timeout.set_edit(qr_event_id)
        return await evt.az.intent.send_message(evt.room_id, timeout)

    return await _finish_sign_in(evt, user, login_as=login_as)
Ejemplo n.º 18
0
async def facebook_to_matrix(
        msg: Union[graphql.MessageText,
                   mqtt.Message]) -> TextMessageEventContent:
    if isinstance(msg, mqtt.Message):
        text = msg.text
        mentions = msg.mentions
    elif isinstance(msg, graphql.MessageText):
        text = msg.text
        mentions = msg.ranges
    else:
        raise ValueError(
            f"Unsupported Facebook message type {type(msg).__name__}")
    text = text or ""
    content = TextMessageEventContent(msgtype=MessageType.TEXT, body=text)
    mention_user_ids = []
    for m in reversed(mentions):
        original = text[m.offset:m.offset + m.length]
        if len(original) > 0 and original[0] == "@":
            original = original[1:]
        mention_user_ids.append(int(m.user_id))
        text = f"{text[:m.offset]}@{m.user_id}\u2063{original}\u2063{text[m.offset + m.length:]}"
    html = escape(text)
    output = []
    if html:
        codeblock = False
        blockquote = False
        line: str
        lines = html.split("\n")
        for i, line in enumerate(lines):
            blockquote, line = _handle_blockquote(output, blockquote, line)
            codeblock, line, post_args = _handle_codeblock_pre(
                output, codeblock, line)
            output.append(_convert_formatting(line))
            if i != len(lines) - 1:
                output.append("<br/>")
            _handle_codeblock_post(output, *post_args)
    html = "".join(output)

    mention_user_map = {}
    for fbid in mention_user_ids:
        user = await u.User.get_by_fbid(fbid)
        if user:
            mention_user_map[fbid] = user.mxid
        else:
            puppet = await pu.Puppet.get_by_fbid(fbid, create=False)
            mention_user_map[fbid] = puppet.mxid if puppet else None

    def _mention_replacer(match: Match) -> str:
        mxid = mention_user_map[int(match.group(1))]
        if not mxid:
            return match.group(2)
        return f"<a href=\"https://matrix.to/#/{mxid}\">{match.group(2)}</a>"

    html = MENTION_REGEX.sub(_mention_replacer, html)
    if html != escape(content.body).replace("\n", "<br/>"):
        content.format = Format.HTML
        content.formatted_body = html
    return content
Ejemplo n.º 19
0
    async def handle_signal_message(self, sender: 'p.Puppet',
                                    message: MessageData) -> None:
        if (sender.uuid, message.timestamp) in self._msgts_dedup:
            self.log.debug(
                f"Ignoring message {message.timestamp} by {sender.uuid}"
                " as it was already handled (message.timestamp in dedup queue)"
            )
            return
        old_message = await DBMessage.get_by_signal_id(sender.uuid,
                                                       message.timestamp,
                                                       self.chat_id,
                                                       self.receiver)
        if old_message is not None:
            self.log.debug(
                f"Ignoring message {message.timestamp} by {sender.uuid}"
                " as it was already handled (message.id found in database)")
            return
        self.log.debug(
            f"Started handling message {message.timestamp} by {sender.uuid}")
        self.log.trace(f"Message content: {message}")
        self._msgts_dedup.appendleft((sender.uuid, message.timestamp))
        intent = sender.intent_for(self)
        await intent.set_typing(self.mxid, False)
        event_id = None
        reply_to = await self._find_quote_event_id(message.quote)

        for attachment in message.all_attachments:
            content = await self._handle_signal_attachment(intent, attachment)
            if content:
                if reply_to and not message.body:
                    # If there's no text, set the first image as the reply
                    content.set_reply(reply_to)
                    reply_to = None
                event_id = await self._send_message(
                    intent, content, timestamp=message.timestamp)

        if message.body:
            content = TextMessageEventContent(msgtype=MessageType.TEXT,
                                              body=message.body)
            if reply_to:
                content.set_reply(reply_to)
            event_id = await self._send_message(intent,
                                                content,
                                                timestamp=message.timestamp)

        if event_id:
            msg = DBMessage(mxid=event_id,
                            mx_room=self.mxid,
                            sender=sender.uuid,
                            timestamp=message.timestamp,
                            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 Signal message {message.timestamp} -> {event_id}")
        else:
            self.log.debug(f"Didn't get event ID for {message.timestamp}")
Ejemplo n.º 20
0
async def twitter_to_matrix(message: MessageData) -> TextMessageEventContent:
    content = TextMessageEventContent(msgtype=MessageType.TEXT,
                                      body=message.text,
                                      format=Format.HTML,
                                      formatted_body=message.text)
    for entity in reversed(message.entities.all) if message.entities else []:
        start, end = entity.indices
        if isinstance(entity, MessageEntityURL):
            content.body = content.body[:
                                        start] + entity.expanded_url + content.body[
                                            end:]
            content.formatted_body = (
                f'{content.formatted_body[:start]}'
                f'<a href="{entity.expanded_url}">{entity.display_url}</a>'
                f'{content.formatted_body[end:]}')
        elif isinstance(entity, MessageEntityUserMention):
            puppet = await pu.Puppet.get_by_twid(entity.id, create=False)
            if puppet:
                user_url = f"https://matrix.to/#/{puppet.mxid}"
                content.formatted_body = (
                    f'{content.formatted_body[:start]}'
                    f'<a href="{user_url}">{puppet.name or entity.name}</a>'
                    f'{content.formatted_body[end:]}')
        else:
            # Get the sigil (# or $) from the body
            text = content.formatted_body[start:end][0] + entity.text
            content.formatted_body = (f'{content.formatted_body[:start]}'
                                      f'<font color="#0000ff">{text}</font>'
                                      f'{content.formatted_body[end:]}')
    if content.formatted_body == content.body:
        content.formatted_body = None
        content.format = None
    content.body = html.unescape(content.body)
    return content
Ejemplo n.º 21
0
 def send_markdown(self,
                   room_id: RoomID,
                   markdown: str,
                   msgtype: MessageType = MessageType.TEXT,
                   relates_to: Optional[RelatesTo] = None,
                   **kwargs) -> Awaitable[EventID]:
     content = TextMessageEventContent(msgtype=msgtype, format=Format.HTML)
     content.body, content.formatted_body = parse_markdown(markdown)
     if relates_to:
         content.relates_to = relates_to
     return self.send_message(room_id, content, **kwargs)
Ejemplo n.º 22
0
 async def _add_facebook_reply(self, content: TextMessageEventContent,
                               reply: str) -> None:
     if reply:
         message = DBMessage.get_by_fbid(reply, self.fb_receiver)
         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)
Ejemplo n.º 23
0
 async def send_bridge_notice(self, text: str, edit: Optional[EventID] = None
                              ) -> Optional[EventID]:
     event_id = None
     try:
         content = TextMessageEventContent(msgtype=MessageType.NOTICE, body=text)
         if edit:
             content.set_edit(edit)
         event_id = await self.az.intent.send_message(await self.get_notice_room(), content)
     except Exception:
         self.log.warning("Failed to send bridge notice '%s'", text, exc_info=True)
     return edit or event_id
Ejemplo n.º 24
0
 async def send_bridge_notice(self, text: str, edit: Optional[EventID] = None,
                              important: bool = False) -> Optional[EventID]:
     event_id = None
     try:
         self.log.debug("Sending bridge notice: %s", text)
         content = TextMessageEventContent(body=text, msgtype=(MessageType.TEXT if important
                                                               else MessageType.NOTICE))
         if edit:
             content.set_edit(edit)
         # This is locked to prevent notices going out in the wrong order
         async with self._notice_send_lock:
             event_id = await self.az.intent.send_message(await self.get_notice_room(), content)
     except Exception:
         self.log.warning("Failed to send bridge notice", exc_info=True)
     return edit or event_id
Ejemplo n.º 25
0
Archivo: sed.py Proyecto: maubot/sed
    async def edit_handler(self, evt: MessageEvent, stmt: SedStatement, original_sed: HistoricalSed
                           ) -> None:
        orig_evt = await self.client.get_event(evt.room_id, original_sed.seds_event)
        replaced = self._exec(stmt, orig_evt.content.body)
        content = TextMessageEventContent(
            msgtype=MessageType.NOTICE, body=replaced, format=Format.HTML,
            formatted_body=self.highlight_edits(replaced, orig_evt.content.body,
                                                stmt.highlight_edits),
            relates_to=RelatesTo(rel_type=RelationType.REPLACE, event_id=original_sed.output_event))

        if orig_evt.content.msgtype == MessageType.EMOTE:
            displayname = await self._get_displayname(orig_evt.room_id, orig_evt.sender)
            content.body = f"* {displayname} {content.body}"
            content.formatted_body = f"* {escape(displayname)} {content.formatted_body}"
        await self.client.send_message(evt.room_id, content)
Ejemplo n.º 26
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

        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.trace("Unhandled Matrix event: %s", content)
Ejemplo n.º 27
0
 async def _send_xkcd(self, room_id: RoomID, xkcd: XKCDInfo) -> None:
     info = await self._get_media_info(xkcd.img)
     if self.config["inline"]:
         content = TextMessageEventContent(
             msgtype=MessageType.TEXT, format=Format.HTML,
             external_url=f"https://xkcd.com/{xkcd.num}",
             body=f"{xkcd.num}: **{xkcd.title}**\n"
                  f"{xkcd.img}\n{xkcd.alt}",
             formatted_body=f"{xkcd.num}: <strong>{xkcd.safe_title}</strong><br/>"
                            f"<img src='{info.mxc_uri}' title='{xkcd.alt}'/>")
         content["license"] = "CC-BY-NC-2.5"
         content["license_url"] = "https://xkcd.com/license.html"
         await self.client.send_message(room_id, content)
     else:
         await self.client.send_text(room_id, text=f"{xkcd.num}: **{xkcd.title}**",
                                     html=f"{xkcd.num}: <strong>{xkcd.safe_title}</strong>")
         content = MediaMessageEventContent(url=info.mxc_uri, body=info.file_name,
                                            msgtype=MessageType.IMAGE,
                                            external_url=f"https://xkcd.com/{xkcd.num}",
                                            info=ImageInfo(
                                                mimetype=info.mime_type,
                                                size=info.size,
                                                width=info.width,
                                                height=info.height,
                                            ),)
         content["license"] = "CC-BY-NC-2.5"
         content["license_url"] = "https://xkcd.com/license.html"
         await self.client.send_message(room_id, content)
         await self.client.send_text(room_id, text=xkcd.alt)
Ejemplo n.º 28
0
    async def handle_telegram_poll(self, source: 'AbstractUser',
                                   intent: IntentAPI, evt: Message,
                                   relates_to: RelatesTo) -> EventID:
        poll: Poll = evt.media.poll
        poll_id = self._encode_msgid(source, evt)

        _n = 0

        def n() -> int:
            nonlocal _n
            _n += 1
            return _n

        text_answers = "\n".join(f"{n()}. {answer.text}"
                                 for answer in poll.answers)
        html_answers = "\n".join(f"<li>{answer.text}</li>"
                                 for answer in poll.answers)
        content = TextMessageEventContent(
            msgtype=MessageType.TEXT,
            format=Format.HTML,
            body=f"Poll: {poll.question}\n{text_answers}\n"
            f"Vote with !tg vote {poll_id} <choice number>",
            formatted_body=f"<strong>Poll</strong>: {poll.question}<br/>\n"
            f"<ol>{html_answers}</ol>\n"
            f"Vote with <code>!tg vote {poll_id} &lt;choice number&gt;</code>",
            relates_to=relates_to,
            external_url=self._get_external_url(evt))

        await intent.set_typing(self.mxid, is_typing=False)
        return await self._send_message(intent, content, timestamp=evt.date)
Ejemplo n.º 29
0
 async def respond(
     self,
     content: str | MessageEventContent,
     event_type: EventType = EventType.ROOM_MESSAGE,
     markdown: bool = True,
     allow_html: bool = False,
     reply: bool | str = False,
     edits: EventID | MessageEvent | None = None,
 ) -> EventID:
     if isinstance(content, str):
         content = TextMessageEventContent(msgtype=MessageType.NOTICE, body=content)
         if allow_html or markdown:
             content.format = Format.HTML
             content.body, content.formatted_body = await parse_formatted(
                 content.body, render_markdown=markdown, allow_html=allow_html
             )
     if edits:
         content.set_edit(edits)
     elif reply:
         if reply != "force" and self.disable_reply:
             content.body = f"{self.sender}: {content.body}"
             fmt_body = content.formatted_body or escape(content.body).replace("\n", "<br>")
             content.formatted_body = (
                 f'<a href="https://matrix.to/#/{self.sender}">'
                 f"{self.sender}"
                 f"</a>: {fmt_body}"
             )
         else:
             content.set_reply(self)
     return await self.client.send_message_event(self.room_id, event_type, content)
Ejemplo n.º 30
0
    async def handle_hangouts_message(self, source: 'u.User', sender: 'p.Puppet',
                                      event: ChatMessageEvent) -> None:
        async with self.optional_send_lock(sender.gid):
            if event.id_ in self._dedup:
                return
            self._dedup.appendleft(event.id_)
        if not self.mxid:
            mxid = await self.create_matrix_room(source)
            if not mxid:
                # Failed to create
                return
        if not await self._bridge_own_message_pm(source, sender, f"message {event.id_}"):
            return
        intent = sender.intent_for(self)
        self.log.debug("Handling hangouts message %s", event.id_)

        event_id = None
        if event.attachments:
            self.log.debug("Processing attachments.")
            self.log.trace("Attachments: %s", event.attachments)
            event_id = await self.process_hangouts_attachments(event, intent)
        # Just to fallback to text if something else hasn't worked.
        if not event_id:
            content = TextMessageEventContent(msgtype=MessageType.TEXT, body=event.text)
            event_id = await self._send_message(intent, content, timestamp=event.timestamp)
        DBMessage(mxid=event_id, mx_room=self.mxid, gid=event.id_, receiver=self.receiver,
                  index=0, date=event.timestamp).insert()
        self.log.debug("Handled Hangouts message %s -> %s", event.id_, event_id)
        await self._send_delivery_receipt(event_id)