From 7bfa87c11bf44f64989fc1b7032886063b966f2a Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Thu, 23 Aug 2018 20:42:42 +0200 Subject: [PATCH 01/39] Update API scheme to Layer 85 --- compiler/api/source/main_api.tl | 57 +++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/compiler/api/source/main_api.tl b/compiler/api/source/main_api.tl index 9ee01cb3..d3f227ce 100644 --- a/compiler/api/source/main_api.tl +++ b/compiler/api/source/main_api.tl @@ -191,6 +191,7 @@ inputReportReasonSpam#58dbcab8 = ReportReason; inputReportReasonViolence#1e22c78d = ReportReason; inputReportReasonPornography#2e59d922 = ReportReason; inputReportReasonOther#e1746d0a text:string = ReportReason; +inputReportReasonCopyright#9b89f93a = ReportReason; userFull#f220f3f flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true user:User about:flags.1?string link:contacts.Link profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo common_chats_count:int = UserFull; @@ -342,7 +343,7 @@ config#3213dbba flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:fla nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc; -help.appUpdate#8987f311 id:int critical:Bool url:string text:string = help.AppUpdate; +help.appUpdate#1da7158f flags:# popup:flags.0?true id:int version:string text:string entities:Vector document:flags.1?Document url:flags.2?string = help.AppUpdate; help.noAppUpdate#c45a6536 = help.AppUpdate; help.inviteText#18cb9f78 message:string = help.InviteText; @@ -458,12 +459,11 @@ authorization#7bf2e6f6 hash:long flags:int device_model:string platform:string s account.authorizations#1250abde authorizations:Vector = account.Authorizations; -account.noPassword#5ea182f6 new_salt:bytes new_secure_salt:bytes secure_random:bytes email_unconfirmed_pattern:string = account.Password; -account.password#ca39b447 flags:# has_recovery:flags.0?true has_secure_values:flags.1?true current_salt:bytes new_salt:bytes new_secure_salt:bytes secure_random:bytes hint:string email_unconfirmed_pattern:string = account.Password; +account.password#ad2641f8 flags:# has_recovery:flags.0?true has_secure_values:flags.1?true has_password:flags.2?true current_algo:flags.2?PasswordKdfAlgo srp_B:flags.2?bytes srp_id:flags.2?long hint:flags.3?string email_unconfirmed_pattern:flags.4?string new_algo:PasswordKdfAlgo new_secure_algo:SecurePasswordKdfAlgo secure_random:bytes = account.Password; -account.passwordSettings#7bd9c3f1 email:string secure_salt:bytes secure_secret:bytes secure_secret_id:long = account.PasswordSettings; +account.passwordSettings#9a5c33e5 flags:# email:flags.0?string secure_settings:flags.1?SecureSecretSettings = account.PasswordSettings; -account.passwordInputSettings#21ffa60d flags:# new_salt:flags.0?bytes new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string new_secure_salt:flags.2?bytes new_secure_secret:flags.2?bytes new_secure_secret_id:flags.2?long = account.PasswordInputSettings; +account.passwordInputSettings#c23727c9 flags:# new_algo:flags.0?PasswordKdfAlgo new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string new_secure_settings:flags.2?SecureSecretSettings = account.PasswordInputSettings; auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery; @@ -864,9 +864,9 @@ secureValueTypeTemporaryRegistration#ea02ec33 = SecureValueType; secureValueTypePhone#b320aadb = SecureValueType; secureValueTypeEmail#8e3ca7ee = SecureValueType; -secureValue#b4b4b699 flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?SecureFile reverse_side:flags.2?SecureFile selfie:flags.3?SecureFile files:flags.4?Vector plain_data:flags.5?SecurePlainData hash:bytes = SecureValue; +secureValue#187fa0ca flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?SecureFile reverse_side:flags.2?SecureFile selfie:flags.3?SecureFile translation:flags.6?Vector files:flags.4?Vector plain_data:flags.5?SecurePlainData hash:bytes = SecureValue; -inputSecureValue#67872e8 flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?InputSecureFile reverse_side:flags.2?InputSecureFile selfie:flags.3?InputSecureFile files:flags.4?Vector plain_data:flags.5?SecurePlainData = InputSecureValue; +inputSecureValue#db21d0a7 flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?InputSecureFile reverse_side:flags.2?InputSecureFile selfie:flags.3?InputSecureFile translation:flags.6?Vector files:flags.4?Vector plain_data:flags.5?SecurePlainData = InputSecureValue; secureValueHash#ed1ecdb0 type:SecureValueType hash:bytes = SecureValueHash; @@ -876,10 +876,13 @@ secureValueErrorReverseSide#868a2aa5 type:SecureValueType file_hash:bytes text:s secureValueErrorSelfie#e537ced6 type:SecureValueType file_hash:bytes text:string = SecureValueError; secureValueErrorFile#7a700873 type:SecureValueType file_hash:bytes text:string = SecureValueError; secureValueErrorFiles#666220e9 type:SecureValueType file_hash:Vector text:string = SecureValueError; +secureValueError#869d758f type:SecureValueType hash:bytes text:string = SecureValueError; +secureValueErrorTranslationFile#a1144770 type:SecureValueType file_hash:bytes text:string = SecureValueError; +secureValueErrorTranslationFiles#34636dd8 type:SecureValueType file_hash:Vector text:string = SecureValueError; secureCredentialsEncrypted#33f0ea47 data:bytes hash:bytes secret:bytes = SecureCredentialsEncrypted; -account.authorizationForm#cb976d53 flags:# selfie_required:flags.1?true required_types:Vector values:Vector errors:Vector users:Vector privacy_policy_url:flags.0?string = account.AuthorizationForm; +account.authorizationForm#ad2e1cd8 flags:# required_types:Vector values:Vector errors:Vector users:Vector privacy_policy_url:flags.0?string = account.AuthorizationForm; account.sentEmailCode#811f854f email_pattern:string length:int = account.SentEmailCode; @@ -890,6 +893,24 @@ savedPhoneContact#1142bd56 phone:string first_name:string last_name:string date: account.takeout#4dba4501 id:long = account.Takeout; +passwordKdfAlgoUnknown#d45ab096 = PasswordKdfAlgo; +passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow#3a912d4a salt1:bytes salt2:bytes g:int p:bytes = PasswordKdfAlgo; + +securePasswordKdfAlgoUnknown#4a8537 = SecurePasswordKdfAlgo; +securePasswordKdfAlgoPBKDF2HMACSHA512iter100000#bbf2dda0 salt:bytes = SecurePasswordKdfAlgo; +securePasswordKdfAlgoSHA512#86471d92 salt:bytes = SecurePasswordKdfAlgo; + +secureSecretSettings#1527bcac secure_algo:SecurePasswordKdfAlgo secure_secret:bytes secure_secret_id:long = SecureSecretSettings; + +inputCheckPasswordEmpty#9880f658 = InputCheckPasswordSRP; +inputCheckPasswordSRP#d27ff082 srp_id:long A:bytes M1:bytes = InputCheckPasswordSRP; + +secureRequiredType#829d99da flags:# native_names:flags.0?true selfie_required:flags.1?true translation_required:flags.2?true type:SecureValueType = SecureRequiredType; +secureRequiredTypeOneOf#27477b4 types:Vector = SecureRequiredType; + +help.passportConfigNotModified#bfb9f457 = help.PassportConfig; +help.passportConfig#a098d6af hash:int countries_langs:DataJSON = help.PassportConfig; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -909,7 +930,7 @@ auth.exportAuthorization#e5bfffcd dc_id:int = auth.ExportedAuthorization; auth.importAuthorization#e3ef9613 id:int bytes:bytes = auth.Authorization; auth.bindTempAuthKey#cdd42a05 perm_auth_key_id:long nonce:long expires_at:int encrypted_message:bytes = Bool; auth.importBotAuthorization#67a3ff2c flags:int api_id:int api_hash:string bot_auth_token:string = auth.Authorization; -auth.checkPassword#a63011e password_hash:bytes = auth.Authorization; +auth.checkPassword#d18b4d16 password:InputCheckPasswordSRP = auth.Authorization; auth.requestPasswordRecovery#d897bc66 = auth.PasswordRecovery; auth.recoverPassword#4ea56e92 code:string = auth.Authorization; auth.resendCode#3ef1a9bf phone_number:string phone_code_hash:string = auth.SentCode; @@ -938,11 +959,11 @@ account.updateDeviceLocked#38df3532 period:int = Bool; account.getAuthorizations#e320c158 = account.Authorizations; account.resetAuthorization#df77f3bc hash:long = Bool; account.getPassword#548a30f5 = account.Password; -account.getPasswordSettings#bc8d11bb current_password_hash:bytes = account.PasswordSettings; -account.updatePasswordSettings#fa7c4b86 current_password_hash:bytes new_settings:account.PasswordInputSettings = Bool; +account.getPasswordSettings#9cd4eaf9 password:InputCheckPasswordSRP = account.PasswordSettings; +account.updatePasswordSettings#a59b102f password:InputCheckPasswordSRP new_settings:account.PasswordInputSettings = Bool; account.sendConfirmPhoneCode#1516d7bd flags:# allow_flashcall:flags.0?true hash:string current_number:flags.0?Bool = auth.SentCode; account.confirmPhone#5f2178c3 phone_code_hash:string phone_code:string = Bool; -account.getTmpPassword#4a82327e password_hash:bytes period:int = account.TmpPassword; +account.getTmpPassword#449e0b51 password:InputCheckPasswordSRP period:int = account.TmpPassword; account.getWebAuthorizations#182e6d6f = account.WebAuthorizations; account.resetWebAuthorization#2d01b9ef hash:long = Bool; account.resetWebAuthorizations#682d2594 = Bool; @@ -1080,6 +1101,7 @@ messages.searchStickerSets#c2b7d08b flags:# exclude_featured:flags.0?true q:stri messages.getSplitRanges#1cff7e08 = Vector; messages.markDialogUnread#c286d98f flags:# unread:flags.0?true peer:InputDialogPeer = Bool; messages.getDialogUnreadMarks#22e24e22 = Vector; +messages.clearAllDrafts#7e58ee9c = Bool; updates.getState#edd4882a = updates.State; updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; @@ -1101,7 +1123,7 @@ upload.getFileHashes#c7025931 location:InputFileLocation offset:int = Vector = Bool; help.getInviteText#4d392343 = help.InviteText; help.getSupport#9cdf08cd = help.Support; @@ -1113,6 +1135,7 @@ help.getProxyData#3d7758e1 = help.ProxyData; help.getTermsOfServiceUpdate#2ca51fd1 = help.TermsOfServiceUpdate; help.acceptTermsOfService#ee72f79a id:DataJSON = Bool; help.getDeepLinkInfo#3fedc75f path:string = help.DeepLinkInfo; +help.getPassportConfig#c661ad08 hash:int = help.PassportConfig; channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool; channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector = messages.AffectedMessages; @@ -1172,9 +1195,9 @@ phone.discardCall#78d413a6 peer:InputPhoneCall duration:int reason:PhoneCallDisc phone.setCallRating#1c536a34 peer:InputPhoneCall rating:int comment:string = Updates; phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool; -langpack.getLangPack#9ab5c58e lang_code:string = LangPackDifference; -langpack.getStrings#2e1ee318 lang_code:string keys:Vector = Vector; +langpack.getLangPack#f2f2330a lang_pack:string lang_code:string = LangPackDifference; +langpack.getStrings#efea3803 lang_pack:string lang_code:string keys:Vector = Vector; langpack.getDifference#b2e4d7d from_version:int = LangPackDifference; -langpack.getLanguages#800fd57d = Vector; +langpack.getLanguages#42c6978f lang_pack:string = Vector; -// LAYER 82 +// LAYER 85 From 0e050b45e788623394a51c3a58c1213437a0b7a0 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sat, 15 Dec 2018 08:06:18 +0100 Subject: [PATCH 02/39] Attempt srp --- .../methods/password/enable_cloud_password.py | 113 +++++++++++++++--- 1 file changed, 97 insertions(+), 16 deletions(-) diff --git a/pyrogram/client/methods/password/enable_cloud_password.py b/pyrogram/client/methods/password/enable_cloud_password.py index 80d527c4..1969453a 100644 --- a/pyrogram/client/methods/password/enable_cloud_password.py +++ b/pyrogram/client/methods/password/enable_cloud_password.py @@ -16,13 +16,29 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . +from hashlib import sha256, pbkdf2_hmac import os -from hashlib import sha256 from pyrogram.api import functions, types from ...ext import BaseClient +def compute_hash(algo: types.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow, password: str): + hash1 = sha256(algo.salt1 + password.encode() + algo.salt1).digest() + hash2 = sha256(algo.salt2 + hash1 + algo.salt2).digest() + hash3 = pbkdf2_hmac("sha512", hash2, algo.salt1, 100000) + + return sha256(algo.salt2 + hash3 + algo.salt2).digest() + + +def btoi(b: bytes): + return int.from_bytes(b, "big") + + +def itob(i: int): + return i.to_bytes(256, "big") + + class EnableCloudPassword(BaseClient): def enable_cloud_password(self, password: str, hint: str = "", email: str = ""): """Use this method to enable the Two-Step Verification security feature (Cloud Password) on your account. @@ -46,21 +62,86 @@ class EnableCloudPassword(BaseClient): :class:`Error ` in case of a Telegram RPC error. """ r = self.send(functions.account.GetPassword()) + print(r) - if isinstance(r, types.account.NoPassword): - salt = r.new_salt + os.urandom(8) - password_hash = sha256(salt + password.encode() + salt).digest() + algo = r.new_algo - return self.send( - functions.account.UpdatePasswordSettings( - current_password_hash=salt, - new_settings=types.account.PasswordInputSettings( - new_salt=salt, - new_password_hash=password_hash, - hint=hint, - email=email - ) - ) + p_bytes = algo.p + p = btoi(algo.p) + + g_bytes = itob(algo.g) + g = algo.g + + B_bytes = r.srp_B or b"" + B = btoi(B_bytes) + + srp_id = r.srp_id or 0 + + x_bytes = compute_hash(algo, password) + x = btoi(x_bytes) + + g_x = pow(g, x, p) + + k_bytes = sha256(p_bytes + g_bytes).digest() + k = btoi(k_bytes) + + kg_x = (k * g_x) % p + + while True: + a_bytes = os.urandom(256) + a = btoi(a_bytes) + + A = pow(g, a, p) + A_bytes = itob(A) + + u = btoi(sha256(A_bytes + B_bytes).digest()) + + if u > 0: + break + + g_b = (B - kg_x) % p + + ux = u * x + a_ux = a + ux + S = pow(g_b, a_ux, p) + S_bytes = itob(S) + + K_bytes = sha256(S_bytes).digest() + M1_bytes = sha256( + b"".join([bytes([int(i) ^ int(j)]) for (i, j) in zip(sha256(p_bytes).digest(), sha256(g_bytes).digest())]) + + sha256(algo.salt1).digest() + + sha256(algo.salt2).digest() + + A_bytes + + B_bytes + + K_bytes + ).digest() + + input_check_password = types.InputCheckPasswordSRP(srp_id, A_bytes, M1_bytes) + + r2 = self.send(functions.account.UpdatePasswordSettings( + input_check_password, types.account.PasswordInputSettings( + new_algo=algo, + new_password_hash=b"", + hint="" ) - else: - return False + )) + + print(r2) + + # if isinstance(r, types.account.NoPassword): + # salt = r.new_salt + os.urandom(8) + # password_hash = sha256(salt + password.encode() + salt).digest() + # + # return self.send( + # functions.account.UpdatePasswordSettings( + # current_password_hash=salt, + # new_settings=types.account.PasswordInputSettings( + # new_salt=salt, + # new_password_hash=password_hash, + # hint=hint, + # email=email + # ) + # ) + # ) + # else: + # return False From 89983b75ca534f1e033a3071f044a7c8a8f06d6a Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sat, 15 Dec 2018 08:59:26 +0100 Subject: [PATCH 03/39] Move relevant SRP-related code into another file These functions are going to be used by all *_cloud_password methods --- pyrogram/client/methods/password/utils.py | 103 ++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 pyrogram/client/methods/password/utils.py diff --git a/pyrogram/client/methods/password/utils.py b/pyrogram/client/methods/password/utils.py new file mode 100644 index 00000000..4bf0ddec --- /dev/null +++ b/pyrogram/client/methods/password/utils.py @@ -0,0 +1,103 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +import hashlib +import os + +from pyrogram.api import types + + +def btoi(b: bytes) -> int: + return int.from_bytes(b, "big") + + +def itob(i: int) -> bytes: + return i.to_bytes(256, "big") + + +def sha256(data: bytes) -> bytes: + return hashlib.sha256(data).digest() + + +def xor(a: bytes, b: bytes) -> bytes: + return bytes(i ^ j for i, j in zip(a, b)) + + +def compute_hash(algo: types.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow, password: str) -> bytes: + hash1 = sha256(algo.salt1 + password.encode() + algo.salt1) + hash2 = sha256(algo.salt2 + hash1 + algo.salt2) + hash3 = hashlib.pbkdf2_hmac("sha512", hash2, algo.salt1, 100000) + + return sha256(algo.salt2 + hash3 + algo.salt2) + + +def compute_check(r: types.account.Password, password: str) -> types.InputCheckPasswordSRP: + algo = r.current_algo + + p_bytes = algo.p + p = btoi(algo.p) + + g_bytes = itob(algo.g) + g = algo.g + + B_bytes = r.srp_B + B = btoi(B_bytes) + + srp_id = r.srp_id + + x_bytes = compute_hash(algo, password) + x = btoi(x_bytes) + + g_x = pow(g, x, p) + + k_bytes = sha256(p_bytes + g_bytes) + k = btoi(k_bytes) + + kg_x = (k * g_x) % p + + while True: + a_bytes = os.urandom(256) + a = btoi(a_bytes) + + A = pow(g, a, p) + A_bytes = itob(A) + + u = btoi(sha256(A_bytes + B_bytes)) + + if u > 0: + break + + g_b = (B - kg_x) % p + + ux = u * x + a_ux = a + ux + S = pow(g_b, a_ux, p) + S_bytes = itob(S) + + K_bytes = sha256(S_bytes) + + M1_bytes = sha256( + xor(sha256(p_bytes), sha256(g_bytes)) + + sha256(algo.salt1) + + sha256(algo.salt2) + + A_bytes + + B_bytes + + K_bytes + ) + + return types.InputCheckPasswordSRP(srp_id, A_bytes, M1_bytes) From e3459017efe74c13e9da5eef1eac03a718154c56 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sat, 15 Dec 2018 09:05:50 +0100 Subject: [PATCH 04/39] Re-implement enable_cloud_password using SRP --- .../methods/password/enable_cloud_password.py | 119 ++++-------------- 1 file changed, 21 insertions(+), 98 deletions(-) diff --git a/pyrogram/client/methods/password/enable_cloud_password.py b/pyrogram/client/methods/password/enable_cloud_password.py index 1969453a..e4341480 100644 --- a/pyrogram/client/methods/password/enable_cloud_password.py +++ b/pyrogram/client/methods/password/enable_cloud_password.py @@ -16,34 +16,18 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . -from hashlib import sha256, pbkdf2_hmac import os from pyrogram.api import functions, types +from .utils import compute_hash, btoi, itob from ...ext import BaseClient -def compute_hash(algo: types.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow, password: str): - hash1 = sha256(algo.salt1 + password.encode() + algo.salt1).digest() - hash2 = sha256(algo.salt2 + hash1 + algo.salt2).digest() - hash3 = pbkdf2_hmac("sha512", hash2, algo.salt1, 100000) - - return sha256(algo.salt2 + hash3 + algo.salt2).digest() - - -def btoi(b: bytes): - return int.from_bytes(b, "big") - - -def itob(i: int): - return i.to_bytes(256, "big") - - class EnableCloudPassword(BaseClient): - def enable_cloud_password(self, password: str, hint: str = "", email: str = ""): + def enable_cloud_password(self, password: str, hint: str = "", email: str = None): """Use this method to enable the Two-Step Verification security feature (Cloud Password) on your account. - This password will be asked when you log in on a new device in addition to the SMS code. + This password will be asked when you log-in on a new device in addition to the SMS code. Args: password (``str``): @@ -56,92 +40,31 @@ class EnableCloudPassword(BaseClient): Recovery e-mail. Returns: - True on success, False otherwise. + True on success. Raises: :class:`Error ` in case of a Telegram RPC error. + ``ValueError`` in case there is already a cloud password enabled. """ r = self.send(functions.account.GetPassword()) - print(r) - algo = r.new_algo + if r.has_password: + raise ValueError("There is already a cloud password enabled") - p_bytes = algo.p - p = btoi(algo.p) + r.new_algo.salt1 += os.urandom(32) + new_hash = btoi(compute_hash(r.new_algo, password)) + new_hash = itob(pow(r.new_algo.g, new_hash, btoi(r.new_algo.p))) - g_bytes = itob(algo.g) - g = algo.g - - B_bytes = r.srp_B or b"" - B = btoi(B_bytes) - - srp_id = r.srp_id or 0 - - x_bytes = compute_hash(algo, password) - x = btoi(x_bytes) - - g_x = pow(g, x, p) - - k_bytes = sha256(p_bytes + g_bytes).digest() - k = btoi(k_bytes) - - kg_x = (k * g_x) % p - - while True: - a_bytes = os.urandom(256) - a = btoi(a_bytes) - - A = pow(g, a, p) - A_bytes = itob(A) - - u = btoi(sha256(A_bytes + B_bytes).digest()) - - if u > 0: - break - - g_b = (B - kg_x) % p - - ux = u * x - a_ux = a + ux - S = pow(g_b, a_ux, p) - S_bytes = itob(S) - - K_bytes = sha256(S_bytes).digest() - M1_bytes = sha256( - b"".join([bytes([int(i) ^ int(j)]) for (i, j) in zip(sha256(p_bytes).digest(), sha256(g_bytes).digest())]) - + sha256(algo.salt1).digest() - + sha256(algo.salt2).digest() - + A_bytes - + B_bytes - + K_bytes - ).digest() - - input_check_password = types.InputCheckPasswordSRP(srp_id, A_bytes, M1_bytes) - - r2 = self.send(functions.account.UpdatePasswordSettings( - input_check_password, types.account.PasswordInputSettings( - new_algo=algo, - new_password_hash=b"", - hint="" + self.send( + functions.account.UpdatePasswordSettings( + password=types.InputCheckPasswordEmpty(), + new_settings=types.account.PasswordInputSettings( + new_algo=r.new_algo, + new_password_hash=new_hash, + hint=hint, + email=email + ) ) - )) + ) - print(r2) - - # if isinstance(r, types.account.NoPassword): - # salt = r.new_salt + os.urandom(8) - # password_hash = sha256(salt + password.encode() + salt).digest() - # - # return self.send( - # functions.account.UpdatePasswordSettings( - # current_password_hash=salt, - # new_settings=types.account.PasswordInputSettings( - # new_salt=salt, - # new_password_hash=password_hash, - # hint=hint, - # email=email - # ) - # ) - # ) - # else: - # return False + return True From 40ecc082a65482072b9234c21e9beb9466413e03 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sat, 15 Dec 2018 12:22:33 +0100 Subject: [PATCH 05/39] Re-implement change_cloud_password using SRP --- .../methods/password/change_cloud_password.py | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/pyrogram/client/methods/password/change_cloud_password.py b/pyrogram/client/methods/password/change_cloud_password.py index 4b5e86b3..2afc2fd7 100644 --- a/pyrogram/client/methods/password/change_cloud_password.py +++ b/pyrogram/client/methods/password/change_cloud_password.py @@ -17,9 +17,9 @@ # along with Pyrogram. If not, see . import os -from hashlib import sha256 from pyrogram.api import functions, types +from .utils import compute_hash, compute_check, btoi, itob from ...ext import BaseClient @@ -38,28 +38,30 @@ class ChangeCloudPassword(BaseClient): A new password hint. Returns: - True on success, False otherwise. + True on success. Raises: :class:`Error ` in case of a Telegram RPC error. + ``ValueError`` in case there is no cloud password to change. """ r = self.send(functions.account.GetPassword()) - if isinstance(r, types.account.Password): - current_password_hash = sha256(r.current_salt + current_password.encode() + r.current_salt).digest() + if not r.has_password: + raise ValueError("There is no cloud password to change") - new_salt = r.new_salt + os.urandom(8) - new_password_hash = sha256(new_salt + new_password.encode() + new_salt).digest() + r.new_algo.salt1 += os.urandom(32) + new_hash = btoi(compute_hash(r.new_algo, new_password)) + new_hash = itob(pow(r.new_algo.g, new_hash, btoi(r.new_algo.p))) - return self.send( - functions.account.UpdatePasswordSettings( - current_password_hash=current_password_hash, - new_settings=types.account.PasswordInputSettings( - new_salt=new_salt, - new_password_hash=new_password_hash, - hint=new_hint - ) + self.send( + functions.account.UpdatePasswordSettings( + password=compute_check(r, current_password), + new_settings=types.account.PasswordInputSettings( + new_algo=r.new_algo, + new_password_hash=new_hash, + hint=new_hint ) ) - else: - return False + ) + + return True From efc6023b08f1863a123c6c10b38384d498fa5a69 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sat, 15 Dec 2018 20:08:31 +0100 Subject: [PATCH 06/39] Re-implement remove_cloud_password using SRP --- .../methods/password/remove_cloud_password.py | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/pyrogram/client/methods/password/remove_cloud_password.py b/pyrogram/client/methods/password/remove_cloud_password.py index 5a9875ff..a9320c39 100644 --- a/pyrogram/client/methods/password/remove_cloud_password.py +++ b/pyrogram/client/methods/password/remove_cloud_password.py @@ -16,9 +16,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . -from hashlib import sha256 - from pyrogram.api import functions, types +from .utils import compute_check from ...ext import BaseClient @@ -31,25 +30,26 @@ class RemoveCloudPassword(BaseClient): Your current password. Returns: - True on success, False otherwise. + True on success. Raises: :class:`Error ` in case of a Telegram RPC error. + ``ValueError`` in case there is no cloud password to remove. """ r = self.send(functions.account.GetPassword()) - if isinstance(r, types.account.Password): - password_hash = sha256(r.current_salt + password.encode() + r.current_salt).digest() + if not r.has_password: + raise ValueError("There is no cloud password to remove") - return self.send( - functions.account.UpdatePasswordSettings( - current_password_hash=password_hash, - new_settings=types.account.PasswordInputSettings( - new_salt=b"", - new_password_hash=b"", - hint="" - ) + self.send( + functions.account.UpdatePasswordSettings( + password=compute_check(r, password), + new_settings=types.account.PasswordInputSettings( + new_algo=types.PasswordKdfAlgoUnknown(), + new_password_hash=b"", + hint="" ) ) - else: - return False + ) + + return True From 77f15af8808f31b734b98897e488dc12296764a9 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sat, 22 Dec 2018 23:11:16 +0100 Subject: [PATCH 07/39] Update API schema to Layer 91 --- compiler/api/source/main_api.tl | 213 +++++++++++++++++++++++--------- 1 file changed, 156 insertions(+), 57 deletions(-) diff --git a/compiler/api/source/main_api.tl b/compiler/api/source/main_api.tl index 9ee01cb3..61bd1a41 100644 --- a/compiler/api/source/main_api.tl +++ b/compiler/api/source/main_api.tl @@ -45,7 +45,8 @@ inputMediaPhotoExternal#e5bbfe1a flags:# url:string ttl_seconds:flags.0?int = In inputMediaDocumentExternal#fb52dc99 flags:# url:string ttl_seconds:flags.0?int = InputMedia; inputMediaGame#d33f43f3 id:InputGame = InputMedia; inputMediaInvoice#f4e096c3 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:string = InputMedia; -inputMediaGeoLive#7b1a118f geo_point:InputGeoPoint period:int = InputMedia; +inputMediaGeoLive#ce4e82fd flags:# stopped:flags.0?true geo_point:InputGeoPoint period:flags.1?int = InputMedia; +inputMediaPoll#6b3765b poll:Poll = InputMedia; inputChatPhotoEmpty#1ca48f57 = InputChatPhoto; inputChatUploadedPhoto#927c55b4 file:InputFile = InputChatPhoto; @@ -55,16 +56,14 @@ inputGeoPointEmpty#e4c123d6 = InputGeoPoint; inputGeoPoint#f3b7acc9 lat:double long:double = InputGeoPoint; inputPhotoEmpty#1cd7bf0d = InputPhoto; -inputPhoto#fb95c6c4 id:long access_hash:long = InputPhoto; +inputPhoto#3bb3b94a id:long access_hash:long file_reference:bytes = InputPhoto; -inputFileLocation#14637196 volume_id:long local_id:int secret:long = InputFileLocation; +inputFileLocation#dfdaabe1 volume_id:long local_id:int secret:long file_reference:bytes = InputFileLocation; inputEncryptedFileLocation#f5235d55 id:long access_hash:long = InputFileLocation; -inputDocumentFileLocation#430f0724 id:long access_hash:long version:int = InputFileLocation; +inputDocumentFileLocation#196683d9 id:long access_hash:long file_reference:bytes = InputFileLocation; inputSecureFileLocation#cbc7ee28 id:long access_hash:long = InputFileLocation; inputTakeoutFileLocation#29be5899 = InputFileLocation; -inputAppEvent#770656a8 time:double type:string peer:long data:string = InputAppEvent; - peerUser#9db1bc6d user_id:int = Peer; peerChat#bad0e5bb chat_id:int = Peer; peerChannel#bddde532 channel_id:int = Peer; @@ -81,7 +80,7 @@ storage.fileMp4#b3cea0e4 = storage.FileType; storage.fileWebp#1081464c = storage.FileType; fileLocationUnavailable#7c596b46 volume_id:long local_id:int secret:long = FileLocation; -fileLocation#53d69076 dc_id:int volume_id:long local_id:int secret:long = FileLocation; +fileLocation#91d11eb dc_id:int volume_id:long local_id:int secret:long file_reference:bytes = FileLocation; userEmpty#200250ba id:int = User; user#2e13f4c3 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?string bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User; @@ -102,8 +101,8 @@ chatForbidden#7328bdb id:int title:string = Chat; channel#c88974ac flags:# creator:flags.0?true left:flags.2?true editor:flags.3?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true democracy:flags.10?true signatures:flags.11?true min:flags.12?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string admin_rights:flags.14?ChannelAdminRights banned_rights:flags.15?ChannelBannedRights participants_count:flags.17?int = Chat; channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat; -chatFull#2e02a614 id:int participants:ChatParticipants chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector = ChatFull; -channelFull#76af5481 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int = ChatFull; +chatFull#edd2a791 flags:# id:int participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:flags.3?Vector pinned_msg_id:flags.6?int = ChatFull; +channelFull#1c87a71a flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_view_stats:flags.12?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int = ChatFull; chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant; chatParticipantCreator#da13538a user_id:int = ChatParticipant; @@ -116,7 +115,7 @@ chatPhotoEmpty#37c1011c = ChatPhoto; chatPhoto#6153276a photo_small:FileLocation photo_big:FileLocation = ChatPhoto; messageEmpty#83e5de54 id:int = Message; -message#44f9b43d flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long = Message; +message#44f9b43d flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long = Message; messageService#9e19a1f6 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer reply_to_msg_id:flags.3?int date:int action:MessageAction = Message; messageMediaEmpty#3ded6320 = MessageMedia; @@ -130,6 +129,7 @@ messageMediaVenue#2ec0533f geo:GeoPoint title:string address:string provider:str messageMediaGame#fdb19008 game:Game = MessageMedia; messageMediaInvoice#84551347 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument receipt_msg_id:flags.2?int currency:string total_amount:long start_param:string = MessageMedia; messageMediaGeoLive#7c3c2609 geo:GeoPoint period:int = MessageMedia; +messageMediaPoll#4bd6e798 poll:Poll results:PollResults = MessageMedia; messageActionEmpty#b6aef7b0 = MessageAction; messageActionChatCreate#a6638b9a title:string users:Vector = MessageAction; @@ -153,11 +153,12 @@ messageActionCustomAction#fae69f56 message:string = MessageAction; messageActionBotAllowed#abe9affe domain:string = MessageAction; messageActionSecureValuesSentMe#1b287353 values:Vector credentials:SecureCredentialsEncrypted = MessageAction; messageActionSecureValuesSent#d95c6154 types:Vector = MessageAction; +messageActionContactSignUp#f3f25f76 = MessageAction; dialog#e4def5db flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage = Dialog; photoEmpty#2331b22d id:long = Photo; -photo#9288dd29 flags:# has_stickers:flags.0?true id:long access_hash:long date:int sizes:Vector = Photo; +photo#9c477dd8 flags:# has_stickers:flags.0?true id:long access_hash:long file_reference:bytes date:int sizes:Vector = Photo; photoSizeEmpty#e17e23c type:string = PhotoSize; photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize; @@ -177,6 +178,7 @@ auth.exportedAuthorization#df969c2d id:int bytes:bytes = auth.ExportedAuthorizat inputNotifyPeer#b8bc5b0c peer:InputPeer = InputNotifyPeer; inputNotifyUsers#193b4417 = InputNotifyPeer; inputNotifyChats#4a95e84e = InputNotifyPeer; +inputNotifyBroadcasts#b1db7c7e = InputNotifyPeer; inputPeerNotifySettings#9c3d198e flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int sound:flags.3?string = InputPeerNotifySettings; @@ -190,9 +192,11 @@ wallPaperSolid#63117f24 id:int title:string bg_color:int color:int = WallPaper; inputReportReasonSpam#58dbcab8 = ReportReason; inputReportReasonViolence#1e22c78d = ReportReason; inputReportReasonPornography#2e59d922 = ReportReason; +inputReportReasonChildAbuse#adf44ee3 = ReportReason; inputReportReasonOther#e1746d0a text:string = ReportReason; +inputReportReasonCopyright#9b89f93a = ReportReason; -userFull#f220f3f flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true user:User about:flags.1?string link:contacts.Link profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo common_chats_count:int = UserFull; +userFull#8ea4a881 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true user:User about:flags.1?string link:contacts.Link profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int = UserFull; contact#f911c994 user_id:int mutual:Bool = Contact; @@ -218,7 +222,7 @@ messages.dialogsNotModified#f0e3e596 count:int = messages.Dialogs; messages.messages#8c718e87 messages:Vector chats:Vector users:Vector = messages.Messages; messages.messagesSlice#b446ae3 count:int messages:Vector chats:Vector users:Vector = messages.Messages; -messages.channelMessages#99262e37 flags:# pts:int count:int messages:Vector chats:Vector users:Vector = messages.Messages; +messages.channelMessages#99262e37 flags:# inexact:flags.1?true pts:int count:int messages:Vector chats:Vector users:Vector = messages.Messages; messages.messagesNotModified#74535f21 count:int = messages.Messages; messages.chats#64ff9fd5 chats:Vector = messages.Chats; @@ -254,7 +258,6 @@ updateChatParticipants#7761198 participants:ChatParticipants = Update; updateUserStatus#1bfbd823 user_id:int status:UserStatus = Update; updateUserName#a7332b73 user_id:int first_name:string last_name:string username:string = Update; updateUserPhoto#95313b0c user_id:int date:int photo:UserProfilePhoto previous:Bool = Update; -updateContactRegistered#2575bbb9 user_id:int date:int = Update; updateContactLink#9d2e67c5 user_id:int my_link:ContactLink foreign_link:ContactLink = Update; updateNewEncryptedMessage#12bcbd9a message:EncryptedMessage qts:int = Update; updateEncryptedChatTyping#1710f156 chat_id:int = Update; @@ -305,13 +308,16 @@ updateBotWebhookJSONQuery#9b9240a6 query_id:long data:DataJSON timeout:int = Upd updateBotShippingQuery#e0cdc940 query_id:long user_id:int payload:bytes shipping_address:PostAddress = Update; updateBotPrecheckoutQuery#5d2f3aa9 flags:# query_id:long user_id:int payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string currency:string total_amount:long = Update; updatePhoneCall#ab0f6b1e phone_call:PhoneCall = Update; -updateLangPackTooLong#10c2404b = Update; +updateLangPackTooLong#46560264 lang_code:string = Update; updateLangPack#56022f4d difference:LangPackDifference = Update; updateFavedStickers#e511996d = Update; updateChannelReadMessagesContents#89893b45 channel_id:int messages:Vector = Update; updateContactsReset#7084a7be = Update; updateChannelAvailableMessages#70db6837 channel_id:int available_min_id:int = Update; updateDialogUnreadMark#e16459c3 flags:# unread:flags.0?true peer:DialogPeer = Update; +updateUserPinnedMessage#4c43da18 user_id:int id:int = Update; +updateChatPinnedMessage#22893b26 chat_id:int id:int = Update; +updateMessagePoll#aca1657b flags:# poll_id:long poll:flags.0?Poll results:PollResults = Update; updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; @@ -338,11 +344,11 @@ upload.fileCdnRedirect#f18cda44 dc_id:int file_token:bytes encryption_key:bytes dcOption#18b7a10d flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true cdn:flags.3?true static:flags.4?true id:int ip_address:string port:int secret:flags.10?bytes = DcOption; -config#3213dbba flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true blocked_mode:flags.8?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector dc_txt_domain_name:string chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string autoupdate_url_prefix:flags.7?string gif_search_username:flags.9?string venue_search_username:flags.10?string img_search_username:flags.11?string static_maps_provider:flags.12?string caption_length_max:int message_length_max:int webfile_dc_id:int suggested_lang_code:flags.2?string lang_pack_version:flags.2?int = Config; +config#e6ca25f6 flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true blocked_mode:flags.8?true pfs_enabled:flags.13?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector dc_txt_domain_name:string chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string autoupdate_url_prefix:flags.7?string gif_search_username:flags.9?string venue_search_username:flags.10?string img_search_username:flags.11?string static_maps_provider:flags.12?string caption_length_max:int message_length_max:int webfile_dc_id:int suggested_lang_code:flags.2?string lang_pack_version:flags.2?int base_lang_pack_version:flags.2?int = Config; nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc; -help.appUpdate#8987f311 id:int critical:Bool url:string text:string = help.AppUpdate; +help.appUpdate#1da7158f flags:# popup:flags.0?true id:int version:string text:string entities:Vector document:flags.1?Document url:flags.2?string = help.AppUpdate; help.noAppUpdate#c45a6536 = help.AppUpdate; help.inviteText#18cb9f78 message:string = help.InviteText; @@ -373,16 +379,17 @@ messages.sentEncryptedMessage#560f8935 date:int = messages.SentEncryptedMessage; messages.sentEncryptedFile#9493ff32 date:int file:EncryptedFile = messages.SentEncryptedMessage; inputDocumentEmpty#72f0eaae = InputDocument; -inputDocument#18798952 id:long access_hash:long = InputDocument; +inputDocument#1abfb575 id:long access_hash:long file_reference:bytes = InputDocument; documentEmpty#36f8c871 id:long = Document; -document#87232bc7 id:long access_hash:long date:int mime_type:string size:int thumb:PhotoSize dc_id:int version:int attributes:Vector = Document; +document#59534e4c id:long access_hash:long file_reference:bytes date:int mime_type:string size:int thumb:PhotoSize dc_id:int attributes:Vector = Document; help.support#17c6b5f6 phone_number:string user:User = help.Support; notifyPeer#9fd40bd8 peer:Peer = NotifyPeer; notifyUsers#b4c83b4c = NotifyPeer; notifyChats#c007cec3 = NotifyPeer; +notifyBroadcasts#d612e8ef = NotifyPeer; sendMessageTypingAction#16bf744e = SendMessageAction; sendMessageCancelAction#fd5ec8f5 = SendMessageAction; @@ -403,10 +410,12 @@ contacts.found#b3134d9d my_results:Vector results:Vector chats:Vecto inputPrivacyKeyStatusTimestamp#4f96cb18 = InputPrivacyKey; inputPrivacyKeyChatInvite#bdfb0426 = InputPrivacyKey; inputPrivacyKeyPhoneCall#fabadc5f = InputPrivacyKey; +inputPrivacyKeyPhoneP2P#db9e70d2 = InputPrivacyKey; privacyKeyStatusTimestamp#bc2eab30 = PrivacyKey; privacyKeyChatInvite#500e6dfa = PrivacyKey; privacyKeyPhoneCall#3d662b7b = PrivacyKey; +privacyKeyPhoneP2P#39491cc8 = PrivacyKey; inputPrivacyValueAllowContacts#d09e07b = InputPrivacyRule; inputPrivacyValueAllowAll#184b35ce = InputPrivacyRule; @@ -454,16 +463,15 @@ webPagePending#c586da1c id:long date:int = WebPage; webPage#5f07b4bc flags:# id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document cached_page:flags.10?Page = WebPage; webPageNotModified#85849473 = WebPage; -authorization#7bf2e6f6 hash:long flags:int device_model:string platform:string system_version:string api_id:int app_name:string app_version:string date_created:int date_active:int ip:string country:string region:string = Authorization; +authorization#ad01d61d flags:# current:flags.0?true official_app:flags.1?true password_pending:flags.2?true hash:long device_model:string platform:string system_version:string api_id:int app_name:string app_version:string date_created:int date_active:int ip:string country:string region:string = Authorization; account.authorizations#1250abde authorizations:Vector = account.Authorizations; -account.noPassword#5ea182f6 new_salt:bytes new_secure_salt:bytes secure_random:bytes email_unconfirmed_pattern:string = account.Password; -account.password#ca39b447 flags:# has_recovery:flags.0?true has_secure_values:flags.1?true current_salt:bytes new_salt:bytes new_secure_salt:bytes secure_random:bytes hint:string email_unconfirmed_pattern:string = account.Password; +account.password#ad2641f8 flags:# has_recovery:flags.0?true has_secure_values:flags.1?true has_password:flags.2?true current_algo:flags.2?PasswordKdfAlgo srp_B:flags.2?bytes srp_id:flags.2?long hint:flags.3?string email_unconfirmed_pattern:flags.4?string new_algo:PasswordKdfAlgo new_secure_algo:SecurePasswordKdfAlgo secure_random:bytes = account.Password; -account.passwordSettings#7bd9c3f1 email:string secure_salt:bytes secure_secret:bytes secure_secret_id:long = account.PasswordSettings; +account.passwordSettings#9a5c33e5 flags:# email:flags.0?string secure_settings:flags.1?SecureSecretSettings = account.PasswordSettings; -account.passwordInputSettings#21ffa60d flags:# new_salt:flags.0?bytes new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string new_secure_salt:flags.2?bytes new_secure_secret:flags.2?bytes new_secure_secret_id:flags.2?long = account.PasswordInputSettings; +account.passwordInputSettings#c23727c9 flags:# new_algo:flags.0?PasswordKdfAlgo new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string new_secure_settings:flags.2?SecureSecretSettings = account.PasswordInputSettings; auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery; @@ -663,6 +671,12 @@ textFixed#6c3f19b9 text:RichText = RichText; textUrl#3c2884c1 text:RichText url:string webpage_id:long = RichText; textEmail#de5a0dd6 text:RichText email:string = RichText; textConcat#7e6260d7 texts:Vector = RichText; +textSubscript#ed6a8504 text:RichText = RichText; +textSuperscript#c7fb5e01 text:RichText = RichText; +textMarked#34b8621 text:RichText = RichText; +textPhone#1ccb966a text:RichText phone:string = RichText; +textImage#81ccf4f document_id:long w:int h:int = RichText; +textAnchor#35553762 text:RichText name:string = RichText; pageBlockUnsupported#13567e8a = PageBlock; pageBlockTitle#70abc3fd text:RichText = PageBlock; @@ -675,21 +689,24 @@ pageBlockPreformatted#c070d93e text:RichText language:string = PageBlock; pageBlockFooter#48870999 text:RichText = PageBlock; pageBlockDivider#db20b188 = PageBlock; pageBlockAnchor#ce0d37b0 name:string = PageBlock; -pageBlockList#3a58c7f4 ordered:Bool items:Vector = PageBlock; +pageBlockList#e4e88011 items:Vector = PageBlock; pageBlockBlockquote#263d7c26 text:RichText caption:RichText = PageBlock; pageBlockPullquote#4f4456d3 text:RichText caption:RichText = PageBlock; -pageBlockPhoto#e9c69982 photo_id:long caption:RichText = PageBlock; -pageBlockVideo#d9d71866 flags:# autoplay:flags.0?true loop:flags.1?true video_id:long caption:RichText = PageBlock; +pageBlockPhoto#1759c560 flags:# photo_id:long caption:PageCaption url:flags.0?string webpage_id:flags.0?long = PageBlock; +pageBlockVideo#7c8fe7b6 flags:# autoplay:flags.0?true loop:flags.1?true video_id:long caption:PageCaption = PageBlock; pageBlockCover#39f23300 cover:PageBlock = PageBlock; -pageBlockEmbed#cde200d1 flags:# full_width:flags.0?true allow_scrolling:flags.3?true url:flags.1?string html:flags.2?string poster_photo_id:flags.4?long w:int h:int caption:RichText = PageBlock; -pageBlockEmbedPost#292c7be9 url:string webpage_id:long author_photo_id:long author:string date:int blocks:Vector caption:RichText = PageBlock; -pageBlockCollage#8b31c4f items:Vector caption:RichText = PageBlock; -pageBlockSlideshow#130c8963 items:Vector caption:RichText = PageBlock; +pageBlockEmbed#a8718dc5 flags:# full_width:flags.0?true allow_scrolling:flags.3?true url:flags.1?string html:flags.2?string poster_photo_id:flags.4?long w:flags.5?int h:flags.5?int caption:PageCaption = PageBlock; +pageBlockEmbedPost#f259a80b url:string webpage_id:long author_photo_id:long author:string date:int blocks:Vector caption:PageCaption = PageBlock; +pageBlockCollage#65a0fa4d items:Vector caption:PageCaption = PageBlock; +pageBlockSlideshow#31f9590 items:Vector caption:PageCaption = PageBlock; pageBlockChannel#ef1751b5 channel:Chat = PageBlock; -pageBlockAudio#31b81a7f audio_id:long caption:RichText = PageBlock; - -pagePart#8e3f9ebe blocks:Vector photos:Vector documents:Vector = Page; -pageFull#556ec7aa blocks:Vector photos:Vector documents:Vector = Page; +pageBlockAudio#804361ea audio_id:long caption:PageCaption = PageBlock; +pageBlockKicker#1e148390 text:RichText = PageBlock; +pageBlockTable#bf4dea82 flags:# bordered:flags.0?true striped:flags.1?true title:RichText rows:Vector = PageBlock; +pageBlockOrderedList#9a8ae1e1 items:Vector = PageBlock; +pageBlockDetails#76768bed flags:# open:flags.0?true blocks:Vector title:RichText = PageBlock; +pageBlockRelatedArticles#16115a96 title:RichText articles:Vector = PageBlock; +pageBlockMap#a44f3ef6 geo:GeoPoint zoom:int w:int h:int caption:PageCaption = PageBlock; phoneCallDiscardReasonMissed#85e42301 = PhoneCallDiscardReason; phoneCallDiscardReasonDisconnect#e095c1a0 = PhoneCallDiscardReason; @@ -748,7 +765,7 @@ phoneCallEmpty#5366c915 id:long = PhoneCall; phoneCallWaiting#1b8f4ad1 flags:# id:long access_hash:long date:int admin_id:int participant_id:int protocol:PhoneCallProtocol receive_date:flags.0?int = PhoneCall; phoneCallRequested#83761ce4 id:long access_hash:long date:int admin_id:int participant_id:int g_a_hash:bytes protocol:PhoneCallProtocol = PhoneCall; phoneCallAccepted#6d003d3f id:long access_hash:long date:int admin_id:int participant_id:int g_b:bytes protocol:PhoneCallProtocol = PhoneCall; -phoneCall#ffe6ab67 id:long access_hash:long date:int admin_id:int participant_id:int g_a_or_b:bytes key_fingerprint:long protocol:PhoneCallProtocol connection:PhoneConnection alternative_connections:Vector start_date:int = PhoneCall; +phoneCall#e6f9ddf3 flags:# p2p_allowed:flags.5?true id:long access_hash:long date:int admin_id:int participant_id:int g_a_or_b:bytes key_fingerprint:long protocol:PhoneCallProtocol connection:PhoneConnection alternative_connections:Vector start_date:int = PhoneCall; phoneCallDiscarded#50ca4de1 flags:# need_rating:flags.2?true need_debug:flags.3?true id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = PhoneCall; phoneConnection#9d4c17c0 id:long ip:string ipv6:string port:int peer_tag:bytes = PhoneConnection; @@ -770,7 +787,7 @@ langPackStringDeleted#2979eeb2 key:string = LangPackString; langPackDifference#f385c1f6 lang_code:string from_version:int version:int strings:Vector = LangPackDifference; -langPackLanguage#117698f1 name:string native_name:string lang_code:string = LangPackLanguage; +langPackLanguage#eeca5ce3 flags:# official:flags.0?true rtl:flags.2?true beta:flags.3?true name:string native_name:string lang_code:string base_lang_code:flags.1?string plural_code:string strings_count:int translated_count:int translations_url:string = LangPackLanguage; channelAdminRights#5d7ceba5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true invite_link:flags.6?true pin_messages:flags.7?true add_admins:flags.9?true manage_call:flags.10?true = ChannelAdminRights; @@ -864,9 +881,9 @@ secureValueTypeTemporaryRegistration#ea02ec33 = SecureValueType; secureValueTypePhone#b320aadb = SecureValueType; secureValueTypeEmail#8e3ca7ee = SecureValueType; -secureValue#b4b4b699 flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?SecureFile reverse_side:flags.2?SecureFile selfie:flags.3?SecureFile files:flags.4?Vector plain_data:flags.5?SecurePlainData hash:bytes = SecureValue; +secureValue#187fa0ca flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?SecureFile reverse_side:flags.2?SecureFile selfie:flags.3?SecureFile translation:flags.6?Vector files:flags.4?Vector plain_data:flags.5?SecurePlainData hash:bytes = SecureValue; -inputSecureValue#67872e8 flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?InputSecureFile reverse_side:flags.2?InputSecureFile selfie:flags.3?InputSecureFile files:flags.4?Vector plain_data:flags.5?SecurePlainData = InputSecureValue; +inputSecureValue#db21d0a7 flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?InputSecureFile reverse_side:flags.2?InputSecureFile selfie:flags.3?InputSecureFile translation:flags.6?Vector files:flags.4?Vector plain_data:flags.5?SecurePlainData = InputSecureValue; secureValueHash#ed1ecdb0 type:SecureValueType hash:bytes = SecureValueHash; @@ -876,10 +893,13 @@ secureValueErrorReverseSide#868a2aa5 type:SecureValueType file_hash:bytes text:s secureValueErrorSelfie#e537ced6 type:SecureValueType file_hash:bytes text:string = SecureValueError; secureValueErrorFile#7a700873 type:SecureValueType file_hash:bytes text:string = SecureValueError; secureValueErrorFiles#666220e9 type:SecureValueType file_hash:Vector text:string = SecureValueError; +secureValueError#869d758f type:SecureValueType hash:bytes text:string = SecureValueError; +secureValueErrorTranslationFile#a1144770 type:SecureValueType file_hash:bytes text:string = SecureValueError; +secureValueErrorTranslationFiles#34636dd8 type:SecureValueType file_hash:Vector text:string = SecureValueError; secureCredentialsEncrypted#33f0ea47 data:bytes hash:bytes secret:bytes = SecureCredentialsEncrypted; -account.authorizationForm#cb976d53 flags:# selfie_required:flags.1?true required_types:Vector values:Vector errors:Vector users:Vector privacy_policy_url:flags.0?string = account.AuthorizationForm; +account.authorizationForm#ad2e1cd8 flags:# required_types:Vector values:Vector errors:Vector users:Vector privacy_policy_url:flags.0?string = account.AuthorizationForm; account.sentEmailCode#811f854f email_pattern:string length:int = account.SentEmailCode; @@ -890,6 +910,68 @@ savedPhoneContact#1142bd56 phone:string first_name:string last_name:string date: account.takeout#4dba4501 id:long = account.Takeout; +passwordKdfAlgoUnknown#d45ab096 = PasswordKdfAlgo; +passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow#3a912d4a salt1:bytes salt2:bytes g:int p:bytes = PasswordKdfAlgo; + +securePasswordKdfAlgoUnknown#4a8537 = SecurePasswordKdfAlgo; +securePasswordKdfAlgoPBKDF2HMACSHA512iter100000#bbf2dda0 salt:bytes = SecurePasswordKdfAlgo; +securePasswordKdfAlgoSHA512#86471d92 salt:bytes = SecurePasswordKdfAlgo; + +secureSecretSettings#1527bcac secure_algo:SecurePasswordKdfAlgo secure_secret:bytes secure_secret_id:long = SecureSecretSettings; + +inputCheckPasswordEmpty#9880f658 = InputCheckPasswordSRP; +inputCheckPasswordSRP#d27ff082 srp_id:long A:bytes M1:bytes = InputCheckPasswordSRP; + +secureRequiredType#829d99da flags:# native_names:flags.0?true selfie_required:flags.1?true translation_required:flags.2?true type:SecureValueType = SecureRequiredType; +secureRequiredTypeOneOf#27477b4 types:Vector = SecureRequiredType; + +help.passportConfigNotModified#bfb9f457 = help.PassportConfig; +help.passportConfig#a098d6af hash:int countries_langs:DataJSON = help.PassportConfig; + +inputAppEvent#1d1b1245 time:double type:string peer:long data:JSONValue = InputAppEvent; + +jsonObjectValue#c0de1bd9 key:string value:JSONValue = JSONObjectValue; + +jsonNull#3f6d7b68 = JSONValue; +jsonBool#c7345e6a value:Bool = JSONValue; +jsonNumber#2be0dfa4 value:double = JSONValue; +jsonString#b71e767a value:string = JSONValue; +jsonArray#f7444763 value:Vector = JSONValue; +jsonObject#99c1d49d value:Vector = JSONValue; + +pageTableCell#34566b6a flags:# header:flags.0?true align_center:flags.3?true align_right:flags.4?true valign_middle:flags.5?true valign_bottom:flags.6?true text:flags.7?RichText colspan:flags.1?int rowspan:flags.2?int = PageTableCell; + +pageTableRow#e0c0c5e5 cells:Vector = PageTableRow; + +pageCaption#6f747657 text:RichText credit:RichText = PageCaption; + +pageListItemText#b92fb6cd text:RichText = PageListItem; +pageListItemBlocks#25e073fc blocks:Vector = PageListItem; + +pageListOrderedItemText#5e068047 num:string text:RichText = PageListOrderedItem; +pageListOrderedItemBlocks#98dd8936 num:string blocks:Vector = PageListOrderedItem; + +pageRelatedArticle#b390dc08 flags:# url:string webpage_id:long title:flags.0?string description:flags.1?string photo_id:flags.2?long author:flags.3?string published_date:flags.4?int = PageRelatedArticle; + +page#ae891bec flags:# part:flags.0?true rtl:flags.1?true v2:flags.2?true url:string blocks:Vector photos:Vector documents:Vector = Page; + +help.supportName#8c05f1c9 name:string = help.SupportName; + +help.userInfoEmpty#f3ae2eed = help.UserInfo; +help.userInfo#1eb3758 message:string entities:Vector author:string date:int = help.UserInfo; + +pollAnswer#6ca9c2e9 text:string option:bytes = PollAnswer; + +poll#d5529d06 id:long flags:# closed:flags.0?true question:string answers:Vector = Poll; + +pollAnswerVoters#3b6ddad2 flags:# chosen:flags.0?true option:bytes voters:int = PollAnswerVoters; + +pollResults#5755785a flags:# min:flags.0?true results:flags.1?Vector total_voters:flags.2?int = PollResults; + +chatOnlines#f041e250 onlines:int = ChatOnlines; + +statsURL#47a971e0 url:string = StatsURL; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -909,7 +991,7 @@ auth.exportAuthorization#e5bfffcd dc_id:int = auth.ExportedAuthorization; auth.importAuthorization#e3ef9613 id:int bytes:bytes = auth.Authorization; auth.bindTempAuthKey#cdd42a05 perm_auth_key_id:long nonce:long expires_at:int encrypted_message:bytes = Bool; auth.importBotAuthorization#67a3ff2c flags:int api_id:int api_hash:string bot_auth_token:string = auth.Authorization; -auth.checkPassword#a63011e password_hash:bytes = auth.Authorization; +auth.checkPassword#d18b4d16 password:InputCheckPasswordSRP = auth.Authorization; auth.requestPasswordRecovery#d897bc66 = auth.PasswordRecovery; auth.recoverPassword#4ea56e92 code:string = auth.Authorization; auth.resendCode#3ef1a9bf phone_number:string phone_code_hash:string = auth.SentCode; @@ -938,11 +1020,11 @@ account.updateDeviceLocked#38df3532 period:int = Bool; account.getAuthorizations#e320c158 = account.Authorizations; account.resetAuthorization#df77f3bc hash:long = Bool; account.getPassword#548a30f5 = account.Password; -account.getPasswordSettings#bc8d11bb current_password_hash:bytes = account.PasswordSettings; -account.updatePasswordSettings#fa7c4b86 current_password_hash:bytes new_settings:account.PasswordInputSettings = Bool; +account.getPasswordSettings#9cd4eaf9 password:InputCheckPasswordSRP = account.PasswordSettings; +account.updatePasswordSettings#a59b102f password:InputCheckPasswordSRP new_settings:account.PasswordInputSettings = Bool; account.sendConfirmPhoneCode#1516d7bd flags:# allow_flashcall:flags.0?true hash:string current_number:flags.0?Bool = auth.SentCode; account.confirmPhone#5f2178c3 phone_code_hash:string phone_code:string = Bool; -account.getTmpPassword#4a82327e password_hash:bytes period:int = account.TmpPassword; +account.getTmpPassword#449e0b51 password:InputCheckPasswordSRP period:int = account.TmpPassword; account.getWebAuthorizations#182e6d6f = account.WebAuthorizations; account.resetWebAuthorization#2d01b9ef hash:long = Bool; account.resetWebAuthorizations#682d2594 = Bool; @@ -958,21 +1040,27 @@ account.sendVerifyEmailCode#7011509f email:string = account.SentEmailCode; account.verifyEmail#ecba39db email:string code:string = Bool; account.initTakeoutSession#f05b4804 flags:# contacts:flags.0?true message_users:flags.1?true message_chats:flags.2?true message_megagroups:flags.3?true message_channels:flags.4?true files:flags.5?true file_max_size:flags.5?int = account.Takeout; account.finishTakeoutSession#1d2652ee flags:# success:flags.0?true = Bool; +account.confirmPasswordEmail#8fdf1920 code:string = Bool; +account.resendPasswordEmail#7a7f2a15 = Bool; +account.cancelPasswordEmail#c1cbd5b6 = Bool; +account.getContactSignUpNotification#9f07c728 = Bool; +account.setContactSignUpNotification#cff43f61 silent:Bool = Bool; +account.getNotifyExceptions#53577479 flags:# compare_sound:flags.1?true peer:flags.0?InputNotifyPeer = Updates; users.getUsers#d91a548 id:Vector = Vector; users.getFullUser#ca30a5b1 id:InputUser = UserFull; users.setSecureValueErrors#90c894b5 id:InputUser errors:Vector = Bool; +contacts.getContactIDs#2caa4a42 hash:int = Vector; contacts.getStatuses#c4a353ee = Vector; contacts.getContacts#c023849f hash:int = contacts.Contacts; contacts.importContacts#2c800be5 contacts:Vector = contacts.ImportedContacts; contacts.deleteContact#8e953744 id:InputUser = contacts.Link; contacts.deleteContacts#59ab389e id:Vector = Bool; +contacts.deleteByPhones#1013fd9e phones:Vector = Bool; contacts.block#332b49fc id:InputUser = Bool; contacts.unblock#e54100bd id:InputUser = Bool; contacts.getBlocked#f57c350f offset:int limit:int = contacts.Blocked; -contacts.exportCard#84e53737 = Vector; -contacts.importCard#4fe196fe export_card:Vector = User; contacts.search#11f812d8 q:string limit:int = contacts.Found; contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer; contacts.getTopPeers#d4982db5 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true phone_calls:flags.3?true groups:flags.10?true channels:flags.15?true offset:int limit:int hash:int = contacts.TopPeers; @@ -1038,10 +1126,10 @@ messages.getSavedGifs#83bf3d52 hash:int = messages.SavedGifs; messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool; messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults; messages.setInlineBotResults#eb5ea206 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM = Bool; -messages.sendInlineBotResult#b16e06fe flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string = Updates; +messages.sendInlineBotResult#b16e06fe flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string = Updates; messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData; -messages.editMessage#c000e4c8 flags:# no_webpage:flags.1?true stop_geo_live:flags.12?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector geo_point:flags.13?InputGeoPoint = Updates; -messages.editInlineBotMessage#adc3e828 flags:# no_webpage:flags.1?true stop_geo_live:flags.12?true id:InputBotInlineMessageID message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector geo_point:flags.13?InputGeoPoint = Bool; +messages.editMessage#d116f31e flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector = Updates; +messages.editInlineBotMessage#83557dba flags:# no_webpage:flags.1?true id:InputBotInlineMessageID message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector = Bool; messages.getBotCallbackAnswer#810a9fec flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes = messages.BotCallbackAnswer; messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool; messages.getPeerDialogs#e470bcfd peers:Vector = messages.PeerDialogs; @@ -1080,6 +1168,12 @@ messages.searchStickerSets#c2b7d08b flags:# exclude_featured:flags.0?true q:stri messages.getSplitRanges#1cff7e08 = Vector; messages.markDialogUnread#c286d98f flags:# unread:flags.0?true peer:InputDialogPeer = Bool; messages.getDialogUnreadMarks#22e24e22 = Vector; +messages.clearAllDrafts#7e58ee9c = Bool; +messages.updatePinnedMessage#d2aaf7ec flags:# silent:flags.0?true peer:InputPeer id:int = Updates; +messages.sendVote#10ea6184 peer:InputPeer msg_id:int options:Vector = Updates; +messages.getPollResults#73bb643b peer:InputPeer msg_id:int = Updates; +messages.getOnlines#6e2be050 peer:InputPeer = ChatOnlines; +messages.getStatsURL#83f6c0cd peer:InputPeer = StatsURL; updates.getState#edd4882a = updates.State; updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; @@ -1101,8 +1195,7 @@ upload.getFileHashes#c7025931 location:InputFileLocation offset:int = Vector = Bool; +help.getAppUpdate#522d5a7d source:string = help.AppUpdate; help.getInviteText#4d392343 = help.InviteText; help.getSupport#9cdf08cd = help.Support; help.getAppChangelog#9010ef6f prev_app_version:string = Updates; @@ -1113,6 +1206,12 @@ help.getProxyData#3d7758e1 = help.ProxyData; help.getTermsOfServiceUpdate#2ca51fd1 = help.TermsOfServiceUpdate; help.acceptTermsOfService#ee72f79a id:DataJSON = Bool; help.getDeepLinkInfo#3fedc75f path:string = help.DeepLinkInfo; +help.getAppConfig#98914110 = JSONValue; +help.saveAppLog#6f02f748 events:Vector = Bool; +help.getPassportConfig#c661ad08 hash:int = help.PassportConfig; +help.getSupportName#d360e72c = help.SupportName; +help.getUserInfo#38a08d3 user_id:InputUser = help.UserInfo; +help.editUserInfo#66b91b70 user_id:InputUser message:string entities:Vector = help.UserInfo; channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool; channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector = messages.AffectedMessages; @@ -1138,7 +1237,6 @@ channels.deleteChannel#c0111fe3 channel:InputChannel = Updates; channels.toggleInvites#49609307 channel:InputChannel enabled:Bool = Updates; channels.exportMessageLink#ceb77163 channel:InputChannel id:int grouped:Bool = ExportedMessageLink; channels.toggleSignatures#1f69b606 channel:InputChannel enabled:Bool = Updates; -channels.updatePinnedMessage#a72ded52 flags:# silent:flags.0?true channel:InputChannel id:int = Updates; channels.getAdminedPublicChannels#8d8d82d7 = messages.Chats; channels.editBanned#bfd915cd channel:InputChannel user_id:InputUser banned_rights:ChannelBannedRights = Updates; channels.getAdminLog#33ddf480 flags:# channel:InputChannel q:string events_filter:flags.0?ChannelAdminLogEventsFilter admins:flags.1?Vector max_id:long min_id:long limit:int = channels.AdminLogResults; @@ -1172,9 +1270,10 @@ phone.discardCall#78d413a6 peer:InputPhoneCall duration:int reason:PhoneCallDisc phone.setCallRating#1c536a34 peer:InputPhoneCall rating:int comment:string = Updates; phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool; -langpack.getLangPack#9ab5c58e lang_code:string = LangPackDifference; -langpack.getStrings#2e1ee318 lang_code:string keys:Vector = Vector; -langpack.getDifference#b2e4d7d from_version:int = LangPackDifference; -langpack.getLanguages#800fd57d = Vector; +langpack.getLangPack#f2f2330a lang_pack:string lang_code:string = LangPackDifference; +langpack.getStrings#efea3803 lang_pack:string lang_code:string keys:Vector = Vector; +langpack.getDifference#9d51e814 lang_code:string from_version:int = LangPackDifference; +langpack.getLanguages#42c6978f lang_pack:string = Vector; +langpack.getLanguage#6a596502 lang_pack:string lang_code:string = LangPackLanguage; -// LAYER 82 +// LAYER 91 From aef02f049cea0f947f71947d45e4f29516694121 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sat, 22 Dec 2018 23:54:18 +0100 Subject: [PATCH 08/39] Add Poll and PollAnswer types --- .../client/types/messages_and_media/poll.py | 79 +++++++++++++++++++ .../types/messages_and_media/poll_answer.py | 32 ++++++++ 2 files changed, 111 insertions(+) create mode 100644 pyrogram/client/types/messages_and_media/poll.py create mode 100644 pyrogram/client/types/messages_and_media/poll_answer.py diff --git a/pyrogram/client/types/messages_and_media/poll.py b/pyrogram/client/types/messages_and_media/poll.py new file mode 100644 index 00000000..2e3c3d1a --- /dev/null +++ b/pyrogram/client/types/messages_and_media/poll.py @@ -0,0 +1,79 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from typing import List + +import pyrogram +from pyrogram.api import types +from .poll_answer import PollAnswer +from ..pyrogram_type import PyrogramType + + +class Poll(PyrogramType): + def __init__(self, + *, + client: "pyrogram.client.ext.BaseClient", + id: int, + closed: bool, + question: str, + answers: List[PollAnswer], + answer_chosen: int = None, + total_voters: int): + super().__init__(client) + + self.id = id + self.closed = closed + self.question = question + self.answers = answers + self.answer_chosen = answer_chosen + self.total_voters = total_voters + + @staticmethod + def _parse(client, media_poll: types.MessageMediaPoll) -> "Poll": + poll = media_poll.poll + results = media_poll.results.results + total_voters = media_poll.results.total_voters + answer_chosen = None + + answers = [] + + for i, answer in enumerate(poll.answers): + voters = None + + if results: + result = results[i] + voters = result.voters + + if result.chosen: + answer_chosen = i + + answers.append(PollAnswer( + text=answer.text, + voters=voters, + client=client + )) + + return Poll( + id=poll.id, + closed=poll.closed, + question=poll.question, + answers=answers, + answer_chosen=answer_chosen, + total_voters=total_voters, + client=client + ) diff --git a/pyrogram/client/types/messages_and_media/poll_answer.py b/pyrogram/client/types/messages_and_media/poll_answer.py new file mode 100644 index 00000000..a9dbc513 --- /dev/null +++ b/pyrogram/client/types/messages_and_media/poll_answer.py @@ -0,0 +1,32 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +import pyrogram +from ..pyrogram_type import PyrogramType + + +class PollAnswer(PyrogramType): + def __init__(self, + *, + client: "pyrogram.client.ext.BaseClient", + text: str, + voters: int = None): + super().__init__(client) + + self.text = text + self.voters = voters From 1ef3bc758f1e0f93fcf9248a16c549d5c940361c Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sat, 22 Dec 2018 23:54:42 +0100 Subject: [PATCH 09/39] Export Poll and PollAnswer types --- pyrogram/__init__.py | 3 ++- pyrogram/client/types/__init__.py | 2 +- pyrogram/client/types/messages_and_media/__init__.py | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pyrogram/__init__.py b/pyrogram/__init__.py index 6ea6aa5c..44d27f79 100644 --- a/pyrogram/__init__.py +++ b/pyrogram/__init__.py @@ -31,7 +31,8 @@ from .client.types import ( InputMediaVideo, InputMediaDocument, InputMediaAudio, InputMediaAnimation, InputPhoneContact, Location, Message, MessageEntity, Dialog, Dialogs, Photo, PhotoSize, Sticker, User, UserStatus, UserProfilePhotos, Venue, Animation, Video, VideoNote, Voice, CallbackQuery, Messages, ForceReply, - InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove + InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove, + Poll, PollAnswer ) from .client import ( Client, ChatAction, ParseMode, Emoji, diff --git a/pyrogram/client/types/__init__.py b/pyrogram/client/types/__init__.py index 8289a947..0fdfc685 100644 --- a/pyrogram/client/types/__init__.py +++ b/pyrogram/client/types/__init__.py @@ -31,7 +31,7 @@ from .input_media import ( from .messages_and_media import ( Audio, Contact, Document, Animation, Location, Photo, PhotoSize, Sticker, Venue, Video, VideoNote, Voice, UserProfilePhotos, - Message, Messages, MessageEntity + Message, Messages, MessageEntity, Poll, PollAnswer ) from .user_and_chats import ( Chat, ChatMember, ChatMembers, ChatPhoto, diff --git a/pyrogram/client/types/messages_and_media/__init__.py b/pyrogram/client/types/messages_and_media/__init__.py index 3ab359ae..527fee27 100644 --- a/pyrogram/client/types/messages_and_media/__init__.py +++ b/pyrogram/client/types/messages_and_media/__init__.py @@ -26,6 +26,8 @@ from .message_entity import MessageEntity from .messages import Messages from .photo import Photo from .photo_size import PhotoSize +from .poll import Poll +from .poll_answer import PollAnswer from .sticker import Sticker from .user_profile_photos import UserProfilePhotos from .venue import Venue From 2b25b9469ba59fc61cef2f6c6d92efdec9717cc6 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sat, 22 Dec 2018 23:55:04 +0100 Subject: [PATCH 10/39] Parse Poll objects inside Message --- pyrogram/client/types/messages_and_media/message.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pyrogram/client/types/messages_and_media/message.py b/pyrogram/client/types/messages_and_media/message.py index 25c35456..baeac31f 100644 --- a/pyrogram/client/types/messages_and_media/message.py +++ b/pyrogram/client/types/messages_and_media/message.py @@ -262,6 +262,7 @@ class Message(PyrogramType): location: "pyrogram.Location" = None, venue: "pyrogram.Venue" = None, web_page: bool = None, + poll: "pyrogram.Poll" = None, new_chat_members: List[User] = None, left_chat_member: User = None, new_chat_title: str = None, @@ -317,6 +318,7 @@ class Message(PyrogramType): self.location = location self.venue = venue self.web_page = web_page + self.poll = poll self.new_chat_members = new_chat_members self.left_chat_member = left_chat_member self.new_chat_title = new_chat_title @@ -440,6 +442,7 @@ class Message(PyrogramType): sticker = None document = None web_page = None + poll = None media = message.media @@ -494,6 +497,8 @@ class Message(PyrogramType): elif isinstance(media, types.MessageMediaWebPage): web_page = True media = None + elif isinstance(media, types.MessageMediaPoll): + poll = pyrogram.Poll._parse(client, media) else: media = None @@ -542,6 +547,7 @@ class Message(PyrogramType): sticker=sticker, document=document, web_page=web_page, + poll=poll, views=message.views, via_bot=User._parse(client, users.get(message.via_bot_id, None)), outgoing=message.out, From 8eab47123ae6d4338159d8d74ad40061253259a1 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sat, 22 Dec 2018 23:55:16 +0100 Subject: [PATCH 11/39] Add Filters.poll to filter Poll messages --- pyrogram/client/filters/filters.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyrogram/client/filters/filters.py b/pyrogram/client/filters/filters.py index fa9eace3..3e5c8fd5 100644 --- a/pyrogram/client/filters/filters.py +++ b/pyrogram/client/filters/filters.py @@ -121,6 +121,9 @@ class Filters: web_page = create("WebPage", lambda _, m: m.web_page) """Filter messages sent with a webpage preview.""" + poll = create("Poll", lambda _, m: m.poll) + """Filter messages that contain :obj:`Poll ` objects.""" + private = create("Private", lambda _, m: bool(m.chat and m.chat.type == "private")) """Filter messages sent in private chats.""" From e669a6a9ae3e1cd23e348267bcf35929bc9b858a Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 23 Dec 2018 00:33:15 +0100 Subject: [PATCH 12/39] Add send_poll method --- pyrogram/client/methods/messages/__init__.py | 4 +- pyrogram/client/methods/messages/send_poll.py | 65 +++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 pyrogram/client/methods/messages/send_poll.py diff --git a/pyrogram/client/methods/messages/__init__.py b/pyrogram/client/methods/messages/__init__.py index 35dae756..77919d17 100644 --- a/pyrogram/client/methods/messages/__init__.py +++ b/pyrogram/client/methods/messages/__init__.py @@ -33,6 +33,7 @@ from .send_location import SendLocation from .send_media_group import SendMediaGroup from .send_message import SendMessage from .send_photo import SendPhoto +from .send_poll import SendPoll from .send_sticker import SendSticker from .send_venue import SendVenue from .send_video import SendVideo @@ -62,6 +63,7 @@ class Messages( SendVenue, SendVideo, SendVideoNote, - SendVoice + SendVoice, + SendPoll ): pass diff --git a/pyrogram/client/methods/messages/send_poll.py b/pyrogram/client/methods/messages/send_poll.py new file mode 100644 index 00000000..e5229b85 --- /dev/null +++ b/pyrogram/client/methods/messages/send_poll.py @@ -0,0 +1,65 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from typing import Union, List + +import pyrogram +from pyrogram.api import functions, types +from pyrogram.client.ext import BaseClient + + +class SendPoll(BaseClient): + # TODO: Docs + def send_poll(self, + chat_id: Union[int, str], + question: str, + options: List[str], + disable_notification: bool = None, + reply_to_message_id: int = None, + reply_markup: Union["pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply"] = None) -> "pyrogram.Message": + r = self.send( + functions.messages.SendMedia( + peer=self.resolve_peer(chat_id), + media=types.InputMediaPoll( + poll=types.Poll( + id=0, + question=question, + answers=[ + types.PollAnswer(text=o, option=bytes([i])) + for i, o in enumerate(options) + ] + ) + ), + message="", + silent=disable_notification or None, + reply_to_msg_id=reply_to_message_id, + random_id=self.rnd_id(), + reply_markup=reply_markup.write() if reply_markup else None + ) + ) + + for i in r.updates: + if isinstance(i, (types.UpdateNewMessage, types.UpdateNewChannelMessage)): + return pyrogram.Message._parse( + self, i.message, + {i.id: i for i in r.users}, + {i.id: i for i in r.chats} + ) From 84fef9ecf18da186cfb98be09050e24b3e12a332 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 23 Dec 2018 00:55:00 +0100 Subject: [PATCH 13/39] Add missing file_reference argument where applicable --- pyrogram/client/client.py | 6 ++-- .../client/methods/chats/set_chat_photo.py | 3 +- .../methods/messages/edit_message_media.py | 30 ++++++++++++------- .../client/methods/messages/send_animation.py | 3 +- .../client/methods/messages/send_audio.py | 3 +- .../client/methods/messages/send_document.py | 3 +- .../methods/messages/send_media_group.py | 12 +++++--- .../client/methods/messages/send_photo.py | 3 +- .../client/methods/messages/send_sticker.py | 3 +- .../client/methods/messages/send_video.py | 3 +- .../methods/messages/send_video_note.py | 3 +- .../client/methods/messages/send_voice.py | 3 +- .../users/delete_user_profile_photos.py | 3 +- 13 files changed, 51 insertions(+), 27 deletions(-) diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py index f5aa33ea..2782a940 100644 --- a/pyrogram/client/client.py +++ b/pyrogram/client/client.py @@ -1223,7 +1223,6 @@ class Client(Methods, BaseClient): volume_id: int = None, local_id: int = None, secret: int = None, - version: int = 0, size: int = None, progress: callable = None, progress_args: tuple = ()) -> str: @@ -1271,13 +1270,14 @@ class Client(Methods, BaseClient): location = types.InputFileLocation( volume_id=volume_id, local_id=local_id, - secret=secret + secret=secret, + file_reference=b"" ) else: # Any other file can be more easily accessed by id and access_hash location = types.InputDocumentFileLocation( id=id, access_hash=access_hash, - version=version + file_reference=b"" ) limit = 1024 * 1024 diff --git a/pyrogram/client/methods/chats/set_chat_photo.py b/pyrogram/client/methods/chats/set_chat_photo.py index 1d4ab5e5..7d276648 100644 --- a/pyrogram/client/methods/chats/set_chat_photo.py +++ b/pyrogram/client/methods/chats/set_chat_photo.py @@ -61,7 +61,8 @@ class SetChatPhoto(BaseClient): photo = types.InputChatPhoto( id=types.InputPhoto( id=s[0], - access_hash=s[1] + access_hash=s[1], + file_reference=b"" ) ) diff --git a/pyrogram/client/methods/messages/edit_message_media.py b/pyrogram/client/methods/messages/edit_message_media.py index 50fd1f48..02b26ea6 100644 --- a/pyrogram/client/methods/messages/edit_message_media.py +++ b/pyrogram/client/methods/messages/edit_message_media.py @@ -84,7 +84,8 @@ class EditMessageMedia(BaseClient): media = types.InputMediaPhoto( id=types.InputPhoto( id=media.photo.id, - access_hash=media.photo.access_hash + access_hash=media.photo.access_hash, + file_reference=b"" ) ) elif media.media.startswith("http"): @@ -110,7 +111,8 @@ class EditMessageMedia(BaseClient): media = types.InputMediaPhoto( id=types.InputPhoto( id=unpacked[2], - access_hash=unpacked[3] + access_hash=unpacked[3], + file_reference=b"" ) ) @@ -138,7 +140,8 @@ class EditMessageMedia(BaseClient): media = types.InputMediaDocument( id=types.InputDocument( id=media.document.id, - access_hash=media.document.access_hash + access_hash=media.document.access_hash, + file_reference=b"" ) ) elif media.media.startswith("http"): @@ -164,7 +167,8 @@ class EditMessageMedia(BaseClient): media = types.InputMediaDocument( id=types.InputDocument( id=unpacked[2], - access_hash=unpacked[3] + access_hash=unpacked[3], + file_reference=b"" ) ) @@ -191,7 +195,8 @@ class EditMessageMedia(BaseClient): media = types.InputMediaDocument( id=types.InputDocument( id=media.document.id, - access_hash=media.document.access_hash + access_hash=media.document.access_hash, + file_reference=b"" ) ) elif media.media.startswith("http"): @@ -217,7 +222,8 @@ class EditMessageMedia(BaseClient): media = types.InputMediaDocument( id=types.InputDocument( id=unpacked[2], - access_hash=unpacked[3] + access_hash=unpacked[3], + file_reference=b"" ) ) @@ -246,7 +252,8 @@ class EditMessageMedia(BaseClient): media = types.InputMediaDocument( id=types.InputDocument( id=media.document.id, - access_hash=media.document.access_hash + access_hash=media.document.access_hash, + file_reference=b"" ) ) elif media.media.startswith("http"): @@ -272,7 +279,8 @@ class EditMessageMedia(BaseClient): media = types.InputMediaDocument( id=types.InputDocument( id=unpacked[2], - access_hash=unpacked[3] + access_hash=unpacked[3], + file_reference=b"" ) ) @@ -294,7 +302,8 @@ class EditMessageMedia(BaseClient): media = types.InputMediaDocument( id=types.InputDocument( id=media.document.id, - access_hash=media.document.access_hash + access_hash=media.document.access_hash, + file_reference=b"" ) ) elif media.media.startswith("http"): @@ -320,7 +329,8 @@ class EditMessageMedia(BaseClient): media = types.InputMediaDocument( id=types.InputDocument( id=unpacked[2], - access_hash=unpacked[3] + access_hash=unpacked[3], + file_reference=b"" ) ) diff --git a/pyrogram/client/methods/messages/send_animation.py b/pyrogram/client/methods/messages/send_animation.py index 7b1467f2..2ebfd87f 100644 --- a/pyrogram/client/methods/messages/send_animation.py +++ b/pyrogram/client/methods/messages/send_animation.py @@ -167,7 +167,8 @@ class SendAnimation(BaseClient): media = types.InputMediaDocument( id=types.InputDocument( id=unpacked[2], - access_hash=unpacked[3] + access_hash=unpacked[3], + file_reference=b"" ) ) diff --git a/pyrogram/client/methods/messages/send_audio.py b/pyrogram/client/methods/messages/send_audio.py index 685e2a2b..47db3cbe 100644 --- a/pyrogram/client/methods/messages/send_audio.py +++ b/pyrogram/client/methods/messages/send_audio.py @@ -167,7 +167,8 @@ class SendAudio(BaseClient): media = types.InputMediaDocument( id=types.InputDocument( id=unpacked[2], - access_hash=unpacked[3] + access_hash=unpacked[3], + file_reference=b"" ) ) diff --git a/pyrogram/client/methods/messages/send_document.py b/pyrogram/client/methods/messages/send_document.py index da61aaea..f42c83d7 100644 --- a/pyrogram/client/methods/messages/send_document.py +++ b/pyrogram/client/methods/messages/send_document.py @@ -148,7 +148,8 @@ class SendDocument(BaseClient): media = types.InputMediaDocument( id=types.InputDocument( id=unpacked[2], - access_hash=unpacked[3] + access_hash=unpacked[3], + file_reference=b"" ) ) diff --git a/pyrogram/client/methods/messages/send_media_group.py b/pyrogram/client/methods/messages/send_media_group.py index d5549089..0b18cac3 100644 --- a/pyrogram/client/methods/messages/send_media_group.py +++ b/pyrogram/client/methods/messages/send_media_group.py @@ -77,7 +77,8 @@ class SendMediaGroup(BaseClient): media = types.InputMediaPhoto( id=types.InputPhoto( id=media.photo.id, - access_hash=media.photo.access_hash + access_hash=media.photo.access_hash, + file_reference=b"" ) ) else: @@ -99,7 +100,8 @@ class SendMediaGroup(BaseClient): media = types.InputMediaPhoto( id=types.InputPhoto( id=unpacked[2], - access_hash=unpacked[3] + access_hash=unpacked[3], + file_reference=b"" ) ) elif isinstance(i, pyrogram.InputMediaVideo): @@ -127,7 +129,8 @@ class SendMediaGroup(BaseClient): media = types.InputMediaDocument( id=types.InputDocument( id=media.document.id, - access_hash=media.document.access_hash + access_hash=media.document.access_hash, + file_reference=b"" ) ) else: @@ -149,7 +152,8 @@ class SendMediaGroup(BaseClient): media = types.InputMediaDocument( id=types.InputDocument( id=unpacked[2], - access_hash=unpacked[3] + access_hash=unpacked[3], + file_reference=b"" ) ) diff --git a/pyrogram/client/methods/messages/send_photo.py b/pyrogram/client/methods/messages/send_photo.py index 00d06ea1..26f6948a 100644 --- a/pyrogram/client/methods/messages/send_photo.py +++ b/pyrogram/client/methods/messages/send_photo.py @@ -142,7 +142,8 @@ class SendPhoto(BaseClient): media = types.InputMediaPhoto( id=types.InputPhoto( id=unpacked[2], - access_hash=unpacked[3] + access_hash=unpacked[3], + file_reference=b"" ), ttl_seconds=ttl_seconds ) diff --git a/pyrogram/client/methods/messages/send_sticker.py b/pyrogram/client/methods/messages/send_sticker.py index 045e1b21..936d2e54 100644 --- a/pyrogram/client/methods/messages/send_sticker.py +++ b/pyrogram/client/methods/messages/send_sticker.py @@ -127,7 +127,8 @@ class SendSticker(BaseClient): media = types.InputMediaDocument( id=types.InputDocument( id=unpacked[2], - access_hash=unpacked[3] + access_hash=unpacked[3], + file_reference=b"" ) ) diff --git a/pyrogram/client/methods/messages/send_video.py b/pyrogram/client/methods/messages/send_video.py index fa0a51a6..15b98732 100644 --- a/pyrogram/client/methods/messages/send_video.py +++ b/pyrogram/client/methods/messages/send_video.py @@ -170,7 +170,8 @@ class SendVideo(BaseClient): media = types.InputMediaDocument( id=types.InputDocument( id=unpacked[2], - access_hash=unpacked[3] + access_hash=unpacked[3], + file_reference=b"" ) ) diff --git a/pyrogram/client/methods/messages/send_video_note.py b/pyrogram/client/methods/messages/send_video_note.py index dd64c14a..32202a0b 100644 --- a/pyrogram/client/methods/messages/send_video_note.py +++ b/pyrogram/client/methods/messages/send_video_note.py @@ -146,7 +146,8 @@ class SendVideoNote(BaseClient): media = types.InputMediaDocument( id=types.InputDocument( id=unpacked[2], - access_hash=unpacked[3] + access_hash=unpacked[3], + file_reference=b"" ) ) diff --git a/pyrogram/client/methods/messages/send_voice.py b/pyrogram/client/methods/messages/send_voice.py index 27621fa0..014e8e9f 100644 --- a/pyrogram/client/methods/messages/send_voice.py +++ b/pyrogram/client/methods/messages/send_voice.py @@ -146,7 +146,8 @@ class SendVoice(BaseClient): media = types.InputMediaDocument( id=types.InputDocument( id=unpacked[2], - access_hash=unpacked[3] + access_hash=unpacked[3], + file_reference=b"" ) ) diff --git a/pyrogram/client/methods/users/delete_user_profile_photos.py b/pyrogram/client/methods/users/delete_user_profile_photos.py index 764c3f3e..91c59247 100644 --- a/pyrogram/client/methods/users/delete_user_profile_photos.py +++ b/pyrogram/client/methods/users/delete_user_profile_photos.py @@ -49,7 +49,8 @@ class DeleteUserProfilePhotos(BaseClient): input_photos.append( types.InputPhoto( id=s[0], - access_hash=s[1] + access_hash=s[1], + file_reference=b"" ) ) From 7ee89c94cb837311cf7f7e90e2700dce2615e46f Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 23 Dec 2018 01:00:31 +0100 Subject: [PATCH 14/39] Update pin/unpin_chat_message It is now possible to pin messages in basic groups as well as in the own user's chat. --- .../client/methods/chats/pin_chat_message.py | 25 ++++++------------- .../methods/chats/unpin_chat_message.py | 21 ++++++---------- 2 files changed, 15 insertions(+), 31 deletions(-) diff --git a/pyrogram/client/methods/chats/pin_chat_message.py b/pyrogram/client/methods/chats/pin_chat_message.py index 5a4ab50d..7b99e8ff 100644 --- a/pyrogram/client/methods/chats/pin_chat_message.py +++ b/pyrogram/client/methods/chats/pin_chat_message.py @@ -18,7 +18,7 @@ from typing import Union -from pyrogram.api import functions, types +from pyrogram.api import functions from ...ext import BaseClient @@ -27,7 +27,7 @@ class PinChatMessage(BaseClient): chat_id: Union[int, str], message_id: int, disable_notification: bool = None) -> bool: - """Use this method to pin a message in a supergroup or a channel. + """Use this method to pin a message in a group, channel or your own chat. You must be an administrator in the chat for this to work and must have the "can_pin_messages" admin right in the supergroup or "can_edit_messages" admin right in the channel. @@ -49,19 +49,10 @@ class PinChatMessage(BaseClient): :class:`Error ` in case of a Telegram RPC error. ``ValueError`` if a chat_id doesn't belong to a supergroup or a channel. """ - peer = self.resolve_peer(chat_id) - - if isinstance(peer, types.InputPeerChannel): - self.send( - functions.channels.UpdatePinnedMessage( - channel=peer, - id=message_id, - silent=disable_notification or None - ) + self.send( + functions.messages.UpdatePinnedMessage( + peer=self.resolve_peer(chat_id), + id=message_id, + silent=disable_notification or None ) - elif isinstance(peer, types.InputPeerChat): - raise ValueError("The chat_id \"{}\" belongs to a basic group".format(chat_id)) - else: - raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id)) - - return True + ) diff --git a/pyrogram/client/methods/chats/unpin_chat_message.py b/pyrogram/client/methods/chats/unpin_chat_message.py index 435f38d7..9eb35eea 100644 --- a/pyrogram/client/methods/chats/unpin_chat_message.py +++ b/pyrogram/client/methods/chats/unpin_chat_message.py @@ -18,14 +18,14 @@ from typing import Union -from pyrogram.api import functions, types +from pyrogram.api import functions from ...ext import BaseClient class UnpinChatMessage(BaseClient): def unpin_chat_message(self, chat_id: Union[int, str]) -> bool: - """Use this method to unpin a message in a supergroup or a channel. + """Use this method to unpin a message in a group, channel or your own chat. You must be an administrator in the chat for this to work and must have the "can_pin_messages" admin right in the supergroup or "can_edit_messages" admin right in the channel. @@ -40,18 +40,11 @@ class UnpinChatMessage(BaseClient): :class:`Error ` in case of a Telegram RPC error. ``ValueError`` if a chat_id doesn't belong to a supergroup or a channel. """ - peer = self.resolve_peer(chat_id) - - if isinstance(peer, types.InputPeerChannel): - self.send( - functions.channels.UpdatePinnedMessage( - channel=peer, - id=0 - ) + self.send( + functions.messages.UpdatePinnedMessage( + peer=self.resolve_peer(chat_id), + id=0 ) - elif isinstance(peer, types.InputPeerChat): - raise ValueError("The chat_id \"{}\" belongs to a basic group".format(chat_id)) - else: - raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id)) + ) return True From 0371f4ce8b8a07dd7295e68e98d7499085fdb256 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 23 Dec 2018 01:05:44 +0100 Subject: [PATCH 15/39] Make cloud password methods raise NotImplementedError. See #178 The protocol changed (SRP) and they are currently not re-implemented. --- .../methods/password/change_cloud_password.py | 44 +++++++++---------- .../methods/password/enable_cloud_password.py | 42 +++++++++--------- .../methods/password/remove_cloud_password.py | 37 ++++++++-------- 3 files changed, 59 insertions(+), 64 deletions(-) diff --git a/pyrogram/client/methods/password/change_cloud_password.py b/pyrogram/client/methods/password/change_cloud_password.py index d7d0e1fa..6ae660f9 100644 --- a/pyrogram/client/methods/password/change_cloud_password.py +++ b/pyrogram/client/methods/password/change_cloud_password.py @@ -16,10 +16,6 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . -import os -from hashlib import sha256 - -from pyrogram.api import functions, types from ...ext import BaseClient @@ -46,23 +42,25 @@ class ChangeCloudPassword(BaseClient): Raises: :class:`Error ` in case of a Telegram RPC error. """ - r = self.send(functions.account.GetPassword()) + raise NotImplementedError - if isinstance(r, types.account.Password): - current_password_hash = sha256(r.current_salt + current_password.encode() + r.current_salt).digest() - - new_salt = r.new_salt + os.urandom(8) - new_password_hash = sha256(new_salt + new_password.encode() + new_salt).digest() - - return self.send( - functions.account.UpdatePasswordSettings( - current_password_hash=current_password_hash, - new_settings=types.account.PasswordInputSettings( - new_salt=new_salt, - new_password_hash=new_password_hash, - hint=new_hint - ) - ) - ) - else: - return False + # r = self.send(functions.account.GetPassword()) + # + # if isinstance(r, types.account.Password): + # current_password_hash = sha256(r.current_salt + current_password.encode() + r.current_salt).digest() + # + # new_salt = r.new_salt + os.urandom(8) + # new_password_hash = sha256(new_salt + new_password.encode() + new_salt).digest() + # + # return self.send( + # functions.account.UpdatePasswordSettings( + # current_password_hash=current_password_hash, + # new_settings=types.account.PasswordInputSettings( + # new_salt=new_salt, + # new_password_hash=new_password_hash, + # hint=new_hint + # ) + # ) + # ) + # else: + # return False diff --git a/pyrogram/client/methods/password/enable_cloud_password.py b/pyrogram/client/methods/password/enable_cloud_password.py index 4eb8df6a..f5a71f4b 100644 --- a/pyrogram/client/methods/password/enable_cloud_password.py +++ b/pyrogram/client/methods/password/enable_cloud_password.py @@ -16,10 +16,6 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . -import os -from hashlib import sha256 - -from pyrogram.api import functions, types from ...ext import BaseClient @@ -48,22 +44,24 @@ class EnableCloudPassword(BaseClient): Raises: :class:`Error ` in case of a Telegram RPC error. """ - r = self.send(functions.account.GetPassword()) + raise NotImplementedError - if isinstance(r, types.account.NoPassword): - salt = r.new_salt + os.urandom(8) - password_hash = sha256(salt + password.encode() + salt).digest() - - return self.send( - functions.account.UpdatePasswordSettings( - current_password_hash=salt, - new_settings=types.account.PasswordInputSettings( - new_salt=salt, - new_password_hash=password_hash, - hint=hint, - email=email - ) - ) - ) - else: - return False + # r = self.send(functions.account.GetPassword()) + # + # if isinstance(r, types.account.NoPassword): + # salt = r.new_salt + os.urandom(8) + # password_hash = sha256(salt + password.encode() + salt).digest() + # + # return self.send( + # functions.account.UpdatePasswordSettings( + # current_password_hash=salt, + # new_settings=types.account.PasswordInputSettings( + # new_salt=salt, + # new_password_hash=password_hash, + # hint=hint, + # email=email + # ) + # ) + # ) + # else: + # return False diff --git a/pyrogram/client/methods/password/remove_cloud_password.py b/pyrogram/client/methods/password/remove_cloud_password.py index 3dab720d..92ae666d 100644 --- a/pyrogram/client/methods/password/remove_cloud_password.py +++ b/pyrogram/client/methods/password/remove_cloud_password.py @@ -16,9 +16,6 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . -from hashlib import sha256 - -from pyrogram.api import functions, types from ...ext import BaseClient @@ -37,20 +34,22 @@ class RemoveCloudPassword(BaseClient): Raises: :class:`Error ` in case of a Telegram RPC error. """ - r = self.send(functions.account.GetPassword()) + raise NotImplementedError - if isinstance(r, types.account.Password): - password_hash = sha256(r.current_salt + password.encode() + r.current_salt).digest() - - return self.send( - functions.account.UpdatePasswordSettings( - current_password_hash=password_hash, - new_settings=types.account.PasswordInputSettings( - new_salt=b"", - new_password_hash=b"", - hint="" - ) - ) - ) - else: - return False + # r = self.send(functions.account.GetPassword()) + # + # if isinstance(r, types.account.Password): + # password_hash = sha256(r.current_salt + password.encode() + r.current_salt).digest() + # + # return self.send( + # functions.account.UpdatePasswordSettings( + # current_password_hash=password_hash, + # new_settings=types.account.PasswordInputSettings( + # new_salt=b"", + # new_password_hash=b"", + # hint="" + # ) + # ) + # ) + # else: + # return False From 2101dfb8db04714855de0389becb78f10bd09176 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 23 Dec 2018 01:09:20 +0100 Subject: [PATCH 16/39] Show a meaningful error and hint to read more when using cloud password --- pyrogram/client/methods/password/change_cloud_password.py | 5 ++++- pyrogram/client/methods/password/enable_cloud_password.py | 5 ++++- pyrogram/client/methods/password/remove_cloud_password.py | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/pyrogram/client/methods/password/change_cloud_password.py b/pyrogram/client/methods/password/change_cloud_password.py index 6ae660f9..6a65d51b 100644 --- a/pyrogram/client/methods/password/change_cloud_password.py +++ b/pyrogram/client/methods/password/change_cloud_password.py @@ -42,7 +42,10 @@ class ChangeCloudPassword(BaseClient): Raises: :class:`Error ` in case of a Telegram RPC error. """ - raise NotImplementedError + raise NotImplementedError( + "Cloud password methods are currently not available. " + "See https://github.com/pyrogram/pyrogram/issues/178" + ) # r = self.send(functions.account.GetPassword()) # diff --git a/pyrogram/client/methods/password/enable_cloud_password.py b/pyrogram/client/methods/password/enable_cloud_password.py index f5a71f4b..28542815 100644 --- a/pyrogram/client/methods/password/enable_cloud_password.py +++ b/pyrogram/client/methods/password/enable_cloud_password.py @@ -44,7 +44,10 @@ class EnableCloudPassword(BaseClient): Raises: :class:`Error ` in case of a Telegram RPC error. """ - raise NotImplementedError + raise NotImplementedError( + "Cloud password methods are currently not available. " + "See https://github.com/pyrogram/pyrogram/issues/178" + ) # r = self.send(functions.account.GetPassword()) # diff --git a/pyrogram/client/methods/password/remove_cloud_password.py b/pyrogram/client/methods/password/remove_cloud_password.py index 92ae666d..9b45fc8e 100644 --- a/pyrogram/client/methods/password/remove_cloud_password.py +++ b/pyrogram/client/methods/password/remove_cloud_password.py @@ -34,7 +34,10 @@ class RemoveCloudPassword(BaseClient): Raises: :class:`Error ` in case of a Telegram RPC error. """ - raise NotImplementedError + raise NotImplementedError( + "Cloud password methods are currently not available. " + "See https://github.com/pyrogram/pyrogram/issues/178" + ) # r = self.send(functions.account.GetPassword()) # From f8de518f6bb68acdf657191e3fa43eca331b5491 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 23 Dec 2018 13:21:32 +0100 Subject: [PATCH 17/39] Update future version --- pyrogram/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyrogram/__init__.py b/pyrogram/__init__.py index 44d27f79..b2f930b3 100644 --- a/pyrogram/__init__.py +++ b/pyrogram/__init__.py @@ -23,7 +23,7 @@ __copyright__ = "Copyright (C) 2017-2018 Dan Tès Date: Sun, 23 Dec 2018 13:28:53 +0100 Subject: [PATCH 18/39] Refactor Poll. Move PollAnswer into poll.py and rename it to PollOption --- pyrogram/__init__.py | 2 +- pyrogram/client/types/__init__.py | 2 +- .../types/messages_and_media/__init__.py | 1 - .../client/types/messages_and_media/poll.py | 35 ++++++++++++------- .../types/messages_and_media/poll_answer.py | 32 ----------------- 5 files changed, 25 insertions(+), 47 deletions(-) delete mode 100644 pyrogram/client/types/messages_and_media/poll_answer.py diff --git a/pyrogram/__init__.py b/pyrogram/__init__.py index b2f930b3..94b6c951 100644 --- a/pyrogram/__init__.py +++ b/pyrogram/__init__.py @@ -32,7 +32,7 @@ from .client.types import ( Location, Message, MessageEntity, Dialog, Dialogs, Photo, PhotoSize, Sticker, User, UserStatus, UserProfilePhotos, Venue, Animation, Video, VideoNote, Voice, CallbackQuery, Messages, ForceReply, InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove, - Poll, PollAnswer + Poll ) from .client import ( Client, ChatAction, ParseMode, Emoji, diff --git a/pyrogram/client/types/__init__.py b/pyrogram/client/types/__init__.py index 0fdfc685..983c3804 100644 --- a/pyrogram/client/types/__init__.py +++ b/pyrogram/client/types/__init__.py @@ -31,7 +31,7 @@ from .input_media import ( from .messages_and_media import ( Audio, Contact, Document, Animation, Location, Photo, PhotoSize, Sticker, Venue, Video, VideoNote, Voice, UserProfilePhotos, - Message, Messages, MessageEntity, Poll, PollAnswer + Message, Messages, MessageEntity, Poll ) from .user_and_chats import ( Chat, ChatMember, ChatMembers, ChatPhoto, diff --git a/pyrogram/client/types/messages_and_media/__init__.py b/pyrogram/client/types/messages_and_media/__init__.py index 527fee27..d77b3494 100644 --- a/pyrogram/client/types/messages_and_media/__init__.py +++ b/pyrogram/client/types/messages_and_media/__init__.py @@ -27,7 +27,6 @@ from .messages import Messages from .photo import Photo from .photo_size import PhotoSize from .poll import Poll -from .poll_answer import PollAnswer from .sticker import Sticker from .user_profile_photos import UserProfilePhotos from .venue import Venue diff --git a/pyrogram/client/types/messages_and_media/poll.py b/pyrogram/client/types/messages_and_media/poll.py index 2e3c3d1a..a7277f49 100644 --- a/pyrogram/client/types/messages_and_media/poll.py +++ b/pyrogram/client/types/messages_and_media/poll.py @@ -20,10 +20,21 @@ from typing import List import pyrogram from pyrogram.api import types -from .poll_answer import PollAnswer from ..pyrogram_type import PyrogramType +class PollOption(PyrogramType): + def __init__(self, + *, + client: "pyrogram.client.ext.BaseClient", + text: str, + voters: int): + super().__init__(client) + + self.text = text + self.voters = voters + + class Poll(PyrogramType): def __init__(self, *, @@ -31,16 +42,16 @@ class Poll(PyrogramType): id: int, closed: bool, question: str, - answers: List[PollAnswer], - answer_chosen: int = None, + options: List[PollOption], + option_chosen: int = None, total_voters: int): super().__init__(client) self.id = id self.closed = closed self.question = question - self.answers = answers - self.answer_chosen = answer_chosen + self.options = options + self.option_chosen = option_chosen self.total_voters = total_voters @staticmethod @@ -48,21 +59,21 @@ class Poll(PyrogramType): poll = media_poll.poll results = media_poll.results.results total_voters = media_poll.results.total_voters - answer_chosen = None + option_chosen = None - answers = [] + options = [] for i, answer in enumerate(poll.answers): - voters = None + voters = 0 if results: result = results[i] voters = result.voters if result.chosen: - answer_chosen = i + option_chosen = i - answers.append(PollAnswer( + options.append(PollOption( text=answer.text, voters=voters, client=client @@ -72,8 +83,8 @@ class Poll(PyrogramType): id=poll.id, closed=poll.closed, question=poll.question, - answers=answers, - answer_chosen=answer_chosen, + options=options, + option_chosen=option_chosen, total_voters=total_voters, client=client ) diff --git a/pyrogram/client/types/messages_and_media/poll_answer.py b/pyrogram/client/types/messages_and_media/poll_answer.py deleted file mode 100644 index a9dbc513..00000000 --- a/pyrogram/client/types/messages_and_media/poll_answer.py +++ /dev/null @@ -1,32 +0,0 @@ -# Pyrogram - Telegram MTProto API Client Library for Python -# Copyright (C) 2017-2018 Dan Tès -# -# This file is part of Pyrogram. -# -# Pyrogram is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published -# by the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Pyrogram is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with Pyrogram. If not, see . - -import pyrogram -from ..pyrogram_type import PyrogramType - - -class PollAnswer(PyrogramType): - def __init__(self, - *, - client: "pyrogram.client.ext.BaseClient", - text: str, - voters: int = None): - super().__init__(client) - - self.text = text - self.voters = voters From 0ce7498f810af9ddabfa96ed491f2f4751815106 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 23 Dec 2018 15:37:44 +0100 Subject: [PATCH 19/39] Parse the pinned message on basic chats too --- pyrogram/client/types/user_and_chats/chat.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pyrogram/client/types/user_and_chats/chat.py b/pyrogram/client/types/user_and_chats/chat.py index 5b5a54a8..7b4240dd 100644 --- a/pyrogram/client/types/user_and_chats/chat.py +++ b/pyrogram/client/types/user_and_chats/chat.py @@ -199,11 +199,11 @@ class Chat(PyrogramType): parsed_chat.can_set_sticker_set = full_chat.can_set_stickers parsed_chat.sticker_set_name = full_chat.stickerset - if full_chat.pinned_msg_id: - parsed_chat.pinned_message = client.get_messages( - parsed_chat.id, - message_ids=full_chat.pinned_msg_id - ) + if full_chat.pinned_msg_id: + parsed_chat.pinned_message = client.get_messages( + parsed_chat.id, + message_ids=full_chat.pinned_msg_id + ) if isinstance(full_chat.exported_invite, types.ChatInviteExported): parsed_chat.invite_link = full_chat.exported_invite.link From 1f82eaa26f6a48c927814ea069306a1b24045611 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 23 Dec 2018 16:19:24 +0100 Subject: [PATCH 20/39] Add vote_poll method --- pyrogram/client/methods/messages/__init__.py | 4 +- pyrogram/client/methods/messages/vote_poll.py | 39 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 pyrogram/client/methods/messages/vote_poll.py diff --git a/pyrogram/client/methods/messages/__init__.py b/pyrogram/client/methods/messages/__init__.py index 77919d17..0401aa35 100644 --- a/pyrogram/client/methods/messages/__init__.py +++ b/pyrogram/client/methods/messages/__init__.py @@ -39,6 +39,7 @@ from .send_venue import SendVenue from .send_video import SendVideo from .send_video_note import SendVideoNote from .send_voice import SendVoice +from .vote_poll import VotePoll class Messages( @@ -64,6 +65,7 @@ class Messages( SendVideo, SendVideoNote, SendVoice, - SendPoll + SendPoll, + VotePoll ): pass diff --git a/pyrogram/client/methods/messages/vote_poll.py b/pyrogram/client/methods/messages/vote_poll.py new file mode 100644 index 00000000..06b45d13 --- /dev/null +++ b/pyrogram/client/methods/messages/vote_poll.py @@ -0,0 +1,39 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from typing import Union + +from pyrogram.api import functions +from pyrogram.client.ext import BaseClient + + +class VotePoll(BaseClient): + # TODO: Docs + def vote_poll(self, + chat_id: Union[int, str], + message_id: id, + option: int) -> bool: + self.send( + functions.messages.SendVote( + peer=self.resolve_peer(chat_id), + msg_id=message_id, + options=[bytes([option])] + ) + ) + + return True From 10f1e063269438b720531bc4d31552b8803d951c Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 23 Dec 2018 16:30:42 +0100 Subject: [PATCH 21/39] Rework vote_poll to work with all polls generated by different clients --- pyrogram/client/methods/messages/vote_poll.py | 4 +++- pyrogram/client/types/messages_and_media/poll.py | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pyrogram/client/methods/messages/vote_poll.py b/pyrogram/client/methods/messages/vote_poll.py index 06b45d13..fcb3932f 100644 --- a/pyrogram/client/methods/messages/vote_poll.py +++ b/pyrogram/client/methods/messages/vote_poll.py @@ -28,11 +28,13 @@ class VotePoll(BaseClient): chat_id: Union[int, str], message_id: id, option: int) -> bool: + poll = self.get_messages(chat_id, message_id).poll + self.send( functions.messages.SendVote( peer=self.resolve_peer(chat_id), msg_id=message_id, - options=[bytes([option])] + options=[poll.options[option]._data] ) ) diff --git a/pyrogram/client/types/messages_and_media/poll.py b/pyrogram/client/types/messages_and_media/poll.py index a7277f49..42ba8640 100644 --- a/pyrogram/client/types/messages_and_media/poll.py +++ b/pyrogram/client/types/messages_and_media/poll.py @@ -28,11 +28,13 @@ class PollOption(PyrogramType): *, client: "pyrogram.client.ext.BaseClient", text: str, - voters: int): + voters: int, + data: bytes): super().__init__(client) self.text = text self.voters = voters + self._data = data class Poll(PyrogramType): @@ -76,6 +78,7 @@ class Poll(PyrogramType): options.append(PollOption( text=answer.text, voters=voters, + data=answer.option, client=client )) From 03aa5094f7f278286ddb9db2036dd83155fa9770 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 23 Dec 2018 16:40:09 +0100 Subject: [PATCH 22/39] Add retract_vote method --- pyrogram/client/methods/messages/__init__.py | 4 +- .../client/methods/messages/retract_vote.py | 38 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 pyrogram/client/methods/messages/retract_vote.py diff --git a/pyrogram/client/methods/messages/__init__.py b/pyrogram/client/methods/messages/__init__.py index 0401aa35..66340283 100644 --- a/pyrogram/client/methods/messages/__init__.py +++ b/pyrogram/client/methods/messages/__init__.py @@ -24,6 +24,7 @@ from .edit_message_text import EditMessageText from .forward_messages import ForwardMessages from .get_history import GetHistory from .get_messages import GetMessages +from .retract_vote import RetractVote from .send_animation import SendAnimation from .send_audio import SendAudio from .send_chat_action import SendChatAction @@ -66,6 +67,7 @@ class Messages( SendVideoNote, SendVoice, SendPoll, - VotePoll + VotePoll, + RetractVote ): pass diff --git a/pyrogram/client/methods/messages/retract_vote.py b/pyrogram/client/methods/messages/retract_vote.py new file mode 100644 index 00000000..ff1be582 --- /dev/null +++ b/pyrogram/client/methods/messages/retract_vote.py @@ -0,0 +1,38 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +from typing import Union + +from pyrogram.api import functions +from pyrogram.client.ext import BaseClient + + +class RetractVote(BaseClient): + # TODO: Docs + def retract_vote(self, + chat_id: Union[int, str], + message_id: id) -> bool: + self.send( + functions.messages.SendVote( + peer=self.resolve_peer(chat_id), + msg_id=message_id, + options=[] + ) + ) + + return True From f477171344f5d7fde075b077c82fc14ff721d8a7 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 23 Dec 2018 16:45:54 +0100 Subject: [PATCH 23/39] Document vote_poll --- pyrogram/client/methods/messages/vote_poll.py | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/pyrogram/client/methods/messages/vote_poll.py b/pyrogram/client/methods/messages/vote_poll.py index fcb3932f..7d2d10cc 100644 --- a/pyrogram/client/methods/messages/vote_poll.py +++ b/pyrogram/client/methods/messages/vote_poll.py @@ -23,11 +23,30 @@ from pyrogram.client.ext import BaseClient class VotePoll(BaseClient): - # TODO: Docs def vote_poll(self, chat_id: Union[int, str], message_id: id, option: int) -> bool: + """Use this method to vote a poll. + + Args: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + message_id (``int``): + Unique poll message identifier inside this chat. + + option (``int``): + Index of the poll option you want to vote for (0 to 9). + + Returns: + On success, True is returned. + + Raises: + :class:`Error ` in case of a Telegram RPC error. + """ poll = self.get_messages(chat_id, message_id).poll self.send( From 03d6c49d924c7d2ba0b3f16496a086ca25ea57ca Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 23 Dec 2018 16:52:25 +0100 Subject: [PATCH 24/39] Document send_poll method --- pyrogram/client/methods/messages/send_poll.py | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/pyrogram/client/methods/messages/send_poll.py b/pyrogram/client/methods/messages/send_poll.py index e5229b85..33f44767 100644 --- a/pyrogram/client/methods/messages/send_poll.py +++ b/pyrogram/client/methods/messages/send_poll.py @@ -24,7 +24,6 @@ from pyrogram.client.ext import BaseClient class SendPoll(BaseClient): - # TODO: Docs def send_poll(self, chat_id: Union[int, str], question: str, @@ -35,6 +34,37 @@ class SendPoll(BaseClient): "pyrogram.ReplyKeyboardMarkup", "pyrogram.ReplyKeyboardRemove", "pyrogram.ForceReply"] = None) -> "pyrogram.Message": + """Use this method to send a new poll. + + Args: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + question (``str``): + The poll question, as string. + + options (List of ``str``): + The poll options, as list of strings (2 to 10 options are allowed). + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + reply_to_message_id (``int``, *optional*): + If the message is a reply, ID of the original message. + + reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*): + Additional interface options. An object for an inline keyboard, custom reply keyboard, + instructions to remove reply keyboard or to force a reply from the user. + + Returns: + On success, the sent :obj:`Message ` is returned. + + Raises: + :class:`Error ` in case of a Telegram RPC error. + """ r = self.send( functions.messages.SendMedia( peer=self.resolve_peer(chat_id), From 2994929903aeee20ddc56a74bd05d5cfbdf02640 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 23 Dec 2018 16:59:45 +0100 Subject: [PATCH 25/39] Document Poll --- .../client/types/messages_and_media/poll.py | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/pyrogram/client/types/messages_and_media/poll.py b/pyrogram/client/types/messages_and_media/poll.py index 42ba8640..9e1f481d 100644 --- a/pyrogram/client/types/messages_and_media/poll.py +++ b/pyrogram/client/types/messages_and_media/poll.py @@ -38,6 +38,28 @@ class PollOption(PyrogramType): class Poll(PyrogramType): + """This object represents a Poll + + Args: + id (``int``): + The poll id in this chat. + + closed (``bool``): + Whether the poll is closed or not. + + question (``str``): + Poll question + + options (List of :obj:`PollOption`): + The available poll options. + + total_voters (``int``): + Total amount of voters for this poll. + + option_chosen (``int``, *optional*): + The index of your chosen option (in case you voted already), None otherwise. + """ + def __init__(self, *, client: "pyrogram.client.ext.BaseClient", @@ -45,16 +67,16 @@ class Poll(PyrogramType): closed: bool, question: str, options: List[PollOption], - option_chosen: int = None, - total_voters: int): + total_voters: int, + option_chosen: int = None): super().__init__(client) self.id = id self.closed = closed self.question = question self.options = options - self.option_chosen = option_chosen self.total_voters = total_voters + self.option_chosen = option_chosen @staticmethod def _parse(client, media_poll: types.MessageMediaPoll) -> "Poll": @@ -87,7 +109,7 @@ class Poll(PyrogramType): closed=poll.closed, question=poll.question, options=options, - option_chosen=option_chosen, total_voters=total_voters, + option_chosen=option_chosen, client=client ) From 3fe7fb20bee8285ad06b0508673aeed80898e5ee Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 23 Dec 2018 17:06:34 +0100 Subject: [PATCH 26/39] Document retract_vote --- .../client/methods/messages/retract_vote.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/pyrogram/client/methods/messages/retract_vote.py b/pyrogram/client/methods/messages/retract_vote.py index ff1be582..e0852355 100644 --- a/pyrogram/client/methods/messages/retract_vote.py +++ b/pyrogram/client/methods/messages/retract_vote.py @@ -23,10 +23,26 @@ from pyrogram.client.ext import BaseClient class RetractVote(BaseClient): - # TODO: Docs def retract_vote(self, chat_id: Union[int, str], message_id: id) -> bool: + """Use this method to retract your vote in a poll. + + Args: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your personal cloud (Saved Messages) you can simply use "me" or "self". + For a contact that exists in your Telegram address book you can use his phone number (str). + + message_id (``int``): + Unique poll message identifier inside this chat. + + Returns: + On success, True is returned. + + Raises: + :class:`Error ` in case of a Telegram RPC error. + """ self.send( functions.messages.SendVote( peer=self.resolve_peer(chat_id), From a551f1fe1eff70a523e8b5d553ee2f33b80f687c Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Sun, 23 Dec 2018 19:52:49 +0100 Subject: [PATCH 27/39] Fix Filters.poll docstrings link to Poll type --- pyrogram/client/filters/filters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyrogram/client/filters/filters.py b/pyrogram/client/filters/filters.py index cbd44a10..1347f993 100644 --- a/pyrogram/client/filters/filters.py +++ b/pyrogram/client/filters/filters.py @@ -122,7 +122,7 @@ class Filters: """Filter messages sent with a webpage preview.""" poll = create("Poll", lambda _, m: m.poll) - """Filter messages that contain :obj:`Poll ` objects.""" + """Filter messages that contain :obj:`Poll ` objects.""" private = create("Private", lambda _, m: bool(m.chat and m.chat.type == "private")) """Filter messages sent in private chats.""" From c4280f017eda7b5950d15e23f2db6a6d6583ff63 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Mon, 24 Dec 2018 14:13:47 +0100 Subject: [PATCH 28/39] Add hide_via parameter to send_inline_bot_result --- pyrogram/client/methods/bots/send_inline_bot_result.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pyrogram/client/methods/bots/send_inline_bot_result.py b/pyrogram/client/methods/bots/send_inline_bot_result.py index 23d36cba..8c6a38b5 100644 --- a/pyrogram/client/methods/bots/send_inline_bot_result.py +++ b/pyrogram/client/methods/bots/send_inline_bot_result.py @@ -28,7 +28,8 @@ class SendInlineBotResult(BaseClient): query_id: int, result_id: str, disable_notification: bool = None, - reply_to_message_id: int = None): + reply_to_message_id: int = None, + hide_via: bool = None): """Use this method to send an inline bot result. Bot results can be retrieved using :obj:`get_inline_bot_results ` @@ -51,6 +52,9 @@ class SendInlineBotResult(BaseClient): reply_to_message_id (``bool``, *optional*): If the message is a reply, ID of the original message. + hide_via (``bool``): + Sends the message with *via @bot* hidden. + Returns: On success, the sent Message is returned. @@ -64,6 +68,7 @@ class SendInlineBotResult(BaseClient): id=result_id, random_id=self.rnd_id(), silent=disable_notification or None, - reply_to_msg_id=reply_to_message_id + reply_to_msg_id=reply_to_message_id, + hide_via=hide_via or None ) ) From c833b3842add3833b38af03b664fbfa3d7f50b7e Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Mon, 24 Dec 2018 14:19:21 +0100 Subject: [PATCH 29/39] Put PollOption back in a separate file, its docstrings must be visible --- pyrogram/__init__.py | 2 +- pyrogram/client/types/__init__.py | 2 +- .../types/messages_and_media/__init__.py | 1 + .../client/types/messages_and_media/poll.py | 19 ++------ .../types/messages_and_media/poll_option.py | 46 +++++++++++++++++++ 5 files changed, 52 insertions(+), 18 deletions(-) create mode 100644 pyrogram/client/types/messages_and_media/poll_option.py diff --git a/pyrogram/__init__.py b/pyrogram/__init__.py index 94b6c951..6400abc1 100644 --- a/pyrogram/__init__.py +++ b/pyrogram/__init__.py @@ -32,7 +32,7 @@ from .client.types import ( Location, Message, MessageEntity, Dialog, Dialogs, Photo, PhotoSize, Sticker, User, UserStatus, UserProfilePhotos, Venue, Animation, Video, VideoNote, Voice, CallbackQuery, Messages, ForceReply, InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove, - Poll + Poll, PollOption ) from .client import ( Client, ChatAction, ParseMode, Emoji, diff --git a/pyrogram/client/types/__init__.py b/pyrogram/client/types/__init__.py index 983c3804..24de120f 100644 --- a/pyrogram/client/types/__init__.py +++ b/pyrogram/client/types/__init__.py @@ -31,7 +31,7 @@ from .input_media import ( from .messages_and_media import ( Audio, Contact, Document, Animation, Location, Photo, PhotoSize, Sticker, Venue, Video, VideoNote, Voice, UserProfilePhotos, - Message, Messages, MessageEntity, Poll + Message, Messages, MessageEntity, Poll, PollOption ) from .user_and_chats import ( Chat, ChatMember, ChatMembers, ChatPhoto, diff --git a/pyrogram/client/types/messages_and_media/__init__.py b/pyrogram/client/types/messages_and_media/__init__.py index d77b3494..d402ae48 100644 --- a/pyrogram/client/types/messages_and_media/__init__.py +++ b/pyrogram/client/types/messages_and_media/__init__.py @@ -27,6 +27,7 @@ from .messages import Messages from .photo import Photo from .photo_size import PhotoSize from .poll import Poll +from .poll_option import PollOption from .sticker import Sticker from .user_profile_photos import UserProfilePhotos from .venue import Venue diff --git a/pyrogram/client/types/messages_and_media/poll.py b/pyrogram/client/types/messages_and_media/poll.py index 9e1f481d..ab59b1ad 100644 --- a/pyrogram/client/types/messages_and_media/poll.py +++ b/pyrogram/client/types/messages_and_media/poll.py @@ -20,25 +20,12 @@ from typing import List import pyrogram from pyrogram.api import types +from .poll_option import PollOption from ..pyrogram_type import PyrogramType -class PollOption(PyrogramType): - def __init__(self, - *, - client: "pyrogram.client.ext.BaseClient", - text: str, - voters: int, - data: bytes): - super().__init__(client) - - self.text = text - self.voters = voters - self._data = data - - class Poll(PyrogramType): - """This object represents a Poll + """This object represents a Poll. Args: id (``int``): @@ -48,7 +35,7 @@ class Poll(PyrogramType): Whether the poll is closed or not. question (``str``): - Poll question + Poll question. options (List of :obj:`PollOption`): The available poll options. diff --git a/pyrogram/client/types/messages_and_media/poll_option.py b/pyrogram/client/types/messages_and_media/poll_option.py new file mode 100644 index 00000000..175b3701 --- /dev/null +++ b/pyrogram/client/types/messages_and_media/poll_option.py @@ -0,0 +1,46 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +import pyrogram +from ..pyrogram_type import PyrogramType + + +class PollOption(PyrogramType): + def __init__(self, + *, + client: "pyrogram.client.ext.BaseClient", + text: str, + voters: int, + data: bytes): + """This object represents a Poll Option. + + Args: + text (``str``): + Text of the poll option. + + voters (``int``): + The number of users who voted this option. + + data (``bytes``): + Unique data that identifies this option (among all the other options in a poll). + """ + super().__init__(client) + + self.text = text + self.voters = voters + self.data = data From 46d206610ed1e3e50cf23c31deefd0e20e9cf9af Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Mon, 24 Dec 2018 14:20:54 +0100 Subject: [PATCH 30/39] Add Poll related stuff in docs --- docs/source/pyrogram/Client.rst | 3 +++ docs/source/pyrogram/Types.rst | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/docs/source/pyrogram/Client.rst b/docs/source/pyrogram/Client.rst index 50e213ea..a0177a12 100644 --- a/docs/source/pyrogram/Client.rst +++ b/docs/source/pyrogram/Client.rst @@ -62,6 +62,9 @@ Messages delete_messages get_messages get_history + send_poll + vote_poll + retract_vote Chats ----- diff --git a/docs/source/pyrogram/Types.rst b/docs/source/pyrogram/Types.rst index 8763c0e1..47c755f9 100644 --- a/docs/source/pyrogram/Types.rst +++ b/docs/source/pyrogram/Types.rst @@ -40,6 +40,8 @@ Messages & Media Location Venue Sticker + Poll + PollOption Bots ---- @@ -146,6 +148,12 @@ Input Media .. autoclass:: Sticker :members: +.. autoclass:: Poll + :members: + +.. autoclass:: PollOption + :members: + .. Bots ---- From 098b06d1b742bf8aa874115789e3bd644e91a896 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Mon, 24 Dec 2018 14:32:53 +0100 Subject: [PATCH 31/39] Fix poll docstrings and vote_poll wrong attribute access --- pyrogram/client/methods/messages/vote_poll.py | 2 +- .../types/messages_and_media/poll_option.py | 25 ++++++++++--------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/pyrogram/client/methods/messages/vote_poll.py b/pyrogram/client/methods/messages/vote_poll.py index 7d2d10cc..3404a7bd 100644 --- a/pyrogram/client/methods/messages/vote_poll.py +++ b/pyrogram/client/methods/messages/vote_poll.py @@ -53,7 +53,7 @@ class VotePoll(BaseClient): functions.messages.SendVote( peer=self.resolve_peer(chat_id), msg_id=message_id, - options=[poll.options[option]._data] + options=[poll.options[option].data] ) ) diff --git a/pyrogram/client/types/messages_and_media/poll_option.py b/pyrogram/client/types/messages_and_media/poll_option.py index 175b3701..240368fc 100644 --- a/pyrogram/client/types/messages_and_media/poll_option.py +++ b/pyrogram/client/types/messages_and_media/poll_option.py @@ -21,24 +21,25 @@ from ..pyrogram_type import PyrogramType class PollOption(PyrogramType): + """This object represents a Poll Option. + + Args: + text (``str``): + Text of the poll option. + + voters (``int``): + The number of users who voted this option. + + data (``bytes``): + Unique data that identifies this option among all the other options in a poll. + """ + def __init__(self, *, client: "pyrogram.client.ext.BaseClient", text: str, voters: int, data: bytes): - """This object represents a Poll Option. - - Args: - text (``str``): - Text of the poll option. - - voters (``int``): - The number of users who voted this option. - - data (``bytes``): - Unique data that identifies this option (among all the other options in a poll). - """ super().__init__(client) self.text = text From 684b90100598119960264a14540ea761aae43d8b Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 25 Dec 2018 14:42:56 +0100 Subject: [PATCH 32/39] Reword AUTH_KEY_DUPLICATED error message --- compiler/error/source/406_NOT_ACCEPTABLE.tsv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/error/source/406_NOT_ACCEPTABLE.tsv b/compiler/error/source/406_NOT_ACCEPTABLE.tsv index 3a88a7b6..27e4c5bd 100644 --- a/compiler/error/source/406_NOT_ACCEPTABLE.tsv +++ b/compiler/error/source/406_NOT_ACCEPTABLE.tsv @@ -1,2 +1,2 @@ id message -AUTH_KEY_DUPLICATED Authorization error. You must log out and log in again with your phone number. We apologize for the inconvenience. \ No newline at end of file +AUTH_KEY_DUPLICATED Authorization error - you must delete your session file and log in again with your phone number \ No newline at end of file From 68426eaf7240e098b90c856461403bda518b1659 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 25 Dec 2018 14:48:59 +0100 Subject: [PATCH 33/39] Add FILEREF_UPGRADE_NEEDED error --- compiler/error/source/406_NOT_ACCEPTABLE.tsv | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/error/source/406_NOT_ACCEPTABLE.tsv b/compiler/error/source/406_NOT_ACCEPTABLE.tsv index 27e4c5bd..e94706ed 100644 --- a/compiler/error/source/406_NOT_ACCEPTABLE.tsv +++ b/compiler/error/source/406_NOT_ACCEPTABLE.tsv @@ -1,2 +1,3 @@ id message -AUTH_KEY_DUPLICATED Authorization error - you must delete your session file and log in again with your phone number \ No newline at end of file +AUTH_KEY_DUPLICATED Authorization error - you must delete your session file and log in again with your phone number +FILEREF_UPGRADE_NEEDED The file reference has expired - you must obtain the original media message \ No newline at end of file From b18890fddd21d034c6737da0e028b93a8027be8e Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 25 Dec 2018 14:51:03 +0100 Subject: [PATCH 34/39] Add INPUT_USER_DEACTIVATED error --- compiler/error/source/400_BAD_REQUEST.tsv | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/error/source/400_BAD_REQUEST.tsv b/compiler/error/source/400_BAD_REQUEST.tsv index 08db4c4f..fa41142a 100644 --- a/compiler/error/source/400_BAD_REQUEST.tsv +++ b/compiler/error/source/400_BAD_REQUEST.tsv @@ -76,4 +76,5 @@ USER_IS_BLOCKED The user blocked you YOU_BLOCKED_USER You blocked this user ADMINS_TOO_MUCH The chat has too many administrators BOTS_TOO_MUCH The chat has too many bots -USER_ADMIN_INVALID The action requires admin privileges \ No newline at end of file +USER_ADMIN_INVALID The action requires admin privileges +INPUT_USER_DEACTIVATED The target user has been deactivated \ No newline at end of file From e3371c90f254ac4ceffdc729584af590bf5aa49c Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 25 Dec 2018 15:38:56 +0100 Subject: [PATCH 35/39] Add PASSWORD_RECOVERY_NA and PASSWORD_EMPTY errors --- compiler/error/source/400_BAD_REQUEST.tsv | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/error/source/400_BAD_REQUEST.tsv b/compiler/error/source/400_BAD_REQUEST.tsv index fa41142a..c1a02761 100644 --- a/compiler/error/source/400_BAD_REQUEST.tsv +++ b/compiler/error/source/400_BAD_REQUEST.tsv @@ -77,4 +77,6 @@ YOU_BLOCKED_USER You blocked this user ADMINS_TOO_MUCH The chat has too many administrators BOTS_TOO_MUCH The chat has too many bots USER_ADMIN_INVALID The action requires admin privileges -INPUT_USER_DEACTIVATED The target user has been deactivated \ No newline at end of file +INPUT_USER_DEACTIVATED The target user has been deactivated +PASSWORD_RECOVERY_NA The password recovery e-mail is not available +PASSWORD_EMPTY The password entered is empty \ No newline at end of file From d91acfe2ca4baff2c05eee2b27d81d1ac89a02eb Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 25 Dec 2018 15:41:55 +0100 Subject: [PATCH 36/39] Re-implement password-protected log-ins and support password recovery --- pyrogram/client/client.py | 44 ++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py index 64ed4e8e..226f783c 100644 --- a/pyrogram/client/client.py +++ b/pyrogram/client/client.py @@ -18,7 +18,6 @@ import base64 import binascii -import getpass import json import logging import math @@ -46,9 +45,12 @@ from pyrogram.api.errors import ( PhoneNumberUnoccupied, PhoneCodeInvalid, PhoneCodeHashEmpty, PhoneCodeExpired, PhoneCodeEmpty, SessionPasswordNeeded, PasswordHashInvalid, FloodWait, PeerIdInvalid, FirstnameInvalid, PhoneNumberBanned, - VolumeLocNotFound, UserMigrate, FileIdInvalid, ChannelPrivate, PhoneNumberOccupied) + VolumeLocNotFound, UserMigrate, FileIdInvalid, ChannelPrivate, PhoneNumberOccupied, + PasswordRecoveryNa, PasswordEmpty +) from pyrogram.client.handlers import DisconnectHandler from pyrogram.client.handlers.handler import Handler +from pyrogram.client.methods.password.utils import compute_check from pyrogram.crypto import AES from pyrogram.session import Auth, Session from .dispatcher import Dispatcher @@ -574,21 +576,46 @@ class Client(Methods, BaseClient): self.first_name = None except SessionPasswordNeeded as e: print(e.MESSAGE) - r = self.send(functions.account.GetPassword()) while True: try: + r = self.send(functions.account.GetPassword()) if self.password is None: print("Hint: {}".format(r.hint)) - self.password = getpass.getpass("Enter password: ") - if type(self.password) is str: - self.password = r.current_salt + self.password.encode() + r.current_salt + self.password = input("Enter password (empty to recover): ") - password_hash = sha256(self.password).digest() + if self.password == "": + r = self.send(functions.auth.RequestPasswordRecovery()) - r = self.send(functions.auth.CheckPassword(password_hash)) + print("An e-mail containing the recovery code has been sent to {}".format( + r.email_pattern + )) + + r = self.send( + functions.auth.RecoverPassword( + code=input("Enter password recovery code: ") + ) + ) + else: + r = self.send( + functions.auth.CheckPassword( + password=compute_check(r, self.password) + ) + ) + except PasswordEmpty as e: + if password_hash_invalid_raises: + raise + 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 @@ -601,6 +628,7 @@ class Client(Methods, BaseClient): else: print(e.MESSAGE.format(x=e.x)) time.sleep(e.x) + self.password = None except Exception as e: log.error(e, exc_info=True) else: From 7e4b96cfdf1d1503c58f64a7800ebdfab82b0f3f Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 25 Dec 2018 16:20:09 +0100 Subject: [PATCH 37/39] Suppress PyPep8Naming soft-warnings --- pyrogram/client/methods/password/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyrogram/client/methods/password/utils.py b/pyrogram/client/methods/password/utils.py index 4bf0ddec..01c3fe49 100644 --- a/pyrogram/client/methods/password/utils.py +++ b/pyrogram/client/methods/password/utils.py @@ -46,6 +46,7 @@ def compute_hash(algo: types.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter1000 return sha256(algo.salt2 + hash3 + algo.salt2) +# noinspection PyPep8Naming def compute_check(r: types.account.Password, password: str) -> types.InputCheckPasswordSRP: algo = r.current_algo From 9066c9687e656c81a2917f3edd1051bda0d0375e Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 25 Dec 2018 18:11:16 +0100 Subject: [PATCH 38/39] Update README.md (Layer 91) Closes #178, closes #179 --- README.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.rst b/README.rst index 17df05c5..478683c3 100644 --- a/README.rst +++ b/README.rst @@ -17,8 +17,8 @@ Pyrogram app.run() -**Pyrogram** is a brand new Telegram_ Client Library written from the ground up in Python and C. It can be used for building -custom Telegram applications that interact with the MTProto API as both User and Bot. +**Pyrogram** is a brand new Telegram_ Client Library written from the ground up in Python and C. It can be used for +building custom Telegram applications that interact with the MTProto API as both User and Bot. Features -------- @@ -26,7 +26,7 @@ Features - **Easy to use**: You can easily install Pyrogram using pip and start building your app right away. - **High-level**: The low-level details of MTProto are abstracted and automatically handled. - **Fast**: Crypto parts are boosted up by TgCrypto_, a high-performance library written in pure C. -- **Updated** to the latest Telegram API version, currently Layer 82 on top of MTProto 2.0. +- **Updated** to the latest Telegram API version, currently Layer 91 on top of MTProto 2.0. - **Documented**: The Pyrogram API is well documented and resembles the Telegram Bot API. - **Full API**, allowing to execute any advanced action an official client is able to do, and more. @@ -99,7 +99,7 @@ Copyright & License
- Schema Layer @@ -114,10 +114,10 @@ Copyright & License .. |description| replace:: **Telegram MTProto API Client Library for Python** -.. |scheme| image:: "https://img.shields.io/badge/SCHEME-LAYER%2082-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30" +.. |scheme| image:: "https://img.shields.io/badge/schema-layer%2091-eda738.svg?longCache=true&colorA=262b30" :target: compiler/api/source/main_api.tl :alt: Scheme Layer -.. |tgcrypto| image:: "https://img.shields.io/badge/TGCRYPTO-V1.1.1-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30" +.. |tgcrypto| image:: "https://img.shields.io/badge/tgcrypto-v1.1.1-eda738.svg?longCache=true&colorA=262b30" :target: https://github.com/pyrogram/tgcrypto :alt: TgCrypto From c3f4fab58b4728920d8e8b3083fb683544e3dbd6 Mon Sep 17 00:00:00 2001 From: Dan <14043624+delivrance@users.noreply.github.com> Date: Tue, 25 Dec 2018 18:12:54 +0100 Subject: [PATCH 39/39] Update docs version --- docs/source/start/Installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/start/Installation.rst b/docs/source/start/Installation.rst index efebac5a..f139ddb2 100644 --- a/docs/source/start/Installation.rst +++ b/docs/source/start/Installation.rst @@ -82,7 +82,7 @@ If no error shows up you are good to go. >>> import pyrogram >>> pyrogram.__version__ - '0.9.3' + '0.9.4' .. _TgCrypto: https://docs.pyrogram.ml/resources/TgCrypto .. _develop: http://github.com/pyrogram/pyrogram