Skip to content

Commit 074910c

Browse files
Fix QRLogin
1 parent 593d17f commit 074910c

2 files changed

Lines changed: 99 additions & 38 deletions

File tree

pyrogram/client.py

Lines changed: 56 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,7 @@ async def authorize(self) -> User:
502502
print("Password hint: {}".format(await self.get_password_hint()))
503503

504504
if not self.password:
505-
self.password = await ainput("Enter password (empty to recover): ", hide=self.hide_password, loop=self.loop)
505+
self.password = await ainput("Enter 2FA password (empty to recover): ", hide=self.hide_password, loop=self.loop)
506506

507507
try:
508508
if not self.password:
@@ -557,12 +557,30 @@ async def authorize(self) -> User:
557557

558558
return signed_up
559559

560-
async def authorize_qr(self, except_ids: List[int] = []) -> User:
560+
async def authorize_qr(self, except_ids: List[int] = []) -> "User":
561561
from qrcode import QRCode
562+
562563
qr_login = QRLogin(self, except_ids)
564+
await qr_login.recreate()
565+
566+
qr = QRCode(version=1)
567+
signed_in = None
563568

564569
while True:
565570
try:
571+
print(
572+
"\x1b[2J\n"
573+
f"Welcome to Pyrogram (version {__version__})\n"
574+
"Pyrogram is free software and comes with ABSOLUTELY NO WARRANTY. Licensed\n"
575+
f"under the terms of the {__license__}.\n"
576+
"Scan the QR code below to login\n"
577+
"Settings -> Privacy and Security -> Active Sessions -> Scan QR Code.",
578+
flush=True
579+
)
580+
581+
qr.clear()
582+
qr.add_data(qr_login.url)
583+
qr.print_ascii(tty=True)
566584
log.info("Waiting for QR code being scanned.")
567585

568586
signed_in = await qr_login.wait()
@@ -573,21 +591,42 @@ async def authorize_qr(self, except_ids: List[int] = []) -> User:
573591
except asyncio.TimeoutError:
574592
log.info("Recreating QR code.")
575593
await qr_login.recreate()
576-
print("\x1b[2J")
577-
print(f"Welcome to Pyrogram (version {__version__})")
578-
print(f"Pyrogram is free software and comes with ABSOLUTELY NO WARRANTY. Licensed\n"
579-
f"under the terms of the {__license__}.\n")
580-
print("Scan the QR code below to login")
581-
print("Settings -> Privacy and Security -> Active Sessions -> Scan QR Code.\n")
582-
583-
qrcode = QRCode(version=1)
584-
qrcode.add_data(qr_login.url)
585-
qrcode.print_ascii(invert=True)
586-
except SessionPasswordNeeded:
587-
print(f"Password hint: {await self.get_password_hint()}")
588-
return await self.check_password(
589-
await ainput("Enter 2FA password: ", hide=self.hide_password, loop=self.loop)
590-
)
594+
except SessionPasswordNeeded as e:
595+
print(e.MESSAGE)
596+
597+
while True:
598+
print("Password hint: {}".format(await self.get_password_hint()))
599+
600+
if not self.password:
601+
self.password = await ainput("Enter 2FA password (empty to recover): ", hide=self.hide_password, loop=self.loop)
602+
603+
try:
604+
if not self.password:
605+
confirm = await ainput("Confirm password recovery (y/N): ", loop=self.loop)
606+
607+
if confirm.lower() == "y":
608+
email_pattern = await self.send_recovery_code()
609+
print(f"The recovery code has been sent to {email_pattern}")
610+
611+
while True:
612+
recovery_code = await ainput("Enter recovery code: ", loop=self.loop)
613+
614+
try:
615+
return await self.recover_password(recovery_code)
616+
except BadRequest as e:
617+
print(e.MESSAGE)
618+
except Exception as e:
619+
log.exception(e)
620+
raise
621+
else:
622+
self.password = None
623+
else:
624+
return await self.check_password(self.password)
625+
except BadRequest as e:
626+
print(e.MESSAGE)
627+
self.password = None
628+
else:
629+
break
591630

