Split up telegram document handling and send stickers as m.sticker

Also add sticker resizing (max 256x256). Cached stickers won't be resized,
delete the `telegram_file` database table if you want all stickers to be
resized.

Fixes #104
This commit is contained in:
Tulir Asokan
2018-04-07 00:32:15 +03:00
parent 4498ab4721
commit 006a5971ea
2 changed files with 85 additions and 34 deletions
+77 -29
View File
@@ -19,6 +19,7 @@ from datetime import datetime
import asyncio
import random
import mimetypes
import unicodedata
import hashlib
import logging
import re
@@ -926,29 +927,51 @@ class Portal:
relates_to=relates_to, timestamp=evt.date,
external_url=self.get_external_url(evt))
async def handle_telegram_document(self, source, intent, evt: Message, relates_to=None):
document = evt.media.document
file = await util.transfer_file_to_matrix(self.db, source.client, intent, document,
document.thumb)
if not file:
return None
name = evt.message
width, height = file.width, file.height
for attr in document.attributes:
@staticmethod
def _parse_telegram_document_attributes(attributes):
attrs = {
"name": None,
"mime_type": None,
"is_sticker": False,
"sticker_alt": None,
"width": None,
"height": None,
}
for attr in attributes:
if isinstance(attr, DocumentAttributeFilename):
name = name or attr.file_name
if not file.was_converted:
(mime_from_name, _) = mimetypes.guess_type(name)
file.mime_type = mime_from_name or file.mime_type
attrs["name"] = attrs["name"] or attr.file_name
attrs["mime_type"] = mimetypes.guess_type(attr.file_name)
elif isinstance(attr, DocumentAttributeSticker):
name = f"Sticker for {attr.alt}"
elif isinstance(attr, DocumentAttributeVideo) and (not width or not height):
width, height = attr.w, attr.h
attrs["is_sticker"] = True
attrs["sticker_alt"] = attr.alt
attrs["name"] = f"{attr.alt} ({unicodedata.name(attr.alt[0]).lower()})"
elif isinstance(attr, DocumentAttributeVideo):
attrs["width"], attrs["height"] = attr.w, attr.h
print([str(attr) for attr in attributes])
print(attrs)
return attrs
@staticmethod
def _parse_telegram_document_meta(evt, file, attrs):
document = evt.media.document
name = evt.message or attrs["name"]
if attrs["is_sticker"]:
alt = attrs["sticker_alt"]
name = f"{alt} ({unicodedata.name(alt[0]).lower()})"
mime_type = document.mime_type or file.mime_type
info = {
"size": file.size,
"mimetype": mime_type,
}
if attrs["mime_type"] and not file.was_converted:
file.mime_type = attrs["mime_type"] or file.mime_type
if file.width and file.height:
info["w"], info["h"] = file.width, file.height
elif attrs["width"] and attrs["height"]:
info["w"], info["h"] = attrs["width"], attrs["height"]
if file.thumbnail:
info["thumbnail_url"] = file.thumbnail.mxc
info["thumbnail_info"] = {
@@ -957,20 +980,45 @@ class Portal:
"w": file.thumbnail.width or document.thumb.w,
"size": file.thumbnail.size,
}
if height and width:
info["h"] = height
info["w"] = width
type = "m.file"
if mime_type.startswith("video/"):
type = "m.video"
elif mime_type.startswith("audio/"):
type = "m.audio"
elif mime_type.startswith("image/"):
type = "m.image"
return info, name
async def handle_telegram_document(self, source, intent, evt: Message, relates_to=None):
document = evt.media.document
attrs = self._parse_telegram_document_attributes(document.attributes)
file = await util.transfer_file_to_matrix(self.db, source.client, intent, document,
document.thumb, is_sticker=attrs["is_sticker"])
if not file:
return None
info, name = self._parse_telegram_document_meta(evt, file, attrs)
await intent.set_typing(self.mxid, is_typing=False)
return await intent.send_file(self.mxid, file.mxc, info=info, text=name, file_type=type,
relates_to=relates_to, timestamp=evt.date,
external_url=self.get_external_url(evt))
kwargs = {
"room_id": self.mxid,
"url": file.mxc,
"info": info,
"text": name,
"relates_to": relates_to,
"timestamp": evt.date,
"external_url": self.get_external_url(evt)
}
if attrs["is_sticker"]:
return await intent.send_sticker(**kwargs)
mime_type = info["mimetype"]
if mime_type.startswith("video/"):
kwargs["type"] = "m.video"
elif mime_type.startswith("audio/"):
kwargs["type"] = "m.audio"
elif mime_type.startswith("image/"):
kwargs["type"] = "m.image"
else:
kwargs["type"] = "m.file"
return await intent.send_file(**kwargs)
def handle_telegram_location(self, source, intent, evt, relates_to=None):
location = evt.media.geo
+8 -5
View File
@@ -44,18 +44,20 @@ from ..db import TelegramFile as DBTelegramFile
log = logging.getLogger("mau.util")
def _convert_webp(file, to="png"):
def _convert_webp(file, to="png", thumbnail_to=None):
if not Image:
return "image/webp", file
return "image/webp", file, None, None
try:
image = Image.open(BytesIO(file)).convert("RGBA")
if thumbnail_to:
image.thumbnail(thumbnail_to, Image.ANTIALIAS)
new_file = BytesIO()
image.save(new_file, to)
w, h = image.size
return f"image/{to}", new_file.getvalue(), w, h
except Exception:
log.exception(f"Failed to convert webp to {to}")
return "image/webp", file
return "image/webp", file, None, None
def _temp_file_name(ext):
@@ -123,7 +125,7 @@ async def transfer_thumbnail_to_matrix(client, intent, thumbnail_loc, video, mim
width=width, height=height)
async def transfer_file_to_matrix(db, client, intent, location, thumbnail=None):
async def transfer_file_to_matrix(db, client, intent, location, thumbnail=None, is_sticker=False):
id = _location_to_id(location)
if not id:
return None
@@ -141,7 +143,8 @@ async def transfer_file_to_matrix(db, client, intent, location, thumbnail=None):
image_converted = False
if mime_type == "image/webp":
mime_type, file, width, height = _convert_webp(file, to="png")
mime_type, file, width, height = _convert_webp(file, to="png", thumbnail_to=(
256, 256) if is_sticker else None)
thumbnail = None
image_converted = True