Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bridge files as links #957

Merged
merged 3 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions mautrix_telegram/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ def do_update(self, helper: ConfigUpdateHelper) -> None:
copy("bridge.caption_in_message")
copy("bridge.image_as_file_size")
copy("bridge.image_as_file_pixels")
copy("bridge.document_as_link_size.bot")
copy("bridge.document_as_link_size.channel")
copy("bridge.parallel_file_transfer")
copy("bridge.federate_rooms")
copy("bridge.always_custom_emoji_reaction")
Expand Down
5 changes: 5 additions & 0 deletions mautrix_telegram/example-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,11 @@ bridge:
image_as_file_size: 10
# Maximum number of pixels in an image before sending to Telegram as a document. Defaults to 4096x4096 = 16777216.
image_as_file_pixels: 16777216
# Maximum size of Telegram documents before linking to Telegrm instead of bridge
# to Matrix media.
document_as_link_size:
channel:
bot:
# Enable experimental parallel file transfer, which makes uploads/downloads much faster by
# streaming from/to Matrix and using many connections for Telegram.
# Note that generating HQ thumbnails for videos is not possible with streamed transfers.
Expand Down
9 changes: 7 additions & 2 deletions mautrix_telegram/portal.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,10 @@ def peer(self) -> TypePeer | TypeInputPeer:
def is_direct(self) -> bool:
return self.peer_type == "user"

@property
def is_channel(self) -> bool:
return self.peer_type == "channel"

@property
def has_bot(self) -> bool:
return bool(self.bot) and (
Expand Down Expand Up @@ -2809,7 +2813,7 @@ async def handle_telegram_edit(
intent = sender.intent_for(self) if sender else self.main_intent
is_bot = sender.is_bot if sender else False
converted = await self._msg_conv.convert(
source, intent, is_bot, evt, no_reply_fallback=True
source, intent, is_bot, self.is_channel, evt, no_reply_fallback=True
)
converted.content.set_edit(editing_msg.mxid)
await intent.set_typing(self.mxid, timeout=0)
Expand Down Expand Up @@ -3025,6 +3029,7 @@ async def _convert_batch_msg(
source,
intent,
is_bot,
self.is_channel,
msg,
client=client,
deterministic_reply_id=self.bridge.homeserver_software.is_hungry,
Expand Down Expand Up @@ -3529,7 +3534,7 @@ async def _handle_telegram_message(
else:
intent = self.main_intent
is_bot = sender.is_bot if sender else False
converted = await self._msg_conv.convert(source, intent, is_bot, evt)
converted = await self._msg_conv.convert(source, intent, is_bot, self.is_channel, evt)
if not converted:
return
await intent.set_typing(self.mxid, timeout=0)
Expand Down
105 changes: 103 additions & 2 deletions mautrix_telegram/portal_util/message_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ async def convert(
source: au.AbstractUser,
intent: IntentAPI,
is_bot: bool,
is_channel: bool,
evt: Message,
no_reply_fallback: bool = False,
deterministic_reply_id: bool = False,
Expand All @@ -169,8 +170,13 @@ async def convert(
if not client:
client = source.client
if hasattr(evt, "media") and isinstance(evt.media, self._allowed_media):
convert_media = self._media_converters[type(evt.media)]
converted = await convert_media(source=source, intent=intent, evt=evt, client=client)
if self._should_convert_full_document(evt.media, is_bot, is_channel):
convert_media = self._media_converters[type(evt.media)]
converted = await convert_media(
source=source, intent=intent, evt=evt, client=client
)
else:
converted = await self._convert_document_thumb_only(source, intent, evt, client)
elif evt.message:
converted = await self._convert_text(source, intent, is_bot, evt, client)
else:
Expand Down Expand Up @@ -203,6 +209,16 @@ async def convert(
)
return converted

def _should_convert_full_document(self, media, is_bot: bool, is_channel: bool) -> bool:
if not isinstance(media, MessageMediaDocument):
return True
size = media.document.size
if is_bot and self.config["bridge.document_as_link_size.bot"]:
return size < self.config["bridge.document_as_link_size.bot"] * 1000**2
if is_channel and self.config["bridge.document_as_link_size.channel"]:
return size < self.config["bridge.document_as_link_size.channel"] * 1000**2
return True

@staticmethod
def _caption_to_message(converted: ConvertedMessage) -> None:
content, caption = converted.content, converted.caption
Expand Down Expand Up @@ -492,6 +508,91 @@ def _adjust_ttl(ttl: int | None) -> int | None:
# but we can only count it from read receipt.
return ttl * 5

async def _convert_document_thumb_only(
self,
source: au.AbstractUser,
intent: IntentAPI,
evt: Message,
client: MautrixTelegramClient,
) -> ConvertedMessage | None:
document = evt.media.document

if not document:
return None

external_link_content = "Unsupported file, please access directly on Telegram"

external_url = self._get_external_url(evt)
# We don't generate external URLs for bot users so only set if known
if external_url is not None:
external_link_content = (
f"Unsupported file, please access directly on Telegram here: {external_url}"
)

attrs = _parse_document_attributes(document.attributes)
file = None

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
if thumb_loc:
try:
file = await util.transfer_thumbnail_to_matrix(
client,
intent,
thumb_loc,
video=None,
mime_type=document.mime_type,
encrypt=self.portal.encrypted,
async_upload=self.config["homeserver.async_media"],
)
except Exception:
self.log.exception("Failed to transfer thumbnail")
if not file:
name = attrs.name or ""
caption = f"\n{evt.message}" if evt.message else ""
return ConvertedMessage(
content=TextMessageEventContent(
msgtype=MessageType.NOTICE,
body=f"{name}{caption}\n{external_link_content}",
)
)

info, name = _parse_document_meta(evt, file, attrs, thumb_size)

event_type = EventType.ROOM_MESSAGE
if not name:
ext = sane_mimetypes.guess_extension(file.mime_type) or ""
name = "unnamed_file" + ext

content = MediaMessageEventContent(
body=name,
info=info,
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

caption_content = (
await formatter.telegram_to_matrix(evt, source, client) if evt.message else None
)
caption_content = f"{caption_content}\n{external_link_content}"

return ConvertedMessage(
type=event_type,
content=content,
caption=caption_content,
disappear_seconds=self._adjust_ttl(evt.media.ttl_seconds),
)

async def _convert_document(
self,
source: au.AbstractUser,
Expand Down
1 change: 1 addition & 0 deletions mautrix_telegram/util/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
convert_image,
transfer_custom_emojis_to_matrix,
transfer_file_to_matrix,
transfer_thumbnail_to_matrix,
unicode_custom_emoji_map,
)
from .parallel_file_transfer import parallel_transfer_to_telegram
Expand Down