Added ability to download profile photos, changes to get_dialogs()

The `get_dialogs()` method now returns dialogs and "entities",
which can be an User, a Chat or even a Channel.
In order to use them you may want to make use of .get_input_peer()
and .get_display_name() methods
This commit is contained in:
Lonami 2016-10-03 19:44:01 +02:00
parent 7399bfacd1
commit 2a666f7ee0
2 changed files with 90 additions and 45 deletions

View File

@ -58,7 +58,10 @@ class InteractiveTelegramClient(TelegramClient):
while True: while True:
# Retrieve the top dialogs # Retrieve the top dialogs
dialog_count = 10 dialog_count = 10
dialogs, displays, inputs = self.get_dialogs(dialog_count)
# Entities represent the user, chat or channel
# corresponding to the dialog on the same index
dialogs, entities = self.get_dialogs(dialog_count)
i = None i = None
while i is None: while i is None:
@ -66,9 +69,9 @@ class InteractiveTelegramClient(TelegramClient):
print_title('Dialogs window') print_title('Dialogs window')
# Display them so the user can choose # Display them so the user can choose
for i, display in enumerate(displays): for i, entity in enumerate(entities):
i += 1 # 1-based index for normies i += 1 # 1-based index for normies
print('{}. {}'.format(i, display)) print('{}. {}'.format(i, self.get_display_name(entity)))
# Let the user decide who they want to talk to # Let the user decide who they want to talk to
print() print()
@ -92,19 +95,20 @@ class InteractiveTelegramClient(TelegramClient):
except ValueError: except ValueError:
pass pass
# Retrieve the selected user # Retrieve the selected user (or chat, or channel)
display = displays[i] entity = entities[i]
input_peer = inputs[i] input_peer = self.get_input_peer(entity)
# Show some information # Show some information
print_title('Chat with "{}"'.format(display)) print_title('Chat with "{}"'.format(self.get_display_name(entity)))
print('Available commands:') print('Available commands:')
print(' !q: Quits the current chat.') print(' !q: Quits the current chat.')
print(' !Q: Quits the current chat and exits.') print(' !Q: Quits the current chat and exits.')
print(' !h: prints the latest messages (message History) of the chat.') print(' !h: prints the latest messages (message History) of the chat.')
print(' !p <path>: sends a Photo located at the given path.') print(' !up <path>: Uploads and sends a Photo located at the given path.')
print(' !f <path>: sends a File document located at the given path.') print(' !uf <path>: Uploads and sends a File document located at the given path.')
print(' !d <msg-id>: Downloads the given message media (if any).') print(' !dm <msg-id>: Downloads the given message Media (if any).')
print(' !dp: Downloads the current dialog Profile picture.')
print() print()
# And start a while loop to chat # And start a while loop to chat
@ -139,20 +143,31 @@ class InteractiveTelegramClient(TelegramClient):
msg.date.hour, msg.date.minute, msg.id, name, content)) msg.date.hour, msg.date.minute, msg.id, name, content))
# Send photo # Send photo
elif msg.startswith('!p '): elif msg.startswith('!up '):
# Slice the message to get the path # Slice the message to get the path
self.send_photo(path=msg[len('!p '):], peer=input_peer) self.send_photo(path=msg[len('!p '):], peer=input_peer)
# Send file (document) # Send file (document)
elif msg.startswith('!f '): elif msg.startswith('!uf '):
# Slice the message to get the path # Slice the message to get the path
self.send_document(path=msg[len('!f '):], peer=input_peer) self.send_document(path=msg[len('!f '):], peer=input_peer)
# Download media # Download media
elif msg.startswith('!d '): elif msg.startswith('!dm '):
# Slice the message to get message ID # Slice the message to get message ID
self.download_media(msg[len('!d '):]) self.download_media(msg[len('!d '):])
# Download profile photo
elif msg == '!dp':
output = str('usermedia/propic_{}'.format(entity.id))
print('Downloading profile picture...')
success = self.download_profile_photo(entity.photo, output)
if success:
print('Profile picture downloaded to {}'.format(output))
else:
print('"{}" does not seem to have a profile picture.'
.format(self.get_display_name(entity)))
# Send chat message (if any) # Send chat message (if any)
elif msg: elif msg:
self.send_message(input_peer, msg, markdown=True, no_web_page=True) self.send_message(input_peer, msg, markdown=True, no_web_page=True)

