Update to telethon 1.8. Fixes #334
This commit is contained in:
@@ -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
@@ -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):
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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}"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user