Merge branch 'develop' into asyncio

# Conflicts:
#	pyrogram/client/client.py
#	pyrogram/client/methods/messages/send_message.py
This commit is contained in:
Dan 2019-01-11 13:01:26 +01:00
commit 48b50780ce
4 changed files with 119 additions and 69 deletions

View file

@ -13,6 +13,7 @@ Utilities
start start
stop stop
restart
idle idle
run run
add_handler add_handler

View file

@ -113,18 +113,28 @@ class Client(Methods, BaseClient):
Only applicable for new sessions and will be ignored in case previously Only applicable for new sessions and will be ignored in case previously
created sessions are loaded. created sessions are loaded.
phone_number (``str``, *optional*): phone_number (``str`` | ``callable``, *optional*):
Pass your phone number (with your Country Code prefix included) to avoid Pass your phone number as string (with your Country Code prefix included) to avoid entering it manually.
entering it manually. Only applicable for new sessions. Or pass a callback function which accepts no arguments and must return the correct phone number as string
(e.g., "391234567890").
Only applicable for new sessions.
phone_code (``str`` | ``callable``, *optional*): phone_code (``str`` | ``callable``, *optional*):
Pass the phone code as string (for test numbers only), or pass a callback function which accepts Pass the phone code as string (for test numbers only) to avoid entering it manually. Or pass a callback
a single positional argument *(phone_number)* and must return the correct phone code (e.g., "12345"). function which accepts a single positional argument *(phone_number)* and must return the correct phone code
as string (e.g., "12345").
Only applicable for new sessions. Only applicable for new sessions.
password (``str``, *optional*): password (``str``, *optional*):
Pass your Two-Step Verification password (if you have one) to avoid entering it Pass your Two-Step Verification password as string (if you have one) to avoid entering it manually.
manually. Only applicable for new sessions. Or pass a callback function which accepts a single positional argument *(password_hint)* and must return
the correct password as string (e.g., "password").
Only applicable for new sessions.
recovery_code (``callable``, *optional*):
Pass a callback function which accepts a single positional argument *(email_pattern)* and must return the
correct password recovery code as string (e.g., "987654").
Only applicable for new sessions.
force_sms (``str``, *optional*): force_sms (``str``, *optional*):
Pass True to force Telegram sending the authorization code via SMS. Pass True to force Telegram sending the authorization code via SMS.
@ -182,6 +192,7 @@ class Client(Methods, BaseClient):
phone_number: str = None, phone_number: str = None,
phone_code: Union[str, callable] = None, phone_code: Union[str, callable] = None,
password: str = None, password: str = None,
recovery_code: callable = None,
force_sms: bool = False, force_sms: bool = False,
first_name: str = None, first_name: str = None,
last_name: str = None, last_name: str = None,
@ -207,6 +218,7 @@ class Client(Methods, BaseClient):
self.phone_number = phone_number self.phone_number = phone_number
self.phone_code = phone_code self.phone_code = phone_code
self.password = password self.password = password
self.recovery_code = recovery_code
self.force_sms = force_sms self.force_sms = force_sms
self.first_name = first_name self.first_name = first_name
self.last_name = last_name self.last_name = last_name
@ -350,6 +362,16 @@ class Client(Methods, BaseClient):
return self return self
async def restart(self):
"""Use this method to restart the Client.
Requires no parameters.
Raises:
``ConnectionError`` in case you try to restart a stopped Client.
"""
await self.stop()
await self.start()
async def idle(self, stop_signals: tuple = (SIGINT, SIGTERM, SIGABRT)): async def idle(self, stop_signals: tuple = (SIGINT, SIGTERM, SIGABRT)):
"""Blocks the program execution until one of the signals are received, """Blocks the program execution until one of the signals are received,
then gently stop the Client by closing the underlying connection. then gently stop the Client by closing the underlying connection.
@ -476,20 +498,25 @@ class Client(Methods, BaseClient):
async def authorize_user(self): async def authorize_user(self):
phone_number_invalid_raises = self.phone_number is not None phone_number_invalid_raises = self.phone_number is not None
phone_code_invalid_raises = self.phone_code is not None phone_code_invalid_raises = self.phone_code is not None
password_hash_invalid_raises = self.password is not None password_invalid_raises = self.password is not None
first_name_invalid_raises = self.first_name is not None first_name_invalid_raises = self.first_name is not None
async def default_phone_number_callback():
while True:
phone_number = await ainput("Enter phone number: ")
confirm = await ainput("Is \"{}\" correct? (y/n): ".format(phone_number))
if confirm in ("y", "1"):
return phone_number
elif confirm in ("n", "2"):
continue
while True: while True:
if self.phone_number is None: self.phone_number = (
self.phone_number = await ainput("Enter phone number: ") await default_phone_number_callback() if self.phone_number is None
else str(await self.phone_number()) if callable(self.phone_number)
while True: else str(self.phone_number)
confirm = await ainput("Is \"{}\" correct? (y/n): ".format(self.phone_number)) )
if confirm in ("y", "1"):
break
elif confirm in ("n", "2"):
self.phone_number = await ainput("Enter phone number: ")
self.phone_number = self.phone_number.strip("+") self.phone_number = self.phone_number.strip("+")
@ -505,23 +532,21 @@ class Client(Methods, BaseClient):
await self.session.stop() await self.session.stop()
self.dc_id = e.x self.dc_id = e.x
self.auth_key = await Auth(self.dc_id, self.test_mode, self.ipv6, self._proxy).create()
self.auth_key = await Auth(
self.dc_id,
self.test_mode,
self.ipv6,
self._proxy
).create()
self.session = Session( self.session = Session(
self, self,
self.dc_id, self.dc_id,
self.auth_key self.auth_key
) )
await self.session.start()
r = await self.send( await self.session.start()
functions.auth.SendCode(
self.phone_number,
self.api_id,
self.api_hash
)
)
break
except (PhoneNumberInvalid, PhoneNumberBanned) as e: except (PhoneNumberInvalid, PhoneNumberBanned) as e:
if phone_number_invalid_raises: if phone_number_invalid_raises:
raise raise
@ -536,6 +561,7 @@ class Client(Methods, BaseClient):
time.sleep(e.x) time.sleep(e.x)
except Exception as e: except Exception as e:
log.error(e, exc_info=True) log.error(e, exc_info=True)
raise
else: else:
break break
@ -555,10 +581,23 @@ class Client(Methods, BaseClient):
) )
while True: while True:
if not phone_registered:
self.first_name = (
await ainput("First name: ") if self.first_name is None
else str(await self.first_name()) if callable(self.first_name)
else str(self.first_name)
)
self.last_name = (
await ainput("Last name: ") if self.last_name is None
else str(await self.last_name()) if callable(self.last_name)
else str(self.last_name)
)
self.phone_code = ( self.phone_code = (
await ainput("Enter phone code: ") if self.phone_code is None await ainput("Enter phone code: ") if self.phone_code is None
else self.phone_code if type(self.phone_code) is str else str(await self.phone_code(self.phone_number)) if callable(self.phone_code)
else str(self.phone_code(self.phone_number)) else str(self.phone_code)
) )
try: try:
@ -576,9 +615,6 @@ class Client(Methods, BaseClient):
phone_registered = False phone_registered = False
continue continue
else: else:
self.first_name = self.first_name if self.first_name is not None else input("First name: ")
self.last_name = self.last_name if self.last_name is not None else input("Last name: ")
try: try:
r = await self.send( r = await self.send(
functions.auth.SignUp( functions.auth.SignUp(
@ -608,61 +644,62 @@ class Client(Methods, BaseClient):
except SessionPasswordNeeded as e: except SessionPasswordNeeded as e:
print(e.MESSAGE) print(e.MESSAGE)
async def default_password_callback(password_hint: str) -> str:
print("Hint: {}".format(password_hint))
return await ainput("Enter password (empty to recover): ")
async def default_recovery_callback(email_pattern: str) -> str:
print("An e-mail containing the recovery code has been sent to {}".format(email_pattern))
return await ainput("Enter password recovery code: ")
while True: while True:
try: try:
r = await self.send(functions.account.GetPassword()) r = await self.send(functions.account.GetPassword())
if self.password is None: self.password = (
print("Hint: {}".format(r.hint)) await default_password_callback(r.hint) if self.password is None
self.password = await ainput("Enter password: ") else str((await self.password(r.hint)) or "") if callable(self.password)
else str(self.password)
)
self.password = await ainput("Enter password (empty to recover): ") if self.password == "":
r = await self.send(functions.auth.RequestPasswordRecovery())
if self.password == "": self.recovery_code = (
r = await self.send(functions.auth.RequestPasswordRecovery()) await default_recovery_callback(r.email_pattern) if self.recovery_code is None
else str(await self.recovery_code(r.email_pattern)) if callable(self.recovery_code)
else str(self.recovery_code)
)
print("An e-mail containing the recovery code has been sent to {}".format( r = await self.send(
r.email_pattern functions.auth.RecoverPassword(
)) code=self.recovery_code
r = await self.send(
functions.auth.RecoverPassword(
code=await ainput("Enter password recovery code: ")
)
) )
else: )
r = await self.send( else:
functions.auth.CheckPassword( r = await self.send(
password=compute_check(r, self.password) functions.auth.CheckPassword(
) password=compute_check(r, self.password)
) )
except PasswordEmpty as e: )
if password_hash_invalid_raises: except (PasswordEmpty, PasswordRecoveryNa, PasswordHashInvalid) as e:
raise if password_invalid_raises:
else:
print(e.MESSAGE)
self.password = None
except PasswordRecoveryNa as e:
if password_hash_invalid_raises:
raise
else:
print(e.MESSAGE)
self.password = None
except PasswordHashInvalid as e:
if password_hash_invalid_raises:
raise raise
else: else:
print(e.MESSAGE) print(e.MESSAGE)
self.password = None self.password = None
self.recovery_code = None
except FloodWait as e: except FloodWait as e:
if password_hash_invalid_raises: if password_invalid_raises:
raise raise
else: else:
print(e.MESSAGE.format(x=e.x)) print(e.MESSAGE.format(x=e.x))
time.sleep(e.x) time.sleep(e.x)
self.password = None self.password = None
self.recovery_code = None
except Exception as e: except Exception as e:
log.error(e, exc_info=True) log.error(e, exc_info=True)
raise
else: else:
break break
break break
@ -674,6 +711,7 @@ class Client(Methods, BaseClient):
time.sleep(e.x) time.sleep(e.x)
except Exception as e: except Exception as e:
log.error(e, exc_info=True) log.error(e, exc_info=True)
raise
else: else:
break break

View file

@ -109,6 +109,9 @@ class Filters:
video = create("Video", lambda _, m: bool(m.video)) video = create("Video", lambda _, m: bool(m.video))
"""Filter messages that contain :obj:`Video <pyrogram.Video>` objects.""" """Filter messages that contain :obj:`Video <pyrogram.Video>` objects."""
media_group = create("MediaGroup", lambda _, m: bool(m.media_group_id))
"""Filter messages containing photos or videos being part of an album."""
voice = create("Voice", lambda _, m: bool(m.voice)) voice = create("Voice", lambda _, m: bool(m.voice))
"""Filter messages that contain :obj:`Voice <pyrogram.Voice>` note objects.""" """Filter messages that contain :obj:`Voice <pyrogram.Voice>` note objects."""
@ -287,7 +290,7 @@ class Filters:
""" """
def f(_, m): def f(_, m):
m.matches = [i for i in _.p.finditer(m.text or "")] m.matches = [i for i in _.p.finditer(m.text or m.caption or "")]
return bool(m.matches) return bool(m.matches)
return create("Regex", f, p=re.compile(pattern, flags)) return create("Regex", f, p=re.compile(pattern, flags))

View file

@ -88,10 +88,18 @@ class SendMessage(BaseClient):
) )
if isinstance(r, types.UpdateShortSentMessage): if isinstance(r, types.UpdateShortSentMessage):
peer = await self.resolve_peer(chat_id)
peer_id = (
peer.user_id
if isinstance(peer, types.InputPeerUser)
else -peer.chat_id
)
return pyrogram.Message( return pyrogram.Message(
message_id=r.id, message_id=r.id,
chat=pyrogram.Chat( chat=pyrogram.Chat(
id=list((await self.resolve_peer(chat_id)).__dict__.values())[0], id=peer_id,
type="private", type="private",
client=self client=self
), ),