View File

@ -11,8 +11,10 @@ from telethon.tl import Session
# The Requests and types that we'll be using # The Requests and types that we'll be using
from telethon.tl.functions.upload import SaveBigFilePartRequest from telethon.tl.functions.upload import SaveBigFilePartRequest
from telethon.tl.types import \ from telethon.tl.types import \
User, Chat, Channel, \
PeerUser, PeerChat, PeerChannel, \ PeerUser, PeerChat, PeerChannel, \
InputPeerUser, InputPeerChat, InputPeerChannel, InputPeerEmpty, \ InputPeerUser, InputPeerChat, InputPeerChannel, InputPeerEmpty, \
UserProfilePhotoEmpty, ChatPhotoEmpty, \
InputFile, InputFileLocation, InputMediaUploadedPhoto, InputMediaUploadedDocument, \ InputFile, InputFileLocation, InputMediaUploadedPhoto, InputMediaUploadedDocument, \
MessageMediaContact, MessageMediaDocument, MessageMediaPhoto, \ MessageMediaContact, MessageMediaDocument, MessageMediaPhoto, \
DocumentAttributeAudio, DocumentAttributeFilename, InputDocumentFileLocation DocumentAttributeAudio, DocumentAttributeFilename, InputDocumentFileLocation
@ -217,33 +219,34 @@ class TelegramClient:
# region Dialogs ("chats") requests # region Dialogs ("chats") requests
def get_dialogs(self, count=10, offset_date=None, offset_id=0, offset_peer=InputPeerEmpty()): def get_dialogs(self, count=10, offset_date=None, offset_id=0, offset_peer=InputPeerEmpty()):
"""Returns a tuple of lists ([dialogs], [displays], [input_peers]) with 'count' items each""" """Returns a tuple of lists ([dialogs], [entities]) with 'count' items each.
The `entity` represents the user, chat or channel corresponding to that dialog"""
r = self.invoke(GetDialogsRequest(offset_date=offset_date, r = self.invoke(GetDialogsRequest(offset_date=offset_date,
offset_id=offset_id, offset_id=offset_id,
offset_peer=offset_peer, offset_peer=offset_peer,
limit=count)) limit=count))
return (r.dialogs, return (r.dialogs,
[self.find_display_name(d.peer, r.users, r.chats) for d in r.dialogs], [self.find_user_or_chat(d.peer, r.users, r.chats) for d in r.dialogs])
[self.find_input_peer(d.peer, r.users, r.chats) for d in r.dialogs])
# endregion # endregion
# region Message requests # region Message requests
def send_message(self, input_peer, message, markdown=False, no_web_page=False): def send_message(self, input_peer, message, markdown=False, no_web_page=False):
"""Sends a message to the given input peer""" """Sends a message to the given input peer and returns the sent message ID"""
if markdown: if markdown:
msg, entities = parse_message_entities(message) msg, entities = parse_message_entities(message)
else: else:
msg, entities = message, [] msg, entities = message, []
msg_id = utils.generate_random_long()
self.invoke(SendMessageRequest(peer=input_peer, self.invoke(SendMessageRequest(peer=input_peer,
message=msg, message=msg,
random_id=utils.generate_random_long(), random_id=msg_id,
entities=entities, entities=entities,
no_webpage=no_web_page)) no_webpage=no_web_page))
return msg_id
def get_message_history(self, input_peer, limit=20, def get_message_history(self, input_peer, limit=20,
offset_date=None, offset_id=0, max_id=0, min_id=0, add_offset=0): offset_date=None, offset_id=0, max_id=0, min_id=0, add_offset=0):
@ -409,6 +412,32 @@ class TelegramClient:
# region Downloading media requests # region Downloading media requests
def download_profile_photo(self, profile_photo, file_path,
add_extension=True, download_big=True):
"""Downloads the profile photo for an user or a chat (including channels).
Returns False if no photo was providen, or if it was Empty"""
if (not profile_photo or
isinstance(profile_photo, UserProfilePhotoEmpty) or
isinstance(profile_photo, ChatPhotoEmpty)):
return False
# Photos are always compressed into a .jpg by Telegram
if add_extension:
file_path += '.jpg'
if download_big:
photo_location = profile_photo.photo_big
else:
photo_location = profile_photo.photo_small
# Download the media with the largest size input file location
self.download_file_loc(InputFileLocation(volume_id=photo_location.volume_id,
local_id=photo_location.local_id,
secret=photo_location.secret),
file_path)
return True
def download_msg_media(self, message_media, file_path, add_extension=True, progress_callback=None): def download_msg_media(self, message_media, file_path, add_extension=True, progress_callback=None):
"""Downloads the given MessageMedia (Photo, Document or Contact) """Downloads the given MessageMedia (Photo, Document or Contact)
into the desired file_path, optionally finding its extension automatically into the desired file_path, optionally finding its extension automatically
@ -556,43 +585,44 @@ class TelegramClient:
# region Utilities # region Utilities
@staticmethod @staticmethod
def find_display_name(peer, users, chats): def get_display_name(entity):
"""Searches the display name for peer in both users and chats. """Gets the input peer for the given "entity" (user, chat or channel)
Returns None if it was not found""" Returns None if it was not found"""
try: if isinstance(entity, User):
if type(peer) is PeerUser: if entity.last_name is not None:
user = next(u for u in users if u.id == peer.user_id) return '{} {}'.format(entity.first_name, entity.last_name)
if user.last_name is not None: return entity.first_name
return '{} {}'.format(user.first_name, user.last_name)
return user.first_name
elif type(peer) is PeerChat: if isinstance(entity, Chat) or isinstance(entity, Channel):
return next(c for c in chats if c.id == peer.chat_id).title return entity.title
elif type(peer) is PeerChannel:
return next(c for c in chats if c.id == peer.channel_id).title
except StopIteration:
pass
return None
@staticmethod @staticmethod
def find_input_peer(peer, users, chats): def get_input_peer(entity):
"""Searches the given peer in both users and chats and returns an InputPeer for it. """Gets the input peer for the given "entity" (user, chat or channel).
Returns None if it was not found"""
if isinstance(entity, User):
return InputPeerUser(entity.id, entity.access_hash)
if isinstance(entity, Chat):
return InputPeerChat(entity.id)
if isinstance(entity, Channel):
return InputPeerChannel(entity.id, entity.access_hash)
@staticmethod
def find_user_or_chat(peer, users, chats):
"""Finds the corresponding user or chat given a peer.
Returns None if it was not found""" Returns None if it was not found"""
try: try:
if type(peer) is PeerUser: if isinstance(peer, PeerUser):
user = next(u for u in users if u.id == peer.user_id) user = next(u for u in users if u.id == peer.user_id)
return InputPeerUser(user.id, user.access_hash) return user
elif type(peer) is PeerChat: elif isinstance(peer, PeerChat):
chat = next(c for c in chats if c.id == peer.chat_id) chat = next(c for c in chats if c.id == peer.chat_id)
return InputPeerChat(chat.id) return chat
elif type(peer) is PeerChannel: elif isinstance(peer, PeerChannel):
channel = next(c for c in chats if c.id == peer.channel_id) channel = next(c for c in chats if c.id == peer.channel_id)
return InputPeerChannel(channel.id, channel.access_hash) return channel
except StopIteration: except StopIteration:
return None return None