示例#1
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
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>")
示例#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
示例#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
示例#5
0
 async def _handle_instagram_reel_share(self, source: 'u.User', intent: IntentAPI,
                                        item: ThreadItem) -> Optional[EventID]:
     media = item.reel_share.media
     prefix_html = None
     if item.reel_share.type == ReelShareType.REPLY:
         if item.reel_share.reel_owner_id == source.igpk:
             prefix = "Replied to your story"
         else:
             username = media.user.username
             prefix = f"Sent @{username}'s story"
             user_link = f'<a href="https://www.instagram.com/{username}/">@{username}</a>'
             prefix_html = f"Sent {user_link}'s story"
     elif item.reel_share.type == ReelShareType.REACTION:
         prefix = "Reacted to your story"
     elif item.reel_share.type == ReelShareType.MENTION:
         if item.reel_share.mentioned_user_id == source.igpk:
             prefix = "Mentioned you in their story"
         else:
             prefix = "You mentioned them in your story"
     else:
         self.log.debug(f"Unsupported reel share type {item.reel_share.type}")
         return None
     prefix_content = TextMessageEventContent(msgtype=MessageType.NOTICE, body=prefix)
     if prefix_html:
         prefix_content.format = Format.HTML
         prefix_content.formatted_body = prefix_html
     content = TextMessageEventContent(msgtype=MessageType.TEXT, body=item.reel_share.text)
     if not content.body and isinstance(media, MediaShareItem):
         content.body = media.caption.text if media.caption else ""
     if not content.body:
         content.body = "<no caption>"
     await self._send_message(intent, prefix_content, timestamp=item.timestamp // 1000)
     if isinstance(media, ExpiredMediaItem):
         # TODO send message about expired story
         pass
     else:
         fake_item_id = f"fi.mau.instagram.reel_share.{item.user_id}.{media.pk}"
         existing = await DBMessage.get_by_item_id(fake_item_id, self.receiver)
         if existing:
             # If the user already reacted or replied to the same reel share item,
             # use a Matrix reply instead of reposting the image.
             content.set_reply(existing.mxid)
         else:
             media_event_id = await self._handle_instagram_media(source, intent, item)
             await DBMessage(mxid=media_event_id, mx_room=self.mxid, item_id=fake_item_id,
                             receiver=self.receiver, sender=media.user.pk).insert()
     return await self._send_message(intent, content, timestamp=item.timestamp // 1000)
示例#6
0
async def telegram_to_matrix(
        evt: Message,
        source: "AbstractUser",
        main_intent: Optional[IntentAPI] = None,
        prefix_text: Optional[str] = None,
        prefix_html: Optional[str] = None,
        override_text: str = None,
        override_entities: List[TypeMessageEntity] = None,
        no_reply_fallback: bool = False) -> TextMessageEventContent:
    content = TextMessageEventContent(
        msgtype=MessageType.TEXT,
        body=add_surrogate(override_text or evt.message),
    )
    entities = override_entities or evt.entities
    if entities:
        content.format = Format.HTML
        content.formatted_body = _telegram_entities_to_matrix_catch(
            content.body, entities)

    if prefix_html:
        if not content.formatted_body:
            content.format = Format.HTML
            content.formatted_body = escape(content.body)
        content.formatted_body = prefix_html + content.formatted_body
    if prefix_text:
        content.body = prefix_text + content.body

    if evt.fwd_from:
        await _add_forward_header(source, content, evt.fwd_from)

    if evt.reply_to_msg_id and not no_reply_fallback:
        await _add_reply_header(source, content, evt, main_intent)

    if isinstance(evt, Message) and evt.post and evt.post_author:
        if not content.formatted_body:
            content.formatted_body = escape(content.body)
        content.body += f"\n- {evt.post_author}"
        content.formatted_body += f"<br/><i>- <u>{evt.post_author}</u></i>"

    content.body = del_surrogate(content.body)

    if content.formatted_body:
        content.formatted_body = del_surrogate(
            content.formatted_body.replace("\n", "<br/>"))

    return content
示例#7
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)
示例#8
0
文件: sed.py 项目: 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)
示例#9
0
文件: sed.py 项目: maubot/sed
 async def _try_replace_event(self, event_id: EventID, stmt: SedStatement, orig_evt: MessageEvent
                              ) -> bool:
     replaced = self._exec(stmt, orig_evt.content.body)
     if replaced == orig_evt.content.body:
         return False
     content = TextMessageEventContent(
         msgtype=MessageType.NOTICE, body=replaced, format=Format.HTML,
         formatted_body=self.highlight_edits(replaced, orig_evt.content.body,
                                             stmt.highlight_edits))
     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}"
     output_event = await orig_evt.reply(content)
     self.history[event_id] = HistoricalSed(output_event=output_event,
                                            seds_event=orig_evt.event_id)
     return True
