Save and reuse MXC URIs of bridged files. Fixes #40

This commit is contained in:
Tulir Asokan
2018-02-19 23:27:38 +02:00
parent c21a55ebc7
commit f9d2d32ef0
2 changed files with 62 additions and 19 deletions
+13 -1
View File
@@ -14,7 +14,8 @@
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from sqlalchemy import Column, UniqueConstraint, ForeignKey, ForeignKeyConstraint, Integer, String from sqlalchemy import (Column, UniqueConstraint, ForeignKey, ForeignKeyConstraint, Integer,
String, Boolean)
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from .base import Base from .base import Base
@@ -103,6 +104,16 @@ class BotChat(Base):
type = Column(String, nullable=False) type = Column(String, nullable=False)
class TelegramFile(Base):
query = None
__tablename__ = "telegram_file"
id = Column(String, primary_key=True)
mxc = Column(String)
mime_type = Column(String)
was_converted = Column(Boolean)
def init(db_session): def init(db_session):
Portal.query = db_session.query_property() Portal.query = db_session.query_property()
Message.query = db_session.query_property() Message.query = db_session.query_property()
@@ -110,3 +121,4 @@ def init(db_session):
User.query = db_session.query_property() User.query = db_session.query_property()
Puppet.query = db_session.query_property() Puppet.query = db_session.query_property()
BotChat.query = db_session.query_property() BotChat.query = db_session.query_property()
TelegramFile.query = db_session.query_property()
+49 -18
View File
@@ -32,7 +32,7 @@ from telethon.errors.rpc_error_list import *
from telethon.tl.types import * from telethon.tl.types import *
from mautrix_appservice import MatrixRequestError, IntentError from mautrix_appservice import MatrixRequestError, IntentError
from .db import Portal as DBPortal, Message as DBMessage from .db import Portal as DBPortal, Message as DBMessage, TelegramFile as DBTelegramFile
from . import puppet as p, user as u, formatter from . import puppet as p, user as u, formatter
mimetypes.init() mimetypes.init()
@@ -763,21 +763,21 @@ class Portal:
async def handle_telegram_photo(self, source, intent, media, relates_to=None): async def handle_telegram_photo(self, source, intent, media, relates_to=None):
largest_size = self._get_largest_photo_size(media.photo) largest_size = self._get_largest_photo_size(media.photo)
file = await source.client.download_file_bytes(largest_size.location) file = await self.transfer_file_to_matrix(source.client, intent, largest_size.location)
mime_type = magic.from_buffer(file, mime=True) if not file:
uploaded = await intent.upload_file(file, mime_type) return None
info = { info = {
"h": largest_size.h, "h": largest_size.h,
"w": largest_size.w, "w": largest_size.w,
"size": len(largest_size.bytes) if ( "size": len(largest_size.bytes) if (
isinstance(largest_size, PhotoCachedSize)) else largest_size.size, isinstance(largest_size, PhotoCachedSize)) else largest_size.size,
"orientation": 0, "orientation": 0,
"mimetype": mime_type, "mimetype": file.mime_type,
} }
name = media.caption name = media.caption
await intent.set_typing(self.mxid, is_typing=False) await intent.set_typing(self.mxid, is_typing=False)
return await intent.send_image(self.mxid, uploaded["content_uri"], info=info, return await intent.send_image(self.mxid, file.mxc, info=info, text=name,
text=name, relates_to=relates_to) relates_to=relates_to)
def convert_webp(self, file, to="png"): def convert_webp(self, file, to="png"):
try: try:
@@ -789,24 +789,52 @@ class Portal:
self.log.exception(f"Failed to convert webp to {to}") self.log.exception(f"Failed to convert webp to {to}")
return "image/webp", file return "image/webp", file
async def handle_telegram_document(self, source, intent, media, relates_to=None): async def transfer_file_to_matrix(self, client, intent, location):
file = await source.client.download_file_bytes(media.document) if isinstance(location, (Document, InputDocumentFileLocation)):
id = f"{location.id}-{location.version}"
elif not isinstance(location, (FileLocation, InputFileLocation)):
id = f"{location.volume_id}-{location.local_id}"
else:
return None
db_file = DBTelegramFile.query.get(id)
if db_file:
return db_file
try:
file = await client.download_file_bytes(location)
except LocationInvalidError:
return None
mime_type = magic.from_buffer(file, mime=True) mime_type = magic.from_buffer(file, mime=True)
dont_change_mime = False
image_converted = False
if mime_type == "image/webp": if mime_type == "image/webp":
mime_type, file = self.convert_webp(file, to="png") mime_type, file = self.convert_webp(file, to="png")
dont_change_mime = True image_converted = True
uploaded = await intent.upload_file(file, mime_type) uploaded = await intent.upload_file(file, mime_type)
db_file = DBTelegramFile(id=id, mxc=uploaded["content_uri"],
mime_type=mime_type, was_converted=image_converted)
self.db.add(db_file)
self.db.commit()
return db_file
async def handle_telegram_document(self, source, intent, media, relates_to=None):
file = await self.transfer_file_to_matrix(source.client, intent, media.document)
if not file:
return None
name = media.caption name = media.caption
for attr in media.document.attributes: for attr in media.document.attributes:
if not name and isinstance(attr, DocumentAttributeFilename): if not name and isinstance(attr, DocumentAttributeFilename):
name = attr.file_name name = attr.file_name
if not dont_change_mime: if not file.was_converted:
(mime_from_name, _) = mimetypes.guess_type(name) (mime_from_name, _) = mimetypes.guess_type(name)
mime_type = mime_from_name or mime_type file.mime_type = mime_from_name or file.mime_type
elif isinstance(attr, DocumentAttributeSticker): elif isinstance(attr, DocumentAttributeSticker):
name = f"Sticker for {attr.alt}" name = f"Sticker for {attr.alt}"
mime_type = media.document.mime_type or mime_type mime_type = media.document.mime_type or file.mime_type
info = { info = {
"size": media.document.size, "size": media.document.size,
"mimetype": mime_type, "mimetype": mime_type,
@@ -819,8 +847,8 @@ class Portal:
elif mime_type.startswith("image/"): elif mime_type.startswith("image/"):
type = "m.image" type = "m.image"
await intent.set_typing(self.mxid, is_typing=False) await intent.set_typing(self.mxid, is_typing=False)
return await intent.send_file(self.mxid, uploaded["content_uri"], info=info, return await intent.send_file(self.mxid, file.mxc, info=info, text=name, file_type=type,
text=name, file_type=type, relates_to=relates_to) relates_to=relates_to)
def handle_telegram_location(self, source, intent, location, relates_to=None): def handle_telegram_location(self, source, intent, location, relates_to=None):
long = location.long long = location.long
@@ -937,6 +965,9 @@ class Portal:
self.log.debug("Unhandled Telegram message: %s", evt) self.log.debug("Unhandled Telegram message: %s", evt)
return return
if not response:
return
mxid = response["event_id"] mxid = response["event_id"]
DBMessage.query \ DBMessage.query \
.filter(DBMessage.mx_room == self.mxid, .filter(DBMessage.mx_room == self.mxid,
@@ -1064,8 +1095,8 @@ class Portal:
def new_db_instance(self): def new_db_instance(self):
return DBPortal(tgid=self.tgid, tg_receiver=self.tg_receiver, peer_type=self.peer_type, return DBPortal(tgid=self.tgid, tg_receiver=self.tg_receiver, peer_type=self.peer_type,
mxid=self.mxid, username=self.username, title=self.title, mxid=self.mxid, username=self.username, title=self.title,
about=self.about, photo_id=self.photo_id) about=self.about, photo_id=self.photo_id)
def migrate_and_save(self, new_id): def migrate_and_save(self, new_id):
existing = DBPortal.query.get(self.tgid_full) existing = DBPortal.query.get(self.tgid_full)