예제 #1
0
 async def _handle_twitter_attachment(
         self, source: 'u.User', sender: 'p.Puppet',
         message: MessageData) -> Optional[MediaMessageEventContent]:
     content = None
     intent = sender.intent_for(self)
     media = message.attachment.media
     if media:
         reuploaded_info = await self._reupload_twitter_media(
             source, media.media_url_https, intent)
         thumbnail_info = None
         if media.video_info:
             thumbnail_info = reuploaded_info
             best_variant = None
             for variant in media.video_info.variants:
                 if ((not best_variant or (variant.bitrate or 0) >
                      (best_variant.bitrate or 0)
                      or self._is_better_mime(best_variant, variant))):
                     best_variant = variant
             reuploaded_info = await self._reupload_twitter_media(
                 source, best_variant.url, intent)
         content = MediaMessageEventContent(
             body=reuploaded_info.file_name,
             url=reuploaded_info.mxc,
             file=reuploaded_info.decryption_info,
             external_url=media.media_url_https)
         if message.attachment.video:
             content.msgtype = MessageType.VIDEO
             content.info = VideoInfo(
                 mimetype=reuploaded_info.mime_type,
                 size=reuploaded_info.size,
                 width=media.original_info.width,
                 height=media.original_info.height,
                 duration=media.video_info.duration_millis // 1000)
         elif message.attachment.photo or message.attachment.animated_gif:
             content.msgtype = MessageType.IMAGE
             content.info = ImageInfo(mimetype=reuploaded_info.mime_type,
                                      size=reuploaded_info.size,
                                      width=media.original_info.width,
                                      height=media.original_info.height)
         if thumbnail_info:
             content.info.thumbnail_url = thumbnail_info.mxc
             content.info.thumbnail_file = thumbnail_info.decryption_info
             content.info.thumbnail_info = ThumbnailInfo(
                 mimetype=thumbnail_info.mime_type,
                 size=thumbnail_info.size,
                 width=media.original_info.width,
                 height=media.original_info.height)
         # Remove the attachment link from message.text
         start, end = media.indices
         message.text = message.text[:start] + message.text[end:]
     elif message.attachment.tweet:
         # TODO special handling for tweets?
         pass
     return content
예제 #2
0
async def make_qr(intent: IntentAPI,
                  data: Union[str, bytes],
                  body: str = None) -> MediaMessageEventContent:
    # TODO always encrypt QR codes?
    buffer = io.BytesIO()
    image = qrcode.make(data)
    size = image.pixel_size
    image.save(buffer, "PNG")
    qr = buffer.getvalue()
    mxc = await intent.upload_media(qr, "image/png", "qr.png", len(qr))
    return MediaMessageEventContent(body=body or data,
                                    url=mxc,
                                    msgtype=MessageType.IMAGE,
                                    info=ImageInfo(mimetype="image/png",
                                                   size=len(qr),
                                                   width=size,
                                                   height=size))
예제 #3
0
    async def process_hangouts_attachments(
            self, event: ChatMessageEvent,
            intent: IntentAPI) -> Optional[EventID]:
        attachments_pb = event._event.chat_message.message_content.attachment

        if len(event.attachments) > 1:
            self.log.warning("Can't handle more that one attachment")
            return None

        attachment = event.attachments[0]
        attachment_pb = attachments_pb[0]

        embed_item = attachment_pb.embed_item

        # Get the filename from the headers
        async with self.az.http_session.request("GET", attachment) as resp:
            value, params = cgi.parse_header(
                resp.headers["Content-Disposition"])
            mime = resp.headers["Content-Type"]
            filename = params.get('filename', attachment.split("/")[-1])

        # TODO: This also catches movies, but I can't work out how they present
        #       differently to images
        if embed_item.type[0] == hangouts.ITEM_TYPE_PLUS_PHOTO:
            data = await self._get_remote_bytes(attachment)
            upload_mime = mime
            decryption_info = None
            if self.encrypted and encrypt_attachment:
                data, decryption_info = encrypt_attachment(data)
                upload_mime = "application/octet-stream"
            mxc_url = await intent.upload_media(data,
                                                mime_type=upload_mime,
                                                filename=filename)
            if decryption_info:
                decryption_info.url = mxc_url
            content = MediaMessageEventContent(url=mxc_url,
                                               file=decryption_info,
                                               body=filename,
                                               info=ImageInfo(size=len(data),
                                                              mimetype=mime),
                                               msgtype=MessageType.IMAGE)
            return await self._send_message(intent,
                                            content,
                                            timestamp=event.timestamp)
        return None
예제 #4
0
 async def _handle_instagram_media(self, source: 'u.User',
                                   intent: IntentAPI,
                                   item: ThreadItem) -> Optional[EventID]:
     # TODO maybe use a dict and item.item_type instead of a ton of ifs
     method = self._reupload_instagram_media
     if item.media:
         media_data = item.media
     elif item.visual_media:
         media_data = item.visual_media.media
     elif item.animated_media:
         media_data = item.animated_media
         method = self._reupload_instagram_animated
     elif item.voice_media:
         media_data = item.voice_media
         method = self._reupload_instagram_voice
     elif item.reel_share:
         media_data = item.reel_share.media
     elif item.story_share:
         media_data = item.story_share.media
     elif item.media_share:
         media_data = item.media_share
     else:
         media_data = None
     if not media_data:
         self.log.debug(f"Unsupported media type in item {item}")
         return None
     elif isinstance(media_data, ExpiredMediaItem):
         self.log.debug(f"Expired media in item {item}")
         # TODO send error message
         return None
     reuploaded = await method(source, media_data, intent)
     if not reuploaded:
         self.log.trace(f"Upload of {media_data} failed")
         # TODO error message?
         return None
     content = MediaMessageEventContent(body=reuploaded.file_name,
                                        external_url=reuploaded.url,
                                        url=reuploaded.mxc,
                                        file=reuploaded.decryption_info,
                                        info=reuploaded.info,
                                        msgtype=reuploaded.msgtype)
     return await self._send_message(intent,
                                     content,
                                     timestamp=item.timestamp // 1000)
예제 #5
0
 async def _handle_facebook_sticker(self, intent: IntentAPI,
                                    sticker: FBSticker,
                                    reply_to: str) -> EventID:
     # TODO handle animated stickers?
     mxc, mime, size, decryption_info = await self._reupload_fb_file(
         sticker.url, intent, encrypt=self.encrypted)
     return await self._send_message(
         intent,
         event_type=EventType.STICKER,
         content=MediaMessageEventContent(
             url=mxc,
             file=decryption_info,
             msgtype=MessageType.STICKER,
             body=sticker.label,
             info=ImageInfo(width=sticker.width,
                            size=size,
                            height=sticker.height,
                            mimetype=mime),
             relates_to=self._get_facebook_reply(reply_to)))
예제 #6
0
    async def handle_telegram_document(self, source: 'AbstractUser', intent: IntentAPI,
                                       evt: Message, relates_to: RelatesTo = None
                                       ) -> Optional[EventID]:
        document = evt.media.document

        attrs = self._parse_telegram_document_attributes(document.attributes)

        if document.size > config["bridge.max_document_size"] * 1000 ** 2:
            name = attrs.name or ""
            caption = f"\n{evt.message}" if evt.message else ""
            return await intent.send_notice(self.mxid, f"Too large file {name}{caption}")

        thumb_loc, thumb_size = self._get_largest_photo_size(document)
        if thumb_size and not isinstance(thumb_size, (PhotoSize, PhotoCachedSize)):
            self.log.debug(f"Unsupported thumbnail type {type(thumb_size)}")
            thumb_loc = None
            thumb_size = None
        parallel_id = source.tgid if config["bridge.parallel_file_transfer"] else None
        file = await util.transfer_file_to_matrix(source.client, intent, document, thumb_loc,
                                                  is_sticker=attrs.is_sticker,
                                                  tgs_convert=config["bridge.animated_sticker"],
                                                  filename=attrs.name, parallel_id=parallel_id)
        if not file:
            return None

        info, name = self._parse_telegram_document_meta(evt, file, attrs, thumb_size)

        await intent.set_typing(self.mxid, is_typing=False)

        event_type = EventType.ROOM_MESSAGE
        # Riot only supports images as stickers, so send animated webm stickers as m.video
        if attrs.is_sticker and file.mime_type.startswith("image/"):
            event_type = EventType.STICKER
        content = MediaMessageEventContent(
            body=name or "unnamed file", info=info, url=file.mxc, relates_to=relates_to,
            external_url=self._get_external_url(evt),
            msgtype={
                "video/": MessageType.VIDEO,
                "audio/": MessageType.AUDIO,
                "image/": MessageType.IMAGE,
            }.get(info.mimetype[:6], MessageType.FILE))
        return await intent.send_message_event(self.mxid, event_type, content, timestamp=evt.date)
예제 #7
0
 def _make_media_content(attachment: Attachment) -> MediaMessageEventContent:
     if attachment.content_type.startswith("image/"):
         msgtype = MessageType.IMAGE
         info = ImageInfo(mimetype=attachment.content_type,
                          width=attachment.width, height=attachment.height)
     elif attachment.content_type.startswith("video/"):
         msgtype = MessageType.VIDEO
         info = VideoInfo(mimetype=attachment.content_type,
                          width=attachment.width, height=attachment.height)
     elif attachment.voice_note or attachment.content_type.startswith("audio/"):
         msgtype = MessageType.AUDIO
         info = AudioInfo(mimetype=attachment.content_type)
     else:
         msgtype = MessageType.FILE
         info = FileInfo(mimetype=attachment.content_type)
     if not attachment.custom_filename:
         ext = mimetypes.guess_extension(attachment.content_type) or ""
         attachment.custom_filename = attachment.id + ext
     return MediaMessageEventContent(msgtype=msgtype, info=info,
                                     body=attachment.custom_filename)
예제 #8
0
 async def _reupload(self,
                     status: int) -> Optional[MediaMessageEventContent]:
     url = self.config["url"].format(status=status)
     self.log.info(f"Reuploading {url}")
     resp = await self.http.get(url)
     if resp.status != 200:
         resp.raise_for_status()
     data = await resp.read()
     img = Image.open(BytesIO(data))
     width, height = img.size
     mimetype = Image.MIME[img.format]
     filename = f"{status}{guess_extension(mimetype)}"
     mxc = await self.client.upload_media(data, mimetype, filename=filename)
     return MediaMessageEventContent(msgtype=MessageType.IMAGE,
                                     body=filename,
                                     url=mxc,
                                     info=ImageInfo(mimetype=mimetype,
                                                    size=len(data),
                                                    width=width,
                                                    height=height))
예제 #9
0
 async def send_movie_info(self, evt: MessageEvent, movie) -> None:
     mxc_uri = await self.client.upload_media(data=movie.get_image_binary())
     text_message = f'{movie.title}'
     if len(movie.overview) > 200:
         three_dotts = " [...]"
     else:
         three_dotts = ""
     html_message = f"""<p><b>{escape(movie.title)}</b></p>
     <p>{escape(movie.overview)[:200]}{three_dotts}</p>
     <p>taken from www.themoviedb.org</p>"""
     content = TextMessageEventContent(
         msgtype=MessageType.TEXT, format=Format.HTML,
         body=f"{text_message}",
         formatted_body=f"{html_message}")
     await evt.respond(content)
     content = MediaMessageEventContent(
         msgtype=MessageType.IMAGE,
         body=f"Image {movie.title}",
         url=f"{mxc_uri}")
     await evt.respond(content)
예제 #10
0
 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)
예제 #11
0
    def send_file(
        self,
        room_id: RoomID,
        url: ContentURI,
        info: BaseFileInfo | None = None,
        file_name: str | None = None,
        file_type: MessageType = MessageType.FILE,
        relates_to: RelatesTo | None = None,
        **kwargs,
    ) -> Awaitable[EventID]:
        """
        Send a file to a room.

        Args:
            room_id: The ID of the room to send the message to.
            url: The Matrix content repository URI of the file. You can upload files using
                :meth:`~MediaRepositoryMethods.upload_media`.
            info: Additional metadata about the file, e.g. mimetype, image size, video duration, etc
            file_name: The name for the file to send.
            file_type: The general file type to send. The file type can be further specified by
                setting the ``mimetype`` field of the ``info`` parameter. Defaults to
                :attr:`MessageType.FILE` (unspecified file type, e.g. document)
            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.
        """
        return self.send_message(
            room_id,
            MediaMessageEventContent(url=url,
                                     info=info,
                                     body=file_name,
                                     relates_to=relates_to,
                                     msgtype=file_type),
            **kwargs,
        )
예제 #12
0
 async def _handle_twitter_attachment(
         self, source: 'u.User', sender: 'p.Puppet',
         message: MessageData) -> Optional[MediaMessageEventContent]:
     content = None
     intent = sender.intent_for(self)
     media = message.attachment.media
     if media:
         # TODO this doesn't actually work for gifs and videos
         #      The actual url is in media.video_info.variants[0].url
         #      Gifs are also videos
         reuploaded_info = await self._reupload_twitter_media(
             source, media, intent)
         content = MediaMessageEventContent(
             body=reuploaded_info.file_name,
             url=reuploaded_info.mxc,
             file=reuploaded_info.decryption_info,
             external_url=media.media_url_https)
         if message.attachment.video:
             content.msgtype = MessageType.VIDEO
             content.info = VideoInfo(
                 mimetype=reuploaded_info.mime_type,
                 size=reuploaded_info.size,
                 width=media.original_info.width,
                 height=media.original_info.height,
                 duration=media.video_info.duration_millis // 1000)
         elif message.attachment.photo or message.attachment.animated_gif:
             content.msgtype = MessageType.IMAGE
             content.info = ImageInfo(mimetype=reuploaded_info.mime_type,
                                      size=reuploaded_info.size,
                                      width=media.original_info.width,
                                      height=media.original_info.height)
         # Remove the attachment link from message.text
         start, end = media.indices
         message.text = message.text[:start] + message.text[end:]
     elif message.attachment.tweet:
         # TODO special handling for tweets?
         pass
     return content
예제 #13
0
 async def handle_telegram_photo(
         self,
         source: 'AbstractUser',
         intent: IntentAPI,
         evt: Message,
         relates_to: RelatesTo = None) -> Optional[EventID]:
     media: MessageMediaPhoto = evt.media
     if media.photo is None and media.ttl_seconds:
         return await self._send_message(
             intent,
             TextMessageEventContent(msgtype=MessageType.NOTICE,
                                     body="Photo has expired"))
     loc, largest_size = self._get_largest_photo_size(media.photo)
     if loc is None:
         content = TextMessageEventContent(
             msgtype=MessageType.TEXT,
             body="Failed to bridge image",
             external_url=self._get_external_url(evt))
         return await self._send_message(intent,
                                         content,
                                         timestamp=evt.date)
     file = await util.transfer_file_to_matrix(source.client,
                                               intent,
                                               loc,
                                               encrypt=self.encrypted)
     if not file:
         return None
     if self.get_config("inline_images") and (evt.message or evt.fwd_from
                                              or evt.reply_to_msg_id):
         content = await formatter.telegram_to_matrix(
             evt,
             source,
             self.main_intent,
             prefix_html=
             f"<img src='{file.mxc}' alt='Inline Telegram photo'/><br/>",
             prefix_text="Inline image: ")
         content.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)
     info = ImageInfo(height=largest_size.h,
                      width=largest_size.w,
                      orientation=0,
                      mimetype=file.mime_type,
                      size=(len(largest_size.bytes) if
                            (isinstance(largest_size, PhotoCachedSize)) else
                            largest_size.size))
     ext = sane_mimetypes.guess_extension(file.mime_type)
     name = f"disappearing_image{ext}" if media.ttl_seconds else f"image{ext}"
     await intent.set_typing(self.mxid, is_typing=False)
     content = MediaMessageEventContent(
         msgtype=MessageType.IMAGE,
         info=info,
         body=name,
         relates_to=relates_to,
         external_url=self._get_external_url(evt))
     if file.decryption_info:
         content.file = file.decryption_info
     else:
         content.url = file.mxc
     result = await self._send_message(intent, content, timestamp=evt.date)
     if media.ttl_seconds:
         self.loop.create_task(
             self._expire_telegram_photo(intent, result, media.ttl_seconds))
     if evt.message:
         caption_content = await formatter.telegram_to_matrix(
             evt, source, self.main_intent, no_reply_fallback=True)
         caption_content.external_url = content.external_url
         result = await self._send_message(intent,
                                           caption_content,
                                           timestamp=evt.date)
     return result
예제 #14
0
    async def handle_telegram_document(
            self,
            source: 'AbstractUser',
            intent: IntentAPI,
            evt: Message,
            relates_to: RelatesTo = None) -> Optional[EventID]:
        document = evt.media.document

        attrs = self._parse_telegram_document_attributes(document.attributes)

        if document.size > config["bridge.max_document_size"] * 1000**2:
            name = attrs.name or ""
            caption = f"\n{evt.message}" if evt.message else ""
            # TODO encrypt
            return await intent.send_notice(self.mxid,
                                            f"Too large file {name}{caption}")

        thumb_loc, thumb_size = self._get_largest_photo_size(document)
        if thumb_size and not isinstance(thumb_size,
                                         (PhotoSize, PhotoCachedSize)):
            self.log.debug(f"Unsupported thumbnail type {type(thumb_size)}")
            thumb_loc = None
            thumb_size = None
        parallel_id = source.tgid if config[
            "bridge.parallel_file_transfer"] else None
        file = await util.transfer_file_to_matrix(
            source.client,
            intent,
            document,
            thumb_loc,
            is_sticker=attrs.is_sticker,
            tgs_convert=config["bridge.animated_sticker"],
            filename=attrs.name,
            parallel_id=parallel_id,
            encrypt=self.encrypted)
        if not file:
            return None

        info, name = self._parse_telegram_document_meta(
            evt, file, attrs, thumb_size)

        await intent.set_typing(self.mxid, is_typing=False)

        event_type = EventType.ROOM_MESSAGE
        # Elements only support images as stickers, so send animated webm stickers as m.video
        if attrs.is_sticker and file.mime_type.startswith("image/"):
            event_type = EventType.STICKER
            # Tell clients to render the stickers as 256x256 if they're bigger
            if info.width > 256 or info.height > 256:
                if info.width > info.height:
                    info.height = int(info.height / (info.width / 256))
                    info.width = 256
                else:
                    info.width = int(info.width / (info.height / 256))
                    info.height = 256
            if info.thumbnail_info:
                info.thumbnail_info.width = info.width
                info.thumbnail_info.height = info.height
        if attrs.is_gif:
            info["fi.mau.telegram.gif"] = True
            info["fi.mau.loop"] = True
            info["fi.mau.autoplay"] = True
            info["fi.mau.no_audio"] = True

        content = MediaMessageEventContent(
            body=name or "unnamed file",
            info=info,
            relates_to=relates_to,
            external_url=self._get_external_url(evt),
            msgtype={
                "video/": MessageType.VIDEO,
                "audio/": MessageType.AUDIO,
                "image/": MessageType.IMAGE,
            }.get(info.mimetype[:6], MessageType.FILE))
        if file.decryption_info:
            content.file = file.decryption_info
        else:
            content.url = file.mxc
        res = await self._send_message(intent,
                                       content,
                                       event_type=event_type,
                                       timestamp=evt.date)
        if evt.message:
            caption_content = await formatter.telegram_to_matrix(
                evt, source, self.main_intent, no_reply_fallback=True)
            caption_content.external_url = content.external_url
            res = await self._send_message(intent,
                                           caption_content,
                                           timestamp=evt.date)
        return res
예제 #15
0
from mautrix.types import (MessageType, MediaMessageEventContent, ImageInfo,
                           ThumbnailInfo, ContentURI)

gif_versions: Dict[str, MediaMessageEventContent] = {
    "crap":
    MediaMessageEventContent(
        msgtype=MessageType.IMAGE,
        body="putkiteippi.gif",
        url=ContentURI("mxc://maunium.net/IkSoSYYrtaYJQeCaABSLqKiD"),
        info=ImageInfo(
            mimetype="image/gif",
            width=364,
            height=153,
            size=2079294,
            thumbnail_url=ContentURI(
                "mxc://maunium.net/iivOnCDjcGqGvnwnNWxSbAvb"),
            thumbnail_info=ThumbnailInfo(
                mimetype="image/png",
                width=364,
                height=153,
                size=51302,
            ),
        ),
    ),
    "low":
    MediaMessageEventContent(
        msgtype=MessageType.IMAGE,
        body="putkiteippi.gif",
        url=ContentURI("mxc://maunium.net/PXbFbVEmcGzkaMXwUOrzZcQM"),
        info=ImageInfo(
예제 #16
0
 async def _handle_facebook_attachment(self, intent: IntentAPI,
                                       attachment: AttachmentClass,
                                       reply_to: str) -> Optional[EventID]:
     if isinstance(attachment, AudioAttachment):
         mxc, mime, size, decryption_info = await self._reupload_fb_file(
             attachment.url,
             intent,
             attachment.filename,
             encrypt=self.encrypted)
         event_id = await self._send_message(
             intent,
             MediaMessageEventContent(
                 url=mxc,
                 file=decryption_info,
                 msgtype=MessageType.AUDIO,
                 body=attachment.filename,
                 info=AudioInfo(size=size,
                                mimetype=mime,
                                duration=attachment.duration),
                 relates_to=self._get_facebook_reply(reply_to)))
     # elif isinstance(attachment, VideoAttachment):
     # TODO
     elif isinstance(attachment, FileAttachment):
         mxc, mime, size, decryption_info = await self._reupload_fb_file(
             attachment.url,
             intent,
             attachment.name,
             encrypt=self.encrypted)
         event_id = await self._send_message(
             intent,
             MediaMessageEventContent(
                 url=mxc,
                 file=decryption_info,
                 msgtype=MessageType.FILE,
                 body=attachment.name,
                 info=FileInfo(size=size, mimetype=mime),
                 relates_to=self._get_facebook_reply(reply_to)))
     elif isinstance(attachment, ImageAttachment):
         mxc, mime, size, decryption_info = await self._reupload_fb_file(
             attachment.large_preview_url or attachment.preview_url,
             intent,
             encrypt=self.encrypted)
         event_id = await self._send_message(
             intent,
             MediaMessageEventContent(
                 url=mxc,
                 file=decryption_info,
                 msgtype=MessageType.IMAGE,
                 body=f"image.{attachment.original_extension}",
                 info=ImageInfo(size=size,
                                mimetype=mime,
                                width=attachment.large_preview_width,
                                height=attachment.large_preview_height),
                 relates_to=self._get_facebook_reply(reply_to)))
     elif isinstance(attachment, LocationAttachment):
         content = await self._convert_facebook_location(intent, attachment)
         content.relates_to = self._get_facebook_reply(reply_to)
         event_id = await self._send_message(intent, content)
     elif isinstance(attachment, ShareAttachment):
         # These are handled in the text formatter
         return None
     else:
         self.log.warning(f"Unsupported attachment type: {attachment}")
         return None
     self._last_bridged_mxid = event_id
     return event_id