diff --git a/compiler/api/source/main_api.tl b/compiler/api/source/main_api.tl index fa2c7af8..d8c3eae1 100644 --- a/compiler/api/source/main_api.tl +++ b/compiler/api/source/main_api.tl @@ -1,4 +1,4 @@ -// https://raw.githubusercontent.com/telegramdesktop/tdesktop/dev/Telegram/Resources/scheme.tl +// https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/Resources/tl/api.tl /////////////////////////////// ///////// Main application API @@ -86,7 +86,7 @@ storage.fileMp4#b3cea0e4 = storage.FileType; storage.fileWebp#1081464c = storage.FileType; 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 support:flags.23?true scam:flags.24?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; +user#938458c1 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 support:flags.23?true scam:flags.24?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?Vector bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User; userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto; userProfilePhoto#ecd75d8c photo_id:long photo_small:FileLocation photo_big:FileLocation dc_id:int = UserProfilePhoto; @@ -101,11 +101,11 @@ userStatusLastMonth#77ebc742 = UserStatus; chatEmpty#9ba2d800 id:int = Chat; chat#3bda1bde flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true deactivated:flags.5?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat; chatForbidden#7328bdb id:int title:string = Chat; -channel#4df30834 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?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?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat; +channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?Vector admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights 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#1b7c9db3 flags:# can_set_username:flags.7?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:flags.3?Vector pinned_msg_id:flags.6?int folder_id:flags.11?int = ChatFull; -channelFull#10916653 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 can_set_location:flags.16?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 folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation pts:int = ChatFull; +chatFull#1b7c9db3 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:flags.3?Vector pinned_msg_id:flags.6?int folder_id:flags.11?int = ChatFull; +channelFull#2d895c74 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 can_set_location:flags.16?true has_scheduled:flags.19?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 folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int pts:int = ChatFull; chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant; chatParticipantCreator#da13538a user_id:int = ChatParticipant; @@ -118,7 +118,7 @@ chatPhotoEmpty#37c1011c = ChatPhoto; chatPhoto#475cdbd5 photo_small:FileLocation photo_big:FileLocation dc_id:int = 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 from_scheduled:flags.18?true legacy:flags.19?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#452c0e65 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 legacy:flags.19?true edit_hide:flags.21?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 restriction_reason:flags.22?Vector = 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 legacy:flags.19?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; @@ -172,11 +172,10 @@ photoStrippedSize#e0b0bc2e type:string bytes:bytes = PhotoSize; geoPointEmpty#1117dd5f = GeoPoint; geoPoint#296f104 long:double lat:double access_hash:long = GeoPoint; -auth.checkedPhone#811ea28e phone_registered:Bool = auth.CheckedPhone; - -auth.sentCode#38faab5f flags:# phone_registered:flags.0?true type:auth.SentCodeType phone_code_hash:string next_type:flags.1?auth.CodeType timeout:flags.2?int terms_of_service:flags.3?help.TermsOfService = auth.SentCode; +auth.sentCode#5e002502 flags:# type:auth.SentCodeType phone_code_hash:string next_type:flags.1?auth.CodeType timeout:flags.2?int = auth.SentCode; auth.authorization#cd050916 flags:# tmp_sessions:flags.0?int user:User = auth.Authorization; +auth.authorizationSignUpRequired#44747e9a flags:# terms_of_service:flags.0?help.TermsOfService = auth.Authorization; auth.exportedAuthorization#df969c2d id:int bytes:bytes = auth.ExportedAuthorization; @@ -201,7 +200,7 @@ inputReportReasonOther#e1746d0a text:string = ReportReason; inputReportReasonCopyright#9b89f93a = ReportReason; inputReportReasonGeoIrrelevant#dbd4feed = ReportReason; -userFull#edf17c12 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 settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int = UserFull; +userFull#edf17c12 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int = UserFull; contact#f911c994 user_id:int mutual:Bool = Contact; @@ -323,6 +322,9 @@ updateChatDefaultBannedRights#54c01850 peer:Peer default_banned_rights:ChatBanne updateFolderPeers#19360dc0 folder_peers:Vector pts:int pts_count:int = Update; updatePeerSettings#6a7e7366 peer:Peer settings:PeerSettings = Update; updatePeerLocated#b4afcfb0 peers:Vector = Update; +updateNewScheduledMessage#39a51dfb message:Message = Update; +updateDeleteScheduledMessages#90866cee peer:Peer messages:Vector = Update; +updateTheme#8216fba3 theme:Theme = Update; updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; @@ -353,7 +355,7 @@ config#330b4067 flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:fla nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc; -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.appUpdate#1da7158f flags:# can_not_skip: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; @@ -419,6 +421,7 @@ inputPrivacyKeyPhoneP2P#db9e70d2 = InputPrivacyKey; inputPrivacyKeyForwards#a4dd4c08 = InputPrivacyKey; inputPrivacyKeyProfilePhoto#5719bacc = InputPrivacyKey; inputPrivacyKeyPhoneNumber#352dafa = InputPrivacyKey; +inputPrivacyKeyAddedByPhone#d1219bdd = InputPrivacyKey; privacyKeyStatusTimestamp#bc2eab30 = PrivacyKey; privacyKeyChatInvite#500e6dfa = PrivacyKey; @@ -427,6 +430,7 @@ privacyKeyPhoneP2P#39491cc8 = PrivacyKey; privacyKeyForwards#69ec56a3 = PrivacyKey; privacyKeyProfilePhoto#96151fed = PrivacyKey; privacyKeyPhoneNumber#d19ae46d = PrivacyKey; +privacyKeyAddedByPhone#42ffd42b = PrivacyKey; inputPrivacyValueAllowContacts#d09e07b = InputPrivacyRule; inputPrivacyValueAllowAll#184b35ce = InputPrivacyRule; @@ -470,7 +474,7 @@ messages.affectedMessages#84d19185 pts:int pts_count:int = messages.AffectedMess webPageEmpty#eb1477e8 id:long = WebPage; 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; +webPage#fa64e172 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 documents:flags.11?Vector cached_page:flags.10?Page = WebPage; webPageNotModified#85849473 = WebPage; 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; @@ -496,6 +500,7 @@ chatInvite#dfc2f58e flags:# channel:flags.0?true broadcast:flags.1?true public:f inputStickerSetEmpty#ffb62b95 = InputStickerSet; inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet; inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet; +inputStickerSetAnimatedEmoji#28703c8 = InputStickerSet; stickerSet#eeb46f27 flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumb:flags.4?PhotoSize thumb_dc_id:flags.4?int count:int hash:int = StickerSet; @@ -559,8 +564,8 @@ channelMessagesFilter#cd77d957 flags:# exclude_new_messages:flags.1?true ranges: channelParticipant#15ebac1d user_id:int date:int = ChannelParticipant; channelParticipantSelf#a3289a6d user_id:int inviter_id:int date:int = ChannelParticipant; -channelParticipantCreator#e3e2e1f9 user_id:int = ChannelParticipant; -channelParticipantAdmin#5daa6e23 flags:# can_edit:flags.0?true self:flags.1?true user_id:int inviter_id:flags.1?int promoted_by:int date:int admin_rights:ChatAdminRights = ChannelParticipant; +channelParticipantCreator#808d15a4 flags:# user_id:int rank:flags.0?string = ChannelParticipant; +channelParticipantAdmin#ccbebbaf flags:# can_edit:flags.0?true self:flags.1?true user_id:int inviter_id:flags.1?int promoted_by:int date:int admin_rights:ChatAdminRights rank:flags.2?string = ChannelParticipant; channelParticipantBanned#1c0facaf flags:# left:flags.0?true user_id:int kicked_by:int date:int banned_rights:ChatBannedRights = ChannelParticipant; channelParticipantsRecent#de3f3c79 = ChannelParticipantsFilter; @@ -761,7 +766,7 @@ payments.paymentForm#3f56aea3 flags:# can_save_credentials:flags.2?true password payments.validatedRequestedInfo#d1451883 flags:# id:flags.0?string shipping_options:flags.1?Vector = payments.ValidatedRequestedInfo; payments.paymentResult#4e5f810d updates:Updates = payments.PaymentResult; -payments.paymentVerficationNeeded#6b56b921 url:string = payments.PaymentResult; +payments.paymentVerificationNeeded#d8411139 url:string = payments.PaymentResult; payments.paymentReceipt#500911e1 flags:# date:int bot_id:int invoice:Invoice provider_id:int info:flags.0?PaymentRequestedInfo shipping:flags.1?ShippingOption currency:string total_amount:long credentials_title:string users:Vector = payments.PaymentReceipt; @@ -828,6 +833,7 @@ channelAdminLogEventActionDefaultBannedRights#2df5fc0a prev_banned_rights:ChatBa channelAdminLogEventActionStopPoll#8f079643 message:Message = ChannelAdminLogEventAction; channelAdminLogEventActionChangeLinkedChat#a26f881b prev_value:int new_value:int = ChannelAdminLogEventAction; channelAdminLogEventActionChangeLocation#e6b76ae prev_value:ChannelLocation new_value:ChannelLocation = ChannelAdminLogEventAction; +channelAdminLogEventActionToggleSlowMode#53909779 prev_value:int new_value:int = ChannelAdminLogEventAction; channelAdminLogEvent#3b5a3e40 id:long date:int user_id:int action:ChannelAdminLogEventAction = ChannelAdminLogEvent; @@ -1003,7 +1009,7 @@ inputWallPaperSlug#72091c80 slug:string = InputWallPaper; account.wallPapersNotModified#1c199183 = account.WallPapers; account.wallPapers#702b65a9 hash:int wallpapers:Vector = account.WallPapers; -codeSettings#302f59f3 flags:# allow_flashcall:flags.0?true current_number:flags.1?true app_hash_persistent:flags.2?true app_hash:flags.3?string = CodeSettings; +codeSettings#debebe83 flags:# allow_flashcall:flags.0?true current_number:flags.1?true allow_app_hash:flags.4?true = CodeSettings; wallPaperSettings#a12f40b8 flags:# blur:flags.1?true motion:flags.2?true background_color:flags.0?int intensity:flags.3?int = WallPaperSettings; @@ -1039,6 +1045,17 @@ channelLocation#209b82db geo_point:GeoPoint address:string = ChannelLocation; peerLocated#ca461b5d peer:Peer expires:int distance:int = PeerLocated; +restrictionReason#d072acb4 platform:string reason:string text:string = RestrictionReason; + +inputTheme#3c5693e9 id:long access_hash:long = InputTheme; +inputThemeSlug#f5890df1 slug:string = InputTheme; + +themeDocumentNotModified#483d270c = Theme; +theme#f7d90ce0 flags:# creator:flags.0?true default:flags.1?true id:long access_hash:long slug:string title:string document:flags.2?Document installs_count:int = Theme; + +account.themesNotModified#f41eb622 = account.Themes; +account.themes#7f676421 hash:int themes:Vector = account.Themes; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -1050,7 +1067,7 @@ invokeWithMessagesRange#365275f2 {X:Type} range:MessageRange query:!X = X; invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X; auth.sendCode#a677244f phone_number:string api_id:int api_hash:string settings:CodeSettings = auth.SentCode; -auth.signUp#1b067634 phone_number:string phone_code_hash:string phone_code:string first_name:string last_name:string = auth.Authorization; +auth.signUp#80eee427 phone_number:string phone_code_hash:string first_name:string last_name:string = auth.Authorization; auth.signIn#bcd51581 phone_number:string phone_code_hash:string phone_code:string = auth.Authorization; auth.logOut#5717da40 = Bool; auth.resetAuthorizations#9fab0d1a = Bool; @@ -1120,6 +1137,13 @@ account.installWallPaper#feed5769 wallpaper:InputWallPaper settings:WallPaperSet account.resetWallPapers#bb3b9804 = Bool; account.getAutoDownloadSettings#56da0b3f = account.AutoDownloadSettings; account.saveAutoDownloadSettings#76f36233 flags:# low:flags.0?true high:flags.1?true settings:AutoDownloadSettings = Bool; +account.uploadTheme#1c3db333 flags:# file:InputFile thumb:flags.0?InputFile file_name:string mime_type:string = Document; +account.createTheme#2b7ffd7f slug:string title:string document:InputDocument = Theme; +account.updateTheme#3b8ea202 flags:# format:string theme:InputTheme slug:flags.0?string title:flags.1?string document:flags.2?InputDocument = Theme; +account.saveTheme#f257106c theme:InputTheme unsave:Bool = Bool; +account.installTheme#7ae43737 flags:# dark:flags.0?true format:flags.1?string theme:flags.1?InputTheme = Bool; +account.getTheme#8d9d742b format:string theme:InputTheme document_id:long = Theme; +account.getThemes#285946f8 format:string hash:int = account.Themes; users.getUsers#d91a548 id:Vector = Vector; users.getFullUser#ca30a5b1 id:InputUser = UserFull; @@ -1154,9 +1178,9 @@ messages.deleteHistory#1c015b09 flags:# just_clear:flags.0?true revoke:flags.1?t messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector = messages.AffectedMessages; messages.receivedMessages#5a954c0 max_id:int = Vector; messages.setTyping#a3825e50 peer:InputPeer action:SendMessageAction = Bool; -messages.sendMessage#fa88427a flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector = Updates; -messages.sendMedia#b8d1262b flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector = Updates; -messages.forwardMessages#708e0195 flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true grouped:flags.9?true from_peer:InputPeer id:Vector random_id:Vector to_peer:InputPeer = Updates; +messages.sendMessage#520c3870 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector schedule_date:flags.10?int = Updates; +messages.sendMedia#3491eba9 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector schedule_date:flags.10?int = Updates; +messages.forwardMessages#d9fee60e flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true grouped:flags.9?true from_peer:InputPeer id:Vector random_id:Vector to_peer:InputPeer schedule_date:flags.10?int = Updates; messages.reportSpam#cf1592db peer:InputPeer = Bool; messages.getPeerSettings#3672e09c peer:InputPeer = PeerSettings; messages.report#bd82b658 peer:InputPeer id:Vector reason:ReportReason = Bool; @@ -1200,9 +1224,9 @@ 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 hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string = Updates; +messages.sendInlineBotResult#220815b0 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 schedule_date:flags.10?int = Updates; messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData; -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.editMessage#48f71778 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 schedule_date:flags.15?int = 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; @@ -1236,7 +1260,7 @@ messages.faveSticker#b9ffc55b id:InputDocument unfave:Bool = Bool; messages.getUnreadMentions#46578472 peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages; messages.readMentions#f0189d3 peer:InputPeer = messages.AffectedHistory; messages.getRecentLocations#bbc45b09 peer:InputPeer limit:int hash:int = messages.Messages; -messages.sendMultiMedia#2095512f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector = Updates; +messages.sendMultiMedia#cc0110cb flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector schedule_date:flags.10?int = Updates; messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile; messages.searchStickerSets#c2b7d08b flags:# exclude_featured:flags.0?true q:string hash:int = messages.FoundStickerSets; messages.getSplitRanges#1cff7e08 = Vector; @@ -1258,6 +1282,10 @@ messages.getSearchCounters#732eef00 peer:InputPeer filters:Vector = messages.Messages; +messages.sendScheduledMessages#bd38850a peer:InputPeer id:Vector = Updates; +messages.deleteScheduledMessages#59ae2b16 peer:InputPeer id:Vector = Updates; updates.getState#edd4882a = updates.State; updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; @@ -1269,7 +1297,7 @@ photos.deletePhotos#87cf7f2f id:Vector = Vector; photos.getUserPhotos#91cd32a8 user_id:InputUser offset:int max_id:long limit:int = photos.Photos; upload.saveFilePart#b304a621 file_id:long file_part:int bytes:bytes = Bool; -upload.getFile#e3a6cfb5 location:InputFileLocation offset:int limit:int = upload.File; +upload.getFile#b15a9afc flags:# precise:flags.0?true location:InputFileLocation offset:int limit:int = upload.File; upload.saveBigFilePart#de7b673d file_id:long file_part:int file_total_parts:int bytes:bytes = Bool; upload.getWebFile#24e6818d location:InputWebFileLocation offset:int limit:int = upload.WebFile; upload.getCdnFile#2000bcc3 file_token:bytes offset:int limit:int = upload.CdnFile; @@ -1307,7 +1335,7 @@ channels.getParticipant#546dd7a6 channel:InputChannel user_id:InputUser = channe channels.getChannels#a7f6bbb id:Vector = messages.Chats; channels.getFullChannel#8736a09 channel:InputChannel = messages.ChatFull; channels.createChannel#3d5fb10f flags:# broadcast:flags.0?true megagroup:flags.1?true title:string about:string geo_point:flags.2?InputGeoPoint address:flags.2?string = Updates; -channels.editAdmin#70f893ba channel:InputChannel user_id:InputUser admin_rights:ChatAdminRights = Updates; +channels.editAdmin#d33c8902 channel:InputChannel user_id:InputUser admin_rights:ChatAdminRights rank:string = Updates; channels.editTitle#566decd0 channel:InputChannel title:string = Updates; channels.editPhoto#f12e57c9 channel:InputChannel photo:InputChatPhoto = Updates; channels.checkUsername#10e6bd2c channel:InputChannel username:string = Bool; @@ -1330,6 +1358,7 @@ channels.getGroupsForDiscussion#f5dad378 = messages.Chats; channels.setDiscussionGroup#40582bb2 broadcast:InputChannel group:InputChannel = Bool; channels.editCreator#8f38cd1f channel:InputChannel user_id:InputUser password:InputCheckPasswordSRP = Updates; channels.editLocation#58e63f6d channel:InputChannel geo_point:InputGeoPoint address:string = Bool; +channels.toggleSlowMode#edd49ef0 channel:InputChannel seconds:int = Updates; bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON; bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool; @@ -1364,4 +1393,4 @@ langpack.getLanguage#6a596502 lang_pack:string lang_code:string = LangPackLangua folders.editPeerFolders#6847d0ab folder_peers:Vector = Updates; folders.deleteFolder#1c295881 folder_id:int = Updates; -// LAYER 103 \ No newline at end of file +// LAYER 105 \ No newline at end of file diff --git a/compiler/docs/compiler.py b/compiler/docs/compiler.py index 212cf370..34db202f 100644 --- a/compiler/docs/compiler.py +++ b/compiler/docs/compiler.py @@ -128,10 +128,10 @@ def pyrogram_api(): utilities=""" Utilities start - stop - restart idle + stop run + restart add_handler remove_handler stop_transmission @@ -249,6 +249,22 @@ def pyrogram_api(): set_game_score get_game_high_scores """, + authorization=""" + Authorization + connect + disconnect + initialize + terminate + send_code + resend_code + sign_in + sign_up + get_password_hint + check_password + send_recovery_code + recover_password + accept_terms_of_service + """, advanced=""" Advanced send @@ -293,6 +309,7 @@ def pyrogram_api(): ChatMember ChatPermissions Dialog + Restriction """, messages_media=""" Messages & Media @@ -349,6 +366,11 @@ def pyrogram_api(): InputMessageContent InputMessageContent InputTextMessageContent + """, + authorization=""" + Authorization + SentCode + TermsOfService """ ) diff --git a/compiler/docs/template/methods.rst b/compiler/docs/template/methods.rst index 0de7ee87..a0c6df92 100644 --- a/compiler/docs/template/methods.rst +++ b/compiler/docs/template/methods.rst @@ -106,10 +106,24 @@ Bots {bots} +Authorization +------------- + +.. autosummary:: + :nosignatures: + + {authorization} + +.. toctree:: + :hidden: + + {authorization} + Advanced -------- -Learn more about these methods at :doc:`Advanced Usage <../../topics/advanced-usage>`. +Methods used only when dealing with the raw Telegram API. +Learn more about how to use the raw API at :doc:`Advanced Usage <../../topics/advanced-usage>`. .. autosummary:: :nosignatures: diff --git a/compiler/docs/template/types.rst b/compiler/docs/template/types.rst index 635a81d3..5c91e68f 100644 --- a/compiler/docs/template/types.rst +++ b/compiler/docs/template/types.rst @@ -92,4 +92,17 @@ InputMessageContent .. toctree:: :hidden: - {input_message_content} \ No newline at end of file + {input_message_content} + +Authorization +------------- + +.. autosummary:: + :nosignatures: + + {authorization} + +.. toctree:: + :hidden: + + {authorization} \ No newline at end of file diff --git a/compiler/error/source/500_INTERNAL_SERVER_ERROR.tsv b/compiler/error/source/500_INTERNAL_SERVER_ERROR.tsv index 4bbea8ea..a2fd91cb 100644 --- a/compiler/error/source/500_INTERNAL_SERVER_ERROR.tsv +++ b/compiler/error/source/500_INTERNAL_SERVER_ERROR.tsv @@ -10,4 +10,5 @@ WORKER_BUSY_TOO_LONG_RETRY Telegram is having internal problems. Please try agai INTERDC_X_CALL_ERROR Telegram is having internal problems at DC{x}. Please try again later INTERDC_X_CALL_RICH_ERROR Telegram is having internal problems at DC{x}. Please try again later FOLDER_DEAC_AUTOFIX_ALL Telegram is having internal problems. Please try again later -MSGID_DECREASE_RETRY Telegram is having internal problems. Please try again later \ No newline at end of file +MSGID_DECREASE_RETRY Telegram is having internal problems. Please try again later +MEMBER_OCCUPY_PRIMARY_LOC_FAILED Telegram is having internal problems. Please try again later \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index 47d0107e..2544800d 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -58,7 +58,7 @@ html_show_copyright = False html_theme_options = { "canonical_url": "https://docs.pyrogram.org/", "collapse_navigation": True, - "sticky_navigation": True, + "sticky_navigation": False, "logo_only": True, "display_version": True, "style_external_links": True diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py index c61eb6ea..964bf440 100644 --- a/pyrogram/client/client.py +++ b/pyrogram/client/client.py @@ -19,7 +19,6 @@ import asyncio import logging import math -import mimetypes import os import re import shutil @@ -44,13 +43,14 @@ from pyrogram.errors import ( PhoneCodeExpired, PhoneCodeEmpty, SessionPasswordNeeded, PasswordHashInvalid, FloodWait, PeerIdInvalid, FirstnameInvalid, PhoneNumberBanned, VolumeLocNotFound, UserMigrate, ChannelPrivate, PhoneNumberOccupied, - PasswordRecoveryNa, PasswordEmpty, AuthBytesInvalid -) + PasswordRecoveryNa, PasswordEmpty, AuthBytesInvalid, + BadRequest) from pyrogram.session import Auth, Session from .ext import utils, Syncer, BaseClient, Dispatcher from .ext.utils import ainput from .methods import Methods from .storage import Storage, FileStorage, MemoryStorage +from .types import User, SentCode, TermsOfService log = logging.getLogger(__name__) @@ -188,8 +188,6 @@ class Client(Methods, BaseClient): """ - terms_of_service_displayed = False - def __init__( self, session_name: Union[str, Storage], @@ -286,70 +284,65 @@ class Client(Methods, BaseClient): self._proxy["enabled"] = bool(value.get("enabled", True)) self._proxy.update(value) - async def start(self): - """Start the client. + async def connect(self) -> bool: + """ + Connect the client to Telegram servers. - This method connects the client to Telegram and, in case of new sessions, automatically manages the full login - process using an interactive prompt (by default). - - Has no parameters. + Returns: + ``bool``: On success, in case the passed-in session is authorized, True is returned. Otherwise, in case + the session needs to be authorized, False is returned. Raises: - ConnectionError: In case you try to start an already started client. - - Example: - .. code-block:: python - :emphasize-lines: 4 - - from pyrogram import Client - - app = Client("my_account") - app.start() - - ... # Call API methods - - app.stop() + ConnectionError: In case you try to connect an already connected client. """ - if self.is_started: - raise ConnectionError("Client has already been started") + if self.is_connected: + raise ConnectionError("Client is already connected") self.load_config() await self.load_session() - self.load_plugins() self.session = Session(self, self.storage.dc_id, self.storage.auth_key) await self.session.start() - self.is_started = True - try: - if self.storage.user_id is None: - if self.bot_token is None: - self.storage.is_bot = False - await self.authorize_user() - else: - self.storage.is_bot = True - await self.authorize_bot() + self.is_connected = True - if not self.storage.is_bot: - if self.takeout: - self.takeout_id = (await self.send(functions.account.InitTakeoutSession())).id - log.warning("Takeout session {} initiated".format(self.takeout_id)) + return bool(self.storage.user_id) - now = time.time() + async def disconnect(self): + """Disconnect the client from Telegram servers. - if abs(now - self.storage.date) > Client.OFFLINE_SLEEP: - await self.get_initial_dialogs() - await self.get_contacts() - else: - await self.send(functions.messages.GetPinnedDialogs(folder_id=0)) - await self.get_initial_dialogs_chunk() - else: - await self.send(functions.updates.GetState()) - except Exception as e: - self.is_started = False - await self.session.stop() - raise e + Raises: + ConnectionError: In case you try to disconnect an already disconnected client or in case you try to + disconnect a client that needs to be terminated first. + """ + if not self.is_connected: + raise ConnectionError("Client is already disconnected") + + if self.is_initialized: + raise ConnectionError("Can't disconnect an initialized client") + + await self.session.stop() + self.storage.close() + self.is_connected = False + + async def initialize(self): + """Initialize the client by starting up workers. + + This method will start updates and download workers. + It will also load plugins and start the internal dispatcher. + + Raises: + ConnectionError: In case you try to initialize a disconnected client or in case you try to initialize an + already initialized client. + """ + if not self.is_connected: + raise ConnectionError("Can't initialize a disconnected client") + + if self.is_initialized: + raise ConnectionError("Client is already initialized") + + self.load_plugins() for _ in range(Client.UPDATES_WORKERS): self.updates_worker_tasks.append( @@ -368,35 +361,21 @@ class Client(Methods, BaseClient): await self.dispatcher.start() await Syncer.add(self) - mimetypes.init() + Syncer.add(self) - return self + self.is_initialized = True - async def stop(self): - """Stop the Client. + async def terminate(self): + """Terminate the client by shutting down workers. - This method disconnects the client from Telegram and stops the underlying tasks. - - Has no parameters. + This method does the opposite of :meth:`~Client.initialize`. + It will stop the dispatcher and shut down updates and download workers. Raises: - ConnectionError: In case you try to stop an already stopped client. - - Example: - .. code-block:: python - :emphasize-lines: 8 - - from pyrogram import Client - - app = Client("my_account") - app.start() - - ... # Call API methods - - app.stop() + ConnectionError: In case you try to terminate a client that is already terminated. """ - if not self.is_started: - raise ConnectionError("Client is already stopped") + if not self.is_initialized: + raise ConnectionError("Client is already terminated") if self.takeout_id: await self.send(functions.account.FinishTakeoutSession()) @@ -430,8 +409,491 @@ class Client(Methods, BaseClient): self.media_sessions.clear() - self.is_started = False - await self.session.stop() + self.is_initialized = False + + async def send_code(self, phone_number: str) -> SentCode: + """Send the confirmation code to the given phone number. + + Parameters: + phone_number (``str``): + Phone number in international format (includes the country prefix). + + Returns: + :obj:`SentCode`: On success, an object containing information on the sent confirmation code is returned. + + Raises: + BadRequest: In case the phone number is invalid. + """ + phone_number = phone_number.strip(" +") + + while True: + try: + r = await self.send( + functions.auth.SendCode( + phone_number=phone_number, + api_id=self.api_id, + api_hash=self.api_hash, + settings=types.CodeSettings() + ) + ) + except (PhoneMigrate, NetworkMigrate) as e: + await self.session.stop() + + self.storage.dc_id = e.x + self.storage.auth_key = await Auth(self, self.storage.dc_id).create() + self.session = Session(self, self.storage.dc_id, self.storage.auth_key) + + await self.session.start() + else: + return SentCode._parse(r) + + async def resend_code(self, phone_number: str, phone_code_hash: str) -> SentCode: + """Re-send the confirmation code using a different type. + + The type of the code to be re-sent is specified in the *next_type* attribute of the :obj:`SentCode` object + returned by :meth:`send_code`. + + Parameters: + phone_number (``str``): + Phone number in international format (includes the country prefix). + + phone_code_hash (``str``): + Confirmation code identifier. + + Returns: + :obj:`SentCode`: On success, an object containing information on the re-sent confirmation code is returned. + + Raises: + BadRequest: In case the arguments are invalid. + """ + phone_number = phone_number.strip(" +") + + r = await self.send( + functions.auth.ResendCode( + phone_number=phone_number, + phone_code_hash=phone_code_hash + ) + ) + + return SentCode._parse(r) + + async def sign_in(self, phone_number: str, phone_code_hash: str, phone_code: str) -> Union[ + User, TermsOfService, bool]: + """Authorize a user in Telegram with a valid confirmation code. + + Parameters: + phone_number (``str``): + Phone number in international format (includes the country prefix). + + phone_code_hash (``str``): + Code identifier taken from the result of :meth:`~Client.send_code`. + + phone_code (``str``): + The valid confirmation code you received (either as Telegram message or as SMS in your phone number). + + Returns: + :obj:`User` | :obj:`TermsOfService` | bool: On success, in case the authorization completed, the user is + returned. In case the phone number needs to be registered first AND the terms of services accepted (with + :meth:`~Client.accept_terms_of_service`), an object containing them is returned. In case the phone number + needs to be registered, but the terms of services don't need to be accepted, False is returned instead. + + Raises: + BadRequest: In case the arguments are invalid. + SessionPasswordNeeded: In case a password is needed to sign in. + """ + phone_number = phone_number.strip(" +") + + r = await self.send( + functions.auth.SignIn( + phone_number=phone_number, + phone_code_hash=phone_code_hash, + phone_code=phone_code + ) + ) + + if isinstance(r, types.auth.AuthorizationSignUpRequired): + if r.terms_of_service: + return TermsOfService._parse(terms_of_service=r.terms_of_service) + + return False + else: + self.storage.user_id = r.user.id + self.storage.is_bot = False + + return User._parse(self, r.user) + + async def sign_up(self, phone_number: str, phone_code_hash: str, first_name: str, last_name: str = "") -> User: + """Register a new user in Telegram. + + Parameters: + phone_number (``str``): + Phone number in international format (includes the country prefix). + + phone_code_hash (``str``): + Code identifier taken from the result of :meth:`~Client.send_code`. + + first_name (``str``): + New user first name. + + last_name (``str``, *optional*): + New user last name. Defaults to "" (empty string). + + Returns: + :obj:`User`: On success, the new registered user is returned. + + Raises: + BadRequest: In case the arguments are invalid. + """ + phone_number = phone_number.strip(" +") + + r = await self.send( + functions.auth.SignUp( + phone_number=phone_number, + first_name=first_name, + last_name=last_name, + phone_code_hash=phone_code_hash + ) + ) + + self.storage.user_id = r.user.id + self.storage.is_bot = False + + return User._parse(self, r.user) + + async def sign_in_bot(self, bot_token: str) -> User: + """Authorize a bot using its bot token generated by BotFather. + + Parameters: + bot_token (``str``): + The bot token generated by BotFather + + Returns: + :obj:`User`: On success, the bot identity is return in form of a user object. + + Raises: + BadRequest: In case the bot token is invalid. + """ + while True: + try: + r = await self.send( + functions.auth.ImportBotAuthorization( + flags=0, + api_id=self.api_id, + api_hash=self.api_hash, + bot_auth_token=bot_token + ) + ) + except UserMigrate as e: + await self.session.stop() + + self.storage.dc_id = e.x + self.storage.auth_key = await Auth(self, self.storage.dc_id).create() + self.session = Session(self, self.storage.dc_id, self.storage.auth_key) + + await self.session.start() + else: + self.storage.user_id = r.user.id + self.storage.is_bot = True + + return User._parse(self, r.user) + + async def get_password_hint(self) -> str: + """Get your Two-Step Verification password hint. + + Returns: + ``str``: On success, the password hint as string is returned. + """ + return (await self.send(functions.account.GetPassword())).hint + + async def check_password(self, password: str) -> User: + """Check your Two-Step Verification password and log in. + + Parameters: + password (``str``): + Your Two-Step Verification password. + + Returns: + :obj:`User`: On success, the authorized user is returned. + + Raises: + BadRequest: In case the password is invalid. + """ + r = await self.send( + functions.auth.CheckPassword( + password=compute_check( + await self.send(functions.account.GetPassword()), + password + ) + ) + ) + + self.storage.user_id = r.user.id + self.storage.is_bot = False + + return User._parse(self, r.user) + + async def send_recovery_code(self) -> str: + """Send a code to your email to recover your password. + + Returns: + ``str``: On success, the hidden email pattern is returned and a recovery code is sent to that email. + + Raises: + BadRequest: In case no recovery email was set up. + """ + return (await self.send( + functions.auth.RequestPasswordRecovery() + )).email_pattern + + async def recover_password(self, recovery_code: str) -> User: + """Recover your password with a recovery code and log in. + + Parameters: + recovery_code (``str``): + The recovery code sent via email. + + Returns: + :obj:`User`: On success, the authorized user is returned and the Two-Step Verification password reset. + + Raises: + BadRequest: In case the recovery code is invalid. + """ + r = await self.send( + functions.auth.RecoverPassword( + code=recovery_code + ) + ) + + self.storage.user_id = r.user.id + self.storage.is_bot = False + + return User._parse(self, r.user) + + async def accept_terms_of_service(self, terms_of_service_id: str) -> bool: + """Accept the given terms of service. + + Parameters: + terms_of_service_id (``str``): + The terms of service identifier. + """ + r = await self.send( + functions.help.AcceptTermsOfService( + id=types.DataJSON( + data=terms_of_service_id + ) + ) + ) + + assert r + + return True + + async def authorize(self) -> User: + if self.bot_token is not None: + return await self.sign_in_bot(self.bot_token) + + while True: + if self.phone_number is None: + while True: + value = await ainput("Enter phone number or bot token: ") + confirm = await ainput("Is \"{}\" correct? (y/n): ".format(value)) + + if confirm in ("y", "1"): + break + elif confirm in ("n", "2"): + continue + + if ":" in value: + self.bot_token = value + return await self.sign_in_bot(value) + else: + self.phone_number = value + + try: + sent_code = await self.send_code(self.phone_number) + except BadRequest as e: + print(e.MESSAGE) + self.phone_number = None + except FloodWait as e: + print(e.MESSAGE.format(x=e.x)) + time.sleep(e.x) + except Exception as e: + log.error(e, exc_info=True) + else: + break + + if self.force_sms: + sent_code = await self.resend_code(self.phone_number, sent_code.phone_code_hash) + + print("The confirmation code has been sent via {}".format( + { + "app": "Telegram app", + "sms": "SMS", + "call": "phone call", + "flash_call": "phone flash call" + }[sent_code.type] + )) + + while True: + if self.phone_code is None: + self.phone_code = await ainput("Enter confirmation code: ") + + try: + signed_in = await self.sign_in(self.phone_number, sent_code.phone_code_hash, self.phone_code) + except BadRequest as e: + print(e.MESSAGE) + self.phone_code = None + except SessionPasswordNeeded as e: + print(e.MESSAGE) + + while True: + print("Password hint: {}".format(await self.get_password_hint())) + + if self.password is None: + self.password = await ainput("Enter password (empty to recover): ") + + try: + if self.password == "": + confirm = await ainput("Confirm password recovery (y/n): ") + + if confirm in ("y", "1"): + email_pattern = await self.send_recovery_code() + print("The recovery code has been sent to {}".format(email_pattern)) + + while True: + recovery_code = await ainput("Enter recovery code: ") + + try: + return await self.recover_password(recovery_code) + except BadRequest as e: + print(e.MESSAGE) + except FloodWait as e: + print(e.MESSAGE.format(x=e.x)) + time.sleep(e.x) + except Exception as e: + log.error(e, exc_info=True) + raise + + elif confirm in ("n", "2"): + self.password = None + else: + return await self.check_password(self.password) + except BadRequest as e: + print(e.MESSAGE) + self.password = None + except FloodWait as e: + print(e.MESSAGE.format(x=e.x)) + time.sleep(e.x) + except Exception as e: + log.error(e, exc_info=True) + raise + except FloodWait as e: + print(e.MESSAGE.format(x=e.x)) + time.sleep(e.x) + except Exception as e: + log.error(e, exc_info=True) + else: + break + + if isinstance(signed_in, User): + return signed_in + + while True: + self.first_name = await ainput("Enter first name: ") + self.last_name = await ainput("Enter last name (empty to skip): ") + + try: + signed_up = await self.sign_up( + self.phone_number, + sent_code.phone_code_hash, + self.first_name, + self.last_name + ) + except BadRequest as e: + print(e.MESSAGE) + self.first_name = None + self.last_name = None + except FloodWait as e: + print(e.MESSAGE.format(x=e.x)) + time.sleep(e.x) + else: + break + + if isinstance(signed_in, TermsOfService): + print("\n" + signed_in.text + "\n") + await self.accept_terms_of_service(signed_in.id) + + return signed_up + + def start(self): + """Start the client. + + This method connects the client to Telegram and, in case of new sessions, automatically manages the full + authorization process using an interactive prompt. + + Returns: + :obj:`Client`: The started client itself. + + Raises: + ConnectionError: In case you try to start an already started client. + + Example: + .. code-block:: python + :emphasize-lines: 4 + + from pyrogram import Client + + app = Client("my_account") + app.start() + + ... # Call API methods + + app.stop() + """ + is_authorized = self.connect() + + try: + if not is_authorized: + self.authorize() + + if not self.storage.is_bot and self.takeout: + self.takeout_id = self.send(functions.account.InitTakeoutSession()).id + log.warning("Takeout session {} initiated".format(self.takeout_id)) + + self.send(functions.updates.GetState()) + except Exception as e: + self.disconnect() + raise e + else: + self.initialize() + return self + + def stop(self): + """Stop the Client. + + This method disconnects the client from Telegram and stops the underlying tasks. + + Returns: + :obj:`Client`: The stopped client itself. + + Raises: + ConnectionError: In case you try to stop an already stopped client. + + Example: + .. code-block:: python + :emphasize-lines: 8 + + from pyrogram import Client + + app = Client("my_account") + app.start() + + ... # Call API methods + + app.stop() + """ + self.terminate() + self.disconnect() return self @@ -441,7 +903,8 @@ class Client(Methods, BaseClient): This method will first call :meth:`~Client.stop` and then :meth:`~Client.start` in a row in order to restart a client using a single method. - Has no parameters. + Returns: + :obj:`Client`: The restarted client itself. Raises: ConnectionError: In case you try to restart a stopped Client. @@ -466,6 +929,8 @@ class Client(Methods, BaseClient): await self.stop() await self.start() + return self + @staticmethod async def idle(stop_signals: tuple = (SIGINT, SIGTERM, SIGABRT)): """Block the main script execution until a signal is received. @@ -530,12 +995,6 @@ class Client(Methods, BaseClient): sequence. It makes running a client less verbose, but is not suitable in case you want to run more than one client in a single main script, since idle() will block after starting the own client. - Has no parameters. - - Args: - coroutine: (``Coroutine``, *optional*): - Pass a coroutine to run it until is complete. - Raises: ConnectionError: In case you try to run an already started client. @@ -645,8 +1104,6 @@ class Client(Methods, BaseClient): This method must be called inside a progress callback function in order to stop the transmission at the desired time. The progress callback is called every time a file chunk is uploaded/downloaded. - Has no parameters. - Example: .. code-block:: python :emphasize-lines: 9 @@ -673,8 +1130,6 @@ class Client(Methods, BaseClient): More detailed information about session strings can be found at the dedicated page of :doc:`Storage Engines <../../topics/storage-engines>`. - Has no parameters. - Returns: ``str``: The session serialized into a printable, url-safe string. @@ -1077,6 +1532,7 @@ class Client(Methods, BaseClient): access_hash=data.access_hash, thumb_size=data.thumb_size, peer_id=data.peer_id, + peer_access_hash=data.peer_access_hash, volume_id=data.volume_id, local_id=data.local_id, file_size=data.file_size, @@ -1219,8 +1675,8 @@ class Client(Methods, BaseClient): Raises: RPCError: In case of a Telegram RPC error. """ - if not self.is_started: - raise ConnectionError("Client has not been started") + if not self.is_connected: + raise ConnectionError("Client has not been started yet") if self.no_updates: data = functions.InvokeWithoutUpdates(query=data) @@ -1452,37 +1908,37 @@ class Client(Methods, BaseClient): log.warning('[{}] No plugin loaded from "{}"'.format( self.session_name, root)) - async def get_initial_dialogs_chunk(self, offset_date: int = 0): - while True: - try: - r = await self.send( - functions.messages.GetDialogs( - offset_date=offset_date, - offset_id=0, - offset_peer=types.InputPeerEmpty(), - limit=self.DIALOGS_AT_ONCE, - hash=0, - exclude_pinned=True - ) - ) - except FloodWait as e: - log.warning("get_dialogs flood: waiting {} seconds".format(e.x)) - await asyncio.sleep(e.x) - else: - log.info("Total peers: {}".format(self.storage.peers_count)) - return r - - async def get_initial_dialogs(self): - await self.send(functions.messages.GetPinnedDialogs(folder_id=0)) - - dialogs = await self.get_initial_dialogs_chunk() - offset_date = utils.get_offset_date(dialogs) - - while len(dialogs.dialogs) == self.DIALOGS_AT_ONCE: - dialogs = await self.get_initial_dialogs_chunk(offset_date) - offset_date = utils.get_offset_date(dialogs) - - await self.get_initial_dialogs_chunk() + # def get_initial_dialogs_chunk(self, offset_date: int = 0): + # while True: + # try: + # r = self.send( + # functions.messages.GetDialogs( + # offset_date=offset_date, + # offset_id=0, + # offset_peer=types.InputPeerEmpty(), + # limit=self.DIALOGS_AT_ONCE, + # hash=0, + # exclude_pinned=True + # ) + # ) + # except FloodWait as e: + # log.warning("get_dialogs flood: waiting {} seconds".format(e.x)) + # time.sleep(e.x) + # else: + # log.info("Total peers: {}".format(self.storage.peers_count)) + # return r + # + # def get_initial_dialogs(self): + # self.send(functions.messages.GetPinnedDialogs(folder_id=0)) + # + # dialogs = self.get_initial_dialogs_chunk() + # offset_date = utils.get_offset_date(dialogs) + # + # while len(dialogs.dialogs) == self.DIALOGS_AT_ONCE: + # dialogs = self.get_initial_dialogs_chunk(offset_date) + # offset_date = utils.get_offset_date(dialogs) + # + # self.get_initial_dialogs_chunk() async def resolve_peer(self, peer_id: Union[int, str]): @@ -1504,9 +1960,11 @@ class Client(Methods, BaseClient): ``InputPeer``: On success, the resolved peer id is returned in form of an InputPeer object. Raises: - RPCError: In case of a Telegram RPC error. KeyError: In case the peer doesn't exist in the internal database. """ + if not self.is_connected: + raise ConnectionError("Client has not been started yet") + try: return self.storage.get_peer_by_id(peer_id) except KeyError: @@ -1698,7 +2156,7 @@ class Client(Methods, BaseClient): file_part += 1 if progress: - await progress(self, min(file_part * part_size, file_size), file_size, *progress_args) + await progress(min(file_part * part_size, file_size), file_size, *progress_args) except Client.StopTransmission: raise except Exception as e: @@ -1733,7 +2191,7 @@ class Client(Methods, BaseClient): access_hash: int, thumb_size: str, peer_id: int, - volume_id: int, + peer_access_hash: int, volume_id: int, local_id: int, file_size: int, @@ -1777,7 +2235,10 @@ class Client(Methods, BaseClient): if media_type == 1: location = types.InputPeerPhotoFileLocation( - peer=self.resolve_peer(peer_id), + peer=types.InputPeerUser( + user_id=peer_id, + access_hash=peer_access_hash + ), volume_id=volume_id, local_id=local_id, big=is_big or None @@ -1833,7 +2294,6 @@ class Client(Methods, BaseClient): if progress: await progress( - self, min(offset, file_size) if file_size != 0 else offset, @@ -1919,7 +2379,6 @@ class Client(Methods, BaseClient): if progress: await progress( - self, min(offset, file_size) if file_size != 0 else offset, diff --git a/pyrogram/client/ext/base_client.py b/pyrogram/client/ext/base_client.py index 230c681f..15906adf 100644 --- a/pyrogram/client/ext/base_client.py +++ b/pyrogram/client/ext/base_client.py @@ -49,7 +49,6 @@ class BaseClient: PARENT_DIR = Path(sys.argv[0]).parent INVITE_LINK_RE = re.compile(r"^(?:https?://)?(?:www\.)?(?:t(?:elegram)?\.(?:org|me|dog)/joinchat/)([\w-]+)$") - BOT_TOKEN_RE = re.compile(r"^\d+:[\w-]+$") DIALOGS_AT_ONCE = 100 UPDATES_WORKERS = 1 DOWNLOAD_WORKERS = 4 @@ -102,7 +101,8 @@ class BaseClient: self.media_sessions = {} self.media_sessions_lock = asyncio.Lock() - self.is_started = None + self.is_connected = None + self.is_initialized = None self.takeout_id = None diff --git a/pyrogram/client/ext/file_data.py b/pyrogram/client/ext/file_data.py index 9a19cd5d..ad1da9b6 100644 --- a/pyrogram/client/ext/file_data.py +++ b/pyrogram/client/ext/file_data.py @@ -20,8 +20,9 @@ class FileData: def __init__( self, *, media_type: int = None, dc_id: int = None, document_id: int = None, access_hash: int = None, - thumb_size: str = None, peer_id: int = None, volume_id: int = None, local_id: int = None, is_big: bool = None, - file_size: int = None, mime_type: str = None, file_name: str = None, date: int = None + thumb_size: str = None, peer_id: int = None, peer_access_hash: int = None, volume_id: int = None, + local_id: int = None, is_big: bool = None, file_size: int = None, mime_type: str = None, file_name: str = None, + date: int = None ): self.media_type = media_type self.dc_id = dc_id @@ -29,6 +30,7 @@ class FileData: self.access_hash = access_hash self.thumb_size = thumb_size self.peer_id = peer_id + self.peer_access_hash = peer_access_hash self.volume_id = volume_id self.local_id = local_id self.is_big = is_big diff --git a/pyrogram/client/ext/utils.py b/pyrogram/client/ext/utils.py index 4093d645..47f2c240 100644 --- a/pyrogram/client/ext/utils.py +++ b/pyrogram/client/ext/utils.py @@ -116,8 +116,8 @@ def get_input_media_from_file_id( raise ValueError("This file_id can only be used for download: {}".format(file_id_str)) if media_type == 2: - unpacked = struct.unpack(". import os -from struct import unpack from typing import Union from pyrogram.api import functions, types @@ -62,15 +61,8 @@ class SetChatPhoto(BaseClient): if os.path.exists(photo): photo = types.InputChatUploadedPhoto(file=self.save_file(photo)) else: - unpacked = unpack(" +# +# 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 .terms_of_service import TermsOfService +from .sent_code import SentCode + +__all__ = ["TermsOfService", "SentCode"] diff --git a/pyrogram/client/types/authorization/sent_code.py b/pyrogram/client/types/authorization/sent_code.py new file mode 100644 index 00000000..b534df00 --- /dev/null +++ b/pyrogram/client/types/authorization/sent_code.py @@ -0,0 +1,86 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2019 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 pyrogram.api import types +from ..object import Object + + +class SentCode(Object): + """Contains info on a sent confirmation code. + + Parameters: + type (``str``): + Type of the current sent code. + Can be *"app"* (code sent via Telegram), *"sms"* (code sent via SMS), *"call"* (code sent via voice call) or + *"flash_call"* (code is in the last 5 digits of the caller's phone number). + + phone_code_hash (``str``): + Confirmation code identifier useful for the next authorization steps (either :meth:`~Client.sign_in` or + :meth:`~Client.sign_up`). + + next_type (``str``): + Type of the next code to be sent with :meth:`~Client.resend_code`. + Can be *"sms"* (code will be sent via SMS), *"call"* (code will be sent via voice call) or *"flash_call"* + (code will be in the last 5 digits of caller's phone number). + + timeout (``int``): + Delay in seconds before calling :meth:`~Client.resend_code`. + """ + + def __init__( + self, *, + type: str, + phone_code_hash: str, + next_type: str = None, + timeout: int = None + ): + super().__init__() + + self.type = type + self.phone_code_hash = phone_code_hash + self.next_type = next_type + self.timeout = timeout + + @staticmethod + def _parse(sent_code: types.auth.SentCode) -> "SentCode": + type = sent_code.type + + if isinstance(type, types.auth.SentCodeTypeApp): + type = "app" + elif isinstance(type, types.auth.SentCodeTypeSms): + type = "sms" + elif isinstance(type, types.auth.SentCodeTypeCall): + type = "call" + elif isinstance(type, types.auth.SentCodeTypeFlashCall): + type = "flash_call" + + next_type = sent_code.next_type + + if isinstance(next_type, types.auth.CodeTypeSms): + next_type = "sms" + elif isinstance(next_type, types.auth.CodeTypeCall): + next_type = "call" + elif isinstance(next_type, types.auth.CodeTypeFlashCall): + next_type = "flash_call" + + return SentCode( + type=type, + phone_code_hash=sent_code.phone_code_hash, + next_type=next_type, + timeout=sent_code.timeout + ) diff --git a/pyrogram/client/types/authorization/terms_of_service.py b/pyrogram/client/types/authorization/terms_of_service.py new file mode 100644 index 00000000..dfc322d3 --- /dev/null +++ b/pyrogram/client/types/authorization/terms_of_service.py @@ -0,0 +1,56 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2019 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 + +from pyrogram.api import types +from ..messages_and_media import MessageEntity +from ..object import Object + + +class TermsOfService(Object): + """Telegram's Terms of Service returned by :meth:`~Client.sign_in`. + + Parameters: + id (``str``): + Terms of Service identifier. + + text (``str``): + Terms of Service text. + + entities (List of :obj:`MessageEntity`): + Special entities like URLs that appear in the text. + """ + + def __init__(self, *, id: str, text: str, entities: List[MessageEntity]): + super().__init__() + + self.id = id + self.text = text + self.entities = entities + + @staticmethod + def _parse(terms_of_service: types.help.TermsOfService) -> "TermsOfService": + return TermsOfService( + id=terms_of_service.id.data, + text=terms_of_service.text, + entities=[ + MessageEntity._parse(None, entity, {}) + for entity in terms_of_service.entities + ] + ) diff --git a/pyrogram/client/types/messages_and_media/message.py b/pyrogram/client/types/messages_and_media/message.py index 58c461bd..bc1ff186 100644 --- a/pyrogram/client/types/messages_and_media/message.py +++ b/pyrogram/client/types/messages_and_media/message.py @@ -2743,7 +2743,7 @@ class Message(Object, Update): revoke=revoke ) - async def click(self, x: int or str, y: int = 0, quote: bool = None, timeout: int = 10): + async def click(self, x: int or str = 0, y: int = None, quote: bool = None, timeout: int = 10): """Bound method *click* of :obj:`Message`. Use as a shortcut for clicking a button attached to the message instead of: diff --git a/pyrogram/client/types/messages_and_media/photo.py b/pyrogram/client/types/messages_and_media/photo.py index 8ccaaf19..ce117a25 100644 --- a/pyrogram/client/types/messages_and_media/photo.py +++ b/pyrogram/client/types/messages_and_media/photo.py @@ -77,10 +77,10 @@ class Photo(Object): return Photo( file_id=encode( pack( - " Union[List[Union[StrippedThumbnail, "Thumbnail"]], None]: if isinstance(media, types.Photo): raw_thumbnails = media.sizes[:-1] - media_type = 0 + media_type = 2 elif isinstance(media, types.Document): raw_thumbnails = media.thumbs media_type = 14 @@ -87,10 +87,10 @@ class Thumbnail(Object): Thumbnail( file_id=encode( pack( - ". -from typing import Union +from typing import Union, List import pyrogram from pyrogram.api import types from .chat_permissions import ChatPermissions from .chat_photo import ChatPhoto +from .restriction import Restriction from ..object import Object from ...ext import utils @@ -87,8 +88,8 @@ class Chat(Object): members_count (``int``, *optional*): Chat members count, for groups, supergroups and channels only. - restriction_reason (``str``, *optional*): - The reason why this chat might be unavailable to some users. + restrictions (List of :obj:`Restriction`, *optional*): + The list of reasons why this chat might be unavailable to some users. This field is available only in case *is_restricted* is True. permissions (:obj:`ChatPermissions` *optional*): @@ -120,7 +121,7 @@ class Chat(Object): sticker_set_name: str = None, can_set_sticker_set: bool = None, members_count: int = None, - restriction_reason: str = None, + restrictions: List[Restriction] = None, permissions: "pyrogram.ChatPermissions" = None, distance: int = None ): @@ -143,7 +144,7 @@ class Chat(Object): self.sticker_set_name = sticker_set_name self.can_set_sticker_set = can_set_sticker_set self.members_count = members_count - self.restriction_reason = restriction_reason + self.restrictions = restrictions self.permissions = permissions self.distance = distance @@ -162,7 +163,7 @@ class Chat(Object): first_name=user.first_name, last_name=user.last_name, photo=ChatPhoto._parse(client, user.photo, peer_id), - restriction_reason=user.restriction_reason, + restrictions=pyrogram.List([Restriction._parse(r) for r in user.restriction_reason]) or None, client=client ) @@ -183,6 +184,7 @@ class Chat(Object): @staticmethod def _parse_channel_chat(client, channel: types.Channel) -> "Chat": peer_id = utils.get_channel_id(channel.id) + restriction_reason = getattr(channel, "restriction_reason", []) return Chat( id=peer_id, @@ -193,7 +195,7 @@ class Chat(Object): title=channel.title, username=getattr(channel, "username", None), photo=ChatPhoto._parse(client, getattr(channel, "photo", None), peer_id), - restriction_reason=getattr(channel, "restriction_reason", None), + restrictions=pyrogram.List([Restriction._parse(r) for r in restriction_reason]) or None, permissions=ChatPermissions._parse(getattr(channel, "default_banned_rights", None)), members_count=getattr(channel, "participants_count", None), client=client diff --git a/pyrogram/client/types/user_and_chats/chat_photo.py b/pyrogram/client/types/user_and_chats/chat_photo.py index 623aaca8..95160447 100644 --- a/pyrogram/client/types/user_and_chats/chat_photo.py +++ b/pyrogram/client/types/user_and_chats/chat_photo.py @@ -54,20 +54,40 @@ class ChatPhoto(Object): if not isinstance(chat_photo, (types.UserProfilePhoto, types.ChatPhoto)): return None + photo_id = getattr(chat_photo, "photo_id", 0) loc_small = chat_photo.photo_small loc_big = chat_photo.photo_big + peer = client.resolve_peer(peer_id) + + if isinstance(peer, types.InputPeerUser): + peer_id = peer.user_id + peer_access_hash = peer.access_hash + x = 0 + elif isinstance(peer, types.InputPeerChat): + peer_id = -peer.chat_id + peer_access_hash = 0 + x = -1 + else: + peer_id += 1000727379968 + peer_access_hash = peer.access_hash + x = -234 + return ChatPhoto( small_file_id=encode( pack( - " +# +# 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 pyrogram.api import types +from ..object import Object + + +class Restriction(Object): + """A restriction applied to bots or chats. + + Parameters: + platform (``str``): + The platform the restriction is applied to, e.g. "ios", "android" + + reason (``str``): + The restriction reason, e.g. "porn", "copyright". + + text (``str``): + The restriction text. + """ + + def __init__(self, *, platform: str, reason: str, text: str): + super().__init__(None) + + self.platform = platform + self.reason = reason + self.text = text + + @staticmethod + def _parse(restriction: types.RestrictionReason) -> "Restriction": + return Restriction( + platform=restriction.platform, + reason=restriction.reason, + text=restriction.text + ) diff --git a/pyrogram/client/types/user_and_chats/user.py b/pyrogram/client/types/user_and_chats/user.py index bfc1fc08..13068ff7 100644 --- a/pyrogram/client/types/user_and_chats/user.py +++ b/pyrogram/client/types/user_and_chats/user.py @@ -17,10 +17,12 @@ # along with Pyrogram. If not, see . import html +from typing import List import pyrogram from pyrogram.api import types from .chat_photo import ChatPhoto +from .restriction import Restriction from ..object import Object from ..update import Update @@ -101,8 +103,8 @@ class User(Object, Update): photo (:obj:`ChatPhoto `, *optional*): User's or bot's current profile photo. Suitable for downloads only. - restriction_reason (``str``, *optional*): - The reason why this bot might be unavailable to some users. + restrictions (List of :obj:`Restriction`, *optional*): + The list of reasons why this bot might be unavailable to some users. This field is available only in case *is_restricted* is True. """ @@ -130,7 +132,7 @@ class User(Object, Update): dc_id: int = None, phone_number: str = None, photo: ChatPhoto = None, - restriction_reason: str = None + restrictions: List[Restriction] = None ): super().__init__(client) @@ -154,7 +156,7 @@ class User(Object, Update): self.dc_id = dc_id self.phone_number = phone_number self.photo = photo - self.restriction_reason = restriction_reason + self.restrictions = restrictions def __format__(self, format_spec): if format_spec == "mention": @@ -186,7 +188,7 @@ class User(Object, Update): dc_id=getattr(user.photo, "dc_id", None), phone_number=user.phone, photo=ChatPhoto._parse(client, user.photo, user.id), - restriction_reason=user.restriction_reason, + restrictions=pyrogram.List([Restriction._parse(r) for r in user.restriction_reason]) or None, client=client ) @@ -322,3 +324,26 @@ class User(Object, Update): """ return self._client.unblock_user(self.id) + + def get_common_chats(self): + """Bound method *get_common_chats* of :obj:`User`. + + Use as a shortcut for: + + .. code-block:: python + + client.get_common_chats(123456789) + + Example: + .. code-block:: python + + user.get_common_chats() + + Returns: + True on success. + + Raises: + RPCError: In case of a Telegram RPC error. + """ + + return self._client.get_common_chats(self.id) diff --git a/pyrogram/connection/connection.py b/pyrogram/connection/connection.py index 571e6042..2cd55526 100644 --- a/pyrogram/connection/connection.py +++ b/pyrogram/connection/connection.py @@ -38,6 +38,7 @@ class Connection: def __init__(self, dc_id: int, test_mode: bool, ipv6: bool, proxy: dict, mode: int = 3): self.dc_id = dc_id + self.test_mode = test_mode self.ipv6 = ipv6 self.proxy = proxy self.address = DataCenter(dc_id, test_mode, ipv6) @@ -57,7 +58,8 @@ class Connection: self.protocol.close() await asyncio.sleep(1) else: - log.info("Connected! DC{} - IPv{} - {}".format( + log.info("Connected! {} DC{} - IPv{} - {}".format( + "Test" if self.test_mode else "Production", self.dc_id, "6" if self.ipv6 else "4", self.mode.__name__ diff --git a/pyrogram/session/session.py b/pyrogram/session/session.py index a0d879b4..c19aeaf3 100644 --- a/pyrogram/session/session.py +++ b/pyrogram/session/session.py @@ -22,11 +22,10 @@ from datetime import datetime, timedelta from hashlib import sha1 from io import BytesIO -from pyrogram.api.all import layer - import pyrogram from pyrogram import __copyright__, __license__, __version__ from pyrogram.api import functions, types +from pyrogram.api.all import layer from pyrogram.api.core import TLObject, MsgContainer, Int, Long, FutureSalt, FutureSalts from pyrogram.connection import Connection from pyrogram.crypto import MTProto @@ -413,9 +412,9 @@ class Session: raise e from None (log.warning if retries < 2 else log.info)( - "{}: {} Retrying {}".format( + "[{}] Retrying {} due to {}".format( Session.MAX_RETRIES - retries + 1, - datetime.now(), type(data))) + data.QUALNAME, e)) await asyncio.sleep(0.5) return await self.send(data, retries - 1, timeout)