Merge branch 'master' into asyncio

This commit is contained in:
Lonami Exo 2018-05-06 13:09:17 +02:00
commit 41f0e0c0a8
4 changed files with 73 additions and 71 deletions

View File

@ -9,6 +9,14 @@ the ``session``, and defaults to be the session name (or full path). That is,
if you create a ``TelegramClient('anon')`` instance and connect, an if you create a ``TelegramClient('anon')`` instance and connect, an
``anon.session`` file will be created on the working directory. ``anon.session`` file will be created on the working directory.
Note that if you pass a string it will be a file in the current working
directory, although you can also pass absolute paths.
The session file contains enough information for you to login without
re-sending the code, so if you have to enter the code more than once,
maybe you're changing the working directory, renaming or removing the
file, or using random names.
These database files using ``sqlite3`` contain the required information to These database files using ``sqlite3`` contain the required information to
talk to the Telegram servers, such as to which IP the client should connect, talk to the Telegram servers, such as to which IP the client should connect,
port, authorization key so that messages can be encrypted, and so on. port, authorization key so that messages can be encrypted, and so on.

View File

@ -39,6 +39,13 @@ Note that ``'some_name'`` will be used to save your session (persistent
information such as access key and others) as ``'some_name.session'`` in information such as access key and others) as ``'some_name.session'`` in
your disk. This is by default a database file using Python's ``sqlite3``. your disk. This is by default a database file using Python's ``sqlite3``.
.. note::
It's important that the library always accesses the same session file so
that you don't need to re-send the code over and over again. By default it
creates the file in your working directory, but absolute paths work too.
Before using the client, you must be connected to Telegram. Before using the client, you must be connected to Telegram.
Doing so is very easy: Doing so is very easy:

View File

