mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2024-11-25 10:53:44 +03:00
Make lint happier
This commit is contained in:
parent
63c89af983
commit
02a847b64a
|
@ -70,10 +70,10 @@ If you've installed Telethon via pip, launch an interactive python3 session and
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
>>> from telethon import InteractiveTelegramClient
|
>>> from telethon import InteractiveTelegramClient
|
||||||
>>> # 'sessionid' can be 'yourname'. It'll be saved as yourname.session
|
>>> # 'session_id' can be 'your_name'. It'll be saved as your_name.session
|
||||||
>>> # Also (obviously) replace the api_id and api_hash with your values
|
>>> # Also (obviously) replace the api_id and api_hash with your values
|
||||||
...
|
...
|
||||||
>>> client = InteractiveTelegramClient('sessionid', '+34600000000',
|
>>> client = InteractiveTelegramClient('session_id', '+34600000000',
|
||||||
... api_id=12345, api_hash='0123456789abcdef0123456789abcdef')
|
... api_id=12345, api_hash='0123456789abcdef0123456789abcdef')
|
||||||
|
|
||||||
==================
|
==================
|
||||||
|
@ -93,7 +93,7 @@ Then, simply run ``python3 try_telethon.py`` to start the interactive example.
|
||||||
|
|
||||||
Using Telethon
|
Using Telethon
|
||||||
==============
|
==============
|
||||||
If you really want to learn how to use Telethon, it is **highly adviced** that
|
If you really want to learn how to use Telethon, it is **highly advised** that
|
||||||
you take a look to the
|
you take a look to the
|
||||||
`InteractiveTelegramClient <telethon/interactive_telegram_client.py>`_ file
|
`InteractiveTelegramClient <telethon/interactive_telegram_client.py>`_ file
|
||||||
and check how it works. This file contains everything you'll need to build
|
and check how it works. This file contains everything you'll need to build
|
||||||
|
@ -208,7 +208,7 @@ Once this is done, pass the proxy settings to the ``TelegramClient`` constructor
|
||||||
|
|
||||||
>>> from telethon import InteractiveTelegramClient
|
>>> from telethon import InteractiveTelegramClient
|
||||||
>>> import socks
|
>>> import socks
|
||||||
>>> client = InteractiveTelegramClient('sessionid', '+34600000000',
|
>>> client = InteractiveTelegramClient('session_id', '+34600000000',
|
||||||
... api_id=12345, api_hash='0123456789abcdef0123456789abcdef',
|
... api_id=12345, api_hash='0123456789abcdef0123456789abcdef',
|
||||||
... proxy=(socks.SOCKS5, 'localhost', 4444))
|
... proxy=(socks.SOCKS5, 'localhost', 4444))
|
||||||
|
|
||||||
|
|
|
@ -63,20 +63,20 @@ def get_create_path_for(tlobject):
|
||||||
return os.path.join(out_dir, get_file_name(tlobject, add_extension=True))
|
return os.path.join(out_dir, get_file_name(tlobject, add_extension=True))
|
||||||
|
|
||||||
|
|
||||||
def get_path_for_type(type, relative_to='.'):
|
def get_path_for_type(type_, relative_to='.'):
|
||||||
"""Similar to getting the path for a TLObject, it might not be possible
|
"""Similar to getting the path for a TLObject, it might not be possible
|
||||||
to have the TLObject itself but rather its name (the type);
|
to have the TLObject itself but rather its name (the type);
|
||||||
this method works in the same way, returning a relative path"""
|
this method works in the same way, returning a relative path"""
|
||||||
if type.lower() in {'int', 'long', 'int128', 'int256', 'double',
|
if type_.lower() in {'int', 'long', 'int128', 'int256', 'double',
|
||||||
'vector', 'string', 'bool', 'true', 'bytes', 'date'}:
|
'vector', 'string', 'bool', 'true', 'bytes', 'date'}:
|
||||||
path = 'index.html#%s' % type.lower()
|
path = 'index.html#%s' % type_.lower()
|
||||||
|
|
||||||
elif '.' in type:
|
elif '.' in type_:
|
||||||
# If it's not a core type, then it has to be a custom Telegram type
|
# If it's not a core type, then it has to be a custom Telegram type
|
||||||
namespace, name = type.split('.')
|
namespace, name = type_.split('.')
|
||||||
path = 'types/%s/%s' % (namespace, get_file_name(name, add_extension=True))
|
path = 'types/%s/%s' % (namespace, get_file_name(name, add_extension=True))
|
||||||
else:
|
else:
|
||||||
path = 'types/%s' % get_file_name(type, add_extension=True)
|
path = 'types/%s' % get_file_name(type_, add_extension=True)
|
||||||
|
|
||||||
return get_relative_path(path, relative_to)
|
return get_relative_path(path, relative_to)
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ def build_menu(docs, filename, relative_main_index):
|
||||||
def generate_index(folder, original_paths):
|
def generate_index(folder, original_paths):
|
||||||
"""Generates the index file for the specified folder"""
|
"""Generates the index file for the specified folder"""
|
||||||
|
|
||||||
# Determine the namespaces listed here (as subfolders)
|
# Determine the namespaces listed here (as sub folders)
|
||||||
# and the files (.html files) that we should link to
|
# and the files (.html files) that we should link to
|
||||||
namespaces = []
|
namespaces = []
|
||||||
files = []
|
files = []
|
||||||
|
@ -320,10 +320,10 @@ def generate_documentation(scheme_file):
|
||||||
docs.write_text('The following %d methods return this type as a result.' % len(functions))
|
docs.write_text('The following %d methods return this type as a result.' % len(functions))
|
||||||
|
|
||||||
docs.begin_table(2)
|
docs.begin_table(2)
|
||||||
for function in functions:
|
for func in functions:
|
||||||
link = get_create_path_for(function)
|
link = get_create_path_for(func)
|
||||||
link = get_relative_path(link, relative_to=filename)
|
link = get_relative_path(link, relative_to=filename)
|
||||||
docs.add_row(get_class_name(function), link=link)
|
docs.add_row(get_class_name(func), link=link)
|
||||||
docs.end_table()
|
docs.end_table()
|
||||||
docs.end_body()
|
docs.end_body()
|
||||||
|
|
||||||
|
|
|
@ -188,6 +188,7 @@ messages = result.messages</pre>
|
||||||
you're still able to invoke these methods manually.</p>
|
you're still able to invoke these methods manually.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
<script>
|
<script>
|
||||||
contentDiv = document.getElementById("contentDiv");
|
contentDiv = document.getElementById("contentDiv");
|
||||||
searchDiv = document.getElementById("searchDiv");
|
searchDiv = document.getElementById("searchDiv");
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from .aes import AES
|
from .aes import AES
|
||||||
from .rsa import RSA, RSAServerKey
|
from .rsa import RSA, RSAServerKey
|
||||||
from .auth_key import AuthKey
|
from .auth_key import AuthKey
|
||||||
from .factorizator import Factorizator
|
from .factorization import Factorization
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from random import randint
|
from random import randint
|
||||||
|
|
||||||
|
|
||||||
class Factorizator:
|
class Factorization:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def find_small_multiplier_lopatin(what):
|
def find_small_multiplier_lopatin(what):
|
||||||
"""Finds the small multiplier by using Lopatin's method"""
|
"""Finds the small multiplier by using Lopatin's method"""
|
||||||
|
@ -25,7 +25,7 @@ class Factorizator:
|
||||||
|
|
||||||
x = c
|
x = c
|
||||||
z = y - x if x < y else x - y
|
z = y - x if x < y else x - y
|
||||||
g = Factorizator.gcd(z, what)
|
g = Factorization.gcd(z, what)
|
||||||
if g != 1:
|
if g != 1:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@ -58,5 +58,5 @@ class Factorizator:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def factorize(pq):
|
def factorize(pq):
|
||||||
"""Factorizes the given number and returns both the divisor and the number divided by the divisor"""
|
"""Factorizes the given number and returns both the divisor and the number divided by the divisor"""
|
||||||
divisor = Factorizator.find_small_multiplier_lopatin(pq)
|
divisor = Factorization.find_small_multiplier_lopatin(pq)
|
||||||
return divisor, pq // divisor
|
return divisor, pq // divisor
|
|
@ -112,7 +112,7 @@ class RPCError(Exception):
|
||||||
'FILE_PARTS_INVALID': 'The number of file parts is invalid.',
|
'FILE_PARTS_INVALID': 'The number of file parts is invalid.',
|
||||||
'FILE_PART_(\d+)_MISSING':
|
'FILE_PART_(\d+)_MISSING':
|
||||||
'Part {} of the file is missing from storage.',
|
'Part {} of the file is missing from storage.',
|
||||||
'MD5_CHECKSUM_INVALID': 'The MD5 checksums do not match.',
|
'MD5_CHECKSUM_INVALID': 'The MD5 check-sums do not match.',
|
||||||
'PHOTO_INVALID_DIMENSIONS': 'The photo dimensions are invalid.',
|
'PHOTO_INVALID_DIMENSIONS': 'The photo dimensions are invalid.',
|
||||||
'FIELD_NAME_INVALID': 'The field with the name FIELD_NAME is invalid.',
|
'FIELD_NAME_INVALID': 'The field with the name FIELD_NAME is invalid.',
|
||||||
'FIELD_NAME_EMPTY': 'The field with the name FIELD_NAME is missing.',
|
'FIELD_NAME_EMPTY': 'The field with the name FIELD_NAME is missing.',
|
||||||
|
|
|
@ -41,8 +41,8 @@ def calc_msg_key(data):
|
||||||
return sha1(data)[4:20]
|
return sha1(data)[4:20]
|
||||||
|
|
||||||
|
|
||||||
def generate_key_data_from_nonces(server_nonce, new_nonce):
|
def generate_key_data_from_nonce(server_nonce, new_nonce):
|
||||||
"""Generates the key data corresponding to the given nonces"""
|
"""Generates the key data corresponding to the given nonce"""
|
||||||
hash1 = sha1(bytes(new_nonce + server_nonce))
|
hash1 = sha1(bytes(new_nonce + server_nonce))
|
||||||
hash2 = sha1(bytes(server_nonce + new_nonce))
|
hash2 = sha1(bytes(server_nonce + new_nonce))
|
||||||
hash3 = sha1(bytes(new_nonce + new_nonce))
|
hash3 = sha1(bytes(new_nonce + new_nonce))
|
||||||
|
@ -68,10 +68,11 @@ def sha256(data):
|
||||||
|
|
||||||
def get_password_hash(pw, current_salt):
|
def get_password_hash(pw, current_salt):
|
||||||
"""Gets the password hash for the two-step verification.
|
"""Gets the password hash for the two-step verification.
|
||||||
curent_salt should be the byte array provided by invoking GetPasswordRequest()"""
|
current_salt should be the byte array provided by invoking GetPasswordRequest()"""
|
||||||
|
|
||||||
# Passwords are encoded as UTF-8
|
# Passwords are encoded as UTF-8
|
||||||
# https://github.com/DrKLO/Telegram/blob/e31388/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java#L2003
|
# At https://github.com/DrKLO/Telegram/blob/e31388
|
||||||
|
# src/main/java/org/telegram/ui/LoginActivity.java#L2003
|
||||||
data = pw.encode('utf-8')
|
data = pw.encode('utf-8')
|
||||||
|
|
||||||
pw_hash = current_salt + data + current_salt
|
pw_hash = current_salt + data + current_salt
|
||||||
|
|
|
@ -86,7 +86,7 @@ class InteractiveTelegramClient(TelegramClient):
|
||||||
|
|
||||||
# Display them so the user can choose
|
# Display them so the user can choose
|
||||||
for i, entity in enumerate(entities):
|
for i, entity in enumerate(entities):
|
||||||
i += 1 # 1-based index for normies
|
i += 1 # 1-based index
|
||||||
try:
|
try:
|
||||||
print('{}. {}'.format(i, get_display_name(entity)))
|
print('{}. {}'.format(i, get_display_name(entity)))
|
||||||
except UnicodeEncodeError:
|
except UnicodeEncodeError:
|
||||||
|
|
|
@ -2,7 +2,7 @@ import os
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from .. import helpers as utils
|
from .. import helpers as utils
|
||||||
from ..crypto import AES, RSA, AuthKey, Factorizator
|
from ..crypto import AES, RSA, AuthKey, Factorization
|
||||||
from ..network import MtProtoPlainSender
|
from ..network import MtProtoPlainSender
|
||||||
from ..utils import BinaryReader, BinaryWriter
|
from ..utils import BinaryReader, BinaryWriter
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ def do_authentication(transport):
|
||||||
|
|
||||||
# Step 2 sending: DH Exchange
|
# Step 2 sending: DH Exchange
|
||||||
new_nonce = os.urandom(32)
|
new_nonce = os.urandom(32)
|
||||||
p, q = Factorizator.factorize(pq)
|
p, q = Factorization.factorize(pq)
|
||||||
with BinaryWriter() as pq_inner_data_writer:
|
with BinaryWriter() as pq_inner_data_writer:
|
||||||
pq_inner_data_writer.write_int(
|
pq_inner_data_writer.write_int(
|
||||||
0x83c95aec, signed=False) # PQ Inner Data
|
0x83c95aec, signed=False) # PQ Inner Data
|
||||||
|
@ -120,12 +120,12 @@ def do_authentication(transport):
|
||||||
encrypted_answer = reader.tgread_bytes()
|
encrypted_answer = reader.tgread_bytes()
|
||||||
|
|
||||||
# Step 3 sending: Complete DH Exchange
|
# Step 3 sending: Complete DH Exchange
|
||||||
key, iv = utils.generate_key_data_from_nonces(server_nonce, new_nonce)
|
key, iv = utils.generate_key_data_from_nonce(server_nonce, new_nonce)
|
||||||
plain_text_answer = AES.decrypt_ige(encrypted_answer, key, iv)
|
plain_text_answer = AES.decrypt_ige(encrypted_answer, key, iv)
|
||||||
|
|
||||||
g, dh_prime, ga, time_offset = None, None, None, None
|
g, dh_prime, ga, time_offset = None, None, None, None
|
||||||
with BinaryReader(plain_text_answer) as dh_inner_data_reader:
|
with BinaryReader(plain_text_answer) as dh_inner_data_reader:
|
||||||
dh_inner_data_reader.read(20) # hashsum
|
dh_inner_data_reader.read(20) # hash sum
|
||||||
code = dh_inner_data_reader.read_int(signed=False)
|
code = dh_inner_data_reader.read_int(signed=False)
|
||||||
if code != 0xb5890dba:
|
if code != 0xb5890dba:
|
||||||
raise AssertionError('Invalid DH Inner Data code: {}'.format(code))
|
raise AssertionError('Invalid DH Inner Data code: {}'.format(code))
|
||||||
|
|
|
@ -46,7 +46,7 @@ class MtProtoPlainSender:
|
||||||
# See https://core.telegram.org/mtproto/description#message-identifier-msg-id
|
# See https://core.telegram.org/mtproto/description#message-identifier-msg-id
|
||||||
ms_time = int(time.time() * 1000)
|
ms_time = int(time.time() * 1000)
|
||||||
new_msg_id = (((ms_time // 1000) << 32)
|
new_msg_id = (((ms_time // 1000) << 32)
|
||||||
| # "must approximately equal unixtime*2^32"
|
| # "must approximately equal unix time*2^32"
|
||||||
((ms_time % 1000) << 22)
|
((ms_time % 1000) << 22)
|
||||||
| # "approximate moment in time the message was created"
|
| # "approximate moment in time the message was created"
|
||||||
random.randint(0, 524288)
|
random.randint(0, 524288)
|
||||||
|
|
|
@ -282,9 +282,9 @@ class MtProtoSender:
|
||||||
def handle_pong(self, msg_id, sequence, reader, request):
|
def handle_pong(self, msg_id, sequence, reader, request):
|
||||||
self.logger.debug('Handling pong')
|
self.logger.debug('Handling pong')
|
||||||
reader.read_int(signed=False) # code
|
reader.read_int(signed=False) # code
|
||||||
recv_msg_id = reader.read_long(signed=False)
|
received_msg_id = reader.read_long(signed=False)
|
||||||
|
|
||||||
if recv_msg_id == request.msg_id:
|
if received_msg_id == request.msg_id:
|
||||||
self.logger.warning('Pong confirmed a request')
|
self.logger.warning('Pong confirmed a request')
|
||||||
request.confirm_received = True
|
request.confirm_received = True
|
||||||
|
|
||||||
|
@ -478,7 +478,7 @@ class MtProtoSender:
|
||||||
self.logger.info('Receiving updates cancelled')
|
self.logger.info('Receiving updates cancelled')
|
||||||
except OSError:
|
except OSError:
|
||||||
self.logger.warning('OSError on updates thread, %s logging out',
|
self.logger.warning('OSError on updates thread, %s logging out',
|
||||||
'was' if self.logging_out else 'was not')
|
'was' if self.logging_out else 'was not')
|
||||||
|
|
||||||
if self.logging_out:
|
if self.logging_out:
|
||||||
# This error is okay when logging out, means we got disconnected
|
# This error is okay when logging out, means we got disconnected
|
||||||
|
|
|
@ -116,30 +116,30 @@ def parse_message_entities(msg):
|
||||||
del msg[entity.offset]
|
del msg[entity.offset]
|
||||||
|
|
||||||
# Iterate over all the entities but the current
|
# Iterate over all the entities but the current
|
||||||
for subentity in [e for e in entities if e is not entity]:
|
for sub_entity in [e for e in entities if e is not entity]:
|
||||||
# First case, one in one out: so*me_th_in*g.
|
# First case, one in one out: so*me_th_in*g.
|
||||||
# In this case, the current entity length is decreased by two,
|
# In this case, the current entity length is decreased by two,
|
||||||
# and all the subentities offset decreases 1
|
# and all the sub_entities offset decreases 1
|
||||||
if (subentity.offset > entity.offset and
|
if (sub_entity.offset > entity.offset and
|
||||||
subentity.offset + subentity.length <
|
sub_entity.offset + sub_entity.length <
|
||||||
entity.offset + entity.length):
|
entity.offset + entity.length):
|
||||||
entity.length -= 2
|
entity.length -= 2
|
||||||
subentity.offset -= 1
|
sub_entity.offset -= 1
|
||||||
|
|
||||||
# Second case, both inside: so*me_th*in_g.
|
# Second case, both inside: so*me_th*in_g.
|
||||||
# In this case, the current entity length is decreased by one,
|
# In this case, the current entity length is decreased by one,
|
||||||
# and all the subentities offset and length decrease 1
|
# and all the sub_entities offset and length decrease 1
|
||||||
elif (entity.offset < subentity.offset < entity.offset +
|
elif (entity.offset < sub_entity.offset < entity.offset +
|
||||||
entity.length < subentity.offset + subentity.length):
|
entity.length < sub_entity.offset + sub_entity.length):
|
||||||
entity.length -= 1
|
entity.length -= 1
|
||||||
subentity.offset -= 1
|
sub_entity.offset -= 1
|
||||||
subentity.length -= 1
|
sub_entity.length -= 1
|
||||||
|
|
||||||
# Third case, both outside: so*me*th_in_g.
|
# Third case, both outside: so*me*th_in_g.
|
||||||
# In this case, the current entity is left untouched,
|
# In this case, the current entity is left untouched,
|
||||||
# and all the subentities offset decreases 2
|
# and all the sub_entities offset decreases 2
|
||||||
elif subentity.offset > entity.offset + entity.length:
|
elif sub_entity.offset > entity.offset + entity.length:
|
||||||
subentity.offset -= 2
|
sub_entity.offset -= 2
|
||||||
|
|
||||||
# Finally, we can join our poor mutilated message back and return
|
# Finally, we can join our poor mutilated message back and return
|
||||||
msg = ''.join(msg)
|
msg = ''.join(msg)
|
||||||
|
|
|
@ -17,8 +17,7 @@ from .tl.functions import InitConnectionRequest, InvokeWithLayerRequest
|
||||||
from .tl.functions.account import GetPasswordRequest
|
from .tl.functions.account import GetPasswordRequest
|
||||||
from .tl.functions.auth import (CheckPasswordRequest, LogOutRequest,
|
from .tl.functions.auth import (CheckPasswordRequest, LogOutRequest,
|
||||||
SendCodeRequest, SignInRequest,
|
SendCodeRequest, SignInRequest,
|
||||||
SignUpRequest)
|
SignUpRequest, ImportBotAuthorizationRequest)
|
||||||
from .tl.functions.auth import ImportBotAuthorizationRequest
|
|
||||||
from .tl.functions.help import GetConfigRequest
|
from .tl.functions.help import GetConfigRequest
|
||||||
from .tl.functions.messages import (
|
from .tl.functions.messages import (
|
||||||
GetDialogsRequest, GetHistoryRequest, ReadHistoryRequest, SendMediaRequest,
|
GetDialogsRequest, GetHistoryRequest, ReadHistoryRequest, SendMediaRequest,
|
||||||
|
@ -34,7 +33,7 @@ from .tl.types import (
|
||||||
MessageMediaContact, MessageMediaDocument, MessageMediaPhoto,
|
MessageMediaContact, MessageMediaDocument, MessageMediaPhoto,
|
||||||
UserProfilePhotoEmpty)
|
UserProfilePhotoEmpty)
|
||||||
from .utils import (find_user_or_chat, get_input_peer,
|
from .utils import (find_user_or_chat, get_input_peer,
|
||||||
get_appropiate_part_size, get_extension)
|
get_appropriated_part_size, get_extension)
|
||||||
|
|
||||||
|
|
||||||
class TelegramClient:
|
class TelegramClient:
|
||||||
|
@ -273,7 +272,7 @@ class TelegramClient:
|
||||||
|
|
||||||
self.session = None
|
self.session = None
|
||||||
return True
|
return True
|
||||||
except:
|
except (RPCError, ConnectionError):
|
||||||
# Something happened when logging out, restore the state back
|
# Something happened when logging out, restore the state back
|
||||||
self.sender.logging_out = False
|
self.sender.logging_out = False
|
||||||
return False
|
return False
|
||||||
|
@ -282,8 +281,7 @@ class TelegramClient:
|
||||||
def list_sessions():
|
def list_sessions():
|
||||||
"""Lists all the sessions of the users who have ever connected
|
"""Lists all the sessions of the users who have ever connected
|
||||||
using this client and never logged out"""
|
using this client and never logged out"""
|
||||||
return [path.splitext(path.basename(f))[
|
return [path.splitext(path.basename(f))[0]
|
||||||
0] # splitext = split ext (not spli text!)
|
|
||||||
for f in listdir('.') if f.endswith('.session')]
|
for f in listdir('.') if f.endswith('.session')]
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
@ -424,7 +422,7 @@ class TelegramClient:
|
||||||
"""
|
"""
|
||||||
file_size = path.getsize(file_path)
|
file_size = path.getsize(file_path)
|
||||||
if not part_size_kb:
|
if not part_size_kb:
|
||||||
part_size_kb = get_appropiate_part_size(file_size)
|
part_size_kb = get_appropriated_part_size(file_size)
|
||||||
|
|
||||||
if part_size_kb > 512:
|
if part_size_kb > 512:
|
||||||
raise ValueError('The part size must be less or equal to 512KB')
|
raise ValueError('The part size must be less or equal to 512KB')
|
||||||
|
@ -534,7 +532,7 @@ class TelegramClient:
|
||||||
add_extension=True,
|
add_extension=True,
|
||||||
download_big=True):
|
download_big=True):
|
||||||
"""Downloads the profile photo for an user or a chat (including channels).
|
"""Downloads the profile photo for an user or a chat (including channels).
|
||||||
Returns False if no photo was providen, or if it was Empty"""
|
Returns False if no photo was provided, or if it was Empty"""
|
||||||
|
|
||||||
if (not profile_photo or
|
if (not profile_photo or
|
||||||
isinstance(profile_photo, UserProfilePhotoEmpty) or
|
isinstance(profile_photo, UserProfilePhotoEmpty) or
|
||||||
|
@ -693,7 +691,7 @@ class TelegramClient:
|
||||||
if not file_size:
|
if not file_size:
|
||||||
raise ValueError('A part size value must be provided')
|
raise ValueError('A part size value must be provided')
|
||||||
else:
|
else:
|
||||||
part_size_kb = get_appropiate_part_size(file_size)
|
part_size_kb = get_appropriated_part_size(file_size)
|
||||||
|
|
||||||
part_size = int(part_size_kb * 1024)
|
part_size = int(part_size_kb * 1024)
|
||||||
if part_size % 1024 != 0:
|
if part_size % 1024 != 0:
|
||||||
|
|
|
@ -31,7 +31,7 @@ class Session:
|
||||||
try:
|
try:
|
||||||
os.remove('{}.session'.format(self.session_user_id))
|
os.remove('{}.session'.format(self.session_user_id))
|
||||||
return True
|
return True
|
||||||
except:
|
except OSError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -54,7 +54,7 @@ class Session:
|
||||||
# Refer to mtproto_plain_sender.py for the original method, this is a simple copy
|
# Refer to mtproto_plain_sender.py for the original method, this is a simple copy
|
||||||
ms_time = int(time.time() * 1000)
|
ms_time = int(time.time() * 1000)
|
||||||
new_msg_id = (((ms_time // 1000 + self.time_offset) << 32)
|
new_msg_id = (((ms_time // 1000 + self.time_offset) << 32)
|
||||||
| # "must approximately equal unixtime*2^32"
|
| # "must approximately equal unix time*2^32"
|
||||||
((ms_time % 1000) << 22)
|
((ms_time % 1000) << 22)
|
||||||
| # "approximate moment in time the message was created"
|
| # "approximate moment in time the message was created"
|
||||||
random.randint(0, 524288)
|
random.randint(0, 524288)
|
||||||
|
|
|
@ -77,8 +77,8 @@ def find_user_or_chat(peer, users, chats):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_appropiate_part_size(file_size):
|
def get_appropriated_part_size(file_size):
|
||||||
"""Gets the appropiate part size when uploading or downloading files,
|
"""Gets the appropriated part size when uploading or downloading files,
|
||||||
given an initial file size"""
|
given an initial file size"""
|
||||||
if file_size <= 1048576: # 1MB
|
if file_size <= 1048576: # 1MB
|
||||||
return 32
|
return 32
|
||||||
|
|
|
@ -42,12 +42,12 @@ class TLObject:
|
||||||
([0-9a-f]+) # The constructor ID is in hexadecimal form
|
([0-9a-f]+) # The constructor ID is in hexadecimal form
|
||||||
|
|
||||||
(?:\s # After that, we want to match its arguments (name:type)
|
(?:\s # After that, we want to match its arguments (name:type)
|
||||||
\{? # For handling the start of the «{X:Type}» case
|
{? # For handling the start of the «{X:Type}» case
|
||||||
\w+ # The argument name will always be an alpha-only name
|
\w+ # The argument name will always be an alpha-only name
|
||||||
: # Then comes the separator between name:type
|
: # Then comes the separator between name:type
|
||||||
[\w\d<>#.?!]+ # The type is slightly more complex, since it's alphanumeric and it can
|
[\w\d<>#.?!]+ # The type is slightly more complex, since it's alphanumeric and it can
|
||||||
# also have Vector<type>, flags:# and flags.0?default, plus :!X as type
|
# also have Vector<type>, flags:# and flags.0?default, plus :!X as type
|
||||||
\}? # For handling the end of the «{X:Type}» case
|
}? # For handling the end of the «{X:Type}» case
|
||||||
)* # Match 0 or more arguments
|
)* # Match 0 or more arguments
|
||||||
\s # Leave a space between the arguments and the equal
|
\s # Leave a space between the arguments and the equal
|
||||||
=
|
=
|
||||||
|
@ -58,12 +58,12 @@ class TLObject:
|
||||||
|
|
||||||
# Sub-regex to match the arguments (sadly, it cannot be embedded in the first regex)
|
# Sub-regex to match the arguments (sadly, it cannot be embedded in the first regex)
|
||||||
args_match = re.findall(r'''
|
args_match = re.findall(r'''
|
||||||
(\{)? # We may or may not capture the opening brace
|
({)? # We may or may not capture the opening brace
|
||||||
(\w+) # First we capture any alpha name with length 1 or more
|
(\w+) # First we capture any alpha name with length 1 or more
|
||||||
: # Which is separated from its type by a colon
|
: # Which is separated from its type by a colon
|
||||||
([\w\d<>#.?!]+) # The type is slightly more complex, since it's alphanumeric and it can
|
([\w\d<>#.?!]+) # The type is slightly more complex, since it's alphanumeric and it can
|
||||||
# also have Vector<type>, flags:# and flags.0?default, plus :!X as type
|
# also have Vector<type>, flags:# and flags.0?default, plus :!X as type
|
||||||
(\})? # We may or not capture the closing brace
|
(})? # We may or not capture the closing brace
|
||||||
''', tl, re.IGNORECASE | re.VERBOSE)
|
''', tl, re.IGNORECASE | re.VERBOSE)
|
||||||
|
|
||||||
# Retrieve the matched arguments
|
# Retrieve the matched arguments
|
||||||
|
|
|
@ -250,7 +250,7 @@ class TLGenerator:
|
||||||
'"""File generated by TLObjects\' generator. All changes will be ERASED"""')
|
'"""File generated by TLObjects\' generator. All changes will be ERASED"""')
|
||||||
builder.writeln()
|
builder.writeln()
|
||||||
|
|
||||||
builder.writeln('from ..tl import types, functions')
|
builder.writeln('from . import types, functions')
|
||||||
builder.writeln()
|
builder.writeln()
|
||||||
|
|
||||||
# Create a variable to indicate which layer this is
|
# Create a variable to indicate which layer this is
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import telethon.helpers as utils
|
import telethon.helpers as utils
|
||||||
from telethon.crypto import AES, Factorizator
|
from telethon.crypto import AES, Factorization
|
||||||
|
|
||||||
|
|
||||||
class CryptoTests(unittest.TestCase):
|
class CryptoTests(unittest.TestCase):
|
||||||
|
@ -23,11 +23,11 @@ class CryptoTests(unittest.TestCase):
|
||||||
def test_sha1():
|
def test_sha1():
|
||||||
string = 'Example string'
|
string = 'Example string'
|
||||||
|
|
||||||
hashsum = utils.sha1(string.encode('utf-8'))
|
hash_sum = utils.sha1(string.encode('utf-8'))
|
||||||
expected = b'\nT\x92|\x8d\x06:)\x99\x04\x8e\xf8j?\xc4\x8e\xd3}m9'
|
expected = b'\nT\x92|\x8d\x06:)\x99\x04\x8e\xf8j?\xc4\x8e\xd3}m9'
|
||||||
|
|
||||||
assert hashsum == expected, 'Invalid sha1 hashsum representation (should be {}, but is {})'\
|
assert hash_sum == expected, 'Invalid sha1 hash_sum representation (should be {}, but is {})'\
|
||||||
.format(expected, hashsum)
|
.format(expected, hash_sum)
|
||||||
|
|
||||||
def test_aes_encrypt(self):
|
def test_aes_encrypt(self):
|
||||||
value = AES.encrypt_ige(self.plain_text, self.key, self.iv)
|
value = AES.encrypt_ige(self.plain_text, self.key, self.iv)
|
||||||
|
@ -67,8 +67,12 @@ class CryptoTests(unittest.TestCase):
|
||||||
msg_key = b'\xba\x1a\xcf\xda\xa8^Cbl\xfa\xb6\x0c:\x9b\xb0\xfc'
|
msg_key = b'\xba\x1a\xcf\xda\xa8^Cbl\xfa\xb6\x0c:\x9b\xb0\xfc'
|
||||||
|
|
||||||
key, iv = utils.calc_key(shared_key, msg_key, client=True)
|
key, iv = utils.calc_key(shared_key, msg_key, client=True)
|
||||||
expected_key = b"\xaf\xe3\x84Qm\xe0!\x0c\xd91\xe4\x9a\xa0v_gcx\xa1\xb0\xc9\xbc\x16'v\xcf,\x9dM\xae\xc6\xa5"
|
expected_key = b"\xaf\xe3\x84Qm\xe0!\x0c\xd91\xe4\x9a\xa0v_gc" \
|
||||||
expected_iv = b'\xb8Q\xf3\xc5\xa3]\xc6\xdf\x9e\xe0Q\xbd"\x8d\x13\t\x0e\x9a\x9d^8\xa2\xf8\xe7\x00w\xd9\xc1\xa7\xa0\xf7\x0f'
|
b"x\xa1\xb0\xc9\xbc\x16'v\xcf,\x9dM\xae\xc6\xa5"
|
||||||
|
|
||||||
|
expected_iv = b'\xb8Q\xf3\xc5\xa3]\xc6\xdf\x9e\xe0Q\xbd"\x8d' \
|
||||||
|
b'\x13\t\x0e\x9a\x9d^8\xa2\xf8\xe7\x00w\xd9\xc1' \
|
||||||
|
b'\xa7\xa0\xf7\x0f'
|
||||||
|
|
||||||
assert key == expected_key, 'Invalid key (expected ("{}"), got ("{}"))'.format(
|
assert key == expected_key, 'Invalid key (expected ("{}"), got ("{}"))'.format(
|
||||||
expected_key, key)
|
expected_key, key)
|
||||||
|
@ -79,8 +83,12 @@ class CryptoTests(unittest.TestCase):
|
||||||
msg_key = b'\x86m\x92i\xcf\x8b\x93\xaa\x86K\x1fi\xd04\x83]'
|
msg_key = b'\x86m\x92i\xcf\x8b\x93\xaa\x86K\x1fi\xd04\x83]'
|
||||||
|
|
||||||
key, iv = utils.calc_key(shared_key, msg_key, client=False)
|
key, iv = utils.calc_key(shared_key, msg_key, client=False)
|
||||||
expected_key = b'\xdd0X\xb6\x93\x8e\xc9y\xef\x83\xf8\x8cj\xa7h\x03\xe2\xc6\xb16\xc5\xbb\xfc\xe7\xdf\xd6\xb1g\xf7u\xcfk'
|
expected_key = b'\xdd0X\xb6\x93\x8e\xc9y\xef\x83\xf8\x8cj' \
|
||||||
expected_iv = b'\xdcL\xc2\x18\x01J"X\x86lb\xb6\xb547\xfd\xe2a4\xb6\xaf}FS\xd7[\xe0N\r\x19\xfb\xbc'
|
b'\xa7h\x03\xe2\xc6\xb16\xc5\xbb\xfc\xe7' \
|
||||||
|
b'\xdf\xd6\xb1g\xf7u\xcfk'
|
||||||
|
|
||||||
|
expected_iv = b'\xdcL\xc2\x18\x01J"X\x86lb\xb6\xb547\xfd' \
|
||||||
|
b'\xe2a4\xb6\xaf}FS\xd7[\xe0N\r\x19\xfb\xbc'
|
||||||
|
|
||||||
assert key == expected_key, 'Invalid key (expected ("{}"), got ("{}"))'.format(
|
assert key == expected_key, 'Invalid key (expected ("{}"), got ("{}"))'.format(
|
||||||
expected_key, key)
|
expected_key, key)
|
||||||
|
@ -95,11 +103,11 @@ class CryptoTests(unittest.TestCase):
|
||||||
value, expected)
|
value, expected)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def test_generate_key_data_from_nonces():
|
def test_generate_key_data_from_nonce():
|
||||||
server_nonce = b'I am the server nonce.'
|
server_nonce = b'I am the server nonce.'
|
||||||
new_nonce = b'I am a new calculated nonce.'
|
new_nonce = b'I am a new calculated nonce.'
|
||||||
|
|
||||||
key, iv = utils.generate_key_data_from_nonces(server_nonce, new_nonce)
|
key, iv = utils.generate_key_data_from_nonce(server_nonce, new_nonce)
|
||||||
expected_key = b'?\xc4\xbd\xdf\rWU\x8a\xf5\x0f+V\xdc\x96up\x1d\xeeG\x00\x81|\x1eg\x8a\x8f{\xf0y\x80\xda\xde'
|
expected_key = b'?\xc4\xbd\xdf\rWU\x8a\xf5\x0f+V\xdc\x96up\x1d\xeeG\x00\x81|\x1eg\x8a\x8f{\xf0y\x80\xda\xde'
|
||||||
expected_iv = b'Q\x9dpZ\xb7\xdd\xcb\x82_\xfa\xf4\x90\xecn\x10\x9cD\xd2\x01\x8d\x83\xa0\xa4^\xb8\x91,\x7fI am'
|
expected_iv = b'Q\x9dpZ\xb7\xdd\xcb\x82_\xfa\xf4\x90\xecn\x10\x9cD\xd2\x01\x8d\x83\xa0\xa4^\xb8\x91,\x7fI am'
|
||||||
|
|
||||||
|
@ -109,9 +117,9 @@ class CryptoTests(unittest.TestCase):
|
||||||
key, expected_iv)
|
key, expected_iv)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def test_factorizator():
|
def test_factorize():
|
||||||
pq = 3118979781119966969
|
pq = 3118979781119966969
|
||||||
p, q = Factorizator.factorize(pq)
|
p, q = Factorization.factorize(pq)
|
||||||
|
|
||||||
assert p == 1719614201, 'Factorized pair did not yield the correct result'
|
assert p == 1719614201, 'Factorized pair did not yield the correct result'
|
||||||
assert q == 1813767169, 'Factorized pair did not yield the correct result'
|
assert q == 1813767169, 'Factorized pair did not yield the correct result'
|
||||||
|
|
|
@ -12,10 +12,10 @@ def run_server_echo_thread(port):
|
||||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||||
s.bind(('', port))
|
s.bind(('', port))
|
||||||
s.listen(1)
|
s.listen(1)
|
||||||
conn, addr = s.accept()
|
connection, address = s.accept()
|
||||||
with conn:
|
with connection:
|
||||||
data = conn.recv(16)
|
data = connection.recv(16)
|
||||||
conn.send(data)
|
connection.send(data)
|
||||||
|
|
||||||
server = threading.Thread(target=server_thread)
|
server = threading.Thread(target=server_thread)
|
||||||
server.start()
|
server.start()
|
||||||
|
|
|
@ -7,18 +7,18 @@ from telethon.interactive_telegram_client import (InteractiveTelegramClient,
|
||||||
|
|
||||||
def load_settings(path='api/settings'):
|
def load_settings(path='api/settings'):
|
||||||
"""Loads the user settings located under `api/`"""
|
"""Loads the user settings located under `api/`"""
|
||||||
settings = {}
|
result = {}
|
||||||
with open(path, 'r', encoding='utf-8') as file:
|
with open(path, 'r', encoding='utf-8') as file:
|
||||||
for line in file:
|
for line in file:
|
||||||
value_pair = line.split('=')
|
value_pair = line.split('=')
|
||||||
left = value_pair[0].strip()
|
left = value_pair[0].strip()
|
||||||
right = value_pair[1].strip()
|
right = value_pair[1].strip()
|
||||||
if right.isnumeric():
|
if right.isnumeric():
|
||||||
settings[left] = int(right)
|
result[left] = int(right)
|
||||||
else:
|
else:
|
||||||
settings[left] = right
|
result[left] = right
|
||||||
|
|
||||||
return settings
|
return result
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
Loading…
Reference in New Issue
Block a user