Skip to content

Commit

Permalink
Allow sending multiple files as album (closes #455)
Browse files Browse the repository at this point in the history
  • Loading branch information
Lonami committed Jan 15, 2018
1 parent 494c90a commit 36e2101
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 22 deletions.
103 changes: 81 additions & 22 deletions telethon/telegram_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
from .tl.functions.messages import (
GetDialogsRequest, GetHistoryRequest, SendMediaRequest,
SendMessageRequest, GetChatsRequest, GetAllDraftsRequest,
CheckChatInviteRequest, ReadMentionsRequest
CheckChatInviteRequest, ReadMentionsRequest,
SendMultiMediaRequest, UploadMediaRequest
)

from .tl.functions import channels
Expand All @@ -53,7 +54,8 @@
InputUserSelf, UserProfilePhoto, ChatPhoto, UpdateMessageID,
UpdateNewChannelMessage, UpdateNewMessage, UpdateShortSentMessage,
PeerUser, InputPeerUser, InputPeerChat, InputPeerChannel, MessageEmpty,
ChatInvite, ChatInviteAlready, PeerChannel, Photo, InputPeerSelf
ChatInvite, ChatInviteAlready, PeerChannel, Photo, InputPeerSelf,
InputSingleMedia, InputMediaPhoto, InputPhoto
)
from .tl.types.messages import DialogsSlice
from .extensions import markdown
Expand Down Expand Up @@ -512,15 +514,21 @@ def get_drafts(self): # TODO: Ability to provide a `filter`

@staticmethod
def _get_response_message(request, result):
"""Extracts the response message known a request and Update result"""
"""
Extracts the response message known a request and Update result.
The request may also be the ID of the message to match.
"""
# Telegram seems to send updateMessageID first, then updateNewMessage,
# however let's not rely on that just in case.
msg_id = None
for update in result.updates:
if isinstance(update, UpdateMessageID):
if update.random_id == request.random_id:
msg_id = update.id
break
if isinstance(request, int):
msg_id = request
else:
msg_id = None
for update in result.updates:
if isinstance(update, UpdateMessageID):
if update.random_id == request.random_id:
msg_id = update.id
break

for update in result.updates:
if isinstance(update, (UpdateNewChannelMessage, UpdateNewMessage)):
Expand Down Expand Up @@ -861,21 +869,34 @@ def send_file(self, entity, file, caption='',
If "is_voice_note" in kwargs, despite its value, and the file is
sent as a document, it will be sent as a voice note.
Returns:
The message containing the sent file.
Returns:
The message (or messages) containing the sent file.
"""
as_photo = False
if isinstance(file, str):
lowercase_file = file.lower()
as_photo = any(
lowercase_file.endswith(ext)
for ext in ('.png', '.jpg', '.gif', '.jpeg')
)
# First check if the user passed an iterable, in which case
# we may want to send as an album if all are photo files.
if hasattr(file, '__iter__'):
# Convert to tuple so we can iterate several times
file = tuple(x for x in file)
if all(utils.is_image(x) for x in file):
return self._send_album(
entity, file, caption=caption,
progress_callback=progress_callback, reply_to=reply_to,
allow_cache=allow_cache
)
# Not all are images, so send all the files one by one
return [
self.send_file(
entity, x, allow_cache=False,
caption=caption, force_document=force_document,
progress_callback=progress_callback, reply_to=reply_to,
attributes=attributes, thumb=thumb, **kwargs
) for x in file
]

file_handle = self.upload_file(
file, progress_callback=progress_callback, allow_cache=allow_cache)

if as_photo and not force_document:
if utils.is_image(file) and not force_document:
media = InputMediaUploadedPhoto(file_handle, caption)
else:
mime_type = None
Expand Down Expand Up @@ -945,14 +966,52 @@ def send_file(self, entity, file, caption='',
attributes=attributes, thumb=thumb, **kwargs
)

def send_voice_note(self, entity, file, caption='', upload_progress=None,
def send_voice_note(self, entity, file, caption='', progress_callback=None,
reply_to=None):
"""Wrapper method around .send_file() with is_voice_note=()"""
return self.send_file(entity, file, caption,
upload_progress=upload_progress,
progress_callback=progress_callback,
reply_to=reply_to,
is_voice_note=()) # empty tuple is enough

def _send_album(self, entity, files, caption='',
progress_callback=None, reply_to=None,
allow_cache=True):
"""Specialized version of .send_file for albums"""
entity = self.get_input_entity(entity)
reply_to = self._get_reply_to(reply_to)
try:
# Need to upload the media first
media = [
self(UploadMediaRequest(entity, InputMediaUploadedPhoto(
self.upload_file(file),
caption=caption
)))
for file in files
]
# Now we can construct the multi-media request
result = self(SendMultiMediaRequest(
entity, reply_to_msg_id=reply_to, multi_media=[
InputSingleMedia(InputMediaPhoto(
InputPhoto(m.photo.id, m.photo.access_hash),
caption=caption
))
for m in media
]
))
return [
self._get_response_message(update.id, result)
for update in result.updates
if isinstance(update, UpdateMessageID)
]
except FilePartMissingError:
if not allow_cache:
raise
return self._send_album(
entity, files, allow_cache=False, caption=caption,
progress_callback=progress_callback, reply_to=reply_to
)

# endregion

# region Downloading media requests
Expand Down Expand Up @@ -1421,4 +1480,4 @@ def get_input_entity(self, peer):
'Make sure you have encountered this peer before.'.format(peer)
)

# endregion
# endregion
6 changes: 6 additions & 0 deletions telethon/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,12 @@ def get_input_media(media, user_caption=None, is_photo=False):
_raise_cast_fail(media, 'InputMedia')


def is_image(file):
"""Returns True if the file extension looks like an image file"""
return (isinstance(file, str) and
bool(re.search(r'\.(png|jpe?g|gif)$', file, re.IGNORECASE)))


def parse_phone(phone):
"""Parses the given phone, or returns None if it's invalid"""
if isinstance(phone, int):
Expand Down

0 comments on commit 36e2101

Please sign in to comment.