@ -72,7 +72,6 @@ from .tl.functions.channels import (
) )
from .tl.types import ( from .tl.types import (
DocumentAttributeAudio, DocumentAttributeFilename, DocumentAttributeAudio, DocumentAttributeFilename,
InputDocumentFileLocation, InputFileLocation,
InputMediaUploadedDocument, InputMediaUploadedPhoto, InputPeerEmpty, InputMediaUploadedDocument, InputMediaUploadedPhoto, InputPeerEmpty,
Message, MessageMediaContact, MessageMediaDocument, MessageMediaPhoto, Message, MessageMediaContact, MessageMediaDocument, MessageMediaPhoto,
InputUserSelf, UserProfilePhoto, ChatPhoto, UpdateMessageID, InputUserSelf, UserProfilePhoto, ChatPhoto, UpdateMessageID,
@ -105,6 +104,14 @@ class TelegramClient(TelegramBareClient):
used otherwise. If it's ``None``, the session will not be saved, used otherwise. If it's ``None``, the session will not be saved,
and you should call :meth:`.log_out()` when you're done. and you should call :meth:`.log_out()` when you're done.
Note that if you pass a string it will be a file in the current
working directory, although you can also pass absolute paths.
The session file contains enough information for you to login
without re-sending the code, so if you have to enter the code
more than once, maybe you're changing the working directory,
renaming or removing the file, or using random names.
api_id (`int` | `str`): api_id (`int` | `str`):
The API ID you obtained from https://my.telegram.org. The API ID you obtained from https://my.telegram.org.
@ -531,7 +538,8 @@ class TelegramClient(TelegramBareClient):
offset_peer=InputPeerEmpty(), _total=None): offset_peer=InputPeerEmpty(), _total=None):
""" """
Returns an iterator over the dialogs, yielding 'limit' at most. Returns an iterator over the dialogs, yielding 'limit' at most.
Dialogs are the open "chats" or conversations with other people. Dialogs are the open "chats" or conversations with other people,
groups you have joined, or channels you are subscribed to.
Args: Args:
limit (`int` | `None`): limit (`int` | `None`):
@ -739,6 +747,11 @@ class TelegramClient(TelegramBareClient):
message (`str` | :tl:`Message`): message (`str` | :tl:`Message`):
The message to be sent, or another message object to resend. The message to be sent, or another message object to resend.
The maximum length for a message is 35,000 bytes or 4,096
characters. Longer messages will not be sliced automatically,
and you should slice them manually if the text to send is
longer than said length.
reply_to (`int` | :tl:`Message`, optional): reply_to (`int` | :tl:`Message`, optional):
Whether to reply to a message or not. If an integer is provided, Whether to reply to a message or not. If an integer is provided,
it should be the ID of the message that it should reply to. it should be the ID of the message that it should reply to.
@ -1881,59 +1894,48 @@ class TelegramClient(TelegramBareClient):
``None`` if no photo was provided, or if it was Empty. On success ``None`` if no photo was provided, or if it was Empty. On success
the file path is returned since it may differ from the one given. the file path is returned since it may differ from the one given.
""" """
photo = entity # hex(crc32(x.encode('ascii'))) for x in
possible_names = [] # ('User', 'Chat', 'UserFull', 'ChatFull')
try: ENTITIES = (0x2da17977, 0xc5af5d94, 0x1f4661b9, 0xd49a2697)
is_entity = entity.SUBCLASS_OF_ID in ( # ('InputPeer', 'InputUser', 'InputChannel')
0x2da17977, 0xc5af5d94, 0x1f4661b9, 0xd49a2697 INPUTS = (0xc91c90b6, 0xe669bf46, 0x40f202fd)
) if not isinstance(entity, TLObject) or entity.SUBCLASS_OF_ID in INPUTS:
except AttributeError:
return None # Not even a TLObject as attribute access failed
if is_entity:
# Maybe it is an user or a chat? Or their full versions?
#
# The hexadecimal numbers above are simply:
# hex(crc32(x.encode('ascii'))) for x in
# ('User', 'Chat', 'UserFull', 'ChatFull')
entity = await self.get_entity(entity) entity = await self.get_entity(entity)
possible_names = []
if entity.SUBCLASS_OF_ID not in ENTITIES:
photo = entity
else:
if not hasattr(entity, 'photo'): if not hasattr(entity, 'photo'):
# Special case: may be a ChatFull with photo:Photo # Special case: may be a ChatFull with photo:Photo
# This is different from a normal UserProfilePhoto and Chat # This is different from a normal UserProfilePhoto and Chat
if hasattr(entity, 'chat_photo'): if not hasattr(entity, 'chat_photo'):
return await self._download_photo(
entity.chat_photo, file,
date=None, progress_callback=None
)
else:
# Give up
return None return None
return await self._download_photo(
entity.chat_photo, file, date=None, progress_callback=None)
for attr in ('username', 'first_name', 'title'): for attr in ('username', 'first_name', 'title'):
possible_names.append(getattr(entity, attr, None)) possible_names.append(getattr(entity, attr, None))
photo = entity.photo photo = entity.photo
if not isinstance(photo, UserProfilePhoto) and \ if isinstance(photo, (UserProfilePhoto, ChatPhoto)):
not isinstance(photo, ChatPhoto): loc = photo.photo_big if download_big else photo.photo_small
return None else:
try:
loc = utils.get_input_location(photo)
except TypeError:
return None
photo_location = photo.photo_big if download_big else photo.photo_small
file = self._get_proper_filename( file = self._get_proper_filename(
file, 'profile_photo', '.jpg', file, 'profile_photo', '.jpg',
possible_names=possible_names possible_names=possible_names
) )
# Download the media with the largest size input file location
try: try:
await self.download_file( await self.download_file(loc, file)
InputFileLocation( return file
volume_id=photo_location.volume_id,
local_id=photo_location.local_id,
secret=photo_location.secret
),
file
)
except LocationInvalidError: except LocationInvalidError:
# See issue #500, Android app fails as of v4.6.0 (1155). # See issue #500, Android app fails as of v4.6.0 (1155).
# The fix seems to be using the full channel chat photo. # The fix seems to be using the full channel chat photo.
@ -1947,7 +1949,6 @@ class TelegramClient(TelegramBareClient):
else: else:
# Until there's a report for chats, no need to. # Until there's a report for chats, no need to.
return None return None
return file
async def download_media(self, message, file=None, progress_callback=None): async def download_media(self, message, file=None, progress_callback=None):
""" """
@ -2028,16 +2029,8 @@ class TelegramClient(TelegramBareClient):
f.close() f.close()
return file return file
await self.download_file( await self.download_file(photo.location, file, file_size=photo.size,
InputFileLocation( progress_callback=progress_callback)
volume_id=photo.location.volume_id,
local_id=photo.location.local_id,
secret=photo.location.secret
),
file,
file_size=photo.size,
progress_callback=progress_callback
)
return file return file
async def _download_document(self, document, file, date, progress_callback): async def _download_document(self, document, file, date, progress_callback):
@ -2073,16 +2066,8 @@ class TelegramClient(TelegramBareClient):
date=date, possible_names=possible_names date=date, possible_names=possible_names
) )
await self.download_file( await self.download_file(document, file, file_size=file_size,
InputDocumentFileLocation( progress_callback=progress_callback)
id=document.id,
access_hash=document.access_hash,
version=document.version
),
file,
file_size=file_size,
progress_callback=progress_callback
)
return file return file
@staticmethod @staticmethod
@ -2186,8 +2171,10 @@ class TelegramClient(TelegramBareClient):
Downloads the given input location to a file. Downloads the given input location to a file.
Args: Args:
input_location (:tl:`InputFileLocation`): input_location (:tl:`FileLocation` | :tl:`InputFileLocation`):
The file location from which the file will be downloaded. The file location from which the file will be downloaded.
See `telethon.utils.get_input_location` source for a complete
list of supported types.
file (`str` | `file`, optional): file (`str` | `file`, optional):
The output file path, directory, or stream-like object. The output file path, directory, or stream-like object.

