Update to telethon 1.8. Fixes #334

This commit is contained in:
Tulir Asokan
2019-06-20 21:42:22 +03:00
parent 070601689a
commit 5fba658c66
5 changed files with 96 additions and 41 deletions
+1 -1
View File
@@ -309,7 +309,7 @@ class AbstractUser(ABC):
if await puppet.update_displayname(self, update): if await puppet.update_displayname(self, update):
puppet.save() puppet.save()
elif isinstance(update, UpdateUserPhoto): elif isinstance(update, UpdateUserPhoto):
if await puppet.update_avatar(self, update.photo.photo_big): if await puppet.update_avatar(self, update.photo):
puppet.save() puppet.save()
else: else:
self.log.warning("Unexpected other user info update: %s", update) self.log.warning("Unexpected other user info update: %s", update)
+58 -28
View File
@@ -47,13 +47,13 @@ from telethon.errors import (ChatAdminRequiredError, ChatNotModifiedError, Photo
PhotoInvalidDimensionsError, PhotoSaveFileInvalidError) PhotoInvalidDimensionsError, PhotoSaveFileInvalidError)
from telethon.tl.patched import Message, MessageService from telethon.tl.patched import Message, MessageService
from telethon.tl.types import ( from telethon.tl.types import (
Channel, ChatAdminRights, ChatBannedRights, ChannelFull, ChannelParticipantAdmin, Channel, ChatAdminRights, ChatBannedRights, ChannelFull, ChannelParticipantAdmin, Document,
ChannelParticipantCreator, ChannelParticipantsRecent, ChannelParticipantsSearch, Chat, ChannelParticipantCreator, ChannelParticipantsRecent, ChannelParticipantsSearch, Chat,
ChatFull, ChatInviteEmpty, ChatParticipantAdmin, ChatParticipantCreator, ChatPhoto, Poll, ChatFull, ChatInviteEmpty, ChatParticipantAdmin, ChatParticipantCreator, ChatPhoto, Poll,
DocumentAttributeFilename, DocumentAttributeImageSize, DocumentAttributeSticker, DocumentAttributeFilename, DocumentAttributeImageSize, DocumentAttributeSticker, PhotoEmpty,
DocumentAttributeVideo, FileLocation, GeoPoint, InputChannel, InputChatUploadedPhoto, DocumentAttributeVideo, GeoPoint, InputChannel, InputChatUploadedPhoto, InputPhotoFileLocation,
InputPeerChannel, InputPeerChat, InputPeerUser, InputUser, InputUserSelf, MessageMediaPoll, InputPeerChannel, InputPeerChat, InputPeerUser, InputUser, InputUserSelf, MessageMediaPoll,
MessageActionChannelCreate, MessageActionChatAddUser, MessageActionChatCreate, MessageActionChannelCreate, MessageActionChatAddUser, MessageActionChatCreate, ChatPhotoEmpty,
MessageActionChatDeletePhoto, MessageActionChatDeleteUser, MessageActionChatEditPhoto, MessageActionChatDeletePhoto, MessageActionChatDeleteUser, MessageActionChatEditPhoto,
MessageActionChatEditTitle, MessageActionChatJoinedByLink, MessageActionChatMigrateTo, MessageActionChatEditTitle, MessageActionChatJoinedByLink, MessageActionChatMigrateTo,
MessageActionPinMessage, MessageActionGameScore, MessageMediaContact, MessageMediaDocument, MessageActionPinMessage, MessageActionGameScore, MessageMediaContact, MessageMediaDocument,
@@ -63,7 +63,7 @@ from telethon.tl.types import (
TypeDocumentAttribute, TypeInputPeer, TypeMessageAction, TypeMessageEntity, TypePeer, TypeDocumentAttribute, TypeInputPeer, TypeMessageAction, TypeMessageEntity, TypePeer,
TypePhotoSize, TypeUpdates, TypeUser, PhotoSize, TypeUserFull, UpdateChatUserTyping, TypePhotoSize, TypeUpdates, TypeUser, PhotoSize, TypeUserFull, UpdateChatUserTyping,
UpdateNewChannelMessage, UpdateNewMessage, UpdateUserTyping, User, UserFull, MessageEntityPre, UpdateNewChannelMessage, UpdateNewMessage, UpdateUserTyping, User, UserFull, MessageEntityPre,
InputMediaUploadedDocument) InputMediaUploadedDocument, InputPeerPhotoFileLocation)
from mautrix_appservice import MatrixRequestError, IntentError, AppService, IntentAPI from mautrix_appservice import MatrixRequestError, IntentError, AppService, IntentAPI
from .types import MatrixEventID, MatrixRoomID, MatrixUserID, TelegramID from .types import MatrixEventID, MatrixRoomID, MatrixUserID, TelegramID
@@ -575,7 +575,7 @@ class Portal:
changed = await self.update_title(entity.title) or changed changed = await self.update_title(entity.title) or changed
if isinstance(entity.photo, ChatPhoto): if isinstance(entity.photo, ChatPhoto):
changed = await self.update_avatar(user, entity.photo.photo_big) or changed changed = await self.update_avatar(user, entity.photo) or changed
if changed: if changed:
self.save() self.save()
@@ -616,12 +616,21 @@ class Portal:
return False return False
@staticmethod @staticmethod
def _get_largest_photo_size(photo: Union[Photo, List[TypePhotoSize]] def _get_largest_photo_size(photo: Union[Photo, Document]
) -> Optional[TypePhotoSize]: ) -> Tuple[Optional[InputPhotoFileLocation],
Optional[TypePhotoSize]]:
if not photo: if not photo:
return None return None, None
return max(photo.sizes if isinstance(photo, Photo) else photo, key=(lambda photo2: ( largest = max(photo.sizes if isinstance(photo, Photo) else photo.thumbs,
len(photo2.bytes) if not isinstance(photo2, PhotoSize) else photo2.size))) key=(lambda photo2: (len(photo2.bytes)
if not isinstance(photo2, PhotoSize)
else photo2.size)))
return InputPhotoFileLocation(
id=photo.id,
access_hash=photo.access_hash,
file_reference=photo.file_reference,
thumb_size=largest.type,
), largest
async def remove_avatar(self, _: 'AbstractUser', save: bool = False) -> None: async def remove_avatar(self, _: 'AbstractUser', save: bool = False) -> None:
await self.main_intent.set_room_avatar(self.mxid, None) await self.main_intent.set_room_avatar(self.mxid, None)
@@ -629,11 +638,33 @@ class Portal:
if save: if save:
self.save() self.save()
async def update_avatar(self, user: 'AbstractUser', photo: FileLocation, async def update_avatar(self, user: 'AbstractUser',
photo: Union[ChatPhoto, ChatPhotoEmpty, Photo, PhotoEmpty],
save: bool = False) -> bool: save: bool = False) -> bool:
photo_id = f"{photo.volume_id}-{photo.local_id}" if isinstance(photo, ChatPhoto):
loc = InputPeerPhotoFileLocation(
peer=await self.get_input_entity(user),
local_id=photo.photo_big.local_id,
volume_id=photo.photo_big.volume_id,
big=True
)
photo_id = f"{loc.volume_id}-{loc.local_id}"
elif isinstance(photo, Photo):
loc, largest = self._get_largest_photo_size(photo)
photo_id = f"{largest.location.volume_id}-{largest.location.local_id}"
elif isinstance(photo, (ChatPhotoEmpty, PhotoEmpty)):
photo_id = ""
loc = None
else:
raise ValueError(f"Unknown photo type {type(photo)}")
if self.photo_id != photo_id: if self.photo_id != photo_id:
file = await util.transfer_file_to_matrix(user.client, self.main_intent, photo) if not photo_id:
await self.main_intent.set_room_avatar(self.mxid, "")
self.photo_id = ""
if save:
self.save()
return True
file = await util.transfer_file_to_matrix(user.client, self.main_intent, loc)
if file: if file:
await self.main_intent.set_room_avatar(self.mxid, file.mxc) await self.main_intent.set_room_avatar(self.mxid, file.mxc)
self.photo_id = photo_id self.photo_id = photo_id
@@ -1212,8 +1243,8 @@ class Portal:
and isinstance(update.message, MessageService) and isinstance(update.message, MessageService)
and isinstance(update.message.action, MessageActionChatEditPhoto)) and isinstance(update.message.action, MessageActionChatEditPhoto))
if is_photo_update: if is_photo_update:
loc = self._get_largest_photo_size(update.message.action.photo).location loc, size = self._get_largest_photo_size(update.message.action.photo)
self.photo_id = f"{loc.volume_id}-{loc.local_id}" self.photo_id = f"{size.location.volume_id}-{size.location.local_id}"
self.save() self.save()
break break
@@ -1368,8 +1399,8 @@ class Portal:
async def handle_telegram_photo(self, source: 'AbstractUser', intent: IntentAPI, evt: Message, async def handle_telegram_photo(self, source: 'AbstractUser', intent: IntentAPI, evt: Message,
relates_to: Dict = None) -> Optional[Dict]: relates_to: Dict = None) -> Optional[Dict]:
largest_size = self._get_largest_photo_size(evt.media.photo) loc, largest_size = self._get_largest_photo_size(evt.media.photo)
file = await util.transfer_file_to_matrix(source.client, intent, largest_size.location) file = await util.transfer_file_to_matrix(source.client, intent, loc)
if not file: if not file:
return None return None
if self.get_config("inline_images") and (evt.message if self.get_config("inline_images") and (evt.message
@@ -1429,7 +1460,7 @@ class Portal:
@staticmethod @staticmethod
def _parse_telegram_document_meta(evt: Message, file: DBTelegramFile, attrs: Dict, def _parse_telegram_document_meta(evt: Message, file: DBTelegramFile, attrs: Dict,
thumb: TypePhotoSize) -> Tuple[Dict, str]: thumb_size: TypePhotoSize) -> Tuple[Dict, str]:
document = evt.media.document document = evt.media.document
name = evt.message or attrs["name"] name = evt.message or attrs["name"]
if attrs["is_sticker"]: if attrs["is_sticker"]:
@@ -1461,8 +1492,8 @@ class Portal:
info["thumbnail_url"] = file.thumbnail.mxc info["thumbnail_url"] = file.thumbnail.mxc
info["thumbnail_info"] = { info["thumbnail_info"] = {
"mimetype": file.thumbnail.mime_type, "mimetype": file.thumbnail.mime_type,
"h": file.thumbnail.height or thumb.h, "h": file.thumbnail.height or thumb_size.h,
"w": file.thumbnail.width or thumb.w, "w": file.thumbnail.width or thumb_size.w,
"size": file.thumbnail.size, "size": file.thumbnail.size,
} }
@@ -1473,16 +1504,16 @@ class Portal:
document = evt.media.document document = evt.media.document
attrs = self._parse_telegram_document_attributes(document.attributes) attrs = self._parse_telegram_document_attributes(document.attributes)
thumb = self._get_largest_photo_size(document.thumbs) thumb_loc, thumb_size = self._get_largest_photo_size(document)
if thumb and not isinstance(thumb, (PhotoSize, PhotoCachedSize)): if thumb_size and not isinstance(thumb_size, (PhotoSize, PhotoCachedSize)):
self.log.debug(f"Unsupported thumbnail type {type(thumb)}") self.log.debug(f"Unsupported thumbnail type {type(thumb_size)}")
thumb = None thumb = None
file = await util.transfer_file_to_matrix(source.client, intent, document, thumb, file = await util.transfer_file_to_matrix(source.client, intent, document, thumb_loc,
is_sticker=attrs["is_sticker"]) is_sticker=attrs["is_sticker"])
if not file: if not file:
return None return None
info, name = self._parse_telegram_document_meta(evt, file, attrs, thumb) info, name = self._parse_telegram_document_meta(evt, file, attrs, thumb_size)
await intent.set_typing(self.mxid, is_typing=False) await intent.set_typing(self.mxid, is_typing=False)
@@ -1819,8 +1850,7 @@ class Portal:
if isinstance(action, MessageActionChatEditTitle): if isinstance(action, MessageActionChatEditTitle):
await self.update_title(action.title, save=True) await self.update_title(action.title, save=True)
elif isinstance(action, MessageActionChatEditPhoto): elif isinstance(action, MessageActionChatEditPhoto):
largest_size = self._get_largest_photo_size(action.photo) await self.update_avatar(source, action.photo, save=True)
await self.update_avatar(source, largest_size.location, save=True)
elif isinstance(action, MessageActionChatDeletePhoto): elif isinstance(action, MessageActionChatDeletePhoto):
await self.remove_avatar(source, save=True) await self.remove_avatar(source, save=True)
elif isinstance(action, MessageActionChatAddUser): elif isinstance(action, MessageActionChatAddUser):
+29 -6
View File
@@ -22,7 +22,8 @@ import asyncio
import logging import logging
import re import re
from telethon.tl.types import UserProfilePhoto, User, FileLocation, UpdateUserName, PeerUser from telethon.tl.types import (UserProfilePhoto, User, UpdateUserName, PeerUser, TypeInputPeer,
InputPeerPhotoFileLocation, UserProfilePhotoEmpty)
from mautrix_appservice import AppService, IntentAPI, IntentError, MatrixRequestError from mautrix_appservice import AppService, IntentAPI, IntentError, MatrixRequestError
from .types import MatrixUserID, TelegramID from .types import MatrixUserID, TelegramID
@@ -113,6 +114,9 @@ class Puppet:
match = regex.match(self.displayname) match = regex.match(self.displayname)
return match.group(1) or self.displayname return match.group(1) or self.displayname
def get_input_entity(self, user: 'AbstractUser') -> Awaitable[TypeInputPeer]:
return user.client.get_input_entity(PeerUser(user_id=self.tgid))
# region Custom puppet management # region Custom puppet management
def _fresh_intent(self) -> IntentAPI: def _fresh_intent(self) -> IntentAPI:
return (self.az.intent.user(self.custom_mxid, self.access_token) return (self.az.intent.user(self.custom_mxid, self.access_token)
@@ -348,7 +352,7 @@ class Puppet:
changed = await self.update_displayname(source, info) or changed changed = await self.update_displayname(source, info) or changed
if isinstance(info.photo, UserProfilePhoto): if isinstance(info.photo, UserProfilePhoto):
changed = await self.update_avatar(source, info.photo.photo_big) or changed changed = await self.update_avatar(source, info.photo) or changed
self.is_bot = info.bot self.is_bot = info.bot
@@ -384,13 +388,32 @@ class Puppet:
return True return True
return False return False
async def update_avatar(self, source: 'AbstractUser', photo: FileLocation) -> bool: async def update_avatar(self, source: 'AbstractUser',
photo: Union[UserProfilePhoto, UserProfilePhotoEmpty]) -> bool:
if self.disable_updates: if self.disable_updates:
return False return False
photo_id = f"{photo.volume_id}-{photo.local_id}"
if isinstance(photo, UserProfilePhotoEmpty):
photo_id = ""
else:
photo_id = str(photo.photo_id)
if self.photo_id != photo_id: if self.photo_id != photo_id:
file = await util.transfer_file_to_matrix(source.client, self.default_mxid_intent, if not photo_id:
photo) self.photo_id = ""
try:
await self.default_mxid_intent.set_avatar("")
except MatrixRequestError:
self.log.exception("Failed to set avatar")
self.photo_id = ""
return True
loc = InputPeerPhotoFileLocation(
peer=await self.get_input_entity(source),
local_id=photo.photo_big.local_id,
volume_id=photo.photo_big.volume_id,
big=True
)
file = await util.transfer_file_to_matrix(source.client, self.default_mxid_intent, loc)
if file: if file:
self.photo_id = photo_id self.photo_id = photo_id
try: try:
+7 -5
View File
@@ -23,8 +23,9 @@ import asyncio
import magic import magic
from sqlalchemy.exc import IntegrityError, InvalidRequestError from sqlalchemy.exc import IntegrityError, InvalidRequestError
from telethon.tl.types import (Document, FileLocation, InputFileLocation, InputDocumentFileLocation, from telethon.tl.types import (Document, InputFileLocation, InputDocumentFileLocation,
TypePhotoSize, PhotoSize, PhotoCachedSize) TypePhotoSize, PhotoSize, PhotoCachedSize, InputPhotoFileLocation,
InputPeerPhotoFileLocation)
from telethon.errors import (AuthBytesInvalidError, AuthKeyInvalidError, LocationInvalidError, from telethon.errors import (AuthBytesInvalidError, AuthKeyInvalidError, LocationInvalidError,
SecurityError) SecurityError)
from mautrix_appservice import IntentAPI from mautrix_appservice import IntentAPI
@@ -47,7 +48,8 @@ except ImportError:
log = logging.getLogger("mau.util") # type: logging.Logger log = logging.getLogger("mau.util") # type: logging.Logger
TypeLocation = Union[Document, InputDocumentFileLocation, FileLocation, InputFileLocation] TypeLocation = Union[Document, InputDocumentFileLocation, InputPeerPhotoFileLocation,
InputFileLocation, InputPhotoFileLocation]
def convert_image(file: bytes, source_mime: str = "image/webp", target_type: str = "png", def convert_image(file: bytes, source_mime: str = "image/webp", target_type: str = "png",
@@ -99,9 +101,9 @@ def _read_video_thumbnail(data: bytes, video_ext: str = "mp4", frame_ext: str =
def _location_to_id(location: TypeLocation) -> str: def _location_to_id(location: TypeLocation) -> str:
if isinstance(location, (Document, InputDocumentFileLocation)): if isinstance(location, (Document, InputDocumentFileLocation, InputPhotoFileLocation)):
return f"{location.id}-{location.access_hash}" return f"{location.id}-{location.access_hash}"
elif isinstance(location, (FileLocation, InputFileLocation)): elif isinstance(location, (InputFileLocation, InputPeerPhotoFileLocation)):
return f"{location.volume_id}-{location.local_id}" return f"{location.volume_id}-{location.local_id}"
+1 -1
View File
@@ -38,7 +38,7 @@ setuptools.setup(
"ruamel.yaml>=0.15.35,<0.16", "ruamel.yaml>=0.15.35,<0.16",
"future-fstrings>=0.4.2", "future-fstrings>=0.4.2",
"python-magic>=0.4.15,<0.5", "python-magic>=0.4.15,<0.5",
"telethon>=1.5.5,<1.7", "telethon>=1.7,<1.9",
"telethon-session-sqlalchemy>=0.2.14,<0.3", "telethon-session-sqlalchemy>=0.2.14,<0.3",
], ],
extras_require=extras, extras_require=extras,