async def _handle_signal_attachment( self, intent: IntentAPI, attachment: Attachment) -> MediaMessageEventContent: self.log.trace(f"Reuploading attachment {attachment}") if not attachment.content_type: attachment.content_type = (magic.from_file( attachment.incoming_filename, mime=True) if magic is not None else "application/octet-stream") content = self._make_media_content(attachment) with open(attachment.incoming_filename, "rb") as file: data = file.read() if self.config["signal.remove_file_after_handling"]: os.remove(attachment.incoming_filename) upload_mime_type = attachment.content_type if self.encrypted and encrypt_attachment: data, content.file = encrypt_attachment(data) upload_mime_type = "application/octet-stream" content.url = await intent.upload_media(data, mime_type=upload_mime_type, filename=attachment.id) if content.file: content.file.url = content.url content.url = None return content
async def _reupload_instagram_file(self, source: 'u.User', url: str, msgtype: MessageType, info: FileInfo, intent: IntentAPI ) -> Optional[ReuploadedMediaInfo]: async with await source.client.raw_http_get(url) as resp: data = await resp.read() info.mimetype = resp.headers["Content-Type"] or magic.from_buffer(data, mime=True) info.size = len(data) extension = { "image/webp": ".webp", "image/jpeg": ".jpg", "video/mp4": ".mp4", "audio/mp4": ".m4a", }.get(info.mimetype) extension = extension or mimetypes.guess_extension(info.mimetype) or "" file_name = f"{msgtype.value[2:]}{extension}" upload_mime_type = info.mimetype upload_file_name = file_name decryption_info = None if self.encrypted and encrypt_attachment: data, decryption_info = encrypt_attachment(data) upload_mime_type = "application/octet-stream" upload_file_name = None mxc = await call_with_net_retry(intent.upload_media, data, mime_type=upload_mime_type, filename=upload_file_name, _action="upload media") if decryption_info: decryption_info.url = mxc mxc = None return ReuploadedMediaInfo(mxc=mxc, url=url, decryption_info=decryption_info, file_name=file_name, msgtype=msgtype, info=info)
async def _reupload_twitter_media( self, source: 'u.User', url: str, intent: IntentAPI) -> ReuploadedMediaInfo: file_name = URL(url).name data, mime_type = await source.client.download_media(url) upload_mime_type = mime_type upload_file_name = file_name decryption_info = None if self.encrypted and encrypt_attachment: data, decryption_info = encrypt_attachment(data) upload_mime_type = "application/octet-stream" upload_file_name = None mxc = await call_with_net_retry(intent.upload_media, data, mime_type=upload_mime_type, filename=upload_file_name, _action="upload media") if decryption_info: decryption_info.url = mxc mxc = None return ReuploadedMediaInfo(mxc, decryption_info, mime_type, file_name, len(data))
async def transfer_thumbnail_to_matrix( client: MautrixTelegramClient, intent: IntentAPI, thumbnail_loc: TypeLocation, video: bytes, mime: str, encrypt: bool) -> Optional[DBTelegramFile]: if not Image or not VideoFileClip: return None loc_id = _location_to_id(thumbnail_loc) if not loc_id: return None db_file = DBTelegramFile.get(loc_id) if db_file: return db_file video_ext = sane_mimetypes.guess_extension(mime) if VideoFileClip and video_ext and video: try: file, width, height = _read_video_thumbnail(video, video_ext, frame_ext="png") except OSError: return None mime_type = "image/png" else: file = await client.download_file(thumbnail_loc) width, height = None, None mime_type = magic.from_buffer(file, mime=True) decryption_info = None upload_mime_type = mime_type if encrypt: file, decryption_info_dict = encrypt_attachment(file) decryption_info = EncryptedFile.deserialize(decryption_info_dict) upload_mime_type = "application/octet-stream" content_uri = await intent.upload_media(file, upload_mime_type) if decryption_info: decryption_info.url = content_uri db_file = DBTelegramFile(id=loc_id, mxc=content_uri, mime_type=mime_type, was_converted=False, timestamp=int(time.time()), size=len(file), width=width, height=height, decryption_info=decryption_info) try: db_file.insert() except (IntegrityError, InvalidRequestError) as e: log.exception( f"{e.__class__.__name__} while saving transferred file thumbnail data. " "This was probably caused by two simultaneous transfers of the same file, " "and might (but probably won't) cause problems with thumbnails or something." ) return db_file
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_dict = encrypt_attachment(data) decryption_info = EncryptedFile.deserialize( decryption_info_dict) 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
async def _handle_signal_attachment( self, intent: IntentAPI, attachment: Attachment) -> MediaMessageEventContent: self.log.trace(f"Reuploading attachment {attachment}") if not attachment.content_type: attachment.content_type = (magic.from_file( attachment.incoming_filename, mime=True) if magic is not None else "application/octet-stream") content = self._make_media_content(attachment) # The bridge and signald can share files but have different filepaths. This can happen in # a Docker deployment when signald and this bridge are in different containers. In this # case, convert the file path from one context to another incoming_attachment_dir = self.config["signal.incoming_attachment_dir"] if incoming_attachment_dir is None: path = attachment.incoming_filename else: filename = os.path.basename(attachment.incoming_filename) path = os.path.join(self.config["signal.outgoing_attachment_dir"], filename) self.log.debug( f"Changing attachment from {attachment.incoming_filename} to {path}" ) with open(path, "rb") as file: data = file.read() if self.config["signal.remove_file_after_handling"]: os.remove(path) upload_mime_type = attachment.content_type if self.encrypted and encrypt_attachment: data, content.file = encrypt_attachment(data) upload_mime_type = "application/octet-stream" content.url = await intent.upload_media(data, mime_type=upload_mime_type, filename=attachment.id) if content.file: content.file.url = content.url content.url = None return content
async def _reupload_twitter_media( self, source: 'u.User', attachment: MessageAttachmentMedia, intent: IntentAPI) -> ReuploadedMediaInfo: file_name = path.basename(attachment.media_url_https) data, mime_type = await source.client.download_media(attachment) upload_mime_type = mime_type upload_file_name = file_name decryption_info = None if self.encrypted and encrypt_attachment: data, decryption_info = encrypt_attachment(data) upload_mime_type = "application/octet-stream" upload_file_name = None mxc = await intent.upload_media(data, mime_type=upload_mime_type, filename=upload_file_name) if decryption_info: decryption_info.url = mxc mxc = None return ReuploadedMediaInfo(mxc, decryption_info, mime_type, file_name, len(data))
async def _unlocked_transfer_file_to_matrix( client: MautrixTelegramClient, intent: IntentAPI, loc_id: str, location: TypeLocation, thumbnail: TypeThumbnail, is_sticker: bool, tgs_convert: Optional[dict], filename: Optional[str], encrypt: bool, parallel_id: Optional[int]) -> Optional[DBTelegramFile]: db_file = DBTelegramFile.get(loc_id) if db_file: return db_file converted_anim = None if parallel_id and isinstance(location, Document) and (not is_sticker or not tgs_convert): db_file = await parallel_transfer_to_matrix(client, intent, loc_id, location, filename, encrypt, parallel_id) mime_type = location.mime_type file = None else: try: file = await client.download_file(location) except (LocationInvalidError, FileIdInvalidError): return None except (AuthBytesInvalidError, AuthKeyInvalidError, SecurityError) as e: log.exception(f"{e.__class__.__name__} while downloading a file.") return None width, height = None, None mime_type = magic.from_buffer(file, mime=True) image_converted = False # A weird bug in alpine/magic makes it return application/octet-stream for gzips... is_tgs = (mime_type == "application/gzip" or (mime_type == "application/octet-stream" and magic.from_buffer(file).startswith("gzip"))) if is_sticker and tgs_convert and is_tgs: converted_anim = await convert_tgs_to(file, tgs_convert["target"], **tgs_convert["args"]) mime_type = converted_anim.mime file = converted_anim.data width, height = converted_anim.width, converted_anim.height image_converted = mime_type != "application/gzip" thumbnail = None if mime_type == "image/webp": new_mime_type, file, width, height = convert_image( file, source_mime="image/webp", target_type="png", thumbnail_to=(256, 256) if is_sticker else None) image_converted = new_mime_type != mime_type mime_type = new_mime_type thumbnail = None decryption_info = None upload_mime_type = mime_type if encrypt and encrypt_attachment: file, decryption_info = encrypt_attachment(file) upload_mime_type = "application/octet-stream" content_uri = await intent.upload_media(file, upload_mime_type) if decryption_info: decryption_info.url = content_uri db_file = DBTelegramFile(id=loc_id, mxc=content_uri, decryption_info=decryption_info, mime_type=mime_type, was_converted=image_converted, timestamp=int(time.time()), size=len(file), width=width, height=height) if thumbnail and (mime_type.startswith("video/") or mime_type == "image/gif"): if isinstance(thumbnail, (PhotoSize, PhotoCachedSize)): thumbnail = thumbnail.location try: db_file.thumbnail = await transfer_thumbnail_to_matrix( client, intent, thumbnail, video=file, mime_type=mime_type, encrypt=encrypt) except FileIdInvalidError: log.warning(f"Failed to transfer thumbnail for {thumbnail!s}", exc_info=True) elif converted_anim and converted_anim.thumbnail_data: db_file.thumbnail = await transfer_thumbnail_to_matrix( client, intent, location, video=None, encrypt=encrypt, custom_data=converted_anim.thumbnail_data, mime_type=converted_anim.thumbnail_mime, width=converted_anim.width, height=converted_anim.height) try: db_file.insert() except (IntegrityError, InvalidRequestError) as e: log.exception( f"{e.__class__.__name__} while saving transferred file data. " "This was probably caused by two simultaneous transfers of the same file, " "and should not cause any problems.") return db_file