示例#10
0
 def send_markdown(self,
                   room_id: RoomID,
                   markdown: str,
                   msgtype: MessageType = MessageType.TEXT,
                   edits: Optional[Union[EventID, MessageEvent]] = None,
                   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:
         if edits:
             raise ValueError(
                 "Can't use edits and relates_to at the same time.")
         content.relates_to = relates_to
     elif edits:
         content.set_edit(edits)
     return self.send_message(room_id, content, **kwargs)
示例#11
0
async def telegram_to_matrix(
    evt: Message | SponsoredMessage,
    source: au.AbstractUser,
    main_intent: IntentAPI | None = None,
    prefix_text: str | None = None,
    prefix_html: str | None = None,
    override_text: str = None,
    override_entities: list[TypeMessageEntity] = None,
    no_reply_fallback: bool = False,
    require_html: bool = False,
) -> TextMessageEventContent:
    content = TextMessageEventContent(
        msgtype=MessageType.TEXT,
        body=add_surrogate(override_text or evt.message),
    )
    entities = override_entities or evt.entities
    if entities:
        content.format = Format.HTML
        html = await _telegram_entities_to_matrix_catch(
            add_surrogate(content.body), entities)
        content.formatted_body = del_surrogate(html)

    if require_html:
        content.ensure_has_html()

    if prefix_html:
        content.ensure_has_html()
        content.formatted_body = prefix_html + content.formatted_body
    if prefix_text:
        content.body = prefix_text + content.body

    if getattr(evt, "fwd_from", None):
        await _add_forward_header(source, content, evt.fwd_from)

    if getattr(evt, "reply_to", None) and not no_reply_fallback:
        await _add_reply_header(source, content, evt, main_intent)

    if isinstance(evt, Message) and evt.post and evt.post_author:
        content.ensure_has_html()
        content.body += f"\n- {evt.post_author}"
        content.formatted_body += f"<br/><i>- <u>{evt.post_author}</u></i>"

    return content
示例#12
0
文件: matrix.py 项目: rrosajp/maubot
 async def send_markdown(
     self,
     room_id: RoomID,
     markdown: str,
     *,
     allow_html: bool = False,
     msgtype: MessageType = MessageType.TEXT,
     edits: EventID | MessageEvent | None = None,
     relates_to: RelatesTo | None = None,
     **kwargs,
 ) -> EventID:
     content = TextMessageEventContent(msgtype=msgtype, format=Format.HTML)
     content.body, content.formatted_body = await parse_formatted(
         markdown, allow_html=allow_html
     )
     if relates_to:
         if edits:
             raise ValueError("Can't use edits and relates_to at the same time.")
         content.relates_to = relates_to
     elif edits:
         content.set_edit(edits)
     return await self.send_message(room_id, content, **kwargs)
示例#13
0
    async def _respond_formatted(self, e: MessageEvent, m: str) -> None:
        """Respond with formatted message in m.text matrix format,
        not m.notice.

        This is needed as mobile clients (Riot 0.9.10, RiotX) currently
        do not seem to render markdown / HTML in m.notice events
        which are conventionally send by bots.

        Desktop/web Riot.im does render MD/HTML in m.notice, however.
        """
        # IRC people don't like notices.
        if '@appservice-irc:matrix.org' in await self.client.get_joined_members(
                e.room_id):
            t = MessageType.TEXT
        else:  # But matrix people do.
            t = MessageType.NOTICE

        c = TextMessageEventContent(msgtype=t,
                                    formatted_body=m,
                                    format="org.matrix.custom.html")
        c.body, c.formatted_body = parse_formatted(m, allow_html=True)
        await e.respond(c, markdown=True, allow_html=True)
示例#14
0
文件: handler.py 项目: umrysh/github
    async def send_message(self, evt_type: EventType, evt: Event, room_id: RoomID,
                           delivery_ids: Set[str], aggregation: Optional[Dict[str, Any]] = None
                           ) -> None:
        try:
            tpl = self.messages[str(evt_type)]
        except TemplateNotFound:
            self.log.debug(f"Unhandled event of type {evt_type} -- {delivery_ids}")
            return

        aborted = False

        def abort() -> None:
            nonlocal aborted
            aborted = True

        args = {
            **attr.asdict(evt, recurse=False),
            **expand_enum(ACTION_CLASSES.get(evt_type)),
            **OTHER_ENUMS,
            "util": TemplateUtil,
            "abort": abort,
            "aggregation": aggregation,
        }
        args["templates"] = self.templates.proxy(args)
        content = TextMessageEventContent(msgtype=self.msgtype, format=Format.HTML,
                                          formatted_body=tpl.render(**args))
        if not content.formatted_body or aborted:
            return
        content.formatted_body = spaces.sub(space, content.formatted_body.strip())
        content.body = parse_html(content.formatted_body)
        content["xyz.maubot.github.webhook"] = {
            "delivery_ids": list(delivery_ids),
            "event_type": str(evt_type),
            **(evt.meta() if hasattr(evt, "meta") else {}),
        }
        await self.bot.client.send_message(room_id, content)