View File

@ -10,17 +10,17 @@ dummyHttpWait = HttpWait;
//int128 4*[ int ] = Int128; //int128 4*[ int ] = Int128;
//int256 8*[ int ] = Int256; //int256 8*[ int ] = Int256;
resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector<long> = ResPQ; resPQ#05162463 nonce:int128 server_nonce:int128 pq:bytes server_public_key_fingerprints:Vector<long> = ResPQ;
p_q_inner_data#83c95aec pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 = P_Q_inner_data; p_q_inner_data#83c95aec pq:bytes p:bytes q:bytes nonce:int128 server_nonce:int128 new_nonce:int256 = P_Q_inner_data;
p_q_inner_data_temp#3c6a84d4 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 expires_in:int = P_Q_inner_data; p_q_inner_data_temp#3c6a84d4 pq:bytes p:bytes q:bytes nonce:int128 server_nonce:int128 new_nonce:int256 expires_in:int = P_Q_inner_data;
server_DH_params_fail#79cb045d nonce:int128 server_nonce:int128 new_nonce_hash:int128 = Server_DH_Params; server_DH_params_fail#79cb045d nonce:int128 server_nonce:int128 new_nonce_hash:int128 = Server_DH_Params;
server_DH_params_ok#d0e8075c nonce:int128 server_nonce:int128 encrypted_answer:string = Server_DH_Params; server_DH_params_ok#d0e8075c nonce:int128 server_nonce:int128 encrypted_answer:bytes = Server_DH_Params;
server_DH_inner_data#b5890dba nonce:int128 server_nonce:int128 g:int dh_prime:string g_a:string server_time:int = Server_DH_inner_data; server_DH_inner_data#b5890dba nonce:int128 server_nonce:int128 g:int dh_prime:bytes g_a:bytes server_time:int = Server_DH_inner_data;
client_DH_inner_data#6643b654 nonce:int128 server_nonce:int128 retry_id:long g_b:string = Client_DH_Inner_Data; client_DH_inner_data#6643b654 nonce:int128 server_nonce:int128 retry_id:long g_b:bytes = Client_DH_Inner_Data;
dh_gen_ok#3bcbf734 nonce:int128 server_nonce:int128 new_nonce_hash1:int128 = Set_client_DH_params_answer; dh_gen_ok#3bcbf734 nonce:int128 server_nonce:int128 new_nonce_hash1:int128 = Set_client_DH_params_answer;
dh_gen_retry#46dc1fb9 nonce:int128 server_nonce:int128 new_nonce_hash2:int128 = Set_client_DH_params_answer; dh_gen_retry#46dc1fb9 nonce:int128 server_nonce:int128 new_nonce_hash2:int128 = Set_client_DH_params_answer;
@ -28,7 +28,7 @@ dh_gen_fail#a69dae02 nonce:int128 server_nonce:int128 new_nonce_hash3:int128 = S
bind_auth_key_inner#75a3f765 nonce:long temp_auth_key_id:long perm_auth_key_id:long temp_session_id:long expires_at:int = BindAuthKeyInner; bind_auth_key_inner#75a3f765 nonce:long temp_auth_key_id:long perm_auth_key_id:long temp_session_id:long expires_at:int = BindAuthKeyInner;
//rpc_result#f35c6d01 req_msg_id:long result:string = RpcResult; //rpc_result#f35c6d01 req_msg_id:long result:bytes = RpcResult;
rpc_error#2144ca19 error_code:int error_message:string = RpcError; rpc_error#2144ca19 error_code:int error_message:string = RpcError;
rpc_answer_unknown#5e2ad36e = RpcDropAnswer; rpc_answer_unknown#5e2ad36e = RpcDropAnswer;
@ -46,10 +46,10 @@ destroy_session_none#62d350c9 session_id:long = DestroySessionRes;
new_session_created#9ec20908 first_msg_id:long unique_id:long server_salt:long = NewSession; new_session_created#9ec20908 first_msg_id:long unique_id:long server_salt:long = NewSession;
//msg_container#73f1f8dc messages:vector<%Message> = MessageContainer; //msg_container#73f1f8dc messages:vector<%Message> = MessageContainer;
//message msg_id:long seqno:int bytes:int body:string = Message; //message msg_id:long seqno:int bytes:int body:bytes = Message;
//msg_copy#e06046b2 orig_message:Message = MessageCopy; //msg_copy#e06046b2 orig_message:Message = MessageCopy;
gzip_packed#3072cfa1 packed_data:string = Object; gzip_packed#3072cfa1 packed_data:bytes = Object;
msgs_ack#62d6b459 msg_ids:Vector<long> = MsgsAck; msgs_ack#62d6b459 msg_ids:Vector<long> = MsgsAck;
@ -63,15 +63,15 @@ msgs_all_info#8cc0d131 msg_ids:Vector<long> info:string = MsgsAllInfo;
msg_detailed_info#276d3ec6 msg_id:long answer_msg_id:long bytes:int status:int = MsgDetailedInfo; msg_detailed_info#276d3ec6 msg_id:long answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
msg_new_detailed_info#809db6df answer_msg_id:long bytes:int status:int = MsgDetailedInfo; msg_new_detailed_info#809db6df answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
rsa_public_key n:string e:string = RSAPublicKey; rsa_public_key n:string e:bytes = RSAPublicKey;
---functions--- ---functions---
req_pq_multi#be7e8ef1 nonce:int128 = ResPQ; req_pq_multi#be7e8ef1 nonce:int128 = ResPQ;
req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:string q:string public_key_fingerprint:long encrypted_data:string = Server_DH_Params; req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:bytes q:bytes public_key_fingerprint:long encrypted_data:string = Server_DH_Params;
set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:string = Set_client_DH_params_answer; set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:bytes = Set_client_DH_params_answer;
rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer; rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer;
get_future_salts#b921bd04 num:int = FutureSalts; get_future_salts#b921bd04 num:int = FutureSalts;