592631
def set_parse_mode(self, parse_mode: Optional["enums.ParseMode"]):
593632
"""Set the parse mode to be used globally by the client.

pyrogram/qrlogin.py

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,54 @@
2424

2525
import pyrogram
2626
from pyrogram import filters, handlers, raw, types
27-
from pyrogram.methods.messages.inline_session import get_session
27+
from pyrogram.session import Session
28+
from pyrogram.session.auth import Auth
2829

2930
log = logging.getLogger(__name__)
3031

32+
33+
async def get_session(client: "pyrogram.Client", dc_id: int) -> Session:
34+
if dc_id == await client.storage.dc_id():
35+
return client.session
36+
37+
async with client.media_sessions_lock:
38+
if client.media_sessions.get(dc_id):
39+
return client.media_sessions[dc_id]
40+
41+
session = client.media_sessions[dc_id] = Session(
42+
client,
43+
dc_id,
44+
await Auth(client, dc_id, await client.storage.test_mode()).create(),
45+
await client.storage.test_mode(),
46+
is_media=False,
47+
)
48+
49+
await session.start()
50+
51+
return session
52+
53+
3154
class QRLogin:
3255
def __init__(self, client, except_ids: List[int] = []):
33-
self.client = client
34-
self.request = raw.functions.auth.ExportLoginToken(
35-
api_id=client.api_id,
36-
api_hash=client.api_hash,
37-
except_ids=except_ids
38-
)
39-
self.r = None
56+
self.client: "pyrogram.Client" = client
57+
self.except_ids: List[int] = except_ids
58+
self.r: "raw.base.auth.LoginToken" = None
4059

4160
async def recreate(self):
42-
self.r = await self.client.invoke(self.request)
61+
self.r = await self.client.invoke(
62+
raw.functions.auth.ExportLoginToken(
63+
api_id=self.client.api_id,
64+
api_hash=self.client.api_hash,
65+
except_ids=self.except_ids,
66+
)
67+
)
68+
69+
if isinstance(self.r, raw.types.auth.LoginTokenMigrateTo):
70+
await self.client.storage.dc_id(self.r.dc_id)
71+
self.client.session = await get_session(self.client, self.r.dc_id)
72+
self.r = await self.client.invoke(
73+
raw.functions.auth.ImportLoginToken(token=self.r.token)
74+
)
4375

4476
return self.r
4577

@@ -58,9 +90,7 @@ async def raw_handler(client, update, users, chats):
5890
handler = self.client.add_handler(
5991
handlers.RawUpdateHandler(
6092
raw_handler,
61-
filters=filters.create(
62-
lambda _, __, u: isinstance(u, raw.types.UpdateLoginToken)
63-
)
93+
filters=filters.create(lambda _, __, u: isinstance(u, raw.types.UpdateLoginToken)),
6494
)
6595
)
6696

@@ -74,14 +104,6 @@ async def raw_handler(client, update, users, chats):
74104

75105
await self.recreate()
76106

77-
if isinstance(self.r, raw.types.auth.LoginTokenMigrateTo):
78-
session = await get_session(self.client, self.r.dc_id)
79-
self.r = await session.invoke(
80-
raw.functions.auth.ImportLoginToken(
81-
token=self.token
82-
)
83-
)
84-
85107
if isinstance(self.r, raw.types.auth.LoginTokenSuccess):
86108
user = types.User._parse(self.client, self.r.authorization.user)
87109

@@ -90,7 +112,7 @@ async def raw_handler(client, update, users, chats):
90112

91113
return user
92114

93-
raise TypeError('Unexpected login token response: {}'.format(self.r))
115+
raise TypeError("Unexpected login token response: {}".format(self.r))
94116

95117
@property
96118
def url(self) -> str:

0 commit comments

Comments
 (0)