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
-
@@ -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
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
diff --git a/compiler/error/source/400_BAD_REQUEST.tsv b/compiler/error/source/400_BAD_REQUEST.tsv
index 08db4c4f..c1a02761 100644
--- a/compiler/error/source/400_BAD_REQUEST.tsv
+++ b/compiler/error/source/400_BAD_REQUEST.tsv
@@ -76,4 +76,7 @@ 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
+PASSWORD_RECOVERY_NA The password recovery e-mail is not available
+PASSWORD_EMPTY The password entered is empty
\ No newline at end of file
diff --git a/compiler/error/source/406_NOT_ACCEPTABLE.tsv b/compiler/error/source/406_NOT_ACCEPTABLE.tsv
index 3a88a7b6..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 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
+FILEREF_UPGRADE_NEEDED The file reference has expired - you must obtain the original media message
\ No newline at end of file
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
----
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
diff --git a/pyrogram/__init__.py b/pyrogram/__init__.py
index 34d33a48..7c5c8d88 100644
--- a/pyrogram/__init__.py
+++ b/pyrogram/__init__.py
@@ -39,7 +39,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, PollOption
)
from .client import (
Client, ChatAction, ParseMode, Emoji,
diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py
index 3f315f1d..fa69b51d 100644
--- a/pyrogram/client/client.py
+++ b/pyrogram/client/client.py
@@ -45,8 +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
@@ -578,21 +582,47 @@ class Client(Methods, BaseClient):
self.first_name = None
except SessionPasswordNeeded as e:
print(e.MESSAGE)
- r = await self.send(functions.account.GetPassword())
while True:
try:
+ r = await self.send(functions.account.GetPassword())
if self.password is None:
print("Hint: {}".format(r.hint))
self.password = await ainput("Enter password: ")
- if type(self.password) is str:
- self.password = r.current_salt + self.password.encode() + r.current_salt
+ self.password = await ainput("Enter password (empty to recover): ")
- password_hash = sha256(self.password).digest()
+ if self.password == "":
+ r = await self.send(functions.auth.RequestPasswordRecovery())
- r = await self.send(functions.auth.CheckPassword(password_hash))
+ print("An e-mail containing the recovery code has been sent to {}".format(
+ r.email_pattern
+ ))
+
+ r = await self.send(
+ functions.auth.RecoverPassword(
+ code=await ainput("Enter password recovery code: ")
+ )
+ )
+ else:
+ r = await 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
@@ -605,6 +635,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:
@@ -1263,7 +1294,7 @@ 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:
@@ -1311,13 +1342,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/filters/filters.py b/pyrogram/client/filters/filters.py
index 69d3fad3..1347f993 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."""
diff --git a/pyrogram/client/methods/bots/send_inline_bot_result.py b/pyrogram/client/methods/bots/send_inline_bot_result.py
index 0fade63b..3c9ab5ac 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
)
)
diff --git a/pyrogram/client/methods/chats/pin_chat_message.py b/pyrogram/client/methods/chats/pin_chat_message.py
index c0902234..663e59e8 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 = await self.resolve_peer(chat_id)
-
- if isinstance(peer, types.InputPeerChannel):
- await self.send(
- functions.channels.UpdatePinnedMessage(
- channel=peer,
- id=message_id,
- silent=disable_notification or None
- )
+ await self.send(
+ functions.messages.UpdatePinnedMessage(
+ peer=await 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/set_chat_photo.py b/pyrogram/client/methods/chats/set_chat_photo.py
index 5091b3bc..76bab5e0 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/chats/unpin_chat_message.py b/pyrogram/client/methods/chats/unpin_chat_message.py
index fafb8a05..4fad19d8 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):
async 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 = await self.resolve_peer(chat_id)
-
- if isinstance(peer, types.InputPeerChannel):
- await self.send(
- functions.channels.UpdatePinnedMessage(
- channel=peer,
- id=0
- )
+ await self.send(
+ functions.messages.UpdatePinnedMessage(
+ peer=await 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
diff --git a/pyrogram/client/methods/messages/__init__.py b/pyrogram/client/methods/messages/__init__.py
index 35dae756..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
@@ -33,11 +34,13 @@ 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
from .send_video_note import SendVideoNote
from .send_voice import SendVoice
+from .vote_poll import VotePoll
class Messages(
@@ -62,6 +65,9 @@ class Messages(
SendVenue,
SendVideo,
SendVideoNote,
- SendVoice
+ SendVoice,
+ SendPoll,
+ VotePoll,
+ RetractVote
):
pass
diff --git a/pyrogram/client/methods/messages/edit_message_media.py b/pyrogram/client/methods/messages/edit_message_media.py
index 6c87ead8..596a1beb 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/retract_vote.py b/pyrogram/client/methods/messages/retract_vote.py
new file mode 100644
index 00000000..e0852355
--- /dev/null
+++ b/pyrogram/client/methods/messages/retract_vote.py
@@ -0,0 +1,54 @@
+# 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):
+ 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),
+ msg_id=message_id,
+ options=[]
+ )
+ )
+
+ return True
diff --git a/pyrogram/client/methods/messages/send_animation.py b/pyrogram/client/methods/messages/send_animation.py
index 923a1fbf..ce1bb319 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 5c949e59..f37925be 100644
--- a/pyrogram/client/methods/messages/send_audio.py
+++ b/pyrogram/client/methods/messages/send_audio.py
@@ -166,7 +166,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 2dfc0c8b..60a57f95 100644
--- a/pyrogram/client/methods/messages/send_document.py
+++ b/pyrogram/client/methods/messages/send_document.py
@@ -147,7 +147,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 68783cfe..72a937b9 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 aeeffa62..13023628 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_poll.py b/pyrogram/client/methods/messages/send_poll.py
new file mode 100644
index 00000000..33f44767
--- /dev/null
+++ b/pyrogram/client/methods/messages/send_poll.py
@@ -0,0 +1,95 @@
+# 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):
+ 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":
+ """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),
+ 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}
+ )
diff --git a/pyrogram/client/methods/messages/send_sticker.py b/pyrogram/client/methods/messages/send_sticker.py
index 688ee87b..19404535 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 7f11ba5b..d4ab9551 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 edb595af..f00d26a0 100644
--- a/pyrogram/client/methods/messages/send_video_note.py
+++ b/pyrogram/client/methods/messages/send_video_note.py
@@ -145,7 +145,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 ac2e876e..037cd64e 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/messages/vote_poll.py b/pyrogram/client/methods/messages/vote_poll.py
new file mode 100644
index 00000000..3404a7bd
--- /dev/null
+++ b/pyrogram/client/methods/messages/vote_poll.py
@@ -0,0 +1,60 @@
+# 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):
+ 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(
+ functions.messages.SendVote(
+ peer=self.resolve_peer(chat_id),
+ msg_id=message_id,
+ options=[poll.options[option].data]
+ )
+ )
+
+ return True
diff --git a/pyrogram/client/methods/password/change_cloud_password.py b/pyrogram/client/methods/password/change_cloud_password.py
index f0271f14..94b9f568 100644
--- a/pyrogram/client/methods/password/change_cloud_password.py
+++ b/pyrogram/client/methods/password/change_cloud_password.py
@@ -17,17 +17,17 @@
# 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
class ChangeCloudPassword(BaseClient):
async def change_cloud_password(self,
- current_password: str,
- new_password: str,
- new_hint: str = "") -> bool:
+ current_password: str,
+ new_password: str,
+ new_hint: str = "") -> bool:
"""Use this method to change your Two-Step Verification password (Cloud Password) with a new one.
Args:
@@ -41,28 +41,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 = await 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 await 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
- )
+ await 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
diff --git a/pyrogram/client/methods/password/enable_cloud_password.py b/pyrogram/client/methods/password/enable_cloud_password.py
index 0d57f2e0..c9b17660 100644
--- a/pyrogram/client/methods/password/enable_cloud_password.py
+++ b/pyrogram/client/methods/password/enable_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, btoi, itob
from ...ext import BaseClient
@@ -27,10 +27,10 @@ class EnableCloudPassword(BaseClient):
async def enable_cloud_password(self,
password: str,
hint: str = "",
- email: str = "") -> bool:
+ email: str = None) -> bool:
"""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``):
@@ -43,27 +43,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 = await 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()
+ if r.has_password:
+ raise ValueError("There is already a cloud password enabled")
- return await 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
- )
+ 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)))
+
+ await 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
)
)
- else:
- return False
+ )
+
+ return True
diff --git a/pyrogram/client/methods/password/remove_cloud_password.py b/pyrogram/client/methods/password/remove_cloud_password.py
index e3e70aae..dddb5e68 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
@@ -32,25 +31,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 = await 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 await self.send(
- functions.account.UpdatePasswordSettings(
- current_password_hash=password_hash,
- new_settings=types.account.PasswordInputSettings(
- new_salt=b"",
- new_password_hash=b"",
- hint=""
- )
+ await 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
diff --git a/pyrogram/client/methods/password/utils.py b/pyrogram/client/methods/password/utils.py
new file mode 100644
index 00000000..01c3fe49
--- /dev/null
+++ b/pyrogram/client/methods/password/utils.py
@@ -0,0 +1,104 @@
+# 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)
+
+
+# noinspection PyPep8Naming
+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)
diff --git a/pyrogram/client/methods/users/delete_user_profile_photos.py b/pyrogram/client/methods/users/delete_user_profile_photos.py
index 64c22fa8..5070d2bf 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""
)
)
diff --git a/pyrogram/client/types/__init__.py b/pyrogram/client/types/__init__.py
index 8289a947..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
+ 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 3ab359ae..d402ae48 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_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/message.py b/pyrogram/client/types/messages_and_media/message.py
index 3267c4dc..184f910e 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,
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..ab59b1ad
--- /dev/null
+++ b/pyrogram/client/types/messages_and_media/poll.py
@@ -0,0 +1,102 @@
+# 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_option import PollOption
+from ..pyrogram_type import 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",
+ id: int,
+ closed: bool,
+ question: str,
+ options: List[PollOption],
+ total_voters: int,
+ option_chosen: int = None):
+ super().__init__(client)
+
+ self.id = id
+ self.closed = closed
+ self.question = question
+ self.options = options
+ self.total_voters = total_voters
+ self.option_chosen = option_chosen
+
+ @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
+ option_chosen = None
+
+ options = []
+
+ for i, answer in enumerate(poll.answers):
+ voters = 0
+
+ if results:
+ result = results[i]
+ voters = result.voters
+
+ if result.chosen:
+ option_chosen = i
+
+ options.append(PollOption(
+ text=answer.text,
+ voters=voters,
+ data=answer.option,
+ client=client
+ ))
+
+ return Poll(
+ id=poll.id,
+ closed=poll.closed,
+ question=poll.question,
+ options=options,
+ total_voters=total_voters,
+ option_chosen=option_chosen,
+ client=client
+ )
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..240368fc
--- /dev/null
+++ b/pyrogram/client/types/messages_and_media/poll_option.py
@@ -0,0 +1,47 @@
+# 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):
+ """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):
+ super().__init__(client)
+
+ self.text = text
+ self.voters = voters
+ self.data = data
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