mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2024-11-22 09:26:37 +03:00
Added two-step verification (fixes #4) and more info for errors
This commit is contained in:
parent
be94bff576
commit
6c93d08b8d
|
@ -126,6 +126,8 @@ class RPCError(Exception):
|
|||
'CHAT_ADMIN_REQUIRED': 'Chat admin privileges are required to do that in the specified chat '
|
||||
'(for example, to send a message in a channel which is not yours).',
|
||||
|
||||
'PASSWORD_HASH_INVALID': 'The password (and thus its hash value) you entered is invalid.',
|
||||
|
||||
# 401 UNAUTHORIZED
|
||||
'AUTH_KEY_UNREGISTERED': 'The key is not registered in the system.',
|
||||
|
||||
|
@ -141,6 +143,8 @@ class RPCError(Exception):
|
|||
|
||||
'AUTH_KEY_PERM_EMPTY': 'The method is unavailable for temporary authorization key, not bound to permanent.',
|
||||
|
||||
'SESSION_PASSWORD_NEEDED': 'Two-steps verification is enabled and a password is required.',
|
||||
|
||||
# 420 FLOOD
|
||||
'FLOOD_WAIT_(\d+)': 'A wait of {} seconds is required.'
|
||||
}
|
||||
|
@ -164,6 +168,10 @@ class RPCError(Exception):
|
|||
self.additional_data = None
|
||||
super().__init__(self, error_msg)
|
||||
|
||||
# Add another field to easily determine whether this error
|
||||
# should be handled as a password-required error
|
||||
self.password_required = message == 'SESSION_PASSWORD_NEEDED'
|
||||
|
||||
called_super = True
|
||||
break
|
||||
|
||||
|
|
|
@ -58,4 +58,24 @@ def sha1(data):
|
|||
sha.update(data)
|
||||
return sha.digest()
|
||||
|
||||
|
||||
def sha256(data):
|
||||
"""Calculates the SHA256 digest for the given data"""
|
||||
sha = hashlib.sha256()
|
||||
sha.update(data)
|
||||
return sha.digest()
|
||||
|
||||
|
||||
def get_password_hash(pw, current_salt):
|
||||
"""Gets the password hash for the two-step verification.
|
||||
curent_salt should be the byte array provided by invoking GetPasswordRequest()"""
|
||||
|
||||
# Passwords are encoded as UTF-8
|
||||
# https://github.com/DrKLO/Telegram/blob/e31388/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java#L2003
|
||||
data = pw.encode('utf-8')
|
||||
|
||||
pw_hash = current_salt+data+current_salt
|
||||
return sha256(pw_hash)
|
||||
|
||||
|
||||
# endregion
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
from telethon.tl.types import UpdateShortChatMessage
|
||||
from telethon.tl.types import UpdateShortMessage
|
||||
from telethon import TelegramClient
|
||||
from telethon import TelegramClient, RPCError
|
||||
|
||||
from telethon.utils import get_display_name, get_input_peer
|
||||
|
||||
import shutil
|
||||
from getpass import getpass
|
||||
|
||||
# Get the (current) number of lines in the terminal
|
||||
cols, rows = shutil.get_terminal_size()
|
||||
|
@ -51,7 +52,16 @@ class InteractiveTelegramClient(TelegramClient):
|
|||
code_ok = False
|
||||
while not code_ok:
|
||||
code = input('Enter the code you just received: ')
|
||||
code_ok = self.sign_in(user_phone, code)
|
||||
try:
|
||||
code_ok = self.sign_in(user_phone, code)
|
||||
|
||||
# Two-step verification may be enabled
|
||||
except RPCError as e:
|
||||
if e.password_required:
|
||||
pw = getpass('Two step verification is enabled. Please enter your password: ')
|
||||
code_ok = self.sign_in(password=pw)
|
||||
else:
|
||||
raise e
|
||||
|
||||
def run(self):
|
||||
# Listen for updates
|
||||
|
|
|
@ -12,13 +12,19 @@ from telethon.tl import Session
|
|||
from telethon.tl.functions.upload import SaveBigFilePartRequest
|
||||
from telethon.tl.functions import InvokeWithLayerRequest, InitConnectionRequest
|
||||
from telethon.tl.functions.help import GetConfigRequest
|
||||
from telethon.tl.functions.auth import SendCodeRequest, SignInRequest, SignUpRequest, LogOutRequest
|
||||
from telethon.tl.functions.upload import SaveFilePartRequest, GetFileRequest
|
||||
from telethon.tl.functions.messages import \
|
||||
GetDialogsRequest, GetHistoryRequest, \
|
||||
SendMessageRequest, SendMediaRequest, \
|
||||
ReadHistoryRequest
|
||||
|
||||
from telethon.tl.functions.auth import \
|
||||
SendCodeRequest, CheckPasswordRequest, \
|
||||
SignInRequest, SignUpRequest, LogOutRequest
|
||||
|
||||
# The following is required to get the password salt
|
||||
from telethon.tl.functions.account import GetPasswordRequest
|
||||
|
||||
# All the types we need to work with
|
||||
from telethon.tl.types import \
|
||||
InputPeerEmpty, \
|
||||
|
@ -41,7 +47,7 @@ from telethon.tl.all_tlobjects import layer
|
|||
class TelegramClient:
|
||||
|
||||
# Current TelegramClient version
|
||||
__version__ = '0.6'
|
||||
__version__ = '0.7'
|
||||
|
||||
# region Initialization
|
||||
|
||||
|
@ -161,21 +167,32 @@ class TelegramClient:
|
|||
except InvalidDCError as error:
|
||||
self.reconnect_to_dc(error.new_dc)
|
||||
|
||||
def sign_in(self, phone_number, code):
|
||||
"""Completes the authorization of a phone number by providing the received code"""
|
||||
if phone_number not in self.phone_code_hashes:
|
||||
raise ValueError('Please make sure you have called send_code_request first.')
|
||||
def sign_in(self, phone_number=None, code=None, password=None):
|
||||
"""Completes the authorization of a phone number by providing the received code.
|
||||
|
||||
try:
|
||||
result = self.invoke(SignInRequest(
|
||||
phone_number, self.phone_code_hashes[phone_number], code))
|
||||
If no phone or code is provided, then the sole password will be used. The password
|
||||
should be used after a normal authorization attempt has happened and an RPCError
|
||||
with `.password_required = True` was raised"""
|
||||
if phone_number and code:
|
||||
if phone_number not in self.phone_code_hashes:
|
||||
raise ValueError('Please make sure you have called send_code_request first.')
|
||||
|
||||
except RPCError as error:
|
||||
if error.message.startswith('PHONE_CODE_'):
|
||||
print(error)
|
||||
return False
|
||||
else:
|
||||
raise error
|
||||
try:
|
||||
result = self.invoke(SignInRequest(
|
||||
phone_number, self.phone_code_hashes[phone_number], code))
|
||||
|
||||
except RPCError as error:
|
||||
if error.message.startswith('PHONE_CODE_'):
|
||||
print(error)
|
||||
return False
|
||||
else:
|
||||
raise error
|
||||
elif password:
|
||||
salt = self.invoke(GetPasswordRequest()).current_salt
|
||||
result = self.invoke(CheckPasswordRequest(utils.get_password_hash(password, salt)))
|
||||
else:
|
||||
raise ValueError('You must provide a phone_number and a code for the first time, '
|
||||
'and a password only if an RPCError was raised before.')
|
||||
|
||||
# Result is an Auth.Authorization TLObject
|
||||
self.session.user = result.user
|
||||
|
|
Loading…
Reference in New Issue
Block a user